Reading Strategy settings, variables, rules and building blocks in few easy step using Xml
Page contents
Thanks to a previous example : https://strategyquant.com/doc/programming-for-sq/viewing-and-changing-strategy-parameters-version-2/, we can read a strategy and its variables.
This code example is going one step further by reading the XML of a strategy, its variables and its rules.
1/ Overview of a Strategy
- A Strategy Xml can be read with the XMLUtil.elementToString() function.
You can run a Custom Analysis XmlStrategyToFile or XmlStrategyToFileSym (for symmetrical strategies) to transform any strategy from your Databank to your drive to an output path like “C:\Temp”
Then you can open this XML file in any xml reader (like https://jsonformatter.org/xml-editor for example.)
You can read a template as well , if you load your template in Databank.
(Please see the attached Custom Project StrategyToXmlFile and the attached Xml of a strategy Strategy 1.6.22(1)B)
- A Strategies have rules and Variables :
If you open the SQ.utils.Bouillant extension, you will find a similar structure to open a strategy:
In “toBuildingBlocksName” function, we can see two regions of the code , Variables and Rules as well
2/ We are using XMLUtil Class and JDOM Element
https://strategyquant.com/sqxapi/com/strategyquant/lib/XMLUtil.html
3/ How to read strategy variables
Once, we get the XML from a Strategy, we have to get its variables values. They can be Boolean, Integer, or Double.
To get them, you have three functions :
-
getVariableValueToBoolean
-
getVariableValueToInt
-
getVariableValueToDouble
From these functions, we can get any variables values from a strategy.
4/ For example, we can get a Stop loss value or a Profit Target value from the strategy settings and insert the results in a databank :
With this extension, you can find a Custom Analysis “CA_StrategySettings” or “CA_StrategySettingsSym” (for symmetrical strategies)
These Custom Analysis will call SQ.utils.Bouillant extension and request a Stop Loss value or a Profit Target value from a signal :
@Override public boolean filterStrategy(String project, String task, String databankName, ResultsGroup rg) throws Exception { Bouillant BouilltParaObj1 = new Bouillant(); int IntTemp = BouilltParaObj1.getVariableValueToInt(rg, false,"LongProfitTarget"); rg.specialValues().set("CA_Long_ProfitTarget", IntTemp); IntTemp = BouilltParaObj1.getVariableValueToInt(rg, false,"ShortProfitTarget"); rg.specialValues().set("CA_Short_ProfitTarget", IntTemp); IntTemp = BouilltParaObj1.getVariableValueToInt(rg, false,"LongStopLoss"); rg.specialValues().set("CA_Long_StopLoss", IntTemp); IntTemp = BouilltParaObj1.getVariableValueToInt(rg, false,"ShortStopLoss"); rg.specialValues().set("CA_Short_StopLoss", IntTemp); IntTemp = BouilltParaObj1.getVariableValueToInt(rg, false,"LongTrailingStop"); rg.specialValues().set("CA_Long_TrailingStop", IntTemp); IntTemp = BouilltParaObj1.getVariableValueToInt(rg, false,"ShortTrailingStop"); rg.specialValues().set("CA_Short_TrailingStop", IntTemp); return true; }
(As you can see in the attached Custom Project : StrategySettings)
Once you run “CA_StrategySettings” custom analysis or “CA_StrategySettingsSym” custom analysis , you can read a stop loss value or a profit target value from any strategy settings and update your databank with the following snippets :
Here are the Profit Target value and Stop Loss Value from the strategy Settings
5/ How to read XML Strategy Step by Step
(from our function toBuildingBlocksName in SQ.Utils.Bouillant)
A/ Our example is using getStrategyBase function to get the root Element of a strategy :
StrategyBase strategyBase = getStrategyBase(strategyRG, symmetricVariables); Element Strategie1 = strategyBase.getStrategyXml();
B/ From the Strategie1 Element, it is getting a specific child Element
Element elParams = Strategie1.getChild("Strategy").getChild("Variables");
or
elParams = Strategie1.getChild("Strategy").getChild("Rules").getChild("Events"); // go to Rules section of the Strategy Xml
C/ From the elParams Element, it is getting the childrens elements to a List of Elements paramElems
List<Element> paramElems = elParams.getChildren();
D/ From List of Elements paramElems, you can get each Element and read the NodeValue by using getNodeValue :
for (i = 0; i < paramElems.size(); i++) { Element elParam = paramElems.get(i); String name = XMLUtil.getNodeValue(elParam,"name"); String id = XMLUtil.getNodeValue(elParam,"id"); if (name.equals(TradingSignalName)) // We search the Id of the TradingSignalName, necessary to find the corresponding rule { SignalID = id; } }
E/ From List of Elements paramElems, you can get each AttributeValue of an Element :
String elParamKey = elParam.getAttributeValue("key");
F/ From an Element to a String
Like for a strategy, if you want to read an Element and look at its structure, you can use the function XMLUtil.elementToString as well and send it to a file.
String String1 = XMLUtil.elementToString(elParam);
6/ Application : How to call this function, how to use it .
With this example, we have DataBank Column snippets to apply this function :
Bouillant BouilltParaObj1 = new Bouillant(); String StringTotal = BouilltParaObj1.toBuildingBlocksName(results,false, "LongEntrySignal",100);
This function is asking the Building Blocks with a ResultsGroup and a TradingSignal Name. (LongEntrySignal, ShortEntrySignal, LongExitSignal, ShortExitSignal,…)
It is really practical because you can select an Entry Long, or an Entry Short, or an Exit Long or Short signal, or all of them together
You can even select whatever signal name from your template.
Here is the code :
package SQ.Columns.Databanks; import SQ.Utils.Bouillant; import com.strategyquant.lib.L; import com.strategyquant.tradinglib.*; import com.strategyquant.tradinglib.optimization.WalkForwardMatrixResult; import com.strategyquant.lib.ValuesMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; // Created in France by Emmanuel Bouillant-Evrard for the StrategyQuantX Community :) public class StrategyBuildingBlocks extends DatabankColumn { public static final Logger Log = LoggerFactory.getLogger(StrategyBuildingBlocks.class); public StrategyBuildingBlocks() { super(L.tsq("Strategy Building Blocks"), DatabankColumn.Text, ValueTypes.Maximize, 0, 0, 1); setWidth(1000); } //------------------------------------------------------------------------ @Override public double getNumericValue(ResultsGroup results, String resultKey, byte direction, byte plType, byte sampleType) throws Exception { return 0; } //------------------------------------------------------------------------ @Override public String getValue(ResultsGroup results, String resultKey, byte direction, byte plType, byte sampleType) throws Exception { try { String StringTotal = ""; Bouillant BouilltParaObj1 = new Bouillant(); String String1 = BouilltParaObj1.toBuildingBlocksName(results,false, "LongEntrySignal",100); // 100 to have the name of Building blocks BouilltParaObj1 = new Bouillant(); String String2 = BouilltParaObj1.toBuildingBlocksName(results,false, "ShortEntrySignal",100); if (String1 == null || String2 == null) { return "";} int maxLength = (String1.length() < 100)?String1.length():100; String1 = String1.substring(0, maxLength); maxLength = (String2.length() < 100)?String2.length():100; String2 = String2.substring(0, maxLength); if (!String1.equals("") && !String2.equals("")) { StringTotal += String1 + "," + String2; } else if (!String1.equals("")) { StringTotal += String1; } else if (!String2.equals("")) { StringTotal += String2; } return StringTotal; } catch(Exception e) { Log.error("Error StrategyBuildingBlocks", e); return ""; // "Error: " + e.getMessage(); } } }
7/ The Building Blocks Name of each Strategy in the DataBank
8/ The toBuildingBlocksName function code
/** * This Function read the variables name, the rules, then return a String of Building Blocks name. * @param strategyRG * @param symmetricVariables * @param TradingSignal * @return */ public String toBuildingBlocksName(ResultsGroup strategyRG, boolean symmetricVariables, String TradingSignalName, int breakdownLevelMax) throws Exception { try { int i; String SignalID = ""; String strName = strategyRG.getName(); if(strName.startsWith("Filtered portfolio") || strName.startsWith("Portfolio")) { return ""; } StrategyBase strategyBase = getStrategyBase(strategyRG, symmetricVariables); Element Strategie1 = strategyBase.getStrategyXml(); if (DebugMode){ OutputPath = "C:\\Temp\\"+strName+".txt"; FileWriter myWriter = new FileWriter(OutputPath , true); } if (Strategie1 != null) { //#region ********************************************************* Variables ************************************************************************************** String test4 = "" ; Element elParams = Strategie1.getChild("Strategy").getChild("Variables"); if (elParams != null) { List<Element> paramElems = elParams.getChildren(); VariableValues = new HashMap<String, String>(); VariableID = new HashMap<String, String>(); VariableType = new HashMap<String, String>(); for (i = 0; i < paramElems.size(); i++) { Element elParam = paramElems.get(i); if (elParam != null) { String name = XMLUtil.getNodeValue(elParam,"name"); String id = XMLUtil.getNodeValue(elParam,"id"); String value = XMLUtil.getNodeValue(elParam,"value"); String Type = XMLUtil.getNodeValue(elParam,"type"); VariableID.put(name, id); VariableValues.put(name, value); VariableType.put(name, Type); if (DebugMode){ test4 = XMLUtil.getNodeValue(elParam,"name") + " " + XMLUtil.getNodeValue(elParam,"id"); myWriter.write("name + id : "+ test4 + "\r\n"); myWriter.write("VariableID : "+ VariableID.get(name) + "\r\n"); test4 = XMLUtil.getNodeValue(elParam,"name") + " " + XMLUtil.getNodeValue(elParam,"value"); myWriter.write("name + value : "+ test4 + "\r\n"); myWriter.write("VariableValues : "+ VariableValues.get(name) + "\r\n"); test4 = XMLUtil.getNodeValue(elParam,"name") + " " + XMLUtil.getNodeValue(elParam,"type"); myWriter.write("name + type : "+ test4 + "\r\n"); myWriter.write("VariableType : "+ VariableType.get(name) + "\r\n");} if (name.equals(TradingSignalName)) // We search the Id of the TradingSignalName, necessary to find the corresponding rule { SignalID = id; } } } if (DebugMode){ myWriter.close();} } //#endregion //#region ********************************************************* Rules ****************************************************************************************** elParams = Strategie1.getChild("Strategy").getChild("Rules").getChild("Events"); // go to Rules section of the Strategy Xml if (elParams != null) { List<Element> paramElems = elParams.getChildren(); for (i = 0; i < paramElems.size(); i++) { Element elParam2 = paramElems.get(i); if (elParam2 != null) { String elParamKey2 = elParam2.getAttributeValue("key"); if (elParamKey2.equals("OnBarUpdate")) // Looks for OnBarUpdate rule { List<Element> paramElems3 = elParam2.getChildren(); for (int i2 = 0; i2 < paramElems3.size(); i2++) { Element elParam3 = paramElems3.get(i2); if (elParam3 != null) { String elParamKey3 = elParam3.getAttributeValue("name"); if (elParamKey3.equals("Trading signals")) // Look for Trading signals rule { Element elParams4 = elParam3.getChild("signals"); List<Element> paramElems4 = elParams4.getChildren(); for (int i3 = 0; i3 < paramElems4.size(); i3++) { Element elParam4 = paramElems4.get(i3); if (elParam4 != null) { String elParamKey4 = elParam4.getAttributeValue("variable"); if (elParamKey4.equals(SignalID)) // Look for the requested TradingSignalName from its ID : SignalID { if (DebugMode){ myWriter = new FileWriter(OutputPath , true); String String1 = XMLUtil.elementToString(elParam4); myWriter.write(String1); myWriter.close();} Element elParams5 = elParam4.getChild("Item"); if (DebugMode){ myWriter = new FileWriter(OutputPath , true); String String1 = XMLUtil.elementToString(elParams5); myWriter.write(String1); myWriter.close();} if (elParams5 != null) { String key = elParams5.getAttributeValue("key"); // the name if (!key.equals("Boolean")) { Element elParam5Temp = elParams5; Element elParam6Temp = elParams5; strName = strategyRG.getName(); ArrayListElemsAll = new ArrayList<Element>(); UpdateNodesValues(strategyRG, symmetricVariables, elParams5, breakdownLevelMax, 0); AddToList(strategyRG, symmetricVariables, elParams5, breakdownLevelMax, 0); if (!StrategyName2.equals(strName) && OutPutBlockMode) { StrategyName2 = strName; strategyBase = getStrategyBase(strategyRG, symmetricVariables); Strategie1 = strategyBase.getStrategyXml(); OutputPath = "C:\\Temp\\"+strName+"B.txt"; FileWriter myWriter = new FileWriter(OutputPath , false); String String1 = XMLUtil.elementToString(Strategie1); myWriter.write(String1+"\r\n"); myWriter.close(); } StringBuilder sb = new StringBuilder(); // create the Building Blocks Name String if (ArrayListElemsAll.size()>=1) { if (DebugMode){ myWriter = new FileWriter(OutputPath , true); myWriter.write("ArrayListElemsAll.size() : "+ String.valueOf(ArrayListElemsAll.size()) + "\r\n"); String String1 = XMLUtil.elementToString(elParam5Temp); myWriter.write(String1); myWriter.close();} for (int i5 = 0; i5 < ArrayListElemsAll.size(); i5++) { elParam6Temp = ArrayListElemsAll.get(i5); if (elParam6Temp != null) { String elParamKey8Temp = elParam6Temp.getAttributeValue("name"); // elParam6Temp.getAttributeValue("key"); sb.append(elParamKey8Temp); sb.append(ParamDelimiter); } } } return sb.toString(); }} }} } }} } }} } } //#endregion } return ""; } catch (Exception e) { if (DebugMode2){ String strName = strategyRG.getName(); OutputPath = "C:\\Temp\\toBuildingBlocksName "+strName+".txt"; FileWriter myWriter = new FileWriter(OutputPath , true); myWriter.write("Error : "+ strName +" "+ "\r\n"); myWriter.write("Error : "+ e + "\r\n"); StrategyBase strategyBase = getStrategyBase(strategyRG, symmetricVariables); Element Strategie1 = strategyBase.getStrategyXml(); String String1 = XMLUtil.elementToString(Strategie1); myWriter.write(String1 + "\r\n"); myWriter.close();} Log.error("Error toBuildingBlocksName", e); return ""; } }
9/ To read each Building Blocks, this example is using two recursive functions : UpdateNodesValues and AddToList.
These functions are searching for each Building Blocks whatever is the XML structure of the strategy.
Here is the Code :
/** * Recursive function that read the Building Blocks Name from the Xml to update Nodes Values * @param strategyRG * @param symmetricVariables * @param Element * @param breakdownLevelMax * @param breakdownLevel * @return */ public void UpdateNodesValues(ResultsGroup strategyRG, boolean symmetricVariables, Element element, int breakdownLevelMax, int breakdownLevel) throws Exception { int i; if (element != null) { breakdownLevel = breakdownLevel + 1 ; List<Element> element1 = element.getChildren(); if (element1.size() >= 1) { for (i = 0; i < element1.size(); i++) { Element element11 = element1.get(i); if (element11 != null) { UpdateNodesValues(strategyRG, symmetricVariables, element11, breakdownLevelMax, breakdownLevel); } } } } if (element != null) { List<Element> listParams = element.getChildren("Param"); if (listParams.size() >= 1) { boolean VariableFounded = false; for (i = 0; i < listParams.size(); i++) { Element courant = listParams.get(i); if (courant != null) { String elParamKey5 = courant.getAttributeValue("key"); // the name String elParamVariable3 = courant.getAttributeValue("variable"); // the variable if (elParamKey5.startsWith("#") && elParamVariable3=="true") { String Temp1 = VariableValues.get(courant.getText()); String Temp2 = courant.getText(); if (Temp1 != null) { courant.setText(Temp1); } VariableFounded = true; } } } } } } /** * Recursive function that read the Building Blocks Name from the Xml to the ArrayList : ArrayListElemsAll * @param strategyRG * @param symmetricVariables * @param Element * @param breakdownLevelMax * @param breakdownLevel * @return */ public void AddToList(ResultsGroup strategyRG, boolean symmetricVariables, Element element, int breakdownLevelMax, int breakdownLevel) throws Exception { int i; boolean Condition1 = false; if (element != null) { breakdownLevel = breakdownLevel + 1 ; String elParamKey = element.getAttributeValue("key"); String categoryType = element.getAttributeValue("categoryType"); if (categoryType == null){categoryType = "";} if (breakdownLevelMax < 100) { Condition1 = (elParamKey.equals("IsRising") || elParamKey.equals("IsFalling") || elParamKey.equals("IsLowerCount") || elParamKey.equals("IsGreaterCount") || elParamKey.equals("IsLower") || elParamKey.equals("IsLowerOrEqual") || elParamKey.equals("IsGreater") || elParamKey.equals("IsGreaterOrEqual") || elParamKey.equals("CrossesAbove") || elParamKey.equals("CrossesBelow") || elParamKey.startsWith("CBlock")); } if (!Condition1) { List<Element> element1 = element.getChildren(); if (element1.size() >= 1) { for (i = 0; i < element1.size(); i++) { Element element11 = element1.get(i); if (element11 != null) { AddToList(strategyRG, symmetricVariables, element11, breakdownLevelMax, breakdownLevel); } } } } List<Element> listParams = element.getChildren("Param"); if (listParams.size() >= 1 || Condition1) { Element elementCopy = (Element)element.clone(); elParamKey = elementCopy.getAttributeValue("key"); listParams = elementCopy.getChildren("Param"); List<Element> listParamsBlock = element.getChildren("Block"); if (listParamsBlock.size() == 0 || Condition1) { if ((listParams.size() == 1 && !elParamKey.equals("Number")) || !elParamKey.equals("Number")) { if (breakdownLevel <= breakdownLevelMax) { ArrayListElemsAll.add(elementCopy); } } } if (breakdownLevel <= breakdownLevelMax && OutPutBlockMode) { String strName = strategyRG.getName(); OutputPath = "C:\\Temp\\"+strName+".txt"; FileWriter myWriter = new FileWriter(OutputPath , true); String String1 = XMLUtil.elementToString(elementCopy); myWriter.write(String1+"\r\n"); myWriter.write("________________________________________________________________________________________________________________________________________________________________________________________________________________"+"\r\n"); myWriter.close(); } } } }
10/ Conclusion
In Summary, it is simple, we usually start by reading the structure of the XML file from the XMLUtil.elementToString function, then we are using the above functions (A,B,C,D,E)
We can find more functions if necessary at :
https://strategyquant.com/sqxapi/com/strategyquant/lib/XMLUtil.html
Source of information :
https://strategyquant.com/doc/programming-for-sq/viewing-and-changing-strategy-parameters-version-2/
https://www.w3schools.com/xml/default.asp
https://strategyquant.com/sqxapi/com/strategyquant/lib/XMLUtil.html
https://www.tutorialspoint.com/java_xml/java_jdom_parse_document.htm
A useful Xml reader :
https://jsonformatter.org/xml-editor
My name is Emmanuel Bouillant-Evrard, Engineer specialized in Finance. I worked in Research and Development in Finance in USA, Canada and France for 24 years.
excellent
Thank you
Thanks Emmanuel !
Great job done