I like the concept of StrategyRunner and Backtester but I'm rather interested in automating the optimization.
I think Backtester cannot be used for optimization because:
1. There is no way to set the parameters.
2. No parallelism.
Any plan to support optimization in StrategyRunner?
So that I can implement the results filter in the code.
I think Backtester cannot be used for optimization because:
1. There is no way to set the parameters.
2. No parallelism.
Any plan to support optimization in StrategyRunner?
So that I can implement the results filter in the code.
Rename
No, no plans for this. Optimization would occur at some level ABOVE this class. In other words, maybe in your own custom extension that’s intended to replace the Optimization built into WL7.
All right.
So Symbol by Symbol optimization + the results filter would be still a solution, then.
https://www.wealth-lab.com/Discussion/Symbol-by-Symbol-optimization-results-filter-7025
So Symbol by Symbol optimization + the results filter would be still a solution, then.
https://www.wealth-lab.com/Discussion/Symbol-by-Symbol-optimization-results-filter-7025
It can happen as a 'side effect' if this request makes it to the top of the queue:
https://www.wealth-lab.com/Discussion/Advanced-filtering-of-optimization-results-7015
Provided we succeed with getting the Syncfusion free license mentioned here:
https://www.wealth-lab.com/Discussion/Filter-optimization-results-6482
https://www.wealth-lab.com/Discussion/Advanced-filtering-of-optimization-results-7015
Provided we succeed with getting the Syncfusion free license mentioned here:
https://www.wealth-lab.com/Discussion/Filter-optimization-results-6482
Hopefully the filtering of optimization results feature request makes it as this is crucial for me to be able to optimize more symbols (tens of symbols to hundreds of symbols).
While there is no direct way to set parameters there are workarounds...
For example, you could use Globals or a Settings file to pass parameters to your strategy. It requires a little code modification to the Called Strategy to grab the param values and assign them, but I've tested both methods and they work for me :)
Here are some code snippets, you would need to modify the parameter names and strategy names etc accordingly, but it might help as a staring point
These where created to work with the Simple Channel Breakout 50 WL strategy (Called Strategy) as an example. I made the entry and exit values optimizable then converted it to a C# code strategy. From there I made the necessary mods to the code.
The Calling Strategy was based on the sample included in the QuickRef for the StrategyRunner helper class.
Have fun!
Code Snippet from My Called Strategy - to be run by sr.RunBacktest("My Called Strategy") in the calling strategy.
Code from My Calling Strategy File - runs the sr.RunBacktest("My Called Strategy")
For example, you could use Globals or a Settings file to pass parameters to your strategy. It requires a little code modification to the Called Strategy to grab the param values and assign them, but I've tested both methods and they work for me :)
Here are some code snippets, you would need to modify the parameter names and strategy names etc accordingly, but it might help as a staring point
These where created to work with the Simple Channel Breakout 50 WL strategy (Called Strategy) as an example. I made the entry and exit values optimizable then converted it to a C# code strategy. From there I made the necessary mods to the code.
The Calling Strategy was based on the sample included in the QuickRef for the StrategyRunner helper class.
Have fun!
Code Snippet from My Called Strategy - to be run by sr.RunBacktest("My Called Strategy") in the calling strategy.
CODE:
public MyStrategy() : base() { // Select the transfer method by commenting out one of the below // use settings Manager to get param values from file getParamsFromSettingsManager(); // or use global to get param values getParamsFromGlobal(); } public void getParamsFromGlobal() { string StrategyKey = "MyStrategyKey"; string ParamKey = "Percentage"; double Percentage = HasGlobal(StrategyKey + ParamKey) ? (double)GetGlobal(StrategyKey + ParamKey) : 2.25; ParamKey = "exitPercentage"; double exitPercentage = HasGlobal(StrategyKey + ParamKey) ? (double)GetGlobal(StrategyKey + ParamKey) : 1.25; AddParameter("Percentage", ParameterTypes.Double, Percentage, 0, 10, 0.01); AddParameter("exitPercentage", ParameterTypes.Double, exitPercentage, 0, 10, 0.01); } public static SettingsManager Param { get; set; } = new SettingsManager(WLHost.Instance.DataFolder.ToString() + "PassParams.ini", false); public void getParamsFromSettingsManager() { double Percentage = Param.Get("Percentage", 2.25); double exitPercentage = Param.Get("exitPercentage", 1.25); AddParameter("Percentage", ParameterTypes.Double, Percentage, 0, 10, 0.01); AddParameter("exitPercentage", ParameterTypes.Double, exitPercentage, 0, 10, 0.01); }
Code from My Calling Strategy File - runs the sr.RunBacktest("My Called Strategy")
CODE:
//Backtest Completed (executes once) public override void BacktestComplete() { // Set up Strategy Runner // ------------------ StrategyRunner sr = new StrategyRunner(); sr.DataSet = DataSetFactory.FindDataSet("DOW 30"); // !IMPORTANT --> Dataset name is CaSeSenSiTiVe sr.DataRange.DataRangeType = DataRangeTypes.RecentYears; sr.DataRange.RecentValue = 10; // most recent 10 years adjust as needed sr.PositionSize.StartingCapital = 100000000; // define starting capital sr.PositionSize.PositionSizeType = PositionSizeTypes.PctOfEquity; double pEquity = 2.5; // define % equity sr.PositionSize.Amount = pEquity; // set position size to 5% of equity adjsut as needed // set paramters for strategy via a Global // ------------------ double Percentage = 2.55; // parameter 1 to be sent to strategy double exitPercentage = 1.55; // parameter 2 to be sent to stratgey string StrategyKey = "MyStrategyKey"; // anything you like, just make it unique string ParamKey = "Percentage"; // a string, suggest you use the parameter name SetGlobal(StrategyKey + ParamKey, Percentage); // set the global key for the first paramter ParamKey = "exitPercentage"; // change the key name for next parameter SetGlobal(StrategyKey + ParamKey, exitPercentage); // set the second parameter // Set Parameters for Strategy via a File (and use SettingsManager to get Params in Strategy // ------------------- string ParamFile = WLHost.Instance.DataFolder.ToString() + "PassParams.ini"; if (File.Exists(ParamFile)) { File.Delete(ParamFile); // just to be safe, kill the file if it exits } // write the settings file directly. For some reason using SettingsManaget.Set does not write to file in this use case // besure to use the same file structre as a settings file. Key=Value string[] Params = { "Percentage=" + Percentage.ToString("N2"), "exitPercentage=" + exitPercentage.ToString("N2") }; File.WriteAllLines(ParamFile, Params); Thread.Sleep(2000); // Wait to allow settings file to update - crude but effective try { // now run the backtester Backtester bt = sr.RunBacktest("My Strategy Name"); // set strategy WriteToDebugLog("My Backtest Parameters: " + pEquity.ToString("N2") + " | " + Percentage.ToString("N2") + " | " + exitPercentage.ToString("N2")); WriteToDebugLog("My Results: " + bt.NetProfit.ToString("N2") + " | " + bt.Metrics.PositionCount.ToString("N") + " | " + bt.Metrics.NSFPositionCount.ToString("N")); } catch (Exception e) { WriteToDebugLog(e.Message.ToString()); } }
Wow, that's clever!
It solves the first issue.
But the second issue remains
It solves the first issue.
QUOTE:
1. There is no way to set the parameters.
But the second issue remains
QUOTE:
2. No parallelism.
Correct
The second is non-trivial.
You could possibly multithread the whole thing passing the new Var's to each new thread via an object where you would create the StrategyRunner set the parameters and call RunBacktest, but there are a lot of complications to consider.
- each thread uses the same object to pass its paramters to the Called Strategy... so you would need a way to be sure the previous params have been read, prior to changing for a new run. The Called Strategy only reads in the initialize method, so changes after this should be ok.
- reporting: the debug window only updates at the end of a backtest run of the Calling Strategy. So you would need to complete your entire loop through your parameters (or loop within loops etc) before you get any feedback. You could potentially write to a file or WLLog but multithreading can create issues here as well, especially with file access.
- colating the bt results: perhaps you could set up a private list(of Backtester) to populate with the bt once the bt.RunBacktest is called. Or transfer the results/metrics.
But really, why go to all that trouble. If you want to run optimizations, why not build an optimzer extension and leverage off the work the WL Team have already done managing all these issues?
I haven't looked at building an optimizer yet but everything else I've look at seems pretty straight forward (although, not always well documented :) ). If you have questions the WL team seem pretty quick to respond and very helpful in my experience.
quick update:
Further to the above Code I suggest adding an exception message loop after the sr.RunBacktest Call to print any exceptions that might have been hit in the strategy to the debug window. Might save you some time toubleshooting.
The second is non-trivial.
You could possibly multithread the whole thing passing the new Var's to each new thread via an object where you would create the StrategyRunner set the parameters and call RunBacktest, but there are a lot of complications to consider.
- each thread uses the same object to pass its paramters to the Called Strategy... so you would need a way to be sure the previous params have been read, prior to changing for a new run. The Called Strategy only reads in the initialize method, so changes after this should be ok.
- reporting: the debug window only updates at the end of a backtest run of the Calling Strategy. So you would need to complete your entire loop through your parameters (or loop within loops etc) before you get any feedback. You could potentially write to a file or WLLog but multithreading can create issues here as well, especially with file access.
- colating the bt results: perhaps you could set up a private list(of Backtester) to populate with the bt once the bt.RunBacktest is called. Or transfer the results/metrics.
But really, why go to all that trouble. If you want to run optimizations, why not build an optimzer extension and leverage off the work the WL Team have already done managing all these issues?
I haven't looked at building an optimizer yet but everything else I've look at seems pretty straight forward (although, not always well documented :) ). If you have questions the WL team seem pretty quick to respond and very helpful in my experience.
quick update:
Further to the above Code I suggest adding an exception message loop after the sr.RunBacktest Call to print any exceptions that might have been hit in the strategy to the debug window. Might save you some time toubleshooting.
CODE:
Backtester bt = sr.RunBacktest("My Strategy Name"); if (bt.ExceptionMessages.Count > 0) { foreach(string eXmessage in bt.ExceptionMessages) { WriteToDebugLog(eXmessage); } }
Your Response
Post
Edit Post
Login is required