Overall Statistics
Total Trades
1
Average Win
0%
Average Loss
0%
Compounding Annual Return
53.090%
Drawdown
4.100%
Expectancy
0
Net Profit
23.802%
Sharpe Ratio
3.216
Probabilistic Sharpe Ratio
89.225%
Loss Rate
0%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
0.442
Beta
-0.008
Annual Standard Deviation
0.136
Annual Variance
0.019
Information Ratio
-0.018
Tracking Error
0.194
Treynor Ratio
-52.729
Total Fees
$1.48
Estimated Strategy Capacity
$1100000000.00
using QuantConnect.Data.Market;
using System;



namespace QuantConnect.Algorithm.CSharp
{
    public class CasualSkyBlueJaguar : QCAlgorithm
    {

		private PercentageVolumeOscillator pvo;
		private Symbol symbol;

        public override void Initialize()
        {
            SetStartDate(2020, 10, 26);  //Set Start Date
            SetCash(100000);             //Set Strategy Cash
            
            symbol = AddEquity("SPY", Resolution.Daily).Symbol;
            
            var fastMovingAveragePeriods = 10;
            var slowMovingAveragePeriods = 15;
            pvo = new PercentageVolumeOscillator(
                    $"{nameof(PercentageVolumeOscillator)}({symbol},{fastMovingAveragePeriods},{slowMovingAveragePeriods})", 
                    fastMovingAveragePeriods, 
                    slowMovingAveragePeriods);
            RegisterIndicator(symbol, pvo, Resolution.Daily);
        }

        /// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
        /// Slice object keyed by symbol containing the stock data
        public override void OnData(Slice data)
        {
            if (!Portfolio.Invested)
            {
               SetHoldings("SPY", 1);
               Debug("Purchased Stock");
            }
            
            // To update the indicator manually... (need to remove `RegisterIndicator`)
            //Plot("Update", "IsReady", Convert.ToInt32(p.Update(data[s])));
            
            Plot("PercentageVolumeOscillator", "Value", pvo.Current.Value);
        }

    }
}



namespace QuantConnect.Indicators
{
    /// <summary>
    /// This indicator computes the Percentage Volume Ocillator (PVO)
    /// PVO = ([Fast moving average of volume] - [Slow moving average of volume]) / [Slow moving average of volume]
    /// </summary>
    public class PercentageVolumeOscillator : TradeBarIndicator, IIndicatorWarmUpPeriodProvider
    {
        private readonly SimpleMovingAverage _slowMovingAverageVolume;
        private readonly SimpleMovingAverage _fastMovingAverageVolume;

        /// <summary>
        /// Creates a new PercentageVolumeOscillator indicator using the specified periods
        /// </summary>
        public PercentageVolumeOscillator(
            string name, 
            int fastMovingAveragePeriods = 5, 
            int slowMovingAveragePeriods = 20) // Try 14 and 28 for Resolution.Daily and longer intervals
            : base(name)
        {
            _slowMovingAverageVolume = new SimpleMovingAverage($"{name}_SlowMovingAverageVolume", slowMovingAveragePeriods);
            _fastMovingAverageVolume = new SimpleMovingAverage($"{name}_FastMovingAverageVolume", fastMovingAveragePeriods);
        }

        /// <summary>
        /// Creates a new PercentageVolumeOscillator indicator using the specified periods
        /// </summary>
        public PercentageVolumeOscillator(int fastMovingAveragePeriods, int slowMovingAveragePeriods)
            : this($"{nameof(PercentageVolumeOscillator)}({fastMovingAveragePeriods},{slowMovingAveragePeriods})",
                  fastMovingAveragePeriods,
                  slowMovingAveragePeriods) { }

        /// <summary>
        /// Gets a flag indicating when this indicator is ready and fully initialized
        /// </summary>
        public override bool IsReady => _slowMovingAverageVolume.IsReady && _fastMovingAverageVolume.IsReady;

        /// <summary>
        /// Resets this indicator to its initial state
        /// </summary>
        public override void Reset()
        {
            _slowMovingAverageVolume.Reset();
            _fastMovingAverageVolume.Reset();
            base.Reset();
        }

        /// <summary>
        /// Required period, in data points, for the indicator to be ready and fully initialized.
        /// </summary>
        public int WarmUpPeriod => _slowMovingAverageVolume.WarmUpPeriod;
        /// <summary>
        /// Computes the next value of this indicator from the given state
        /// </summary>
        /// <param name="input">The input given to the indicator</param>
        /// <returns>A new value for this indicator</returns>
        protected override decimal ComputeNextValue(TradeBar input)
        {
            _slowMovingAverageVolume.Update(input.Time, input.Volume);
            _fastMovingAverageVolume.Update(input.Time, input.Volume);

            if (_slowMovingAverageVolume == 0)
                return 0; // To avoid a divide-by-zero error
            
            var difference = (_fastMovingAverageVolume - _slowMovingAverageVolume);
            var percentageChange = difference / _slowMovingAverageVolume * 100;

            return percentageChange;
        }
    }
}