backtesting.py
backtesting.py copied to clipboard
if sell at close, and close value is fixed, still the final equity value is strange
Expected Behavior
Equity value should be same as of starting equity value
Actual Behavior
It changes based on number of data points
Steps to Reproduce
- Hardcode the close value in dataframe
- run bt.run
- In strategy, do 1 buy and 1 sell, thats all
- see the result
`class SmaCross(Strategy): # Define the two MA lags as class variables # for later optimization n1 = 10 n2 = 20
ii = 0
boughtonce=False
def init(self):
# Precompute the two moving averages
self.sma1 = self.I(SMA, self.data.Close, self.n1)
self.sma2 = self.I(SMA, self.data.Close, self.n2)
def next(self):
# print(self.data.Close)
global ggg
self.ii += 1
# if ggg == False:
if self.ii == 10:
self.buy()
ggg = True
if self.ii == 11:
self.sell()
return
`
GOOG["Close"] = 105
GOOG = GOOG[:300]
print(GOOG)
bt = Backtest(GOOG, SmaCross, cash=1000, commission=0)
stats = bt.run()
print(stats, "\n", stats._trades)
Start 2004-08-19 00:00:00
End 2005-10-25 00:00:00
Duration 432 days 00:00:00
Exposure Time [%] 90.0
Equity Final [$] 2504.86
Equity Peak [$] 2504.86
Return [%] 150.486
Buy & Hold Return [%] 0.0
Return (Ann.) [%] 116.260949
Volatility (Ann.) [%] 1256.562577
Sharpe Ratio 0.092523
Sortino Ratio 7.012154
Calmar Ratio 6.437483
Max. Drawdown [%] -18.06
Avg. Drawdown [%] -18.06
Max. Drawdown Duration 390 days 00:00:00
Avg. Drawdown Duration 390 days 00:00:00
# Trades 1
Win Rate [%] 100.0
Best Trade [%] 164.357798
Worst Trade [%] 164.357798
Avg. Trade [%] 164.357798
Max. Trade Duration 389 days 00:00:00
Avg. Trade Duration 389 days 00:00:00
Profit Factor NaN
Expectancy [%] 164.357798
SQN NaN
_strategy SmaCross
_equity_curve Equ...
_trades Size EntryBa...
dtype: object
Size EntryBar ExitBar EntryPrice ExitPrice PnL ReturnPct EntryTime ExitTime Duration
0 7 30 299 130.8 345.78 1504.86 1.643578 2004-10-01 2005-10-25 389 days
Additional info
Backtesting version: 0.3.3
-
bokeh.__version__: - OS: windows
Yeah I noticed the same. It seems the equity and cash attributes are not being updated correctly for each new candle. I think the fix would be as follows. Under the Broker class we have to fix:
- the equity property should be changed from
@property
def equity(self) -> float:
return self._cash + sum(trade.pl for trade in self.trades)
to
@property
def equity(self) -> float:
return self._cash + sum(trade.value for trade in self.trades)
-
on the _close_trade method we should replace
self._cash += trade.plforself._cash += trade.value -
on the _open_trade method we should add at the end the following line:
self._cash = self._cash - trade.size*trade.entry_price