Documentación
Aplicaciones
Última actualización el 11. 1. 2023 by Mark Fric
Selección programática de módulos
Contenido de la página
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:
- encontrar su elemento .
- 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
Gracias Mark por este excelente ejemplo
Será muy útil