Fibonacci Retracement
//------------------------------------------------------------------------------
//
// Графический объект FibonacciRetracement. Copyright (c) 2023 Tiger Trade Capital AG. 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);
}
}
}
Last updated