1 votos

¿Dónde está el sesgo en este sencillo marco de backtesting?

Normalmente escribo mis backtests en Python o en un entorno de backtesting dedicado, pero quería experimentar con algunas de las funcionalidades de análisis predictivo de R. Escribí un backtester extremadamente simple para hacer algunas investigaciones rápidas y sucias, pero terminé con algún sesgo obvio en mis resultados. Supongo que el sesgo está relacionado con la búsqueda de futuros, pero no estoy seguro.

El código utiliza un enfoque de ventana móvil y crea una lista de ventanas de entrenamiento/prueba adyacentes, según un enfoque estándar de walk-forward. Para cada ventana de entrenamiento, ajusta un modelo de regresión lineal simple basado en algunas variables retardadas y en el rendimiento del día siguiente. A continuación, prueba el modelo en la ventana de prueba adyacente. Tras recorrer la lista de ventanas, los resultados se agregan y se representan.

Basándome en mi código y en la salida que se muestra a continuación, ¿es obvio de dónde viene el sesgo? Mirando los resultados, debo haber mezclado de alguna manera mis ventanas de entrenamiento y de prueba, o posiblemente no estoy entendiendo la forma correcta de retrasar las variables dependientes. En cualquier caso, estoy atascado y cualquier consejo sería muy apreciado.

## Libraries
library(caret);library(quantmod);library(TTR);require(ggplot2);require(reshape)

## Data Acquisition and Wrangling

# download data from yahoo/google
getSymbols("SPY", from="2010-01-01")

# create returns series and some indicators
# remember to lag indicators to prevent look-ahead bias!
ret <- Delt(Op(SPY), Cl(SPY)) #day session return
atr <- ATR(SPY[, c(2:4)], 14)$atr
rsi <- RSI(Cl(SPY), n=14, maType="SMA") #relative strength index
dpo <- DPO(Cl(SPY)) #de-trended price oscillator

dat <- cbind(ret, lag(atr, 1), lag(rsi, 1), lag(dpo, 1))
colnames(dat) <- c("ret", "atr", "rsi", "dpo")
dat <- as.data.frame(dat[complete.cases(dat), ]) #get rid of rows with NA, NaN etc

## Set up train-test windows

# create indexes for TSCV windows
init = 200 #initial window 
horiz = 50 #prediction horizon 
wdw <- createTimeSlices(1:nrow(dat), initialWindow = init, horizon = horiz, 
                        skip = horiz-1, fixedWindow = TRUE)

trainSlices <- wdw[[1]]
testSlices <- wdw[[2]]

# verify visually correct window setup:
trainSlices[[length(trainSlices)]]
testSlices[[length(testSlices)]]

# train and test linear regression model on rolling window
ModelReturn <- list() #store return of each out-of-sample window
BHReturn <- list() #store buy and hold returns for comparison
for(i in c(1:length(testSlices)))
{
  model <- lm(ret ~., data=dat[trainSlices[[i]], ])
  preds <- predict(model, newdata=dat[testSlices[[i]], -1])
  returns <- ifelse(preds>0, dat[testSlices[[i]], 1], -dat[testSlices[[i]], 1])

  ModelReturn[[i]] <- returns
  BHReturn[[i]] <- dat[testSlices[[i]], "ret"]
}

# plot results
results <- data.frame(unlist(ModelReturn), unlist(BHReturn))
colnames(results) <- c("Model", "BuyHold")
plot(cumprod(1+results$Model)-1, type='l', col='blue', ylab="Cum.Return")
lines(cumprod(1+results$BuyHold)-1, col='red')

Y la salida: Output

2voto

Mark McDonald Puntos 2503

La función DPO() desplaza la serie hacia el futuro, creando así el sesgo de anticipación que provoca los resultados extravagantes. Configurando shift a cero se encarga de esto.

Desde el ?DPO documentación:

Descripción

El oscilador de precios de tendencia (DPO) elimina la tendencia de los precios -u otras series- restando una media móvil del precio.

DPO(x, n = 10, maType, shift = n/2 + 1, percent = FALSE, ...)

Argumentos:

desplazamiento: El número de períodos para desplazar la media móvil.

Finanhelp.com

FinanHelp es una comunidad para personas con conocimientos de economía y finanzas, o quiere aprender. Puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X