/// <summary> /// Calcualte the Unrealized Pnl for this Tax lot /// </summary> /// <param name="env"></param> /// <returns></returns> public static double CalculateUnrealizedPnl(PostingEngineEnvironment env, TaxLotStatus taxLotStatus, double residualQuantity = 0, double endPrice = 0) { double multiplier = 1.0; if (env.SecurityDetails.ContainsKey(taxLotStatus.Trade.BloombergCode)) { multiplier = env.SecurityDetails[taxLotStatus.Trade.BloombergCode].Multiplier; } double fxrate = 1.0; // Lets get fx rate if needed if (!taxLotStatus.Trade.SettleCurrency.Equals(env.BaseCurrency)) { fxrate = Convert.ToDouble(FxRates.Find(env.ValueDate, taxLotStatus.Trade.SettleCurrency).Rate); } var eodPrice = 0.0; var prevEodPrice = 0.0; if (env.ValueDate == taxLotStatus.Trade.TradeDate) { prevEodPrice = taxLotStatus.Trade.SettleNetPrice; var eodMarketPrice = MarketPrices.GetPrice(env, env.ValueDate, taxLotStatus.Trade); if (!eodMarketPrice.Valid) { env.AddMessage(eodMarketPrice.Error); } eodPrice = eodMarketPrice.Price; } else { prevEodPrice = MarketPrices.GetPrice(env, env.PreviousValueDate, taxLotStatus.Trade).Price; eodPrice = MarketPrices.GetPrice(env, env.ValueDate, taxLotStatus.Trade).Price; } eodPrice = endPrice != 0 ? endPrice : eodPrice; // Use residual Quantity if specified var quantity = residualQuantity != 0.0 ? residualQuantity : taxLotStatus.Quantity; var priceDiff = (eodPrice - prevEodPrice); var unrealizedPnl = priceDiff * quantity; return(unrealizedPnl * fxrate * multiplier); }
/// <summary> /// Relieves the passed TaxLostStatus, this should only be used for FORWARDS etc that expire /// </summary> /// <param name="env">Environment</param> /// <param name="lot">The Tax Lot to relieve</param> /// <param name="trade">The current trade</param> /// <param name="quantity">Quantity to relieve</param> /// <param name="fxrate">Appropriate fxrate</param> internal static TaxLot RelieveTaxLot(PostingEngineEnvironment env, Transaction taxLotToRelieve, Transaction trade, double quantity, bool reverse = false) { var SettleNetPrice = trade.SettleNetPrice; if (taxLotToRelieve.LpOrderId.Equals(trade.LpOrderId)) { // Same, so we are dealing with the same trade, so we are backing out the same trade SettleNetPrice = MarketPrices.GetPrice(env, trade.SettleDate, trade).Price; } var prevFxRate = FxRates.Find(taxLotToRelieve.TradeDate, taxLotToRelieve.SettleCurrency).Rate; var investmentAtCost = quantity * taxLotToRelieve.SettleNetPrice * prevFxRate; if (reverse) { investmentAtCost = investmentAtCost * -1; } var tl = new TaxLot { Trade = trade, TradeDate = trade.TradeDate, InvestmentAtCost = investmentAtCost, // Needs to be the Investment Cost that we are relieving from the Tax BusinessDate = env.ValueDate, OpeningLotId = taxLotToRelieve.LpOrderId, ClosingLotId = trade.LpOrderId, TradePrice = taxLotToRelieve.SettleNetPrice, CostBasis = SettleNetPrice, Quantity = quantity }; CalculateRealizedPnl(env, tl); tl.Save(env.Connection, env.Transaction); return(tl); }
/// <summary> /// Run for each day that the Tax Lot remains open / partially closed /// </summary> /// <param name="env"></param> /// <param name="element">Trade we aee interested in</param> public void DailyEvent(PostingEngineEnvironment env, Transaction element) { double fxrate = 1.0; // Lets get fx rate if needed if (!element.SettleCurrency.Equals(env.BaseCurrency)) { fxrate = Convert.ToDouble(FxRates.Find(env.ValueDate, element.SettleCurrency).Rate); } // Calculate the unrealized PNL if (env.TaxLotStatus.ContainsKey(element.LpOrderId)) { // Determine if we need to accumulate unrealized PNL var taxlot = env.TaxLotStatus[element.LpOrderId]; // Check to see if the TaxLot is still open and it has a non zero Quantity if (!taxlot.Status.ToLowerInvariant().Equals("closed") && Math.Abs(taxlot.Quantity) > 0) { var listOfTags = new List <Tag> { Tag.Find("SecurityType"), Tag.Find("CustodianCode") }; // We have an open / partially closed tax lot so now need to calculate unrealized Pnl var quantity = taxlot.Quantity; var prevEodPrice = 0.0; var eodPrice = 0.0; if (env.ValueDate == element.TradeDate) { eodPrice = MarketPrices.GetPrice(env, env.ValueDate, element).Price; prevEodPrice = element.SettleNetPrice; } else { prevEodPrice = MarketPrices.GetPrice(env, env.PreviousValueDate, element).Price; eodPrice = MarketPrices.GetPrice(env, env.ValueDate, element).Price; } var unrealizedPnl = CommonRules.CalculateUnrealizedPnl(env, taxlot); AccountToFrom fromToAccounts = null; if (element.IsDerivative()) { var originalAccount = AccountUtils.GetDerivativeAccountType(unrealizedPnl); if (originalAccount.Contains("(Liabilities)")) { // This needs to be registered as a Credit to the Libabilities unrealizedPnl *= -1; } fromToAccounts = new AccountUtils().GetAccounts(env, originalAccount, "Change in Unrealized Derivatives Contracts at Fair Value", listOfTags, taxlot.Trade); } else { var originalAccount = taxlot.Side == "SHORT" ? "Mark to Market Shorts" : "Mark to Market Longs"; fromToAccounts = new AccountUtils().GetAccounts(env, originalAccount, "CHANGE IN UNREALIZED GAIN/(LOSS)", listOfTags, taxlot.Trade); } var fund = env.GetFund(element); var debit = new Journal(element) { Account = fromToAccounts.From, When = env.ValueDate, Symbol = taxlot.Symbol, Quantity = quantity, FxRate = fxrate, Value = env.SignedValue(fromToAccounts.From, fromToAccounts.To, true, unrealizedPnl), CreditDebit = env.DebitOrCredit(fromToAccounts.From, unrealizedPnl), StartPrice = prevEodPrice, EndPrice = eodPrice, Event = Event.DAILY_UNREALIZED_PNL, Fund = fund, }; var credit = new Journal(element) { Account = fromToAccounts.To, When = env.ValueDate, FxRate = fxrate, Quantity = quantity, Value = env.SignedValue(fromToAccounts.From, fromToAccounts.To, false, unrealizedPnl), CreditDebit = env.DebitOrCredit(fromToAccounts.To, env.SignedValue(fromToAccounts.From, fromToAccounts.To, false, unrealizedPnl)), Event = Event.DAILY_UNREALIZED_PNL, StartPrice = prevEodPrice, EndPrice = eodPrice, Fund = fund, }; Logger.Info($"[Journals] ==> From : {debit.CreditDebit}::{debit.Value}::{debit.Account.Type.Category.Name} --> To : {credit.CreditDebit}::{credit.Value}::{credit.Account.Type.Category.Name} ({unrealizedPnl})"); env.Journals.AddRange(new[] { debit, credit }); // For Derivatives this is un-necessary as we do not have an investment at cost, but we do have Fx on unsettled if (taxlot.Quantity != 0.0) { if (element.TradeDate != env.ValueDate) { new FxPosting().CreateFxUnsettled(env, element); } } } } else { if (fxrate != 1.0) { if (element.TradeDate != env.ValueDate && element.SettleDate >= env.ValueDate) { /* * var fxJournals = FxPosting.CreateFx( * env, * "DUE FROM/(TO) PRIME BROKERS ( Unsettled Activity )", * "fx gain or loss on unsettled balance", * "daily", * element.Quantity, null, element); * env.Journals.AddRange(fxJournals); */ } } } }
private void GenerateDailyUnrealized(PostingEngineEnvironment env, TaxLotStatus taxLotStatus, Transaction element, double quantity, double fxRate) { var prevEodPrice = 0.0; var eodPrice = 0.0; if (env.ValueDate == taxLotStatus.Trade.TradeDate) { prevEodPrice = taxLotStatus.Trade.SettleNetPrice; eodPrice = MarketPrices.GetPrice(env, env.ValueDate, taxLotStatus.Trade).Price; } else { prevEodPrice = MarketPrices.GetPrice(env, env.PreviousValueDate, taxLotStatus.Trade).Price; eodPrice = MarketPrices.GetPrice(env, env.ValueDate, taxLotStatus.Trade).Price; } var endPrice = element.SettleNetPrice; if (taxLotStatus.Quantity == 0.0) { eodPrice = endPrice; } var unrealizedPnl = CommonRules.CalculateUnrealizedPnl(env, taxLotStatus, quantity, eodPrice); var originalAccount = taxLotStatus.Side == "SHORT" ? "Mark to Market Shorts" : "Mark to Market Longs"; var fromToAccounts = new AccountUtils().GetAccounts(env, originalAccount, "CHANGE IN UNREALIZED GAIN/(LOSS)", listOfTags, taxLotStatus.Trade); if (taxLotStatus.Side == "SHORT") { unrealizedPnl *= -1; } var fund = env.GetFund(taxLotStatus.Trade); var debit = new Journal(taxLotStatus.Trade) { Account = fromToAccounts.From, When = env.ValueDate, Symbol = taxLotStatus.Symbol, Quantity = quantity, FxRate = fxRate, Value = env.SignedValue(fromToAccounts.From, fromToAccounts.To, true, unrealizedPnl), CreditDebit = env.DebitOrCredit(fromToAccounts.From, unrealizedPnl), StartPrice = prevEodPrice, EndPrice = eodPrice, Event = Event.DAILY_UNREALIZED_PNL, Fund = fund, }; var credit = new Journal(debit) { Account = fromToAccounts.To, Value = env.SignedValue(fromToAccounts.From, fromToAccounts.To, false, unrealizedPnl), CreditDebit = env.DebitOrCredit(fromToAccounts.To, unrealizedPnl), }; env.Journals.AddRange(new[] { debit, credit }); if (fxRate != 1.0) { if (element.TradeDate != env.ValueDate && element.SettleDate >= env.ValueDate) { var fxJournals = FxPosting.CreateFx( env, "DUE FROM/(TO) PRIME BROKERS ( Unsettled Activity )", "fx gain or loss on unsettled balance", "daily", quantity, null, element); env.Journals.AddRange(fxJournals); } if (taxLotStatus.Quantity != 0.0) { if (env.ValueDate.Equals(new DateTime(2019, 12, 17))) { } if (element.TradeDate != env.ValueDate) { // Has to happen for every day var fxJournalsForInvestmentAtCost = FxPosting.CreateFx( env, CommonRules.GetFXMarkToMarketAccountType(element, "FX MARKET TO MARKET ON STOCK COST"), "Change in unrealized due to fx on original Cost", "daily", quantity, taxLotStatus, element); env.Journals.AddRange(fxJournalsForInvestmentAtCost); new FxPosting().CreateFxUnsettled(env, element); } } } }
/// <summary> /// Generate all of the journal entries we need for reduction / closeout of a tax lot /// </summary> /// <param name="env"></param> /// <param name="lot"></param> /// <param name="taxlotStatus"></param> /// <param name="element"></param> /// <param name="workingQuantity"></param> /// <param name="fxrate"></param> /// <param name="multiplier"></param> private void GenerateJournals(PostingEngineEnvironment env, TaxLotDetail lot, TaxLotStatus taxlotStatus, Transaction element, double workingQuantity, double fxrate, double multiplier) { var buyTrade = env.FindTrade(lot.Trade.LpOrderId); var taxlot = CommonRules.RelieveTaxLot(env, lot, element, workingQuantity, true); // Has to happen for every day var fxJournalsForInvestmentAtCost = FxPosting.CreateFx( env, CommonRules.GetFXMarkToMarketAccountType(element, "FX MARKET TO MARKET ON STOCK COST"), "Change in unrealized due to fx on original Cost", "daily", workingQuantity, taxlotStatus, buyTrade); env.Journals.AddRange(fxJournalsForInvestmentAtCost); taxlotStatus.Quantity += workingQuantity; if (taxlotStatus.Quantity == 0) { taxlotStatus.Status = "Closed"; } else { taxlotStatus.Status = "Partially Closed"; } if (taxlotStatus.Quantity == 0.0) { // Is this really needed, as if the tax lot is zero then should not generate any additional unrealized pnl //GenerateDailyUnrealized(env, taxlotStatus, element, workingQuantity * -1, fxrate); } var eodPrice = MarketPrices.GetPrice(env, env.PreviousValueDate, buyTrade).Price; // Calculate the unrealized Backout PNL for the created Tax Lot var unrealizedPnl = Math.Abs(taxlot.Quantity) * (eodPrice - buyTrade.SettleNetPrice) * multiplier * fxrate; unrealizedPnl *= CommonRules.DetermineSign(element); // Need to backout the Unrealized PNL here, as we are reducing the position of the TaxLot CommonRules.ReverseUnrealizedPnl( env, buyTrade, element, unrealizedPnl * -1, buyTrade.SettleNetPrice, element.SettleNetPrice, fxrate); // Original FxRate var changeDueToFx = fxrate - taxlotStatus.FxRate; // Original Trade Price var changeInRealizedPnlDueToFx = changeDueToFx * (taxlot.TradePrice) * Math.Abs(taxlot.Quantity); var changeInUnRealizedPnlDueToFx = changeDueToFx * (taxlot.CostBasis - taxlot.TradePrice) * Math.Abs(taxlot.Quantity); CommonRules.PostRealizedPnl( env, buyTrade, taxlot.RealizedPnl, taxlot.TradePrice, taxlot.CostBasis, fxrate); if (fxrate != 1.0) { PostRealizedFxGain(env, buyTrade, changeInRealizedPnlDueToFx, taxlot.TradePrice, taxlot.CostBasis, changeDueToFx); } var fxChange = new FxPosting().CreateFxUnsettled(env, buyTrade); List <SqlParameter> sqlParams = new List <SqlParameter>(); sqlParams.Add(new SqlParameter("@busDate", env.ValueDate)); sqlParams.Add(new SqlParameter("@LpOrderId", lot.Trade.LpOrderId)); var dataTable = new SqlHelper(env.ConnectionString).GetDataTables("ClosingTaxLot", CommandType.StoredProcedure, sqlParams.ToArray()); var changeInUnRealized = 1.0; if (dataTable[0].Rows.Count > 0) { changeInUnRealized = Convert.ToDouble(dataTable[0].Rows[0][2]); } var changeInUnRealizedFx = 0.0; if (dataTable[1].Rows.Count > 0) { changeInUnRealizedFx = Convert.ToDouble(dataTable[1].Rows[0][2]); } if (changeInUnRealizedFx != 0.0) { var closeOut = changeInUnRealizedFx + fxChange; PostUnrealizedFxGain(env, buyTrade, closeOut, taxlot.TradePrice, taxlot.CostBasis, changeDueToFx); } var sumFxMarkToMarket = 0.0; if (dataTable[2].Rows.Count > 0) { sumFxMarkToMarket = Convert.ToDouble(dataTable[2].Rows[0][2]); sumFxMarkToMarket += fxJournalsForInvestmentAtCost[0].Value; ReversePosting(env, "Change in unrealized due to fx on original Cost", CommonRules.GetFXMarkToMarketAccountType(element, "FX MARKET TO MARKET ON STOCK COST"), buyTrade, sumFxMarkToMarket); } var listOfFromTags = new List <Tag> { Tag.Find("SecurityType"), Tag.Find("CustodianCode") }; /* * var markToMarketAccount = (buyTrade.IsShort() || buyTrade.IsCover()) ? "Mark to Market Shorts" : "Mark to Market Longs"; * * var fromTo = new AccountUtils().GetAccounts(env, "CHANGE IN UNREALIZED GAIN/(LOSS)", markToMarketAccount, listOfFromTags, element); * * if (fxrate == 1.0) * { * changeInUnRealized = Convert.ToDouble(taxlot.RealizedPnl) * -1; * } * * // Now Generate Entries for the trade that is drawing down on the taxLot * var fromJournal = new Journal(buyTrade) * { * Account = fromTo.From, * When = env.ValueDate, * * CreditDebit = env.DebitOrCredit(fromTo.From, changeInUnRealized), * Value = env.SignedValue(fromTo.From, fromTo.To, true, changeInUnRealized), * Event = Event.UNREALIZED_PNL, * Fund = env.GetFund(element), * * StartPrice = taxlot.TradePrice, * EndPrice = taxlot.CostBasis, * FxRate = fxrate, * }; * * var toJournal = new Journal(buyTrade) * { * Account = fromTo.To, * When = env.ValueDate, * * CreditDebit = env.DebitOrCredit(fromTo.To, changeInUnRealized * -1), * Value = env.SignedValue(fromTo.From, fromTo.To, false, changeInUnRealized), * Event = Event.UNREALIZED_PNL, * Fund = env.GetFund(element), * * StartPrice = taxlot.TradePrice, * EndPrice = taxlot.CostBasis, * FxRate = fxrate, * }; * * env.Journals.AddRange(new[] { fromJournal, toJournal }); */ }
/// <summary> /// Run for each day that the Tax Lot remains open / partially closed /// </summary> /// <param name="env"></param> /// <param name="element">Trade we aee interested in</param> public void DailyEvent(PostingEngineEnvironment env, Transaction element) { double fxrate = 1.0; // Lets get fx rate if needed if (!element.SettleCurrency.Equals(env.BaseCurrency)) { fxrate = Convert.ToDouble(FxRates.Find(env.ValueDate, element.SettleCurrency).Rate); } // Calculate the unrealized PNL if (env.TaxLotStatus.ContainsKey(element.LpOrderId)) { // Determine if we need to accumulate unrealized PNL var taxlot = env.TaxLotStatus[element.LpOrderId]; // Check to see if the TaxLot is still open and it has a non zero Quantity if (!taxlot.Status.ToLowerInvariant().Equals("closed") && Math.Abs(taxlot.Quantity) > 0) { var listOfTags = new List <Tag> { Tag.Find("SecurityType"), Tag.Find("CustodianCode") }; var split = element.Symbol.Split(new char[] { '/', ' ' }); var baseCurrency = split[0]; var riskCurrency = split[1]; var eodPrice = MarketPrices.GetPrice(env, env.ValueDate, element).Price; var prevEodPrice = MarketPrices.GetPrice(env, env.PreviousValueDate, element).Price; if (baseCurrency.Equals(env.BaseCurrency)) { if (env.ValueDate == element.TradeDate) { prevEodPrice = element.SettleNetPrice; } } else { if (env.ValueDate == element.TradeDate) { prevEodPrice = element.SettleNetPrice; } } // we need to do this when there is no price for the trade from market data if (prevEodPrice == 0.0) { prevEodPrice = element.SettleNetPrice; } if (eodPrice == 0.0) { eodPrice = element.SettleNetPrice; } // We have an open / partially closed tax lot so now need to calculate unrealized Pnl var quantity = taxlot.Quantity; var rateDiff = (eodPrice - prevEodPrice); var unrealizedPnl = (rateDiff * quantity); if (baseCurrency.Equals(env.BaseCurrency)) { unrealizedPnl = unrealizedPnl / eodPrice; } var originalAccount = AccountUtils.GetDerivativeAccountType(unrealizedPnl); var fromToAccounts = new AccountUtils().GetAccounts(env, originalAccount, "Change in Unrealized Derivatives Contracts at Fair Value", listOfTags, taxlot.Trade); var fund = env.GetFund(element); var debit = new Journal(element) { Account = fromToAccounts.From, When = env.ValueDate, Symbol = taxlot.Symbol, Quantity = quantity, FxRate = rateDiff, Value = env.SignedValue(fromToAccounts.From, fromToAccounts.To, false, unrealizedPnl), CreditDebit = env.DebitOrCredit(fromToAccounts.From, unrealizedPnl), StartPrice = prevEodPrice, EndPrice = eodPrice, Event = Event.DAILY_UNREALIZED_PNL, Fund = fund, }; var credit = new Journal(debit) { Account = fromToAccounts.To, Value = env.SignedValue(fromToAccounts.From, fromToAccounts.To, true, unrealizedPnl), CreditDebit = env.DebitOrCredit(fromToAccounts.To, unrealizedPnl), }; env.Journals.AddRange(new[] { debit, credit }); if (element.LpOrderId.Equals("3bbf9793-d01a-4a08-b25c-448fd1777ef1")) { } if (element.TradeDate != env.ValueDate && element.SettleDate >= env.ValueDate) { var fxJournals = FxPosting.CreateFx( env, "daily", quantity, null, element); env.Journals.AddRange(fxJournals); } } } else { if (fxrate != 1.0) { if (element.TradeDate != env.ValueDate && element.SettleDate >= env.ValueDate) { var fxJournals = FxPosting.CreateFx( env, "daily", element.Quantity, null, element); env.Journals.AddRange(fxJournals); } } } }
public void SettlementDateEvent(PostingEngineEnvironment env, Transaction element) { // On Settlement Date we backout the Tax Lot for FORWARDS if (env.TaxLotStatus.ContainsKey(element.LpOrderId)) { var taxlotStatus = env.TaxLotStatus[element.LpOrderId]; var split = element.Symbol.Split(new char[] { '/', ' ' }); var baseCurrency = split[0]; var riskCurrency = split[1]; var accountBuy = new AccountUtils().CreateAccount(atSettledCash, new List <string> { element.SecurityType, element.CustodianCode, baseCurrency }); var accountSell = new AccountUtils().CreateAccount(atSettledCash, new List <string> { element.SecurityType, element.CustodianCode, riskCurrency }); new AccountUtils().SaveAccountDetails(env, accountBuy); new AccountUtils().SaveAccountDetails(env, accountSell); var fxCurrency = riskCurrency; var tradePrice = taxlotStatus.TradePrice; var baseQuantity = element.Quantity; var eodPrice = MarketPrices.GetPrice(env, env.ValueDate, element).Price; var fxRate = FxRates.Find(env.ValueDate, fxCurrency).Rate; var buyValue = baseQuantity * tradePrice * fxRate; var sellValue = baseQuantity * eodPrice * fxRate; if (element.IsBuy()) // BUY { var realizedPnl = sellValue - buyValue; var debit = new Journal(accountBuy, Event.SETTLED_CASH, env.ValueDate) { Source = element.LpOrderId, Fund = env.GetFund(element), FxCurrency = baseCurrency, Symbol = element.Symbol, SecurityId = element.SecurityId, Quantity = Convert.ToDouble(element.Quantity), FxRate = tradePrice, StartPrice = tradePrice, EndPrice = eodPrice, Value = env.SignedValue(accountBuy, accountSell, true, buyValue), CreditDebit = env.DebitOrCredit(accountBuy, buyValue), }; var credit = new Journal(accountSell, Event.SETTLED_CASH, env.ValueDate) { Source = element.LpOrderId, Fund = env.GetFund(element), FxCurrency = riskCurrency, Symbol = element.Symbol, SecurityId = element.SecurityId, Quantity = Convert.ToDouble(element.Quantity), FxRate = tradePrice, StartPrice = 0, EndPrice = 0, Value = env.SignedValue(accountBuy, accountSell, true, sellValue * -1), CreditDebit = env.DebitOrCredit(accountSell, sellValue), }; env.Journals.AddRange(new[] { credit, debit }); var originalAccount = AccountUtils.GetDerivativeAccountType(realizedPnl); // Realized Pnl to go along with the Settled Cash CommonRules.GenerateJournalEntry(env, element, listOfTags, realizedAccountType, Event.REALIZED_PNL, realizedPnl); CommonRules.GenerateJournalEntries(env, element, listOfTags, originalAccount, "Change in Unrealized Derivatives Contracts at Fair Value", realizedPnl * -1); } else // SELL { var realizedPnl = buyValue - sellValue; var debit = new Journal(accountBuy, Event.SETTLED_CASH, env.ValueDate) { Source = element.LpOrderId, Fund = env.GetFund(element), FxCurrency = baseCurrency, Symbol = element.Symbol, SecurityId = element.SecurityId, Quantity = Convert.ToDouble(element.Quantity), FxRate = tradePrice, StartPrice = 0, EndPrice = 0, Value = env.SignedValue(accountBuy, accountSell, true, sellValue), CreditDebit = env.DebitOrCredit(accountBuy, element.Quantity), }; var credit = new Journal(accountSell, Event.SETTLED_CASH, env.ValueDate) { Source = element.LpOrderId, Fund = env.GetFund(element), FxCurrency = riskCurrency, Symbol = element.Symbol, SecurityId = element.SecurityId, Quantity = Convert.ToDouble(element.Quantity), FxRate = tradePrice, StartPrice = 0, EndPrice = 0, Value = env.SignedValue(accountBuy, accountSell, true, buyValue * -1), CreditDebit = env.DebitOrCredit(accountSell, element.Quantity), }; env.Journals.AddRange(new[] { credit, debit }); var originalAccount = AccountUtils.GetDerivativeAccountType(realizedPnl); // Realized Pnl to go along with the Settled Cash CommonRules.GenerateJournalEntry(env, element, listOfTags, realizedAccountType, Event.REALIZED_PNL, realizedPnl); CommonRules.GenerateJournalEntries(env, element, listOfTags, originalAccount, "Change in Unrealized Derivatives Contracts at Fair Value", realizedPnl * -1); } if (taxlotStatus.Quantity != 0) { var buyTrade = env.FindTrade(taxlotStatus.OpenId); var taxlot = CommonRules.RelieveTaxLot(env, buyTrade, element, taxlotStatus.Quantity * -1, true); taxlotStatus.Quantity = 0; taxlotStatus.Status = "Closed"; } } }
internal static void GenerateTradeDateJournals(PostingEngineEnvironment env, Transaction element) { double multiplier = 1.0; if (env.SecurityDetails.ContainsKey(element.BloombergCode)) { multiplier = env.SecurityDetails[element.BloombergCode].Multiplier; } double fxrate = 1.0; // Lets get fx rate if needed if (!element.SettleCurrency.Equals(env.BaseCurrency)) { fxrate = Convert.ToDouble(FxRates.Find(env.ValueDate, element.SettleCurrency).Rate); } var tradeAllocations = env.Allocations.Where(i => i.LpOrderId == element.LpOrderId).ToList(); // Retrieve Allocation Objects for this trade if (tradeAllocations.Count() > 2) { env.AddMessage($"#of allocations > 2 please investigate {element.LpOrderId}"); return; } if (tradeAllocations.Count() == 2) { var debitEntry = tradeAllocations[0].Side == element.Side ? tradeAllocations[0] : tradeAllocations[1]; if (debitEntry.Symbol.Equals("@CASHUSD")) { env.AddMessage($"Unexpected Cash allocation please investigate {element.LpOrderId}"); return; } } var accountToFrom = GetFromToAccount(env, element); if (accountToFrom.To == null || accountToFrom.From == null) { env.AddMessage($"Unable to identify From/To accounts for trade {element.OrderSource} :: {element.Side}"); return; } if (element.NetMoney != 0.0) { var moneyUSD = Math.Abs(element.NetMoney) * fxrate; // BUY -- Debit // SELL -- Credit if (element.IsSell() || element.IsCover()) { moneyUSD = moneyUSD * -1; } var eodPrice = MarketPrices.GetPrice(env, env.ValueDate, element).Price; var fromJournal = new Journal(element, accountToFrom.From, Event.TRADE_DATE, env.ValueDate) { CreditDebit = env.DebitOrCredit(accountToFrom.From, moneyUSD), Value = env.SignedValue(accountToFrom.From, accountToFrom.To, true, moneyUSD), FxRate = fxrate, StartPrice = element.SettleNetPrice, EndPrice = eodPrice, Fund = env.GetFund(element), }; var toJournal = new Journal(element, accountToFrom.To, Event.TRADE_DATE, env.ValueDate) { FxRate = fxrate, CreditDebit = env.DebitOrCredit(accountToFrom.To, moneyUSD * -1), Value = env.SignedValue(accountToFrom.From, accountToFrom.To, false, moneyUSD), StartPrice = element.SettleNetPrice, EndPrice = eodPrice, Fund = env.GetFund(element), }; env.Journals.AddRange(new[] { fromJournal, toJournal }); } }
internal static void GenerateCloseOutPostings(PostingEngineEnvironment env, TaxLotDetail lot, TaxLot taxlot, Transaction element, TaxLotStatus taxlotStatus, string fund) { double multiplier = 1.0; if (env.SecurityDetails.ContainsKey(element.BloombergCode)) { multiplier = env.SecurityDetails[element.BloombergCode].Multiplier; } double fxrate = 1.0; // Lets get fx rate if needed if (!element.SettleCurrency.Equals(env.BaseCurrency)) { fxrate = Convert.ToDouble(FxRates.Find(env.ValueDate, element.SettleCurrency).Rate); } var prevPrice = MarketPrices.GetPrice(env, env.PreviousValueDate, lot.Trade).Price; var unrealizedPnl = Math.Abs(taxlotStatus.Quantity) * (element.SettleNetPrice - prevPrice) * multiplier; unrealizedPnl = Math.Abs(unrealizedPnl) * CommonRules.DetermineSign(taxlotStatus.Trade); var buyTrade = env.FindTrade(lot.Trade.LpOrderId); ReverseUnrealizedPnl( env, buyTrade, element, unrealizedPnl, MarketPrices.GetPrice(env, env.PreviousValueDate, lot.Trade).Price, element.SettleNetPrice, fxrate); var PnL = taxlot.RealizedPnl; PostRealizedPnl( env, buyTrade, PnL, taxlot.TradePrice, taxlot.CostBasis, fxrate); var listOfFromTags = new List <Tag> { Tag.Find("SecurityType"), Tag.Find("CustodianCode") }; Account fromAccount = null; Account toAccount = null; if (element.IsDerivative()) { return; } else { var accountType = (buyTrade.IsShort() || buyTrade.IsCover()) ? "SHORT POSITIONS AT COST" : "LONG POSITIONS AT COST"; var markToMarketAccount = (buyTrade.IsShort() || buyTrade.IsCover()) ? "Mark to Market Shorts" : "Mark to Market Longs"; fromAccount = new AccountUtils().CreateAccount(AccountType.Find(accountType), listOfFromTags, element); toAccount = new AccountUtils().CreateAccount(AccountType.Find(markToMarketAccount), listOfFromTags, element); } new AccountUtils().SaveAccountDetails(env, fromAccount); new AccountUtils().SaveAccountDetails(env, toAccount); // Now Generate Entries var fromJournal = new Journal(element) { Account = fromAccount, CreditDebit = env.DebitOrCredit(fromAccount, PnL), When = env.ValueDate, StartPrice = taxlot.TradePrice, EndPrice = taxlot.CostBasis, Value = PnL, FxRate = 1, Event = Event.REALIZED_PNL, Fund = env.GetFund(element), }; var toJournal = new Journal(fromJournal) { Account = toAccount, CreditDebit = env.DebitOrCredit(toAccount, PnL * -1), Value = PnL * -1, }; env.Journals.AddRange(new[] { fromJournal, toJournal }); }