Documentación

Aplicaciones

Última actualización el 4. 8. 2021 por Mark Fric

Ejemplo - análisis personalizado por estrategia

Haremos un análisis personalizado por estrategia como primer ejemplo. Es sólo una simple demostración, pero muestra cómo utilizar análisis personalizados también en conjunción con columnas personalizadas del banco de datos.

Este ejemplo hará lo siguiente:

  • El método de análisis personalizado revisará los resultados almacenados tras las pruebas retrospectivas de la estrategia y calculará cuántas comprobaciones cruzadas se utilizaron.
  • A continuación, una nueva columna personalizada de la base de datos mostrará este número en la base de datos

 

Paso 1 - Crear un nuevo fragmento de análisis personalizado

Abrir Editor de códigohaga clic en Crear nuevo y elija Análisis personalizado (CA) en la parte inferior. Ponle un nombre CAExampleStr.

Esto creará un nuevo fragmento CAExampleStr.java en carpeta Usuario/Snippets/SQ/Análisis personalizado

Este fragmento tiene actualmente el siguiente aspecto:

paquete SQ.CustomAnalysis;

importar com.strategyquant.lib.*;

import java.util.ArrayList;

import com.strategyquant.datalib.*;
import com.strategyquant.tradinglib.*;

public class CAExampleStr extends CustomAnalysisMethod {

    //------------------------------------------------------------------------
    //------------------------------------------------------------------------
    //------------------------------------------------------------------------
    
    public CAExampleStr() {
        super("CAExampleStr", TYPE_FILTER_STRATEGY);
    }
    
    //------------------------------------------------------------------------
    
    @Override
    public boolean filterStrategy(String project, String task, String databankName, ResultsGroup rg) throws Exception {
        return true;
    }
    
    
    //------------------------------------------------------------------------
    
    @Override
    public ArrayList processDatabank(String project, String task, String databankName, ArrayList databankRG) throws Exception {
        return databankRG;
    }
}

 

Como puede ver, tiene tres métodos:

CAExampleStr()
un constructor, lo único que debe establecer aquí es el nombre de su método CA que aparecerá en la interfaz de usuario, y el tipo.

Por defecto, el tipo es TYPE_FILTER_STRATEGY, lo que significa que es un método por estrategia.

Otras opciones posibles son:

TYPE_PROCESS_DATABANK - CA que procesará todo el banco de datos, lo mostraremos en otro ejemplo.
TYPE_BOTH - CA que tiene métodos tanto para el análisis por estrategia como por banco de datos.

El tipo de análisis personalizado determina si este fragmento se muestra en la opción por estrategia o por banco de datos.


filtroEstrategia()

este es un método que es llamado para cada estrategia antes de ser guardada en el banco de datos - si el uso de análisis personalizados está configurado. Obtendrá una estrategia con backtests y crosscheck completados (objeto ResultsGroup) y debe devolver true/false.


procesarBancoDeDatos()
es un método que se llama para procesar todo el banco de datos - obtendrá un array de todos los resultados de la estrategia (ResultsGroups) como parámetro, y devuelve un array de ResutsGroups.

Como nuestro método es sólo por estrategia, no necesitamos procesarBancoDeDatos() y podemos eliminarlo del fragmento.

 

Paso 2 - Implementar el método filterStrategy()

Toda la "magia" se hará en filtroEstrategia() aquí contaremos el número de comprobaciones cruzadas que se utilizaron en la estrategia. ¿Cómo hacerlo?

Grupo de resultados es un objeto que contiene la estrategia y todos los resultados del backtest + crosscheck de la estrategia. Como su nombre indica, es un grupo de resultados que se almacenan bajo varias claves.

Para contar cuántas comprobaciones cruzadas se han utilizado, hay que recorrer todos los resultados y comprobar si existen los resultados de una comprobación cruzada determinada. Si no es así, no se ha utilizado la comprobación cruzada.

Para hacerlo más complicado, algunos resultados (por ejemplo Monte Carlo) se almacenan de una manera especial, por lo que debemos determinar su uso de forma diferente.

En el momento de redactar este artículo hay 8 controles cruzados diferentes en SQ:

  1. ¿Y si las simulaciones
  2. Manipulación de operaciones Monte Carlo
  3. Mayor precisión en las pruebas retrospectivas
  4. Pruebas retrospectivas en otros mercados
  5. Métodos de repetición de Monte Carlo
  6. perfil / Sys. Param. Permutación
  7. Optimización Walk-Forward
  8. Matriz Walk-Forward

Si configurara Retester para realizar todas las comprobaciones cruzadas y luego registrara todas las claves+resultados que se almacenan en ResultsGroup obtendría estas claves:
Cartera
Principal: EURUSD_M1/H1
Comprobación_cruzada_WhatIf
CrossCheck_HigherPrecision
MercadoAdicional: GBPUSD_M1/H1
MercadoAdicional: USDJPY_M1/H1
WF: 10 carreras : 20 % OOS
WF: 5 carreras : 10 % OOS
...
WF: 15 carreras : 40 % OOS
WF: 20 carreras : 40 % OOS

Tenga en cuenta que el número real de claves depende también de la configuración de las comprobaciones cruzadas. Por ejemplo, cada backtest adicional se almacena bajo su propia clave, lo mismo es válido para las ejecuciones WF + combinaciones OOS.

Debido a que había algunos mercados adicionales también hay una clave especial Cartera que contiene el resultado de la cartera principal + backtests adicionales.

Observe también que no hay teclas para Montecarlo ni para Perfil opc. Param. Permutación, se almacenan de otra manera que explicaremos más adelante.

 

Paso 3 - Recuento de los cheques cruzados "simples

Por simples entendemos las comprobaciones cruzadas que almacenan sus resultados como claves diferentes en Grupo de resultados objetos.

Determinar si se ha utilizado la comprobación cruzada es sencillo en este caso, el código de filtroEstrategia() quedaría así:

public boolean filterStrategy(String project, String task, String databankName, ResultsGroup rg) throws Exception {
        List keys = rg.getResultKeys();
        
        int crosschecksCount = 0;
        boolean additionalMarketUsed = false;
        int wfCount = 0;
        
        for(int i=0; i 1) {
            // contiene la matriz Walk-Forward
            crosschecksCount++;
            
            // esto es complicado, cuando se ejecutó la matriz WF no podemos determinar
            // si también se ejecutó una única Optimización Walk-Forward,
            // así que la contaremos también
            crosschecksCount++;
        }

        // contiene manipulación de operaciones Monte Carlo
        // contiene métodos de repetición de Monte Carlo
        // contiene Opt. profile / Sys. Param. Permutación
        // POR HACER
        ...

Determinación del uso de ¿Y si y Mayor precisión Las comprobaciones cruzadas son sencillas: sólo hay que comprobar si la lista de claves contiene la constante.

Determinación del uso de Pruebas retrospectivas en otros mercados es similar, sólo debemos ser conscientes de que cada backtest adicional se almacena con su propia clave.

Determinación del uso de Optimización Walk-Forward y Matriz también es sencillo, sólo comprobaremos el número (si lo hay) de claves con prefijo "WF:"

Lo único delicado aquí es que si Matriz Walk-Forward entonces no podemos reconocer si también se ha utilizado una única Optimización Walk-Forward porque utiliza la misma clave para almacenar su resultado.

Nuestra crosschecksCount contiene ahora el número real de estas comprobaciones cruzadas utilizadas, excepto para Montecarlo y Perfil Opt. controles cruzados que reconoceremos en el siguiente paso.

 

Paso 4 - Reconocer el uso de la comprobación cruzada "especial

Como ya se ha dicho, Montecarlo y Perfil opc. Param. Permutación los resultados se almacenan de forma especial.

En lugar de guardarlos bajo su propia clave en ResultsGroup, se almacenan en objetos especiales en el resultado principal.

Podemos determinar si la manipulación de Monte Carlo cruzar los resultados se utilizó así:

// obtener el resultado principal, aquí es donde se almacenan las simulaciones Monte Carlo
Resultado principalResultado = rg.mainResult();
        
if(mainResult.getInt("MonteCarloManipulation_NumberOfSimulations") > 0) {
// contiene el resultado de la manipulación de operaciones Monte Carlo
        crosschecksCount++;
}

Main result es el resultado principal del backtest, aquí es donde se almacenan los resultados MC. Simplemente intentamos obtener un número de simulaciones MC, y si es mayor que 0 sabemos que contiene algunos resultados de manipulación MC.

Podemos comprobar el uso f Monte Carlo retest crosscheck de forma similar:

if(mainResult.getInt("MonteCarloRetest_NumberOfSimulations") > 0) {
    // contiene el resultado de la manipulación de las operaciones Monte Carlo
    crosschecksCount++;
}

El último que hay que comprobar es Opt. profile / Sys. Param. Permutación. Se almacena en su propio objeto en ResutsGroup, simplemente comprobaremos si el objeto existe allí:

if(rg.getOptimizationProfile() != null) {
        // contiene el perfil de optimización / Sys. Param. Resultado de permutación
        crosschecksCount++;
    }

 

Paso 5 - Guardar el recuento en los resultados de la estrategia

Ahora que tenemos el recuento de los controles cruzados utilizados, queremos mostrarlo en una nueva columna del banco de datos (que se creará más adelante)

Ahora mismo, tenemos el número de comprobaciones cruzadas guardado en una variable crosschecksCount, pero necesitamos guardarlo como un dato personalizado en ResultsGroup, para que el fragmento de columna del banco de datos pueda recuperarlo cuando vaya a mostrar su valor.

Afortunadamente, esto es bastante simple - podemos guardar cualquier objeto a valores especiales ResultsGroup llamando:

rg.specialValues().set(String clave, Object valor);

en nuestro caso:

rg.specialValues().set("CA_NumberOfCrosschecks", crosschecksCount);

Recuperaremos este valor en el siguiente paso para mostrarlo en el banco de datos.

Como último devolveremos true porque no vamos a utilizar este análisis personalizado para filtrar.


Nota -
Si desea utilizar el análisis personalizado para el filtrado simplemente devolverá verdadero o falso en este método basado en los cálculos de las pruebas retrospectivas de la estrategia y dependiendo de esto la estrategia se descarta o se guarda en el banco de datos.

Así que el código completo de nuestro filtroEstrategia() método es:

    @Override
public boolean filterStrategy(String project, String task, String databankName, ResultsGroup rg) throws Exception {
    List keys = rg.getResultKeys();
    
    int crosschecksCount = 0;
    boolean additionalMarketUsed = false;
    int wfCount = 0;
    
    for(int i=0; i 1) {
        // contiene resultados de la matriz Walk-Forward
        crosschecksCount++;
        
        // esto es complicado, cuando se ejecutó la matriz WF no podemos determinar
        // si también se ejecutó una única Optimización Walk-Forward,
        // así que lo contaremos también
        crosschecksCount++;
    }

    // obtener resultado principal, aquí es donde se almacenan las simulaciones Monte Carlo
    Resultado principalResultado = rg.mainResult();
    
    if(mainResult.getInt("MonteCarloManipulation_NumberOfSimulations") > 0) {
        // contiene el resultado de la manipulación de operaciones Monte Carlo
        crosschecksCount++;
    }

    if(mainResult.getInt("MonteCarloRetest_NumberOfSimulations") > 0) {
        // contiene el resultado de la manipulación de las operaciones Monte Carlo
        crosschecksCount++;
    }
    
    if(rg.getOptimizationProfile() != null) {
        // contiene perfil de optimización / Sys. Param. Resultado de permutación
        crosschecksCount++;
    }
    
    
    rg.specialValues().set("CA_NumberOfCrosschecks", crosschecksCount);
    
    return true;
}

 

Paso 6 - Añadir una nueva columna a la base de datos

Hemos calculado el número de comprobaciones cruzadas utilizadas en el fragmento de análisis personalizado y ahora queremos mostrarlo en el banco de datos. Para ello, debemos crear una nueva columna en el banco de datos.

De nuevo, haga clic en Crear nuevo en Editor de código y esta vez cree una nueva columna de base de datos. Nómbrala NúmeroOfCC.

El código predeterminado para el fragmento de columna del banco de datos tiene el siguiente aspecto:

paquete SQ.Columns.Databanks;

import com.strategyquant.lib.*;
import com.strategyquant.datalib.*;
import com.strategyquant.tradinglib.*;

public class NúmeroOfCC extends DatabankColumna {
    
  public NúmeroOfCC() {
    super("NúmeroOfCC",
          DatabankColumn.Decimal2, // formato de visualización del valor
          ValueTypes.Maximize, // si el valor debe maximizarse / minimizarse / aproximarse a un valor
          0, // valor objetivo si se elige la aproximación
          0, // mínimo medio de este valor
          100); // máximo medio de este valor
    
    setWidth(80); // ancho de columna por defecto en píxeles
    
    setTooltip("Su tooltip aquí");
    
    /* Si esta nueva columna depende de otras columnas que tienen que ser calculadas primero, ponlas aquí.
       Asegúrate de no crear dependencias circulares, como A depende de B y B depende de A.
       Las columnas (=valores estadísticos) se identifican por el nombre de la clase)
    */
    //setDependencies("DrawdownPct", "NumberOfTrades");
  }
  
  //------------------------------------------------------------------------

  /**
   * Este método debe devolver el valor calculado de esta nueva columna. Típicamente debería calcularlo a partir de la lista de órdenes
   * o a partir de algunos valores estadísticos ya calculados (otras columnas del banco de datos).
   */
  @Override
  public double compute(SQStats stats, StatsTypeCombination combination, OrdersList ordersList, SettingsMap settings, SQStats statsLong, SQStats statsShort) throws Exception {
    
    /* un ejemplo - así se pueden obtener otros valores de los que depende este nuevo valor */
    //int numberOfTrades = stats.getInt("NumberOfTrades");
    //double drawdownPct = stats.getDouble("DrawdownPct");
    
    /* un ejemplo - calcular el beneficio neto a partir de la lista de operaciones */
    double netProfit = 0;

    for(int i = 0; i<ordersList.size(); i++) {
      Orden = ordersList.get(i);
      
      if(order.isBalanceOrder()) {
        // no contar órdenes de saldo (depósitos, retiradas) en
        continuar;
      }
      
      /* el método getPLByStatsType devuelve automáticamente PL dependiendo del tipo de estadísticas - en dinero, % o pips */
      double PL = getPLByStatsType(orden, combinación);
      
      netProfit += PL;
    }

    /* redondea y devuelve el valor. Se guardará en las estadísticas bajo la clave "NumberOfCC" */
    return round2(netProfit);
  }
}

Tiene un calcular() que puede recorrer las estadísticas y los pedidos para calcular la métrica concreta.

En nuestro caso no lo necesitamos, no utilizaremos nada de pedidos, en su lugar recuperaremos el valor de CA_NúmeroDeCruces que calculamos en el análisis personalizado.

Así que podemos eliminar todo el calcular() y utilizar un método especial getValue() en su lugar. Este no se genera por defecto, se utiliza para recuperar valores especiales, por ejemplo símbolo y timeframe.

getValue() obtiene el Grupo de resultados como parámetro y devuelve una Cadena que se muestra en el banco de datos para esta columna.

Nuestra columna simplificada del banco de datos tiene este aspecto:

paquete SQ.Columns.Databanks;

import com.strategyquant.lib.*;
import com.strategyquant.datalib.*;
import com.strategyquant.tradinglib.*;

public class NúmeroOfCC extends DatabankColumna {
    
  public NúmeroOfCC() {
    super("NumeroOfCC", DatabankColumn.Decimal2, ValueTypes.Maximize, 0, 0, 100);
  }
  
  //------------------------------------------------------------------------
  
  @Override
  public String getValue(ResultsGroup rg, String resultKey, byte direction, byte plType, byte sampleType) throws Exception {
    int value = rg.specialValues().getInt("CA_NumberOfCrosschecks", -1);
    
    if(value == -1) {
      // esto significa que no se ha establecido el valor
      return NOT_AVAILABLE;
    } else {
      // devuelve el valor convertido a String
      return java.lang.Integer.toString(valor);
    }
  }
}

 

Tenga en cuenta que hemos utilizado una llamada:

rg.specialValues().getInt("CA_NumberOfCrosschecks", -1);

para obtener el valor almacenado previamente por el análisis personalizado como int (número).

El valor -1 es el valor por defecto, se utilizará si no hay ningún valor para la clave CA_NúmeroDeCruces será encontrado. Esto ocurrirá si no configuramos UI para ejecutar nuestro método de análisis personalizado.

En este caso devolveremos N/A string.

 

Nota especial sobre el filtrado según esta columna

Debido a que esta columna del banco de datos es especial, y su valor es calculado por el análisis personalizado, NO PUEDE ser utilizada en los filtros personalizados de Ranking. Esto no funcionará porque estos filtros se evalúan antes de que se inicie el análisis personalizado, y esta columna no tendrá valor entonces.

Para filtrar utilizando valores calculados por análisis personalizados, utilice el retorno verdadero/falso de la función filtroEstrategia() en el fragmento de análisis personalizado.

 

Paso 7 - Hacer que todo funcione

Tras compilar correctamente nuestros nuevos fragmentos en Editor de código y reinicio de StrategyQuant ahora podemos intentar utilizarlo. Podemos hacerlo en Retester para ver cómo funciona.

Primero creamos una nueva vista del banco de datos, la llamamos Vista CA y añadimos algunas columnas + nuestra nueva columna NúmeroOfCC allí.

Cuando cambiemos a esta vista veremos que nuestra nueva columna es N/A:

Esto se debe a que el valor que muestra aún no se ha calculado.

Utilicemos el nuevo fragmento de análisis personalizado en el archivo Retester - sólo debemos configurarlo en la pestaña Ranking de la siguiente manera:

Nuestro fragmento de análisis personalizado de ejemplo se aplicará ahora a cada estrategia que se vuelva a probar en Retester antes de guardarla en un banco de datos.

Así pues, intentemos activar algunas comprobaciones cruzadas en Retester y ejecútalo.

Dependiendo del número de controles cruzados que haya seleccionado, debería ver algún número en la nueva columna:

 

Conclusión

Se trata de una simple demostración de la nueva funcionalidad de análisis personalizado aplicada por estrategia. Como se puede ver:

  • puede utilizarse para calcular nuevas métricas que traspasen los límites de un único backtest o crosscheck
  • puede utilizarse para aplicar nuevas métricas que se muestran en el banco de datos
  • puede utilizarse para filtrar (devolviendo false)

 

¿Le ha resultado útil este artículo? El artículo era útil El artículo no era útil

Suscríbase a
Notificar a
5 Comentarios
Más antiguo
Más reciente Más votados
Feedbacks de Inline
Ver todos los comentarios
tan8858
tan8858
21. 8. 2021 3:32 pm

Por favor, publique el código completo para el paso 5(mejor como el paso 6 Ejemplo)
En mi editor de código, se informa de un error
"no se puede encontrar el símbolo symbol:class Lista location:class SQ.CustomAnalysis.CAExampleStr"

List keys = rg.getResultKeys();

Lo siento soy nuevo en esto y no pude encontrar el error

Emmanuel
Responder a  tan8858
26. 11. 2021 4:17 pm

A mi también me da el mismo error.

Emmanuel
Responder a  tan8858
26. 11. 2021 4:54 pm

Creo que hay que añadir : importar java.util.*;  
en la parte superior del código
Debería funcionar

tan8858
tan8858
Responder a  Emmanuel
28. 11. 2021 10:00 am

Gracias.

Emmanuel
25. 11. 2021 20:59

¡Excelente!