Description
This indicator allows you to synchronize scrolling between your cTrader charts, to use it you just have to attach the indicator on your charts that you want to synchronize their scrolling and it will scroll your other charts if you scroll one of them.
Features
- Easy to use, you just have to attach it on a chart and it will work
- It can work with different time frame and symbol charts
- Three different modes that allows you to limit the indicator
This indicator is open source, you can contribute on Github:
using cAlgo.API;
using System.Collections.Concurrent;
using System;
using System.Collections.Generic;
using System.Threading;
namespace cAlgo
{
[Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class SynchronizedScrolling : Indicator
{
private static ConcurrentDictionary<string, IndicatorInstanceContainer<SynchronizedScrolling, DateTime?>> _indicatorInstances = new ConcurrentDictionary<string, IndicatorInstanceContainer<SynchronizedScrolling, DateTime?>>();
private static int _numberOfChartsToScroll;
private DateTime _lastScrollTime;
private string _chartKey;
[Parameter("Mode", DefaultValue = Mode.All)]
public Mode Mode { get; set; }
protected override void Initialize()
{
_chartKey = GetChartKey(this);
IndicatorInstanceContainer<SynchronizedScrolling, DateTime?> oldIndicatorContainer;
GetIndicatorInstanceContainer(_chartKey, out oldIndicatorContainer);
_indicatorInstances.AddOrUpdate(_chartKey, new IndicatorInstanceContainer<SynchronizedScrolling, DateTime?>(this), (key, value) => new IndicatorInstanceContainer<SynchronizedScrolling, DateTime?>(this));
if (oldIndicatorContainer != null && oldIndicatorContainer.Data.HasValue)
{
ScrollXTo(oldIndicatorContainer.Data.Value);
}
Chart.ScrollChanged += Chart_ScrollChanged;
}
public override void Calculate(int index)
{
}
public void ScrollXTo(DateTime time)
{
IndicatorInstanceContainer<SynchronizedScrolling, DateTime?> indicatorContainer;
if (GetIndicatorInstanceContainer(_chartKey, out indicatorContainer))
{
indicatorContainer.Data = time;
}
if (Bars[0].OpenTime > time)
{
LoadMoreHistory();
}
else
{
Chart.ScrollXTo(time);
}
}
private void LoadMoreHistory()
{
var numberOfLoadedBars = Bars.LoadMoreHistory();
if (numberOfLoadedBars == 0)
{
Chart.DrawStaticText("ScrollError", "Synchronized Scrolling: Can't load more data to keep in sync with other charts as more historical data is not available for this chart", VerticalAlignment.Bottom, HorizontalAlignment.Left, Color.Red);
}
}
private void Chart_ScrollChanged(ChartScrollEventArgs obj)
{
IndicatorInstanceContainer<SynchronizedScrolling, DateTime?> indicatorContainer;
if (GetIndicatorInstanceContainer(_chartKey, out indicatorContainer))
{
indicatorContainer.Data = null;
}
if (_numberOfChartsToScroll > 0)
{
Interlocked.Decrement(ref _numberOfChartsToScroll);
return;
}
var firstBarTime = obj.Chart.Bars.OpenTimes[obj.Chart.FirstVisibleBarIndex];
if (_lastScrollTime == firstBarTime)
return;
_lastScrollTime = firstBarTime;
switch (Mode)
{
case Mode.Symbol:
ScrollCharts(firstBarTime, indicator => indicator.SymbolName.Equals(SymbolName, StringComparison.Ordinal));
break;
case Mode.TimeFrame:
ScrollCharts(firstBarTime, indicator => indicator.TimeFrame == TimeFrame);
break;
default:
ScrollCharts(firstBarTime);
break;
}
}
private void ScrollCharts(DateTime firstBarTime, Func<Indicator, bool> predicate = null)
{
var toScroll = new List<SynchronizedScrolling>(_indicatorInstances.Values.Count);
foreach (var indicatorContianer in _indicatorInstances)
{
SynchronizedScrolling indicator;
if (indicatorContianer.Value.GetIndicator(out indicator) == false || indicator == this || (predicate != null && predicate(indicator) == false))
continue;
toScroll.Add(indicator);
}
Interlocked.CompareExchange(ref _numberOfChartsToScroll, toScroll.Count, _numberOfChartsToScroll);
foreach (var indicator in toScroll)
{
try
{
indicator.BeginInvokeOnMainThread(() => indicator.ScrollXTo(firstBarTime));
} catch (Exception)
{
Interlocked.Decrement(ref _numberOfChartsToScroll);
}
}
}
private string GetChartKey(SynchronizedScrolling indicator)
{
return string.Format("{0}_{1}_{2}", indicator.SymbolName, indicator.TimeFrame, indicator.Chart.ChartType);
}
private bool GetIndicatorInstanceContainer(string chartKey, out IndicatorInstanceContainer<SynchronizedScrolling, DateTime?> indicatorContainer)
{
if (_indicatorInstances.TryGetValue(chartKey, out indicatorContainer))
{
return true;
}
indicatorContainer = null;
return false;
}
}
public enum Mode
{
All,
TimeFrame,
Symbol
}
public class IndicatorInstanceContainer<T, TData> where T : Indicator
{
private readonly WeakReference _indicatorWeakReference;
public IndicatorInstanceContainer(T indicator)
{
_indicatorWeakReference = new WeakReference(indicator);
}
public TData Data { get; set; }
public bool GetIndicator(out T indicator)
{
if (_indicatorWeakReference.IsAlive)
{
indicator = (T)_indicatorWeakReference.Target;
return true;
}
indicator = null;
return false;
}
}
}
Spotware
Joined on 23.09.2013
- Distribution: Free
- Language: C#
- Trading platform: cTrader Automate
- File name: Synchronized Scrolling.algo
- Rating: 5
- Installs: 1914
- Modified: 30/03/2022 11:41
Comments
it doesnn't work for the renko bar chart
This is a great tool, however, I encounter a little problem in usage:
When I set 2 charts with the same Symbol and same TimeFrame, if I scroll in the first chart and the indicator works well in the second chart, then while I swap to scroll the 2nd chart, the first chart won't get synchronized scrolled.
In other words, synchronizing scrolling can only get triggered by one chart, not all charts' scrolling could trigger synchronizing. Please help and advise. Thanks.
great tool! I wish many of these features were a part of ctrader from installation.
A really great tool that is saving me loads of time.
Many Thanks
I've changed the code so that the indicators scroll to the chart's right side. Helpful for market replay.