The code of the Fibonacci Retracement graphic object
//------------------------------------------------------------------------------ // // Графический объект FibonacciRetracement. Copyright (c) 2020 Ilya Smirnov. All rights reserved. // //------------------------------------------------------------------------------ using System; using System.Collections.Generic; using System.ComponentModel; using System.Runtime.Serialization; using System.Windows; using System.Windows.Media; using TigerTrade.Chart.Base; using TigerTrade.Chart.Objects.Common; using TigerTrade.Chart.Objects.Enums; using TigerTrade.Dx; using TigerTrade.Dx.Enums; namespace TigerTrade.Chart.Objects.Custom { [DataContract(Name = "FibonacciRetracementObject", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Objects.Custom")] [ChartObject("X_FibonacciRetracement", "Fibonacci Retracement", 2, Type = typeof(FibonacciRetracementObject))] public sealed class FibonacciRetracementObject : ObjectBase { [Browsable(false)] private XBrush LineBrush { get; set; } [Browsable(false)] public XPen LinePen { get; private set; } private XColor _lineColor; [DataMember(Name = "LineColor")] [Category("Линия"), DisplayName("Цвет линии")] public XColor LineColor { get => _lineColor; set { if (value == _lineColor) { return; } _lineColor = value; LineBrush = new XBrush(_lineColor); LinePen = new XPen(LineBrush, LineWidth, LineStyle); OnPropertyChanged(); } } private int _lineWidth; [DataMember(Name = "LineWidth")] [Category("Линия"), DisplayName("Толщина линии")] public int LineWidth { get => _lineWidth; set { value = Math.Max(1, Math.Min(10, value)); if (value == _lineWidth) { return; } _lineWidth = value; LinePen = new XPen(LineBrush, _lineWidth, LineStyle); OnPropertyChanged(); } } private XDashStyle _lineStyle; [DataMember(Name = "LineStyle")] [Category("Линия"), DisplayName("Стиль линии")] public XDashStyle LineStyle { get => _lineStyle; set { if (value == _lineStyle) { return; } _lineStyle = value; LinePen = new XPen(LineBrush, LineWidth, _lineStyle); OnPropertyChanged(); } } private bool _openStart; [DataMember(Name = "OpenStart")] [Category("Линия"), DisplayName("Продлить влево")] public bool OpenStart { get => _openStart; set { if (value == _openStart) { return; } _openStart = value; OnPropertyChanged(); } } private bool _openEnd; [DataMember(Name = "OpenEnd")] [Category("Линия"), DisplayName("Продлить вправо")] public bool OpenEnd { get => _openEnd; set { if (value == _openEnd) { return; } _openEnd = value; OnPropertyChanged(); } } private ObjectTextAlignment _textAlignment; [DataMember(Name = "TextAlignment")] [Category("Текст"), DisplayName("Расположение")] public ObjectTextAlignment TextAlignment { get => _textAlignment; set { if (value == _textAlignment) { return; } _textAlignment = value; OnPropertyChanged(); } } private bool _customLevels; [DataMember(Name = "CustomLevels")] [Category("Свои уровни"), DisplayName("Включить")] public bool CustomLevels { get => _customLevels; set { if (value == _customLevels) { return; } _customLevels = value; OnPropertyChanged(); } } private List<ObjectLine> _lines; [DataMember(Name = "Levels")] [Category("Свои уровни"), DisplayName("Уровни")] public List<ObjectLine> Levels { get => _lines ?? (_lines = new List<ObjectLine>()); set { if (Equals(value, _lines)) { return; } _lines = value; OnPropertyChanged(); } } private Point[] _startPoints; private Point[] _endPoints; private double[] _split; protected override int PenWidth => LineWidth; public FibonacciRetracementObject() { LineColor = Colors.Black; LineWidth = 1; LineStyle = XDashStyle.Solid; OpenStart = false; OpenEnd = false; TextAlignment = ObjectTextAlignment.LeftBottom; CustomLevels = false; Levels = new List<ObjectLine> { new ObjectLine(0.0, Colors.Black), new ObjectLine(23.6, Colors.Black), new ObjectLine(38.2, Colors.Black), new ObjectLine(50.0, Colors.Black), new ObjectLine(61.8, Colors.Black), new ObjectLine(78.6, Colors.Black), new ObjectLine(100.0, Colors.Black) }; } protected override void Draw(DxVisualQueue visual, ref List<ObjectLabelInfo> labels) { CalcPoint(); if (_startPoints == null || _endPoints == null) { return; } var p1 = ToPoint(ControlPoints[0]); var p2 = ToPoint(ControlPoints[1]); if (InMove && p1 != new Point() && p2 != new Point()) { visual.DrawLine(LinePen, p1.X, p1.Y, p2.X, p2.Y); } for (var i = 0; i < _startPoints.Length; i++) { if (CustomLevels) { var level = Levels[i]; if (!level.ShowLine) { continue; } visual.DrawLine(level.LinePen, _startPoints[i], _endPoints[i]); if (TextAlignment != ObjectTextAlignment.Hide) { visual.DrawString(GetStr(i), Canvas.ChartFont, level.LineBrush, GetRect(i, level.LineWidth)); } } else { visual.DrawLine(LinePen, _startPoints[i], _endPoints[i]); if (TextAlignment != ObjectTextAlignment.Hide) { visual.DrawString(GetStr(i), Canvas.ChartFont, LineBrush, GetRect(i, LineWidth)); } } } } private string GetStr(int i) { var p = DataProvider.Symbol.FormatPrice((decimal)GetPrice(i), true); return $"{_split[i]:P2} ({p})"; } private Rect GetRect(int i, int lineWidth) { var textSize = Canvas.ChartFont.GetSize(GetStr(i)); var x = 0.0; var y = 0.0; var width = textSize.Width; switch (TextAlignment) { case ObjectTextAlignment.LeftTop: case ObjectTextAlignment.CenterTop: case ObjectTextAlignment.RightTop: y = _startPoints[i].Y - 4 - Math.Ceiling(lineWidth / 2.0) - textSize.Height; break; case ObjectTextAlignment.LeftMiddle: case ObjectTextAlignment.CenterMiddle: case ObjectTextAlignment.RightMiddle: y = _startPoints[i].Y - 4 - Math.Ceiling(lineWidth / 2.0); break; case ObjectTextAlignment.LeftBottom: case ObjectTextAlignment.CenterBottom: case ObjectTextAlignment.RightBottom: y = _startPoints[i].Y + 4 + Math.Ceiling(lineWidth / 2.0); break; } switch (TextAlignment) { case ObjectTextAlignment.LeftTop: case ObjectTextAlignment.LeftMiddle: case ObjectTextAlignment.LeftBottom: x = Math.Min(_startPoints[i].X, _endPoints[i].X) + 4; break; case ObjectTextAlignment.CenterTop: case ObjectTextAlignment.CenterMiddle: case ObjectTextAlignment.CenterBottom: x = (_startPoints[i].X + _endPoints[i].X - width) / 2.0; break; case ObjectTextAlignment.RightTop: case ObjectTextAlignment.RightMiddle: case ObjectTextAlignment.RightBottom: x = Math.Max(_startPoints[i].X, _endPoints[i].X) - width - 4; break; } return new Rect(x, y, width, textSize.Height); } private void CalcPoint() { if (CustomLevels) { _split = new double[Levels.Count]; for (var i = 0; i < Levels.Count; i++) { _split[i] = Levels[i].Value / 100.0; } } else { _split = new[] { 0.0, 0.236, 0.382, 0.5, 0.618, 0.786, 1.0 }; } var p1 = ToPoint(ControlPoints[0]); var p2 = ToPoint(ControlPoints[1]); _startPoints = new Point[_split.Length]; _endPoints = new Point[_split.Length]; var op = new ObjectPoint(ControlPoints[0].X, 0.0); for (var i = 0; i < _split.Length; i++) { op.Y = GetPrice(i); var p3 = ToPoint(op); _startPoints[i] = new Point(p1.X, p3.Y); _endPoints[i] = new Point(p2.X, p3.Y); } for (var i = 0; i < _startPoints.Length; i++) { if (_startPoints[i].X <= _endPoints[i].X) { if (OpenStart) { _startPoints[i].X = 0; } if (OpenEnd) { _endPoints[i].X = Canvas.Rect.Right; } } else { if (OpenStart) { _endPoints[i].X = 0; } if (OpenEnd) { _startPoints[i].X = Canvas.Rect.Right; } } } } private double GetPrice(int lineIndex) { return (ControlPoints[0].Y - ControlPoints[1].Y) * _split[lineIndex] + ControlPoints[1].Y; } protected override bool InObject(int x, int y) { if (_startPoints == null || _endPoints == null || _startPoints.Length != Levels.Count) { return false; } for (var i = 0; i < _startPoints.Length; i++) { if (_startPoints[i] == new Point() || (CustomLevels && !Levels[i].ShowLine)) { continue; } var result = InLineSegment(x, y, _startPoints[i], _endPoints[i], PenWidth + 2); if (result) { return true; } } return false; } private static bool InLineSegment(int x, int y, Point p1, Point p2, int width) { var n1 = Math.Max(p1.X, p2.X); var n2 = Math.Min(p1.X, p2.X); var n3 = Math.Max(p1.Y, p2.Y); var n4 = Math.Min(p1.Y, p2.Y); return (Math.Abs(Dist(x, y, p1, p2)) <= width) && (x <= n1 + width) && (x >= n2 - width) && (y <= n3 + width) && (y >= n4 - width); } private static double Dist(int x, int y, Point p1, Point p2) { var d1 = p1.X - p2.X; var d2 = p1.Y - p2.Y; return ((x - p1.X) * (p2.Y - p1.Y) - (p2.X - p1.X) * (y - p1.Y)) / Math.Sqrt(d1 * d1 + d2 * d2); } public override void ApplyTheme(IChartTheme theme) { base.ApplyTheme(theme); LineColor = theme.ChartObjectLineColor; foreach (var level in Levels) { level.LineColor = theme.ChartObjectLineColor; } } public override void CopyTemplate(ObjectBase objectBase, bool style) { if (objectBase is FibonacciRetracementObject obj) { LineColor = obj.LineColor; LineWidth = obj.LineWidth; LineStyle = obj.LineStyle; OpenStart = obj.OpenStart; OpenEnd = obj.OpenEnd; TextAlignment = obj.TextAlignment; CustomLevels = obj.CustomLevels; Levels = new List<ObjectLine>(); foreach (var level in obj.Levels) { Levels.Add(new ObjectLine(level)); } OnPropertyChanged(nameof(Levels)); } base.CopyTemplate(objectBase, style); } } }