Metric shows incorrect value in portfolio statistics
I found an inconsistency in the portfolio statistics calculation. The Best Trade [%] metric displays 0.929219%, but when I analyze my actual trades using pf.trades.records_readable, the best trade is actually 1.157143% (Trade ID 153), not 0.93%.
context: My backtest uses negative capital (short-only strategy), which may be related to this calculation error.At first, I thought Portfolio Statistics might be using the current capital value for the calculation, but since the backtest keeps the account always in negative, this wouldn't be consistent either.
issue: On the trades plot/chart, many trades are also missing and not visible, which suggests a broader problem with how trades are being processed or displayed.
capital used: The capital I'm using is the same that I passed in init_cash=CAPITAL and for the calculation trades_clean['PnL %'], which is 1,000,000.
Statistics :
Start 2025-01-02 09:00:00
End 2025-11-05 00:55:00
Period 139 days 21:45:00
Start Value 1000000.0
End Value 968867.126509
Total Return [%] -3.113287
Benchmark Return [%] 19.514706
Max Gross Exposure [%] 517.763041
Total Fees Paid 0.0
Max Drawdown [%] 5.361855
Max Drawdown Duration 127 days 05:50:00
Total Trades 198
Total Closed Trades 198
Total Open Trades 0
Open Trade PnL 0.0
Win Rate [%] 19.191919
Best Trade [%] 0.929219 ← ERROR
Worst Trade [%] -0.323281
Avg Winning Trade [%] 0.141278
Avg Losing Trade [%] -0.054745
Avg Winning Trade Duration 0 days 01:25:23.684210526
Avg Losing Trade Duration 0 days 00:27:08.025477707
Profit Factor 0.790508
Expectancy -171.578702
Sharpe Ratio -1.128709
Calmar Ratio -1.477118
Omega Ratio 0.942158
Sortino Ratio -1.637499
Trades Records :
CAPITAL = 1000000.0 # Initial capital
trades = pf.trades.records_readable
trades_clean = trades.copy()
trades_clean['PnL %'] = trades_clean['PnL'] / CAPITAL * 100
top_10_trades = trades_clean.sort_values(by='PnL %', ascending=False).head(10)
print(top_10_trades.to_string(index=False))
PnL% ERROR ----------------------------------->------------------------------------------------------------------------------------------------->--------------------------------------------v
Exit Trade Id Column Size Entry Timestamp Avg Entry Price Entry Fees Exit Timestamp Avg Exit Price Exit Fees PnL Return Direction Status Position Id PnL %
153 0 7142.857143 2025-09-02 09:35:00 566.73 0.0 2025-09-02 10:25:00 565.1100 0.0 11571.428571 0.002859 Short Closed 153 1.157143
13 0 33333.333333 2025-01-24 09:35:00 532.24 0.0 2025-01-24 10:15:00 531.9300 0.0 10333.333333 0.000582 Short Closed 13 1.033333
169 0 3333.333333 2025-09-25 09:35:00 595.60 0.0 2025-09-25 11:55:00 592.6100 0.0 9966.666667 0.005020 Short Closed 169 0.996667
146 0 11111.111111 2025-08-19 09:35:00 577.23 0.0 2025-08-19 10:50:00 576.3700 0.0 9555.555556 0.001490 Short Closed 146 0.955556
53 0 50000.000000 2025-03-26 09:35:00 492.81 0.0 2025-03-26 09:45:00 492.6300 0.0 9000.000000 0.000365 Short Closed 53 0.900000
Trades PnL and Cumulative Return :
All trade statistics, including "Best Trade [%]", are computed from the internal trade records associated with the portfolio object (pf.trades). The stats are a direct aggregation of those records, where the percent return per trade is typically based on trade PnL relative to position sizing. In this context, the "Best Trade [%]" metric represents the highest positive value in the "Return" column of the closed pf.trades object. Each trade return is calculated relative to its own position value, so the statistic is self contained. I am not sure what your graphs are intended to show. Trade PnL is plotted correctly, and the equity curve probably just needs some zooming, but with positive trades it should generally track the benchmark.
Exactly, this is the Trade Return, calculated relative to the position value, and not the PnL relative to total capital.
I found 0.929219%
Thanks for clarifying!