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

Plotting Signals of custom strategy from File #1023

Closed algomaschine closed 1 year ago

algomaschine commented 1 year ago

Expected Behavior

I'd like to test my EOD signals from file, pls advise.

image

1) PnL (equity) curve (ideally) in the same units as the price 1.1) PnL in USD using leverage would be great too 2) Entry signals (red arrow above the bar if it's SELL and GREEN arrow below the bar if it's BUY)

Actual Behavior

image

Steps to Reproduce

Use the file attached as signals (if 'predict' column > 0, then it's Close-Open for that date, otherwise Open-Close)


from backtesting import Backtest, Strategy
from backtesting.lib import crossover
import pandas as pd
import yfinance as yf

class SignalBasedStrategy(Strategy):
    def init(self):
        self.buy_signal = self.data.predict > 0  # Buy signal
        self.sell_signal = self.data.predict < 0  # Sell signal

    def next(self):
        if not self.position:  # Check if there's an open position
            if self.buy_signal[-1]:  # Check the last signal
                self.buy()
            elif self.sell_signal[-1]:  # Check the last signal
                self.sell()
        else:  # if there's an open position
            if (self.position.is_long and self.sell_signal[-1]) or (self.position.is_short and self.buy_signal[-1]):
                self.position.close()

# Load signals
df = pd.read_csv('forecast_StackedEnsemble_BestOfFamily_2_AutoML_98_20230502_55040.csv', parse_dates=['day'], index_col='day')

# Download BTC price data
btc_data = yf.download('BTC-USD', start=df.index.min(), end=df.index.max())
btc_data = btc_data.resample('D').apply({'Open': 'first', 'High': 'max', 'Low': 'min', 'Close': 'last', 'Volume': 'sum'})

# Merge with existing data
df = pd.concat([btc_data, df], axis=1).dropna()

# Initialize Backtest
bt = Backtest(df, SignalBasedStrategy, commission=.002,
              exclusive_orders=True)

# Run Backtest
stats = bt.run()
bt.plot()
[forecast_StackedEnsemble_BestOfFamily_2_AutoML_98_20230502_55040.csv](https://github.com/kernc/backtesting.py/files/12063529/forecast_StackedEnsemble_BestOfFamily_2_AutoML_98_20230502_55040.csv)
algomaschine commented 1 year ago

this is the content of signals file:

day,predict,Actual 2023-01-02,0.0531879749438913,20.0 2023-01-03,39.92000234218711,-10.0 2023-01-04,19.994896959853136,10.0 2023-01-05,-9.918166245145391,10.0 2023-01-06,10.017815606825405,10.0 2023-01-07,10.02356454268156,-10.0 2023-01-08,10.023118153464806,20.0 2023-01-09,-9.903714691790428,20.0 2023-01-10,19.97901181408831,40.0 2023-01-11,19.98812552797062,10.0 2023-01-12,39.999132289768006,20.0 2023-01-13,10.026090514503265,40.0 2023-01-14,20.00626340414205,-0.0 2023-01-15,40.011850851668825,20.0 2023-01-16,0.0603337575476009,10.0 2023-01-17,19.987213278004155,30.0 2023-01-18,10.018793345426504,-20.0 2023-01-19,29.95040042828049,30.0 2023-01-20,-19.90895005296049,30.0 2023-01-21,29.93289440361552,40.0 2023-01-22,29.9905669087484,-10.0 2023-01-23,39.9364233210416,30.0 2023-01-24,-9.912966720431776,-20.0 2023-01-25,29.948417667838367,20.0 2023-01-26,-19.861365512715416,-20.0 2023-01-27,20.0123791478764,30.0 2023-01-28,-19.862371463267014,-20.0 2023-01-29,29.94680178628022,40.0 2023-01-30,-19.88149210975736,-40.0 2023-01-31,39.9851963685262,30.0 2023-02-01,-39.82368450384777,0.0 2023-02-02,29.94401056930907,-20.0 2023-02-03,-4.629673506619665,-20.0 2023-02-04,-19.870930425611046,10.0 2023-02-05,-19.84527646682563,-30.0 2023-02-06,10.014942735682158,0.0 2023-02-07,-29.803197026044643,40.0 2023-02-08,0.0600208487794178,-40.0 2023-02-09,39.95606721648065,-30.0 2023-02-10,-39.82368450384777,-30.0 2023-02-11,-29.81676638781579,20.0 2023-02-12,-29.81357523572509,20.0 2023-02-13,19.984754269483243,-20.0 2023-02-14,19.98638590339281,40.0 2023-02-15,-19.856808255859544,40.0 2023-02-16,39.94280588839524,-10.0 2023-02-17,40.00200967473767,40.0 2023-02-18,-9.90441039251504,10.0 2023-02-19,39.96290790357287,-20.0 2023-02-20,10.017245248892037,20.0 2023-02-21,-19.871367486414744,-40.0 2023-02-22,20.00714062425026,-20.0 2023-02-23,-39.81603472117447,-30.0 2023-02-24,-19.89542820107492,-40.0 2023-02-25,-29.77955749523849,-30.0 2023-02-26,-39.82368450384777,30.0 2023-02-27,-29.80341613882072,-20.0 2023-02-28,29.93359287736959,-10.0 2023-03-01,-19.84853961632998,0.0 2023-03-02,-9.905260951867366,-20.0 2023-03-03,-4.626147150930774,-10.0 2023-03-04,-19.86965331549449,-30.0 2023-03-05,-9.952550418193889,10.0 2023-03-06,-29.80349886701165,10.0 2023-03-07,10.017815606825405,-40.0 2023-03-08,10.184471703407812,-20.0 2023-03-09,-39.83087498819919,-40.0 2023-03-10,-19.860302952605245,-0.0 2023-03-11,-39.82368450384777,-0.0 2023-03-12,0.0571380745834496,30.0 2023-03-13,0.0586861711179183,30.0 2023-03-14,29.98691330443921,20.0 2023-03-15,30.002773573722067,-30.0 2023-03-16,20.00448142281073,40.0 2023-03-17,-29.81854428800514,40.0 2023-03-18,39.96468825571592,-10.0 2023-03-19,40.01145772656749,30.0 2023-03-20,-9.90385717058787,10.0 2023-03-21,29.97111424383282,30.0 2023-03-22,10.015747760828493,0.0 2023-03-23,29.948522023686703,30.0 2023-03-24,-4.629486700376437,-40.0 2023-03-25,29.9531423122362,-10.0 2023-03-26,-39.82368450384777,30.0 2023-03-27,-9.908590521957263,-30.0 2023-03-28,29.9364688930547,10.0 2023-03-29,-29.82172718009442,30.0 2023-03-30,10.015747760828493,-20.0 2023-03-31,30.00243726451874,20.0 2023-04-01,-19.916271387108885,-20.0 2023-04-02,19.97550487154984,-40.0 2023-04-03,-19.859182628106318,-0.0 2023-04-04,-39.83087498819919,30.0 2023-04-05,0.0548355613735738,-30.0 2023-04-06,29.946565410504665,-10.0 2023-04-07,-29.81568823971004,-20.0 2023-04-08,-9.895039663617196,-10.0 2023-04-09,-19.88280095588092,0.0 2023-04-10,-9.92123144018847,30.0 2023-04-11,9.902424652971495,30.0 2023-04-12,29.99281232127636,-10.0 2023-04-13,29.94317790420328,40.0 2023-04-14,-9.896011534125604,-20.0 2023-04-15,39.9474760535489,-20.0 2023-04-16,-19.871577942712257,20.0 2023-04-17,-19.852574357033756,-30.0 2023-04-18,19.980588766781704,40.0 2023-04-19,-30.281236307110333,-40.0 2023-04-20,40.01150479934485,-40.0 2023-04-21,-39.82368450384777,-40.0 2023-04-22,-39.82368450384777,30.0 2023-04-23,-39.83087498819919,-10.0 2023-04-24,29.966560548565084,-20.0 2023-04-25,-9.915071082111153,20.0 2023-04-26,-19.874894842214463,10.0 2023-04-27,20.02058496940594,30.0 2023-04-28,10.030820201213425,-20.0 2023-04-29,29.94370938745607,-30.0 2023-04-30,-19.828879545998745,30.0 2023-05-01,-29.793561924128717,-40.0 2023-05-02,29.964518277724512,40.0 2023-05-03,-39.82368450384777,0.0 2023-05-04,39.94795723145784,-20.0 2023-05-05,0.0561968516648145,20.0 2023-05-06,-19.922459658743342,-40.0 2023-05-07,19.998391491369503,0.0 2023-05-08,-39.83087498819919,-40.0 2023-05-09,0.0531879749438913,-0.0 2023-05-10,-39.82368450384777,-0.0 2023-05-11,0.0571380745834496,-40.0 2023-05-12,0.0563836579080425,-10.0 2023-05-13,-39.82368450384777,20.0 2023-05-14,-9.922720864784456,30.0 2023-05-15,19.99536933022716,10.0 2023-05-16,29.948812624776217,-20.0 2023-05-17,10.01814976393358,0.0 2023-05-18,-19.87001256427161,-30.0 2023-05-19,-4.628125410085196,10.0 2023-05-20,-29.8233455763844,40.0 2023-05-21,10.017815606825405,-40.0 2023-05-22,39.96557593136947,30.0 2023-05-23,-39.82368450384777,-10.0 2023-05-24,29.94478299412204,-40.0 2023-05-25,-9.896729900995386,30.0 2023-05-26,-39.82368450384777,30.0 2023-05-27,29.941781521134768,10.0 2023-05-28,29.964591434758407,40.0 2023-05-29,10.015747760828493,-40.0 2023-05-30,40.00804122837906,-0.0 2023-05-31,-39.83087498819919,-30.0 2023-06-01,0.0612382725891466,0.0 2023-06-02,-29.79219899963372,30.0 2023-06-03,0.0563836579080425,-10.0 2023-06-04,29.93650009186914,30.0 2023-06-05,-5.217186415494395,-40.0 2023-06-06,29.94783275633972,30.0 2023-06-07,-39.83087498819919,-40.0 2023-06-08,29.99909294280814,30.0 2023-06-09,-39.80970784839098,-0.0 2023-06-10,29.949905222108196,-10.0 2023-06-11,0.05473607147836,20.0 2023-06-12,-5.221742277396559,0.0 2023-06-13,19.986335596921887,-20.0 2023-06-14,0.0596901760546779,-20.0 2023-06-15,-19.85113493440095,10.0 2023-06-16,-19.88662125198809,30.0 2023-06-17,10.025670254936037,10.0 2023-06-18,29.984361319366744,0.0 2023-06-19,10.022016446147092,30.0 2023-06-20,0.05473607147836,20.0 2023-06-21,29.967053622873248,40.0 2023-06-22,20.034224112956675,-20.0 2023-06-23,40.00482687407769,30.0 2023-06-24,-19.87008246126653,-20.0 2023-06-25,29.96196866559773,-30.0 2023-06-26,-19.86909173424233,-10.0 2023-06-27,-29.79225014749501,30.0 2023-06-28,-9.90993202927629,-40.0 2023-06-29,29.945310879635727,20.0 2023-06-30,-39.81603472117447,-10.0 2023-07-01,19.99149707397507,30.0 2023-07-02,-9.903236076908524,10.0 2023-07-03,29.94131624277337,20.0 2023-07-04,10.016615760187571,-40.0 2023-07-05,20.000671799520976,-30.0 2023-07-06,-44.50909556381037,-20.0 2023-07-07,-29.8328121909295,30.0 2023-07-08,-19.892542193636213,-20.0 2023-07-09,29.95123578101156,-20.0 2023-07-10,-19.895022819360896,40.0 2023-07-11,-19.87057171918287,20.0 2023-07-12,39.975811973494494,-10.0 2023-07-13,19.964531385667723,40.0

peacerider commented 1 year ago

I got the following warning.

UserWarning: Some prices are larger than initial cash value. Note that fractional trading is not supported. If you want to trade Bitcoin, increase initial cash, or trade μBTC or satoshis instead (GH-134).

Example

# Initialize Backtest
bt = Backtest(df, SignalBasedStrategy, cash=1000000 , commission=.002,
              exclusive_orders=True)
eervin123 commented 1 year ago

Regarding the warning that is unrelated, just follow the instructions, ie. add more initial cash or divide your data by 100,000 to make the price very small.

Entry signals (red arrow above the bar if it's SELL and GREEN arrow below the bar if it's BUY)

I don't think they will be red or green but I'm pretty sure they'll be plotted as bars. I use a -1, 1, 0 system and they are very clearly visible. In your case you might want to convert your signals from bool to +1, -1, 0 for plotting purposes.

As for your signals not plotting you need to wrap that column with an indicator so the library knows to treat it as a signal for plotting etc. See the quick example where @kernc explains the following

In init(), we declare and compute indicators indirectly by wrapping them in self.I(). The wrapper is passed a function (our SMA function) along with any arguments to call it with (our close values and the MA lag). Indicators wrapped in this way will be automatically plotted, and their legend strings will be intelligently inferred.

So, to do this with a simple column in your dataframe just create a simple function that returns that column. In my case the column name is signal.

def SIGNAL(self):
     return self.data.signal

def init(self):
    super().init()
    self.signal = self.I(self.SIGNAL)
    if self.signal == -1
         self.sell()
    elif self.signal == 1
... 

Regarding your other questions.

PnL (equity) curve (ideally) in the same units as the price

I'm not exactly sure if this was what you are going for but if you want to plot equity in money not percent then set bt.plot(relative_equity=False)

1.1) PnL in USD using leverage would be great too

To use leverage simply set margin=0.5 this would be 2x leverage. 0.1 would be 10x leverage. bt = Backtest(df, SignalBasedStrategy, cash=1000000 , margin=0.5, commission=.002, exclusive_orders=True)

Hopefully this is helpful.

kernc commented 1 year ago

Indeed, something like

def init(self):
    self.buy_signal = self.I(lambda: self.data.predict > 0, name='entry')