ninjatrader/indicators/vwap/VWAP.cs

407 lines
13 KiB
C#
Raw Normal View History

2024-06-25 18:32:22 +00:00
#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;
#endregion
namespace NinjaTrader.NinjaScript.Indicators
{
public class VWAP : Indicator
{
private static int CHART_BARS = 0;
private static int TICK_BARS = 1;
2024-06-25 18:32:22 +00:00
private double TotalVolume;
private double VolumeWeightedPrice;
private double VolumeWeightedSquaredPrice;
2024-06-25 18:32:22 +00:00
private double TotalVolumeTick;
private double VolumeWeightedPriceTick;
private double VolumeWeightedSquaredPriceTick;
private DateTime MostRecentDate = DateTime.MinValue;
2024-06-25 18:32:22 +00:00
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;
2024-06-25 18:32:22 +00:00
Precision = VWAPPrecision.Chart;
Price = VWAPPrice.OHLC;
StdDevMultiplier = 1.0;
2024-06-25 18:32:22 +00:00
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");
2024-06-25 18:32:22 +00:00
}
else if (State == State.Configure)
{
ClearOutputWindow();
if (Precision == VWAPPrecision.Tick)
{
AddDataSeries(Instrument.FullName, new BarsPeriod
{
BarsPeriodType = BarsPeriodType.Tick,
Value = 1
}, Bars.TradingHours.Name);
2024-06-25 18:32:22 +00:00
}
}
else if (State == State.DataLoaded)
{
2024-06-27 16:21:42 +00:00
ResetVWAP();
2024-06-25 18:32:22 +00:00
}
}
private double GetPrice(int bars, int index = 0)
2024-06-25 18:32:22 +00:00
{
switch (Price)
{
case VWAPPrice.Open:
return Opens[bars][index];
2024-06-25 18:32:22 +00:00
case VWAPPrice.High:
return Highs[bars][index];
2024-06-25 18:32:22 +00:00
case VWAPPrice.Low:
return Lows[bars][index];
2024-06-25 18:32:22 +00:00
case VWAPPrice.Close:
return Closes[bars][index];
2024-06-25 18:32:22 +00:00
case VWAPPrice.HL:
return (Highs[bars][index] + Lows[bars][index]) / 2;
2024-06-25 18:32:22 +00:00
case VWAPPrice.HLC:
return (Highs[bars][index] + Lows[bars][index] + Closes[bars][index]) / 3;
2024-06-25 18:32:22 +00:00
case VWAPPrice.OHLC:
default:
return (Opens[bars][index] + Highs[bars][index] + Lows[bars][index] + Closes[bars][index]) / 4;
2024-06-25 18:32:22 +00:00
}
}
2024-06-27 16:21:42 +00:00
private void ResetVWAP()
{
TotalVolume = 0;
VolumeWeightedPrice = 0;
VolumeWeightedSquaredPrice = 0;
2024-06-27 16:21:42 +00:00
TotalVolumeTick = 0;
VolumeWeightedPriceTick = 0;
VolumeWeightedSquaredPriceTick = 0;
2024-06-27 16:21:42 +00:00
}
2024-06-25 18:32:22 +00:00
protected override void OnBarUpdate()
{
2024-06-27 16:21:42 +00:00
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;
}
2024-06-27 16:21:42 +00:00
MostRecentDate = currentDate;
2024-06-27 16:21:42 +00:00
}
2024-06-25 18:32:22 +00:00
if (CHART_BARS == BarsInProgress)
{
double price = GetPrice(CHART_BARS);
double vwap;
double variance;
double volumeWeightedPrice;
double volumeWeightedSquaredPrice;
double totalVolume;
2024-06-25 18:32:22 +00:00
if (Precision == VWAPPrecision.Tick)
{
volumeWeightedPrice = VolumeWeightedPriceTick;
volumeWeightedSquaredPrice = VolumeWeightedSquaredPriceTick;
totalVolume = TotalVolumeTick;
2024-06-25 18:32:22 +00:00
}
else if (Calculate == Calculate.OnBarClose)
2024-06-25 18:32:22 +00:00
{
VolumeWeightedPrice += Volume[0] * price;
VolumeWeightedSquaredPrice += Volume[0] * Math.Pow(price, 2);
2024-06-25 18:32:22 +00:00
TotalVolume += Volume[0];
volumeWeightedPrice = VolumeWeightedPrice;
volumeWeightedSquaredPrice = VolumeWeightedSquaredPrice;
totalVolume = TotalVolume;
2024-06-25 18:32:22 +00:00
}
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];
2024-06-25 18:32:22 +00:00
}
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);
2024-06-25 18:32:22 +00:00
}
else if (TICK_BARS == BarsInProgress)
{
double price = GetPrice(TICK_BARS);
2024-06-25 18:32:22 +00:00
TotalVolumeTick += Volumes[TICK_BARS][0];
VolumeWeightedPriceTick += Volumes[TICK_BARS][0] * price;
VolumeWeightedSquaredPriceTick += Volumes[TICK_BARS][0] * Math.Pow(price, 2);
2024-06-25 18:32:22 +00:00
}
}
public override string DisplayName
{
get { return Name; }
}
#region Properties
[TypeConverter(typeof(VWAPPrecisionConverter))]
[PropertyEditor("NinjaTrader.Gui.Tools.StringStandardValuesEditorKey")]
[Display(Name = "Precision", Description = "Precision with which VWAP is calculated", Order = 1, GroupName = "VWAP")]
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", Order = 2, GroupName = "VWAP")]
public VWAPPrice Price
{ get; set; }
[Range(0.1, double.MaxValue)]
[Display(Name = "Standard Deviation Multiplier", Description = "Multiplier for standard deviation bands", Order = 3, GroupName = "VWAP")]
public double StdDevMultiplier
{ get; set; }
2024-06-25 18:32:22 +00:00
#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 Period";
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
{
2024-06-27 16:21:42 +00:00
private VWAP[] cacheVWAP;
public VWAP VWAP()
2024-06-25 18:32:22 +00:00
{
2024-06-27 16:21:42 +00:00
return VWAP(Input);
2024-06-25 18:32:22 +00:00
}
2024-06-27 16:21:42 +00:00
public VWAP VWAP(ISeries<double> input)
2024-06-25 18:32:22 +00:00
{
2024-06-27 16:21:42 +00:00
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);
2024-06-25 18:32:22 +00:00
}
}
}
namespace NinjaTrader.NinjaScript.MarketAnalyzerColumns
{
public partial class MarketAnalyzerColumn : MarketAnalyzerColumnBase
{
2024-06-27 16:21:42 +00:00
public Indicators.VWAP VWAP()
2024-06-25 18:32:22 +00:00
{
2024-06-27 16:21:42 +00:00
return indicator.VWAP(Input);
2024-06-25 18:32:22 +00:00
}
2024-06-27 16:21:42 +00:00
public Indicators.VWAP VWAP(ISeries<double> input )
2024-06-25 18:32:22 +00:00
{
2024-06-27 16:21:42 +00:00
return indicator.VWAP(input);
2024-06-25 18:32:22 +00:00
}
}
}
namespace NinjaTrader.NinjaScript.Strategies
{
public partial class Strategy : NinjaTrader.Gui.NinjaScript.StrategyRenderBase
{
2024-06-27 16:21:42 +00:00
public Indicators.VWAP VWAP()
2024-06-25 18:32:22 +00:00
{
2024-06-27 16:21:42 +00:00
return indicator.VWAP(Input);
2024-06-25 18:32:22 +00:00
}
2024-06-27 16:21:42 +00:00
public Indicators.VWAP VWAP(ISeries<double> input )
2024-06-25 18:32:22 +00:00
{
2024-06-27 16:21:42 +00:00
return indicator.VWAP(input);
2024-06-25 18:32:22 +00:00
}
}
}
#endregion