Table of Contents

Trading data and events

The trading entities of the chart, the instrument, the portfolio, the position, the orders and the trades, are available to every chart indicator through TradingManager, and the indicator can override the handlers of their changes. A chart strategy adds its own position and the order methods on top of this (see Overview and lifecycle).

Trading data

TradingManager returns an ITradingManager and can be null, so check it before use. Its properties:

  • Security: the selected instrument, a Security;
  • Portfolio: the selected portfolio, a Portfolio;
  • Position: the current position, a Position;
  • MyTrades: the executed trades, MyTrade objects;
  • Orders: the placed orders, Order objects.

Events

Override these methods to receive the updates:

The handlers receive every order and trade of the chart instrument and portfolio, including the ones placed by hand, not only the ones of your strategy.

The two portfolio methods are not interchangeable: OnPortfolioChanged fires many times per second while a position is open, so reloading data there produces a request storm; OnPortfolioSelected fires once per account switch.

protected override void OnPortfolioSelected(Portfolio portfolio)
{
    // Raised once per account switch: the place for per-account work
    this.LogInfo($"Selected portfolio: {portfolio}");
}

protected override void OnPortfolioChanged(Portfolio portfolio)
{
    // Raised on every balance or PnL update: no logging here, only refresh the displayed values
    RedrawChart();
}

Example

The indicator below draws the portfolio, the position, the active orders and the trades on the chart, logs the new orders, order changes, trades, failures and the portfolio selection, and only redraws the chart in OnPortfolioChanged and OnPositionChanged:

using System.Drawing;
using System.Linq;
using System.Text;

using ATAS.DataFeedsCore;
using ATAS.Indicators;
using OFT.Rendering.Context;
using OFT.Rendering.Tools;
using Utils.Common.Logging;

namespace ATAS.Docs.Samples.ChartStrategies;

public class TradingInfoIndicator : Indicator
{
    private readonly RenderFont _font = new("Arial", 10);

    public TradingInfoIndicator()
    {
        EnableCustomDrawing = true;
        SubscribeToDrawingEvents(DrawingLayouts.Final);
    }

    protected override void OnCalculate(int bar, decimal value)
    {
    }

    protected override void OnNewOrder(Order order)
    {
        this.LogInfo($"New order: {order}");
    }

    protected override void OnOrderChanged(Order order)
    {
        this.LogInfo($"Order changed: {order}");
    }

    protected override void OnNewMyTrade(MyTrade myTrade)
    {
        this.LogInfo($"New trade: {myTrade}");
    }

    protected override void OnOrderRegisterFailed(Order order, string message)
    {
        this.LogWarn($"Order {order} was not registered: {message}");
    }

    protected override void OnOrderCancelFailed(Order order, string message)
    {
        this.LogWarn($"Order {order} was not cancelled: {message}");
    }

    protected override void OnOrderModifyFailed(Order order, Order newOrder, string error)
    {
        this.LogWarn($"Order {order} was not modified: {error}");
    }

    protected override void OnPortfolioSelected(Portfolio portfolio)
    {
        // Raised once per account switch: the place for per-account work
        this.LogInfo($"Selected portfolio: {portfolio}");
    }

    protected override void OnPortfolioChanged(Portfolio portfolio)
    {
        // Raised on every balance or PnL update: no logging here, only refresh the displayed values
        RedrawChart();
    }

    protected override void OnPositionChanged(Position position)
    {
        RedrawChart();
    }

    protected override void OnRender(RenderContext context, DrawingLayouts layout)
    {
        var tradingManager = TradingManager;

        if (tradingManager == null)
            return;

        var label = new StringBuilder();

        if (tradingManager.Security != null)
            label.AppendLine($"Security: {tradingManager.Security}");

        if (tradingManager.Portfolio != null)
            label.AppendLine($"Portfolio: {tradingManager.Portfolio}");

        if (tradingManager.Position != null)
            label.AppendLine($"Position: {tradingManager.Position}");

        var orders = tradingManager.Orders
            .Where(t => t.State == OrderStates.Active)
            .ToList();

        if (orders.Count > 0)
        {
            label.AppendLine();
            label.AppendLine("---------------------Active orders:------------------------");

            foreach (var order in orders)
                label.AppendLine(order.ToString());
        }

        var myTrades = tradingManager.MyTrades.ToList();

        if (myTrades.Count > 0)
        {
            label.AppendLine();
            label.AppendLine("---------------------MyTrades:------------------------");

            foreach (var myTrade in myTrades)
                label.AppendLine(myTrade.ToString());
        }

        if (label.Length == 0)
            return;

        var text = label.ToString();
        var size = context.MeasureString(text, _font);
        context.FillRectangle(Color.DarkRed, new Rectangle(25, 25, size.Width + 50, size.Height + 50));
        context.DrawString(text, _font, Color.Azure, 50, 50);
    }
}

The portfolio, position, orders and trades on the chart

Trading statistics

TradingStatisticsProvider (check it for null) gives the trading statistics of the platform as ITradingStatistics objects:

  • RawStatistics: all orders, own trades and closed trades of the platform, without any filter. Use it for the Orders, MyTrades, HistoryMyTrades and DailyNotes collections; Equity and Statistics are not available in it.
  • FilteredStatistics: the statistics filtered by the period, accounts, instruments and playbooks selected in the statistics window of the platform, with the calculated Equity and Statistics collections. The filter is in the From, To, Accounts, Securities and Playbooks properties of the provider; changing them from an indicator changes the filter of the statistics window too.

The collections of an ITradingStatistics: Orders (Order), MyTrades (MyTrade), HistoryMyTrades (the closed trades, HistoryMyTrade), DailyNotes (DailyNote), Equity (EquityValue) and Statistics (IStatisticsParameterGroup). Each one can be enumerated and raises Added, Changed, Removed and Cleared.

The provider replaces these objects when the statistics source changes (for example, when a Replay connection is started or stopped) and raises RawStatisticsSourceChanged and FilteredStatisticsSourceChanged with the new object: unsubscribe from the old one and subscribe to the new one, do not keep the old reference. After the statistics are rebuilt or cleared, StatisticsRebuilt is raised. The Realtime and Replay properties and LoadHistoryAsync of the provider are obsolete.

The indicator below logs the closed trades of the account and instrument of the chart and every new closed trade:

using System;
using System.Linq;

using ATAS.DataFeedsCore;
using ATAS.DataFeedsCore.Statistics;
using ATAS.Indicators;
using Utils.Common.Logging;

namespace ATAS.Docs.Samples.ChartStrategies;

public class ClosedTradesStatisticsIndicator : Indicator
{
    private ITradingStatistics? _statistics;

    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);
    }

    private void OnStatisticsSourceChanged(ITradingStatistics statistics)
    {
        SetStatistics(statistics);
    }

    private void SetStatistics(ITradingStatistics? statistics)
    {
        if (_statistics != null)
            _statistics.HistoryMyTrades.Added -= OnClosedTradeAdded;

        _statistics = statistics;

        if (_statistics == null)
            return;

        _statistics.HistoryMyTrades.Added += OnClosedTradeAdded;
        LogClosedTrades();
    }

    private void LogClosedTrades()
    {
        var trades = _statistics?.HistoryMyTrades
            .Where(IsChartTrade)
            .ToList();

        if (trades != null)
            this.LogInfo($"Closed trades: {trades.Count}, total PnL: {trades.Sum(t => t.PnL)}");
    }

    private void OnClosedTradeAdded(HistoryMyTrade trade)
    {
        if (IsChartTrade(trade))
            this.LogInfo($"New closed trade: {trade.OpenTime} - {trade.CloseTime}, PnL: {trade.PnL}");
    }

    // Only the trades of the account and instrument selected on the chart
    private bool IsChartTrade(HistoryMyTrade trade)
    {
        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);
    }
}