public void OnDayClose(DateTime todaysDate, decimal totalCapitalToday) { //update the status of each trade foreach (TradeTracker t in TradeTrackers.Values) { t.Update(todaysDate, _data, _fxData); } //update position stats foreach (var kvp in Positions) { int id = kvp.Key; Position p = kvp.Value; decimal fxRate = p.Currency == null || p.Currency.ID == 1 ? 1 : _fxData[p.Currency.ID][0].Close; decimal? lastPrice = !_data.ContainsKey(id) || _data[id].CurrentBar < 0 ? (decimal?)null : _data[id][0].Close; p.GetPnL(lastPrice, fxRate); } //Capital usage and profit/loss for the day Capital.AddLong(Positions.Sum(x => x.Value.Capital.Long.Last())); Capital.AddShort(Positions.Sum(x => x.Value.Capital.Short.Last())); decimal todaysPnl = TradeTrackers.Sum(x => x.Value.TodaysPnL); _logger.Log(LogLevel.Trace, string.Format("Portfolio {0} @ {1}: Capital used: {2:0.00} P/L: {3:0.00}", Name, todaysDate, Capital.TodaysCapitalGross, todaysPnl)); //P/L curves ProfitLossEquityCurve.AddChange((double)todaysPnl, todaysDate); ProfitLossLongEquityCurve.AddValue((double)TradeTrackers.Sum(x => x.Value.TotalPnlLong), todaysDate); ProfitLossShortEquityCurve.AddValue((double)TradeTrackers.Sum(x => x.Value.TotalPnlShort), todaysDate); //ROAC if (Capital.TodaysCapitalGross == 0) { _deferredPnL += todaysPnl; RoacEquityCurve.AddReturn(0, todaysDate); } else { RoacEquityCurve.AddReturn((double)((_deferredPnL + todaysPnl) / Capital.TodaysCapitalGross), todaysDate); _deferredPnL = 0; } //ROTC if (totalCapitalToday == 0) { RotcEquityCurve.AddReturn(0, todaysDate); } else { RotcEquityCurve.AddReturn((double)(todaysPnl / totalCapitalToday), todaysDate); } Capital.EndOfDay(); }
public void Update(DateTime currentDate, Dictionary <int, TimeSeries> data, Dictionary <int, TimeSeries> fxData) { TodaysPnL = 0; if (!Open) { return; } //Update positions foreach (var kvp in Positions) { int id = kvp.Key; Position p = kvp.Value; decimal fxRate = p.Currency == null || p.Currency.ID <= 1 ? 1 : fxData[p.Currency.ID][0].Close; TodaysPnL += p.GetPnL(data[id].CurrentBar < 0 ? (decimal?)null : data[id][0].Close, fxRate); } //Update currency positions foreach (var kvp in CurrencyPositions) { int id = kvp.Key; if (fxData[id].CurrentBar < 0) { continue; } CurrencyPosition p = kvp.Value; decimal fxRate = fxData[id][0].Close; TodaysPnL += p.Update(fxRate); } if (Positions.Any(x => x.Value.Capital.Gross.Count > 0)) { Capital.AddLong(Positions.Sum(x => x.Value.Capital.Long.Last())); Capital.AddShort(Positions.Sum(x => x.Value.Capital.Short.Last())); } if (Capital.TodaysCapitalGross != 0) { _currentEquity *= (double)(1 + TodaysPnL / Capital.TodaysCapitalGross); } #if DEBUG _logger.Log(LogLevel.Trace, string.Format("Trade tracker ID {0} @ {1}, todays capital usage {2:0.00}, P/L: {3:0.00}", Trade.ID, currentDate, Capital.TodaysCapitalGross, TodaysPnL)); #endif Capital.EndOfDay(); _totalPnL += TodaysPnL; CumulativeReturns.Add(currentDate, _currentEquity); CumulativePnL.Add(currentDate, _totalPnL); Open = Positions.Values.Sum(x => x.Quantity) != 0 || CurrencyPositions.Values.Sum(x => x.Quantity) != 0 || (_ordersRemaining > 0 && _ordersRemaining < Trade.Orders.Count); }
public void MultiPeriodCapitalUsageIsRecordedCorrectly() { var pos = new Position(_instrument); var o = new Order { Quantity = 100, Price = 10, Instrument = _instrument, FXRateToBase = 1 }; pos.AddOrder(o); Assert.AreEqual(1000, pos.Capital.TodaysCapitalGross); Assert.AreEqual(1000, pos.Capital.TodaysCapitalLong); pos.GetPnL(10, 1); var o2 = new Order { Quantity = 100, Price = 10, Instrument = _instrument, FXRateToBase = 1 }; pos.AddOrder(o2); var o3 = new Order { Quantity = -200, Price = 10, Instrument = _instrument, FXRateToBase = 1 }; pos.AddOrder(o3); pos.GetPnL(10, 1); Assert.AreEqual(2000, pos.Capital.Gross.Last()); Assert.AreEqual(2000, pos.Capital.Long.Last()); pos.GetPnL(10, 1); var o4 = new Order { Quantity = 150, Price = 10, Instrument = _instrument, FXRateToBase = 1 }; pos.AddOrder(o4); pos.GetPnL(10, 1); List<decimal> expectedCapitalGross = new List<decimal> { 1000, 2000, 0, 1500 }; for (int i = 0; i < expectedCapitalGross.Count; i++) { Assert.AreEqual(expectedCapitalGross[i], pos.Capital.Gross[i]); } }
public void CapitalUsageWhenEnteringAndExitingNearCloseIsCounted() { var pos = new Position(_instrument); var o = new Order { Quantity = 100, Price = 10, Instrument = _instrument, FXRateToBase = 1, TradeDate = new DateTime(2000, 1, 1, 15, 59, 0) }; pos.AddOrder(o); var o2 = new Order { Quantity = -100, Price = 10, Instrument = _instrument, FXRateToBase = 1, TradeDate = new DateTime(2000, 1, 1, 15, 59, 1) }; pos.AddOrder(o2); pos.GetPnL(10, 1); Assert.AreEqual(1000, pos.Capital.Gross.Last()); Assert.AreEqual(1000, pos.Capital.Long.Last()); }
public void UnrealizedPnLReturnsDiffernceBetweenTotalAndRealizedPnL() { var pos = new Position(_instrument); var o = new Order { Quantity = 100, Price = 10, Instrument = _instrument, FXRateToBase = 1 }; pos.AddOrder(o); var o2 = new Order { Quantity = -50, Price = 11, Instrument = _instrument, FXRateToBase = 1 }; pos.AddOrder(o2); pos.GetPnL(11, 1); Assert.AreEqual(50, pos.UnrealizedPnL); }
public void PnLIsDeferredForROACCalculationsIfNoCapitalIsUsed() { var pos = new Position(_instrument); var c = new CashTransaction { Amount = 10, Instrument = _instrument, FXRateToBase = 1 }; pos.AddCashTransaction(c); pos.GetPnL(10, 1); var o = new Order { Quantity = 100, Price = 10, Instrument = _instrument, FXRateToBase = 1 }; pos.AddOrder(o); pos.GetPnL(10, 1); Assert.AreEqual(1 + (10d / (10 * 100)), pos.ROAC); }
public void GetPnLReturnsCorrectPnLWithBothRealizedAndUnrealizedPnL() { var pos = new Position(_instrument); var o = new Order { Quantity = 100, Price = 10, Instrument = _instrument, FXRateToBase = 1 }; pos.AddOrder(o); var o2 = new Order { Quantity = -50, Price = 11, Instrument = _instrument, FXRateToBase = 1, }; pos.AddOrder(o2); Assert.AreEqual(50 * 1 + 50 * 2, pos.GetPnL(12, 1)); Assert.AreEqual(-2 * 50, pos.GetPnL(10, 1)); }
public void GetPnLReturnsCorrectPnLIncludingFXRateChanges() { var pos = new Position(_instrument); var o = new Order { Quantity = 100, Price = 10, Instrument = _instrument, FXRateToBase = 1 }; pos.AddOrder(o); Assert.AreEqual(45, pos.GetPnL(11, 0.95m)); }
public void GetPnLReturnsCorrectPnLForShortPositions() { var pos = new Position(_instrument); var o = new Order { Quantity = -100, Price = 10, Instrument = _instrument, FXRateToBase = 1 }; pos.AddOrder(o); Assert.AreEqual(-100, pos.GetPnL(11, 1)); Assert.AreEqual(100, pos.GetPnL(10, 1)); }
public void GetPnLIncludesCommissions() { var pos = new Position(_instrument); var o = new Order { Quantity = 100, Price = 10, Instrument = _instrument, FXRateToBase = 1, Commission = -5 }; pos.AddOrder(o); Assert.AreEqual(100 - 5, pos.GetPnL(11, 1)); Assert.AreEqual(-100, pos.GetPnL(10, 1)); }
public void ROACValueReflectsFXRateChanges() { var pos = new Position(_instrument); var o = new Order { Quantity = 100, Price = 10, Instrument = _instrument, FXRateToBase = 1 }; pos.AddOrder(o); var o2 = new Order { Quantity = -50, Price = 11, Instrument = _instrument, FXRateToBase = 1 }; pos.AddOrder(o2); pos.GetPnL(10, 1.1m); Assert.AreEqual(1.1, pos.ROAC); }
public void ROACValueIsCorrectWithCashTransactions() { var pos = new Position(_instrument); var o = new Order { Quantity = 100, Price = 10, Instrument = _instrument, FXRateToBase = 1 }; pos.AddOrder(o); var c = new CashTransaction { Amount = 10, Instrument = _instrument, FXRateToBase = 1 }; pos.AddCashTransaction(c); pos.GetPnL(10, 1); Assert.AreEqual(1.01, pos.ROAC); }
public void ROACValueIsCorrectAfterPartialExit() { var pos = new Position(_instrument); var o = new Order { Quantity = 100, Price = 10, Instrument = _instrument, FXRateToBase = 1 }; pos.AddOrder(o); var o2 = new Order { Quantity = -50, Price = 11, Instrument = _instrument, FXRateToBase = 1 }; pos.AddOrder(o2); pos.GetPnL(10, 1); Assert.AreEqual(1.05, pos.ROAC); }
public void ROACValueIsCorrectAfterReversing() { var pos = new Position(_instrument); var o = new Order { Quantity = 100, Price = 10, Instrument = _instrument, FXRateToBase = 1 }; pos.AddOrder(o); var o2 = new Order { Quantity = -150, Price = 11, Instrument = _instrument, FXRateToBase = 1 }; pos.AddOrder(o2); pos.GetPnL(11, 1); Assert.AreEqual(Math.Round(1 + (1.0 * 100) / (10 * 100 + 11 * 50), 5), Math.Round(pos.ROAC, 5)); }
public void CapitalUsageWhenEnteringAndExitingMultipleOrdersNearCloseIsCountedWithShortPosition() { var pos = new Position(_instrument); decimal orderCapitalUsage = 0; var o = new Order { Quantity = -100, Price = 10, Instrument = _instrument, FXRateToBase = 1, TradeDate = new DateTime(2000, 1, 1, 15, 59, 0) }; orderCapitalUsage = pos.AddOrder(o); Assert.AreEqual(0, orderCapitalUsage); var o2 = new Order { Quantity = -100, Price = 10, Instrument = _instrument, FXRateToBase = 1, TradeDate = new DateTime(2000, 1, 1, 15, 59, 1) }; orderCapitalUsage = pos.AddOrder(o2); Assert.AreEqual(0, orderCapitalUsage); var o3 = new Order { Quantity = 100, Price = 10, Instrument = _instrument, FXRateToBase = 1, TradeDate = new DateTime(2000, 1, 1, 15, 59, 2) }; orderCapitalUsage = pos.AddOrder(o3); Assert.AreEqual(1000, orderCapitalUsage); var o4 = new Order { Quantity = 100, Price = 10, Instrument = _instrument, FXRateToBase = 1, TradeDate = new DateTime(2000, 1, 1, 15, 59, 3) }; orderCapitalUsage = pos.AddOrder(o4); Assert.AreEqual(1000, orderCapitalUsage); pos.GetPnL(10, 1); Assert.AreEqual(2000, pos.Capital.Gross.Last()); Assert.AreEqual(2000, pos.Capital.Short.Last()); }