426 lines
14 KiB
C#
426 lines
14 KiB
C#
#region Using declarations
|
|
using NinjaTrader.Cbi;
|
|
using NinjaTrader.Data;
|
|
using NinjaTrader.Gui;
|
|
using NinjaTrader.NinjaScript.MarketAnalyzerColumns;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.ComponentModel.DataAnnotations;
|
|
using System.Globalization;
|
|
using System.Windows.Media;
|
|
using System.Xml.Serialization;
|
|
#endregion
|
|
|
|
namespace NinjaTrader.NinjaScript.Indicators
|
|
{
|
|
public class VWAP : Indicator
|
|
{
|
|
private static int CHART_BARS = 0;
|
|
private static int TICK_BARS = 1;
|
|
|
|
private double TotalVolume;
|
|
private double VolumeWeightedPrice;
|
|
private double VolumeWeightedSquaredPrice;
|
|
|
|
private double TotalVolumeTick;
|
|
private double VolumeWeightedPriceTick;
|
|
private double VolumeWeightedSquaredPriceTick;
|
|
|
|
private DateTime MostRecentDate = DateTime.MinValue;
|
|
|
|
protected override void OnStateChange()
|
|
{
|
|
if (State == State.SetDefaults)
|
|
{
|
|
Description = "Volume Weighted Average Price (VWAP)";
|
|
Name = "VWAP";
|
|
Calculate = Calculate.OnBarClose;
|
|
IsOverlay = true;
|
|
DisplayInDataBox = true;
|
|
DrawOnPricePanel = true;
|
|
ScaleJustification = Gui.Chart.ScaleJustification.Right;
|
|
IsSuspendedWhileInactive = true;
|
|
BarsRequiredToPlot = 0;
|
|
|
|
Precision = VWAPPrecision.Chart;
|
|
Price = VWAPPrice.HLC; // Typical Price
|
|
StdDevMultiplier = 1.0;
|
|
|
|
AddPlot(new Stroke(Brushes.Yellow, DashStyleHelper.Solid, 3), PlotStyle.Line, "VWAP");
|
|
AddPlot(new Stroke(Brushes.Transparent, DashStyleHelper.Solid, 3), PlotStyle.Line, "Upper Standard Deviation");
|
|
AddPlot(new Stroke(Brushes.Transparent, DashStyleHelper.Solid, 3), PlotStyle.Line, "Lower Standard Deviation");
|
|
}
|
|
else if (State == State.Configure)
|
|
{
|
|
ClearOutputWindow();
|
|
|
|
if (Precision == VWAPPrecision.Tick)
|
|
{
|
|
AddDataSeries(Instrument.FullName, new BarsPeriod
|
|
{
|
|
BarsPeriodType = BarsPeriodType.Tick,
|
|
Value = 1
|
|
}, Bars.TradingHours.Name);
|
|
}
|
|
}
|
|
else if (State == State.DataLoaded)
|
|
{
|
|
ResetVWAP();
|
|
}
|
|
else if (State == State.Historical)
|
|
{
|
|
SetZOrder(-1); // Display behind bars on chart.
|
|
}
|
|
}
|
|
|
|
private double GetPrice(int bars, int index = 0)
|
|
{
|
|
switch (Price)
|
|
{
|
|
case VWAPPrice.Open:
|
|
return Opens[bars][index];
|
|
case VWAPPrice.High:
|
|
return Highs[bars][index];
|
|
case VWAPPrice.Low:
|
|
return Lows[bars][index];
|
|
case VWAPPrice.Close:
|
|
return Closes[bars][index];
|
|
case VWAPPrice.HL:
|
|
return (Highs[bars][index] + Lows[bars][index]) / 2;
|
|
case VWAPPrice.HLC:
|
|
return (Highs[bars][index] + Lows[bars][index] + Closes[bars][index]) / 3;
|
|
case VWAPPrice.OHLC:
|
|
default:
|
|
return (Opens[bars][index] + Highs[bars][index] + Lows[bars][index] + Closes[bars][index]) / 4;
|
|
}
|
|
}
|
|
|
|
private void ResetVWAP()
|
|
{
|
|
TotalVolume = 0;
|
|
VolumeWeightedPrice = 0;
|
|
VolumeWeightedSquaredPrice = 0;
|
|
|
|
TotalVolumeTick = 0;
|
|
VolumeWeightedPriceTick = 0;
|
|
VolumeWeightedSquaredPriceTick = 0;
|
|
}
|
|
|
|
protected override void OnBarUpdate()
|
|
{
|
|
if (Bars.IsFirstBarOfSession && IsFirstTickOfBar)
|
|
{
|
|
DateTime currentDate = Time[0].Date;
|
|
if (MostRecentDate != currentDate)
|
|
ResetVWAP();
|
|
|
|
if (CHART_BARS == BarsInProgress)
|
|
{
|
|
// Prevent visible jumps in VWAP when a new session begins.
|
|
PlotBrushes[0][0] = Brushes.Transparent;
|
|
PlotBrushes[1][0] = Brushes.Transparent;
|
|
PlotBrushes[2][0] = Brushes.Transparent;
|
|
}
|
|
|
|
MostRecentDate = currentDate;
|
|
}
|
|
|
|
if (CHART_BARS == BarsInProgress)
|
|
{
|
|
double price = GetPrice(CHART_BARS);
|
|
double vwap;
|
|
double variance;
|
|
|
|
double volumeWeightedPrice;
|
|
double volumeWeightedSquaredPrice;
|
|
double totalVolume;
|
|
|
|
if (Precision == VWAPPrecision.Tick)
|
|
{
|
|
volumeWeightedPrice = VolumeWeightedPriceTick;
|
|
volumeWeightedSquaredPrice = VolumeWeightedSquaredPriceTick;
|
|
totalVolume = TotalVolumeTick;
|
|
}
|
|
else if (Calculate == Calculate.OnBarClose)
|
|
{
|
|
VolumeWeightedPrice += Volume[0] * price;
|
|
VolumeWeightedSquaredPrice += Volume[0] * Math.Pow(price, 2);
|
|
TotalVolume += Volume[0];
|
|
|
|
volumeWeightedPrice = VolumeWeightedPrice;
|
|
volumeWeightedSquaredPrice = VolumeWeightedSquaredPrice;
|
|
totalVolume = TotalVolume;
|
|
}
|
|
else
|
|
{
|
|
if (IsFirstTickOfBar && !Bars.IsFirstBarOfSession)
|
|
{
|
|
int previousBar = 1;
|
|
double previousPrice = GetPrice(CHART_BARS, previousBar);
|
|
|
|
VolumeWeightedPrice += Volume[previousBar] * previousPrice;
|
|
VolumeWeightedSquaredPrice += Volume[previousBar] * Math.Pow(previousPrice, 2);
|
|
TotalVolume += Volume[previousBar];
|
|
}
|
|
|
|
volumeWeightedPrice = VolumeWeightedPrice + (Volume[0] * price);
|
|
volumeWeightedSquaredPrice = VolumeWeightedSquaredPrice + (Volume[0] * Math.Pow(price, 2));
|
|
totalVolume = TotalVolume + Volume[0];
|
|
}
|
|
|
|
vwap = volumeWeightedPrice / totalVolume;
|
|
|
|
variance = (volumeWeightedSquaredPrice / totalVolume) - Math.Pow(vwap, 2);
|
|
double stdDev = Math.Sqrt(variance);
|
|
|
|
Value[0] = vwap;
|
|
Values[1][0] = vwap + (StdDevMultiplier * stdDev);
|
|
Values[2][0] = vwap - (StdDevMultiplier * stdDev);
|
|
}
|
|
else if (TICK_BARS == BarsInProgress)
|
|
{
|
|
double price = GetPrice(TICK_BARS);
|
|
TotalVolumeTick += Volumes[TICK_BARS][0];
|
|
VolumeWeightedPriceTick += Volumes[TICK_BARS][0] * price;
|
|
VolumeWeightedSquaredPriceTick += Volumes[TICK_BARS][0] * Math.Pow(price, 2);
|
|
}
|
|
}
|
|
|
|
public override string DisplayName
|
|
{
|
|
get { return Name; }
|
|
}
|
|
|
|
[Browsable(false)]
|
|
[XmlIgnore]
|
|
public Series<double> UpperStdDev
|
|
{
|
|
get { return Values[1]; }
|
|
}
|
|
|
|
[Browsable(false)]
|
|
[XmlIgnore]
|
|
public Series<double> LowerStdDev
|
|
{
|
|
get { return Values[2]; }
|
|
}
|
|
|
|
#region Properties
|
|
[TypeConverter(typeof(VWAPPrecisionConverter))]
|
|
[PropertyEditor("NinjaTrader.Gui.Tools.StringStandardValuesEditorKey")]
|
|
[Display(Name = "Precision", Description = "Precision with which VWAP is calculated", GroupName = "VWAP", Order = 1)]
|
|
public VWAPPrecision Precision
|
|
{ get; set; }
|
|
|
|
[TypeConverter(typeof(VWAPPriceConverter))]
|
|
[PropertyEditor("NinjaTrader.Gui.Tools.StringStandardValuesEditorKey")]
|
|
[Display(Name = "Average Price", Description = "How the average price of the bar is calculated", GroupName = "VWAP", Order = 2)]
|
|
public VWAPPrice Price
|
|
{ get; set; }
|
|
|
|
[Range(0.1, double.MaxValue)]
|
|
[Display(Name = "Standard Deviation Multiplier", Description = "Multiplier for standard deviation bands", GroupName = "VWAP", Order = 3)]
|
|
public double StdDevMultiplier
|
|
{ get; set; }
|
|
#endregion
|
|
}
|
|
}
|
|
|
|
public enum VWAPPrecision
|
|
{
|
|
Chart,
|
|
Tick
|
|
}
|
|
|
|
public enum VWAPPrice
|
|
{
|
|
Open,
|
|
High,
|
|
Low,
|
|
Close,
|
|
HL,
|
|
HLC,
|
|
OHLC
|
|
}
|
|
|
|
public class VWAPPrecisionConverter : TypeConverter
|
|
{
|
|
private const string CHART = "Chart";
|
|
private const string TICK = "Tick";
|
|
|
|
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
|
|
{
|
|
List<string> values = new List<string>() { CHART, TICK };
|
|
return new StandardValuesCollection(values);
|
|
}
|
|
|
|
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
|
{
|
|
switch (value.ToString())
|
|
{
|
|
case CHART:
|
|
return VWAPPrecision.Chart;
|
|
case TICK:
|
|
return VWAPPrecision.Tick;
|
|
}
|
|
return VWAPPrecision.Chart;
|
|
}
|
|
|
|
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
|
{
|
|
VWAPPrecision enumValue = (VWAPPrecision)Enum.Parse(typeof(VWAPPrecision), value.ToString());
|
|
switch (enumValue)
|
|
{
|
|
case VWAPPrecision.Chart:
|
|
return CHART;
|
|
case VWAPPrecision.Tick:
|
|
return TICK;
|
|
}
|
|
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 VWAPPriceConverter : TypeConverter
|
|
{
|
|
private const string OPEN = "Open";
|
|
private const string HIGH = "High";
|
|
private const string LOW = "Low";
|
|
private const string CLOSE = "Close";
|
|
private const string HIGH_LOW = "(High + Low) / 2";
|
|
private const string HIGH_LOW_CLOSE = "(High + Low + Close) / 3";
|
|
private const string OPEN_HIGH_LOW_CLOSE = "(Open + High + Low + Close) / 4";
|
|
|
|
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
|
|
{
|
|
List<string> values = new List<string>() { OPEN, HIGH, LOW, CLOSE, HIGH_LOW, HIGH_LOW_CLOSE, OPEN_HIGH_LOW_CLOSE };
|
|
return new StandardValuesCollection(values);
|
|
}
|
|
|
|
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
|
{
|
|
switch (value.ToString())
|
|
{
|
|
case OPEN:
|
|
return VWAPPrice.Open;
|
|
case HIGH:
|
|
return VWAPPrice.High;
|
|
case LOW:
|
|
return VWAPPrice.Low;
|
|
case CLOSE:
|
|
return VWAPPrice.Close;
|
|
case HIGH_LOW:
|
|
return VWAPPrice.HL;
|
|
case HIGH_LOW_CLOSE:
|
|
return VWAPPrice.HLC;
|
|
case OPEN_HIGH_LOW_CLOSE:
|
|
return VWAPPrice.OHLC;
|
|
}
|
|
return VWAPPrice.OHLC;
|
|
}
|
|
|
|
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
|
{
|
|
VWAPPrice enumValue = (VWAPPrice)Enum.Parse(typeof(VWAPPrice), value.ToString());
|
|
switch (enumValue)
|
|
{
|
|
case VWAPPrice.Open:
|
|
return OPEN;
|
|
case VWAPPrice.High:
|
|
return HIGH;
|
|
case VWAPPrice.Low:
|
|
return LOW;
|
|
case VWAPPrice.Close:
|
|
return CLOSE;
|
|
case VWAPPrice.HL:
|
|
return HIGH_LOW;
|
|
case VWAPPrice.HLC:
|
|
return HIGH_LOW_CLOSE;
|
|
case VWAPPrice.OHLC:
|
|
return OPEN_HIGH_LOW_CLOSE;
|
|
}
|
|
return OPEN_HIGH_LOW_CLOSE;
|
|
}
|
|
|
|
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 VWAP[] cacheVWAP;
|
|
public VWAP VWAP()
|
|
{
|
|
return VWAP(Input);
|
|
}
|
|
|
|
public VWAP VWAP(ISeries<double> input)
|
|
{
|
|
if (cacheVWAP != null)
|
|
for (int idx = 0; idx < cacheVWAP.Length; idx++)
|
|
if (cacheVWAP[idx] != null && cacheVWAP[idx].EqualsInput(input))
|
|
return cacheVWAP[idx];
|
|
return CacheIndicator<VWAP>(new VWAP(), input, ref cacheVWAP);
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace NinjaTrader.NinjaScript.MarketAnalyzerColumns
|
|
{
|
|
public partial class MarketAnalyzerColumn : MarketAnalyzerColumnBase
|
|
{
|
|
public Indicators.VWAP VWAP()
|
|
{
|
|
return indicator.VWAP(Input);
|
|
}
|
|
|
|
public Indicators.VWAP VWAP(ISeries<double> input )
|
|
{
|
|
return indicator.VWAP(input);
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace NinjaTrader.NinjaScript.Strategies
|
|
{
|
|
public partial class Strategy : NinjaTrader.Gui.NinjaScript.StrategyRenderBase
|
|
{
|
|
public Indicators.VWAP VWAP()
|
|
{
|
|
return indicator.VWAP(Input);
|
|
}
|
|
|
|
public Indicators.VWAP VWAP(ISeries<double> input )
|
|
{
|
|
return indicator.VWAP(input);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|