strategia con l'oscillatore stocastico in Python

Backtest di una strategia con l’oscillatore stocastico in Python

Sommario

In questo articolo continuiamo il tema dei test delle strategie di trading basate sui segnali di alcuni dei classici “indicatori tecnici” che molti trader usano nel loro processo decisionale. Dato che il precedente articolo è focalizzato sulle bande di Bollinger, in questo articolo descriviamo come implementare il backtest di una strategia con l’oscillatore stocastico in Python.

L’oscillatore stocastico

Cominciamo ad introdurre la teoria alla base dell’oscillatore stocastico. Investopedia definisce l’oscillatore stocastico come:

L’oscillatore stocastico è un indicatore di momentum che confronta il prezzo di chiusura di un titolo con l’intervallo dei suoi prezzi in un certo periodo di tempo. La sensibilità dell’oscillatore ai movimenti del mercato è data dal periodo di tempo o prendendo una media mobile del risultato.

L’oscillatore stocastico è formato da due valori/linee %K e %D che sono calcolate tramite le seguenti formule:

%K = 100(C – L14)/(H14 – L14)

%D = Sma3(%K)

Dove:

  • C = il prezzo di chiusura più recente
  • L14 = il prezzo minimo delle 14 sessioni di trading precedenti
  • H14 = il prezzo massimo delle 14 sessioni di trading precedenti
  • Sma3 = media mobile a 3 periodi di %K

La teoria alla base di questo indicatore prevede che in un mercato con trend rialzista, i prezzi di chiusura tendono al limite superiore. Al contrario, in un trend ribassista i prezzi di chiusura tendo al limite inferiore. I segnali di  trading sono creati quando la %K attraversa la media mobile %D.

La strategia

In questo articolo vediamo come testare due diverse logiche basate sull’oscillatore stocastico:

  1. Si genera un segnale di entrata SHORT quando la linea %K incrocia al ribasso la linea %D e la linea %K è sopra 80. Il segnale di uscita è generato quando la linea %K attraversa di nuovo la linea %D, indipendentemente dal valore effettivo della linea %K . Si genera un segnale di ingresso LONG quando la linea %K attraversa la linea %D e la linea %K è sotto 20 in quel momento. Il segnale di uscita è generato quando linea %K attraversa nuovamente la linea %D, indipendentemente dal valore della linea %K. In questa  logica ne ci sono 3 possibili stati – long, short, flat (cioè nessuna posizione).
  2. Si prevede solo 2 possibili stati: long o short. Una volta  entrati in posizione, la posizione è mantenuta fino a quando non viene dato un segnale opposto, a quel punto la posizione viene invertita (cioè da long a short o da short a long). Ad esempio, se un’entrata long è segnalata dalla linea %K che attraversa la linea %D mentre la linea %K è inferiore a 20, la posizione viene mantenuta fino a quando la linea %K attraversa la linea %D mentre la linea %K è superiore a 80.

Il codice

Vediamo l’implementazione del codice e testiamo la strategia sul titolo Apple Inc.

				
					import pandas as pd
import yfinance as yf
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style('whitegrid')

# Download dei dati storici di Apple in un dataframe Pandas
df = yf.download("AAPL", start="2010-01-01", end="2020-01-01")
# Stampa le prime 5 righe del dataframe per controllare il formato
print(df.head())

				
			
strategia con l'oscillatore stocastico in Python
				
					# Calcolo dei minimi a finestra mobile dei 14 periodi precedenti
df['L14'] = df['Low'].rolling(window=14).min()
# Calcolo dei massimi a finestra mobile dei 14 periodi precedenti
df['H14'] = df['High'].rolling(window=14).max()
# Calcolo della linea %K
df['%K'] = 100*((df['Close'] - df['L14']) / (df['H14'] - df['L14']) )
# Calcolo della linea %D
df['%D'] = df['%K'].rolling(window=3).mean()
				
			

Creiamo inoltre un grafico (con 2 sottotrame) per visualizzare il prezzo Apple nel tempo, insieme a una rappresentazione visiva dell’oscillatore stocastico

				
					fig, axes = plt.subplots(nrows=2, ncols=1,figsize=(20,10))
df['Adj Close'].plot(ax=axes[0]); axes[0].set_title('Close')
df[['%K','%D']].plot(ax=axes[1]); axes[1].set_title('Oscillator')
plt.show()

				
			
strategia con l'oscillatore stocastico in Python
				
					# Crea i segnali d'entrata SHORT quando la linea %K attraversa al 
# ribasso la linea %D e il valore è sopra gli 80 
df['Sell Entry'] = ((df['%K'] < df['%D']) & (df['%K'].shift(1) > df['%D'].shift(1))) & (df['%D'] > 80) 
# Crea i segnali d'uscita SHORT quando la linea %K attraversa al rialzo la linea %D 
df['Sell Exit'] = ((df['%K'] > df['%D']) & (df['%K'].shift(1) < df['%D'].shift(1))) 

# Crea le posizione SHORT (-1 per lo short e 0 per il flat) tramite i valori booleani 
df['Short'] = np.nan 
df.loc[df['Sell Entry'],'Short'] = -1 
df.loc[df['Sell Exit'],'Short'] = 0 

# Posizione flat per il giorno 1 
df['Short'][0] = 0 
# Riempimento in avanti delle posizioni per rappresentare il mantenimento delle posizioni a mercato 
df['Short'] = df['Short'].fillna(method='pad')

# Crea i segnali d'entra LONG quando la linea %K attraversa al rialzo la linea %D e il valore è minore di 20 
df['Buy Entry'] = ((df['%K'] > df['%D']) & (df['%K'].shift(1) < df['%D'].shift(1))) & (df['%D'] < 20) 
# Crea i segnali d'uscita LONG quanto la linea %K attraversa al ribasso la linea %D 
df['Buy Exit'] = ((df['%K'] < df['%D']) & (df['%K'].shift(1) > df['%D'].shift(1))) 

# Crea le posizione LONG (1 per il long e 0 per il flat) tramite i valori booleani 
df['Long'] = np.nan  
df.loc[df['Buy Entry'],'Long'] = 1  
df.loc[df['Buy Exit'],'Long'] = 0  
# Posizione flat per il giorno 1 
df['Long'][0] = 0
# Riempimento in avanti delle posizioni per rappresentare il mantenimento delle posizioni a mercato
df['Long'] = df['Long'].fillna(method='pad') 

# Unisce le posizione long e short per ottenere le posizioni della strategia
# (1 per il long, -1 per lo short e 0 per il flat) 
df['Position'] = df['Long'] + df['Short']

				
			

Dopo aver implementato la logica della strategia, visualizziamo il grafico della posizione nel tempo per avere un’idea di quando siamo long e di quando siamo short:

				
					df['Position'].plot(figsize=(20,10))
plt.show()
				
			
strategia con l'oscillatore stocastico in Python

Passiamo quindi a calcolare e visualizzare i rendimenti della strategia

				
					
# Calcola i rendimenti giornalieri di Apple
df['Market Returns'] = df['Close'].pct_change()
# Calcola i rendimenti della strategia moltiplicando i rendimenti di Apple per le posizioni del giorno precedente 
df['Strategy Returns'] = df['Market Returns'] * df['Position'].shift(1)
# Grafico dei rendimenti
df[['Strategy Returns','Market Returns']].cumsum().plot()
plt.show()
				
			
strategia con l'oscillatore stocastico in Python

Vediamo che i rendimenti sono almeno positivi, ma avremmo potuto fare molto meglio semplicemente con il Buy&Hold delle azioni Apple. La strategia è quindi leggermente deludente.

Vediamo quindi come implementare la seconda strategia, dove siamo long o short. Di seguito riportiamo l’intero codice e visualizziamo la curva di equity finale.

				
					import pandas as pd
import yfinance as yf
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style('whitegrid')

df = yf.download("AAPL", start="2010-01-01", end="2020-01-01")
df['L14'] = df['Low'].rolling(window=14).min()
df['H14'] = df['High'].rolling(window=14).max()
df['%K'] = 100*((df['Close'] - df['L14']) / (df['H14'] - df['L14']) )
df['%D'] = df['%K'].rolling(window=3).mean()
# Sengali
df['Sell Entry'] = ((df['%K'] < df['%D']) & (df['%K'].shift(1) > df['%D'].shift(1))) & (df['%D'] > 80)
df['Buy Entry'] = ((df['%K'] > df['%D']) & (df['%K'].shift(1) < df['%D'].shift(1))) & (df['%D'] < 20)
# Posizione
df['Position'] = np.nan
# posizione -1 per i segnali short
df.loc[df['Sell Entry'],'Position'] = -1
# posizione 1 per i segnali long
df.loc[df['Buy Entry'],'Position'] = 1
# posizione iniziale flat
df['Position'].iloc[0] = 0
# Riempimento in avanti per simulare il mantenimento delle posizioni
df['Position'] = df['Position'].fillna(method='ffill')
# Rendimenti giornalieri di Apple
df['Market Returns'] = df['Close'].pct_change()
# Calcola i rendimenti della strategia moltiplicando i rendimenti di Apple per le posizioni del giorno precedente
df['Strategy Returns'] = df['Market Returns'] * df['Position'].shift(1)
# Grafico dei rendimenti
df[['Strategy Returns','Market Returns']].cumsum().plot(figsize=(20,10))
plt.show()
				
			
strategia con l'oscillatore stocastico in Python

Sfortunatamente questa strategia produce  risultati peggiori della precedente, con un rendimento complessivo fortemente negativo.

Ancora una volta abbiamo dimostrato che usare un semplice indicatore tecnico come l’Oscillatore Stocastico non è sufficiente per generare rendimenti superiori (shock horror!), almeno per il titolo Apple nel periodo di backtesting considerato. Immagino che i titoli per i quali questa strategia ha funzionato in modo sufficientemente robusto e su cui fare effettivamente affidamento  siano pochi e rari.

Codice completo

In questo articolo abbiamo descritto come implementare il backtest di una strategia con l’oscillatore stocastico in Python. Per il codice completo riportato in questo articolo, si può consultare il seguente repository di github:
https://github.com/datatrading-info/Backtest_Strategie

Benvenuto su DataTrading!

Sono Gianluca, ingegnere software e data scientist. Sono appassionato di coding, finanza e trading. Leggi la mia storia.

Ho creato DataTrading per aiutare le altre persone ad utilizzare nuovi approcci e nuovi strumenti, ed applicarli correttamente al mondo del trading.

DataTrading vuole essere un punto di ritrovo per scambiare esperienze, opinioni ed idee.

SCRIVIMI SU TELEGRAM

Per informazioni, suggerimenti, collaborazioni...

Scroll to Top