The code of the Open Interest indicator
//------------------------------------------------------------------------------ // // OpenInterest. Copyright (c) 2019 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.Base.Enums; using TigerTrade.Chart.Indicators.Common; using TigerTrade.Chart.Indicators.Enums; using TigerTrade.Core.UI.Converters; using TigerTrade.Core.Utils.Time; using TigerTrade.Dx; namespace TigerTrade.Chart.Indicators.Custom { [TypeConverter(typeof(EnumDescriptionTypeConverter))] [DataContract(Name = "OpenInterestDataType", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators.Custom")] public enum OpenInterestDataType { [EnumMember(Value = "All"), Description("Все")] All, [EnumMember(Value = "Buys"), Description("Покупки")] Buys, [EnumMember(Value = "Sells"), Description("Продажи")] Sells } [TypeConverter(typeof(EnumDescriptionTypeConverter))] [DataContract(Name = "OpenInterestPeriodType", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators.Custom")] public enum OpenInterestPeriodType { [EnumMember(Value = "Bar"), Description("Бар")] Bar, [EnumMember(Value = "Day"), Description("День")] Day, [EnumMember(Value = "Week"), Description("Неделя")] Week, [EnumMember(Value = "Month"), Description("Месяц")] Month, [EnumMember(Value = "AllBars"), Description("Все бары")] AllBars } [DataContract(Name = "OpenInterestIndicator", Namespace = "http://schemas.datacontract.org/2004/07/TigerTrade.Chart.Indicators.Custom")] [Indicator("X_OpenInterest", "*OpenInterest", false, Type = typeof(OpenInterestIndicator))] internal sealed class OpenInterestIndicator : IndicatorBase { private OpenInterestDataType _dataType; [DataMember(Name = "DataType")] [Category("Параметры"), DisplayName("Тип сделок")] public OpenInterestDataType DataType { get => _dataType; set { if (value == _dataType) { return; } _dataType = value; Clear(); OnPropertyChanged(); OnPropertyChanged(nameof(Title)); } } private OpenInterestPeriodType _period; [DataMember(Name = "Period")] [Category("Параметры"), DisplayName("Период")] public OpenInterestPeriodType Period { get => _period; set { if (value == _period) { return; } _period = value; Clear(); OnPropertyChanged(); OnPropertyChanged(nameof(Title)); } } private IndicatorViewType _type; [DataMember(Name = "Type")] [Category("Параметры"), DisplayName("Тип")] public IndicatorViewType Type { get => _type; set { if (value == _type) { return; } _type = value; OnPropertyChanged(); OnPropertyChanged(nameof(UpColor)); OnPropertyChanged(nameof(DownColor)); OnPropertyChanged(nameof(LineColor)); OnPropertyChanged(nameof(LineWidth)); } } private XBrush _upBrush; private XPen _upPen; private XColor _upColor; [DataMember(Name = "UpColor")] [Category("Стиль"), DisplayName("Цвет при росте")] public XColor UpColor { get => _upColor; set { if (value == _upColor) { return; } _upColor = value; _upBrush = new XBrush(_upColor); _upPen = new XPen(_upBrush, 1); OnPropertyChanged(); } } private XBrush _downBrush; private XPen _downPen; private XColor _downColor; [DataMember(Name = "DownColor")] [Category("Стиль"), DisplayName("Цвет при падении")] public XColor DownColor { get => _downColor; set { if (value == _downColor) { return; } _downColor = value; _downBrush = new XBrush(_downColor); _downPen = new XPen(_downBrush, 1); OnPropertyChanged(); } } private XBrush _lineBrush; private XPen _linePen; 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); OnPropertyChanged(); } } private int _lineWidth; [DataMember(Name = "LineWidth")] [Category("Стиль"), DisplayName("Толщина линии")] public int LineWidth { get => _lineWidth; set { value = Math.Max(1, Math.Min(9, value)); if (value == _lineWidth) { return; } _lineWidth = value; _linePen = new XPen(_lineBrush, _lineWidth); OnPropertyChanged(); } } [Browsable(false)] public override IndicatorCalculation Calculation => IndicatorCalculation.OnEachTick; public override bool IntegerValues => true; private class OpenIntItem { public int Open; public int High; public int Low; public int Close; public int Seq = -1; public int OpenInt; public int StartOpenInt; } private int _lastFullID; private List<OpenIntItem> _items; private List<OpenIntItem> Items => _items ?? (_items = new List<OpenIntItem>()); private void Clear() { _lastFullID = 0; Items.Clear(); } public OpenInterestIndicator() { DataType = OpenInterestDataType.All; Period = OpenInterestPeriodType.Bar; Type = IndicatorViewType.Columns; UpColor = Color.FromArgb(255, 30, 144, 255); DownColor = Color.FromArgb(255, 178, 34, 34); LineColor = Color.FromArgb(255, 30, 144, 255); LineWidth = 1; } protected override void Execute() { if (ClearData) { Clear(); } var timeOffset = TimeHelper.GetSessionOffset(DataProvider.Symbol.Exchange); for (var i = _lastFullID; i < DataProvider.Count; i++) { var cluster = DataProvider.GetRawCluster(i); if (Items.Count < i + 1) { Items.Add(new OpenIntItem()); } var item = Items[i]; var prevItem = i > 0 && Items.Count > 1 ? Items[i - 1] : new OpenIntItem { OpenInt = cluster.OpenPos, StartOpenInt = cluster.OpenPos }; var sequence = 1; switch (Period) { case OpenInterestPeriodType.Bar: sequence = i; break; case OpenInterestPeriodType.Day: sequence = DataProvider.Period.GetSequence(ChartPeriodType.Day, 1, cluster.Time, timeOffset); break; case OpenInterestPeriodType.Week: sequence = DataProvider.Period.GetSequence(ChartPeriodType.Week, 1, cluster.Time, timeOffset); break; case OpenInterestPeriodType.Month: sequence = DataProvider.Period.GetSequence(ChartPeriodType.Month, 1, cluster.Time, timeOffset); break; case OpenInterestPeriodType.AllBars: sequence = -1; break; } switch (DataType) { case OpenInterestDataType.All: { if (Period == OpenInterestPeriodType.AllBars) { item.Close = cluster.OpenPos; item.Open = prevItem.Close; item.High = cluster.OpenPosHigh; item.Low = cluster.OpenPosLow; if (item.Open == 0) { item.Open = item.Close; } } else { var startOpenPosValue = prevItem.StartOpenInt; var prevClose = prevItem.OpenInt - startOpenPosValue; var close = cluster.OpenPos - startOpenPosValue; if (sequence != prevItem.Seq) { startOpenPosValue = prevItem.OpenInt; prevClose = 0; close = cluster.OpenPos - prevItem.OpenInt; if (close == 0) { close = cluster.OpenPosChg; startOpenPosValue -= cluster.OpenPosChg; } } item.Seq = sequence; item.OpenInt = cluster.OpenPos; item.StartOpenInt = startOpenPosValue; item.Close = close; item.Open = prevClose; item.High = cluster.OpenPosHigh - startOpenPosValue; item.Low = cluster.OpenPosLow - startOpenPosValue; } break; } case OpenInterestDataType.Buys: { if (sequence != prevItem.Seq) { prevItem.OpenInt = 0; } item.Seq = sequence; item.Open = prevItem.OpenInt; item.Close = prevItem.OpenInt + cluster.OpenPosAskChg; item.High = Math.Max(item.Open, item.Close); item.Low = Math.Min(item.Open, item.Close); item.OpenInt = item.Close; break; } case OpenInterestDataType.Sells: { if (sequence != prevItem.Seq) { prevItem.OpenInt = 0; } item.Seq = sequence; item.Open = prevItem.OpenInt; item.Close = prevItem.OpenInt + cluster.OpenPosBidChg; item.High = Math.Max(item.Open, item.Close); item.Low = Math.Min(item.Open, item.Close); item.OpenInt = item.Close; break; } } } _lastFullID = Math.Max(DataProvider.Count - 2, 0); } public override bool GetMinMax(out double min, out double max) { min = double.MaxValue; max = double.MinValue; if (Items.Count == 0) { return false; } var longMin = long.MaxValue; var longMax = long.MinValue; if (Type == IndicatorViewType.Candles) { for (var i = 0; i < Canvas.Count; i++) { var index = Canvas.GetIndex(i); if (index < 0 || index >= Items.Count) { continue; } var item = Items[index]; if (longMin > item.Low) { longMin = item.Low; } if (longMax < item.High) { longMax = item.High; } } } else { for (var i = 0; i < Canvas.Count; i++) { var index = Canvas.GetIndex(i); if (index < 0 || index >= Items.Count) { continue; } var item = Items[index]; if (longMin > item.Close) { longMin = item.Close; } if (longMax < item.Close) { longMax = item.Close; } } } if (longMin > longMax) { return false; } min = longMin; max = longMax; return true; } public override void Render(DxVisualQueue visual) { switch (Type) { case IndicatorViewType.Candles: { var width = (int)(Canvas.ColumnWidth * Canvas.ColumnPercent + 0.4); var halfWith = Math.Max((int)(width / 2.0), 1.0); for (var i = 0; i < Canvas.Count; i++) { var index = Canvas.GetIndex(i); if (index < 0 || index >= Items.Count) { continue; } var item = Items[index]; var isUp = item.Close > item.Open; var centerX = (int)Canvas.GetX(index); var openY = (int)GetY((double)item.Open); var highY = (int)GetY((double)item.High); var lowY = (int)GetY((double)item.Low); var closeY = (int)GetY((double)item.Close); var topY = Math.Min(openY, closeY); var bottomY = Math.Max(openY, closeY); var height = bottomY - topY; var backBrush = isUp ? _upBrush : _downBrush; var backPen = isUp ? _upPen : _downPen; if (height == 0 || width <= 1) { if (highY == lowY) { lowY++; } visual.DrawLine(backPen, new Point(centerX, highY), new Point(centerX, lowY)); } else { visual.DrawLine(backPen, new Point(centerX, highY), new Point(centerX, topY)); visual.DrawLine(backPen, new Point(centerX, bottomY), new Point(centerX, lowY)); } if (width > 1) { if (height == 0) { visual.DrawLine(backPen, new Point(centerX - halfWith, topY), new Point(centerX + halfWith + 1, topY)); } else { visual.FillRectangle(backBrush, new Rect(centerX - halfWith, topY, halfWith * 2 + 1, height)); } } } } break; case IndicatorViewType.Columns: { var zeroPoint = GetY(0.0); var width = Math.Max(Canvas.ColumnWidth - 1, 1); for (var i = 0; i < Canvas.Count; i++) { var index = Canvas.GetIndex(i); if (index < 0 || index >= Items.Count) { continue; } var item = Items[index]; var x = Canvas.GetX(index); var y = GetY((double)item.Close); var h = (int)zeroPoint - (int)y; var isUp = h > 0; if (h < 0.0) { y += h; h = -h; } h = Math.Max(1, h); if (width > 1.0) { var rect = new Rect(x - width / 2.0, y, width, h); visual.FillRectangle(isUp ? _upBrush : _downBrush, rect); } else { visual.DrawLine(isUp ? _upPen : _downPen, x, y, x, y + h); } } } break; case IndicatorViewType.Line: { if (Items.Count < 2) { return; } var points = new Point[Canvas.Count]; for (var i = 0; i < Canvas.Count; i++) { var index = Canvas.GetIndex(i); if (index < 0 || index >= Items.Count) { continue; } var item = Items[index]; var x = Canvas.GetX(index); var y = GetY((double)item.Close); points[i] = new Point(x, y); } visual.DrawLines(_linePen, points); } break; } } public override List<IndicatorValueInfo> GetValues(int cursorPos) { var info = new List<IndicatorValueInfo>(); if (cursorPos >= 0 && cursorPos < Items.Count) { var s = Canvas.FormatValue(Items[cursorPos].Close); info.Add(new IndicatorValueInfo(s, Type == IndicatorViewType.Line ? _lineBrush : Canvas.Theme.ChartFontBrush)); } return info; } public override void GetLabels(ref List<IndicatorLabelInfo> labels) { if (Items.Count == 0) { return; } if (DataProvider.Count <= Canvas.Start) { return; } var index = DataProvider.Count - 1 - Canvas.Start; if (index < 0 && index >= Items.Count) { return; } var item = Items[index]; var lastValue = item.Close; if (Type == IndicatorViewType.Line) { labels.Add(new IndicatorLabelInfo(lastValue, _lineColor)); return; } var isUp = false; switch (Type) { case IndicatorViewType.Line: case IndicatorViewType.Columns: isUp = !double.IsNaN(lastValue) && lastValue > 0; break; case IndicatorViewType.Candles: isUp = !double.IsNaN(lastValue) && item.Close > item.Open; break; } labels.Add(new IndicatorLabelInfo(lastValue, isUp ? UpColor : DownColor)); } public override void ApplyColors(IChartTheme theme) { UpColor = theme.PaletteColor6; DownColor = theme.PaletteColor7; LineColor = theme.GetNextColor(); base.ApplyColors(theme); } public override void CopyTemplate(IndicatorBase indicator, bool style) { var i = (OpenInterestIndicator)indicator; DataType = i.DataType; Period = i.Period; Type = i.Type; UpColor = i.UpColor; DownColor = i.DownColor; LineColor = i.LineColor; LineWidth = i.LineWidth; base.CopyTemplate(indicator, style); } public override string ToString() { return $"{Name} ({Period}, {DataType})"; } public override bool GetPropertyVisibility(string propertyName) { switch (propertyName) { case "LineWidth": case "LineColor": return Type == IndicatorViewType.Line; case "UpColor": case "DownColor": return Type != IndicatorViewType.Line; } return true; } } }