diff --git a/backtesting/_stats.py b/backtesting/_stats.py index f2bb4f7c..26141025 100644 --- a/backtesting/_stats.py +++ b/backtesting/_stats.py @@ -67,6 +67,24 @@ def compute_stats( 'ExitTime': [t.exit_time for t in trades], 'Tag': [t.tag for t in trades], }) + + # Retrieve the DataFrame of all indicators from the strategy instance + indicators_df = strategy_instance.get_indicators_dataframe() + + # Iterate over the trades and get the indicator values + for i, trade in trades_df.iterrows(): + entry_bar = trade['EntryBar'] + exit_bar = trade['ExitBar'] + + # Get the indicators at the entry and exit bars + entry_indicators = indicators_df.loc[ohlc_data.index[entry_bar]] + exit_indicators = indicators_df.loc[ohlc_data.index[exit_bar]] + + # Add the indicator values to the trades_df + for column in entry_indicators.index: + trades_df.at[i, f'Entry_{column}'] = entry_indicators[column] + trades_df.at[i, f'Exit_{column}'] = exit_indicators[column] + trades_df['Duration'] = trades_df['ExitTime'] - trades_df['EntryTime'] del trades diff --git a/backtesting/backtesting.py b/backtesting/backtesting.py index 9c168703..b0bde2e8 100644 --- a/backtesting/backtesting.py +++ b/backtesting/backtesting.py @@ -74,6 +74,14 @@ def _check_params(self, params): "can be optimized or run with.") setattr(self, k, v) return params + + def get_indicators_dataframe(self): + """ + Compile all indicator arrays into a DataFrame with the same index as the strategy data. + """ + + indicators_dict = {ind.name: ind for ind in self._indicators} + return pd.DataFrame(indicators_dict, index=self._data.index) def I(self, # noqa: E743 func: Callable, *args, diff --git a/test.py b/test.py new file mode 100644 index 00000000..aede446b --- /dev/null +++ b/test.py @@ -0,0 +1,28 @@ +from backtesting import Backtest, Strategy +from backtesting.lib import crossover + +from backtesting.test import SMA, GOOG + + +class SmaCross(Strategy): + n1 = 10 + n2 = 20 + + def init(self): + close = self.data.Close + self.sma1 = self.I(SMA, close, self.n1) + self.sma2 = self.I(SMA, close, self.n2) + + def next(self): + if crossover(self.sma1, self.sma2): + self.buy() + elif crossover(self.sma2, self.sma1): + self.sell() + + +bt = Backtest(GOOG, SmaCross, + cash=10000, commission=.002, + exclusive_orders=True) + +output = bt.run() +print(output['_trades']) \ No newline at end of file