Trading stategy Smash Day

La strategia smash day è stata ideata da Larry R. Williams, un trader professionista che ha guadagnato 11.300% nell’arco di un anno durante una gara. Sul sito di Oxford Capital Strategies Ltd ho trovato, i dettagli della strategia e una simulazione su 48 future nell’arco di 32 anni, i risultati delle simulazioni sono stimolanti. Le simulazioni che farò io saranno sul solito set di azioni FTSEMIB nel decennio 2010-2020.

Trovo la strategia interessante, non solo perché ideata da un trader in grado di guadagnare così tanto in così poco tempo, ma anche perché è una strategia basata su pattern e quindi non trend following. La stragia è ben descritta sul sito Oxfordstrat, e mi limito a provare a descriverne la logica. La strategia è simmetrica, ossia prevede ingressi long e short, in modo completamente simmetrico (basta sostituire massimi con minimi e segni). Qui di seguito una simulazione con A2A.

Simulazione 10Y sul titolo A2A

Nel caso di entrate long la condizione di ingresso è Long Trades: Close[i − 1] < Low[i − 2], ossia la barra chiude sotto il minimo della barra precedente, se si verifica questa condizione significa c’è stata una accelerazione del movimento verso il basse. Se questo succede possono succedere due cose, che poi sono le due situazioni che si verificano sempre. O il mercato sale o il mercato scende. Se sale il mercato, l’ipotesi sottostante la strategia, è che ci sia un’inversione e che l’accelerazione sia il preludio del cambio di direzione. Per capire se si verifica un cambio di direzione l’approccio usato è di inserire un buy stop sul massimo della candela “smash” . Il grafico qui riportato mostra come lo scenario di applicazione. La strategia prevede anche un filtro in ingresso, il Close[i − 1] > Close[i − Trend_Index], questo, in modo semplificato, significa che il marcato, rispetto al tempo trend_index, si è spostato verso l’alto. Possiamo vedere lo smash day come il momento di rintracciamento, ossia un minimo relativo al periodo in oggetto, incluso tra il tempo i-1 e i-trend_index, all’interno di un ampio movimento crescita.

Focus entrata smash day su posizione long – si evidenza che l’ipotesi sia di un rintracciamento

La strategia smash day scommette sul proseguimento del trend dato da Close[i − 1] > Close[i − Trend_Index], es entra quando c’è un rintracciamento che si ipotizza stia terminando perché c’è stata un’accelerazione di movimento in discesa dato da Close[i − 1] < Low[i − 2]. In questo scenario lo stop subito sotto il Low[i-1] è quasi doveroso, e in effeti fa parte delle strategia. Sia Williams che Oxford Capital Strategies Ltd hanno applicato la strategia al mercato dei future, in questo caso lo stop si posiziona un tick sotto il minimo, nel caso delle azioni questa possibilità non c’è, quindi posiziono il minimo -1% sotto il Low[i-1] nel caso di long e +1% sopra high[i-1] nel caso posizione short.

Nelle diverse simulazioni che ho fatto trovo coerenza con i dati di Oxfordstart, ossia che la strategia performa meglio con trend_index=80 e time_index=40. Il risultato non è però quello atteso, anche togliendo le commissioni il risultato è deludente. Dopo un po’ di prove mi sono accorto che la condizione di uscita veloce +-1% è troppo stretta e la strategia rimane soffocata.

Simulazione 10Y Smashday vs B&H su FTSEMIB

Anche provando diverse combinazioni il risultato, in assenza di commissioni, è deludente con uno sharpe <0.2 (da panico). Risultati simili sono stati ottenuti da Wpatte15 su tradingview. I dati non sono paragonabili con quelli di Oxfordstart per le evidenti differenze nella simulazione (10Y vs 32Y, 40 azioni vs 42 future). Qui di seguito il codice usato, come sempre la piattaforma du backtesting che uso è backtrader, opportunamente personalizzata.

class SmashDay(mts.MultiTickerStrategy):
'''Trade Setup: Long trades: Close[i − 1] < Low[i − 2]
             Short Trades: Close[i − 1] > High[i − 2].
Filter:    Long Trades: Close[i − 1] > Close[i − Trend_Index]. Trend_Index
        Short Trades: Close[i − 1] < Close[i − Trend_Index].
Trade Entry: Long Trades: A buy stop is placed one tick above the High[i − 1]
             Short Trades: A sell stop is placed one tick below the Low[i − 1]

Time Exit: nth day at the close, n = Time_Index. n=40
Quick Exit: Long Trades: A sell stop is placed one tick below the min(Low[k − 1], Low[k]).  Index: k ~ Entry Bar.
Stop Loss Exit: ATR_Length = 20; ATR_Stop = 6;
'''
params = dict(
    trend_index=80, #Trend_Index = [4, 80], Step = 2;
    time_index=40, #Time_Index = [1, 40], Step = 1;
    atr_lenght=20, #ATR_Length = 20;
    atr_stop=6, #ATR_Stop = 6;
) 
def __init__(self):
    super().__init__()
    self.dts = dict()
    self.stops = dict()
    self.exits = dict()
    self.inds = dict()
    #Carico i dati sharpe

    for i, d in enumerate(self.stocks):
        self.inds[d] = dict()
        self.inds[d]['atr'] = bt.indicators.ATR(d,period=self.p.atr_lenght)
        self.inds[d]['high'] = bt.indicators.Highest(d,period=self.p.trend_index)
        self.inds[d]['low'] = bt.indicators.Lowest(d,period=self.p.trend_index)                    


def next(self):
    for i, d in enumerate(self.stocks):
        dt, dn = self.datetime.date(), d._name

        if dn in self.dts:
            dat = self.dts[dn]
        else:
            dat = 0

        dt, dn = self.datetime.date(), d._name
        pos = self.getposition(d)
        if len(d)<self.p.time_index+1+self.p.trend_index:
            continue

        # prima gestisco il setup long/short se non sono già in posizione
        if not pos:
            #imposto la data operazione

            if d.close[0]<d.low[-1]: #setup long
                if d.close[0] > d.close[-self.p.trend_index]: #filtro long
                    mainorder = self.buy(d,exectype=bt.Order.Stop,price=d.high[0],valid=datetime.timedelta(days=1), transmit=False)
                    if not mainorder == None:
                        sexit = self.sell(d,price=d.low[0]-self.inds[d]['atr']*2, 
                                             size=mainorder.size, exectype=bt.Order.Stop,transmit=False, parent=mainorder, info={"mainorder":""})
                        stoploss = self.sell(d,price=d.high[0]-self.inds[d]['atr']*self.p.atr_stop, 
                                             size=mainorder.size, exectype=bt.Order.Stop,transmit=True, parent=mainorder, info={"mainorder":""})
                        self.stops[d]=stoploss
                        self.exits[d]=sexit
                        self.dts[d._name]=d.datetime[0]

            elif d.close[0]>d.high[-1]: #setup short
                if d.close[0] < d.close[-self.p.trend_index]: #filtro long
                    mainorder = self.sell(d,exectype=bt.Order.Stop,price=d.low[0],valid=datetime.timedelta(days=1), transmit=False)
                    if not mainorder == None:
                        sexit = self.buy(d,price=d.high[0]+self.inds[d]['atr']*2, 
                                             size=mainorder.size, exectype=bt.Order.Stop,transmit=False, parent=mainorder, info={"mainorder":""})
                        stoploss = self.buy(d,price=d.low[0]+self.inds[d]['atr']*self.p.atr_stop, 
                                             size=mainorder.size, exectype=bt.Order.Stop,transmit=True, parent=mainorder)
                        self.stops[d]=stoploss
                        self.exits[d]=sexit
                        self.dts[d._name]=d.datetime[0]
        else:
            if d.datetime[-self.p.time_index]>dat:
                self.close(d)
                if d in self.stops:
                    self.cancel(self.stops[d])
                    del self.stops[d]
                if d in self.exits:
                    self.cancel(self.exits[d])
                    del self.exits[d]