cordmaur / 38Cloud-Medium

Walk-through using u-net to detect clouds in satellite images.
11 stars 5 forks source link

Regarding ARIMA trading model and backtesting #1

Open iSevenDays opened 3 years ago

iSevenDays commented 3 years ago

Hello,

Thank you for the article on Medium https://medium.com/analytics-vidhya/a-step-by-step-implementation-of-a-trading-strategy-in-python-using-arima-garch-models-b622e5b3aa39

I found your results very great and I tried to reproduce the model with the backtrader strategy.

The strategy buys when signal == 1, and sells when signal == - 1. I tried both long-only with the exit strategy and long/short strategies.

From 10 000 cash, the strategy achieved negative returns.

I gladly share my code for your testing purposes. I hope you can spot the error in my strategy, but I just can't understand why does it perform badly.

I think we cannot simply compare S&P returns in a way that we treat our signal value 1 as if it is earned on that day this return. The trade must happen and only then we will know if we got the profit the next day.

The attached file SPY_signal.csv has an additional column that I copied from sp500_forecasts_new.csv.

SPY ticker copies S&P index, I used it as an alternative to the buying of the index (which we cannot).

autoencoder_strategy.py

from datetime import datetime
import backtrader as bt
import numpy as np
import yfinance as yf
from helpers.get_feed_for_csv_path import get_feed_for_csv_path
from sizers import MaxRiskSizer

# from strategies.BuyAndHold import BuyAndHold_Buy

class AutoEncoderStrategy(bt.Strategy):
    params = (
        ('sma1', 10),
        ('sma2', 30),
    )

    def __init__(self):
        pass

    def next(self):
        prev_signal = self.data.autoencoder_signal.get(ago=-1)
        prev_sell_signal = prev_signal[0] == -1 if len(prev_signal) else False
        # uncomment to check for at least two sell signals before selling a stock
        #sell_signal = prev_sell_signal and self.data.autoencoder_signal == -1
        sell_signal = self.data.autoencoder_signal == -1

        prev_buy_signal = prev_signal[0] == 1 if len(prev_signal) else False
        #buy_signal = prev_buy_signal and self.data.autoencoder_signal == 1
        buy_signal = self.data.autoencoder_signal == 1
        if buy_signal:

            if self.position.size < 0:
                self.close()
            if self.position.size > 0:
                return  # will not buy more
            self.buy()
        elif sell_signal:

            if self.position.size > 0:
                self.close()
            if self.position.size < 0:
                return  # will not sell more
            self.sell()

ticker = 'SPY'
datapathall = 'stocks_for_optimization_archive'
path = os.path.join(datapathall, f'{ticker}.csv')
fromdate = datetime(2002, 1, 1)
todate = datetime(2020, 12, 31)
data0 = get_feed_for_csv_path(path, fromdate=fromdate,
                              todate=todate)
import pandas as pd

data_csv = pd.read_csv(path, index_col=0)
data_csv.index = pd.to_datetime(data_csv.index)
filtered_period = data_csv.loc[fromdate: todate]['Close']
spy_signal_path = 'SPY_signal.csv'

cerebro = bt.Cerebro(stdstats=False)

if os.path.exists(spy_signal_path):
    data0 = get_feed_for_csv_path(spy_signal_path, fromdate=fromdate,
                                  todate=todate,
                                  has_signal=True)
else:
    pass

cerebro.broker.set_coc(True)
#cerebro.broker.set_coo(True)
# cerebro.addstrategy(St)
cerebro.addstrategy(AutoEncoderStrategy)
cerebro.adddata(data0)

# cerebro.addsizer(bt.sizers.FixedReverser)
cerebro.addsizer(MaxRiskSizer, risk=0.95)
# cerebro.addsizer(bt.sizers.FixedSize, stake=100)

cerebro.addobserver(bt.observers.Value)
cerebro.addobserver(bt.observers.Trades)
cerebro.addobserver(bt.observers.BuySell,
                    barplot=True)
from backtrader.analyzers import VWR
from BasicTradeStats import BasicTradeStats

cerebro.addanalyzer(BasicTradeStats, _name="BasicTradeStats")
vwrkwargs = {}
vwrkwargs['_name'] = 'vwr'
vwrkwargs['tau'] = 0.28  # price variability tolerance (risk tolerance)
vwrkwargs['sdev_max'] = 2.2  # maximum stdev of 2 is preferred, variability tolerance.
cerebro.addanalyzer(VWR, **vwrkwargs)

strategy = cerebro.run()[0]
print('Score: {}'.format(cerebro.broker.getvalue()))
vwr = strategy.analyzers.vwr.get_analysis()['vwr']  # returns variability weighted return
print('Score (vwr): {}, broker value: {}'.format(vwr, cerebro.broker.getvalue()))
print(strategy.analyzers.BasicTradeStats.print())
cerebro.plot()

BasicTradeStats.py https://gist.github.com/iSevenDays/ba44cf853e840559354dc2594eeb1f70

get_feed_for_csv_path.py https://gist.github.com/iSevenDays/378a75e392a92a602cc80ffe8df6ddd7

MaxRiskSizer.py

class MaxRiskSizer(bt.Sizer):
    params = (('risk', 0.95),)

    def __init__(self):
        if self.p.risk > 1 or self.p.risk < 0:
            raise ValueError('The risk parameter is a percentage which must be'
                'entered as a float. e.g. 0.5')

    def _getsizing(self, comminfo, cash, data, isbuy):
        #return comminfo.getsize(data.close[0], self.broker.get_value())
        return round(self.broker.get_value() * self.p.risk / data.close[0])
Bildschirmfoto 2021-02-14 um 14 07 15

SPY_signal.csv.zip

iSevenDays commented 3 years ago
Bildschirmfoto 2021-02-14 um 14 04 24