I observed something strange with how trade profilt/loss (PNLs) are being calculated. If I access the position profit (Position p, p.Profit) inside the Execute() function, then all the PNL calculations is wrong. However, if I remove the line of code that access the position profit, the PNL calculations are done right. I need help to explain why this happens. Thanks.
Strategy Settings:
Single Symbol: AEMD
Data Scale: Daily
Data Range: Most recent 1 yrs
Position size: $5000 (Fixed)
The strategy code being used is shown below:
If the code at line #25 i
is commented out, the PNL calculations is done correctly as shown in the Backtest Results below
However, if line #25 is NOT commented out (as shown in the code below), the PNL calculations are not correct as shown below.
Strategy Settings:
Single Symbol: AEMD
Data Scale: Daily
Data Range: Most recent 1 yrs
Position size: $5000 (Fixed)
The strategy code being used is shown below:
CODE:
using WealthLab.Backtest; using System; using WealthLab.Core; using WealthLab.Indicators; using System.Drawing; using System.Collections.Generic; namespace WealthScript5 { public class MyStrategy : UserStrategyBase { //create indicators and other objects here, this is executed prior to the main trading loop public override void Initialize(BarHistory bars) { } //execute the strategy rules here, this is executed once for each bar in the backtest history public override void Execute(BarHistory bars, int bar) { if (OpenPositions.Count > 0) { foreach (Position pos in OpenPositions) { ClosePosition(pos, OrderType.Market); // double save_pnl = pos.Profit; } } else { int number_of_shares = 100; Transaction tL = PlaceTrade(bars, TransactionType.Buy, OrderType.Limit, 0.9 * bars.Close[bar]); tL.Quantity = number_of_shares; } } //declare private variables belows } }
If the code at line #25 i
CODE:
double save_pnl = pos.Profit;
is commented out, the PNL calculations is done correctly as shown in the Backtest Results below
QUOTE:
Position Symbol Quantity "Entry Date" "Entry Price" "Exit Date" "Exit Price" Profit "Profit %" "Bars Held" "Entry Signal" "Exit Signal" "MFE %" "MAE %"
Long AEMD 100 12/18/2020 2.097 12/21/2020 2.08 -1.6999999999999904 -0.8106819265617503 1 10.925131139723408 -2.002861230329033
Long AEMD 100 1/25/2021 2.3760000000000003 1/26/2021 2.66 28.39999999999998 11.952861952861944 1 15.319865319865315 -1.9360269360269469
Long AEMD 100 1/28/2021 2.178 1/29/2021 2.26 8.199999999999985 3.764921946740122 1 12.94765840220386 -3.5812672176308475
Long AEMD 100 2/11/2021 2.7 2/12/2021 2.793 9.299999999999997 3.4444444444444438 1 11.111111111111104 -10.740740740740742
Long AEMD 100 2/23/2021 2.358 2/24/2021 2.44 8.199999999999985 3.4775233248515627 1 7.71840542832909 -5.852417302798978
Long AEMD 100 3/4/2021 1.971 3/5/2021 1.98 0.8999999999999897 0.45662100456620475 1 11.009639776763068 -10.95890410958905
Long AEMD 100 4/12/2021 1.854 4/13/2021 1.88 2.59999999999998 1.4023732470334305 1 10.571736785329003 -0.7497303128371101
Long AEMD 100 5/13/2021 1.494 5/14/2021 1.55 5.600000000000005 3.748326639892908 1 13.119143239625163 -2.945113788487285
Long AEMD 100 6/10/2021 9.22 6/11/2021 6.91 -231.00000000000006 -25.054229934924084 1 4.121475054229924 -30.911062906724517
Long AEMD 100 6/14/2021 5.841 6/15/2021 5.95 10.899999999999999 1.8661188152713573 1 15.219996575928782 -1.5579524054100358
Long AEMD 100 7/14/2021 3.717 7/15/2021 3.71 -0.7000000000000117 -0.18832391713747962 1 11.380145278450353 -6.10707559860102
Long AEMD 100 7/20/2021 4.437 7/21/2021 5.17 73.29999999999997 16.52017128690556 1 18.77394636015324 -3.5384268649988733
Long AEMD 100 7/22/2021 5.04 7/23/2021 5.83 79 15.674603174603174 1 23.412698412698404 -7.142857142857149
Long AEMD 100 8/11/2021 4.329 8/12/2021 4.37 4.100000000000037 0.9471009471009557 1 13.652113652113657 -0.9009009009008941
Long AEMD 100 9/23/2021 4.2965 9/24/2021 4.17 -12.650000000000006 -2.9442569533341105 1 3.572675433492382 -6.2027231467473465
Long AEMD 100 10/4/2021 3.375 10/5/2021 3.48 10.499999999999998 3.1111111111111107 1 10.814814814814822 -0.7407407407407381
However, if line #25 is NOT commented out (as shown in the code below), the PNL calculations are not correct as shown below.
CODE:
using WealthLab.Backtest; using System; using WealthLab.Core; using WealthLab.Indicators; using System.Drawing; using System.Collections.Generic; namespace WealthScript5 { public class MyStrategy : UserStrategyBase { //create indicators and other objects here, this is executed prior to the main trading loop public override void Initialize(BarHistory bars) { } //execute the strategy rules here, this is executed once for each bar in the backtest history public override void Execute(BarHistory bars, int bar) { if (OpenPositions.Count > 0) { foreach (Position pos in OpenPositions) { ClosePosition(pos, OrderType.Market); double save_pnl = pos.Profit; } } else { int number_of_shares = 100; Transaction tL = PlaceTrade(bars, TransactionType.Buy, OrderType.Limit, 0.9 * bars.Close[bar]); tL.Quantity = number_of_shares; } } //declare private variables belows } }
QUOTE:
Position Symbol Quantity "Entry Date" "Entry Price" "Exit Date" "Exit Price" Profit "Profit %" "Bars Held" "Entry Signal" "Exit Signal" "MFE %" "MAE %"
Long AEMD 100 12/18/2020 2.097 12/21/2020 2.08 97.29999999999998 46.39961850262279 1 10.925131139723408 -2.002861230329033
Long AEMD 100 1/25/2021 2.3760000000000003 1/26/2021 2.66 69.39999999999995 29.208754208754183 1 15.319865319865315 -1.9360269360269469
Long AEMD 100 1/28/2021 2.178 1/29/2021 2.26 89.19999999999999 40.95500459136823 1 12.94765840220386 -3.5812672176308475
Long AEMD 100 2/11/2021 2.7 2/12/2021 2.793 36.999999999999964 13.70370370370369 1 11.111111111111104 -10.740740740740742
Long AEMD 100 2/23/2021 2.358 2/24/2021 2.44 71.19999999999997 30.195080576759953 1 7.71840542832909 -5.852417302798978
Long AEMD 100 3/4/2021 1.971 3/5/2021 1.98 109.89999999999998 55.75849822425163 1 11.009639776763068 -10.95890410958905
Long AEMD 100 4/12/2021 1.854 4/13/2021 1.88 121.59999999999998 65.58791801510246 1 10.571736785329003 -0.7497303128371101
Long AEMD 100 5/13/2021 1.494 5/14/2021 1.55 157.6 105.48862115127176 1 13.119143239625163 -2.945113788487285
Long AEMD 100 6/10/2021 9.22 6/11/2021 6.91 -615 -66.70281995661604 1 4.121475054229924 -30.911062906724517
Long AEMD 100 6/14/2021 5.841 6/15/2021 5.95 -277.1 -47.440506762540664 1 15.219996575928782 -1.5579524054100358
Long AEMD 100 7/14/2021 3.717 7/15/2021 3.71 -64.70000000000002 -17.406510626849613 1 11.380145278450353 -6.10707559860102
Long AEMD 100 7/20/2021 4.437 7/21/2021 5.17 -136.70000000000005 -30.809105251295925 1 18.77394636015324 -3.5384268649988733
Long AEMD 100 7/22/2021 5.04 7/23/2021 5.83 -197.00000000000003 -39.087301587301596 1 23.412698412698404 -7.142857142857149
Long AEMD 100 8/11/2021 4.329 8/12/2021 4.37 -125.89999999999999 -29.08292908292908 1 13.652113652113657 -0.9009009009008941
Long AEMD 100 9/23/2021 4.2965 9/24/2021 4.17 -122.65000000000002 -28.546491330152456 1 3.572675433492382 -6.2027231467473465
Long AEMD 100 10/4/2021 3.375 10/5/2021 3.48 -30.500000000000014 -9.03703703703704 1 10.814814814814822 -0.7407407407407381
Rename
This is smelly code. Your line #25 accesses the Profit of a Position already closed.
You're not intended to access Profit in the middle of a Backtest. How can the final profit be known at that point? That property is meant mainly for Performance Visualizers. You can safely use ProfitAsOf however.
I'll add this note to the QuickRef entry for Profit, thanks for bringing it up.
I'll add this note to the QuickRef entry for Profit, thanks for bringing it up.
Hi Eugene, if the position profit access line is moved before the closing of the position as shown below, the PNL calculations still not correct. What is the correct way to access the position profits in the Execute() function?
CODE:
using WealthLab.Backtest; using System; using WealthLab.Core; using WealthLab.Indicators; using System.Drawing; using System.Collections.Generic; namespace WealthScript5 { public class MyStrategy : UserStrategyBase { //create indicators and other objects here, this is executed prior to the main trading loop public override void Initialize(BarHistory bars) { } //execute the strategy rules here, this is executed once for each bar in the backtest history public override void Execute(BarHistory bars, int bar) { if (OpenPositions.Count > 0) { foreach (Position pos in OpenPositions) { double save_pnl = pos.Profit; ClosePosition(pos, OrderType.Market); } } else { int number_of_shares = 100; Transaction tL = PlaceTrade(bars, TransactionType.Buy, OrderType.Limit, 0.9 * bars.Close[bar]); tL.Quantity = number_of_shares; } } //declare private variables belows } }
QUOTE:
Position Symbol Quantity "Entry Date" "Entry Price" "Exit Date" "Exit Price" Profit "Profit %" "Bars Held" "Entry Signal" "Exit Signal" "MFE %" "MAE %"
Long AEMD 100 12/18/2020 2.097 12/21/2020 2.08 97.29999999999998 46.39961850262279 1 10.925131139723408 -2.002861230329033
Long AEMD 100 1/25/2021 2.3760000000000003 1/26/2021 2.66 69.39999999999995 29.208754208754183 1 15.319865319865315 -1.9360269360269469
Long AEMD 100 1/28/2021 2.178 1/29/2021 2.26 89.19999999999999 40.95500459136823 1 12.94765840220386 -3.5812672176308475
Long AEMD 100 2/11/2021 2.7 2/12/2021 2.793 36.999999999999964 13.70370370370369 1 11.111111111111104 -10.740740740740742
Long AEMD 100 2/23/2021 2.358 2/24/2021 2.44 71.19999999999997 30.195080576759953 1 7.71840542832909 -5.852417302798978
Long AEMD 100 3/4/2021 1.971 3/5/2021 1.98 109.89999999999998 55.75849822425163 1 11.009639776763068 -10.95890410958905
Long AEMD 100 4/12/2021 1.854 4/13/2021 1.88 121.59999999999998 65.58791801510246 1 10.571736785329003 -0.7497303128371101
Long AEMD 100 5/13/2021 1.494 5/14/2021 1.55 157.6 105.48862115127176 1 13.119143239625163 -2.945113788487285
Long AEMD 100 6/10/2021 9.22 6/11/2021 6.91 -615 -66.70281995661604 1 4.121475054229924 -30.911062906724517
Long AEMD 100 6/14/2021 5.841 6/15/2021 5.95 -277.1 -47.440506762540664 1 15.219996575928782 -1.5579524054100358
Long AEMD 100 7/14/2021 3.717 7/15/2021 3.71 -64.70000000000002 -17.406510626849613 1 11.380145278450353 -6.10707559860102
Long AEMD 100 7/20/2021 4.437 7/21/2021 5.17 -136.70000000000005 -30.809105251295925 1 18.77394636015324 -3.5384268649988733
Long AEMD 100 7/22/2021 5.04 7/23/2021 5.83 -197.00000000000003 -39.087301587301596 1 23.412698412698404 -7.142857142857149
Long AEMD 100 8/11/2021 4.329 8/12/2021 4.37 -125.89999999999999 -29.08292908292908 1 13.652113652113657 -0.9009009009008941
Long AEMD 100 9/23/2021 4.2965 9/24/2021 4.17 -122.65000000000002 -28.546491330152456 1 3.572675433492382 -6.2027231467473465
Long AEMD 100 10/4/2021 3.375 10/5/2021 3.48 -30.500000000000014 -9.03703703703704 1 10.814814814814822 -0.7407407407407381
Use ProfitAsOf
Thank you Glitch for pointing that out. I use the following line but the profit/loss are not the same as the position closed profit [# of shares * (Exit Price - Entry Price)]. I guess this is peeking.
What is the proper way to get the most recent closed position [i] profits? Thanks.
CODE:
double save_pnl = pos.ProfitAsOf(bar);
What is the proper way to get the most recent closed position [i] profits? Thanks.
The proper way would be to use Profit if the Position is closed, otherwise use ProfitAsOf, like this:
CODE:
using WealthLab.Backtest; using System; using WealthLab.Core; using WealthLab.Indicators; using System.Drawing; using System.Collections.Generic; namespace WealthScript1 { public class MyStrategy : UserStrategyBase { //Initialize public override void Initialize(BarHistory bars) { } //Execute public override void Execute(BarHistory bars, int idx) { if (idx == bars.Count - 100) PlaceTrade(bars, TransactionType.Buy, OrderType.Market); if (idx == bars.Count - 90) PlaceTrade(bars, TransactionType.Buy, OrderType.Market); if (idx == bars.Count - 80) PlaceTrade(bars, TransactionType.Buy, OrderType.Market); if (idx == bars.Count - 70) PlaceTrade(bars, TransactionType.Buy, OrderType.Market); if (idx == bars.Count - 60) PlaceTrade(bars, TransactionType.Buy, OrderType.Market); if (idx == bars.Count - 50) PlaceTrade(bars, TransactionType.Buy, OrderType.Market); if (idx == bars.Count - 40) PlaceTrade(bars, TransactionType.Buy, OrderType.Market); if (idx == bars.Count - 55) PlaceTrade(bars, TransactionType.Sell, OrderType.Market); if (idx == bars.Count - 45) PlaceTrade(bars, TransactionType.Sell, OrderType.Market); if (idx == bars.Count - 10 || idx == bars.Count - 1) { WriteToDebugLog("---at Bar " + idx + " ---"); foreach (Position pos in GetPositions()) { string s = pos.EntryPrice.ToString("N2") + "\t" + pos.ExitPrice.ToString("N2") + "\t"; if (pos.IsOpen) s += pos.ProfitAsOf(idx).ToString("N2"); else s += pos.Profit.ToString("N2"); WriteToDebugLog(s); } } } //private members } }
Thank you Glitch.
Your Response
Post
Edit Post
Login is required