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: 1567
- 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.