Documentación

Aplicaciones

Última actualización el 11. 1. 2023 by Mark Fric

Selección programática de módulos

Se trata de una ampliación de este artículo: https://strategyquant.com/doc/programming-for-sq/changing-task-config-programmatically/
que demostraba cómo se puede cambiar la configuración de las tareas mediante programación.

La idea general del ejemplo inicial es que se puede cambiar la configuración de la tarea manipulando su configuración XML y volviéndola a aplicar a la tarea.
En este ejemplo demostraremos cómo puede activar/desactivar bloques de construcción de esta forma.

 

Cómo se almacenan los módulos en el XML de la tarea

Todos los bloques se almacenan como hijos y subhijos en el elemento ->.

Cada building block se configura en su propio elemento , identificado unívocamente por su clave - que es el nombre de su fragmento.

 

Hay algunas normas en valores clave:

  • si es bloque de señales o comparaciónla clave sólo contiene el nombre del fragmento, por ejemplo key="ADXCambiosAbajo" o key="IsGreater"
  • si es indicador (no señal), su clave va precedida de la cadena "Indicador", por ejemplo key="Indicadores.ADX"
  • si es preciosu clave va precedida de la cadena "Precios", por ejemplo key="Precios.Altos"
  • si es nivel de parada/límite (para órdenes Stop o Límite), su clave va precedida de la cadena "Niveles de precios Stop/Límite", por ejemplo key="Niveles Precio Stop/Límite.TEMA"
  • si es rango de parada/límite (para órdenes Stop o Límite), su clave va precedida de la cadena "Rangos de precios Stop/Límite", por ejemplo key="Rangos de precios tope/límite.BBWidthRatio"

 

Otro atributo importante del elemento es utilice. Si desea que el bloque se utilice durante la compilación, debe establecerlo en "true"; de lo contrario, en "false".

Así, si quieres activar o desactivar un bloque concreto, sólo tienes que:

  1. encontrar su elemento .
  2. establezca el atributo use en true o false así: ...

 

Hay un ejemplo de Builder task XML adjunto en este artículo - archivo Build-Task1.xml

 

Ejemplo

Como ejemplo de esta funcionalidad cambiaremos programáticamente entre los bloques de señales Keltner Channel (KC) y Bollinger Bands (BB), todos los demás bloques estarán desactivados.

Tendremos un proyecto personalizado con dos tareas - la primera será Custom Analysis task que aplicará nuestro snippet Custom Analysis. La segunda es la tarea Builder - modificaremos la configuración de esta tarea Builder en nuestro snippet CA.

 

Así, cuando ejecute el proyecto personalizado por primera vez, sólo se habilitarán los bloques de señales de las Bandas de Bollinger. Cuando lo ejecute por segunda vez, sólo se habilitarán los bloques del Canal de Keltner, y así sucesivamente.

Todos los bloques de señales de las Bandas de Bollinger tienen fragmentos denominados con el prefijo BB - BB*********, por ejemplo key="BBBarClosesAboveDown", y todos los bloques de señales del Canal de Keltner tienen fragmentos denominados KC*********, por ejemplo key="KCBarClosesAboveLower".
Utilizaremos este prefijo BB o KC para reconocer que se trata de un bloque de señal del Canal de Keltner o de las Bandas de Bollinger.

El siguiente ejemplo es esencialmente el mismo que el del artículo https://strategyquant.com/doc/programming-for-sq/changing-task-config-programmatically/ - sólo que aquí hacemos cosas diferentes con el XML.

Utilizamos JDOM para recorrer el XML y obtener o modificar sus atributos.

Paso 1 - Obtener todos los elementos del bloque

Elemento elBlocks = XMLUtil.getChildElem(elNuevoConfig, "Bloques");
if(elBlocks == null) {
    throw new Exception("No existe ningún elemento , ¿es tarea del constructor?");
}
Elemento buildingBlocks = XMLUtil.getChildElem(elBlocks, "BuildingBlocks");
// alternativamente puedes cargar la configuración de la nueva tarea desde el fichero preparado
//Element elNewConfig = XMLUtil.fileToXmlElement(new File("c:/BuildTask2009_2019.xml"));

List blocks = buildingBlocks.getChildren("Block");

 

Paso 2 - Inicializar la variable activeBlock

utilizaremos esta variable para determinar si se deben utilizar los bloques BB o KC - recuerde, estamos cambiando entre los bloques BB y KC, por lo que debemos determinar cuál de los dos debe activarse y desactivarse.

// usaremos esta variable para cambiar de BB a KC. Los valores son:
// 0 - no se conoce, se activará con el primer bloque encontrado
// 1 - Los bloques BB deben estar activos
// 2 - Los bloques KC deben estar activos
int bloqueactivo = 0;

 

Paso 3 - Recorrer todos los bloques en bucle

for(int i=0; i<blocks.size(); i++) {
    Elemento elBlock = blocks.get(i);

    String clave = elBlock.getAttributeValue("clave");

 

Paso 4 - Si la tecla no empieza por BB o KC, se trata de un bloque diferente y debe desactivarse.

if(!key.startsWith("BB") && !key.startsWith("KC")) {
    // no es un bloque de Bandas de Bollinger ni de Canal de Keltner, desactívelo
    elBlock.setAttribute("use", "false");
    continuar;
}

 

Paso 5 - De lo contrario, se trata de un bloque KC o BB

Ahora reconoce el valor apropiado de la variable activeBlock:

// es un bloque BB o KC
String use = elBlock.getAttributeValue("use");

if(activeBlock == 0) {
    // determinaremos si los bloques BB o KC deben estar activos por el primer bloque encontrado
    if(key.startsWith("BB")) {
        if (use.equals("true")) {
            // BB estaba activo antes, ahora lo estará KC
            activeBlock = 2;
        }
    } else if(key.startsWith("KC")) {
        if(use.equals("true")) {
            // KC estaba activo antes, ahora BB estará activo
            activeBlock = 1;
        }
    }

    if(activeBlock == 0) {
        activeBlock = 1;
    }

    Log.info("----- Activando "+(activeBlock == 1 ? "Bandas Bollinger" : "Canal Keltner")+" bloques.");
}

 

Paso 6: lo más importante

Activar los bloques BB o KC en función del activeBlock estableciendo su atributo de uso en verdadero o falso:

// ahora activa o desactiva el bloque según el valor de activeBlock
if(key.startsWith("BB")) {
    elBlock.setAttribute("use", (activeBlock == 1 ? "true" : "false"));
} else if(key.startsWith("KC")) {
    elBlock.setAttribute("use", (activeBlock == 2 ? "true" : "false"));
}

 

Paso 7 - aplicar la configuración modificada a la tarea

Hemos terminado de pasar por todos los bloques de construcción, ahora sólo tiene que aplicar la configuración modificada a la tarea:

// Aplicar la configuración modificada a la tarea
buildTask.setConfig(elNuevoConfig, true);

// esto notifica a la UI que cargue la configuración modificada y la aplique a la UI.
// Sin esto seguiría funcionando pero no se verían los cambios en la UI
JSONObject jsonData = new JSONObject().put("projectConfig", sqProject.toJSON());
DataToSend dataToSend = new DataToSend(WebSocketConst.UpdateProject, jsonData);
SQWebSocketManager.addToDataQueue(dataToSend, SQConst.CODE_TASKMANAGER);

 

Y esto es todo.

Ahora, cada vez que inicie el proyecto personalizado, éste cambiará los bloques de construcción entre las Bandas de Bollinger y las señales del Canal de Keltner.

 

 

Código completo del fragmento CAChangeBlocksConfig:

paquete SQ.CustomAnalysis;

import com.strategyquant.lib.XMLUtil;
import com.strategyquant.lib.constants.SQConst;
import com.strategyquant.tradinglib.CustomAnalysisMethod;
import com.strategyquant.tradinglib.ResultsGroup;
import com.strategyquant.tradinglib.project.ProjectEngine;
import com.strategyquant.tradinglib.project.SQProject;
import com.strategyquant.tradinglib.project.websocket.DataToSend;
import com.strategyquant.tradinglib.project.websocket.SQWebSocketManager;
import com.strategyquant.tradinglib.project.websocket.WebSocketConst;
import com.strategyquant.tradinglib.taskImpl.ISQTask;
import org.jdom2.Element;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.List;

public class CAChangeBlocksConfig extends CustomAnalysisMethod {

    //------------------------------------------------------------------------
    //------------------------------------------------------------------------
    //------------------------------------------------------------------------

    public CAChangeBlocksConfig() {
        super("CAChangeBlocksConfig", TYPE_PROCESS_DATABANK);
    }

    //------------------------------------------------------------------------

    @Override
    public ArrayList processDatabank(String project, String task, String databankName, ArrayList databankRG) throws Exception {

        // obtener el proyecto actual
        SQProject sqProject = ProjectEngine.get(project);

        // obtener tarea por nombre
        ISQTask buildTask = sqProject.getTaskByName("Construir estrategias");
        if(buildTask == null) {
            throw new Exception("¡No existe tal tarea!");
        }
        // también puede utilizar sqProject.getTasks() para obtener una lista de todas las tareas
        // y recorrerlas hasta obtener la que desea


        // así es como se obtiene el XML de configuración de tareas de la nueva forma (SQ 136 up)
        // Devuelve el elemento XML JDOM con la configuración completa de la tarea
        Elemento elConfig = buildTask.getConfig();
        // crea una nueva config clonandola - es importante hacer un clon,
        // de lo contrario no se aplicará la nueva configuración
        Elemento elNewConfig = elConfig.clone();

        // ahora cambia cualquier cosa en la nueva configuración
        Elemento elBlocks = XMLUtil.getChildElem(elNewConfig, "Blocks");
        if(elBlocks == null) {
            throw new Exception("No existe el elemento , ¿es tarea del constructor?");
        }
        Elemento buildingBlocks = XMLUtil.getChildElem(elBlocks, "BuildingBlocks");
        // alternativamente puedes cargar la configuración de la nueva tarea desde el fichero preparado
        //Element elNewConfig = XMLUtil.fileToXmlElement(new File("c:/BuildTask2009_2019.xml"));

        List blocks = buildingBlocks.getChildren("Block");

        // usaremos esta variable para cambiar de BB a KC. Los valores son:
        // 0 - no se conoce, lo establecerá el primer bloque encontrado
        // 1 - Los bloques BB deben estar activos
        // 2 - Los bloques KC deben estar activos
        int bloqueactivo = 0;

        for(int i=0; i<blocks.size(); i++) {
            Elemento elBlock = blocks.get(i);

            String clave = elBlock.getAttributeValue("clave");

            if(!key.startsWith("BB") && !key.startsWith("KC")) {
                // no es un bloque de Bandas de Bollinger ni de Canal de Keltner, desactívelo
                elBlock.setAttribute("use", "false");
                continuar;
            }

            // es un bloque BB o KC
            String use = elBlock.getAttributeValue("use");

            if(activeBlock == 0) {
                // determinaremos si los bloques BB o KC deben estar activos por el primer bloque encontrado
                if(key.startsWith("BB")) {
                    if (use.equals("true")) {
                        // BB estaba activo antes, ahora lo estará KC
                        activeBlock = 2;
                    }
                } else if(key.startsWith("KC")) {
                    if(use.equals("true")) {
                        // KC estaba activo antes, ahora BB estará activo
                        activeBlock = 1;
                    }
                }

                if(activeBlock == 0) {
                    activeBlock = 1;
                }

                Log.info("----- Activación de "+(activeBlock == 1 ? "Bollinger Bands" : "Keltner Channel")+" bloques.");
            }

            // ahora activa o desactiva el bloque según el valor de activeBlock
            if(key.startsWith("BB")) {
                elBlock.setAttribute("use", (activeBlock == 1 ? "true" : "false"));
            } else if(key.startsWith("KC")) {
                elBlock.setAttribute("use", (activeBlock == 2 ? "true" : "false"));
            }
        }


        // Aplicar la configuración modificada a la tarea
        buildTask.setConfig(elNuevoConfig, true);

        // esto notifica a la UI que cargue la configuración modificada y la aplique a la UI.
        // Sin esto seguiría funcionando pero no se verían los cambios en la UI
        JSONObject jsonData = new JSONObject().put("projectConfig", sqProject.toJSON());
        DataToSend dataToSend = new DataToSend(WebSocketConst.UpdateProject, jsonData);
        SQWebSocketManager.addToDataQueue(dataToSend, SQConst.CODE_TASKMANAGER);

        return databankRG;
    }
}

 

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

Suscríbase a
Notificar a
1 Comentario
Más antiguo
Más reciente Más votados
Feedbacks de Inline
Ver todos los comentarios
Emmanuel
15. 1. 2023 19:24

Gracias Mark por este excelente ejemplo
Será muy útil

Puestos relacionados