Overall Statistics
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using QuantConnect.Data;
using QuantConnect.Data.Fundamental;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Indicators;
using QuantConnect.Parameters;

namespace QuantConnect.Algorithm.CSharp
{
    /// <summary>
    /// </summary>
    public class FundamentalFilterExample : QCAlgorithm
    {

        // Max number of symbols to keep from coarse universe selection 
        private const int MaxCoarseSymbols = 1000;

        private readonly Symbol _tlt = QuantConnect.Symbol.Create("TLT", SecurityType.Equity, Market.USA);
        private readonly Symbol _spy = QuantConnect.Symbol.Create("SPY", SecurityType.Equity, Market.USA);

        // initialize our security changes to nothing
        SecurityChanges _changes = SecurityChanges.None;
        
        public override void Initialize()
        {
            SetStartDate(2015, 01, 01); // Set Start Date
            SetEndDate(2016, 01, 01); // Set End Date
            SetCash(100000); // Set Strategy Cash

            AddSecurity(SecurityType.Equity, _tlt, Resolution.Daily);
            AddSecurity(SecurityType.Equity, _spy, Resolution.Daily);

            // Schedule.On(DateRules.MonthStart(), TimeRules.At(9, 50), () => { Rebalance(); });
			
			UniverseSettings.Resolution = Resolution.Daily;
            AddUniverse(CoarseSelectionFunction, FineSelectionFunction);
            
        }
        
        // this event fires whenever we have changes to our universe
        public override void OnSecuritiesChanged(SecurityChanges changes)
        {
            _changes = changes;
        }

        // sort the data by daily dollar volume and take the top 'NumberOfSymbolsCoarse'
        public IEnumerable<Symbol> CoarseSelectionFunction(IEnumerable<CoarseFundamental> coarse)
        {
            // select only symbols with fundamental data and sort descending by daily dollar volume
            var sortedByDollarVolume = coarse
                .Where(x => x.HasFundamentalData && x.Price > 1.0m && x.Volume > 0)
                .OrderByDescending(x => x.DollarVolume);


            // take the top entries from our sorted collection
            var top = sortedByDollarVolume.Take(MaxCoarseSymbols);
            Debug(Time + " coarse selection count: " + top.Count());
            
            // we need to return only the symbol objects
            return top.Select(x => x.Symbol);
        }

        public IEnumerable<Symbol> FineSelectionFunction(IEnumerable<FineFundamental> fine)
        {
            // at this point we already have:
            // * top X symbols by dollar volume
            // * price > $1.0
            // * volume > 0

            // FIXME - how do we cap 30% max allocated to a single sector without sectors??
            // FIXME - Why isn't market cap (or at least shares outstanding) available?

            /*
             	Quantopian Q500US Rules
             	-------------------------
                Q500US(minimum_market_cap=500000000)
                A default universe containing approximately 500 US equities each day.

                Constituents are chosen at the start of each calendar month by selecting the
                top 500 “tradeable” stocks by 200-day average dollar volume, capped at 30% of
                equities allocated to any single sector

                A stock is considered “tradeable” if it meets the following criteria:

                The stock must be the primary share class for its company.
                The company issuing the stock must have known market capitalization.
                The stock must not be a depository receipt.
                The stock must not be traded over the counter (OTC).
                The stock must not be for a limited partnership.
                The stock must have a known previous-day close price.
                The stock must have had nonzero volume on the previous trading day.
            */
            
         	Debug(Time + " fine universe selection: " + fine.Count());
            
            // Define the approximate Q500US universe
            var filtered = fine.Where(x =>
                {
                	try {
	                    var screen = x.SecurityReference.IsPrimaryShare
	                                 // approximate market cap > 5 billion
	                                 && x.EarningReports.BasicAverageShares.HasValues()
	                                 && x.EarningReports.BasicAverageShares.Value * x.Price > 5e9m
	                                 // no depository receipts
	                                 && !x.SecurityReference.IsDepositaryReceipt
	                                 // no pink sheets
	                                 && x.CompanyReference.PrimaryExchangeID != "OTCPK" &&
	                                 x.CompanyReference.PrimaryExchangeID != "OTCBB"
	                                 // must not be an limited parntership
	                                 && !x.CompanyReference.IsLimitedPartnership
	                                 && !Regex.IsMatch(x.CompanyReference.StandardName, ".*L[. ]?P.?$")
	                                 // non when-issued equities
	                                 && !x.Symbol.EndsWith(".WI");
	                    return screen;
                	} catch (Exception e) {
                		Debug("fine filter exception: " + e);
                		return false;
                	}
                })
                // order decreasing (approximate) market cap
                .OrderByDescending(x => x.EarningReports.BasicAccountingChange.Value * x.Price)
                .Take(500);
                                                           
            Debug("filtered: " + filtered.Count());        
            return filtered.Select(x => x.Symbol);
        }

        public override void OnData(Slice data)
        {
            
            Debug("OnData: " + Time + ", bars: " + data.Count);
            
            // if we have no changes, do nothing
            if (_changes == SecurityChanges.None) return;

            // liquidate removed securities
            foreach (var security in _changes.RemovedSecurities)
            {
                if (security.Invested)
                {
                    Liquidate(security.Symbol);
                }
				Debug("Removing security: " + security.Symbol);
            }

            foreach (var security in _changes.AddedSecurities)
            {
				Debug("Adding security: " + security.Symbol);
            }
            
            // reset
            _changes = SecurityChanges.None;
        }
    }    
}