Parent: Object
A helper class the provides a simplified method of running Strategy backtests and obtaining their performance results. Can be utilized within a WL8 Extension, or even within Strategy code itself.
Determines the backtest settings to use during the backtest.
using WealthLab.Backtest; using WealthLab.Core; using System; namespace WealthScript1 { public class MyStrategy : UserStrategyBase { //Initialize public override void Initialize(BarHistory bars) { } //Execute public override void Execute(BarHistory bars, int idx) { } //Backtest Completed (executes once) public override void BacktestComplete() { StrategyRunner sr = new StrategyRunner(); foreach (BarHistory bars in BacktestData) sr.Symbols.Add(bars.Symbol); sr.BacktestSettings.CommissionType = CommissionType.None; Backtester bt = sr.RunBacktest("RSI Agita"); WriteToDebugLog("RSI Agita on backtested data without commission: " + bt.NetProfit.ToString("N2")); WriteToDebugLog("Number of Trades: " + bt.Metrics.PositionCount.ToString("N0")); double profit = bt.NetProfit; sr.BacktestSettings.CommissionType = CommissionType.Flat; sr.BacktestSettings.CommissionAmount = 4.95; bt = sr.RunBacktest("RSI Agita"); WriteToDebugLog("RSI Agita on backtested data with $4.95 flat commission: " + bt.NetProfit.ToString("N2")); WriteToDebugLog("Number of Trades: " + bt.Metrics.PositionCount.ToString("N0")); profit -= bt.NetProfit; WriteToDebugLog("Difference: " + profit.ToString("N2")); } } }
Specifies a BarHistory instance to use for the benchmark backtest, which is used in computing certain performance metrics like Alpha and Beta. If this property is null or empty, the StrategyRunner will attempt to load benchmark data from the BenchmarkSymbol property.
Specifies the symbol to use for the benchmark backtest, which is used in computing certain performance metrics like Alpha and Beta. If the StrategyRunner's BenchmarkData propery is null or empty, it will attempt to load the benchmark data using this symbol.
using WealthLab.Backtest; using WealthLab.Core; using System; namespace WealthScript1 { public class MyStrategy : UserStrategyBase { //Initialize public override void Initialize(BarHistory bars) { } //Execute public override void Execute(BarHistory bars, int idx) { } //Backtest Completed (executes once) public override void BacktestComplete() { StrategyRunner sr = new StrategyRunner(); foreach (BarHistory bars in BacktestData) sr.Symbols.Add(bars.Symbol); sr.BenchmarkSymbol = "SPY"; Backtester bt = sr.RunBacktest("RSI Agita"); WriteToDebugLog("RSI Agita on backtested data with SPY benchmark Alpha= " + bt.Metrics.Alpha.ToString("N2")); sr.BenchmarkSymbol = "QQQ"; bt = sr.RunBacktest("RSI Agita"); WriteToDebugLog("RSI Agita on backtested data with QQQ benchmark Alpha= " + bt.Metrics.Alpha.ToString("N2")); } } }
If its DataSet and Symbols property are null and/or empty, the StrategyRunner will use the contents of its Data property as source data for backtesting. You can add BarHistory instances to the Data property manually as required.
using WealthLab.Backtest; using WealthLab.Core; using WealthLab.Data; namespace WealthScript1 { public class MyStrategy : UserStrategyBase { //Initialize public override void Initialize(BarHistory bars) { } //Execute public override void Execute(BarHistory bars, int idx) { } //Backtest Completed (executes once) public override void BacktestComplete() { StrategyRunner sr = new StrategyRunner(); sr.Data = BacktestData; Backtester bt = sr.RunBacktest("RSI Agita"); WriteToDebugLog("RSI Agita on currently selected Data: " + bt.NetProfit.ToString("N2")); WriteToDebugLog("Number of Trades: " + bt.Metrics.PositionCount.ToString("N0")); } } }
Determines the data range to use for the backtest. The default range is the most recent 10 years.
using WealthLab.Backtest; using WealthLab.Core; using System; namespace WealthScript1 { public class MyStrategy : UserStrategyBase { //Initialize public override void Initialize(BarHistory bars) { } //Execute public override void Execute(BarHistory bars, int idx) { } //Backtest Completed (executes once) public override void BacktestComplete() { StrategyRunner sr = new StrategyRunner(); foreach (BarHistory bars in BacktestData) sr.Symbols.Add(bars.Symbol); sr.DataRange = new DataRange(DataRangeType.YearRange, 0, DateTime.Now.Date.AddYears(-10), DateTime.Now.Date); Backtester bt = sr.RunBacktest("RSI Agita"); WriteToDebugLog("RSI Agita Current Range on backtested data: " + bt.NetProfit.ToString("N2")); WriteToDebugLog("Number of Trades: " + bt.Metrics.PositionCount.ToString("N0")); sr.DataRange.StartDate = DateTime.Now.Date.AddYears(-20); sr.DataRange.EndDate = DateTime.Now.AddYears(-10); bt = sr.RunBacktest("RSI Agita"); WriteToDebugLog("RSI Agita Ealier Range on backtested data: " + bt.NetProfit.ToString("N2")); WriteToDebugLog("Number of Trades: " + bt.Metrics.PositionCount.ToString("N0")); } } }
If assigned a value, the StrategyRunner will use this DataSet as its data source for the backtest. You can use the DataSetFactory class to obtain a DataSet instance by name.
using WealthLab.Backtest; using WealthLab.Core; using WealthLab.Data; namespace WealthScript1 { public class MyStrategy : UserStrategyBase { //Initialize public override void Initialize(BarHistory bars) { } //Execute public override void Execute(BarHistory bars, int idx) { } //Backtest Completed (executes once) public override void BacktestComplete() { StrategyRunner sr = new StrategyRunner(); DataSet cryptos = DataSetFactory.Instance.Find("Cryptocurrency - Top 20"); sr.DataSet = cryptos; Backtester bt = sr.RunBacktest("RSI Agita"); WriteToDebugLog("RSI Agita on Cryptos Net Profit: " + bt.NetProfit.ToString("N2")); WriteToDebugLog("Number of Trades: " + bt.Metrics.PositionCount.ToString("N0")); } } }
Allows you to perform an optimization, using the Optimization Method specified by name in the optMethod parameter. Prior to running an optimization, you should configure the StrategyRunner data, scale, etc. in the same manner as required by RunBacktest. Also note that if you disabled a Strategy's parameter(s) in the Strategy window, the disabled state will carry over here and the disabled parameters will not participate in the optimization.
There are two versions of this method, allowing you to specify the Strategy to optimize by either name (stratName parameter) or by instance (s parameter).
The last parameter, runComplete, accepts a method containing two parameters and returning void. The StrategyRunner calls this method, is assigned, after each optimization run has been completed. The first parameter of the callback is an OptimizationResult instance and contains information on the results of the current run. The second parameter is a double containing the percent overall completion of the optimization.
The PerformOptimization method returns an instance of the OptimizationResultList class, which is a List<OptimizationResult> with a few additional members and contains the the results of each optimization run.
using WealthLab.Backtest; using WealthLab.Core; using WealthLab.Data; using System; namespace WealthScript1 { public class MyStrategy : UserStrategyBase { //Initialize public override void Initialize(BarHistory bars) { } //Execute public override void Execute(BarHistory bars, int idx) { } //Backtest Completed (executes once) public override void BacktestComplete() { StrategyRunner sr = new StrategyRunner(); sr.DataSet = DataSetFactory.Instance.Find("Dow 30"); OptimizationResultList orl = sr.PerformOptimization("Exhaustive", "Knife Juggler", RunCompleted); foreach (OptimizationResult or in orl) { WriteToDebugLog(or.ToString() + "\t" + or.AnnualizedReturn.ToString("N2") + "%", false); } } //delegated method private void RunCompleted(OptimizationResult or, double pctComplete) { WriteToStatusBar("Run: " + or.RunNumber + " " + pctComplete.ToString("N2") + "%"); } } }
Determines the position to use for the backtest. The default position size is 10% of equity.
using WealthLab.Backtest; using WealthLab.Core; using System; namespace WealthScript1 { public class MyStrategy : UserStrategyBase { //Initialize public override void Initialize(BarHistory bars) { } //Execute public override void Execute(BarHistory bars, int idx) { } //Backtest Completed (executes once) public override void BacktestComplete() { StrategyRunner sr = new StrategyRunner(); foreach (BarHistory bars in BacktestData) sr.Symbols.Add(bars.Symbol); Backtester bt = sr.RunBacktest("RSI Agita"); WriteToDebugLog("RSI Agita on backtested data with 10% of equity: " + bt.NetProfit.ToString("N2")); WriteToDebugLog("Number of Trades: " + bt.Metrics.PositionCount.ToString("N0")); sr.PositionSize.Amount = 20; bt = sr.RunBacktest("RSI Agita"); WriteToDebugLog("RSI Agita on backtested data with 20% of equity: " + bt.NetProfit.ToString("N2")); WriteToDebugLog("Number of Trades: " + bt.Metrics.PositionCount.ToString("N0")); } } }
The RunBacktest method has two overrides. One lets you provide a Strategy Name, and the second an instance of the StrategyBase class that represents an already compiled Strategy. Call this method after having set the various properties that control the backtest scale, range, etc. The result is an instance of the Backtester class. You can query its Metrics property and various other properties to examine the performance results of the backtest.
If the DataSet property is assigned, the StrategyRunner will load the historical data for that DataSet's Symbols into its Data property. If DataSet is null, the StrategyRunner will next check its Symbols property, loading the data for these symbols into its Data property. As a last resort, the StrategyRunner will use its Data property, which you can pre-load with BarHistory instances manually if desired.
using WealthLab.Backtest; using WealthLab.Core; using WealthLab.Data; namespace WealthScript1 { public class MyStrategy : UserStrategyBase { //Initialize public override void Initialize(BarHistory bars) { } //Execute public override void Execute(BarHistory bars, int idx) { } //Backtest Completed (executes once) public override void BacktestComplete() { StrategyRunner sr = new StrategyRunner(); sr.DataSet = DataSetFactory.Instance.Find("Dow 30"); Backtester bt = sr.RunBacktest("Neo Master"); WriteToDebugLog("Neo Master on Dow 30 Net Profit: " + bt.NetProfit.ToString("N2")); } } }
Determines the data scale, or frequency, to use for the backtest. The default scale is Daily.
using WealthLab.Backtest; using WealthLab.Core; using WealthLab.Data; namespace WealthScript1 { public class MyStrategy : UserStrategyBase { //Initialize public override void Initialize(BarHistory bars) { } //Execute public override void Execute(BarHistory bars, int idx) { } //Backtest Completed (executes once) public override void BacktestComplete() { StrategyRunner sr = new StrategyRunner(); string firstSymbol = BacktestData[0].Symbol; sr.Symbols.Add(firstSymbol); Backtester bt = sr.RunBacktest("RSI Agita"); WriteToDebugLog("RSI Agita Daily on " + firstSymbol + ": " + bt.NetProfit.ToString("N2")); WriteToDebugLog("Number of Trades: " + bt.Metrics.PositionCount.ToString("N0")); sr.Scale = HistoryScale.Weekly; bt = sr.RunBacktest("RSI Agita"); WriteToDebugLog("RSI Agita Weekly on " + firstSymbol + ": " + bt.NetProfit.ToString("N2")); WriteToDebugLog("Number of Trades: " + bt.Metrics.PositionCount.ToString("N0")); } } }
You can add symbols to the Symbols property to determine the data the StrategyRunner will execute with. The StrategyRunner will use the Symbols property if its DataSet property is null.
using WealthLab.Backtest; using WealthLab.Core; using WealthLab.Data; namespace WealthScript1 { public class MyStrategy : UserStrategyBase { //Initialize public override void Initialize(BarHistory bars) { } //Execute public override void Execute(BarHistory bars, int idx) { } //Backtest Completed (executes once) public override void BacktestComplete() { StrategyRunner sr = new StrategyRunner(); sr.Symbols.Add("TQQQ"); sr.Symbols.Add("SQQQ"); Backtester bt = sr.RunBacktest("Neo Master"); WriteToDebugLog("Neo Master on TQQQ/SQQQ Net Profit: " + bt.NetProfit.ToString("N2")); } } }