internal static void ReverseUnrealizedPnl(PostingEngineEnvironment env, Transaction openTaxLot, Transaction trade, double unrealizedPnl, double start, double end, double fxrate) { var accountToFrom = UnRealizedPnlPostingAccounts(openTaxLot, unrealizedPnl); new AccountUtils().SaveAccountDetails(env, accountToFrom.From); new AccountUtils().SaveAccountDetails(env, accountToFrom.To); var fromJournal = new Journal(openTaxLot) { When = env.ValueDate, Event = Event.REVERSE_UNREALIZED_PNL, FxRate = fxrate, Fund = env.GetFund(openTaxLot), StartPrice = start, EndPrice = end, Quantity = trade.Quantity, Account = accountToFrom.To, CreditDebit = env.DebitOrCredit(accountToFrom.To, unrealizedPnl), Value = env.SignedValue(accountToFrom.To, accountToFrom.From, true, unrealizedPnl), }; var toJournal = new Journal(fromJournal) { Account = accountToFrom.From, CreditDebit = env.DebitOrCredit(accountToFrom.From, unrealizedPnl), Value = env.SignedValue(accountToFrom.To, accountToFrom.From, false, unrealizedPnl), }; env.Journals.AddRange(new[] { fromJournal, toJournal }); }
/// <summary> /// Contra Journal Entries /// </summary> /// <param name="env"></param> /// <param name="element"></param> /// <param name="tags"></param> /// <param name="from"></param> /// <param name="to"></param> /// <param name="value"></param> internal static void GenerateJournalEntries(PostingEngineEnvironment env, Transaction element, List <Tag> tags, string from, string to, double value) { var fromAccount = new AccountUtils().CreateAccount(AccountType.Find(from), tags, element); var toAccount = new AccountUtils().CreateAccount(AccountType.Find(to), tags, element); new AccountUtils().SaveAccountDetails(env, fromAccount); new AccountUtils().SaveAccountDetails(env, toAccount); var debitJournal = new Journal(element) { Account = fromAccount, When = env.ValueDate, StartPrice = 0, EndPrice = 0, CreditDebit = env.DebitOrCredit(fromAccount, value), Value = env.SignedValue(fromAccount, toAccount, true, value), FxRate = element.TradePrice, Event = Event.REALIZED_PNL, Fund = env.GetFund(element), }; var creditJournal = new Journal(debitJournal) { Account = toAccount, CreditDebit = env.DebitOrCredit(toAccount, value), Value = env.SignedValue(fromAccount, toAccount, false, value), }; env.Journals.AddRange(new[] { debitJournal, creditJournal }); }
private IEnumerable <Journal> ReverseCredit(PostingEngineEnvironment env, DateTime valueDate, dynamic yearEndResult, string fromAccount, string toAccount, string accountName, double balance) { var debitAccount = new AccountUtils().CreateAccount(fromAccount, accountName); var creditAccount = new AccountUtils().CreateAccount(toAccount, accountName); new AccountUtils().SaveAccountDetails(env, debitAccount); new AccountUtils().SaveAccountDetails(env, creditAccount); if (yearEndResult.AccountCategory.Equals("Expenses")) { balance *= -1; } var debitJournal = new Journal(debitAccount, Event.YEAR_END, valueDate) { Source = "year-closeout", Fund = yearEndResult.Fund, Quantity = balance, FxCurrency = env.BaseCurrency, Symbol = env.BaseCurrency, SecurityId = -1, FxRate = 0, StartPrice = 0, EndPrice = 0, Value = balance * -1, CreditDebit = env.DebitOrCredit(debitAccount, balance), }; var creditJournal = new Journal(creditAccount, Event.YEAR_END, valueDate) { Source = "year-closeout", Fund = yearEndResult.Fund, Quantity = balance, FxCurrency = env.BaseCurrency, Symbol = env.BaseCurrency, SecurityId = -1, FxRate = 0, StartPrice = 0, EndPrice = 0, Value = balance, CreditDebit = env.DebitOrCredit(creditAccount, balance), }; return(new Journal[] { creditJournal, debitJournal }); }
private void PostUnrealizedFxGain(PostingEngineEnvironment env, Transaction element, double realizedFxPnl, double start, double end, double fxrate) { var m2mtranslation = "Mark to Market longs fx translation gain or loss"; if (element.IsShort()) { m2mtranslation = "Mark to Market shorts fx translation gain or loss"; } var fromTo = new AccountUtils().GetAccounts(env, m2mtranslation, "change in unrealized do to fx translation", new string[] { element.SettleCurrency }.ToList()); var debit = new Journal(fromTo.From, Event.UNREALIZED_FX_TRANSLATION, env.ValueDate) { Source = element.LpOrderId, Fund = env.GetFund(element), FxCurrency = element.SettleCurrency, Symbol = element.Symbol, SecurityId = element.SecurityId, Quantity = Convert.ToDouble(element.Quantity), FxRate = fxrate, StartPrice = start, EndPrice = end, Value = env.SignedValue(fromTo.From, fromTo.To, true, realizedFxPnl * -1), CreditDebit = env.DebitOrCredit(fromTo.From, realizedFxPnl), }; var credit = new Journal(fromTo.To, Event.UNREALIZED_FX_TRANSLATION, env.ValueDate) { Source = element.LpOrderId, Fund = env.GetFund(element), FxCurrency = element.SettleCurrency, Symbol = element.Symbol, SecurityId = element.SecurityId, Quantity = Convert.ToDouble(element.Quantity), FxRate = fxrate, StartPrice = start, EndPrice = end, Value = env.SignedValue(fromTo.From, fromTo.To, false, realizedFxPnl * -1), CreditDebit = env.DebitOrCredit(fromTo.To, realizedFxPnl), }; env.Journals.AddRange(new List <Journal>(new[] { debit, credit })); }
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); }
internal static void PostRealizedPnl(PostingEngineEnvironment env, Transaction element, double pnL, double start, double end, double fxrate = 1.0) { var accountToFrom = RealizedPnlPostingAccounts(element, pnL); new AccountUtils().SaveAccountDetails(env, accountToFrom.From); new AccountUtils().SaveAccountDetails(env, accountToFrom.To); if (element.IsDerivative()) { // Need to Credit the From and Debit the to pnL *= -1; } else { if (element.IsShort() || element.IsCover()) { pnL *= -1; } } var debitJournal = new Journal(element) { Account = accountToFrom.From, When = env.ValueDate, StartPrice = start, EndPrice = end, CreditDebit = env.DebitOrCredit(accountToFrom.From, pnL), Value = env.SignedValue(accountToFrom.From, accountToFrom.To, true, pnL), FxRate = fxrate, Event = Event.REALIZED_PNL, Fund = env.GetFund(element), }; var creditJournal = new Journal(element, accountToFrom.To, Event.REALIZED_PNL, env.ValueDate) { StartPrice = start, EndPrice = end, FxRate = fxrate, CreditDebit = env.DebitOrCredit(accountToFrom.To, pnL * -1), Value = env.SignedValue(accountToFrom.From, accountToFrom.To, false, pnL), Fund = env.GetFund(element), }; env.Journals.AddRange(new[] { debitJournal, creditJournal }); }
private void PostRealizedFxGain(PostingEngineEnvironment env, Transaction element, double realizedFxPnl, double start, double end, double fxrate) { var accountType = (element.IsShort() || element.IsCover()) ? "SHORT POSITIONS AT COST" : "LONG POSITIONS AT COST"; var fromTo = new AccountUtils().GetAccounts(env, accountType, "REALIZED GAIN/(LOSS) DUE TO FX", new string[] { element.SettleCurrency }.ToList()); var debit = new Journal(fromTo.From, "realized-cash-fx", env.ValueDate) { Source = element.LpOrderId, Fund = env.GetFund(element), FxCurrency = element.SettleCurrency, Symbol = element.Symbol, SecurityId = element.SecurityId, Quantity = Convert.ToDouble(element.Quantity), FxRate = fxrate, StartPrice = start, EndPrice = end, Value = env.SignedValue(fromTo.From, fromTo.To, true, realizedFxPnl), CreditDebit = env.DebitOrCredit(fromTo.From, realizedFxPnl), }; var credit = new Journal(fromTo.To, "realized-cash-fx", env.ValueDate) { Source = element.LpOrderId, Fund = env.GetFund(element), FxCurrency = element.SettleCurrency, Symbol = element.Symbol, SecurityId = element.SecurityId, Quantity = Convert.ToDouble(element.Quantity), FxRate = fxrate, StartPrice = start, EndPrice = end, Value = env.SignedValue(fromTo.From, fromTo.To, false, realizedFxPnl), CreditDebit = env.DebitOrCredit(fromTo.To, realizedFxPnl), }; env.Journals.AddRange(new List <Journal>(new[] { debit, credit })); }
private void ReversePosting(PostingEngineEnvironment env, string fromAccount, string toAccount, Transaction element, double reversalAmount) { var fromTo = new AccountUtils().GetAccounts(env, fromAccount, toAccount, new string[] { element.SettleCurrency }.ToList()); var debit = new Journal(fromTo.From, "reversal-unrealized-cash-fx", env.ValueDate) { Source = element.LpOrderId, Fund = env.GetFund(element), FxCurrency = element.SettleCurrency, Symbol = element.Symbol, SecurityId = element.SecurityId, Quantity = Convert.ToDouble(element.Quantity), FxRate = 0, StartPrice = 0, EndPrice = 0, Value = env.SignedValue(fromTo.From, fromTo.To, true, reversalAmount * -1), CreditDebit = env.DebitOrCredit(fromTo.From, reversalAmount), }; var credit = new Journal(fromTo.To, "reversal-unrealized-cash-fx", env.ValueDate) { Source = element.LpOrderId, Fund = env.GetFund(element), FxCurrency = element.SettleCurrency, Symbol = element.Symbol, SecurityId = element.SecurityId, Quantity = Convert.ToDouble(element.Quantity), FxRate = 0, StartPrice = 0, EndPrice = 0, Value = env.SignedValue(fromTo.From, fromTo.To, false, reversalAmount * -1), CreditDebit = env.DebitOrCredit(fromTo.To, reversalAmount), }; env.Journals.AddRange(new List <Journal>(new[] { debit, credit })); }
internal static void PostRealizedPnl(PostingEngineEnvironment env, Transaction element, double realizedPnl, string from, string to) { var listOfTags = new List <Tag> { Tag.Find("SecurityType"), Tag.Find("CustodianCode") }; var fromAccount = new AccountUtils().CreateAccount(AccountType.Find(from), listOfTags, element); var toAccount = new AccountUtils().CreateAccount(AccountType.Find(to), listOfTags, element); new AccountUtils().SaveAccountDetails(env, fromAccount); new AccountUtils().SaveAccountDetails(env, toAccount); var debitJournal = new Journal(element) { Account = fromAccount, When = env.ValueDate, StartPrice = 0, EndPrice = 0, CreditDebit = env.DebitOrCredit(fromAccount, realizedPnl), Value = env.SignedValue(fromAccount, toAccount, true, realizedPnl), FxRate = element.TradePrice, Event = Event.REALIZED_PNL, Fund = env.GetFund(element), }; var creditJournal = new Journal(element, toAccount, Event.REALIZED_PNL, env.ValueDate) { StartPrice = 0, EndPrice = 0, FxRate = element.TradePrice, CreditDebit = env.DebitOrCredit(toAccount, realizedPnl), Value = env.SignedValue(fromAccount, toAccount, false, realizedPnl), Fund = env.GetFund(element), }; env.Journals.AddRange(new[] { debitJournal, creditJournal }); }
/// <summary> /// Single Sided Entry Journal /// </summary> /// <param name="env"></param> /// <param name="element"></param> /// <param name="tags"></param> /// <param name="accountType"></param> /// <param name="value"></param> internal static void GenerateJournalEntry(PostingEngineEnvironment env, Transaction element, List <Tag> tags, AccountType accountType, string eventName, double value) { var account = new AccountUtils().CreateAccount(accountType, tags, element); new AccountUtils().SaveAccountDetails(env, account); var journal = new Journal(element) { Account = account, When = env.ValueDate, StartPrice = 0, EndPrice = 0, CreditDebit = env.DebitOrCredit(account, value), Value = value, FxRate = element.TradePrice, Event = eventName, Fund = env.GetFund(element), }; env.Journals.AddRange(new[] { journal }); }
internal double CreateFxUnsettled(PostingEngineEnvironment env, Transaction element) { var journals = new List <Journal>(); // TBD: Needs to be optimized, get all data upfront for a ValueDate and drop into the env //env.CallBack?.Invoke($"Create Fx Unsettled {element.SettleCurrency} -- {element.LpOrderId}"); var unsettledPnls = env.UnsettledPnl.Where(i => i.Source.Equals(element.LpOrderId)).ToList(); var fxChange = 0.0; if (unsettledPnls.Count() == 0) { return(0.0); } var prevRate = FxRates.Find(env.PreviousValueDate, unsettledPnls[0].Currency).Rate; var eodRate = FxRates.Find(env.ValueDate, unsettledPnls[0].Currency).Rate; unsettledPnls.ForEach(unsettledPnl => { var change = eodRate - prevRate; var fxCashCredit = change * (unsettledPnl.Credit / unsettledPnl.FxRate); var fxCashDebit = change * (unsettledPnl.Debit / unsettledPnl.FxRate); var fxCash = fxCashCredit - fxCashDebit; var from = ""; var to = ""; if (element.IsDerivative()) { from = fxCash > 0 ? "Mark to Market Derivatives Contracts due to FX Translation (Assets)" : "Mark to Market Derivatives Contracts due to FX Translation (Liabilities)"; if (from.Contains("(Liabilities)")) { fxCash *= -1; } to = "Change in Unrealized Derivatives Contracts due to FX Translation"; } else { var m2mtranslation = "Mark to Market longs fx translation gain or loss"; if (element.IsShort() || element.IsCover()) { m2mtranslation = "Mark to Market shorts fx translation gain or loss"; } from = m2mtranslation; to = "change in unrealized do to fx translation"; } // Get accounts var fromTo = new AccountUtils().GetAccounts(env, from, to, new string[] { unsettledPnl.Currency }.ToList()); var debit = new Journal(fromTo.From, Event.UNREALIZED_FX_TRANSLATION, env.ValueDate) { Source = unsettledPnl.Source, Fund = unsettledPnl.Fund, FxCurrency = unsettledPnl.Currency, Symbol = unsettledPnl.Symbol, SecurityId = unsettledPnl.SecurityId, Quantity = Convert.ToDouble(unsettledPnl.Quantity), FxRate = change, StartPrice = prevRate, EndPrice = eodRate, Value = env.SignedValue(fromTo.From, fromTo.To, true, fxCash), CreditDebit = env.DebitOrCredit(fromTo.From, fxCash), }; fxChange += debit.Value; var credit = new Journal(fromTo.To, Event.UNREALIZED_FX_TRANSLATION, env.ValueDate) { Source = unsettledPnl.Source, Fund = unsettledPnl.Fund, FxCurrency = unsettledPnl.Currency, Symbol = unsettledPnl.Symbol, SecurityId = unsettledPnl.SecurityId, Quantity = Convert.ToDouble(unsettledPnl.Quantity), FxRate = change, StartPrice = prevRate, EndPrice = eodRate, Value = env.SignedValue(fromTo.From, fromTo.To, false, fxCash), CreditDebit = env.DebitOrCredit(fromTo.To, fxCash), }; journals.AddRange(new List <Journal>(new[] { debit, credit })); }); //connection.Close(); env.Journals.AddRange(journals); return(fxChange); }
internal void CreateFxUnsettled(PostingEngineEnvironment env) { var sql = $@"select credit, debit, symbol, quantity, fx_currency, fund, source, fxrate, security_id, side from vwWorkingJournals where [event] = 'unrealizedpnl' and AccountType in ('CHANGE IN UNREALIZED GAIN/(LOSS)', 'Change in Unrealized Derivatives Contracts at Fair Value') and fx_currency != '{env.BaseCurrency}' and [when] < '{env.ValueDate.ToString("MM-dd-yyyy")}'"; env.CallBack?.Invoke("FX for Mark to Market Calculation Started"); var connection = new SqlConnection(env.ConnectionString); connection.Open(); var command = new SqlCommand(sql, connection); command.Transaction = env.Transaction; var reader = command.ExecuteReader(System.Data.CommandBehavior.SingleResult); while (reader.Read()) { var unsettledPnl = new { Credit = Convert.ToDouble(reader.GetFieldValue <decimal>(0)), Debit = Convert.ToDouble(reader.GetFieldValue <decimal>(1)), Symbol = reader.GetFieldValue <string>(2), Quantity = Convert.ToDouble(reader.GetFieldValue <decimal>(3)), Currency = reader.GetFieldValue <string>(4), Fund = reader.GetFieldValue <string>(5), Source = reader.GetFieldValue <string>(6), FxRate = Convert.ToDouble(reader.GetFieldValue <decimal>(7)), SecurityId = reader.GetFieldValue <int>(8), Side = reader.GetFieldValue <string>(9), }; if (unsettledPnl.Currency.Equals(env.BaseCurrency)) { continue; } var prevRate = FxRates.Find(env.PreviousValueDate, unsettledPnl.Currency).Rate; var eodRate = FxRates.Find(env.ValueDate, unsettledPnl.Currency).Rate; var change = eodRate - prevRate; var fxCashCredit = change * (unsettledPnl.Credit / unsettledPnl.FxRate); var fxCashDebit = change * (unsettledPnl.Debit / unsettledPnl.FxRate); var fxCash = fxCashCredit - fxCashDebit; var m2mtranslation = "Mark to Market longs fx translation gain or loss"; if (unsettledPnl.Side.ToLowerInvariant().Equals("short") || unsettledPnl.Side.ToLowerInvariant().Equals("cover")) { m2mtranslation = "Mark to Market shorts fx translation gain or loss"; } // Get accounts var fromTo = new AccountUtils().GetAccounts(env, m2mtranslation, "change in unrealized do to fx translation", new string[] { unsettledPnl.Currency }.ToList()); var debit = new Journal(fromTo.From, Event.UNREALIZED_FX_TRANSLATION, env.ValueDate) { Source = unsettledPnl.Source, Fund = unsettledPnl.Fund, FxCurrency = unsettledPnl.Currency, Symbol = unsettledPnl.Symbol, SecurityId = unsettledPnl.SecurityId, Quantity = Convert.ToDouble(unsettledPnl.Quantity), FxRate = change, StartPrice = prevRate, EndPrice = eodRate, Value = env.SignedValue(fromTo.From, fromTo.To, true, fxCash), CreditDebit = env.DebitOrCredit(fromTo.From, fxCash), }; var credit = new Journal(fromTo.To, Event.UNREALIZED_FX_TRANSLATION, env.ValueDate) { Source = unsettledPnl.Source, Fund = unsettledPnl.Fund, FxCurrency = unsettledPnl.Currency, Symbol = unsettledPnl.Symbol, SecurityId = unsettledPnl.SecurityId, Quantity = Convert.ToDouble(unsettledPnl.Quantity), FxRate = change, StartPrice = prevRate, EndPrice = eodRate, Value = env.SignedValue(fromTo.From, fromTo.To, false, fxCash), CreditDebit = env.DebitOrCredit(fromTo.To, fxCash), }; env.Journals.AddRange(new List <Journal>(new[] { debit, credit })); } connection.Close(); }
/// <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); */ } } } }
internal static List <Journal> CreateFx(PostingEngineEnvironment env, string tradeEvent, double quantity, TaxLotStatus taxlotStatus, Transaction element) { if (tradeEvent.Equals(Event.TRADE_DATE)) { return(new List <Journal>()); } var riskCurrency = element.SettleCurrency; if (element.SecurityType.Equals("FORWARD")) { var split = element.Symbol.Split(new char[] { '/', ' ' }); var baseCurrency = split[0]; riskCurrency = split[1]; } // Check to see if the BaseCurrency == SettleCurrency because if it is then no need to do the FX translation if (env.BaseCurrency.Equals(riskCurrency)) { return(new List <Journal>()); } var prevEodFxRate = Convert.ToDouble(FxRates.Find(env.PreviousValueDate, riskCurrency).Rate); var eodFxRate = Convert.ToDouble(FxRates.Find(env.ValueDate, riskCurrency).Rate); var effectiveRate = eodFxRate - prevEodFxRate; var usdEquivalent = element.Quantity * effectiveRate; var fromAccount = "Mark to Market Derivatives Contracts due to FX (Liabilities)"; var toAccount = "Change in Unrealized Derivatives Contracts due to FX"; if (usdEquivalent > 0) { fromAccount = "Mark to Market Derivatives Contracts due to FX (Assets)"; } // Get accounts var toFrom = new AccountUtils().GetAccounts(env, fromAccount, toAccount, _TAGS, element); var fund = env.GetFund(element); var debit = new Journal(element, toFrom.From, $"{tradeEvent}-unrealizedpnl-fx", env.ValueDate) { Quantity = quantity, FxRate = effectiveRate, StartPrice = prevEodFxRate, FxCurrency = riskCurrency, EndPrice = eodFxRate, Fund = fund, Value = env.SignedValue(toFrom.From, toFrom.To, true, usdEquivalent), CreditDebit = env.DebitOrCredit(toFrom.From, usdEquivalent), }; var credit = new Journal(element, toFrom.To, $"{tradeEvent}-unrealizedpnl-fx", env.ValueDate) { Quantity = quantity, FxRate = effectiveRate, FxCurrency = riskCurrency, StartPrice = prevEodFxRate, EndPrice = eodFxRate, Fund = fund, Value = env.SignedValue(toFrom.From, toFrom.To, false, usdEquivalent), CreditDebit = env.DebitOrCredit(toFrom.To, usdEquivalent), }; return(new List <Journal>(new[] { debit, credit })); }
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; }
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; }
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); } } } }
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 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 }); }
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 }); } }
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 } } }
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"; } } }
/// <summary> /// Create the entries for Unsettled Fx gain / loss /// </summary> /// <param name="env">Environment</param> /// <param name="taxlotStatus"></param> /// <param name="tradeEvent"></param> /// <param name="element">Transaction</param> /// <returns>a list of the created journal entries</returns> internal static List <Journal> CreateFx(PostingEngineEnvironment env, string fromAccount, string toAccount, string tradeEvent, double quantity, TaxLotStatus taxlotStatus, Transaction element) { if (tradeEvent.Equals(Event.TRADE_DATE)) { return(new List <Journal>()); } // TBD: THIS IS FOR BOBBY if (element.SecurityType.Equals("FORWARD")) { return(new List <Journal>()); } // Check to see if the BaseCurrency == SettleCurrency because if it is then no need to do the FX translation if (env.BaseCurrency.Equals(element.SettleCurrency)) { return(new List <Journal>()); } var currency = element.SettleCurrency; var prevEodFxRate = Convert.ToDouble(FxRates.Find(env.PreviousValueDate, currency).Rate); var eodFxRate = Convert.ToDouble(FxRates.Find(env.ValueDate, currency).Rate); var effectiveRate = eodFxRate - prevEodFxRate; var usdEquivalent = element.NetMoney * effectiveRate; if (element.IsBuy()) { usdEquivalent *= -1; } else if (element.IsShort()) { } // Get accounts var toFrom = new AccountUtils().GetAccounts(env, fromAccount, toAccount, _TAGS, element); var debit = new Journal(element, toFrom.From, $"{tradeEvent}-unrealizedpnl-fx", env.ValueDate) { Quantity = quantity, FxRate = effectiveRate, StartPrice = prevEodFxRate, EndPrice = eodFxRate, Fund = env.GetFund(element), Value = env.SignedValue(toFrom.From, toFrom.To, true, usdEquivalent), CreditDebit = env.DebitOrCredit(toFrom.From, usdEquivalent), }; var credit = new Journal(element, toFrom.To, $"{tradeEvent}-unrealizedpnl-fx", env.ValueDate) { Quantity = quantity, FxRate = effectiveRate, StartPrice = prevEodFxRate, EndPrice = eodFxRate, Fund = env.GetFund(element), Value = env.SignedValue(toFrom.From, toFrom.To, false, usdEquivalent), CreditDebit = env.DebitOrCredit(toFrom.To, usdEquivalent), }; return(new List <Journal>(new[] { debit, credit })); }
public bool Run(PostingEngineEnvironment env) { var dates = "select minDate = min([when]), maxDate = max([when]) from vwJournal"; env.CallBack?.Invoke("ExpencesAndRevenues Calculation Started"); var table = new DataTable(); // read the table structure from the database using (var adapter = new SqlDataAdapter(dates, new SqlConnection(env.ConnectionString))) { adapter.Fill(table); adapter.Dispose(); }; var valueDate = Convert.ToDateTime(table.Rows[0]["minDate"]); var endDate = Convert.ToDateTime(table.Rows[0]["maxDate"]); using (var cc = new SqlConnection(env.ConnectionString)) { cc.Open(); SetupEnvironment.Setup(cc); cc.Close(); } var journals = new List <Journal>(); var connection = new SqlConnection(env.ConnectionString); connection.Open(); var transaction = connection.BeginTransaction(); var sqlHelper = new SqlHelper(env.ConnectionString); var rowsCompleted = 1; var numberOfDays = (endDate - valueDate).Days; while (valueDate <= endDate) { if (!valueDate.IsBusinessDate()) { valueDate = valueDate.AddDays(1); rowsCompleted++; continue; } try { var sqlParams = new SqlParameter[] { new SqlParameter("@startDate", new DateTime(valueDate.Year, 1, 1)), new SqlParameter("@businessDate", valueDate), new SqlParameter("@prevbusinessDate", valueDate.PrevBusinessDate()), }; var dataTable = sqlHelper.GetDataTables("DayOverDayIncome", CommandType.StoredProcedure, sqlParams.ToArray()); foreach (DataRow row in dataTable[0].Rows) { int offset = 0; var expencesAndRevenues = new { Fund = Convert.ToString(row[offset++]), Credit = Convert.ToDecimal(row[offset++]), Debit = Convert.ToDecimal(row[offset++]), Balance = Convert.ToDecimal(row[offset++]), }; var accountType = $"Net Income Current Year"; if (AccountType.Find(AccountCategory.AC_EQUITY, accountType, false) == null) { // Need to create the Account Type var createdAccountType = AccountType.FindOrCreate(AccountCategory.AC_EQUITY, accountType); new AccountUtils().Save(env, createdAccountType); } var balance = Convert.ToDouble(expencesAndRevenues.Balance); var account = new AccountUtils().GetAccount(env, accountType, new string[] { env.BaseCurrency }.ToList()); var debit = new Journal(account, "expences-revenues", valueDate) { Source = "calculated-data", Fund = expencesAndRevenues.Fund, Quantity = balance, FxCurrency = env.BaseCurrency, Symbol = env.BaseCurrency, SecurityId = -1, FxRate = 0, StartPrice = 0, EndPrice = 0, // If this number is +ve then its actually a Debit and this is going into a Equity account which needs to be -ve and not +ve Value = balance * -1, CreditDebit = env.DebitOrCredit(account, balance * -1), }; journals.AddRange(new List <Journal>(new[] { debit })); } } catch (Exception ex) { env.CallBack?.Invoke($"Exception on {valueDate.ToString("MM-dd-yyyy")}, {ex.Message}"); } env.CallBack?.Invoke($"Completed ExpencesAndRevenues for {valueDate.ToString("MM-dd-yyyy")}", numberOfDays, rowsCompleted++); valueDate = valueDate.AddDays(1); } if (journals.Count() > 0) { env.CollectData(journals); journals.Clear(); } transaction.Commit(); connection.Close(); return(true); }
/// <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); } } } }
/// <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 prevEodPrice = 0.0; var eodPrice = 0.0; var fxRate = 0.0; if (element.TradeCurrency.Equals(env.BaseCurrency)) { if (env.ValueDate == element.TradeDate) { prevEodPrice = 1 / element.SettleNetPrice; fxRate = FxRates.Find(env.ValueDate, element.SettleCurrency).Rate; eodPrice = fxRate; } else { prevEodPrice = FxRates.Find(env.PreviousValueDate, element.SettleCurrency).Rate; fxRate = FxRates.Find(env.PreviousValueDate, element.SettleCurrency).Rate; eodPrice = fxRate; } } else { if (env.ValueDate == element.TradeDate) { prevEodPrice = element.SettleNetPrice; fxRate = FxRates.Find(env.ValueDate, element.TradeCurrency).Rate; eodPrice = fxRate; } else { prevEodPrice = FxRates.Find(env.PreviousValueDate, element.TradeCurrency).Rate; fxRate = FxRates.Find(env.ValueDate, element.TradeCurrency).Rate; eodPrice = fxRate; } } var unrealizedPnl = 0.0; var quantity = taxlot.Quantity; var rateDiff = (eodPrice - prevEodPrice); if (element.SettleCurrency.Equals(env.BaseCurrency)) { unrealizedPnl = (rateDiff * quantity); } else { unrealizedPnl = (rateDiff * quantity) * fxRate; } 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.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); } } } }