WealthScript Techniques | MultiSystem, or Calling a Strategy From Another

Modified on 2019/10/03 06:48 by Eugene — Categorized as: Knowledge Base

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:


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.Low[bar]*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.

  1. For improved performance the systems are run in multiple threads, not sequentially.
  2. 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)); } } ...