public static MarketPrice GetPrice(PostingEngineEnvironment env, DateTime valueDate, Transaction element) { var eodMarketPrice = Find(valueDate, element); if (!eodMarketPrice.Valid) { env.AddMessage(eodMarketPrice.Error); } return(eodMarketPrice); }
/// <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); }
public bool IsValid(PostingEngineEnvironment env, Transaction element) { if (element.AccrualId == null) { return(true); } var validAccrual = env.IsValidAccrual(element.AccrualId); if (!validAccrual) { env.AddMessage($"trade does not tie back to a valid accrual {element.AccrualId}"); } return(validAccrual); }
private void AccrualPayment(PostingEngineEnvironment env, Transaction element, Accrual accrual) { var accountToFrom = GetFromToAccount(element); if (accountToFrom.To == null || accountToFrom.From == null) { env.AddMessage($"Unable to identify From/To accounts for trade {element.OrderSource} :: {element.Side}"); return; } new AccountUtils().SaveAccountDetails(env, accountToFrom.From); new AccountUtils().SaveAccountDetails(env, accountToFrom.To); double fxrate = 1.0; if (!element.SettleCurrency.Equals(env.BaseCurrency)) { fxrate = Convert.ToDouble(FxRates.Find(env.ValueDate, element.SettleCurrency).Rate); } var moneyUSD = element.LocalNetNotional * fxrate; var debit = new Journal(element) { Account = accountToFrom.From, When = env.ValueDate, FxRate = fxrate, CreditDebit = env.DebitOrCredit(accountToFrom.From, moneyUSD), Value = env.SignedValue(accountToFrom.From, accountToFrom.To, true, moneyUSD), Event = "prepaid-expense", Fund = env.GetFund(element), }; var credit = new Journal(element) { Account = accountToFrom.To, When = env.ValueDate, FxRate = fxrate, CreditDebit = env.DebitOrCredit(accountToFrom.To, moneyUSD), Value = env.SignedValue(accountToFrom.From, accountToFrom.To, false, moneyUSD), Event = "prepaid-expense", Fund = env.GetFund(element), }; env.Journals.Add(debit); env.Journals.Add(credit); }
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 } }
public void TradeDateEvent(PostingEngineEnvironment env, Transaction element) { var accrual = env.FindAccruals(element.AccrualId); var allocations = env.FindAllocations(element.AccrualId); var allocation = allocations.Where(i => !i.Symbol.Equals(element.Symbol)).FirstOrDefault(); //Console.WriteLine(element.Symbol); if (element.Symbol.Equals("ZZ_INVESTOR_CONTRIBUTIONS")) { } if (element.Status.Equals("Cancelled")) { env.AddMessage($"Entry has been cancelled {element.LpOrderId} :: {element.Side}"); return; } // Need to consider both if (element.TradeDate.Date != element.SettleDate.Date) { env.AddMessage($"Journal needs to be checked {element.LpOrderId}, {element.TradeDate}, {element.SettleDate}"); return; } var accountToFrom = GetFromToAccount(element); if (accountToFrom.To == null) { env.AddMessage($"Unable to identify From/To accounts for trade {element.OrderSource} :: {element.Side}"); return; } new AccountUtils().SaveAccountDetails(env, accountToFrom.From); new AccountUtils().SaveAccountDetails(env, accountToFrom.To); double fxrate = 1.0; if (!element.SettleCurrency.Equals(env.BaseCurrency)) { fxrate = Convert.ToDouble(FxRates.Find(env.ValueDate, element.SettleCurrency).Rate); } var moneyUSD = element.LocalNetNotional * fxrate; if (element.LocalNetNotional != 0.0) { var symbol = allocation != null ? allocation.Symbol : element.ParentSymbol; var securityId = allocation != null ? allocation.SecurityId : element.SecurityId; if (symbol == null) { symbol = element.Symbol; } var debit = new Journal(element) { Symbol = symbol, SecurityId = securityId, Account = accountToFrom.From, When = env.ValueDate, FxRate = fxrate, CreditDebit = env.DebitOrCredit(accountToFrom.From, moneyUSD), Value = moneyUSD, Event = Event.JOURNAL, Fund = env.GetFund(element), }; var credit = new Journal(element) { Symbol = symbol, SecurityId = securityId, Account = accountToFrom.To, When = env.ValueDate, FxRate = fxrate, CreditDebit = env.DebitOrCredit(accountToFrom.To, moneyUSD), Value = moneyUSD, Event = Event.JOURNAL, Fund = env.GetFund(element), }; env.Journals.Add(debit); env.Journals.Add(credit); } return; }
public void SettlementDateEvent(PostingEngineEnvironment env, Transaction element) { var accrual = env.FindAccruals(element.AccrualId); var allocations = env.FindAllocations(element.AccrualId); var allocation = allocations.Where(i => !i.Symbol.Equals(element.Symbol)).FirstOrDefault(); if (element.Status.Equals("Cancelled")) { env.AddMessage($"Entry has been cancelled {element.LpOrderId} :: {element.Side}"); return; } // If they are the same we need to do nothing if (element.TradeDate.Date == element.SettleDate.Date) { //env.AddMessage($"Journal needs to be checked {element.LpOrderId}, {element.TradeDate}, {element.SettleDate}"); return; } var accountToFrom = GetSettlementFromToAccount(element); if (accountToFrom.To == null) { env.AddMessage($"Unable to identify From/To accounts for trade {element.OrderSource} :: {element.Side}"); return; } new AccountUtils().SaveAccountDetails(env, accountToFrom.From); new AccountUtils().SaveAccountDetails(env, accountToFrom.To); double fxrate = 1.0; if (!element.SettleCurrency.Equals(env.BaseCurrency)) { fxrate = Convert.ToDouble(FxRates.Find(env.ValueDate, element.SettleCurrency).Rate); } var moneyUSD = element.LocalNetNotional * fxrate; if (element.LocalNetNotional != 0.0) { var symbol = allocation != null ? allocation.Symbol : element.ParentSymbol; var securityId = allocation != null ? allocation.SecurityId : element.SecurityId; if (symbol == null) { symbol = element.Symbol; } var debit = new Journal(accountToFrom.From, "journal", env.ValueDate) { Symbol = symbol, SecurityId = allocation != null ? allocation.SecurityId : element.SecurityId, Source = element.LpOrderId, Quantity = element.Quantity, FxCurrency = element.SettleCurrency, FxRate = fxrate, CreditDebit = env.DebitOrCredit(accountToFrom.From, moneyUSD), Value = moneyUSD, Fund = env.GetFund(element), }; var credit = new Journal(accountToFrom.To, "journal", env.ValueDate) { Symbol = symbol, SecurityId = allocation != null ? allocation.SecurityId : element.SecurityId, Source = element.LpOrderId, Quantity = element.Quantity, FxCurrency = element.SettleCurrency, FxRate = fxrate, CreditDebit = env.DebitOrCredit(accountToFrom.To, moneyUSD * -1), Value = moneyUSD * -1, Fund = env.GetFund(element), }; env.Journals.Add(debit); env.Journals.Add(credit); } return; }
internal void TradeDateEvent(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); } if (element.IsCredit() || element.IsDebit()) { if (element.TransactionCategory == "Cash Dividends") { // We have a Cash Dividend, so how do we treat this return; } } if (element.IsBuy() || element.IsShort()) { 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; } var tl = env.GenerateOpenTaxLot(element, fxrate); } else if (element.IsSell() || element.IsCover()) { // Get Matching Lots var taxLotMethodology = ConfigurationManager.AppSettings["TaxMethod"].ToString(); var workingQuantity = element.Quantity; if (taxLotMethodology.Equals("MINTAX")) { while (workingQuantity != 0.0) { var localOpenTaxLots = env.Methodology.GetOpenLots(env, element, workingQuantity); var lot = localOpenTaxLots[0]; if (!env.TaxLotStatus.ContainsKey(lot.Trade.LpOrderId)) { // TODO: For this open lot there should be a corresponding open to env.AddMessage($"Unable to Find Tax Lot for {lot.Trade.Symbol}::{lot.Trade.Side}::{lot.Trade.Status}"); //Logger.Warn($"Unable to Find Tax Lot for {element.Symbol}::{element.Side}::{element.Status}"); continue; } var taxlotStatus = env.TaxLotStatus[lot.Trade.LpOrderId]; if (taxlotStatus != null && taxlotStatus.Quantity != 0 && !taxlotStatus.Status.ToLowerInvariant().Equals("closed")) { Logger.Info($"Relieving Tax Lot {taxlotStatus.Symbol}::{taxlotStatus.TradePrice}::{taxlotStatus.TradeDate.ToString("MM-dd-yyyy")}::{taxlotStatus.OpenId}::{lot.TaxLiability}::{lot.TaxRate.Rate}::{lot.PotentialPnl}"); // Does the open Lot fully fullfill the quantity ? if (Math.Abs(taxlotStatus.Quantity) >= Math.Abs(workingQuantity)) { // Lets generate all the journal entries we need GenerateJournals(env, lot, taxlotStatus, element, workingQuantity, fxrate, multiplier); break; } else { var quantity = taxlotStatus.Quantity; GenerateJournals(env, lot, taxlotStatus, element, taxlotStatus.Quantity * -1, fxrate, multiplier); workingQuantity += quantity; } } } } else { var openLots = env.Methodology.GetOpenLots(env, element, workingQuantity); 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 env.AddMessage($"Unable to Find Tax Lot for {lot.Trade.Symbol}::{lot.Trade.Side}::{lot.Trade.Status}"); //Logger.Warn($"Unable to Find Tax Lot for {element.Symbol}::{element.Side}::{element.Status}"); continue; } var taxlotStatus = env.TaxLotStatus[lot.Trade.LpOrderId]; if (taxlotStatus != null && taxlotStatus.Quantity != 0 && !taxlotStatus.Status.ToLowerInvariant().Equals("closed")) { Logger.Info($"Relieving Tax Lot {taxlotStatus.TradeDate.ToString("MM-dd-yyyy")}::{taxlotStatus.Symbol}::{taxlotStatus.OpenId}::{lot.TaxLiability}::{lot.TaxRate.Rate}::{lot.PotentialPnl}"); // Does the open Lot fully fullfill the quantity ? if (Math.Abs(taxlotStatus.Quantity) >= Math.Abs(workingQuantity)) { // Lets generate all the journal entries we need GenerateJournals(env, lot, taxlotStatus, element, workingQuantity, fxrate, multiplier); break; } else { var quantity = taxlotStatus.Quantity; GenerateJournals(env, lot, taxlotStatus, element, taxlotStatus.Quantity * -1, fxrate, multiplier); workingQuantity += quantity; } } } } } else { // We have a Debit / Credit Dividends } CommonRules.GenerateTradeDateJournals(env, element); }
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 } }
internal static void GenerateSettlementDateJournals(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); } var accountToFrom = new AccountingRules().GetFromToAccountOnSettlement(env, element); if (accountToFrom.To == null || accountToFrom.From == null) { env.AddMessage($"Unable to identify From/To accounts for trade {element.OrderSource} :: {element.Side}"); return; } // This is the fully loaded value to tbe posting if (element.NetMoney != 0.0) { var moneyUSD = Math.Abs(element.NetMoney) * fxrate; // BUY -- Debit // SELL -- Credit if (element.IsShort() || element.IsSell()) { moneyUSD = moneyUSD * -1; } var debit = new Journal { Source = element.LpOrderId, Account = accountToFrom.From, When = env.ValueDate, FxCurrency = element.SettleCurrency, Symbol = element.Symbol, SecurityId = element.SecurityId, Quantity = element.Quantity, FxRate = fxrate, CreditDebit = env.DebitOrCredit(accountToFrom.From, moneyUSD), Value = env.SignedValue(accountToFrom.From, accountToFrom.To, true, moneyUSD), Event = Event.SETTLEMENT, Fund = env.GetFund(element) }; var credit = new Journal { Source = element.LpOrderId, FxCurrency = element.SettleCurrency, Symbol = element.Symbol, SecurityId = element.SecurityId, Quantity = element.Quantity, FxRate = fxrate, When = env.ValueDate, Account = accountToFrom.To, CreditDebit = env.DebitOrCredit(accountToFrom.To, moneyUSD * -1), Value = env.SignedValue(accountToFrom.From, accountToFrom.To, false, moneyUSD), Event = Event.SETTLEMENT, Fund = env.GetFund(element) }; env.Journals.AddRange(new[] { debit, credit }); } }
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 }); } }