Information

Username: viatrufka
Member since: 05 Feb 2024
Last login: 09 Nov 2024
Status: Active

Activity

Where Created Comments
Algorithms 0 1
Forum Topics 2 0
Jobs 0 0

Last Algorithm Comments

VI
viatrufka · 3 months ago

I've changed the code so that the indicators scroll to the chart's right side. Helpful for market replay.

using cAlgo.API;
using System.Collections.Concurrent;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Text.RegularExpressions;

namespace cAlgo
{
	[Indicator(IsOverlay = true, TimeZone = TimeZones.RussianStandardTime, 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 - Chart.MaxVisibleBars * GetCurrentTimeSpan());
			}
		}
		private TimeSpan GetCurrentTimeSpan()
		{
			var TimeFrameName = Chart.TimeFrame.ToString();

			if (!int.TryParse(Regex.Replace(TimeFrameName, "[^0-9]", ""), out int prefix)) prefix = 1;

			if (TimeFrameName.Contains("Minute"))
				return TimeSpan.FromMinutes(prefix);
			else if (TimeFrameName.Contains("Hour"))
				return TimeSpan.FromHours(prefix);
			else if (TimeFrameName.Contains("Day") || TimeFrameName.Contains("Daily"))
				return TimeSpan.FromDays(prefix);
			else if (TimeFrameName == "Weekly")
				return TimeSpan.FromDays(7);
			else if (TimeFrameName == "Monthly")
				return TimeSpan.FromDays(31);
			else
				return TimeSpan.Zero;
		}
		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 lastBarTime = obj.Chart.Bars.OpenTimes[obj.Chart.LastVisibleBarIndex];

			if (_lastScrollTime == lastBarTime)
				return;

			_lastScrollTime = lastBarTime;

			switch (Mode)
			{
				case Mode.Symbol:
					ScrollCharts(lastBarTime, indicator => indicator.SymbolName.Equals(SymbolName, StringComparison.Ordinal));
					break;

				case Mode.TimeFrame:
					ScrollCharts(lastBarTime, indicator => indicator.TimeFrame == TimeFrame);
					break;
				default:


					ScrollCharts(lastBarTime);
					break;
			}
		}

		private void ScrollCharts(DateTime lastBarTime, 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(lastBarTime));
				}
				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;
		}
	}
}