WealthLab MultiSystem


Using a combination of trading systems can provide an extra level of diversification which could result in a better risk/reward ratio, a smoother equity curve and as a result, a more profitable trading.

MultiSystem is a framework for combining multiple script written for Wealth-Lab © into aggregated trading systems, thus allowing developers to test ideas that previously were difficult to implement.

MultiSystem also extends some of the built-in WealthScript functions and provides systems developers with more flexibility by allowing them to adapt some of these functions to their specific needs.

In order to be run within MultiSystem, individual scripts need a small number of cosmetic changes described in the sample template.

It is recommended that Version 1.1 be used with Wealth-Lab Build 49 or later.


  • MultiSystem V1.1 – the entry point in the MultiSystem V1.1 framework
  • MultiSystem V1.1 Positions - positions classes and methods
  • MultiSystem V1.1 FX - helper classes
  • MultiSystem V1.1 Master Sample – the main entry point for the sample
  • MultiSystem V1.1 System1 – 3 – the 3 sample systems derived from: “Stochastic Price Reverse – market V4.5”, “Basic pullback buyer” and “RSI with ADX filter & ATR”.
  • MultiSystem V1.1 Filtered System - helper class for creating systems that support order and/or symbol filtering.
  • MultiSystem V1.1 Template – template indicating the steps needed to create or adapt a script for MultiSystem. This template is also added as an appendix to this document.
  • SymbolsList – utility module that implements functionality needed by the symbol filter
  • File – utility module that contains the class File, an encapsulation of the file functionality in Wealth-Lab.


In order to use MultiSystem V1.1, you have to download all the scripts that comprise this framework from the web site into Wealth-Lab Develper. In order to run the sample and use the symbol filtering capabilities you will need to create a file containing a list of symbols and save it on the hard drive. This file is used to create a virtual sub-WatchList (see below). This list of symbols should be a subset of the symbols in the WatchList that the MultiSystem will be run on. The name of this file then needs to be passed in the CreateWithSymbols method call in Sample3.

Changes/additions from previous releases

Compared with previous releases, V1.1 implements the following:

  • Bug fixes:
    • wrong number of open positions reported in some cases – fixed. Thanks map@w-l for reporting it and suggesting a fix.
    • wrong entry/exit levels for some trades with slippage on, when using Installxxx/ApplyAutoStops functions – fixed
    • other minor fixes
  • New methods implemented: SellAtTrailingStop and CoverAtTrailingStop, introduced in Wealth-Lab Build 46.
  • All Buy/Short/Sell/Cover methods return a boolean within MultiSystem as even AtMarket or AtClose orders may not be executed, either because a filtering (see below) or because of slippage. Built-in AtMarket and AtClose WealthScript functions currently do not return a boolean value.
  • The methods getPositionGlobalIndex and getActivePositions have been exposed. The first converts a per system position index into a global position index, and the second returns a handle to to a list of open positions.
  • A new File class was added to the package, which encapsulates the file functions in WealthScript. This class is in a separate script and is used by the SymbolsList class, which implements the filtering by symbol, but can be used as a standalone class.
  • Replaced global filters on all Buy and Short methods with individual filter methods for each of the Buy/Short/Sell/Cover methods. This allows for a finer tuning of scripts that use such filters. All these filters are members of the class Positions, and are inherited by the abstract class System and accessible within concrete classes derived from it. They can be used either directly in a concrete system class or in an intermediate filter class, sitting between the abstract System class and the concrete implementation of a specific system class. See the SystemFilter class for an example.


The basic idea of MultiScript is to create a layer of indirection between individual scripts and the global Wealth-Lab position management functions. On the side of scripts, this layer allows modified scripts to act as though they were running in a “virtual” Wealth-lab environment, without knowledge of positions opened or closed by other scripts. For example, LastActivePosition will return the last active position for the system calling it or ApplyAutoStops will only close positions opened by the current script.

The implementation takes advantage of the name resolution scheme in the WealthScript language. If there are two functions, one global, and the other member of a class, objects of that class will always call by default the member function. So I created a base class Positions that implements methods with the same names and arguments as the WL global position management functions, which after doing their internal processing, call the global methods with the same name. This way, system classes derived from this base class, will always call the per-instance methods rather than the global ones. The advantage of this approach is that most scripts will not need any structural changes.On the side of WL position-management functions, the layer makes the calls as though all the different systems were one system.

Each system must then implement the virtual function “runByBar” declared in the System base class, which does the processing for one bar. This functions are then called polymorphically, in turn for each system, by the MultiSystem class, which coordinates the whole process.

By default, no files are created or accessed at runtime. There is a filter class that allows one to read a file defining sub-watchlists, so that each system can then be run on a different set of symbols, but using it is optional.

The samples and template provided illustrate how to adapt existing systems to be used within MultiSystem

Filtering features

MultiSystem allows one to write filtering classes and/or methods separate from the trading system itself, thus enabling code reuse, modularity, event driven functionality, a cleaner code, this with no changes to the original script.

The filters can be implemented in the system itself, or in an intermediate class derived from System, and which can be used as base class by multiple systems, thus allowing a cleaner code and reuse between these. This latter was the approach taken in implementing the class FilteredSystem.

It has to be noted that there is a performance penalty that has to be paid for these features to work, so simulation will be somewhat slower.

There are two types of filters that can be applied: trade filters and symbol filters.

Trade filtering

The filtering methods are called before the Wealth-lab position entry/exit functions are called. The general rule is that if the filter returns true, the execution continues, if it returns false, the entry/exit method returns with a value of “false”, thus indicating that the trade has not been entered.

Here are the prototypes for all the filter methods that can be used in an intermediate filtering class or a system class. FilteredSystem illustrates their use.

function buyAtCloseFilter( Bar : integer; SignalName : string ) : boolean; virtual;
function buyAtLimitFilter( Bar : integer; LimitPrice : float; SignalName : string ) : boolean; virtual;
function buyAtMarketFilter( Bar : integer; SignalName : string ): boolean; virtual;
function buyAtStopFilter( Bar : integer; StopPrice : float; SignalName : string ): boolean; virtual;
function shortAtCloseFilter( Bar : integer; SignalName : string ) : boolean; virtual;
function shortAtLimitFilter( Bar: integer; LimitPrice: float; SignalName: string ): boolean; virtual;
function shortAtMarketFilter( Bar: integer; SignalName: string ) : boolean; virtual;
function shortAtStopFilter( Bar: integer; StopPrice: float; SignalName: string ): boolean; virtual;
function coverAtCloseFilter( Bar, Position : integer; SignalName : string ) : boolean; virtual;
function coverAtLimitFilter( Bar : integer; LimitPrice : float; Position: integer; SignalName : string ) : boolean; virtual;
function coverAtMarketFilter( Bar, Position : integer; SignalName : string ) : boolean; virtual;
function coverAtStopFilter( Bar : integer; StopPrice: float; Position : integer; SignalName : string ) : boolean; virtual;
function sellAtCloseFilter( Bar, Position : integer; SignalName : string ) : boolean; virtual;
function sellAtLimitFilter( Bar : integer; LimitPrice : float; Position : integer; SignalName : string ) : boolean; virtual;
function sellAtMarketFilter( Bar, Position : integer; SignalName : string ) : boolean; virtual;
function sellAtStopFilter( Bar: integer; StopPrice: float; Position: integer; SignalName: string ): boolean; virtual;
function sellAtTrailingStopFilter( Bar: integer; Stop: float; Position: integer; SignalName: string ): boolean; virtual;
function coverAtTrailingStopFilter( Bar: integer; Stop: float; Position: integer; SignalName: string ): boolean; virtual;

Symbol filtering

Allows each and any of the component systems to be run on different subsets of symbols of the WatchList that the MultiSystem is run on.

Symbol filters create virtual sub-watchlists that can be defined on a per system basis. The MultiSystem V1.1 Filtered System implements such a filter using the SymbolList class. This is done by creating a WatchList in WL and defining subsets of symbols of this WatchList for each system. These list should be in text files on disk, with symbols separated by space or new line characters

Sample 3 illustrates how to use symbol filtering. The way to achieve this is by calling this inherited constructor CreateWithSymbols in the FilteredSystem class:

inherited CreateWithSymbols( leadBars, systemName, symbolFileName );

where leadBars and systemName have the usual meaning, and symbolFileName is the name of a text file with the list of symbols this system is meant to use.

Example of the contents of a symbols file:

aol csco cmrc yhoo msft

arba ibm

amzn T INTC

Position naming methods

MultiSystem V1.1 implements 5 methods in the class System that append or prepend strings to trade names generated by each system. As these strings are displayed in the $imulator and scan tool, a user can sort the trade list by system name.

By default, if none of the following methods is called, the system name passed as argument to the System class constructor will be prepended to all the trade names generated by the system. A ':' character is used as separator. If the system name or user defined string are empty, only the trade name will be shown.

Here are the prototypes and short description of these methods: (here you could add an example for each effect )

// appends the system name (passed as an argument
// to the System class constructor) to trade names
// generated by this system.
procedure System.appendSystemNameToTradeName;

// prepends the system name to trade names generated by this system.
procedure System.prependSystemNameToTradeName;

// appends a string to trade names generated by this system
procedure System.appendStrToTradeName( str : String );

// prepends a string to trade names generated by this system
procedure System.prependStrToTradeName( str : String );

// resets the user defined string - no more string
// will be appended or prepended to trade names after this call
procedure System.resetAppendPrependName;


A number of sample scripts are also included, which illustrate the concepts.

MultiSystem V1.1 System Template illustrates the steps that a developer needs to take in order to create a new system or adapt an existing one for use with MultiSystem.

MultiSystem V1.1 Master Sample, FilteredSystem, Sample1, Sample2 and Sample3 are the components needed to run the sample. Master Sample is the entry point, FilteredSystem is an implementation of a filter class, that contains trade filters as well as symbol filters, and Sample1 to 3 are the 3 individual systems to be combined. In order for this sample to run, SymbolsList and File are necessary. Also, a file containing a list of symbols needs to be created and its name passed as an argument in the constructor of System3, which is the only one that uses a symbol filter.


MultiScript based system will run slower than their non-MultiScript counterparts, due to the multiple levels of indirections and using object oriented features such as inheritance, encapsulation and polymorphism in an interpreted language.

MultiSystem duplicates some of the Wealth-Lab native functionality and simulation and scans results are identical within the limits described elsewhere in this document between the two as of Wealth-Lab Developer build 49. This may not hold true for future builds. Please report any such differences.


I tested MultiSystem using individual and combined scripts and compared the statistics with the original systems and there were no differences in results with or without slippage (see limitations).

I have not tested it with adaptive scripts or other more complex ones, although they should be able to take advantage of the MultiSystem framework if they are modified appropriately.

This system cannot be run on the web site as it uses include directives {I ‘xxx’} which don’t work there, but it will work fine on your desktop version of WealthLab Programmer MultiSystems can be run within the Scan tool to generate alerts like any other system. There is a difference however – MultiSystem will generate an alert for each position that is open at the last bar, as opposed to Scan on regular systems, which will only generate one alert for multiple open positions for the same symbol.


Thanks map@w-l for testing MultiSystem and giving me feedback on some bugs and even suggesting a fix.

Appreciation goes to rickty for publishing Stochastic Price Reverse – market V4.5 and Basic pullback buyer and dmacdonal9 for publishing RSI with ADX filter & ATR, the three scripts I used as samples.


I have worked hard on ensuring that this script does what is supposed to do reliably and accurately, but one never knows…

Although I run a whole set of tests and have been using it myself, there may be still bugs that may result in inaccurate results or other yet unknown issues. Please let me know of any problems you may encounter while using it, and I will do my best to fix them.

Please acknowledge that by using the MultiSystem framework, you accept that I, Adrian Michel, its developer, cannot be held responsible for any financial losses or other bad things that may result from its use. Use at your own risk.


System template

This template shows the steps that need to be taken in order to update an existing system or create a new system compatible with MultiSystem.

This template is also provided separately as a script.


// MultiSystem V1.1 System Template
// Adrian Michel 2002, 2003

// This portion (1-8) shows how to change a system to make it compatible with MultiSystem V1.0

// (1) This line includes the MultiSystem classes and makes them visible locally.
//         It may not be necessary, or may even lead to multiple definitions in case
//         there is a master system including all the separate components.
{$I 'System'}

// (2) Start class definition that will become the new system class.
//         Note the syntax indicating that the new class is derived
//         (or inherits from) the class System, defined in MultiSystem V1.0.
type System1 = class( System )

// (3) All the variables used by the system become data members of the new class.
//         They can be declared private (preferred), which allows for better encapsulation.
//         Also, they do not need to be prefixed by the “var” keyword.
constructor Create;

// (4) Declaration of the two main methods: the constructor Create, and runBar.
//         Create is the class constructor, and RunBar is a method that overrides
//         an abstract method with the same name in the class System. This allows
//         for the polymorphic behavior, where the MultiSystem class can run any
//         system class, as long as it is derived from System, the only class
//         MultiSystem knows of.
protected procedure runBar( Bar : integer );override;

// (5) Optional - declaration of various filtering methods
//     The default behavior is 'on', so if these filters are not overriden,
//     they will not have any effect
//     If they return false, the buy/sell/short/cover order will not be entered, fact which can be
//     tested using the return value for these methods (in MultiScript, even atmarket and atclose
//     orders return a boolean value, to indicate whether the position was opened or not.
//     Here are only 2 of the many possible filters
function buyAtCloseFilter( Bar : integer; SignalName : string ) : boolean; override;
function buyAtLimitFilter( Bar : integer; LimitPrice : float; SignalName : string ) : boolean; override;

// (7) called with a symbol name - if Result is true, the system will be run
//     on this symbol, false otherwise. This can be used in conjuction with the
//     SymbolList class to create virtual sub-watchlists
function symbolFilter( symbol : String ) : boolean; override;

// (8) end of class definition

// (9) Definition of the constructor Create. This is implemented as a regular
//         procedure prefixed by the class name, and contains all the initialization
//         code (all the code that is between the variable declaration and the main
//         Bar loop, in a typical system).
constructor System1.Create;
// (10) The constructor needs to call the base class (System) constructor with
//          two parameters: number of lead bars and the system name.
       inherited Create( leadBars, systemName );

// (11) runBar contains the main bar loop, where trades are generated.
//         It does not need the ‘for’ statement, as this method will be called
//         from within MultiSystem for each bar.
procedure System1.runBar( Bar : integer );

// (12) implementation of the buy at close filter - see FilteredSystem V1.1 for a usage example
//      This implementation returns true
function System1.buyAtCloseFilter( Bar : integer; SignalName : string ) : boolean; override;
     Result := true;

// (13) implementation of the buy at limit filter - see FilteredSystem V1.1 for a usage example
//      This implementation returns true
function System1.buyAtLimitFilter( Bar : integer; LimitPrice : float; SignalName : string ) : boolean; override;

     Result := true;

// (14) implementation of the symbol filter - see FilteredSystem V1.0.2 for a usage example
//      This implementation returns true
function FilteredSystem.symbolFilter( symbol : String ) : boolean;
     Result := true;

// This portion shows how to write the multisystem part, where all the individual
// systems will be integrated and run as one. The systems and the master system
// will typically be implemented as different scripts

// (15) a variable of type MultiSystem is created
var ms : MultiSystem;
// (16) a MultiSystem object is created
ms := MultiSystem.Create;

// (17) variables of types of different systems are declared
var sys1, sys2, sys3 : System;

// (18) the system objects are created
sys1 := System1.Create;
sys2 := System2.Create;
sys3 := System3.Create;

// (19) the systems are made known to MultiSystem
ms.addSystem( sys1 );
ms.addSystem( sys2 );
ms.addSystem( sys3 );

// (20) MultiSystem is run by bar (there could be other ways,
//     such as running all the bars in one system, going to the next etc.

// (21) free all the objects and resources used by the script.