Lean  $LEAN_TAG$
IntradayVwap.cs
1 using System;
2 using QuantConnect.Data;
4 
6 {
7  /// <summary>
8  /// Defines the canonical intraday VWAP indicator
9  /// </summary>
10  public class IntradayVwap : IndicatorBase<BaseData>
11  {
12  private DateTime _lastDate;
13  private decimal _sumOfVolume;
14  private decimal _sumOfPriceTimesVolume;
15 
16  /// <summary>
17  /// Gets a flag indicating when this indicator is ready and fully initialized
18  /// </summary>
19  public override bool IsReady => _sumOfVolume > 0;
20 
21  /// <summary>
22  /// Initializes a new instance of the <see cref="IntradayVwap"/> class
23  /// </summary>
24  /// <param name="name">The name of the indicator</param>
25  public IntradayVwap(string name)
26  : base(name)
27  {
28  }
29 
30  /// <summary>
31  /// Computes the new VWAP
32  /// </summary>
34  {
35  decimal volume, averagePrice;
36  if (!TryGetVolumeAndAveragePrice(input, out volume, out averagePrice))
37  {
38  return new IndicatorResult(0, IndicatorStatus.InvalidInput);
39  }
40 
41  // reset vwap on daily boundaries
42  if (_lastDate != input.EndTime.Date)
43  {
44  _sumOfVolume = 0m;
45  _sumOfPriceTimesVolume = 0m;
46  _lastDate = input.EndTime.Date;
47  }
48 
49  // running totals for Σ PiVi / Σ Vi
50  _sumOfVolume += volume;
51  _sumOfPriceTimesVolume += averagePrice * volume;
52 
53  if (_sumOfVolume == 0m)
54  {
55  // if we have no trade volume then use the current price as VWAP
56  return input.Value;
57  }
58 
59  return _sumOfPriceTimesVolume / _sumOfVolume;
60  }
61 
62  /// <summary>
63  /// Computes the next value of this indicator from the given state.
64  /// NOTE: This must be overriden since it's abstract in the base, but
65  /// will never be invoked since we've override the validate method above.
66  /// </summary>
67  /// <param name="input">The input given to the indicator</param>
68  /// <returns>A new value for this indicator</returns>
69  protected override decimal ComputeNextValue(BaseData input)
70  {
71  throw new NotImplementedException($"{nameof(IntradayVwap)}.{nameof(ComputeNextValue)} should never be invoked.");
72  }
73 
74  /// <summary>
75  /// Determines the volume and price to be used for the current input in the VWAP computation
76  /// </summary>
77  protected bool TryGetVolumeAndAveragePrice(BaseData input, out decimal volume, out decimal averagePrice)
78  {
79  var tick = input as Tick;
80 
81  if (tick?.TickType == TickType.Trade)
82  {
83  volume = tick.Quantity;
84  averagePrice = tick.LastPrice;
85  return true;
86  }
87 
88  var tradeBar = input as TradeBar;
89  if (tradeBar?.IsFillForward == false)
90  {
91  volume = tradeBar.Volume;
92  averagePrice = (tradeBar.High + tradeBar.Low + tradeBar.Close) / 3m;
93  return true;
94  }
95 
96  volume = 0;
97  averagePrice = 0;
98  return false;
99  }
100  }
101 }