Overall Statistics
namespace QuantConnect.Algorithm.CSharp
{
    public class AlphaFiveVolatilityUniverse : QCAlgorithm
    {
		private Dictionary<Symbol,AssetData> _universe = new Dictionary<Symbol,AssetData>();
        
        public override void Initialize()
        {
            //1. Required: Five years of backtest history
        	SetStartDate(2014, 1, 1);
        
        	//2. Required: Alpha Streams Models:
        	SetBrokerageModel(BrokerageName.AlphaStreams);
        
	        //3. Required: Significant AUM Capacity
	        SetCash(1000000);
	        
	        //4. Select Desired ETF Universe
	        // See more: https://www.quantconnect.com/docs/algorithm-reference/universes
	        UniverseSettings.Resolution = Resolution.Hour;
			SetUniverseSelection(new VolatilityETFUniverse());
			
			//5. Set Relevent Benchmark
	        var reference = "SPLV";
	        AddEquity(reference, Resolution.Hour);
	        SetBenchmark("SPY");
	
	        // Demonstration: Consolidation
	        // See more: https://www.quantconnect.com/docs/algorithm-reference/consolidating-data
	        Consolidate(reference, CalendarType.Weekly, ConsolidationDemo);
	        
	        // Demonstration: Scheduled Events
	        // See more: https://www.quantconnect.com/docs/algorithm-reference/scheduled-events
	        Schedule.On(DateRules.Every(DayOfWeek.Monday), TimeRules.AfterMarketOpen(reference, 30), ScheduleDemo);
	        
	        // --------
	        // Optional: Framework Algorithm Models
	        SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel());
	        SetExecution(new ImmediateExecutionModel());
	        // --------
        }

        /// 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)
        {
        	// Manually update the Indicators
        	foreach (var key in _universe.Keys)
        	{
        		if (data.Bars.ContainsKey(key))
        		{
        			_universe[key].update(data[key].EndTime, data[key].Close);
        		}
        	}
        }
        
        public override void OnSecuritiesChanged(SecurityChanges changes)
        {
        	foreach (var s in changes.AddedSecurities)
        	{
        		if (!_universe.ContainsKey(s.Symbol))
        		{
        			var history = History(s.Symbol, 30, Resolution.Hour);
        			_universe.Add(s.Symbol, new AssetData(s.Symbol,history));
        		}
        	}
        }
        
        public void ScheduleDemo()
        {
            foreach(var kvp in _universe)
            {
                var symbol = kvp.Key;
                var assetData = kvp.Value;
                var price = ActiveSecurities[symbol].Price;
                
                if (assetData.IsReady() && assetData.Deviating(price))
                {
                    EmitInsights(Insight.Price(symbol, TimeSpan.FromDays(3), InsightDirection.Up));
                }
            }
        }
        
        public void ConsolidationDemo(TradeBar bar)
        {
        	Debug($"{Time} :: {bar.Time} {bar.Close}");
        }
    }
    
    public class AssetData
    {
    	private StandardDeviation _std = new StandardDeviation(30);
    	private SimpleMovingAverage _mean = new SimpleMovingAverage(7);
    	private Symbol _symbol;
    	
    	public AssetData(Symbol symbol, IEnumerable<TradeBar> history)
    	{
    		_symbol = symbol;
    		foreach (var bar in history)
    		{
    			update(bar.EndTime, bar.Close);
    		}
    	}
    	
    	public bool IsReady()
    	{
    		return _std.IsReady;
    	}
    	
    	public void update(DateTime time, decimal price)
    	{
    		_std.Update(time, price);
			_mean.Update(time, price);
    	}
    	
    	public bool Deviating(decimal price)
    	{
    		if (_std == 0)
    		{
    			return false;
    		}
    		return ((price - _mean) / _std) < -3;
    	}
    }
}