kernc / backtesting.py

:mag_right: :chart_with_upwards_trend: :snake: :moneybag: Backtest trading strategies in Python.
https://kernc.github.io/backtesting.py/
GNU Affero General Public License v3.0
5.39k stars 1.05k forks source link

How to optimize parameters using pandas ta indicator Supertrend #1025

Closed azimgiant closed 1 year ago

azimgiant commented 1 year ago

I wanted to backtest the supertrend that is on pandas_ta however I am having trouble using the bt.optimize function. when using talib it is quite straight forward however, with pandas_ta you need to first create the indicator using the def function and then bring it inside the self init function. When I run it I get this error: TypeError: 'float' object cannot be interpreted as an integer. Not sure what I am doing wrong. Does bt.optimize only take whole numbers?

Code: import pandas as pd from backtesting import Backtest, Strategy from backtesting.lib import crossover, plot_heatmaps import yfinance as yf yf.pdr_override() import numpy as np import pandas_ta as ta import talib import seaborn as sns

df = yf.download('SPY', period='60d', interval='2m') df=df[df.Volume!=0]

def indicator(data): supertrend = ta.supertrend(close=df.Close, high=df.High, low=df.Low, length=4, multiplier=0.7) if supertrend is None: supertrend = np.zeros(len(data)) else: supertrend = supertrend[:len(data)] # Truncate to match data length supertrend = np.nan_to_num(supertrend) # Replace NaN values with zeros return supertrend

class ST(Strategy):

 length = 4
muliplier = 0.7

def init(self):
    self.supertrend = self.I(indicator, self.data)
    self.position_open = False

def next(self):
    if crossover(self.data.Close, self.supertrend) and not self.position_open:
        self.buy()
        self.position_open = True

    if crossover(self.supertrend, self.data.Close) and self.position_open:
        self.position.close()
        self.position_open = False

    if crossover(self.supertrend, self.data.Close) and not self.position.is_short:
        self.sell()
        self.position_open = True

    if crossover(self.data.Close, self.supertrend) and self.position.is_short:
        self.position.close()
        self.position_open = False

bt = Backtest(df, ST cash=10_000) stats=bt.run() stats = bt.optimize( multiplier = range(0.5, 3.0, 0.5), length = range(5,50,3), maximize = 'Equity Final [$]', )

eervin123 commented 1 year ago

there were a couple of issues with your code, I fixed them up in the following notebook.

https://colab.research.google.com/drive/1-sKjJOqPR6PSRQ9CMIf8M21maNyuyuav?usp=sharing

1.) Your float issue has to do with the range function. I know there is a way to do this properly but I just use np.arange(0.5, 3.0, 0.5).tolist() which does the same thing and works just fine.

2.) You weren't using the multiplier class variable in your function. In order to pass different parameters for the optimization you needed to pass those different values to your function. (also there was a typo in your original code muliplier that one had me stumped for a min) ;)

Now you can see the two different simulations compared against one another. Hopefully this is helpful.

azimgiant commented 1 year ago

Thank you!!! I tried to solve this all day yesterday with no luck. Surprised how easy this was. I was thinking of a much more complex solution. Thanks again!

eervin123 commented 1 year ago

You bet, I remember the early days when I first jumped in to this lib. I was pretty new to python at the time and @kernc was incredibly gracious with me, I could picture his eyes rolling every time I asked a question but he never griped. :) So, now I try to jump in whenever I can.