Overall Statistics
namespace QuantConnect {   
  
    public class SellHigh7s : QCAlgorithm {
        private int _month = -1;
        
        public override void Initialize() 
        {
            SetStartDate(2019, 1, 1);         
            SetEndDate(DateTime.Now);
            SetCash(100000);

            UniverseSettings.Resolution = Resolution.Daily;
            AddUniverseSelection(new FineFundamentalUniverseSelectionModel(SelectCoarse, SelectFine));
            
            AddAlpha( new MyAlphaModel() );
            SetPortfolioConstruction( new EqualWeightingPortfolioConstructionModel() );
            AddRiskManagement( new MaximumDrawdownPercentPerSecurity() );
            SetExecution( new ImmediateExecutionModel() ); 
        }
        
        IEnumerable<Symbol> SelectCoarse(IEnumerable<CoarseFundamental> coarse) {
            const int size = 9;
            
            if (Time.Month == _month)
                return Universe.Unchanged;

            // The securities with the most dollar volume
            return (from x in coarse
                    where x.Volume > 0 && x.Price > 0
                    orderby x.DollarVolume descending
                    select x.Symbol).Take(size).ToList();
        }
        
        IEnumerable<Symbol> SelectFine(IEnumerable<FineFundamental> fine) {
            _month = Time.Month;

            return fine.Select(f => f.Symbol);
        }
    }
    

    // Algorithm framework model that produces insights
    public class MyAlphaModel : IAlphaModel, INotifiedSecurityChanges {
        private readonly Dictionary<Symbol, RelativeStrengthIndex> _rsiBySymbol = new Dictionary<Symbol, RelativeStrengthIndex>();
        private readonly int _period = 1;
        private readonly Resolution _resolution = Resolution.Daily;
        private int _month = -1;
        
        // Updates this alpha model with the latest data from the algorithm.
        // This is called each time the algorithm receives data for subscribed securities
        public IEnumerable<Insight> Update(QCAlgorithm algorithm, Slice data) {
            // Generate insights on the securities in universe.
            var insights = new List<Insight>();
            
            if (algorithm.Time.Month == _month)
                return insights;
            _month = algorithm.Time.Month;
            
            // Insights for 22 days
            var insightPeriod = _resolution.ToTimeSpan().Multiply(22);

            // Long the lowest RSI; Short the highest RSI
            var i = 0;
            var numInsightsPerSide = _rsiBySymbol.Count / 3;
            foreach (KeyValuePair<Symbol, RelativeStrengthIndex> sym in _rsiBySymbol.OrderBy(key => key.Value)) {
                if (i < numInsightsPerSide)
                    insights.Add(Insight.Price(sym.Key, insightPeriod, InsightDirection.Up));
                else if (i >= _rsiBySymbol.Count - numInsightsPerSide)
                    insights.Add(Insight.Price(sym.Key, insightPeriod, InsightDirection.Down));
                i++;
            }
            return insights;
        }
    
        public void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes) {
            // Handle security changes in from your universe model.
            // clean up data for removed securities
            if (changes.RemovedSecurities.Count > 0) {
                var removed = changes.RemovedSecurities.ToHashSet(x => x.Symbol);
                foreach (var subscription in _rsiBySymbol.Keys.ToList())
                    if (removed.Contains(subscription))
                        _rsiBySymbol.Remove(subscription);
            }

            // initialize data for added securities
            var addedSymbols = new List<Symbol>();
            foreach (var added in changes.AddedSecurities)
                if (!_rsiBySymbol.ContainsKey(added.Symbol)) {
                    // Add RSI indicator
                    _rsiBySymbol[added.Symbol] = algorithm.RSI(added.Symbol, _period, MovingAverageType.Simple, _resolution);
                    addedSymbols.Add(added.Symbol);
                }

            if (addedSymbols.Count > 0)
                // warmup our indicators by pushing history through the consolidators
                algorithm.History(addedSymbols, _period, _resolution)
                    .PushThrough(data =>
                    {
                        RelativeStrengthIndex rsi;
                        if (_rsiBySymbol.TryGetValue(data.Symbol, out rsi))
                            rsi.Update(data.EndTime, data.Value);
                    });
        }
    }
}