Open main menu

4.7.4 Receiving the data for any symbol and any resolution. DataLoader

The studies can download the data for any symbol that is available in the database even if a chart for this instrument is not created in MultiCharts .NET. This service is available via the DataLoader property returned by the IDataLoader interface. This interface contains the methods for starting and stopping the data downloading process.


To start the data downloading process it is necessary to call the following method:

IDataLoaderResult BeginLoadData(InstrumentDataRequest Request, LoadDataCallback Sink, object State)

where InstrumentDataRequest is the structure that determines the data request parameters. It contains the following fields:

  • public string Symbol – instrument name;
  • public string DataFeed – data feed name;
  • public string Exchange – exchange name;
  • public ESymbolCategory Category – symbol category (for example Future, Stock, Index, etc.);
  • public Resolution Resolution – request resolution


Resolution structure helps to determine both regular and irregular resolutions.

  • Example of determining a regular resolution:
Resolution _res = new Resolution(EResolution.Tick, 10);

If the request resolution is set in this way then 10-tick data will be requested.


  • Example of determining an irregular resolution:
Resolution _res = Resolution.CreatePointAndFigure(EResolution.Tick, 10, 0.001, 3, PointAndFigureBasis.HighLow, true);


If the request resolution is set in this way, then the PointAndFigure resolution based on a 10-tick resolution, BoxSize = 0.001, Reversal = 3, HighLow Basis and BreakOnSession enabled will be returned.


  • public DateTime From – the start date of requested data. This field is outdated and it is better to use the Range field.
  • public DateTime To – the end date of requested data. This field is outdated and it is better to use the Range field.
  • public DataRequest Range – the range of requested data
  • public RequestTimeZone TimeZone – the time zone that will be applied to the requested data. The available values are: Local, Exchange and GMT. The Default value is Local.
  • public RequestQuoteField QuoteField – the quote field of the requested data. The available values are: Bid, Ask and Trade. The Default value isTrade.
  • public string SessionName – the name of the session that will be applied to the requested data.
  • public bool FilterOutOfRangeData –this indicates if “odd” data should be cut off. Here the “odd” data is defined as the data before the request start time and the data after the request end time.
  • public bool Subscribe2RT – this indicates whether the real time data subscription is required or if only historical data is needed.


LoadDataCallback – is a delegate with the following signature:

public delegate void LoadDataCallback(IDataLoaderResult Result);

It is used to call back the data request results. This function is called in a separate thread, different from one where BeginLoadData is called.


IDataLoaderResult – the interface that provides information in the data request results. It contains the following fields:
  • bool IsCompleted { get; } – indicates whether the data downloading process is complete or not.
  • object State { get; } – returns the current state that was set in BeginLoadData method.
  • Bar[] Data { get; } – returns the downloaded data as an array of bars.
  • BarPriceLevels[] Data2 { get; } – returns an array of downloaded price levels.
  • InstrumentDataRequest Request { get; } – is used in DataLoader. This attribute helps to make the same request for DataLoader that is used for the instrument bars.
  • Bar? RTData { get; set; } – returns realtime data (if available).
  • BarPriceLevels? RTData2 { get; set; } – returns realtime price levels (if available)
  • DataLoadedEvent Event { get; } – returns a reason for calling the callback function
  • LoadDataCallback. Available values are None, History, RTUpdateLastBar, and RTNewBar.


To cancel data downloading the void EndLoadData(IDataLoaderResult Result) method should be called. The Result of BeginLoadData call should be put into the argument as an attribute.


Dataloader Usage Example

The example indicator will calculate and plot the spreads on the symbols different from ones that are used on the chart where an indicator is applied. Assume that a EUR/USD chart is created and it is necessary to plot the spreads between the Ask price of EUR/JPY and the Bid price of EUR/AUD. This means that an additional data series will not be inserted into the EUR/USD chart and the the indicator will get all required information from the main data source.


1. Create the new indicator with the standard pattern:

namespace PowerLanguage.Indicator {
	public class DataLoader : IndicatorObject {
		public DataLoader(object _ctx):base(_ctx){}
		protected override void Create() {}
		protected override void StartCalc() {}
		protected override void CalcBar(){}
	}
}


2. Add the plot statements that will plot spread values:

private IPlotObject m_spreadPlot;


3. Then create a corresponding object. This should be done in Create() function:

protected override void Create() {
	m_spreadPlot = AddPlot(new PlotAttributes("", EPlotShapes.Line, Color.Red));
}


4. Now it is necessary to form the objects for data requests. To do that, InstrumentDataRequest is to be used but, there’s an easier workaround for this example indicator. Remember, that the Bars.Request property returns the reference to the object of this structure and its fields are filled in accordance with the main data series settings. So in this example, several parameters should be changed while the rest parameters can be left unchanged.

InstrumentDataRequest Request = Bars.Request;
Request.Subscribe2RT = true;

It means that both historical and real-time data should be received.


5. Specify the request range. Here the bars back range is specified. Number of bars back in this example is equal to the number of all complete bars on the main data series chart.

DataRequest _DataRequest = new DataRequest();
_DataRequest.RequestType = DataRequestType.BarsBack;
_DataRequest.Count = Bars.FullSymbolData.Count;
Request.Range = _DataRequest;


6. Two separate requests should be done, the first request will accumulate the Ask price of EUR/JPY and the second the Bid price of EUR/AUD.

For EUR/JPY the request will be as follows:

Request.QuoteField = RequestQuoteField.Ask;
Request.Symbol = "EUR/JPY";

For EUR/AUD the request will be as follows:

Request.QuoteField = RequestQuoteField.Bid;
Request.Symbol = "EUR/AUD";


7. Before requesting the data, it is necessary to declare the call-back function that will be used for receiving the data into study:

public void OnData(IDataLoaderResult Result){ }

It will be implemented later.


8. Having combined all of the above criteria, the following StartCalc() function can be retrieved. It will perform the creating of the requests and the requests themselves:

protected override void StartCalc() {	
	InstrumentDataRequest Request = Bars.Request;
	Request.Subscribe2RT = true;
	
	DataRequest _DataRequest = new DataRequest();
	_DataRequest.RequestType = DataRequestType.BarsBack;
	_DataRequest.Count = Bars.FullSymbolData.Count;
	Request.Range = _DataRequest;
						
	Request.QuoteField = RequestQuoteField.Ask;
	Request.Symbol = "EUR/JPY";

	DataLoader.BeginLoadData(Request, OnData, null);
	
	Request.QuoteField = RequestQuoteField.Bid;
	Request.Symbol = "EUR/AUD";

	DataLoader.BeginLoadData(Request, OnData, null);
}

For requesting data, as seen in the example above, it is necessary to call the BeginLoadData function, which contains formed request object, call-back function pointer and optional State object that will be returned into call-back function specifying its parameters.

9. Next step is to process the data. Accumulation logic should be done in the OnData() function. First, add a reference to the System.Collections.Generic namespace:

using System.Collections.Generic;

Second, container fields where data will be stored should be added into indicator class:

private List<Bar> m_EURJPY = new List<Bar>();
private List<Bar> m_EURAUD = new List<Bar>();

The IDataLoaderResult interface provides access to the call-back function of OnData()

The reason for call-back call can be checked in IDataLoaderResult::Event value. The value DataLoadedEvent.History means that historical data has been received. The value DataLoadedEvent.RTNewBar means that the new real-time bar has been received.

The data processing function can be written in the following manner:

private void ProcessData(List<Bar> Container, IDataLoaderResult Result) {
	switch (Result.Event) {
		case DataLoadedEvent.History: {
			Container.AddRange(Result.Data);
			break;
                }
		case DataLoadedEvent.RTNewBar: {
			if (Result.RTData.HasValue)
			Container.Add(Result.RTData.Value);                        
			break;
                }
	}
}

Please note that the call-back function call is to be performed in a thread separate from one where BeginLoadData has been called.


OnData function will be implemented the following:

public void OnData(IDataLoaderResult Result) {
	if (Result.Request.Symbol == "EUR/JPY") {
		ProcessData(m_EURJPY, Result);
	}

	if (Result.Request.Symbol == "EUR/AUD") {
		ProcessData(m_EURAUD, Result);
	}
}

The call of the ProcessData() function of data processing for a required container will be initiated depending on the request for which the call-back call was received.


10. The final step is to make the plotting of the spreads once all of the required information has been obtained:

protected override void CalcBar() {            
	if (Bars.Status == EBarState.Close){
		int index = Math.Min(Bars.CurrentBar, Math.Min(m_EURAUD.Count - 1, m_EURJPY.Count - 1));
		if (index > 0) {
			m_spreadPlot.Set(Math.Abs(m_EURAUD[index].Close - m_EURJPY[index].Close));
		}
	}
}
NOTE: If either VolumeDelta or CumulativeDelta are specified as ChartType in the request, then the information on Data2 will be relevant for the resulting object, if real-time is requested then RTData2 is required. Data2 is an array of BarPriceLevels.
public struct BarPriceLevels
{
	public Bar Bar { get; set; }
	public PriceLevel[] Levels { get; set; }
	public int Index { get; set; }
	public DateTime Time{ get; set; }
	public uint TickID { get; set; }
}

The Levels property contains VolumeProfile/Volume Delta data. PriceLevel[] is an array of the price levels where Index equals bar index, Time equals bar time, and TickID equals its ID.