Buy & Hold la stretegia vincente

Ho analizzato il comportamento di un paniere di titoli per vederne il comportamento complessivo nel tempo. In particolare già in passato avevo analizzato la strategia buy & hold verificando che è difficile trovare una strategia di trading migliore di quella B&H.

Con l’occasione ho voluto sperimentare backtesting alternativi rispetto a Backtrader. Credo che Backtrader sia una delle piattaforme migliori sul mercato, in grado di simulare in odo molto fedele il comportamento dei broker e anche di agganciarsi a broker reali per algotrading direttamente a mercato. Per fare però delle semplici simulazioni occorre qualcosa di più snello, una volta verificata la bontà di una strategia attraverso un sistema snello si può implementare su backtrader che aggiunge tutti gli orpelli onerosi quali slippage, tobin tax, commissioni e vincoli su ordini in base alla disponibilità economica.

Per implementare un metodo snello ci sono tre framework che ci aiutano:

  • ta-lib: una collezione di funzione per l’analisi tecnica, comprende anche l’identificazione di candlestick e numerosi indicatori
  • pandas-ta, estende in modo semplice le funzioni di pandas aggiungendo diverse funzioni per applciare sul set di dati gli indicatori ta-lib
  • vector-bt: una libreria semplice di backtesting che consente di definire gli entry e exit point di una strategia. In base a questi elementi

Tornando alla strategia B&H, le prime prove fatte coinvolgono la librerie pandas-ta e, dato l’insieme dei titoli, calcolo in guadagno nel tempo per ciascun titolo. Il codice è molto semplice, per prima cosa carico i dati:

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import pandas_ta as ta
df = pd.DataFrame() # Empty DataFrame
assets =["A2A.MI", "AMP.MI", "ATL.MI", "AZM.MI", "BGN.MI", "BMED.MI", "BAMI.MI", "BPE.MI", "BZU.MI",
"CPR.MI", "CNHI.MI", "DIA.MI", "ENEL.MI", "ENI.MI", "EXO.MI", "RACE.MI", "FBK.MI", "G.MI",
"HER.MI", "IP.MI", "ISP.MI", "INW.MI", "IG.MI", "LDO.MI", "MB.MI", "MONC.MI", "NEXI.MI",
"PIRC.MI", "PST.MI", "PRY.MI", "REC.MI", "SPM.MI", "SRG.MI", "STLA.MI", "STM.MI", "TIT.MI",
"TEN.MI", "TRN.MI", "UCG.MI", "UNI.MI"]
dfs = {}
for name in assets:
dfs[name]=pd.read_json(f"{name}_reg_5-60")
dfs[name].set_index(pd.DatetimeIndex(dfs[name]["Date"]), inplace=True)

I dati lì ho in locale per comodità, ma volendoli caricare con pandas-ta è sufficiente sostituire con questo:

dfs[name]=df.ta.ticker(name)

A questo punto calcolo il guadagno nel tempo per ciascun asset e creo una tabella di sintesi:

assets =["A2A.MI", "AMP.MI", "ATL.MI", "AZM.MI", "BGN.MI", "BMED.MI", "BAMI.MI", "BPE.MI", "BZU.MI", 
      "CPR.MI", "CNHI.MI", "DIA.MI", "ENEL.MI", "ENI.MI", "EXO.MI", "RACE.MI", "FBK.MI", "G.MI", 
      "HER.MI", "IP.MI", "ISP.MI", "INW.MI", "IG.MI", "LDO.MI", "MB.MI", "MONC.MI", "NEXI.MI", 
      "PIRC.MI", "PST.MI", "PRY.MI", "REC.MI", "SPM.MI", "SRG.MI", "STLA.MI", "STM.MI", "TIT.MI", 
      "TEN.MI", "TRN.MI", "UCG.MI", "UNI.MI"]

gains = {}
res = pd.DataFrame(columns=["start","end","initial","final","percret","logret"])
total = 0
for name in assets:
    df =dfs[name].copy()
    df.ta.log_return(cumulative=True, append=True)
    df.ta.percent_return(cumulative=True, append=True)
    gains[name] = df["CUMPCTRET_1"][len(df)-1]
    total +=gains[name]
    print(f"Guadagno {name} dal {df.index[0]:%m/%d/%Y} al {df.index[-1]:%m/%d/%Y} di {gains[name]*100:.02f} %")
    p = pd.DataFrame([[df.index[0],df.index[-1],df["Close"][0],df["Close"][len(df)-1],
                             df["CUMPCTRET_1"][len(df)-1],df["CUMLOGRET_1"][len(df)-1]]],
                            columns=["start","end","initial","final","percret","logret"],
                            index=[name])
    
    res = pd.concat([res,p])

print(f"Guadagno medio {total*100/len(gains)} %")



Il risultato mostra come alcuni titoli hanno performato molto, altri molto male:

Guadagno A2A.MI dal 01/03/2000 al 05/05/2022 di -56.15 %
Guadagno AMP.MI dal 10/01/2001 al 05/05/2022 di 2256.41 %
Guadagno ATL.MI dal 09/22/2003 al 05/05/2022 di 88.79 %
Guadagno AZM.MI dal 07/07/2004 al 10/27/2021 di 634.51 %
Guadagno BGN.MI dal 11/15/2006 al 05/05/2022 di 244.51 %
Guadagno BMED.MI dal 01/03/2000 al 05/05/2022 di -45.43 %
Guadagno BAMI.MI dal 01/03/2000 al 05/05/2022 di -97.02 %
Guadagno BPE.MI dal 01/03/2000 al 05/05/2022 di -88.98 %
Guadagno BZU.MI dal 01/03/2000 al 05/05/2022 di 62.49 %
Guadagno CPR.MI dal 07/06/2001 al 05/05/2022 di 6961.02 %
Guadagno CNHI.MI dal 09/30/2013 al 05/05/2022 di 44.78 %
Guadagno DIA.MI dal 07/19/2007 al 05/05/2022 di 868.90 %
Guadagno ENEL.MI dal 01/03/2000 al 05/05/2022 di 1.35 %
Guadagno ENI.MI dal 01/03/2000 al 05/05/2022 di 28.91 %
Guadagno EXO.MI dal 01/03/2000 al 05/05/2022 di 81.08 %
Guadagno RACE.MI dal 01/04/2016 al 05/05/2022 di 339.43 %
Guadagno FBK.MI dal 07/02/2014 al 05/05/2022 di 223.87 %
Guadagno G.MI dal 01/03/2000 al 05/05/2022 di -36.47 %
Guadagno HER.MI dal 03/28/2003 al 05/05/2022 di 95.84 %
Guadagno IP.MI dal 01/03/2000 al 05/05/2022 di 746.27 %
Guadagno ISP.MI dal 01/03/2000 al 05/05/2022 di -44.43 %
Guadagno INW.MI dal 06/22/2015 al 05/05/2022 di 151.35 %
Guadagno IG.MI dal 11/07/2016 al 05/05/2022 di 54.03 %
Guadagno LDO.MI dal 07/18/2005 al 05/05/2022 di -29.44 %
Guadagno MB.MI dal 01/03/2000 al 05/05/2022 di 8.58 %
Guadagno MONC.MI dal 12/16/2013 al 05/05/2022 di 197.93 %
Guadagno NEXI.MI dal 04/18/2019 al 05/05/2022 di 12.75 %
Guadagno PIRC.MI dal 10/04/2017 al 05/05/2022 di -31.25 %
Guadagno PST.MI dal 10/27/2015 al 05/05/2022 di 36.39 %
Guadagno PRY.MI dal 05/03/2007 al 05/05/2022 di 98.21 %
Guadagno REC.MI dal 01/03/2000 al 05/05/2022 di 4034.14 %
Guadagno SPM.MI dal 01/03/2000 al 05/05/2022 di -75.09 %
Guadagno SRG.MI dal 12/06/2001 al 05/05/2022 di 119.76 %
Guadagno STLA.MI dal 01/03/2000 al 05/05/2022 di 58.00 %
Guadagno STM.MI dal 01/03/2000 al 05/05/2022 di -30.69 %
Guadagno TIT.MI dal 01/03/2000 al 05/05/2022 di -93.51 %
Guadagno TEN.MI dal 12/17/2002 al 05/05/2022 di 704.95 %
Guadagno TRN.MI dal 06/23/2004 al 05/05/2022 di 337.61 %
Guadagno UCG.MI dal 01/03/2000 al 05/05/2022 di -93.24 %
Guadagno UNI.MI dal 01/03/2000 al 05/05/2022 di -90.98 %
Guadagno medio 441.97885234421864 %

Il peggiore è stato BAMI con una perdita del 97.02% mentre il migliore è stato CPR con un guadagno del 6961.02%. Per capire l’entità dei due scostamenti, se avessi investito 10.000€ su BAMI oggi avrei 298€, mentre se avessi investito 10.000€ su CPR oggi avrei 706.102€. Sottolineo la precisazione, l’analisi è semplificata, non si considerano un insieme di fattori, oltre ai costi operativi evidenziati sopra (commissioni, ecc. ) non considero i dividendi distribuiti, gli split, gli aumenti di capitale e tutte quelle operazioni di capitale che portano guadagni o perdite all’azionista.

Il punto da capire è se il risultato di 6961.02% è da considerarsi un anomalia o meno. Vediamo altri outperformer come AMP e REC, anche questi sono un’eccezione. Il primo passo per affrontare quest’analisi è il passaggio alla scala logaritmica. Un titolo che perde ogni giorno l’ 1% perde in termini monetari assoluti sempre meno, un titolo che guadagna 1% ogni giorno guadagna in termini monetari assoluti sempre di più. Rispetto alla scala logaritmica i guadagni (e le perdite) seguono un andamento lineare. I risultati visti sopra, calcolati secondo una scala logaritmica diventano:

Guadagno A2A.MI dal 01/03/2000 al 05/05/2022 di -0.82 log
Guadagno AMP.MI dal 10/01/2001 al 05/05/2022 di 3.16 log
Guadagno ATL.MI dal 09/22/2003 al 05/05/2022 di 0.64 log
Guadagno AZM.MI dal 07/07/2004 al 10/27/2021 di 1.99 log
Guadagno BGN.MI dal 11/15/2006 al 05/05/2022 di 1.24 log
Guadagno BMED.MI dal 01/03/2000 al 05/05/2022 di -0.61 log
Guadagno BAMI.MI dal 01/03/2000 al 05/05/2022 di -3.51 log
Guadagno BPE.MI dal 01/03/2000 al 05/05/2022 di -2.21 log
Guadagno BZU.MI dal 01/03/2000 al 05/05/2022 di 0.49 log
Guadagno CPR.MI dal 07/06/2001 al 05/05/2022 di 4.26 log
Guadagno CNHI.MI dal 09/30/2013 al 05/05/2022 di 0.37 log
Guadagno DIA.MI dal 07/19/2007 al 05/05/2022 di 2.27 log
Guadagno ENEL.MI dal 01/03/2000 al 05/05/2022 di 0.01 log
Guadagno ENI.MI dal 01/03/2000 al 05/05/2022 di 0.25 log
Guadagno EXO.MI dal 01/03/2000 al 05/05/2022 di 0.59 log
Guadagno RACE.MI dal 01/04/2016 al 05/05/2022 di 1.48 log
Guadagno FBK.MI dal 07/02/2014 al 05/05/2022 di 1.18 log
Guadagno G.MI dal 01/03/2000 al 05/05/2022 di -0.45 log
Guadagno HER.MI dal 03/28/2003 al 05/05/2022 di 0.67 log
Guadagno IP.MI dal 01/03/2000 al 05/05/2022 di 2.14 log
Guadagno ISP.MI dal 01/03/2000 al 05/05/2022 di -0.59 log
Guadagno INW.MI dal 06/22/2015 al 05/05/2022 di 0.92 log
Guadagno IG.MI dal 11/07/2016 al 05/05/2022 di 0.43 log
Guadagno LDO.MI dal 07/18/2005 al 05/05/2022 di -0.35 log
Guadagno MB.MI dal 01/03/2000 al 05/05/2022 di 0.08 log
Guadagno MONC.MI dal 12/16/2013 al 05/05/2022 di 1.09 log
Guadagno NEXI.MI dal 04/18/2019 al 05/05/2022 di 0.12 log
Guadagno PIRC.MI dal 10/04/2017 al 05/05/2022 di -0.37 log
Guadagno PST.MI dal 10/27/2015 al 05/05/2022 di 0.31 log
Guadagno PRY.MI dal 05/03/2007 al 05/05/2022 di 0.68 log
Guadagno REC.MI dal 01/03/2000 al 05/05/2022 di 3.72 log
Guadagno SPM.MI dal 01/03/2000 al 05/05/2022 di -1.39 log
Guadagno SRG.MI dal 12/06/2001 al 05/05/2022 di 0.79 log
Guadagno STLA.MI dal 01/03/2000 al 05/05/2022 di 0.46 log
Guadagno STM.MI dal 01/03/2000 al 05/05/2022 di -0.37 log
Guadagno TIT.MI dal 01/03/2000 al 05/05/2022 di -2.73 log
Guadagno TEN.MI dal 12/17/2002 al 05/05/2022 di 2.09 log
Guadagno TRN.MI dal 06/23/2004 al 05/05/2022 di 1.48 log
Guadagno UCG.MI dal 01/03/2000 al 05/05/2022 di -2.69 log
Guadagno UNI.MI dal 01/03/2000 al 05/05/2022 di -2.41 log

Il grafico dei guadagni percentuali e logaritmici sono i seguenti:

Osservando il grafico sulle % sembra che i picchi siano delle anomalie, mentre rappresentando i medesimi dati in scala logaritmica sembrano coerenti e seguire un moto Browniano. I dati sono troppo esigui per rappresentarli con un’istogramma sensato, ma facendolo lo stesso otteniamo i seguenti diagrammi:

Istogramma dei guadagni percentuali
Istogramma dei guadagni logaritmici

A occhio, ma servirebbe un matematico, la curva dei guadagni sembra seguire una distribuzione di chi-quadro, mentre la distribuzione dei guadagni logaritmici sembra seguire una distribuzione normale. Ipotesi da approfondire con un set di dati più ampio.

Come ultima simulazione tolgo tutti i titoli che hanno un valore logaritmico elevato fino a ottenere una media di valore logaritmico inferiore a zero. In altre parole sommando i logaritmi di guadagni e perdite prevalgono le perdite. Calcolando il guadagno % otteniamo un guadagno (seppure piccolo) positivo e come si vede il grafico del guadagno logaritmico è stato privato della parte alta con un significativo sbilanciamento verso i numeri negativi.

Guadagno medio -0.0327 log
Guadagno medio 80.35%

In conclusione:

  • Se ci aspettiamo che i guadagni logaritmici seguano una distribuzione normale, il mercato premia sempre un investimento a lungo termine diversificato su un paniere ampio di azioni (B&H)
  • L’andamento nel lungo periodo delle azioni è legato all’analisi fondamentale e alle scelte strategiche operate dalle aziende

Effetto TOBIN TAX

In passato ho fatto degli studi sulle variazioni di quotazione che subiscono i titoli tra chiusura-apertura e apertura-chiusuara. Si tratta di fatto di scomporre il movimenti del titolo in due semi-movimenti, il primo misura il delta prezzo tra chiusura e l’apertura e il secondo tra l’apertura e la chiusura. L’aspetto interessante è che i due semi-movimenti sembrano spesso indipendenti e a volte divergenti. La tobin tax gioca un ruolo significativo perché spinge a chiudere le posizioni a fine giornata, con conseguente calo del prezzo, e ad aprire le posizioni a inizio giornata, con conseguente aumento dei prezzi.

L’immagine sottostante riporta i grafici di alcune azioni ottenuti sommando i delta delle variazioni logaritmiche di prezzo. Titoli quali EXO, RACE non presentano la rampa di salita arancione data dai semimovimenti tra chiusura e apertura.

Tuning rete neurale

Ho iniziato a sperimentare le reti neurali con il framework pyhton keras e dopo le prime prove mi sono reso conto che l’attività di tuning di una rete neurale è molto delicata.

Quanti nodi servono? Quanti livelli? I batch quanto devono essere grossi? E sopratutto quante epoche di trainng servono?

Come prima attività di tuning mi sono concentrato sulle epoche, un numero eccessivo di epoche porta a un overfit della rete, mentre un numero basso porta a una rete troppo poco addestrata. L’idea che mi sono è che allenare una rete neurale è come allenare un pilota su un circuito di Formula 1. Il pilota sale sulla macchina e inizia a guidare a ogni giro migliora le proprie prestazioni, più giri fa e più migliora. Se però il pilota si allena troppo sul medesimo circuito lo impara a memoria e quando cambia circuito le sue prestazioni saranno scarse. L’obiettivo è quindi allenare il pilota senza fargli imparare a memoria il tracciato. Con l’esempio è evidente che se il pilota si allena su un certo tipo di pista e la pista su cui è chiamato a misurarsi ha caratteristiche diverse i risultati del pilota saranno pessimi. Allenare un pilota su un circuito di Formula 1 e farlo gareggiare su un circuito da rally porterà a pessimi risultati.

Per valutare la bontà dell’allenamento utilizzo l’errore quadratico medio tra il risultato del set di dati di test e il risultato stimato dalla rete neurale. Alleno il mio “pilota” ossia la rete neurale facendogli fare un certo numero di giri di allenamento (epoche) sulla pista di allenamento (training set) e poi lo porto sulla pista di test (test set) per verificare quanto sono buone le sue prestazioni. Ripeto l’esperimento 30 volte a parità di numero di giri di test (epoche) e poi faccio variare il numero di giri (epoche). Eseguire l’esperimento 30 volte serve per avere un campione sufficiente per una statistica di base, ogni esperimento produce pesi diversi e quindi reti neurali che si comportano in modo diverso.

L’asset su cui ho allenato la mia rete neurale è l’azione Telecom, la speranza è di ottenere risultati positivi su un asset in forte calo. Il codice del ciclo di test sulle epoche è il seguente

from pandas import DataFrame
from math import sqrt
import matplotlib
# be able to save images on server
#matplotlib.use('Agg')
from matplotlib import pyplot
def experiment(repeats=5, epochs=2):
    error_scores = list()
    for r in range(repeats):
        lstm = testasset("TIT", epochs=epochs)
        rmse = sqrt(lstm.stats())
        print('%d) Test RMSE: %.3f' % (r+1, rmse))
        error_scores.append(rmse)
    return error_scores
    
repeats = 30
results = DataFrame()
epochs = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 50]
for e in epochs:
    results[str(e)] = experiment(repeats, e)
print(results.describe())
results.boxplot()
pyplot.savefig('boxplot_epochs.png')

Per farlo girare c’è voluto diverso tempo, sopratutto per l’apprendimento con 20 e 50 epoche, complessivamente l’esecuzione è durata poco più di due giorni, qui la tabelle dei tempi di esecuzione

	ore	gg
0	0	0
1	0.4375	0.018229167
2	0.875	0.036458333
3	1.3125	0.0546875
4	1.75	0.072916667
5	2.1875	0.091145833
6	2.625	0.109375
7	3.0625	0.127604167
8	3.5	0.145833333
9	3.9375	0.1640625
10	4.375	0.182291667
20	8.75	0.364583333
50	21.875	0.911458333

Nella seguente tabella sono riportati i dati statistici delle misurazioni di esecuzione con le diverse epoche:

              1          2          3          4          5          6  \
count  30.000000  30.000000  30.000000  30.000000  30.000000  30.000000   
mean    0.038587   0.041846   0.034651   0.035500   0.032625   0.032489   
std     0.014706   0.013233   0.009586   0.010110   0.009148   0.009238   
min     0.020083   0.019384   0.022519   0.022931   0.021452   0.021175   
25%     0.026908   0.034563   0.027189   0.027118   0.025462   0.024630   
50%     0.034167   0.039058   0.031203   0.033280   0.031137   0.029748   
75%     0.046000   0.049699   0.040562   0.042590   0.038665   0.036789   
max     0.075115   0.075003   0.060300   0.062060   0.055277   0.053957   

               7          8          9         10         20         50  
count  30.000000  30.000000  30.000000  30.000000  30.000000  30.000000  
mean    0.034010   0.027564   0.029000   0.028845   0.020712   0.018076  
std     0.012303   0.008566   0.010543   0.010209   0.007081   0.006426  
min     0.019001   0.018077   0.017641   0.016518   0.011808   0.009376  
25%     0.023905   0.020850   0.020434   0.021110   0.015664   0.012611  
50%     0.030288   0.024321   0.027368   0.027386   0.018326   0.018031  
75%     0.043142   0.032642   0.033461   0.031322   0.024690   0.022165  
max     0.062863   0.055480   0.063527   0.058419   0.037421   0.031675  

Il set di dati di addestramento non era particolarmente corposo, ca 4700 campioni. Gli stessi dati rappresentati con un grafico a box mostra l’andamento decrescente dell’errore quadratico medio, segno che l’addestramento è efficace e non siamo in una situazione di overfit.

Un ultimo confronto utile è la stima dei valori fatta dale due reti, addestrate agli estremi, 1 epoca e 50 epoche.

Addestramento con 1 epoca
Addestramento con 50 epoche

Il confronto tra i due grafici mette in evidenza il comportamento del “pilota” addestrato bene, rispetto al “pilota” principiante che fatica a star dietro ai rapidi cambi di direzione. La lunghezza del data set coincide anch’esso sull numero di epoche necessarie a un buon addestramento. Tornando alla metafora del pilota, se la pista è lunga, con molti cambi di direzione, accelerazioni, frenate, paraboliche e altro, il pilota riesce ad allenarsi con pochi giri. Se la pista è breve il pilota dovrà ripetere la pista diverse volte per avere un buon allenamento. Maggiore è il numero di epoche necessarie all’addestramento, maggiore è la probabilità di overfit. Benché i risultati non evidenzino l’overfit con il degrado delle performance, dallo studio del grafico con 50 epoche, l’overfit risulta evidente. Lo stesso grafico con 10 epoche risulta più morbido e accompagna bene la curva del titolo.

Addestramento con 10 epoche
Addestramento con 20 epoche

Ciò che però sembra aver appreso la rete neurale è di riproporre in modo molto fedele i movimenti che il titolo ha fatto il giorno precedente. La rete non sembra in grado di anticipare i movimenti del titolo con le sole informazioni disponibili e la miglio strategia che riesce ad attuare è d’inseguimento. Ai fini dello studio di tecniche di AI l’esercizio è stato utile, ai fini di algotrading un po’ meno.

Distribuzione delle chiusure di mercato

Una domanda che mi sono spesso posto è se le chiusure dei titoli seguano una distribuzione normale. Ho fatto alcune prove per verificare se è corretto assumere un andamento normale. Nello studio ho usato l’indice di borsa FTSMIB, ma ripetendo le prove con diversi titoli sembra che il comportamento sia analogo anche per i diversi titoli.

Il codice è scritto per Jupyter, strumento di analisi abbastanza standard, facie da usare e molto diffuso. Primo importo le librerie.

#Import vari
import yfinance as yf
from datetime import datetime
from datetime import timedelta
import scipy.stats as sp
import pylab
import numpy as np
from matplotlib import pyplot as plt

Poi carico i dati da yahoo, poi li normalizzo attraverso un’operazione, anch’essa comune, logaritmo sui dati, e differenza tra i valori logaritmici.

#carico i dati, uso il FTSEMIB
start = '2015-01-01' 
#ticker = "A2A.MI"
ticker = "FTSEMIB.MI"
sp_list = [ticker]
end = '2020-12-31'
print(f'{ticker} Stock download')
spy = yf.download(sp_list, start,end)

log_spy = np.log(spy.Close)
log_return = (np.diff(log_spy ,axis=0))
spy = spy.iloc[1:,:]
spy.loc[:,('log_return')] = log_return
data = spy.log_return.values

E ora i grafici e l’analisi.

Distribuzione normale

Faccio un fittin dei dati con la curva normale.

xmin, xmax = plt.xlim([-0.2,0.2])
x = np.linspace(xmin, xmax, len(data))
print(f'lunghezza dati {len(data)}')
plt.hist(data, bins=100, density=True, alpha=0.5)

r = sp.norm.fit(data)
print(f'fit normale: {r}')
p1 = sp.norm.pdf( x, r[0], r[1])
plt.plot(x, p1, 'r' ,linewidth=2,label='norm Returns')

# Plot the PDF.
title = f"Fitting Distributione Normale {ticker} " 
plt.title(title)
plt.legend()
plt.show()

sp.probplot(data, dist="norm", plot=pylab)
pylab.show()

Il risultato è quello mostrato in figura, il fitting è solamente discreto, si nota un picco nella parte centrale che supera la curva normale e il grafico delle probabilità conferma che le code sono distanti dai valori della distribuzione normale.

Distribuzione t-Student

Il picco centrale suggerisce che una distribuzione t-student con un basso grado di libertà (< 30) possa avere un fitting migliore.

Una distribuzione di t-student sembra in effetti avere un fitting migliore, rimangono delle asimmetrie sulle code e allontanamento dalla norma, con conseguente skewness (asimmetria) e kurtosi.

Approfondimenti da fare: per quali ragioni la distribuzione è t-student?

Prove di trailingstop

Uno degli stop spesso suggerito è il Trailing-Stop, ossia uno stop che si attiva se il prezzo scende di una certa % (o tick, in funzione dello strumento) rispetto all’ultimo massimo dall’attivazione del trailing stop.

Non l’ho usato spesso nei backtesting perché la sua implementazione nelle strategie richiede uno sforzo implementativo maggiore. Ho deciso di fare alcune prove con una strategia, la solita che uso basata sulle mie candele. Le prove che faccio sono: senza stop, 5%, 10%, 15%, 20%, 25%, 30%, 35%, 40%.

La strategia ha già uno stop al 9%, gli asset sono le azioni del MIB, il benchmark è la strategia buy&hold sui medesimi titoli, slipage e commissioni incluse. La strategia che uso ha un buon (discreto) punto di ingresso, ma una pessima uscita. L’entrata anticipa spesso dei missili con alti profitti, ma spesso l’uscita cattura la parabola discendente molto in ritardo, posizioni che potenzialmente guadagnano il 50% risultano perdenti perché chiuse troppo tardi. E’ la situazione ideale per l’uso di Trailingstop. Il backtesting viene fatto con backtrader.

Senza Trailingstop

Trailingstop 5%

Trailingstop 10%

Trailingstop 15%

Trailingstop 20%

Trailingstop 25%

trailingstop 30%

Trailingstop 35%

Trailingstop 40%

Risultati

Nei primi 2 grafici, il trailingstop risulta decisamente penalizzante. La strategia viene ‘soffocata’ dagli stop, che non consentono la normale fluttuazione dei titoli all’interno del trend. Anche sui piccoli rintracciamenti o in presenza di spike, viene chiusa la posizione. Visivamente sembra che l’andamento della strategia sia inversamente correlato rispetto al benchmark, aspetto che andrebbe approfondito.

A partire del 20% di trailingstop sembrano portare un benefico, in particolare lo stop al 35% ha un indice di sharpe 1.41 contro l’indice 1.16 della strategia senza trailingstop.

superata la soglia del 30% l’effetto del trailingstop si smorza, segno che è troppo lasco e incide poco sulla strategia.

Il beneficio maggiore sempre esserci con lo stop al 25%, la curva è più fluida e meno spigolosa, ma anche più piatta. Togliendo la parte iniziale con la rampa molto pronunciata nel periodo 2013-2014, si ottengono i grafici sottostanti che mettono a fuoco da magio 2014 fino al 2020. L’indice di sharpe, che prima era superiore con il trailing stop, ora è più basso. L’uso dello stop rende in ogni caso la strategia meno volatile.

Un test che si potrebbe fare è l’applicazione di un trailing stop considerato solo le chiusure, in questo caso non sarebbe da considerare uno stop, ma una condizione d’uscita. In ogni caso questo tipo di uscita eliminerebbe il problema degli spike ( legati hunter stop intraday), rimarrebbe il tema dei rintracciamenti.

NB: ogni strategia è diversa e l’effetto di un trailingstop può essere completamente diversi su un’altra strategia.

Focus trailingstop 25% 05-2014

Focus senza 05-2014