Open Interest
//------------------------------------------------------------------------------
//
// Индикатор OpenInterest. 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.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;
}
}
}
Last updated