New Features in cTrader Automate API 3.7

New Features in cTrader Automate API 3.7

Historical Data API

cBots and indicators now have a new API to access historical price data.
Previously, historical data was accessible using MarketSeries interface which contains DataSeries for Open, High, Low, Close prices and more. Where each DataSeries is a collection of double numbers that can be accessible using bar index or you can be iterated using loops.

Old API DataSeries


New API keeps the same DataSeries with a different names


Bar Type

Bars object adds more functionality. Now you can choose if you want to operate with a collection of prices or with a collection of bar objects. Bars itself is a collection of Bar objects.
You can get Bar by index or using LastBar property or Last() method to get bar from the end of the collection.
Example: print to log close prices for first and last bar on the chart.

var firstBar = Bars[0];
var lastBar = Bars.LastBar;
Print("First bar close {0}, last bar close {1}", firstBar.Close, lastBar.Close);

First bar close 1.23043, last bar close 1.10541

Or you can iterate through all of the bars.
Example: draw an arrow on each doji bar, where open equals to close.

foreach (var bar in Bars)
    if (bar.Open == bar.Close)
        Chart.DrawIcon(bar.OpenTime.ToString(), ChartIconType.DownArrow, bar.OpenTime, bar.High, Color.Blue);

Bar Events

You can subscribe to events for Bars:

Bars.Tick // Raised when a new tick arrives
Bars.BarOpened // Raised when last bar is closed and new bar is opened
Bars.HistoryLoaded // Raised when more history is loaded due to chart scroll on the left or due to API call
Bars.Reloaded // Raised when bars are refreshed due to reconnect


Automate API adds Ticks to work with tick data.
Ticks is a collection of Tick objects with following properties


Note, in the previous versions tick data was accessible only if cBot or indicator was running on Tick1 chart and only for bid prices using old MarketSeries. With the new API you can access tick data from any timeframe and use bid and ask prices

Load more history

For both Bars and Ticks you can load more history using LoadMoreHistory or LoadMoreHistoryAsync methods.
Below Is an example of loading 10 000 bars on the chart

Print("{0} bar on the chart. Loading 10 000 bars", Bars.Count);
while (Bars.Count < 10000)
    var loadedCount = Bars.LoadMoreHistory();
    Print("Loaded {0} bars", loadedCount);
    if (loadedCount == 0)
Print("Finished, total bars {0}", Bars.Count);

1143 bar on the chart. Loading 10 000 bars
Loaded 1141 bars
Loaded 1162 bars
Loaded 1129 bars
Loaded 1090 bars
Loaded 1126 bars
Loaded 1134 bars
Loaded 1134 bars
Loaded 1166 bars
Finished, total bars 10225

Bars and Ticks for other symbols and timeframes

cBots and indicators can request bars and ticks for other symbols and timeframes. MarketData contains synchronous and asynchronous methods to load this data


Multi-Symbol Backtesting

Backtesting now allows to use and trade on other symbols than the symbol the cBot is running on. You can get desired Symbol using Symbol.GetSymbol or Symbols.GetSymbols methods.
To get events about price updates for received symbols use Symbol.Tick event.

Also, you can get historical data for other symbols during backtesting using MarketData.GetBars and MarketData.GetTicks methods.

The example below shows how to display changing prices on the chart for 4 symbols during visual backtesting.

public Symbol[] MySymbols;

protected override void OnStart()
    MySymbols = Symbols.GetSymbols("EURUSD", "GBPUSD", "USDJPY", "USDCHF");
    foreach (var symbol in MySymbols)
        symbol.Tick += Symbol_Tick;

private void Symbol_Tick(SymbolTickEventArgs obj)
    var sb = new StringBuilder();
    foreach (var symbol in MySymbols)
        var textLine = string.Format("{0} {1} {2}", symbol.Name, symbol.Bid, symbol.Ask);
    Chart.DrawStaticText("symbols", sb.ToString(), VerticalAlignment.Top, HorizontalAlignment.Left, Chart.ColorSettings.ForegroundColor);

Clouds between indicator lines

Indicators now can add a semi-transparent cloud between two lines. Cloud can be easily added by adding Cloud attribute to your indicator.
It takes two line names as required parameters.
Cloud changes its color based on the color of the top line. If one line crosses another, cloud color changes. Line color is multiplied by the default opacity which is 0.2. You can change default behavior by setting following parameters in the Cloud attribute:

Below is an example of an indicator with fast Moving Average and slow Moving Average and a cloud between them

[Cloud("Fast MA", "Slow MA")]
[Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class MaCrossCloud : Indicator
    [Parameter("Fast MA Period", DefaultValue = 21)]
    public int FastMaPeriod { get; set; }

    [Parameter("Slow MA Period", DefaultValue = 50)]
    public int SlowMaPeriod { get; set; }

    [Output("Fast MA", LineColor = "#FF6666")]
    public IndicatorDataSeries FastMaResult { get; set; }

    [Output("Slow MA", LineColor = "#0071C1")]
    public IndicatorDataSeries SlowMaResult { get; set; }

    SimpleMovingAverage FastMa;
    SimpleMovingAverage SlowMa;

    protected override void Initialize()
        FastMa = Indicators.SimpleMovingAverage(Bars.ClosePrices, FastMaPeriod);
        SlowMa = Indicators.SimpleMovingAverage(Bars.ClosePrices, SlowMaPeriod);

    public override void Calculate(int index)
        FastMaResult[index] = FastMa.Result[index];
        SlowMaResult[index] = SlowMa.Result[index];

Set custom bar color

cBots or indicators can set a specific color for an individual bar on the chart. You can set fill color and outline color separately using Chart.SetBarFillColor and Chart.SetBarOutlineColor methods or change both colors together using Chart.SetBarColor.
Also, color for tick volumes can be changed using Chart.SetTickVolumeColor.

Bars will be changed till cBot or indicator is running on the chart. On stop, bar colors will be changed back to the user settings. If you want to reset bar color to users settings during the work of cBot/indicator, you can use Chart.ResetBarColor to reset individual bar or Chart.ResetBarColors to reset all bars. Same for tick volumes with Chart.ResetTickVolumeColor and Chart.ResetTickVolumeColors.

The example below shows how to set bar color to green if its close price is above Simple Moving Average and to red if it is below.

var sma = Indicators.SimpleMovingAverage(Bars.ClosePrices, 14);
for (var i = 0; i < Bars.Count; i++)
    var closePrice = Bars.ClosePrices[i];
    var smaValue = sma.Result[i];
    var barColor = closePrice > smaValue ? Color.Green : Color.Red;
    Chart.SetBarColor(i, barColor);

Changes in charts zoom API

As cTrader 3.7 introduces new smooth chart zooming, zoom values in API was changed.
Old Chart.Zoom property is deprecated. It used to have range of possible values from 0 to 5, which reflected old zoom levels on the user interface.
New Chart.ZoomLevel was added with values from 5 to 500 with a step of 5. These steps repeat new values on the user interface.

Obsoleted API

1. MarketSeries is obsolete. All methods that take a parameter of this type now have a new overload with Bars parameter instead of MarketSeries.
Affected methods to get indicator with a data source from another timeframe or symbol:

2. MarketData.GetSeries() is obsolete. It was replaced with MarketData.GetBars().

Multi Symbol Sample Trend cBot

The Sample Trend cBot trading 3 pairs.

I would have liked to have not used the switch and not declared all the indicators separately so it can scale to more pairs,  but it works trading multiple pairs on a back test

using System;
using System.Linq;
using System.Text;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
using cAlgo.Indicators;

namespace cAlgo.Robots
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class MultSymbol : Robot
        public Symbol[] MySymbols;

        [Parameter("Quantity (Lots)", Group = "Volume", DefaultValue = 1, MinValue = 0.01, Step = 0.01)]
        public double Quantity { get; set; }

        [Parameter("MA Type", Group = "Moving Average")]
        public MovingAverageType MAType { get; set; }

        [Parameter("Source", Group = "Moving Average")]
        public DataSeries SourceSeries { get; set; }

        [Parameter("Slow Periods", Group = "Moving Average", DefaultValue = 10)]
        public int SlowPeriods { get; set; }

        [Parameter("Fast Periods", Group = "Moving Average", DefaultValue = 5)]
        public int FastPeriods { get; set; }

        private MovingAverage slowMaEURUSD;
        private MovingAverage fastMaEURUSD;
        private MovingAverage slowMaUSDJPY;
        private MovingAverage fastMaUSDJPY;
        private MovingAverage slowMaGBPUSD;
        private MovingAverage fastMaGBPUSD;

        private const string label = "MultSymbolcBot";

        protected override void OnStart()
            MySymbols = Symbols.GetSymbols("EURUSD", "USDJPY", "GBPUSD");
            foreach (var symbol in MySymbols)
                symbol.Tick += Symbol_Tick;

            Bars seriesEURUSD = MarketData.GetBars(TimeFrame, "EURUSD");
            Bars seriesUSDJPY = MarketData.GetBars(TimeFrame, "USDJPY");
            Bars seriesGBPUSD = MarketData.GetBars(TimeFrame, "GBPUSD");

            fastMaEURUSD = Indicators.MovingAverage(seriesEURUSD.ClosePrices, FastPeriods, MAType);
            slowMaEURUSD = Indicators.MovingAverage(seriesEURUSD.ClosePrices, SlowPeriods, MAType);

            fastMaUSDJPY = Indicators.MovingAverage(seriesUSDJPY.ClosePrices, FastPeriods, MAType);
            slowMaUSDJPY = Indicators.MovingAverage(seriesUSDJPY.ClosePrices, SlowPeriods, MAType);

            fastMaGBPUSD = Indicators.MovingAverage(seriesGBPUSD.ClosePrices, FastPeriods, MAType);
            slowMaGBPUSD = Indicators.MovingAverage(seriesGBPUSD.ClosePrices, SlowPeriods, MAType);

        protected override void OnTick()


        protected override void OnStop()


        private void Symbol_Tick(SymbolTickEventArgs obj)
            foreach (var symbol in MySymbols)
                switch (symbol.Name)
                    case "EURUSD":
                        SymbolTrade(symbol, fastMaEURUSD, slowMaEURUSD);
                    case "USDJPY":
                        SymbolTrade(symbol, fastMaUSDJPY, slowMaUSDJPY);
                    case "GBPUSD":
                        SymbolTrade(symbol, fastMaGBPUSD, slowMaGBPUSD);

        private double VolumeInUnits
            get { return Symbol.QuantityToVolumeInUnits(Quantity); }

        private void SymbolTrade(Symbol SymbolName, MovingAverage MaNameFast, MovingAverage MaNameSlow)
            var longPosition = Positions.Find(label, SymbolName.Name, TradeType.Buy);
            var shortPosition = Positions.Find(label, SymbolName.Name, TradeType.Sell);

            var currentSlowMa = MaNameSlow.Result.Last(0);
            var currentFastMa = MaNameFast.Result.Last(0);
            var previousSlowMa = MaNameSlow.Result.Last(1);
            var previousFastMa = MaNameFast.Result.Last(1);

            if (previousSlowMa > previousFastMa && currentSlowMa <= currentFastMa && longPosition == null)
                if (shortPosition != null)
                ExecuteMarketOrder(TradeType.Buy, SymbolName.Name, VolumeInUnits, label);
            else if (previousSlowMa < previousFastMa && currentSlowMa >= currentFastMa && shortPosition == null)
                if (longPosition != null)
                ExecuteMarketOrder(TradeType.Sell, SymbolName.Name, VolumeInUnits, label);

Hello Panagiotis,

I'm trying out the following code,

using System.Collections.Generic;
using cAlgo.API;

namespace cAlgo
    [Indicator(IsOverlay = false, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class NewIndicator : Indicator
        private readonly List<Bars> _bars = new List<Bars>();
        private readonly string[] _mySymbols = {"EURUSD","GBPUSD","EURJPY","USDJPY","AUDUSD","USDCHF","GBPJPY","USDCAD","EURGBP","EURCHF"};

        protected override void Initialize()

            foreach (var symbol in _mySymbols)
                Print(string.Format("Loading Symbol {0}", symbol));
                MarketData.GetBarsAsync(TimeFrame.Hour, symbol, bar =>
                    Print(string.Format("{0} Loaded, Bars: {1}", bar.SymbolName, bar.Count));

            Print("End Initializing");

        public override void Calculate(int index) { }

I get the responses in the log in this order, doesn't seem to work properly to me, is this correct, or I'm I doing something wrong, is this the best way to do it? It takes about 16 seconds to load them all (don't get me wrong it's a big improvement), could it load faster?

05/12/2019 14:20:04.782 | Initializing
05/12/2019 14:20:04.782 | Loading Symbol EURUSD
05/12/2019 14:20:04.797 | Loading Symbol GBPUSD
05/12/2019 14:20:04.797 | Loading Symbol EURJPY
05/12/2019 14:20:05.219 | Loading Symbol USDJPY
05/12/2019 14:20:05.235 | Loading Symbol AUDUSD
05/12/2019 14:20:05.657 | Loading Symbol USDCHF
05/12/2019 14:20:06.641 | Loading Symbol GBPJPY
05/12/2019 14:20:07.078 | Loading Symbol USDCAD
05/12/2019 14:20:07.110 | Loading Symbol EURGBP
05/12/2019 14:20:07.485 | Loading Symbol EURCHF
05/12/2019 14:20:07.953 | Loading Symbol AUDJPY
05/12/2019 14:20:08.344 | Loading Symbol NZDUSD
05/12/2019 14:20:08.391 | Loading Symbol CHFJPY
05/12/2019 14:20:08.813 | Loading Symbol EURAUD
05/12/2019 14:20:09.344 | Loading Symbol CADJPY
05/12/2019 14:20:09.782 | Loading Symbol GBPAUD
05/12/2019 14:20:10.250 | Loading Symbol EURCAD
05/12/2019 14:20:10.657 | Loading Symbol AUDCAD
05/12/2019 14:20:11.203 | Loading Symbol GBPCAD
05/12/2019 14:20:11.813 | Loading Symbol USDNOK
05/12/2019 14:20:13.063 | Loading Symbol AUDCHF
05/12/2019 14:20:13.672 | Loading Symbol USDMXN
05/12/2019 14:20:15.094 | Loading Symbol GBPNZD
05/12/2019 14:20:15.110 | Loading Symbol CADCHF
05/12/2019 14:20:15.719 | Loading Symbol USDSEK
05/12/2019 14:20:16.969 | Loading Symbol GBPCHF
05/12/2019 14:20:17.578 | Loading Symbol EURRUB
05/12/2019 14:20:19.453 | Loading Symbol NZDCHF
05/12/2019 14:20:20.078 | Loading Symbol NZDCAD
05/12/2019 14:20:20.703 | Loading Symbol AUDNZD
05/12/2019 14:20:20.735 | End Initializing
05/12/2019 14:20:20.735 | EURUSD Loaded, Bars: 2323
05/12/2019 14:20:20.735 | GBPUSD Loaded, Bars: 2013
05/12/2019 14:20:20.735 | EURJPY Loaded, Bars: 2013
05/12/2019 14:20:20.735 | USDJPY Loaded, Bars: 1650
05/12/2019 14:20:20.750 | AUDUSD Loaded, Bars: 2013
05/12/2019 14:20:20.750 | USDCHF Loaded, Bars: 2013
05/12/2019 14:20:20.750 | GBPJPY Loaded, Bars: 2013
05/12/2019 14:20:20.750 | USDCAD Loaded, Bars: 2013
05/12/2019 14:20:20.750 | EURGBP Loaded, Bars: 2013
05/12/2019 14:20:20.750 | EURCHF Loaded, Bars: 2013
05/12/2019 14:20:20.750 | AUDJPY Loaded, Bars: 2013
05/12/2019 14:20:20.750 | NZDUSD Loaded, Bars: 2013
05/12/2019 14:20:20.750 | CHFJPY Loaded, Bars: 2013
05/12/2019 14:20:20.750 | EURAUD Loaded, Bars: 2013
05/12/2019 14:20:20.750 | CADJPY Loaded, Bars: 2013
05/12/2019 14:20:20.750 | GBPAUD Loaded, Bars: 2013
05/12/2019 14:20:20.750 | EURCAD Loaded, Bars: 2013
05/12/2019 14:20:20.750 | AUDCAD Loaded, Bars: 2013
05/12/2019 14:20:20.750 | GBPCAD Loaded, Bars: 2013
05/12/2019 14:20:20.750 | USDNOK Loaded, Bars: 1170
05/12/2019 14:20:20.750 | AUDCHF Loaded, Bars: 2013
05/12/2019 14:20:20.750 | USDMXN Loaded, Bars: 1170
05/12/2019 14:20:20.750 | GBPNZD Loaded, Bars: 2013
05/12/2019 14:20:20.750 | CADCHF Loaded, Bars: 2013
05/12/2019 14:20:20.750 | USDSEK Loaded, Bars: 1170
05/12/2019 14:20:20.750 | GBPCHF Loaded, Bars: 2013
05/12/2019 14:20:20.750 | EURRUB Loaded, Bars: 1167
05/12/2019 14:20:20.750 | NZDCHF Loaded, Bars: 2013
05/12/2019 14:20:20.985 | NZDCAD Loaded, Bars: 2012
05/12/2019 14:20:21.203 | AUDNZD Loaded, Bars: 2013

PanagiotisCharalampous said:

Waxy said:

Hello Spotware,

These are indeed great additions,

I see now that when I load an indicator that uses multiple MarketSeries (or now Bars) it takes much less than before, and this is great news, but at the start, it takes the same as before, so, I'd like to know if it's possible to improve or what's the best way now to load multiple series loading for indicators/bots during initialization, let's say I want to load 10 symbol bars, can I load these in parallel now?

Hi Xavier,

You can use MarketData.GetBarsAsync() and MarketData.GetTicksAsync() to get data asynchronously.

Best Regards,


Join us on Telegram

¨You are not what you have done, you are what you have overcome.¨ - Anonymous