454 lines
17 KiB
C#
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
|