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

Historical Data Provider API

A Historical Data Provider Extension allows WealthLab 9 to obtain historical price and volume data from a specific source. Typical data sources include:

  • Remote web services
  • Web pages
  • Files stored on the local file system
  • Databases or other proprietary data sources

Build Environment

You can create a Historical Data Provider in a .NET development tool such as Visual Studio 2026. Create a class library project that targets .NET10, then reference the WealthLab.Core library DLL that you'll find in the WL9 installation folder.

Your Historical Data Provider will be a class in this library that descends from DataProviderBase, 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 WL9 installation folder. The next time WL9 starts up, it will discover your Historical Data Provider, making it available in appropriate locations of the WL9 user interface.

Visual Studio 2026 Build Environment

Accessing the Host (WL9) Environment

The IHost interface provides access to the current WealthLab environment. Extensions can use it to retrieve application-level information and services, such as the location of the user's WealthLab data folder or the DataSets defined by the user.

You can access the current IHost instance from anywhere in your extension through the WLHost singleton and its Instance property. For example, the following code retrieves the path to the user's WealthLab data folder:

string folder = WLHost.Instance.DataFolder;

Use WLHost.Instance whenever your extension needs access to functionality exposed by the IHost interface.

Configuring a Historical Data Provider

DataProviderBase ultimately derives from Configurable, which provides the standard WealthLab framework for configuring extensions. By default, DataProviderBase uses a ConfigurableType of ParameterList, so the Historical Data Provider is configured using Parameter instances contained in a ParameterList. Define these Parameters by overriding GenerateParameters. Parameters can represent values such as user names, passwords, API keys, or other Provider-specific settings. WealthLab presents these Parameters in a configuration dialog and allows the user to enter values of the appropriate types.

You can alternatively set ConfigurableType to VanillaString. In that case, your Provider works directly with the Configuration string rather than Parameter instances. If you use VanillaString and require a custom configuration interface, see Providing User Interfaces for WealthLab Components.

Descriptive Properties

The Configurable base class provides descriptive properties that determine how your Historical Data Provider appears in WealthLab. The most important properties to override are:

  • Name
  • GlyphResource
  • Description
  • URL

Data Subsystem Class Hierarchy

WealthLab Data Subsystem

The WealthLab data subsystem contains several classes derived from Configurable. A Historical Data Provider follows this inheritance path:

Configurable
    ProviderBase
        BulkUpdatableProviderBase
            DataProviderBase

Historical Data Provider Properties

The following properties control which capabilities your Historical Data Provider exposes to WealthLab.

SupportsUpdate

public virtual bool SupportsUpdate

Returns true by default. Return false if WealthLab should not attempt to update data from the Provider's source. For example, a Provider reading local files that are maintained by an external process might return false.

IsGeneralPurposeProvider

public virtual bool IsGeneralPurposeProvider

Returns true by default. A general-purpose Provider can service ad-hoc symbol requests from Charts, Strategy windows, and other WealthLab tools. Return false if your Provider can supply data only for a predefined set of symbols.

SupportsPartialBar

public virtual bool SupportsPartialBar

By default, this returns the value of IsGeneralPurposeProvider. Return true if your Provider can supply a partial bar representing the current incomplete trading interval. If this property returns true, implement GetPartialBarInternal, described later.

ShowInNewDataSetList

public virtual bool ShowInNewDataSetList

Returns true by default. When both ShowInNewDataSetList and IsGeneralPurposeProvider return true, the Provider appears as an available Historical Data Provider when users create a new DataSet. Return false if the Provider has a specialized purpose and should not be offered for user-created DataSets. For example, a Provider intended only to service DataSets created by a particular DataSet Provider might return false.

NeedsNewInstancesForEachDataSet

public virtual bool NeedsNewInstancesForEachDataSet

Returns false by default. Return true when each DataSet created with your Provider requires its own separately configured Provider instance. This is useful when configuration is specific to an individual DataSet. For example, an ASCII file Provider might require each DataSet to specify a different folder and file format. WealthLab can create a separate Provider instance for each configured DataSet.

InjectNewProviderInstances

public virtual bool InjectNewProviderInstances

Applies when NeedsNewInstancesForEachDataSet returns true. By default, newly created Provider instances can appear in WealthLab's list of Historical Data Providers. Return false if those generated instances should remain internal and should not appear in the Provider list.

DSString

public string DSString

When NeedsNewInstancesForEachDataSet is true, this property contains the DataSet-specific configuration string used when the Provider instance was created.

DataSet

public DataSet DataSet

When NeedsNewInstancesForEachDataSet is true, this property contains the DataSet associated with the Provider instance.

ReadOnlySymbols

public virtual bool ReadOnlySymbols

Returns false by default. Return true if users should not be allowed to modify the symbols in DataSets associated with your Provider. This is appropriate when membership is determined automatically by the underlying data source.

Symbols

public virtual List<string> Symbols

Override this property when the Provider manages a dynamic, read-only symbol list. This is especially useful when:

  • ReadOnlySymbols is true
  • NeedsNewInstancesForEachDataSet is true
  • The available symbols can change outside WealthLab

For example, if symbols correspond to files in a particular folder, return the symbols currently represented by those files.

AllowConfigurationOfExistingDataSets

public virtual bool AllowConfigurationOfExistingDataSets

Returns true by default. Return false if users should not be allowed to reconfigure a DataSet associated with the Provider after the DataSet has been created.

LookForIntradayWhenNoDaily

public virtual bool LookForIntradayWhenNoDaily

Returns false by default. Return true if WealthLab should attempt to obtain intraday data and compress it when the Provider cannot supply native Daily or higher-scale data. This is useful for Providers that contain intraday data only.

Compress30MinuteBarsFor60Minute

public virtual bool Compress30MinuteBarsFor60Minute

By default, WealthLab can request 30-minute data and compress it when 60-minute data is requested. This behavior allows WealthLab to construct a partial 60-minute bar from a partial 30-minute interval when necessary. Return false if your Provider can supply 60-minute data directly and does not require this behavior.

SymbolsChanged

public virtual void SymbolsChanged(DataSet ds)

WealthLab calls this method when the user changes the symbols belonging to an associated DataSet. Override it if your Provider needs to respond to or persist those changes.

Initialization

public virtual void Initialize()

Override this method to perform any initialization required before your Historical Data Provider begins servicing requests. For example, you might initialize an API client, load cached metadata, or register custom markets.

Returning Historical Data

The primary responsibility of a Historical Data Provider is to return historical data for a symbol, scale, and requested date range.

protected abstract BarHistory GetHistoryInternal(
    string symbol,
    HistoryScale scale,
    DateTime startDate,
    DateTime endDate,
    int maxBars);

Override this method to obtain historical data from your Provider's underlying source and return it as a BarHistory. Interpret the request parameters as follows:

  • If maxBars is non-zero, return up to that number of the most recent bars.
  • If endDate is DateTime.MaxValue, return data through the current date and time.
  • If startDate is DateTime.MinValue, return data as far back into history as possible.
  • Otherwise, use the range specified by startDate and endDate.

A typical implementation should:

  1. Verify that the requested HistoryScale is supported. Return null if it is not.
  2. Verify that the requested symbol is supported. Return null if it is not.
  3. Create a BarHistory using the requested symbol and scale.
  4. Assign SecurityName if available.
  5. Retrieve historical data from the source for as much of the requested range as possible.
  6. Add each bar using the BarHistory Add method.
  7. Return the completed BarHistory.

For example:

protected override BarHistory GetHistoryInternal(
    string symbol,
    HistoryScale scale,
    DateTime startDate,
    DateTime endDate,
    int maxBars)
{
    if (!SupportsScale(scale))
        return null;

    BarHistory bars = new BarHistory(symbol, scale);

    bars.SecurityName = GetSecurityName(symbol);

    // Retrieve data from the source and populate bars.

    return bars;
}

Do Not Return Partial Bars

GetHistoryInternal should return only completed bars. Do not include an incomplete current Daily or intraday bar in the returned BarHistory. If the source includes a potentially incomplete final bar, remove it before returning the history.

public BarData RemovePartialBar(BarHistory bh)

Call this helper method to remove a potentially incomplete final bar. If a partial bar is removed, the method returns it as a BarData instance. Otherwise it returns null. Before calling RemovePartialBar, make sure the BarHistory has the correct MarketDetails assigned to its Market property so WealthLab can correctly determine whether the final interval is complete.

SupportsScale

public virtual bool SupportsScale(HistoryScale scale)

Override this method to indicate whether your underlying source can natively provide the specified scale. Return true only when the source itself supports that scale. Do not return true merely because WealthLab can compress another scale into the requested one. For example, if the source provides Daily data but not Weekly data, return true for Daily and false for Weekly. WealthLab can perform the Daily-to-Weekly compression itself. Correctly implementing SupportsScale reduces unnecessary data requests and improves performance.

GetPartialBarInternal

protected virtual BarData GetPartialBarInternal(
    string symbol,
    HistoryScale scale)

Override this method if your Provider supports partial bars. A partial bar represents the current incomplete interval while a market is still trading. For example, at 2:00 PM during a U.S. stock market session, the current Daily bar contains OHLC/V information from the market open through 2:00 PM but is not yet complete. That bar should not be returned by GetHistoryInternal. Instead, return it from GetPartialBarInternal. Return null if a partial bar is not available.

Returning Symbol Metadata

Historical Data Providers can supply additional information describing individual symbols.

GetMarketForSymbol

public virtual MarketDetails GetMarketForSymbol(string symbol)

Override this method to return the MarketDetails representing the market in which the symbol trades. You can obtain existing markets through MarketManager, or create and register custom MarketDetails instances during Initialize. The default implementation returns the U.S. stock market.

GetSecurityName

public virtual string GetSecurityName(string symbol)

Override this method to return a descriptive security name for the specified symbol.

For example:

MSFT → Microsoft Corp

If the security name is available when loading historical data, you can also assign it directly to the BarHistory's SecurityName property.

GetSymbolInfoForSymbol

public virtual SymbolInfo GetSymbolInfoForSymbol(
    string symbol,
    double price = 0.0)

Override this method to return a SymbolInfo instance describing the requested symbol. SymbolInfo includes metadata such as:

  • Security type
  • Pricing decimal precision
  • Quantity or volume precision
  • Other instrument-specific characteristics

IsSymbolDelisted

public virtual bool IsSymbolDelisted(string symbol)

Return true if the Provider considers the specified symbol to represent a delisted security.

IsSymbolNonTradable

public virtual bool IsSymbolNonTradable(
    string symbol,
    bool excludeBroadMarketIndexes = true)

Return true if the symbol should be considered non-tradable, such as a sentiment series or index. The excludeBroadMarketIndexes parameter controls whether broad market indexes should be excluded from this determination.

Options Support

Historical Data Providers can optionally expose options-related functionality.

GetOptionsSymbol

public virtual string GetOptionsSymbol(
    BarHistory underlierBars,
    OptionType optionType,
    double price,
    DateTime currentDate,
    int minDaysAhead = 0,
    bool useWeeklies = false,
    bool allowExpired = false,
    bool closestStrike = true,
    double multiplier = 100)

Override this method if your Provider supports options data. Return an option symbol in the format expected by your Provider based on the supplied criteria. If the Provider supports option chains, you will typically obtain or cache a chain and select an appropriate contract from it.

The parameters are:

Parameter Description
underlierBars BarHistory of the underlying security. Use it for symbol, scale, and underlying information.
optionType OptionType.Call or OptionType.Put.
price Target price used to select the strike.
currentDate Current date in the Strategy or backtest.
minDaysAhead Minimum number of days to expiration from currentDate.
useWeeklies true to select weekly expirations; false for regular monthly expirations.
allowExpired Allows selection of expired contracts, primarily for historical backtesting. This should normally be false for live trading.
closestStrike If true, select the strike closest to price. If false, select the next higher Call strike or next lower Put strike.
multiplier Contract multiplier. The default is 100.

When allowExpired is enabled, selection of historical contracts depends on whether the required expired contract data is available from the Provider.

GetOptionChain

public virtual OptionChain GetOptionChain(string underlier)

Override this method to return an OptionChain for the specified underlying symbol. Return null if option chains are not supported.

GetOptionChainSnapshot

public virtual List<OptionGreek> GetOptionChainSnapshot(
    string underlier,
    DateTime expiration,
    OptionType optionType = OptionType.Call,
    double lowStrike = 0,
    double highStrike = 0)

Override this method to return an option chain snapshot represented by a collection of OptionGreek instances. The result should contain contracts for the specified:

  • Underlying
  • Expiration
  • Option type
  • Strike range

If highStrike is zero, return all available strikes for the requested expiration. Return an empty list if the functionality is not supported.

GetSymbolExpiry

public virtual DateTime GetSymbolExpiry(string optionSymbol)

The default implementation uses OptionsHelper to parse the option symbol and determine its expiration date. Override this method if the Provider uses an option symbol format that OptionsHelper cannot parse.

GetSymbolStrike

public virtual double GetSymbolStrike(string optionSymbol)

The default implementation uses OptionsHelper to parse the option symbol and obtain its strike price. Override this method if the Provider's option symbol format is not compatible with OptionsHelper.

GetGreeks

public virtual OptionGreek GetGreeks(string optionSymbol)

Override this method to return an OptionGreek containing the Greeks and other option metrics available from your Provider. Populate only the fields supported by the data source. Return null if not implemented.

public virtual OptionGreek GetGreeks(
    string optionSymbol,
    double impliedVolatility,
    double priceUnderlying)

Override this overload if your Provider can calculate theoretical Greeks using a user-supplied implied volatility and underlying price. Return null if not supported.

CalculateIV

public virtual double CalculateIV(
    string optionSymbol,
    double priceOption,
    double priceUnderlying)

Override this method if your Provider can calculate implied volatility from an option price and underlying price. Return Double.NaN if not supported.

CalculateOptionPrice

public virtual double CalculateOptionPrice(
    string optionSymbol,
    double impliedVolatility,
    double priceUnderlying)

Override this method if your Provider can calculate a theoretical option price from implied volatility and the underlying price. Return Double.NaN if not supported.

Returning Quotes

public virtual double GetQuote(string symbol)

Returns a current price for the specified symbol. The default implementation determines whether the symbol's market is currently open. If the market is open, it attempts to obtain the latest price through GetPartialBarInternal. If the market is closed, it calls GetHistoryInternal to obtain the most recent Daily closing price. Override this method if your Provider has a more direct quote API or requires different behavior.

Persistent Storage

public virtual bool UsesPersistentStorage

Returns false by default. Override this property and return true to use WealthLab's built-in persistent storage mechanism. Persistent storage is useful for remote data sources because WealthLab can retain previously downloaded history locally and request only incremental updates instead of downloading the entire history each time.

Loading and Saving Stored History

public virtual BarHistory LoadFromStorage(
    string symbol,
    HistoryScale scale,
    DateTime? startDate = null,
    DateTime? endDate = null,
    int maxBars = 0)

public virtual void SaveToStorage(BarHistory bh)

The default persistent storage mechanism stores historical data in binary .QX files. Override these methods if your Provider requires a custom persistence format or storage location.

ConstructSymbolFileName

protected virtual string ConstructSymbolFileName(
    string symbol,
    HistoryScale scale)

Returns the file name WealthLab uses for a symbol and scale combination. The default name uses the .QX extension. You can call this method when implementing custom storage, or override it if your Provider requires different file naming.

ReloadHistory

public virtual BarHistory ReloadHistory(
    string symbol,
    HistoryScale scale)

WealthLab calls this method when the user requests Reload Chart Data. The default implementation reloads the Provider's stored history. Override it if you also need to remove Provider-specific cached or persisted information before reloading the symbol.

DeleteLocalData

public virtual void DeleteLocalData()

WealthLab calls this method when the user selects Delete all Local Data for this Provider in the Data Manager. The base implementation removes WealthLab's standard persisted data. Override it if your Provider also stores additional files, database records, caches, or other local information that should be deleted.

ClearRequestLists

public virtual void ClearRequestLists()

WealthLab calls this method when the user selects Clear Internal Tracking Info in the Data Manager. The default implementation clears WealthLab's internal tracking of symbol requests and update times. Override it if your Provider maintains additional in-memory or persistent request-tracking information.

Data Corrections

Some data sources can revise previously published historical bars.

SupportsDataCorrections

public virtual bool SupportsDataCorrections

Return true if your Provider can detect and return corrections to previously downloaded data. When enabled, WealthLab calls GetHistoryInternalWithCorrections when updating an existing history.

GetHistoryInternalWithCorrections

protected virtual ResponseWithCorrections
    GetHistoryInternalWithCorrections(
        string symbol,
        HistoryScale scale,
        DateTime startDate,
        DateTime endDate,
        int maxBars,
        DateTime lastRequestDate)

Implement this method using logic similar to GetHistoryInternal, but return a ResponseWithCorrections rather than a BarHistory directly. Populate its two primary properties:

  • Bars - The result of the current historical data request.
  • Corrections - A BarHistory containing corrected bars that changed after lastRequestDate.

CompanionEventProviderName

protected virtual string CompanionEventProviderName

Override this property if your Historical Data Provider has a corresponding Event Provider. Return the Event Provider's Name. This allows WealthLab to coordinate historical data updates with events such as splits or dividends and reload data when appropriate according to the user's Data Preferences.

ReloadOnAnomaly

public virtual bool ReloadOnAnomaly(string symbol)

Determines whether WealthLab should completely reload Daily or higher-scale data when it detects certain anomalies during an update. These can include:

  • A price gap exceeding WealthLab's anomaly threshold
  • A newly detected dividend
  • A newly detected split

The default value is true. Override this method if your Provider should handle these cases differently.

Bulk Provider Updates

public override bool SupportsBulkUpdate

Determines whether the Historical Data Provider supports Provider-wide updates from the Data Manager. A Provider Update is intended to update all persisted data belonging to the Provider. By default, this property returns the value of UsesPersistentStorage. If you use WealthLab's built-in persistence mechanism, you can generally use the default bulk update implementation. If you use custom persistence or require specialized update logic, override the following methods.

PerformBulkUpdate

public override void PerformBulkUpdate(
    IBulkUpdateHost updateHost)

Override this method to perform a Provider-wide update. Use the supplied IBulkUpdateHost to communicate update progress, messages, and status to WealthLab.

CancelBulkUpdate

public override void CancelBulkUpdate()

WealthLab calls this method when the user cancels a Provider Update. Override it to stop your Provider-specific update process.

ScalesWithData

public virtual List<HistoryScale> ScalesWithData

If you implement custom persistent storage and support Provider Updates, override this property to return all HistoryScale instances for which your Provider currently has stored data.

GetSymbolsForScale

public virtual List<string> GetSymbolsForScale(
    HistoryScale scale)

If you use custom persistence and support Provider Updates, override this method to return the symbols for which data is stored at the specified scale.

SupportsParallelRequests

public override bool SupportsParallelRequests()

Override this method and return false if bulk Provider Updates must issue historical data requests sequentially rather than in parallel.

Parallel Processing Considerations

WealthLab can call GetHistoryInternal simultaneously from multiple threads. Your implementation should therefore be thread-safe. Whenever possible, avoid mutable class-level variables inside GetHistoryInternal and use local variables instead. The SupportsParallelRequests setting controls parallel processing during bulk updates, but other parts of WealthLab can still request historical data concurrently. If the underlying API or data source cannot handle concurrent requests, use synchronization in your Provider.

For example:

private readonly object _requestLock = new();

protected override BarHistory GetHistoryInternal(
    string symbol,
    HistoryScale scale,
    DateTime startDate,
    DateTime endDate,
    int maxBars)
{
    lock (_requestLock)
    {
        // Provider request and processing logic.
    }
}

This approach allows WealthLab to make concurrent requests while ensuring that your Provider processes them one at a time.

Tick Data Support

GetTicks

public virtual List<Tick> GetTicks(
    string symbol,
    DateTime startDate,
    DateTime endDate)

Override this method if your Historical Data Provider supports tick data. Return a collection of Tick instances for the requested symbol and date range.

UsesTickDataStore

public virtual bool UsesTickDataStore

Return true to use WealthLab's built-in persistent storage mechanism for tick data. When enabled, WealthLab stores downloaded ticks locally and makes subsequent GetTicks requests only when additional data is required.

Custom Configuration and New DataSet UI

Historical Data Providers can provide custom configuration panels and custom pages for the New DataSet interface. See Providing User Interfaces for WealthLab Components for details.

Integrating with the Symbol Chooser

The Symbol Chooser appears below chart and other symbol entry fields and displays matching securities as the user types. By default, WealthLab can suggest symbols that have already been downloaded or otherwise encountered. A Historical Data Provider can override this behavior and return matches directly from its data source.

public virtual List<SymbolChooserItem> GetChooserSymbols(
    string filter)

Override this method to return SymbolChooserItem instances matching the text supplied in filter. Each SymbolChooserItem should populate the following properties:

  • Symbol - The matching symbol, for example AAPL.
  • SecurityName - The descriptive security name, for example Apple Inc.
  • Source - Assign your DataProviderBase instance.
  • ProviderName - Assign the Provider's Name.

For example:

public override List<SymbolChooserItem> GetChooserSymbols(
    string filter)
{
    List<SymbolChooserItem> results = new();

    // Search the Provider's symbol source.

    results.Add(new SymbolChooserItem
    {
        Symbol = "AAPL",
        SecurityName = "Apple Inc.",
        Source = this,
        ProviderName = Name
    });

    return results;
}

This allows users to discover and select symbols supported by your Provider without first downloading their historical data.