Warning! This section will be deprecated on February 1st 2025. Please move all your Indicators to the cTrader Store catalogue.
Description
Chart Overview control.
Feel free to make your suggestions to improve this indicator!
Demo:
Settings:
using System;
using System.Collections.Generic;
using System.Linq;
using cAlgo.API;
namespace cAlgo
{
[Indicator(IsOverlay = false, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class ChartOverview : Indicator
{
private const int YAxisWidth = 45;
private const int YAxisMargin = 10;
private Polygon _cloudControl;
private Rectangle _viewportControl;
private double _dragOffset;
private int _lastBarsTotal;
[Parameter()]
public DataSeries Input { get; set; }
[Parameter("Cloud opacity", DefaultValue = 0.2, MaxValue = 1, MinValue = 0.0, Group = "Cloud")]
public double CloudOpacity { get; set; }
[Parameter("Cloud color", DefaultValue = "#FFADD8E6", Group = "Cloud")]
public string CloudColor { get; set; }
[Parameter("Viewport opacity", DefaultValue = 0.2, MaxValue = 1, MinValue = 0.0, Group = "Viewport")]
public double ViewportOpacity { get; set; }
[Parameter("Viewport color", DefaultValue = "#FFFFFFE0", Group = "Viewport")]
public string ViewportColor { get; set; }
[Parameter("Density", DefaultValue = 1, MinValue = 0.1, MaxValue = 100)]
public double Density { get; set; }
protected override void Initialize()
{
InitializeControls();
UpdateCloud();
UpdateViewport();
IndicatorArea.ScrollChanged += OnIndicatorAreaScrollChanged;
IndicatorArea.SizeChanged += OnIndicatorAreaSizeChanged;
IndicatorArea.MouseDown += OnIndicatorAreaMouseDown;
IndicatorArea.DragStart += OnIndicatorAreaDragStart;
IndicatorArea.DragEnd += OnIndicatorAreaDragEnd;
IndicatorArea.Drag += OnIndicatorAreaDrag;
_lastBarsTotal = Chart.BarsTotal;
Print("Initialized");
}
public override void Calculate(int index)
{
if (!IsLastBar)
return;
if (_lastBarsTotal != Chart.BarsTotal)
{
UpdateCloud();
UpdateViewport();
_lastBarsTotal = Chart.BarsTotal;
}
}
private void InitializeControls()
{
var cloudColor = Color.FromHex(CloudColor);
var viewportColor = Color.FromHex(ViewportColor);
_cloudControl = new Polygon
{
StrokeThickness = 1,
StrokeColor = cloudColor,
FillColor = Color.FromArgb((int) (cloudColor.A * CloudOpacity), cloudColor),
IsHitTestVisible = false
};
_viewportControl = new Rectangle
{
StrokeThickness = 1,
StrokeColor = viewportColor,
FillColor = Color.FromArgb((int) (viewportColor.A * ViewportOpacity), viewportColor),
IsHitTestVisible = false
};
var canvas = new Canvas
{
BackgroundColor = Color.Transparent,
VerticalAlignment = VerticalAlignment.Stretch,
HorizontalAlignment = HorizontalAlignment.Stretch,
Margin = 0,
IsHitTestVisible = false
};
canvas.AddChild(_cloudControl);
canvas.AddChild(_viewportControl);
var border = new Border
{
BorderThickness = 0,
BackgroundColor = Chart.ColorSettings.BackgroundColor,
VerticalAlignment = VerticalAlignment.Stretch,
HorizontalAlignment = HorizontalAlignment.Stretch,
Child = canvas,
IsHitTestVisible = false,
Margin = string.Format("0 0 -{0} 0", YAxisWidth)
};
IndicatorArea.AddControl(border);
}
private void OnIndicatorAreaDragStart(ChartDragEventArgs args)
{
Chart.IsScrollingEnabled = false;
}
private void OnIndicatorAreaDragEnd(ChartDragEventArgs args)
{
Chart.IsScrollingEnabled = true;
}
private void OnIndicatorAreaDrag(ChartDragEventArgs args)
{
var selectedBarIndex = TransformXToBarIndex(0, IndicatorArea.Width + YAxisWidth, args.MouseX);
ScrollTo(selectedBarIndex);
}
private void OnIndicatorAreaMouseDown(ChartMouseEventArgs args)
{
var selectedBarIndex = TransformXToBarIndex(0, IndicatorArea.Width + YAxisWidth, args.MouseX);
if (selectedBarIndex >= Chart.FirstVisibleBarIndex && selectedBarIndex <= Chart.LastVisibleBarIndex)
_dragOffset = Chart.FirstVisibleBarIndex - selectedBarIndex;
else
_dragOffset = -Chart.MaxVisibleBars / 2.0;
ScrollTo(selectedBarIndex);
}
private void OnIndicatorAreaSizeChanged(ChartSizeEventArgs args)
{
UpdateCloud();
UpdateViewport();
}
private void OnIndicatorAreaScrollChanged(ChartScrollEventArgs args)
{
if (args.BarsDelta != 0)
UpdateViewport();
if (Chart.IsScrollingEnabled)
return;
var min = double.MaxValue;
var max = double.MinValue;
for (var i = Chart.FirstVisibleBarIndex; i <= Chart.LastVisibleBarIndex; i++)
{
if (MarketSeries.Low[i] < min)
min = MarketSeries.Low[i];
if (MarketSeries.High[i] > max)
max = MarketSeries.High[i];
}
Chart.SetYRange(min - Symbol.PipSize * 10, max + Symbol.PipSize * 10);
}
private void UpdateViewport()
{
_viewportControl.Left = TransformBarIndexToX(0, Chart.BarsTotal, Chart.FirstVisibleBarIndex) - 1;
_viewportControl.Width = TransformBarIndexToX(0, Chart.BarsTotal, Chart.LastVisibleBarIndex + 1) - TransformBarIndexToX(0, Chart.BarsTotal, Chart.FirstVisibleBarIndex) + 2;
_viewportControl.Top = -10;
_viewportControl.Height = IndicatorArea.Height + 20;
}
private void UpdateCloud()
{
_cloudControl.Points = GeneratePoints().ToArray();
}
private IEnumerable<Point> GeneratePoints()
{
var density = 1 / Density;
var series = Input;
var high = series.Maximum(series.Count) + Symbol.PipSize;
var low = series.Minimum(series.Count) - Symbol.PipSize;
yield return new Point(0, IndicatorArea.Height);
var lastGroupX = 0.0;
var lastGroupY = 0.0;
for (var i = 0; i < series.Count; i++)
{
var x = TransformBarIndexToX(0, series.Count - 1, i);
var y = TransformPriceToY(low, high, series[i]);
if (Math.Abs(lastGroupX - x) >= density)
{
yield return new Point(lastGroupX, lastGroupY);
lastGroupX = x;
}
lastGroupY = y;
}
yield return new Point(IndicatorArea.Width + YAxisWidth, IndicatorArea.Height);
}
private void ScrollTo(double barIndex)
{
var newBarIndex = (int)Math.Round(barIndex + _dragOffset, MidpointRounding.AwayFromZero);
if (newBarIndex < 0)
newBarIndex = 0;
if (newBarIndex >= Chart.BarsTotal)
newBarIndex = Chart.BarsTotal - 1;
Chart.ScrollXTo(newBarIndex);
}
private double TransformPriceToY(double min, double max, double val)
{
var height = IndicatorArea.Height - YAxisMargin * 2;
var y = height - (val - min) / (max - min) * height;
if (double.IsNaN(y))
y = IndicatorArea.Height;
if (double.IsInfinity(y))
y = 0;
return y + YAxisMargin;
}
private double TransformBarIndexToX(double min, double max, double val)
{
var x = (val - min) / (max - min) * (IndicatorArea.Width + YAxisWidth);
if (double.IsNaN(x))
x = 0;
if (double.IsInfinity(x))
x = IndicatorArea.Width + YAxisWidth;
return x;
}
private double TransformXToBarIndex(double min, double max, double val)
{
var barIndex = (val - min) / (max - min) * (Chart.BarsTotal - 1);
if (double.IsNaN(barIndex))
barIndex = 0;
if (double.IsInfinity(barIndex))
barIndex = Chart.BarsTotal - 1;
return barIndex;
}
}
}
devman
Joined on 22.10.2019
- Distribution: Free
- Language: C#
- Trading platform: cTrader Automate
- File name: Chart Overview.algo
- Rating: 5
- Installs: 1646
- Modified: 13/10/2021 09:55
Note that publishing copyrighted material is strictly prohibited. If you believe there is copyrighted material in this section, please use the Copyright Infringement Notification form to submit a claim.
Comments
Log in to add a comment.
This indicator is amazing, really great quality of life improvement for cTrader!
If I had to come up with a a suggestion (really not sure if it is even possible) then the only thing I could think of is Dates along the X-Axis, not every date of course but maybe spaced out enough that a quick glance could give a guestimate of when that above peak took place.