public void TradeDateEvent(PostingEngineEnvironment env, Transaction element) { double fxrate = 1.0; double multiplier = 1.0; if (env.SecurityDetails.ContainsKey(element.BloombergCode)) { multiplier = env.SecurityDetails[element.BloombergCode].Multiplier; } // 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(); if (element.IsBuy() || element.IsShort()) { var t1 = env.GenerateOpenTaxLot(element, fxrate); if (element.Quantity == 0) { // TODO: Need to review this as we need to see if there is a parent, and what the parents actuall is return; } //tl.Save(env.Connection, env.Transaction); } else if (element.IsSell() || element.IsCover()) { // Get Matching Lots var openLots = env.Methodology.GetOpenLots(env, element, element.Quantity); if (openLots.Count() == 0) { var t1 = env.GenerateOpenTaxLot(element, fxrate); // Whats going on here? // We are skipping anything that does not get an OpenLot env.AddMessage($"There should be for a sell {element.Symbol} have at least one open lot, non found"); } else { var workingQuantity = element.Quantity; foreach (var lot in openLots) { if (workingQuantity == 0) { break; } if (!env.TaxLotStatus.ContainsKey(lot.Trade.LpOrderId)) { // TODO: For this open lot there should be a corresponding open to continue; } var taxlotStatus = env.TaxLotStatus[lot.Trade.LpOrderId]; if (taxlotStatus != null && taxlotStatus.Quantity != 0 && !taxlotStatus.Status.ToLowerInvariant().Equals("closed")) { // Does the open Lot fully fullfill the quantity ? if (Math.Abs(taxlotStatus.Quantity) >= Math.Abs(workingQuantity)) { var taxlot = CommonRules.RelieveTaxLot(env, lot, element, workingQuantity, true); taxlotStatus.Quantity += workingQuantity; if (taxlotStatus.Quantity == 0) { taxlotStatus.Status = "Closed"; } else { taxlotStatus.Status = "Partially Closed"; } CommonRules.GenerateCloseOutPostings(env, lot, taxlot, element, taxlotStatus, tradeAllocations[0].Fund); break; } else { var taxlot = CommonRules.RelieveTaxLot(env, lot, element, taxlotStatus.Quantity * -1); workingQuantity -= Math.Abs(taxlotStatus.Quantity); CommonRules.PostRealizedPnl(env, element, taxlot.RealizedPnl, taxlot.TradePrice, taxlot.CostBasis); taxlotStatus.Quantity = 0; taxlotStatus.Status = "Closed"; } } } } } else { // We have a Debit / Credit Dividends } }
/// <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 }); */ }
public void TradeDateEvent(PostingEngineEnvironment env, Transaction element) { double fxrate = 1.0; double multiplier = 1.0; if (env.SecurityDetails.ContainsKey(element.BloombergCode)) { multiplier = env.SecurityDetails[element.BloombergCode].Multiplier; } // Lets get fx rate if needed if (!element.SettleCurrency.Equals(env.BaseCurrency)) { fxrate = Convert.ToDouble(FxRates.Find(env.ValueDate, element.SettleCurrency).Rate); } if (element.IsBuy() || element.IsShort()) { var t1 = env.GenerateOpenTaxLot(element, fxrate); if (element.Quantity == 0) { // TODO: Need to review this as we need to see if there is a parent, and what the parents actuall is return; } } else if (element.IsSell() || element.IsCover()) { // Get Matching Lots var openLots = env.Methodology.GetOpenLots(env, element, element.Quantity); if (openLots.Count() == 0) { var t1 = env.GenerateOpenTaxLot(element, fxrate); // Whats going on here? // We are skipping anything that does not get an OpenLot env.AddMessage($"There should be for a sell {element.Symbol} have at least one open lot, non found"); } else { var workingQuantity = element.Quantity; foreach (var lot in openLots) { if (workingQuantity == 0) { break; } if (!env.TaxLotStatus.ContainsKey(lot.Trade.LpOrderId)) { // TODO: For this open lot there should be a corresponding open to continue; } var taxlotStatus = env.TaxLotStatus[lot.Trade.LpOrderId]; if (taxlotStatus != null && taxlotStatus.Quantity != 0 && !taxlotStatus.Status.ToLowerInvariant().Equals("closed")) { // Does the open Lot fully fullfill the quantity ? if (Math.Abs(taxlotStatus.Quantity) >= Math.Abs(workingQuantity)) { var taxlot = CommonRules.RelieveTaxLot(env, lot, element, workingQuantity, true); taxlotStatus.Quantity += workingQuantity; if (taxlotStatus.Quantity == 0) { taxlotStatus.Status = "Closed"; } else { taxlotStatus.Status = "Partially Closed"; } CommonRules.GenerateCloseOutPostings(env, lot, taxlot, element, taxlotStatus, env.GetFund(element)); break; } else { var taxlot = CommonRules.RelieveTaxLot(env, lot, element, taxlotStatus.Quantity * -1); workingQuantity -= Math.Abs(taxlotStatus.Quantity); var accountTypes = AccountType.All; var listOfTags = new List <Tag> { Tag.Find("SecurityType"), Tag.Find("CustodianCode") }; Account fromAccount = null; // Debiting Account Account toAccount = null; // Crediting Account var realizedPnl = taxlot.RealizedPnl; var originalAccount = AccountUtils.GetDerivativeAccountType(realizedPnl); if (originalAccount.Contains("(Liabilities)")) { // This needs to be registered as a Credit to the Libabilities realizedPnl *= -1; } var fromToAccounts = new AccountUtils().GetAccounts(env, originalAccount, "REALIZED GAIN/(LOSS)", listOfTags, taxlot.Trade); fromAccount = fromToAccounts.From; toAccount = fromToAccounts.To; var debitJournal = new Journal(element) { Account = fromAccount, When = env.ValueDate, StartPrice = taxlot.TradePrice, EndPrice = taxlot.CostBasis, CreditDebit = env.DebitOrCredit(fromAccount, taxlot.RealizedPnl), Value = env.SignedValue(fromAccount, toAccount, true, taxlot.RealizedPnl), FxRate = fxrate, Event = Event.REALIZED_PNL, Fund = env.GetFund(element), }; var creditJournal = new Journal(debitJournal) { Account = toAccount, CreditDebit = env.DebitOrCredit(toAccount, taxlot.RealizedPnl * -1), Value = env.SignedValue(fromAccount, toAccount, false, taxlot.RealizedPnl), }; env.Journals.AddRange(new[] { debitJournal, creditJournal }); taxlotStatus.Quantity = 0; taxlotStatus.Status = "Closed"; } } } } } else { // We have a Debit / Credit Dividends } }
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"; } } }
public void SettlementDateEvent(PostingEngineEnvironment env, Transaction element) { if (env.TaxLotStatus.ContainsKey(element.LpOrderId)) { var taxlot = env.TaxLotStatus[element.LpOrderId]; var tradeCurrency = element.TradeCurrency; var settleCurrency = element.SettleCurrency; var allocations = env.FindTradeAllocations(element); var buy = allocations.Where(i => i.SecurityType.Equals("SPOT") && i.Side.Equals("BUY")).FirstOrDefault(); var sell = allocations.Where(i => i.SecurityType.Equals("SPOT") && i.Side.Equals("SELL")).FirstOrDefault(); if (buy == null || sell == null) { Logger.Error($"Unable to process {element.SecurityType}::{element.LpOrderId}"); return; } var accountSell = new AccountUtils().CreateAccount(atSettledCash, listOfTradeTags, sell); var accountBuy = new AccountUtils().CreateAccount(atSettledCash, listOfTradeTags, buy); new AccountUtils().SaveAccountDetails(env, accountSell); new AccountUtils().SaveAccountDetails(env, accountBuy); var sellFx = FxRates.Find(env.ValueDate, sell.TradeCurrency); var buyFx = FxRates.Find(env.ValueDate, buy.TradeCurrency); var sellValue = sell.Quantity * sellFx.Rate; var buyValue = buy.Quantity * buyFx.Rate; if (element.IsBuy()) // BUY { var realizedPnl = buyValue + sellValue; var debit = new Journal(accountBuy, Event.SETTLED_CASH, env.ValueDate) { Source = element.LpOrderId, Fund = env.GetFund(element), FxCurrency = element.TradeCurrency, Symbol = element.Symbol, SecurityId = element.SecurityId, Quantity = Convert.ToDouble(buy.Quantity), FxRate = buyFx.Rate, StartPrice = 0, EndPrice = 0, Value = env.SignedValue(accountBuy, accountSell, true, buyValue), CreditDebit = env.DebitOrCredit(accountBuy, buy.Quantity), }; var credit = new Journal(accountSell, Event.SETTLED_CASH, env.ValueDate) { Source = element.LpOrderId, Fund = env.GetFund(element), FxCurrency = element.SettleCurrency, Symbol = element.Symbol, SecurityId = element.SecurityId, Quantity = Convert.ToDouble(sell.Quantity), FxRate = sellFx.Rate, StartPrice = 0, EndPrice = 0, Value = env.SignedValue(accountBuy, accountSell, true, sellValue), CreditDebit = env.DebitOrCredit(accountSell, sell.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); } else // SELL { var realizedPnl = buyValue + sellValue; var debit = new Journal(accountBuy, Event.SETTLED_CASH, env.ValueDate) { Source = element.LpOrderId, Fund = env.GetFund(element), FxCurrency = element.TradeCurrency, Symbol = element.Symbol, SecurityId = element.SecurityId, Quantity = Convert.ToDouble(buy.Quantity), FxRate = buyFx.Rate, StartPrice = 0, EndPrice = 0, Value = env.SignedValue(accountBuy, accountSell, true, buy.Quantity * buyFx.Rate), CreditDebit = env.DebitOrCredit(accountBuy, buy.Quantity), }; var credit = new Journal(accountSell, Event.SETTLED_CASH, env.ValueDate) { Source = element.LpOrderId, Fund = env.GetFund(element), FxCurrency = element.SettleCurrency, Symbol = element.Symbol, SecurityId = element.SecurityId, Quantity = Convert.ToDouble(sell.Quantity), FxRate = sellFx.Rate, StartPrice = 0, EndPrice = 0, Value = env.SignedValue(accountBuy, accountSell, true, sell.Quantity * sellFx.Rate), CreditDebit = env.DebitOrCredit(accountSell, sell.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 (taxlot.Quantity != 0) { var buyTrade = env.FindTrade(taxlot.OpenId); CommonRules.RelieveTaxLot(env, buyTrade, element, taxlot.Quantity * -1, true); taxlot.Quantity = 0; taxlot.Status = "Closed"; //Now we have Realized Pnl } } }