using System;
using QuantConnect.Data;
using QuantConnect.Data.Market;
namespace QuantConnect.Indicators
{
///
/// Defines the canonical intraday VWAP indicator
///
public class IntradayVwap : IndicatorBase
{
private DateTime _lastDate;
private decimal _sumOfVolume;
private decimal _sumOfPriceTimesVolume;
///
/// Gets a flag indicating when this indicator is ready and fully initialized
///
public override bool IsReady => _sumOfVolume > 0;
///
/// Initializes a new instance of the class
///
/// The name of the indicator
public IntradayVwap(string name)
: base(name)
{
}
///
/// Computes the new VWAP
///
protected override IndicatorResult ValidateAndComputeNextValue(BaseData input)
{
decimal volume, averagePrice;
if (!TryGetVolumeAndAveragePrice(input, out volume, out averagePrice))
{
return new IndicatorResult(0, IndicatorStatus.InvalidInput);
}
// reset vwap on daily boundaries
if (_lastDate != input.EndTime.Date)
{
_sumOfVolume = 0m;
_sumOfPriceTimesVolume = 0m;
_lastDate = input.EndTime.Date;
}
// running totals for Σ PiVi / Σ Vi
_sumOfVolume += volume;
_sumOfPriceTimesVolume += averagePrice * volume;
if (_sumOfVolume == 0m)
{
// if we have no trade volume then use the current price as VWAP
return input.Value;
}
return _sumOfPriceTimesVolume / _sumOfVolume;
}
///
/// Computes the next value of this indicator from the given state.
/// NOTE: This must be overriden since it's abstract in the base, but
/// will never be invoked since we've override the validate method above.
///
/// The input given to the indicator
/// A new value for this indicator
protected override decimal ComputeNextValue(BaseData input)
{
throw new NotImplementedException($"{nameof(IntradayVwap)}.{nameof(ComputeNextValue)} should never be invoked.");
}
///
/// Determines the volume and price to be used for the current input in the VWAP computation
///
protected bool TryGetVolumeAndAveragePrice(BaseData input, out decimal volume, out decimal averagePrice)
{
var tick = input as Tick;
if (tick?.TickType == TickType.Trade)
{
volume = tick.Quantity;
averagePrice = tick.LastPrice;
return true;
}
var tradeBar = input as TradeBar;
if (tradeBar?.IsFillForward == false)
{
volume = tradeBar.Volume;
averagePrice = (tradeBar.High + tradeBar.Low + tradeBar.Close) / 3m;
return true;
}
volume = 0;
averagePrice = 0;
return false;
}
}
}