ninjatrader/indicators/FairValueGap.cs

454 lines
17 KiB
C#

#region Using declarations
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Xml.Serialization;
using NinjaTrader.Cbi;
using NinjaTrader.Gui;
using NinjaTrader.Gui.Chart;
using NinjaTrader.Gui.SuperDom;
using NinjaTrader.Gui.Tools;
using NinjaTrader.Data;
using NinjaTrader.NinjaScript;
using NinjaTrader.Core.FloatingPoint;
using NinjaTrader.NinjaScript.DrawingTools;
using System.Globalization;
using NinjaTrader.NinjaScript.Indicators;
#endregion
//This namespace holds Indicators in this folder and is required. Do not change it.
namespace NinjaTrader.NinjaScript.Indicators
{
public class FairValueGap
{
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public double StartPrice { get; set; }
public double EndPrice { get; set; }
}
[TypeConverter("NinjaTrader.NinjaScript.Indicators.FairValueGapsPropertyConverter")]
public class FairValueGaps : Indicator
{
public const int CHART_BARS = 0;
public const int OTHER_BARS = 1;
private List<FairValueGap> fairValueGaps;
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Description = @"ICT Fair Value Gap (FVG)";
Name = "Fair Value Gaps";
Calculate = Calculate.OnPriceChange;
IsOverlay = true;
DisplayInDataBox = true;
DrawOnPricePanel = true;
DrawHorizontalGridLines = true;
DrawVerticalGridLines = true;
PaintPriceMarkers = true;
ScaleJustification = ScaleJustification.Right;
IsSuspendedWhileInactive = true;
MinimumGap = 1.0;
GapBars = CHART_BARS;
BarType = BarsPeriodType.Minute;
Period = 1;
GapUpColor = Brushes.LimeGreen;
GapUpOpacity = 25;
GapDownColor = Brushes.Red;
GapDownOpacity = 25;
}
else if (State == State.Configure)
{
fairValueGaps = new List<FairValueGap>();
if (GapBars != CHART_BARS)
{
AddDataSeries(Instrument.FullName, new BarsPeriod {
BarsPeriodType = BarType, Value = Period }, Bars.TradingHours.Name);
}
}
else if (State == State.Historical)
{
SetZOrder(-1);
}
}
protected override void OnBarUpdate()
{
if (CurrentBar < 4)
return;
if (GapBars == BarsInProgress && IsFirstTickOfBar)
{
double downGap = Lows[GapBars][3] - Highs[GapBars][1];
if (downGap > MinimumGap)
{
fairValueGaps.Add(new FairValueGap
{
StartTime = Times[GapBars][3],
StartPrice = Lows[GapBars][3],
EndPrice = Highs[GapBars][1]
});
}
double upGap = Lows[GapBars][1] - Highs[GapBars][3];
if (upGap > MinimumGap)
{
fairValueGaps.Add(new FairValueGap
{
StartTime = Times[GapBars][3],
StartPrice = Highs[GapBars][3],
EndPrice = Lows[GapBars][1]
});
}
}
var unfilledGaps = from fairValueGap in fairValueGaps
where fairValueGap.EndTime == default(DateTime)
select fairValueGap;
foreach (FairValueGap unfilledGap in unfilledGaps)
{
if (unfilledGap.EndPrice < unfilledGap.StartPrice && Highs[BarsInProgress][0] >= unfilledGap.StartPrice)
unfilledGap.EndTime = Times[BarsInProgress][0];
else if (unfilledGap.EndPrice > unfilledGap.StartPrice && Lows[BarsInProgress][0] <= unfilledGap.StartPrice)
unfilledGap.EndTime = Times[BarsInProgress][0];
}
}
protected override void OnRender(ChartControl chartControl, ChartScale chartScale)
{
base.OnRender(chartControl, chartScale);
SharpDX.Direct2D1.Brush fairValueGapUpBrush = GapUpColor.ToDxBrush(RenderTarget);
fairValueGapUpBrush.Opacity = GapUpOpacity / 100f;
SharpDX.Direct2D1.Brush fairValueGapDownBrush = GapDownColor.ToDxBrush(RenderTarget);
fairValueGapDownBrush.Opacity = GapDownOpacity / 100f;
// TODO: Only render gaps that would actually be visible on the screen.
foreach (FairValueGap fairValueGap in fairValueGaps)
{
int fairValueGapStartX = chartControl.GetXByTime(fairValueGap.StartTime);
int fairValueGapEndX;
if (fairValueGap.EndTime != default(DateTime))
fairValueGapEndX = chartControl.GetXByTime(fairValueGap.EndTime);
else
fairValueGapEndX = ChartPanel.X + ChartPanel.W;
int fairValueGapStartY = chartScale.GetYByValue(Math.Max(fairValueGap.StartPrice, fairValueGap.EndPrice));
int fairValueGapEndY = chartScale.GetYByValue(Math.Min(fairValueGap.StartPrice, fairValueGap.EndPrice));
SharpDX.RectangleF fairValueGapRectangle = new SharpDX.RectangleF(fairValueGapStartX, fairValueGapStartY,
fairValueGapEndX - fairValueGapStartX, fairValueGapEndY - fairValueGapStartY);
RenderTarget.FillRectangle(fairValueGapRectangle,
fairValueGap.EndPrice > fairValueGap.StartPrice ? fairValueGapUpBrush : fairValueGapDownBrush);
}
fairValueGapUpBrush.Dispose();
fairValueGapDownBrush.Dispose();
}
public override string DisplayName
{
get { return Name; }
}
#region Properties
[NinjaScriptProperty]
[Range(1, double.MaxValue)]
[Display(Name = "Minimum Gap Size", Description = "Minimum size of gaps to consider (in points)", Order = 1, GroupName = "Fair Value Gaps")]
public double MinimumGap
{ get; set; }
[TypeConverter(typeof(GapBarsConverter))]
[PropertyEditor("NinjaTrader.Gui.Tools.StringStandardValuesEditorKey")]
[RefreshProperties(RefreshProperties.All)]
[Display(Name = "Detect Gaps On", Description = "The bars used to detect gaps", Order = 2, GroupName = "Fair Value Gaps")]
public int GapBars
{ get; set; }
[TypeConverter(typeof(BarsPeriodTypeConverter))]
[PropertyEditor("NinjaTrader.Gui.Tools.StringStandardValuesEditorKey")]
[Display(Name = "Bar Type", Description = "Type of bars on which to detect gaps", Order = 3, GroupName = "Fair Value Gaps")]
public BarsPeriodType BarType
{ get; set; }
[NinjaScriptProperty]
[Range(1, int.MaxValue)]
[Display(Name = "Period", Description = "Period of the bars used to detect gaps", Order = 4, GroupName = "Fair Value Gaps")]
public int Period
{ get; set; }
[XmlIgnore]
[NinjaScriptProperty]
[Display(Name = "Gap Up", Description = "Color of the area of upward gaps", Order = 5, GroupName = "Fair Value Gaps")]
public Brush GapUpColor
{ get; set; }
[Browsable(false)]
public string GapUpColorSerialization
{
get { return Serialize.BrushToString(GapUpColor); }
set { GapUpColor = Serialize.StringToBrush(value); }
}
[NinjaScriptProperty]
[Range(0, 100)]
[Display(Name = "Gap Up Opacity (%)", Description = "Opacity of the area of upward gaps", Order = 6, GroupName = "Fair Value Gaps")]
public int GapUpOpacity
{ get; set; }
[XmlIgnore]
[NinjaScriptProperty]
[Display(Name = "Gap Down", Description = "Color of the area of downward gaps", Order = 7, GroupName = "Fair Value Gaps")]
public Brush GapDownColor
{ get; set; }
[Browsable(false)]
public string GapDownColorSerialization
{
get { return Serialize.BrushToString(GapDownColor); }
set { GapDownColor = Serialize.StringToBrush(value); }
}
[NinjaScriptProperty]
[Range(0, 100)]
[Display(Name = "Gap Down Opacity (%)", Description = "Opacity of the area of downward gaps", Order = 8, GroupName = "Fair Value Gaps")]
public int GapDownOpacity
{ get; set; }
#endregion
}
public class FairValueGapsPropertyConverter : IndicatorBaseConverter
{
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object component, Attribute[] attrs)
{
FairValueGaps indicator = component as FairValueGaps;
PropertyDescriptorCollection properties = base.GetPropertiesSupported(context) ?
base.GetProperties(context, component, attrs) : TypeDescriptor.GetProperties(component, attrs);
if (indicator == null || properties == null)
return properties;
PropertyDescriptor barType = properties["BarType"];
PropertyDescriptor period = properties["Period"];
properties.Remove(barType);
properties.Remove(period);
if (indicator.GapBars == FairValueGaps.OTHER_BARS)
{
properties.Add(barType);
properties.Add(period);
}
return properties;
}
public override bool GetPropertiesSupported(ITypeDescriptorContext context)
{ return true; }
}
}
public class GapBarsConverter : TypeConverter
{
private const string CHART = "Chart Bars";
private const string OTHER = "Other";
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
List<string> values = new List<string>() { CHART, OTHER };
return new StandardValuesCollection(values);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
switch (value.ToString())
{
case CHART:
return FairValueGaps.CHART_BARS;
case OTHER:
return FairValueGaps.OTHER_BARS;
}
return FairValueGaps.CHART_BARS;
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
switch ((int)value)
{
case FairValueGaps.CHART_BARS:
return CHART;
case FairValueGaps.OTHER_BARS:
return OTHER;
}
return CHART;
}
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{ return true; }
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{ return true; }
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
{ return true; }
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{ return true; }
}
public class BarsPeriodTypeConverter : TypeConverter
{
private const string MINUTE = "Minute";
private const string SECOND = "Second";
private const string DAY = "Day";
private const string WEEK = "Week";
private const string MONTH = "Month";
private const string YEAR = "Year";
private const string RANGE = "Range";
private const string TICK = "Tick";
private const string VOLUME = "Volume";
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
List<string> values = new List<string>() { MINUTE, SECOND, DAY, WEEK, MONTH, YEAR, RANGE, TICK, VOLUME };
return new StandardValuesCollection(values);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
switch (value.ToString())
{
case MINUTE:
return BarsPeriodType.Minute;
case SECOND:
return BarsPeriodType.Second;
case DAY:
return BarsPeriodType.Day;
case WEEK:
return BarsPeriodType.Week;
case MONTH:
return BarsPeriodType.Month;
case YEAR:
return BarsPeriodType.Year;
case RANGE:
return BarsPeriodType.Range;
case TICK:
return BarsPeriodType.Tick;
case VOLUME:
return BarsPeriodType.Volume;
}
return BarsPeriodType.Minute;
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
BarsPeriodType enumValue = (BarsPeriodType)Enum.Parse(typeof(BarsPeriodType), value.ToString());
switch (enumValue)
{
case BarsPeriodType.Minute:
return MINUTE;
case BarsPeriodType.Second:
return SECOND;
case BarsPeriodType.Day:
return DAY;
case BarsPeriodType.Week:
return WEEK;
case BarsPeriodType.Month:
return MONTH;
case BarsPeriodType.Year:
return YEAR;
case BarsPeriodType.Range:
return RANGE;
case BarsPeriodType.Tick:
return TICK;
case BarsPeriodType.Volume:
return VOLUME;
}
return MINUTE;
}
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{ return true; }
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{ return true; }
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
{ return true; }
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{ return true; }
}
#region NinjaScript generated code. Neither change nor remove.
namespace NinjaTrader.NinjaScript.Indicators
{
public partial class Indicator : NinjaTrader.Gui.NinjaScript.IndicatorRenderBase
{
private FairValueGaps[] cacheFairValueGaps;
public FairValueGaps FairValueGaps(double minimumGap, int period, Brush gapUpColor, int gapUpOpacity, Brush gapDownColor, int gapDownOpacity)
{
return FairValueGaps(Input, minimumGap, period, gapUpColor, gapUpOpacity, gapDownColor, gapDownOpacity);
}
public FairValueGaps FairValueGaps(ISeries<double> input, double minimumGap, int period, Brush gapUpColor, int gapUpOpacity, Brush gapDownColor, int gapDownOpacity)
{
if (cacheFairValueGaps != null)
for (int idx = 0; idx < cacheFairValueGaps.Length; idx++)
if (cacheFairValueGaps[idx] != null && cacheFairValueGaps[idx].MinimumGap == minimumGap && cacheFairValueGaps[idx].Period == period && cacheFairValueGaps[idx].GapUpColor == gapUpColor && cacheFairValueGaps[idx].GapUpOpacity == gapUpOpacity && cacheFairValueGaps[idx].GapDownColor == gapDownColor && cacheFairValueGaps[idx].GapDownOpacity == gapDownOpacity && cacheFairValueGaps[idx].EqualsInput(input))
return cacheFairValueGaps[idx];
return CacheIndicator<FairValueGaps>(new FairValueGaps(){ MinimumGap = minimumGap, Period = period, GapUpColor = gapUpColor, GapUpOpacity = gapUpOpacity, GapDownColor = gapDownColor, GapDownOpacity = gapDownOpacity }, input, ref cacheFairValueGaps);
}
}
}
namespace NinjaTrader.NinjaScript.MarketAnalyzerColumns
{
public partial class MarketAnalyzerColumn : MarketAnalyzerColumnBase
{
public Indicators.FairValueGaps FairValueGaps(double minimumGap, int period, Brush gapUpColor, int gapUpOpacity, Brush gapDownColor, int gapDownOpacity)
{
return indicator.FairValueGaps(Input, minimumGap, period, gapUpColor, gapUpOpacity, gapDownColor, gapDownOpacity);
}
public Indicators.FairValueGaps FairValueGaps(ISeries<double> input , double minimumGap, int period, Brush gapUpColor, int gapUpOpacity, Brush gapDownColor, int gapDownOpacity)
{
return indicator.FairValueGaps(input, minimumGap, period, gapUpColor, gapUpOpacity, gapDownColor, gapDownOpacity);
}
}
}
namespace NinjaTrader.NinjaScript.Strategies
{
public partial class Strategy : NinjaTrader.Gui.NinjaScript.StrategyRenderBase
{
public Indicators.FairValueGaps FairValueGaps(double minimumGap, int period, Brush gapUpColor, int gapUpOpacity, Brush gapDownColor, int gapDownOpacity)
{
return indicator.FairValueGaps(Input, minimumGap, period, gapUpColor, gapUpOpacity, gapDownColor, gapDownOpacity);
}
public Indicators.FairValueGaps FairValueGaps(ISeries<double> input , double minimumGap, int period, Brush gapUpColor, int gapUpOpacity, Brush gapDownColor, int gapDownOpacity)
{
return indicator.FairValueGaps(input, minimumGap, period, gapUpColor, gapUpOpacity, gapDownColor, gapDownOpacity);
}
}
}
#endregion