2011-10-10 16 views
5

En el paquete quantstrat he localizado a uno de los principales culpables de la lentitud de la función applyRule y me pregunto si es más eficiente escribir el ciclo while. Cualquier comentario sería útil. Para cualquier persona que experimente envolviendo esta parte en el Paralelo R.Mientras se repite el código de R quantstrat, ¿cómo hacerlo más rápido?

Como opción, ¿se aplicaría? ¿O debería volver a escribir esta parte en una nueva función como ruleProc y nextIndex? También estoy deleitándome con Rcpp, pero eso puede ser un streach. ¿Alguna ayuda y consejo constructivo es muy apreciado?

while (curIndex) { 
    timestamp = Dates[curIndex] 
    if (isTRUE(hold) & holdtill < timestamp) { 
     hold = FALSE 
     holdtill = NULL 
    } 
    types <- sort(factor(names(strategy$rules), levels = c("pre", 
     "risk", "order", "rebalance", "exit", "enter", "entry", 
     "post"))) 
    for (type in types) { 
     switch(type, pre = { 
      if (length(strategy$rules[[type]]) >= 1) { 
       ruleProc(strategy$rules$pre, timestamp = timestamp, 
       path.dep = path.dep, mktdata = mktdata, portfolio = portfolio, 
       symbol = symbol, ruletype = type, mktinstr = mktinstr) 
      } 
     }, risk = { 
      if (length(strategy$rules$risk) >= 1) { 
       ruleProc(strategy$rules$risk, timestamp = timestamp, 
       path.dep = path.dep, mktdata = mktdata, portfolio = portfolio, 
       symbol = symbol, ruletype = type, mktinstr = mktinstr) 
      } 
     }, order = { 
      if (length(strategy$rules[[type]]) >= 1) { 
       ruleProc(strategy$rules[[type]], timestamp = timestamp, 
       path.dep = path.dep, mktdata = mktdata, portfolio = portfolio, 
       symbol = symbol, ruletype = type, mktinstr = mktinstr,) 
      } else { 
       if (isTRUE(path.dep)) { 
       timespan <- paste("::", timestamp, sep = "") 
       } else timespan = NULL 
       ruleOrderProc(portfolio = portfolio, symbol = symbol, 
       mktdata = mktdata, timespan = timespan) 
      } 
     }, rebalance = , exit = , enter = , entry = { 
      if (isTRUE(hold)) next() 
      if (type == "exit") { 
       if (getPosQty(Portfolio = portfolio, Symbol = symbol, 
       Date = timestamp) == 0) next() 
      } 
      if (length(strategy$rules[[type]]) >= 1) { 
       ruleProc(strategy$rules[[type]], timestamp = timestamp, 
       path.dep = path.dep, mktdata = mktdata, portfolio = portfolio, 
       symbol = symbol, ruletype = type, mktinstr = mktinstr) 
      } 
      if (isTRUE(path.dep) && length(getOrders(portfolio = portfolio, 
       symbol = symbol, status = "open", timespan = timestamp, 
       which.i = TRUE))) { 
      } 
     }, post = { 
      if (length(strategy$rules$post) >= 1) { 
       ruleProc(strategy$rules$post, timestamp = timestamp, 
       path.dep = path.dep, mktdata = mktdata, portfolio = portfolio, 
       symbol = symbol, ruletype = type, mktinstr = mktinstr) 
      } 
     }) 
    } 
    if (isTRUE(path.dep)) 
     curIndex <- nextIndex(curIndex) 
    else curIndex = FALSE 
} 
+0

me gustaría duda el problema es inherente a la 'while', pero lo que sucede en el interior del' while'. Su código no parece hacer nada: no hay asignación de valores o un valor de retorno. Por lo tanto, debe ejecutar esto debido a los efectos secundarios de 'ruleProc'. Si uno de los efectos colaterales es que estás asignando valores en otro lugar, empezaría refaccionando eso. En general, es más rápido usar 'lapply' y' do.call' en la lista resultante que la asignación selectiva usando '[<-', en mi experiencia. – Andrie

+0

Si publicó ejemplos de juguetes de cada una de las estructuras de datos subyacentes presentadas, entonces creo que 'for' podría ser vectorizado. Además, las funciones como 'nextIndex' y' ruleProc' necesitan ser reveladas también. Solo después de eso alguien podría hacer una buena evaluación del ciclo 'while'. – John

+0

OK, acabo de buscar este código ... intente primero vectorizar ruleProc. De hecho, si 'ruleProc' es un ejemplo de cuán terriblemente lento es este código, puede tener aceleraciones grandes y fáciles para casi no pensar. Ir a través de 'ruleProc' y simplemente mover todo fuera del bucle gigante' for' que envuelve toda la función. Es mucho. Luego publícalo para vectorización si no puedes hacer esa parte tú mismo. Pero haz el movimiento primero. – John

Respuesta

7

La respuesta de Garrett apunta a la última gran discusión en la lista de R-SIG-Finanzas donde se discutió una pregunta relacionada.

La función applyRules en quantstrat es absolutamente la mayor parte del tiempo que se gasta.

El código del ciclo while copiado en esta pregunta es la parte dependiente de la ruta de la ejecución de applyRules. Creo que todo esto está cubierto en la documentación, pero revisaré brevemente para SO posterity.

Construimos un índice de reducción de dimensión dentro de applyRules para que no tengamos que observar cada marca de tiempo y verificarlo. Solo tomamos nota de los puntos específicos en el tiempo en los que se puede esperar razonablemente que la estrategia actúe en la cartera de pedidos, o donde es razonable esperar que los pedidos se llenen.

Este es dependiente del estado y código dependiente de la trayectoria. La charla ociosa de 'vectorización' no tiene ningún sentido en este contexto. Si necesito saber el estado actual del mercado, el libro de pedidos y mi posición, y si mis órdenes pueden ser modificadas de manera dependiente del tiempo por otras reglas, no veo cómo se puede vectorizar este código.

Desde una perspectiva informática, esta es una máquina de estado. Las máquinas de estado en casi todos los idiomas en los que puedo pensar suelen escribirse como bucles while. Esto no es realmente negociable ni modificable.

La pregunta si el uso de aplica sería de ayuda. las declaraciones de aplicación en R se implementan como bucles, por lo que no, no ayudaría. Incluso una aplicación paralela como mclapply o foreach no puede ayudar porque está dentro de una parte dependiente del estado del código. La evaluación de diferentes puntos de tiempo sin importar el estado no tiene ningún sentido. Notará que las partes no dependientes del estado de quantstrat se vectorizan siempre que sea posible y representan muy poco tiempo de ejecución.

El comentario hecho por John sugiere eliminar el bucle for en ruleProc. Todo lo que el bucle for hace es verificar cada regla asociada con la estrategia en este punto en el tiempo. La única parte de cálculo intensivo de ese bucle es do.call para llamar a la función de regla. El resto del bucle for es simplemente ubicar y hacer coincidir los argumentos para estas funciones, y desde el perfil del código, no toma mucho tiempo. No tendría mucho sentido utilizar aquí una aplicación paralela, ya que las funciones de regla se aplican en orden de tipo, de modo que las directivas de cancelación o de riesgo se pueden aplicar antes de las nuevas directivas de entrada. Al igual que las matemáticas tienen un orden de operaciones, o un banco tiene una orden de procesamiento de depósito/retiro, quantstrat tiene una orden de evaluación de tipo de regla, tal como se establece en la documentación.

Para acelerar la ejecución, hay cuatro cosas principales que se pueden hacer:

  1. escribir un no camino estrategia depende: Esto es apoyado por el código, y las estrategias simples se pueden modelar de esta manera . En este modelo, escribiría una función de regla personalizada que llame a addTxn directamente cuando crea que debería obtener sus rellenos. Podría ser una función vectorizada que funcione en sus indicadores/señales, y debería ser muy rápida.
  2. preprocesar sus señales: si hay menos lugares donde la máquina de estados necesita evaluar el estado de la cartera de pedidos/reglas/cartera para ver si necesita hacer algo, el aumento de velocidad es casi lineal con la reducción de señales. Este es el área que la mayoría de los usuarios descuidan, escribiendo funciones de señal que realmente no evalúan cuándo se puede requerir una acción que modifique las posiciones o la cartera de pedidos.
  3. paralelizar explícitamente partes de su problema de análisis: I comúnmente escribo envolturas explícitamente paralelas para separar las diferentes evaluaciones de parámetros o evaluaciones de símbolos, ver applyParameter para un ejemplo utilizando foreach
  4. reescribir la máquina de estado dentro applyRules en C/C++: Los parches son bienvenidos, pero vea el enlace que Garrett publicó para obtener detalles adicionales.

Puedo asegurarle que la mayoría de las estrategias pueden ejecutarse en una fracción de minutos centrales por símbolo por día por núcleo en los datos de tics, si se tiene cuidado de las funciones de generación de señal. No se recomienda ejecutar grandes backtest en una computadora portátil.

Ref: quantstrat - applyRules

Cuestiones relacionadas