Misurare le performance di una strategia – Parte 1

Una domanda molto importante da porsi nella creazione di un Trading System è quella della misura delle performance della strategia che si sta costruendo. Di solito questa misura viene valutata guardando l’Equity Line, e di solito è una valutazione che ha un certo margine di soggettività.

Nel Trading Algoritmico, avendo a che fare con algoritmi e calcoli, si cerca sempre di diminuire o al limite eliminare la componente soggettiva.

La valutazione delle performance viene dunque ottenuta dai vari reports forniti da tutte le piattaforme che effettuano backtesting, e si utilizzano opportuni indicatori in modo da avere un’indicazione rapida e il più possibile completa del raggiungimento delle prestazioni desiderate.

In più, se si sta creando un Trading System automatico, e si fanno un certo numero di backtest per trovare i migliori parametri della strategia, occorre avere un risultato quantitativo, cioè un numero, tale da poter poter prendere la migliore decisione, magari farla prendere direttamente al computer.

Nella mia ricerca ho trovato vari modi per raggiungere questo scopo. Ne elencherò alcuni, presentando pro e contro, dando, alla fine, il codice in R per calcolare quello che attualmente ritengo sia l’indicatore più affidabile.

Cominciamo!!!

L’indicatore più intuitivo, e anche ingenuo, è quello dato dal Net Profit, cioè il rendimento finale. In pratica, da una serie di test si sceglie quello che, riferito all’ultima data, abbia dato il valore della equity più alto degli altri.

Un altro indicatore, altrettanto ingenuo e anche più pericoloso, è quello del Max Profit, cioè il rendimento massimo ottenuto nel periodo. Questo indicatore si basa sul massimo della equity, ottenuto in qualsiasi data, quindi non necessariamente ngli ultimi tempi.

Che cosa rende questi due indicatori così poco raccomandabili?

La prima cosa di cui questi indicatori non tengono conto sono le perdite che si potrebbero avere. Potrebbero essere così elevate che il capitale potrebbe non bastare, oppure, cosa che capita molto più spesso di quanto non si sospetti, come ho detto in un mio precedente post, potrebbero essere così elevate da non essere sopportate psicologicamente, facendo saltare tutta la strategia o, nel migliore dei casi, facendo attraversare periodi di forte sofferenza.

Oltretutto, non ci danno nessuna informazione sulla regolarità della equity.

Inoltre, il secondo indicatore può essere fuorviante per un altro motivo: il fatto che si sia raggiunto un determinato rendimento non vuol dire che lo si raggiunga di nuovo in futuro. Quindi, il fatto che l’ultimo rendimento sia più basso di quello massimo raggiunto in precedenza, potrebbe voler significare anche che la strategia non sia più efficace già adesso.

Un indicatore appena migliore può essere quello del massimo Drowdown. Ad esempio, in una serie di backtest si seleziona il test che dia la più bassa perdita massima.

Sebbene questo indicatore sia più protettivo dei precedenti nei confronti del capitale, presenta l’inconveniente di non fornire nessuna informazione su quanto guadagni la strategia. In altre parole, potremmo certamente avere perdite contenute, ma anche guadagni altrettanto contenuti! Cioè ritrovarci alla fine una serie di piccole perdite e piccoli guadagni e quindi aver perso solo tempo nel tradare. Ricordiamo che lo scopo del trading non è solo non perdere, ma anche guadagnare ragionevomente bene.

Inoltre non abbiamo ancora indicazioni sulla regolarità della equity line.

I reports di “blotter” riportano un indicatore che mette insieme i punti di forza di questi due tipi di indicatori: il rapporto Net Profit / Max Drowdown. Questo indicatore ci dà una misura sia di quanto si guadagni alla fine, sia fino a quanto si sia ridotto il capitale nel periodo preso in considerazione. E’ un indicatore molto migliore dei precedenti, tuttavia ancora non tiene abbastanza conto della regolarità della equity.

Un altro indicatore utile lo si ottiene dai reports delle varie piattaforme di Trading Algoritmico (tipo Trader Station Metastock, NinjaTrader, ecc., e anche “blotter”): il Profit Factor, che è, per chi è nuovo dell’ambiente, il rapporto tra i profitti totali e le perdite totali. Questo è un indicatore molto utilizzato, infatti riesce a tener conto di tutte le perdite e di tutti i profitti, quindi, in qualche modo, riesce in una certa misura a tener conto dell’andamento dell’equity.

In tanti Trading System questo indicatore può essere più che sufficiente per misurare le performance di una strategia. Tuttavia ancora dà indicazioni non sempre sufficienti su quanto possa essere irregolare l’equity line. In certi casi (piuttosto rari in verità) potrebbe non essere possibile automatizzare completamente la scelta e occorre guardare manualmente l’equity.

Per quanto riguarda l’ultimo indicatore che voglio presentare, occorre fare una premessa. Per non allungare troppo la lettura, preferisco riportare il tutto nella prossima parte, dove fornirò anche il codice R per calcolarlo.

Alla prossima!!!

Test in R: “Faber Portfolio” – Parte 2

Continuiamo la discussione cominciata nella Prima parte.

La sezione seguente

# Adjust prices for splits/dividends (thanks pg)
#IEF = adjustOHLC(IEF)
#SPY = adjustOHLC(SPY)

anche se commentata, utilizza i dati modificati a causa di splits e dividendi. Questa function è da utilizzarsi se si selezionano titoli azionari. Per gli indici, quelli usati nell’esempio, non è necessaria, dato che non subiscono nessuna di queste due operazioni.

Vediamo adesso una function molto utile degli oggetti “xts”

# Convert data to monthly frequency (to.weekly() needs drop.time=FALSE)
IEF = to.monthly(IEF, indexAt=’endof’
SPY = to.monthly(SPY, indexAt=’endof’)

La function “to.monthly()” converte il time-frame dei dati. Nel caso qui presentato i prezzi vengono convertiti da giornalieri a mensili, presentando così, per ogni barra, l’apertura, il max, il min e la chiusura di ogni mese. Nel caso le esigenze lo richiedano, è possibile passare a time-frames diversi, tipo settimanali o annuali. Non è possibile, ovviamente, scendere da periodi più lunghi a quelli più brevi (ad esempio da mesi a giorni).

Nella sezione seguente vedremo l’uso di indicatori tecnici, contenuti nel package “TTR”

# Set up indicators with TTR
print(“Setting up indicators”)
IEF$SMA = SMA(Cl(IEF), 10)
SPY$SMA = SMA(Cl(SPY), 10)

In pratica, abbiamo calcolato le medie mobili a 10 periodi dei prezzi di chiusura dei due titoli.

Vediamo qui anche un’altra caratteristica delle strutture dati di R. Il comando:

SPY$SMA = SMA(Cl(SPY), 10)

aggiunge all’oggetto “SPY” un’ulteriore serie di dati, quella calcolata dalla media mobile. Essendo entrambi oggetti “xts”, la corrispondenza tra prezzi e valore della media viene gestita automaticamente tramite il valore delle date.

#Set up a portfolio object and an account object in blotter
initPortf(name=’default’, symbols=symbols, initDate=initDate)
initAcct(name=’default’, portfolios=’default’, initDate=initDate, initEq=initEq)
verbose = TRUE

Qui cominciamo a vedere l’uso di due oggetti di “blotter”. Il primo è
“portfolio”, un oggetto che tiene traccia dei titoli scelti. Portfolio può
tracciare l’andamento di ogni titolo contenuto periodo per periodo.

Il secondo è “account”, che può contenere uno o più oggetti portfolio, e che
tiene traccia di tutti i titoli e i portfolio nel complesso sempre periodo
per periodo.

A questo punto entriamo nel vivo della simulazione.

#Create trades
for( i in 10:NROW(SPY) ) {
  CurrentDate=time(SPY)[i]
equity = getEndEq(Account=’default’, CurrentDate)
for( symbol in symbols ) {
sym = get(symbol)
ClosePrice = as.numeric(Cl(sym[i,]))
Posn = getPosQty(Portfolio=’default’, Symbol=symbol,Date=CurrentDate)
UnitSize = as.numeric(trunc((equity/NROW(symbols))/ClosePrice))
# Position Entry (assume fill at close)
if( Posn == 0 ) {
# No position, so test to initiate Long position
if( Cl(sym[i,]) > sym[i,’SMA’] ) {
# Store trade with blotter
addTxn(‘default’, Symbol=symbol, TxnDate=CurrentDate, TxnPrice=ClosePrice, TxnQty=UnitSize, TxnFees=0, verbose=verbose)
}
} else {
# Have a position, so check exit
if( Cl(sym[i,]) < sym[i,’SMA’] ) {
# Store trade with blotter
addTxn(Portfolio=’default’, Symbol=symbol, TxnDate=CurrentDate, TxnPrice=ClosePrice, TxnQty=-Posn, TxnFees=0, verbose=verbose)
}
}
} # End symbols loop
# Calculate P&L and resulting equity with blotter
updatePortf(Portfolio=’default’, Dates=CurrentDate)
updateAcct(name=’default’, Dates=CurrentDate)
updateEndEq(Account=’default’, Dates=CurrentDate)
} # End dates loop

Qui ci sono due cicli for, il primo effettua il ciclo sul periodo, cioè mese
per mese, il secondo fa una verifica sulle condizioni simbolo per simbolo e,
in caso di verifica, effettua le transazioni. Queste vengono registrate con
la function “addTxn()” nel portfolio. Alla fine, per ogni periodo, vengono
chiamate tre function per aggiornare il portfolio, l’account e l’equity
finale, cioè “updatePortf()”, “updateAcct()” e “updateEndEq()”.

La parte finale:

# Buy and Hold cumulative equity
buyhold = exp(cumsum( ( 0.5*ROC(Cl(IEF)) + 0.5*ROC(Cl(SPY)) )[-1] ))
# Final values
cat(‘Tactical Asset Allocation Return: ‘,(getEndEq(Account=’default’,
Date=CurrentDate)-initEq)/initEq,’\n’)
cat(‘Buy and Hold Return: ‘,tail(buyhold,1)-1,’\n’)
# Plot Strategy Summary
png(filename=”20091118_blotter_strategy.png”, 720, 720)
#charts.PerformanceSummary(ROC(getAccount(‘default’)$TOTAL$End.Eq)[-1],main=”Tactical
Asset Allocation”)
charts.PerformanceSummary(ROC(getAccount(‘default’)$summary$End.Eq)[-1],main=”Tactical
Asset Allocation”)
dev.off()
# Plot Buy and Hold Summary
png(filename=”20091118_blotter_buyhold.png”, 720, 720)
charts.PerformanceSummary(ROC(buyhold)[-1],main=”Buy & Hold”)
dev.off()

calcola la performance della strategia “buy and hold” e disegna i grafici.
Il disegno dei grafici avviene su immagini png, salvate nella cartella di
lavoro di R.

Bene, siamo alla fine di questa spiegazione. Spero che sia stata utile per
mostrare le grandi potenzialità di R.

A presto.

Test in R: “Faber Portfolio” – Parte 1

Dopo le dovute introduzioni, entriamo nel vivo dell’utilizzo dell’ambiente, e lo faremo in un articolo diviso in più parti.

Partendo da un test relativamente semplice, ne approfitteremo per spiegare i primi elementi di R e dei package per il Trading Algoritmico che utilizzeremo.

Il test utilizzerà un listato preso dal blog “FOSS Trading”, e si basa sulla strategia descritta nell’ articolo: Faber, Mebane T., – “A Quantitative Approach to Tactical Asset Allocation.” – Journal of Risk Management (Spring 2007).

In questo articolo gli autori descrivono una strategia da applicare allo SPY:

  • comprare quando il prezzo di chiusura del titolo supera la SMA a 10 mesi,
  • chiudere e incassare quando la SMA a 10 mesi supera il prezzo di chiusura del titolo.

Le candele sono mensili. In questa strategia si entra solo in long, mai in short.

Tengo a precisare che questa è una strategia che riporto qui solo per spiegare come funziona R, non è consigliabile utilizzarla in reale (almeno senza le opportune verifiche).

Riporto prima il listato e dopo spiegherò le singole parti.

# This code implements the strategy found in:
# Faber, Mebane T., “A Quantitative Approach to Tactical Asset Allocation.”
# Journal of Risk Management (Spring 2007).
# The article implements a simpler version of the 200-day SMA, opting for a
# 10-month SMA because monthly data are more easily available in earlier
# periods and lower granularity should translate to lower transaction costs.
# The rules of the system are relatively simple:
# – Buy when monthly price > 10-month SMA
# – Sell and move to cash when monthly price < 10-month SMA
# 1. All entry and exit prices are on the day of the signal at the close.
# 2. All data series are total return series including dividends, updated monthly.
# NOTE: For the purposes of this demo, we only use price returns.
# 3. Cash returns are estimated with 90-day commercial paper. Margin rates for
# leveraged models are estimated with the broker call rate.
# NOTE: For the purposes of this demo, we ignore interest and leverage.
# 4. Taxes, commissions, and slippage are excluded.
# Data:
# This demo uses monthly data downloaded from Yahoo Finance for two ETFs: SPY and
# IEF. These were chosen to illustrate the classic stock/bond asset portfolio.
# Though longer serires would be preferred, data for IEF begin in mid-2002.
# Load required libraries
library(quantmod)
library(TTR)
library(blotter) # r-forge revision 193
library(PerformanceAnalytics)
# Set initial values
initDate=’2006-07-31′
endDate=’2012-01-31′
initEq=100000
# Set currency and instruments
currency(“USD”)
stock(“IEF”,currency=”USD”,multiplier=1)
stock(“SPY”,currency=”USD”,multiplier=1)
# Load data with quantmod
print(“Loading data”)
symbols = c(“IEF”, “SPY”)
getSymbols(symbols, from=initDate, to=endDate, index.class=c(“POSIXt”,”POSIXct”))
# Adjust prices for splits/dividends (thanks pg)
#IEF = adjustOHLC(IEF)
#SPY = adjustOHLC(SPY)
# Convert data to monthly frequency (to.weekly() needs drop.time=FALSE)
IEF = to.monthly(IEF, indexAt=’endof’)
SPY = to.monthly(SPY, indexAt=’endof’)
# Set up indicators with TTR
print(“Setting up indicators”)
IEF$SMA = SMA(Cl(IEF), 10)
SPY$SMA = SMA(Cl(SPY), 10)
# Set up a portfolio object and an account object in blotter
initPortf(name=’default’, symbols=symbols, initDate=initDate)
initAcct(name=’default’, portfolios=’default’, initDate=initDate, initEq=initEq)
verbose = TRUE
# Create trades
for( i in 10:NROW(SPY) ) {
  CurrentDate=time(SPY)[i]
  equity = getEndEq(Account=’default’, CurrentDate)
  for( symbol in symbols ) {
    sym = get(symbol)
    ClosePrice = as.numeric(Cl(sym[i,]))
    Posn = getPosQty(Portfolio=’default’, Symbol=symbol, Date=CurrentDate)
    UnitSize = as.numeric(trunc((equity/NROW(symbols))/ClosePrice))
    # Position Entry (assume fill at close)
    if( Posn == 0 ) {
    # No position, so test to initiate Long position
      if( Cl(sym[i,]) > sym[i,’SMA’] ) {
        # Store trade with blotter
        addTxn(‘default’, Symbol=symbol, TxnDate=CurrentDate,
          TxnPrice=ClosePrice, TxnQty=UnitSize, TxnFees=0, verbose=verbose)
      }
    } else {
      # Have a position, so check exit
      if( Cl(sym[i,]) < sym[i,’SMA’] ) {
        # Store trade with blotter
        addTxn(Portfolio=’default’, Symbol=symbol, TxnDate=CurrentDate,
          TxnPrice=ClosePrice, TxnQty=-Posn, TxnFees=0, verbose=verbose)
      }
    }
  } # End symbols loop
  # Calculate P&L and resulting equity with blotter
  updatePortf(Portfolio=’default’, Dates=CurrentDate)
  updateAcct(name=’default’, Dates=CurrentDate)
  updateEndEq(Account=’default’, Dates=CurrentDate)
} # End dates loop
# Buy and Hold cumulative equity
buyhold = exp(cumsum( ( 0.5*ROC(Cl(IEF)) + 0.5*ROC(Cl(SPY)) )[-1] ))
# Final values
cat(‘Tactical Asset Allocation Return: ‘,(getEndEq(Account=’default’, Date=CurrentDate)-initEq)/initEq,’\n’)
cat(‘Buy and Hold Return: ‘,tail(buyhold,1)-1,’\n’)
# Plot Strategy Summary
png(filename=”20091118_blotter_strategy.png”, 720, 720)
#charts.PerformanceSummary(ROC(getAccount(‘default’)$TOTAL$End.Eq)[-1],main=”Tactical Asset Allocation”)
charts.PerformanceSummary(ROC(getAccount(‘default’)$summary$End.Eq)[-1],main=”Tactical Asset Allocation”)
dev.off()
# Plot Buy and Hold Summary
png(filename=”20091118_blotter_buyhold.png”, 720, 720)
charts.PerformanceSummary(ROC(buyhold)[-1],main=”Buy & Hold”)
dev.off()

Cominciamo a spiegare passo passo, in modo da mostrare, anche a chi desidera conoscere R un pò più in dettaglio, un assaggio di cosa si può fare con questo ambiente di calcolo sfruttando la potenza dei package di Trading Algoritmico.

Il carattere “#” serve per i commenti. In R esiste solo il commento per la singola riga.

La sezione:

# Load required libraries
library(quantmod)
library(TTR)
library(blotter) # r-forge revision 193
library(PerformanceAnalytics)

carica i packages specializzati per il Trading Algoritmico che utilizzeremo. I packages devono essere installati nell’ambiente. L’installazione avviene in maniera molto semplice utilizzando la function install.packages(), che riesce a installare i packages anche direttamente da internet. Per l’uso basta fare riferimento alla documentazione di R.
La sezione:

# Set initial values
initDate=’2006-07-31′
endDate=’2012-01-31′
initEq=100000

setta i valori che ci servono. I primi due valori sono le date, l’altro è il capitale iniziale. Da notare che le date sono delle stringhe, il formato da utilizzare per l’indicizzazione delle strutture dati “xts”.

La sezione:

# Set currency and instruments
currency(“USD”)
stock(“IEF”,currency=”USD”,multiplier=1)
stock(“SPY”,currency=”USD”,multiplier=1)

setta la moneta da utilizzare e i titoli che saranno utilizzati per il test.

Nella sezione seguente si nota qualcosa di molto interessante:

# Load data with quantmod
print(“Loading data”)
symbols = c(“IEF”, “SPY”)
getSymbols(symbols, from=initDate, to=endDate, index.class=c(“POSIXt”,”POSIXct”))

La variabile “symbols” è un vettore di stringhe costruito con la function “c()”. Per i neofiti, questa function è una delle più utilizzate in R, e genera un vettore a partire dai parametri passati in ingresso. Per maggiori dettagli fare riferimento alla documentazione di R.

La function “getSymbols()” è davvero molto potente. Scarica da una sorgente su internet i dati storici dei simboli che gli vengono passati. La sorgente di default è Yahoo. In uscita restituisce tanti oggetti “xts” quanti sono i simboli nel vettore “symbols”. Gli oggetti xts sono una potentissima struttura dati. Si può accedere agli elementi sia con gli indici (in maniera analoga agli array) sia con le date. Le date devono essere passate come stringhe. I dati storici quindi sono memorizzati in questi due oggetti.

Per adesso ci fermiamo qui. Proseguiremo la spiegazione nella prossima parte.

A presto.