Clone our Wealth-Lab 8 Extension Demo project on GitHub to get a head start in developing your own Extensions!

DataSet Provider API

This document details the API for building DataSet Provider extensions for WealthLab 8. A DataSet Provider provides one or more pre-generated DataSets that users can select for backtesting. These DataSets appear under special nodes in the DataSet tree in WL8.

Wealth-Lab 7 <a href=''>DataSet</a> Providers

Build Environment

You can create a DataSet Provider in a .NET development tool such as Visual Studio 2022. Create a class library project that targets .NET8, then reference the WealthLab.Core library DLL that you'll find in the WL8 installation folder.

Note: If you are using Visual Studio 2022, it will need to be updated to at least version 17.8.6 to use .NET8.

Your DataSet Provider will be a class in this library that descends from DataSetProviderBase, which is defined in the WealthLab.Core library, in the WealthLab.Data namespace. After you implement and build your library, simply copy the resulting assembly DLL into the WL8 installation folder. The next time WL8 starts up, it will discover your DataSet Provider, making it available in appropriate locations of the WL8 user interface.

Visual Studio 2022 Build Environment

Accessing the Host (WL8) Environment

The IHost interface allows your extension to access information about the user's WealthLab environment. For example, the location of the user data folder, or obtaining a list of DataSets defined by the user. At any point in your extension's code base, you can access an instance of the IHost interface using the singleton class WLHost and its Instance property. Example:

//get user data folder
string folder = WLHost.Instance.DataFolder;

Descriptive Properties

public string Name

Return the name that you wish to appear in the tree node that will hold your DataSet Provider's DataSets. If you do not override this property, it will return a default value based on the name of your library assembly.

Core Functionality

public virtual void Initialize()

Override this method to perform any initialization your DataSet Provider might require. Typically, here you would create the instances of the DataSets that you return in the method below.

public abstract List<DataSet> DataSets

Override this method to return a list of DataSet instances, or instances of custom classes that you create that derive from DataSet. Each DataSet in the list will ultimately appear as a sub-node in WL8's DataSet tree.

public virtual void SymbolsModified(DataSet ds, List<string> symbols)

WL8 calls this method when the user modifies the symbols of one of your DataSet Provider's DataSets. Note that this can only occur if the DataSet instance's ReadOnlySymbols property is set to false. The default implementation assigns the symbols to the DataSet's Symbols property. You can override this method to perform further processing, but typically you would call base.SymbolsModified to retain this default behavior.

Creating DataSet Instances

Below is a summary of the important properties and methods you'll work with when managing the DataSet instances your Provider returns. Remember, you can return instances of the DataSet class itself, or of a custom class that derives from DataSet.

public string Name

Assign a value to the DataSet's name.

public List<string> Symbols

Assign to, or populate this List with the symbols that your DataSet comprises.

public virtual List<string> TradableSymbols

Override this property in a DataSet-derived class to return a List of the symbols that should be considered tradable. By default, this property returns the value of the Symbols property. Here you can exclude decommissioned stock symbols that no longer trade, for example. WL8's Strategy Monitor uses this property to process only tradable symbols when a Strategy is activated.

public string GlyphResource

Override this property in a DataSet-derived class to return the name of an image file that is stored as an embedded resource in your library, which will be used to represent the DataSet in the tree. For example, if your library is named "MyCompany.WLExtension", and you have a folder named "Glyphs" with an embedded resource image file named "MyImage.png", the GlyphResource string would be "MyCompany.WLExtension.Glyphs.MyImage.png".

public string PreferredDataProviderName

If your DataSet is linked to a specific Historical Data Provider, you can assign its Name to this property. WL8 will attempt to first load data from that Provider whenever your DataSet is being read.

public bool ReadOnlySymbols

Indicates whether or not the user will be able to modify the symbols in your DataSet.

public Dictionary<string, DateRange> DynamicDateRanges

A Dictionary keyed by symbol strings, containing DateRange instances that represent a date range that WL8 considers when backtesting using this DataSet. If a symbol is registered in this Dictionary with a DateRange, only trades that occur during this range will be included in a backtest. This is WL8's way of modeling dynamic indices, where symbols can enter and leave an index over time.

Self-Loading DataSets

You can derive a new class from DataSet and override the following to cause the DataSet to load its own data when read.

public virtual bool ShouldReturnOwnData

Return true to indicate that the DataSet wishes to load its own data instead of relying on the built-in WL8 Historical Data Provider loading mechanism.

public virtual bool SupportsScale(HistoryScale scale)

Return whether or not the DataSet supports returning data from the specified scale.

public virtual BarHistory GetHistory(string symbol, HistoryScale scale, DateTime startDate, DateTime endDate, int maxBars, DataRequestOptions cb)

WL8 calls this method to request historical data from your DataSet. Interpret the parameters as follows:

  • If maxBars is non-zero, return this many bars of historical data, up to the current date/time.
  • If endDate is DateTime.MaxValue, return data up to the current date/time.
  • if startDate is DateTime.MinValue, return data going back as far as possible into the past.
  • Otherwise, use the date range specified in startDate and endDate.

Your implementation of GetHistory should do the following:

  1. Check to see if the scale passed in the scale parameter (an instance of the HistoryScale is supported, and if not return null.
  2. Determine if the symbol is one that your DataSet supports, and if not return null.
  3. Create a BarHistory instance, using the constructor that accepts the symbol and scale parameters.
  4. Assign a value to the BarHistory's SecurityName property, if a security name can be obtained.
  5. Obtain the historical data from the source, for as much of the requested range as possible.
  6. Use the BarHistory Add method to add the date, open, high, low, close and volume to the instance.
  7. Return the BarHistory instance.
public virtual void PostDataLoad(BarHistory bh)

WL8 calls this after your DataSet loads data in a GetHistory call. The default implementation processes the DynamicDataRanges of your DataSet, calling the BarHistory AddExecutableRange method for each one. If you override this method to perform further processing, be sure to call base.PostDataLoad.

Example: Avoiding Survivorship Bias

To implement a WealthData-like solution for anti-survivorship bias in a custom DataSetProvider, follow steps below. Although technical implementation can vary, this example assumes a hypothetical MySql data provider which keeps track of the range of dates a symbol was a constituent to an index.

  • Create a subclass of DataSet (for example, MySqlDataSet).
  • Your DataSetProvider will create instance(s) of MySqlDataSet and use them when returning its value in the DataSets property override.
  • MySqlDataSet should set its ReadOnlySymbols to true, PreferredDataProviderName to the name of your SQL Historical Provider, and ShouldReturnOwnData to true
  • MySqlDataSet needs to override and implement the GetHistory method.
  • In GetHistory, you'll need to get an instance of the Historical Data Provider (SQL) using the DataProviderFactory.Instance.Find. Once you have the instance, call its GetHistory to get the BarHistory data for the symbol, date range.
  • Now that you have the BarHistory, call its AddExecutableRange method. This defines the range of dates that are valid for when this symbol is in the specific index that your dynamic DataSet is representing.