Table of Contents

Gluing Candles: History + Real-Time

To combine historical candles with real-time data, you need to initialize the appropriate storages: storage for trading objects CsvEntityRegistry, storage for market data StorageRegistry, and snapshot storage registry SnapshotRegistry.

Let's look at an example from the Samples/Candles/CombineHistoryRealtime project:

Setting Up Storages and Connector

public partial class MainWindow
{
    private readonly Connector _connector;
    private const string _connectorFile = "ConnectorFile.json";
    
    // Path to historical data
    private readonly string _pathHistory = Paths.HistoryDataPath;
    
    private Subscription _subscription;
    private ChartCandleElement _candleElement;
    
    public MainWindow()
    {
        InitializeComponent();
        
        // Initialize storages
        var entityRegistry = new CsvEntityRegistry(_pathHistory);
        var storageRegistry = new StorageRegistry
        {
            DefaultDrive = new LocalMarketDataDrive(_pathHistory)
        };
        
        // Create connector with configured storages
        _connector = new Connector(
            entityRegistry.Securities, 
            entityRegistry.PositionStorage, 
            new InMemoryExchangeInfoProvider(), 
            storageRegistry, 
            new SnapshotRegistry("SnapshotRegistry"));
        
        // Register message adapter provider
        ConfigManager.RegisterService<IMessageAdapterProvider>(
            new InMemoryMessageAdapterProvider(_connector.Adapter.InnerAdapters));
        
        // Load connector settings if file exists
        if (File.Exists(_connectorFile))
        {
            _connector.Load(_connectorFile.Deserialize<SettingsStorage>());
        }
        
        // Set default candle data type (5-minute)
        CandleDataTypeEdit.DataType = TimeSpan.FromMinutes(5).TimeFrame();
    }
}

Connection Setup

// Method for configuring connection parameters
private void Setting_Click(object sender, RoutedEventArgs e)
{
    // Call connector configuration window
    if (_connector.Configure(this))
    {
        // Save settings to file
        _connector.Save().Serialize(_connectorFile);
    }
}

// Method for connecting to trading system
private void Connect_Click(object sender, RoutedEventArgs e)
{
    // Set connector as data source for instrument selection
    SecurityPicker.SecurityProvider = _connector;
    
    // Subscribe to candle reception event
    _connector.CandleReceived += Connector_CandleReceived;
    
    // Connect
    _connector.Connect();
}

Processing Candles and Displaying on Chart

// Handler for candle reception event
private void Connector_CandleReceived(Subscription subscription, ICandleMessage candle)
{
    // Draw candle on chart
    Chart.Draw(_candleElement, candle);
}

Creating Candle Subscription

// Method called when an instrument is selected
private void SecurityPicker_SecuritySelected(Security security)
{
    // Check if instrument is selected
    if (security == null) 
        return;
    
    // Unsubscribe from previous subscription if it exists
    if (_subscription != null) 
        _connector.UnSubscribe(_subscription);
    
    // Create new subscription for selected instrument
    _subscription = new(CandleDataTypeEdit.DataType, security)
    {
        MarketData =
        {
            // Request historical data for last 720 days
            From = DateTime.Today.AddDays(-720),
            
            // Mode: load historical data and build in real-time
            BuildMode = MarketDataBuildModes.LoadAndBuild,
        }
    };
    
    // Configure chart
    Chart.ClearAreas();
    
    // Create chart area and element for displaying candles
    var area = new ChartArea();
    _candleElement = new ChartCandleElement();
    
    // Add area and element to chart
    Chart.AddArea(area);
    
    // Link chart element with subscription for automatic drawing
    Chart.AddElement(area, _candleElement, _subscription);
    
    // Start subscription
    _connector.Subscribe(_subscription);
}

Complete MainWindow Class Example

namespace StockSharp.Samples.Candles.CombineHistoryRealtime;

using System;
using System.Windows;
using System.IO;

using Ecng.Common;
using Ecng.Serialization;
using Ecng.Configuration;

using StockSharp.Configuration;
using StockSharp.Algo;
using StockSharp.Algo.Storages;
using StockSharp.Algo.Storages.Csv;
using StockSharp.BusinessEntities;
using StockSharp.Xaml;
using StockSharp.Messages;
using StockSharp.Xaml.Charting;
using StockSharp.Charting;

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow
{
    private readonly Connector _connector;
    private const string _connectorFile = "ConnectorFile.json";

    private readonly string _pathHistory = Paths.HistoryDataPath;

    private Subscription _subscription;
    private ChartCandleElement _candleElement;
    
    public MainWindow()
    {
        InitializeComponent();
        var entityRegistry = new CsvEntityRegistry(_pathHistory);
        var storageRegistry = new StorageRegistry
        {
            DefaultDrive = new LocalMarketDataDrive(_pathHistory)
        };
        _connector = new Connector(
            entityRegistry.Securities, 
            entityRegistry.PositionStorage, 
            new InMemoryExchangeInfoProvider(), 
            storageRegistry, 
            new SnapshotRegistry("SnapshotRegistry"));

        // registering all connectors
        ConfigManager.RegisterService<IMessageAdapterProvider>(
            new InMemoryMessageAdapterProvider(_connector.Adapter.InnerAdapters));

        if (File.Exists(_connectorFile))
        {
            _connector.Load(_connectorFile.Deserialize<SettingsStorage>());
        }

        CandleDataTypeEdit.DataType = TimeSpan.FromMinutes(5).TimeFrame();
    }

    private void Setting_Click(object sender, RoutedEventArgs e)
    {
        if (_connector.Configure(this))
        {
            _connector.Save().Serialize(_connectorFile);
        }
    }

    private void Connect_Click(object sender, RoutedEventArgs e)
    {
        SecurityPicker.SecurityProvider = _connector;
        _connector.CandleReceived += Connector_CandleReceived;
        _connector.Connect();
    }

    private void Connector_CandleReceived(Subscription subscription, ICandleMessage candle)
    {
        Chart.Draw(_candleElement, candle);
    }

    private void SecurityPicker_SecuritySelected(Security security)
    {
        if (security == null) return;
        if (_subscription != null) _connector.UnSubscribe(_subscription);

        _subscription = new(CandleDataTypeEdit.DataType, security)
        {
            MarketData =
            {
                From = DateTime.Today.AddDays(-720),
                BuildMode = MarketDataBuildModes.LoadAndBuild,
            }
        };

        //-----------------Chart--------------------------------
        Chart.ClearAreas();

        var area = new ChartArea();
        _candleElement = new ChartCandleElement();

        Chart.AddArea(area);
        Chart.AddElement(area, _candleElement, _subscription);

        _connector.Subscribe(_subscription);
    }
}

Example Features

  1. Creating Storages:

  2. Creating Subscription:

    • The class Subscription is used
    • The From parameter in MarketData specifies the initial date for loading history
    • BuildMode = MarketDataBuildModes.LoadAndBuild is set for automatic combination of history and real-time
  3. Chart Display:

    • Chart.AddElement method is used to link chart element with subscription
    • The chart is automatically updated when new candles are received
  4. Event Handling:

    • Subscription to CandleReceived event for processing received candles
    • Unsubscribing from previous subscription when selected instrument changes

Extended Capabilities

This example can be extended by adding the following functions:

Tracking Transition to Real-Time Mode

// Subscription to the event of transition to real-time mode
_connector.SubscriptionOnline += OnSubscriptionOnline;

// Event handler
private void OnSubscriptionOnline(Subscription subscription)
{
    if (subscription == _subscription)
    {
        this.GuiAsync(() => StatusLabel.Content = "Online mode");
    }
}

Configuring History Loading Period

// Setting history loading period
private void SetHistoryPeriod(int days)
{
    if (_subscription != null)
    {
        _connector.UnSubscribe(_subscription);
        
        _subscription.MarketData.From = DateTime.Today.AddDays(-days);
        
        _connector.Subscribe(_subscription);
    }
}

Saving Received Data

// Method for saving received data
private void SaveReceivedData()
{
    if (_connector.StorageAdapter != null)
    {
        // Force save cached data to disk
        _connector.StorageAdapter.Flush();
    }
}

Additional Candle Processing

// Extended candle processing with information output
private void ExtendedCandleProcessing(Subscription subscription, ICandleMessage candle)
{
    // Draw candle on chart
    Chart.Draw(_candleElement, candle);
    
    // Output information about candle to logs
    this.GuiAsync(() => 
    {
        var status = subscription.State == SubscriptionStates.Online ? "Real-time" : "History";
        LogControl.LogMessage($"{status}: {candle.OpenTime} - O:{candle.OpenPrice} H:{candle.HighPrice} L:{candle.LowPrice} C:{candle.ClosePrice}");
    });
}