The custom time frame API provides types that enable you to display custom OHLC data from any source, including third-party APIs, synthetic calculations or local files, directly in cTrader charts. API entities, such as CustomTimeFrame and TimeFrameManager, allow you to stream bars from a specific data source and display them in cTrader as a fully fledged period.
Important types include:
TimeFrameManager.Custom.Add(name) to register a new custom period with a user-defined name.
CustomTimeFrame.BarsNeeded to define how and when the data is loaded for the chart.
CustomBars.AppendBars(), CustomBars.UpdateLastBar(), CustomBars.PrependBars() to dynamically manage the series, where each CustomBar holds these six fields: time, open, high, low, close and volume.
Once added, custom periods integrate seamlessly into cTrader Period UI and appear in the dropdown alongside built-in periods. Users may then easily switch between standard and custom periods.
Tip
Use custom periods to visualise external markets, simulate experimental bar types, create advanced correlation/arbitrage charts or backtest strategies on bespoke data.
Custom time frame API objects can be used to do the following:
Feature or operation
Examples
Displaying alternative market data
Load bars from REST APIs (e.g. AlphaVantage) Visualise off-platform prices
Simulating synthetic bar types
Create Heikin-Ashi, Renko or volume bars Generate bars from sentiment or economic indices
Offline data testing
Load custom bars from a CSV or JSON file Backtest on proprietary datasets
Cross-asset strategy overlays
Plot GBP/USD on EUR/USD charts Develop arbitrage models
Enhanced UI control
Toggle, favourite or modify custom periods
Create custom period based on existing data
The plugin here illustrates how to create and display a synthetic intraday period in cTrader.
The plugin builds a 25-minute timeframe by fetching native one-minute bars for the active symbol, aggregating them into 25-minute OHLC candles and then streaming the result into a CustomBar series.
The new timeframe appears in the drop-down menu for selecting timeframes as 25Min.
When users scroll back on the chart, the plugin automatically loads more minute data, prepends older 25-minute bars and keeps the view up to date.
usingSystem;usingSystem.Linq;usingcAlgo.API;usingcAlgo.API.Internals;namespacecAlgo.Plugins{[Plugin(AccessRights = AccessRights.None)]publicclassTF25:Plugin{privateCustomTimeFrame_tf25Min;privatebool_isLoadMoreEnabled=true;protectedoverridevoidOnStart(){// Create the 25-minute TF_tf25Min=TimeFrameManager.Custom.Add("25Min");_tf25Min.Description="25‑minute aggregated from 1‑minute bars";// Hook initial load and on‑scroll‑back_tf25Min.NewChart+=OnNewChart;_tf25Min.BarsNeeded=OnBarsNeeded;}privatevoidOnNewChart(CustomTimeFrameNewChartEventArgsargs){varcb=args.CustomBars;varchart=args.Chart;// Show a loading indicatorvarloading=newTextBlock{Text="Loading 25‑min bars…",FontSize=12};chart.AddControl(loading);// Fetch the 1‑min bars and aggregateMarketData.GetBarsAsync(TimeFrame.Minute,cb.Symbol.Name,minuteBars=>{varaggregated=minuteBars.Select((bar,idx)=>new{bar,idx}).GroupBy(x=>x.idx/25).Select(g=>{varfirst=g.First().bar;varlast=g.Last().bar;returnnewCustomBar(time:first.OpenTime,open:first.Open,high:g.Max(x=>x.bar.High),low:g.Min(x=>x.bar.Low),close:last.Close,volume:g.Sum(x=>(int)x.bar.TickVolume));});chart.RemoveControl(loading);});}privatevoidOnBarsNeeded(CustomTimeFrameBarsNeededArgsargs){varcb=args.CustomBars;// Same logic for explicit BarsNeeded triggersMarketData.GetBarsAsync(TimeFrame.Minute,cb.Symbol.Name,minuteBars=>{varaggregated=minuteBars.Select((bar,idx)=>new{bar,idx}).GroupBy(x=>x.idx/25).Select(g=>newCustomBar(time:g.First().bar.OpenTime,open:g.First().bar.Open,high:g.Max(x=>x.bar.High),low:g.Min(x=>x.bar.Low),close:g.Last().bar.Close,volume:g.Sum(x=>(int)x.bar.TickVolume)));cb.AppendBars(aggregated);args.CustomBars.NeedsMore+=_=>{if(!_isLoadMoreEnabled){Print("Load‑more disabled; skipping additional bars.");return;}Print("NeedsMore fired; loading more history…");minuteBars.LoadMoreHistoryAsync(loadArgs=>{Print($"Loaded {loadArgs.Count} more 1‑min bars");if(loadArgs.Count==0)return;Sleep(TimeSpan.FromSeconds(1));args.CustomBars.PrependBars(minuteBars.Take(loadArgs.Count).Select(bar=>newCustomBar(bar.OpenTime,bar.Open,bar.High,bar.Low,bar.Close,(int)bar.TickVolume)));});};});}protectedoverridevoidOnStop(){}}}
Create custom period using external data
The plugin here demonstrates how to fetch and display external financial data in cTrader charts using custom time frame API objects.
The plugin creates a custom period in cTrader by retrieving historical daily OHLC data for IBM from the AlphaVantage API.
It parses the CSV response and loads the data into a CustomBar series, which is then displayed as a selectable custom period called AlphaVantage in the chart UI.
usingSystem;usingSystem.Collections.Generic;usingcAlgo.API;usingcAlgo.API.Collections;usingcAlgo.API.Indicators;usingcAlgo.API.Internals;namespacecAlgo.Plugins{[Plugin(AccessRights = AccessRights.None)]publicclassCustomTimeFrameAlphaVantage:Plugin{privateconststringApiKey="YOUR_ALPHA_VANTAGE_KEY";privateconststringSymbol="IBM";privateconststringEndpoint=$"https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol={Symbol}&apikey={ApiKey}&datatype=csv&outputsize=full";privateCustomTimeFrame_customTimeFrame;protectedoverridevoidOnStart(){_customTimeFrame=TimeFrameManager.Custom.Add("AlphaVantage");_customTimeFrame.Description="Loads data from AlphaVantage";_customTimeFrame.BarsNeeded=args=>{varresponse=Http.Get(newUri(Endpoint));if(!response.IsSuccessful){Print($"Error loading data from AlphaVantage, status code: {response.StatusCode}, body: {response.Body}");return;}Print($"Response Received: {response.StatusCode}");varohlcData=response.Body.Split(Environment.NewLine)[1..];Array.Reverse(ohlcData);Print($"ohlcData Count: {ohlcData.Length}");varbars=newList<CustomBar>();foreach(varohlcinohlcData){varohlcSplit=ohlc.Split(',');if(ohlcSplit.Length<6)continue;vartime=DateTime.Parse(ohlcSplit[0]);varopen=double.Parse(ohlcSplit[1]);varhigh=double.Parse(ohlcSplit[2]);varlow=double.Parse(ohlcSplit[3]);varclose=double.Parse(ohlcSplit[4]);varvolume=long.Parse(ohlcSplit[5]);bars.Add(newCustomBar(time,open,high,low,close,volume));}Print($"AppendBars Count: {bars.Count}");args.CustomBars.AppendBars(bars);};}protectedoverridevoidOnStop(){// Handle Plugin stop here}}}