public static bool IsShort(this TaxLotStatus tls) { if (string.IsNullOrEmpty(tls.Side)) { return(false); } return(tls.Side.ToLowerInvariant().Equals(SHORT)); }
/// <summary> /// Check to see if the Tax Lot is Still Open for this trade /// </summary> /// <param name="i"></param> /// <returns></returns> internal bool TaxLotsIsOpen(Transaction i) { if (TaxLotStatus.ContainsKey(i.LpOrderId)) { return(!TaxLotStatus[i.LpOrderId].Status.Equals("closed")); } return(false); }
/// <summary> /// Calcualte the Unrealized Pnl for this Tax lot /// </summary> /// <param name="env"></param> /// <returns></returns> public static double CalculateUnrealizedPnl(PostingEngineEnvironment env, TaxLotStatus taxLotStatus, double residualQuantity = 0, double endPrice = 0) { double multiplier = 1.0; if (env.SecurityDetails.ContainsKey(taxLotStatus.Trade.BloombergCode)) { multiplier = env.SecurityDetails[taxLotStatus.Trade.BloombergCode].Multiplier; } double fxrate = 1.0; // Lets get fx rate if needed if (!taxLotStatus.Trade.SettleCurrency.Equals(env.BaseCurrency)) { fxrate = Convert.ToDouble(FxRates.Find(env.ValueDate, taxLotStatus.Trade.SettleCurrency).Rate); } var eodPrice = 0.0; var prevEodPrice = 0.0; if (env.ValueDate == taxLotStatus.Trade.TradeDate) { prevEodPrice = taxLotStatus.Trade.SettleNetPrice; var eodMarketPrice = MarketPrices.GetPrice(env, env.ValueDate, taxLotStatus.Trade); if (!eodMarketPrice.Valid) { env.AddMessage(eodMarketPrice.Error); } eodPrice = eodMarketPrice.Price; } else { prevEodPrice = MarketPrices.GetPrice(env, env.PreviousValueDate, taxLotStatus.Trade).Price; eodPrice = MarketPrices.GetPrice(env, env.ValueDate, taxLotStatus.Trade).Price; } eodPrice = endPrice != 0 ? endPrice : eodPrice; // Use residual Quantity if specified var quantity = residualQuantity != 0.0 ? residualQuantity : taxLotStatus.Quantity; var priceDiff = (eodPrice - prevEodPrice); var unrealizedPnl = priceDiff * quantity; return(unrealizedPnl * fxrate * multiplier); }
/// <summary> /// Creates an open tax lot and also adds it to the TaxLotStatus collection /// </summary> /// <param name="element">Trade to create the tax lot</param> /// <param name="fxrate">FxRate for this trade</param> /// <returns>Generated TaxLot</returns> public TaxLotStatus GenerateOpenTaxLot(Transaction element, double fxrate) { var taxlotStatus = new TaxLotStatus { Trade = element, InvestmentAtCost = element.NetMoney * fxrate, FxRate = fxrate, TradeDate = element.TradeDate, BusinessDate = element.TradeDate, Symbol = element.Symbol, Side = element.Side, OpenId = element.LpOrderId, Status = "Open", OriginalQuantity = element.Quantity, Quantity = element.Quantity, Fund = GetFund(element), TradePrice = element.SettleNetPrice, }; TaxLotStatus.Add(element.LpOrderId, taxlotStatus); return(taxlotStatus); }
/// <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 })); }
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 })); }
private void GenerateDailyUnrealized(PostingEngineEnvironment env, TaxLotStatus taxLotStatus, Transaction element, double quantity, double fxRate) { var prevEodPrice = 0.0; var eodPrice = 0.0; if (env.ValueDate == taxLotStatus.Trade.TradeDate) { prevEodPrice = taxLotStatus.Trade.SettleNetPrice; eodPrice = MarketPrices.GetPrice(env, env.ValueDate, taxLotStatus.Trade).Price; } else { prevEodPrice = MarketPrices.GetPrice(env, env.PreviousValueDate, taxLotStatus.Trade).Price; eodPrice = MarketPrices.GetPrice(env, env.ValueDate, taxLotStatus.Trade).Price; } var endPrice = element.SettleNetPrice; if (taxLotStatus.Quantity == 0.0) { eodPrice = endPrice; } var unrealizedPnl = CommonRules.CalculateUnrealizedPnl(env, taxLotStatus, quantity, eodPrice); var originalAccount = taxLotStatus.Side == "SHORT" ? "Mark to Market Shorts" : "Mark to Market Longs"; var fromToAccounts = new AccountUtils().GetAccounts(env, originalAccount, "CHANGE IN UNREALIZED GAIN/(LOSS)", listOfTags, taxLotStatus.Trade); if (taxLotStatus.Side == "SHORT") { unrealizedPnl *= -1; } var fund = env.GetFund(taxLotStatus.Trade); var debit = new Journal(taxLotStatus.Trade) { Account = fromToAccounts.From, When = env.ValueDate, Symbol = taxLotStatus.Symbol, Quantity = quantity, FxRate = fxRate, Value = env.SignedValue(fromToAccounts.From, fromToAccounts.To, true, unrealizedPnl), CreditDebit = env.DebitOrCredit(fromToAccounts.From, unrealizedPnl), StartPrice = prevEodPrice, EndPrice = eodPrice, Event = Event.DAILY_UNREALIZED_PNL, Fund = fund, }; var credit = new Journal(debit) { Account = fromToAccounts.To, Value = env.SignedValue(fromToAccounts.From, fromToAccounts.To, false, unrealizedPnl), CreditDebit = env.DebitOrCredit(fromToAccounts.To, unrealizedPnl), }; env.Journals.AddRange(new[] { debit, credit }); if (fxRate != 1.0) { if (element.TradeDate != env.ValueDate && element.SettleDate >= env.ValueDate) { var fxJournals = FxPosting.CreateFx( env, "DUE FROM/(TO) PRIME BROKERS ( Unsettled Activity )", "fx gain or loss on unsettled balance", "daily", quantity, null, element); env.Journals.AddRange(fxJournals); } if (taxLotStatus.Quantity != 0.0) { if (env.ValueDate.Equals(new DateTime(2019, 12, 17))) { } if (element.TradeDate != env.ValueDate) { // Has to happen for every day var fxJournalsForInvestmentAtCost = FxPosting.CreateFx( env, CommonRules.GetFXMarkToMarketAccountType(element, "FX MARKET TO MARKET ON STOCK COST"), "Change in unrealized due to fx on original Cost", "daily", quantity, taxLotStatus, element); env.Journals.AddRange(fxJournalsForInvestmentAtCost); new FxPosting().CreateFxUnsettled(env, element); } } } }
/// <summary> /// Generate all of the journal entries we need for reduction / closeout of a tax lot /// </summary> /// <param name="env"></param> /// <param name="lot"></param> /// <param name="taxlotStatus"></param> /// <param name="element"></param> /// <param name="workingQuantity"></param> /// <param name="fxrate"></param> /// <param name="multiplier"></param> private void GenerateJournals(PostingEngineEnvironment env, TaxLotDetail lot, TaxLotStatus taxlotStatus, Transaction element, double workingQuantity, double fxrate, double multiplier) { var buyTrade = env.FindTrade(lot.Trade.LpOrderId); var taxlot = CommonRules.RelieveTaxLot(env, lot, element, workingQuantity, true); // Has to happen for every day var fxJournalsForInvestmentAtCost = FxPosting.CreateFx( env, CommonRules.GetFXMarkToMarketAccountType(element, "FX MARKET TO MARKET ON STOCK COST"), "Change in unrealized due to fx on original Cost", "daily", workingQuantity, taxlotStatus, buyTrade); env.Journals.AddRange(fxJournalsForInvestmentAtCost); taxlotStatus.Quantity += workingQuantity; if (taxlotStatus.Quantity == 0) { taxlotStatus.Status = "Closed"; } else { taxlotStatus.Status = "Partially Closed"; } if (taxlotStatus.Quantity == 0.0) { // Is this really needed, as if the tax lot is zero then should not generate any additional unrealized pnl //GenerateDailyUnrealized(env, taxlotStatus, element, workingQuantity * -1, fxrate); } var eodPrice = MarketPrices.GetPrice(env, env.PreviousValueDate, buyTrade).Price; // Calculate the unrealized Backout PNL for the created Tax Lot var unrealizedPnl = Math.Abs(taxlot.Quantity) * (eodPrice - buyTrade.SettleNetPrice) * multiplier * fxrate; unrealizedPnl *= CommonRules.DetermineSign(element); // Need to backout the Unrealized PNL here, as we are reducing the position of the TaxLot CommonRules.ReverseUnrealizedPnl( env, buyTrade, element, unrealizedPnl * -1, buyTrade.SettleNetPrice, element.SettleNetPrice, fxrate); // Original FxRate var changeDueToFx = fxrate - taxlotStatus.FxRate; // Original Trade Price var changeInRealizedPnlDueToFx = changeDueToFx * (taxlot.TradePrice) * Math.Abs(taxlot.Quantity); var changeInUnRealizedPnlDueToFx = changeDueToFx * (taxlot.CostBasis - taxlot.TradePrice) * Math.Abs(taxlot.Quantity); CommonRules.PostRealizedPnl( env, buyTrade, taxlot.RealizedPnl, taxlot.TradePrice, taxlot.CostBasis, fxrate); if (fxrate != 1.0) { PostRealizedFxGain(env, buyTrade, changeInRealizedPnlDueToFx, taxlot.TradePrice, taxlot.CostBasis, changeDueToFx); } var fxChange = new FxPosting().CreateFxUnsettled(env, buyTrade); List <SqlParameter> sqlParams = new List <SqlParameter>(); sqlParams.Add(new SqlParameter("@busDate", env.ValueDate)); sqlParams.Add(new SqlParameter("@LpOrderId", lot.Trade.LpOrderId)); var dataTable = new SqlHelper(env.ConnectionString).GetDataTables("ClosingTaxLot", CommandType.StoredProcedure, sqlParams.ToArray()); var changeInUnRealized = 1.0; if (dataTable[0].Rows.Count > 0) { changeInUnRealized = Convert.ToDouble(dataTable[0].Rows[0][2]); } var changeInUnRealizedFx = 0.0; if (dataTable[1].Rows.Count > 0) { changeInUnRealizedFx = Convert.ToDouble(dataTable[1].Rows[0][2]); } if (changeInUnRealizedFx != 0.0) { var closeOut = changeInUnRealizedFx + fxChange; PostUnrealizedFxGain(env, buyTrade, closeOut, taxlot.TradePrice, taxlot.CostBasis, changeDueToFx); } var sumFxMarkToMarket = 0.0; if (dataTable[2].Rows.Count > 0) { sumFxMarkToMarket = Convert.ToDouble(dataTable[2].Rows[0][2]); sumFxMarkToMarket += fxJournalsForInvestmentAtCost[0].Value; ReversePosting(env, "Change in unrealized due to fx on original Cost", CommonRules.GetFXMarkToMarketAccountType(element, "FX MARKET TO MARKET ON STOCK COST"), buyTrade, sumFxMarkToMarket); } var listOfFromTags = new List <Tag> { Tag.Find("SecurityType"), Tag.Find("CustodianCode") }; /* * var markToMarketAccount = (buyTrade.IsShort() || buyTrade.IsCover()) ? "Mark to Market Shorts" : "Mark to Market Longs"; * * var fromTo = new AccountUtils().GetAccounts(env, "CHANGE IN UNREALIZED GAIN/(LOSS)", markToMarketAccount, listOfFromTags, element); * * if (fxrate == 1.0) * { * changeInUnRealized = Convert.ToDouble(taxlot.RealizedPnl) * -1; * } * * // Now Generate Entries for the trade that is drawing down on the taxLot * var fromJournal = new Journal(buyTrade) * { * Account = fromTo.From, * When = env.ValueDate, * * CreditDebit = env.DebitOrCredit(fromTo.From, changeInUnRealized), * Value = env.SignedValue(fromTo.From, fromTo.To, true, changeInUnRealized), * Event = Event.UNREALIZED_PNL, * Fund = env.GetFund(element), * * StartPrice = taxlot.TradePrice, * EndPrice = taxlot.CostBasis, * FxRate = fxrate, * }; * * var toJournal = new Journal(buyTrade) * { * Account = fromTo.To, * When = env.ValueDate, * * CreditDebit = env.DebitOrCredit(fromTo.To, changeInUnRealized * -1), * Value = env.SignedValue(fromTo.From, fromTo.To, false, changeInUnRealized), * Event = Event.UNREALIZED_PNL, * Fund = env.GetFund(element), * * StartPrice = taxlot.TradePrice, * EndPrice = taxlot.CostBasis, * FxRate = fxrate, * }; * * env.Journals.AddRange(new[] { fromJournal, toJournal }); */ }
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 }); }