Note:
This article saw the light before Wealth-Lab 6.2 started providing effortless multiple system (and even multiple portfolio) backtesting via Combination Strategy interface. We highly recommend to explore this new feature.
Still, this design pattern of several Strategies in the same script may be useful in two cases which are out of scope for the architecture of Combination Strategies:
- If you need the Combo strategies to interact and thus influence trading decisions in another strategy
- If the Combo strategy equity should be constantly readjusted on a day-to-day basis with regard to the "Percent of Starting Equity" allocation
The problem
Is there a way to programmatically call one strategy from another?
The approach
There are two possible known ways to "call" a strategy.
The first is outlined in our Wiki KB article,
Interacting Dynamically with Portfolio Level Equity, and includes making a call to the undocumented method
TradingSystemExecutor. This is approximately how Wealth-Lab itself executes a strategy internally. Since it's unsupported, you can download the source code for
Community.Components and dig deeper into
Utility.cs class which contains all the code.
The other one is more straightforward and can be easily used to create a primitive "
multi-system" Strategy:
Code examples
Notice a workaround that closes all positions which are still left open on the last bar:
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using WealthLab;
using WealthLab.Indicators;
namespace WealthLab.Strategies
{
public class Multisystem : WealthScript
{
protected override void Execute()
{
System1.Run(this);
System2.Run(this);
}
}
class System1
{
public static void Run(WealthScript obj)
{
for(int bar = 20; bar < obj.Bars.Count; bar++)
{
if (obj.IsLastPositionActive)
{
obj.SellAtStop( bar+1, obj.LastPosition, Lowest.Series( obj.Low,10 )bar );
}
else
{
obj.BuyAtStop( bar+1, Highest.Series( obj.High,20 )bar, "System1" );
}
// Workaround to prevent open positions from one system to be available for the next system to close
if( bar == obj.Bars.Count-1 )
obj.SellAtClose( bar, Position.AllPositions, "Close All" );
}
}
}
class System2
{
public static void Run(WealthScript obj)
{
for(int bar = 20; bar < obj.Bars.Count; bar++)
{
if (obj.IsLastPositionActive)
{
obj.SellAtMarket( bar+1, obj.LastPosition );
}
else
{
obj.BuyAtLimit( bar+1, obj.Lowbar*0.95, "System2" );
}
// Workaround to prevent open positions from one system to be available for the next system to close
if( bar == obj.Bars.Count-1 )
obj.SellAtClose( bar, Position.AllPositions, "Close All" );
}
}
}
}
Below is a more advanced example that builds upon the code above.
- For improved performance the systems are run in multiple threads, not sequentially.
- To communicate between the two systems, you could pass shared objects by reference as parameters such that each task gets his own object by "ref" and the other does not (suggested by Wealth-Lab user Mark Mehl):
...
using System.Threading.Tasks; // <- run the subsystems in multiple threads via Task
public class Multisystem : WealthScript
{
protected override void Execute()
{
Task.Run(() => System1.Run(this, ref sharedObj1, sharedObj2));
Task.Run(() => System2.Run(this, sharedObj1, ref sharedObj2));
}
}
...