Obtaining and using candle data
To receive and use these candles, you need to get an object of the IndicatorCandle class. To do this, we can use the GetCandle(int bar) method by specifying the bar number.
It must be remembered that bar is an integer and the numbering of bars starts from 0 and ends with CurrentBar - 1, where CurrentBar is the total number of loaded bars.
We can get the bar value from the parameters in the overridden OnCalculate method.
If you specify a bar number less than 0 or greater than CurrentBar - 1 when calling the GetCandle(bar) method, this will lead to an error and the calculations will be interrupted.
Performance. On the current bar OnCalculate is called on every tick, so call GetCandle(bar) once and store the result in a local variable instead of requesting the candle repeatedly for the same bar. Iterating cluster (footprint) data via GetAllPriceLevels() is more expensive than accessing the candle itself — do it only when necessary. For more performance recommendations see the «Performance recommendations» article.
The IndicatorCandle object contains data that can be obtained from its properties or by calling methods of this object.
Some properties worth clarifying:
- LastTime - time of the last trade on this bar,
- MaxDelta is the largest delta value that was on this bar,
- MinDelta - the lowest delta value that was on this bar,
- MaxOI - the largest OI value that was on this bar,
- MinOI - the lowest OI value that was on this bar
{
protected override void OnCalculate(int bar, decimal value)
{
var candle = GetCandle(bar);
var open = candle.Open;
var close = candle.Close;
var high = candle.High;
var low = candle.Low;
var volume = candle.Volume;
var ask = candle.Ask;
var bid = candle.Bid;
var delta = candle.Delta;
}
}
Base class for custom indicators.
Definition Indicator.cs:50
Definition IIndicatorAnalyticsService.cs:1
Footprint data
One important data type is bar data at a certain price (footprint data), which is stored in the PriceVolumeInfo object.
PriceVolumeInfo stores such data as: price, the number of volumes traded at the bid price, ask price, etc.
Here are some ways to get PriceVolumeInfo objects from an IndicatorCandle object:
{
protected override void OnCalculate(int bar, decimal value)
{
var candle = GetCandle(bar);
var high = candle.High;
var highData = candle.GetPriceVolumeInfo(high);
if (highData != null)
{
var highVolume = highData.Volume;
}
var maxVolumeData = candle.MaxVolumePriceInfo;
if (maxVolumeData != null)
{
var maxVolume = maxVolumeData.Volume;
}
var allPriceLevels = candle.GetAllPriceLevels();
foreach ( var level in allPriceLevels )
{
if (level == null)
continue;
var price = level.Price;
var volume = level.Volume;
var delta = level.Ask - level.Bid;
}
}
}
Getting online ticks and aggregated trades
To get the online tick data, it is necessary to redefine the OnNewTrade(MarketDataArg arg) method:
{
protected override void OnCalculate(int bar, decimal value)
{
}
{
}
}
Represents a data point in the market.
Definition MarketDataArg.cs:11
API also allows getting aggregated trades. For this, it is necessary to redefine the OnCumulativeTrade(CumulativeTrade arg) method.
This method will be automatically called when a new cumulative trade appears. Such trades may be subsequently modified. In order to keep track of these changes, you need to override the OnUpdateCumulativeTrade(CumulativeTrade trade) method. To check whether an update belongs to the same cumulative trade, compare the trades with the IsEqual extension method (class ATAS.Indicators.Extensions) (it compares the time, direction and first price of the trades) rather than by reference.
Example of realization of the indicator, which outputs the delta of cumulative trades of the volume of more than 3 lots:
public class SampleCumulativeTrades :
Indicator
{
private decimal _lastCumulativeTradeVolume;
protected override void OnCalculate(int bar, decimal value)
{
}
{
AddCumulativeTrade(trade);
}
{
AddCumulativeTrade(trade);
}
{
return;
if (_lastTrade == null || !_lastTrade.IsEqual(trade))
{
_lastTrade = trade;
this[CurrentBar - 1] += GetVolumeByDirection(trade.
Volume, trade.
Direction);
}
else
{
this[CurrentBar - 1] += GetVolumeByDirection(trade.
Volume - _lastCumulativeTradeVolume, trade.
Direction);
}
_lastCumulativeTradeVolume = trade.
Volume;
}
private decimal GetVolumeByDirection(decimal volume,
TradeDirection direction)
{
return volume * (direction == TradeDirection.Buy ? 1 : -1);
}
}
Represents a cumulative trade, which is a trade that includes multiple prints or executions.
Definition CumulativeTrade.cs:10
decimal Volume
Gets or sets the cumulative volume of the trade.
Definition CumulativeTrade.cs:24
TradeDirection Direction
Gets or sets the trade direction (Buy or Sell).
Definition CumulativeTrade.cs:34
TradeDirection
Represents the trade direction.
Definition DataFeedsCore/TradeDirection.cs:7
To get historical cumulative trades, you need to call the RequestForCumulativeTrades method, pass the CumulativeTradesRequest request to the parameters, and override the OnCumulativeTradesResponse method. In the request, you need to specify the beginning and end of the time period for which you want to get cumulative trades, you can also set filters for the minimum and maximum volume of trades. If you specify 0 for the minimum and maximum volumes, then filters by volume will not be applied.
Keep in mind:
- the requested period must not exceed 7 days;
- only one request can be processed at a time: calling RequestForCumulativeTrades again before the response to the previous request has arrived throws an exception;
- if the indicator is hidden and its alerts are disabled, the request is postponed until the indicator becomes visible.
OnFinishRecalculate is called after every recalculation of the indicator, so do not send a new request from it each time.
An example of an indicator that requests cumulative trades for the loaded bars once (limited to the last 7 days of the chart):
using System;
using System.Collections.Generic;
using System.Linq;
public class SampleHistoricalCumulativeTrades :
Indicator
{
private List<CumulativeTrade> _trades = new();
protected override void OnCalculate(int bar, decimal value)
{
}
protected override void OnFinishRecalculate()
{
if (_request != null || CurrentBar <= 0)
return;
var lastTime = GetCandle(CurrentBar - 1).LastTime;
var startTime = GetCandle(0).Time;
if (lastTime - startTime > TimeSpan.FromDays(7))
startTime = lastTime.AddDays(-7);
RequestForCumulativeTrades(_request);
}
protected override void OnCumulativeTradesResponse(
CumulativeTradesRequest request, IEnumerable<CumulativeTrade> cumulativeTrades)
{
if (request != _request)
return;
_trades = cumulativeTrades.ToList();
}
}
Represents a request to retrieve cumulative trade data within a specified time range or for a particu...
Definition CumulativeTradesRequest.cs:43
Working with Market Depth
API allows getting the data about updates of the MarketDepth. For this, the MarketDepthChanged(MarketDataArg arg) method should be redefined:
{
protected override void OnCalculate(int bar, decimal value)
{
}
{
}
}
It is also possible to get the total volume of all Bids and total volume of all Asks with the help of the MarketDepthInfo object properties: CumulativeDomAsks and CumulativeDomBids. Example of the indicator which reflects the history of the total values of the Asks and Bids volumes
using System.Windows.Media;
{
private int _lastCalculatedBar = 0;
private bool _first = true;
public DomPower() : base(true)
{
_asks.Name = "Asks";
_bids.Color = Colors.Green;
DataSeries.Add(_bids);
}
protected override void OnCalculate(int bar, decimal value)
{
}
{
if (_first)
{
_first = false;
_lastCalculatedBar = CurrentBar - 1;
}
var lastCandle = CurrentBar - 1;
var cumAsks = MarketDepthInfo.CumulativeDomAsks;
var cumBids = MarketDepthInfo.CumulativeDomBids;
for (int i = _lastCalculatedBar; i <= lastCandle; i++)
{
_asks[i] = -cumAsks;
_bids[i] = cumBids;
}
_lastCalculatedBar = lastCandle;
}
}
Implementation of the IIndicatorDataProvider interface that provides access to various data and servi...
Definition IndicatorDataProvider.cs:154
const string NewPanel
Represents the name of a new panel.
Definition IndicatorDataProvider.cs:166
Represents a data series of decimal values, each element is a decimal.
Definition ValueDataSeries.cs:26
In some cases it might be necessary to get a snapshot of the MarketDepth data. For this, the MarketDepthInfo.GetMarketDepthSnapshot() function could be used. The function returns a list of all levels in the DOM.
MBO (Market By Orders)
MBO data is available only if the connector and the data feed of the instrument provide it.
To receive MBO (Market By Orders) data, first enable the subscription by calling the SubscribeMarketByOrderData method. The returned Task completes when the subscription is active. After that:
MarketByOrder objects have the following properties:
- Security - security associated with the market by order entry.
- Time - date and time of the market by order entry.
- SourceTimeNs - exchange (source) time of the entry in nanoseconds since the Unix epoch (UTC); 0 if the data feed does not provide it.
- Type - type of market by order update:
- Snapshot - Indicates that the MBO data is from a cache and not from the real-time data stream.
- New - Indicates that the MBO data represents a new order added to the order book of this instrument.
- Change - Indicates that the MBO data represents a change to an existing order.
- Delete - Indicates that the MBO data represents a deletion of an existing order.
- Side - side of the order:
- Bid - a buy order
- Ask - a sell order
- Priority - Priority of this order in the exchange's matching engine queue.
- ExchangeOrderId - Exchange order id of this order.
- Price - Price associated with this order.
- Volume - Volume associated with this order.
- Reason - why the order changed (for example, cancelled or executed); Unknown if the data feed does not state it.
- Flags - batch boundary markers (for example, the beginning and the end of a snapshot) for data feeds that deliver updates in atomic batches.
- ReceivedTime - UTC time when the platform received the entry.
Below is an example of an indicator receiving MBO data.
using System.Collections.Generic;
using Utils.Common.Logging;
public class SampleMBODataIndicator :
Indicator
{
protected override async void OnInitialize()
{
await SubscribeMarketByOrderData();
ProcessMarketByOrders(MarketByOrders);
}
protected override void OnMarketByOrdersChanged(IEnumerable<MarketByOrder> values)
{
this.LogInfo("new MBO data");
ProcessMarketByOrders(values);
}
private void ProcessMarketByOrders(IEnumerable<MarketByOrder> marketByOrders)
{
foreach (var marketByOrder in marketByOrders)
{
this.LogInfo("{0}", marketByOrder);
}
}
#region Overrides of ExtendedIndicator
{
this.LogInfo($"ExchangeOrderId: {trade.ExchangeOrderId}, AggressorExchangeOrderId: {trade.AggressorExchangeOrderId}");
}
#endregion
protected override void OnCalculate(int bar, decimal value)
{
}
}
Definition AccountKind.cs:1
Statistical data
You can get trading statistics using the indicator property TradingStatisticsProvider (check it for null before use). Statistical data is stored in objects that implement ITradingStatistics. The provider offers two of them:
- RawStatistics - all orders, own trades and closed trades of the platform, without any filter. Use it for the Orders, MyTrades, HistoryMyTrades and DailyNotes collections; the Equity and Statistics collections are not available in this object.
- FilteredStatistics - the statistics filtered by the period, accounts, instruments and playbooks selected in the platform's statistics window, including the calculated Equity and Statistics collections. The current filter is available in the provider's From, To, Accounts, Securities and Playbooks properties. These properties are set by the statistics window; changing them from an indicator changes the filter of the statistics window as well.
The provider replaces these objects when the statistics source changes (for example, when a Replay connection is started or stopped) and raises the RawStatisticsSourceChanged and FilteredStatisticsSourceChanged events with the new object. Do not keep a reference to the old object: unsubscribe from it and subscribe to the new one. After the statistics are rebuilt or cleared, the provider raises the StatisticsRebuilt event.
The Realtime and Replay properties and the LoadHistoryAsync method of the provider are obsolete. Use FilteredStatistics or RawStatistics instead.
The ITradingStatistics object contains the following collections:
Each collection can be enumerated, and you can track its changes by subscribing to its events:
- Added
- Changed
- Removed
- Cleared
An example of an indicator that logs the closed trades of the account and instrument selected on the chart, and logs each new closed trade:
using System;
using System.Linq;
using Utils.Common.Logging;
{
protected override void OnCalculate(int bar, decimal value)
{
}
protected override void OnInitialize()
{
if (TradingStatisticsProvider is null)
return;
TradingStatisticsProvider.RawStatisticsSourceChanged += OnStatisticsSourceChanged;
TradingStatisticsProvider.StatisticsRebuilt += LogClosedTrades;
SetStatistics(TradingStatisticsProvider.RawStatistics);
}
protected override void OnDispose()
{
if (TradingStatisticsProvider != null)
{
TradingStatisticsProvider.RawStatisticsSourceChanged -= OnStatisticsSourceChanged;
TradingStatisticsProvider.StatisticsRebuilt -= LogClosedTrades;
}
SetStatistics(null);
}
{
SetStatistics(statistics);
}
{
if (_statistics != null)
_statistics.HistoryMyTrades.Added -= OnClosedTradeAdded;
_statistics = statistics;
if (_statistics == null)
return;
_statistics.HistoryMyTrades.Added += OnClosedTradeAdded;
LogClosedTrades();
}
private void LogClosedTrades()
{
.Where(IsChartTrade)
.ToList();
if (trades != null)
this.LogInfo($"Closed trades: {trades.Count}, total PnL: {trades.Sum(t => t.PnL)}");
}
{
if (IsChartTrade(trade))
this.LogInfo($"New closed trade: {trade.OpenTime} - {trade.CloseTime}, PnL: {trade.PnL}");
}
{
var portfolio = TradingManager?.Portfolio;
var security = TradingManager?.Security;
return portfolio != null
&& security != null
&& trade.AccountID == portfolio.AccountID
&&
string.Equals(trade.
SecurityId, security.SecurityId, StringComparison.InvariantCultureIgnoreCase);
}
}
Represents a historical trade record.
Definition HistoryMyTrade.cs:37
string? SecurityId
Gets or sets the security ID associated with the historical trade.
Definition HistoryMyTrade.cs:55
Definition ITradingStatistics.cs:10
IMutableEnumerable< HistoryMyTrade > HistoryMyTrades
Definition ITradingStatistics.cs:15
Definition ITradingStatistics.cs:1