Overall Statistics
Total Trades
2011
Average Win
0.71%
Average Loss
-0.71%
Compounding Annual Return
24.147%
Drawdown
19.300%
Expectancy
0.401
Net Profit
1968.383%
Sharpe Ratio
1.068
Probabilistic Sharpe Ratio
60.919%
Loss Rate
30%
Win Rate
70%
Profit-Loss Ratio
1.00
Alpha
0.162
Beta
0.401
Annual Standard Deviation
0.183
Annual Variance
0.033
Information Ratio
0.576
Tracking Error
0.197
Treynor Ratio
0.486
Total Fees
$7060.35
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Data.Fundamental;
using QuantConnect.Data.Market;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Indicators;
using QuantConnect.Orders;
using QuantConnect.Orders.Slippage;
using QuantConnect.Securities;

namespace QuantConnect.Algorithm.CSharp
{
    public class FundamentalAlgorithm : QCAlgorithm
    {
        Resolution resolution = Resolution.Daily;
        int num_screener = 100;
        int num_stocks = 10;
        int formation_days = 200;

        IEnumerable<Symbol> symbols = null;
        Symbol spy;
        Symbol ief;

        bool rebalence_flag;
        bool first_month_trade_flag;
        bool trade_flag;

        public override void Initialize()
        {
            SetStartDate(2004, 1, 1);
            SetEndDate(2018, 1, 1);

            SetCash(50000);

            this.spy = AddEquity("SPY", Resolution.Minute).Symbol;
            this.ief = AddEquity("IEF", resolution).Symbol;

            UniverseSettings.Resolution = this.resolution;
            SetUniverseSelection(new FineFundamentalUniverseSelectionModel(CoarseSelectionFunction, FineSelectionFunction));

            Schedule.On(DateRules.MonthStart("SPY"), TimeRules.At(0, 0), () =>
            {
                monthly_rebalance();
            });

            Schedule.On(DateRules.MonthStart("SPY"), TimeRules.At(10, 0), () =>
            {
                Rebalance();
            });

            // rebalance the universe selection once a month
            this.rebalence_flag = false;

            // make sure to run the universe selection at the start of the algorithm even it's not the manth start
            this.first_month_trade_flag = true;
            this.trade_flag = false;
        }

        private void monthly_rebalance()
        {
            this.rebalence_flag = true;
        }

        private void Rebalance()
        {
            var spy_hist = History(this.spy, 120, this.resolution);
            var spyMeanPrice = spy_hist.Select(bar => bar.Close).ToArray().Average();
            var spyPrice = Securities[this.spy].Price;

            if (spyPrice < spyMeanPrice)
            {
                foreach (Symbol symbol in this.Portfolio.Keys)
                {
                    if (symbol.Value != "IEF")
                    {
                        this.Liquidate();
                    }
                }

                SetHoldings("IEF", 1);

                Plot("Investments", "IEF", 1);
                Plot("Investments", "Stocks", 0);
                return;
            }
            else
            {
                if (this.symbols == null)
                    return;

                var chosen = this.calc_return(this.symbols);
                foreach(Symbol symbol in this.Portfolio.Keys)
                {
                    if(symbol.Value == "SPY")
                    {
                        continue;
                    }

                    if (!chosen.Contains(symbol))
                    {
                        this.SetHoldings(symbol, 0);
                    }
                }

                if (chosen.Count() > 0)
                {
	                Plot("Investments", "IEF", 0);
	                Plot("Investments", "Stocks", 1);

                    decimal weight = 0.99m / chosen.Count();
                    foreach (var symbol in chosen)
                    {
                        this.SetHoldings(symbol, weight);
//                        weight = weight - 0.002m;
//                        if(weight <= 0)
//                        {
//                        	break;
//                        }
                    }
                }
            }
        }

        private IEnumerable<Symbol> calc_return(IEnumerable<Symbol> stocks)
        {
            // calculate return for each stock
            ConcurrentDictionary<Symbol, decimal> returnPerStock = new ConcurrentDictionary<Symbol, decimal>();
            foreach (Symbol symbol in stocks)
            {
                var hist = History(symbol, this.formation_days, this.resolution);
                if (hist.Count() > 0)
                {
                    var last = hist.Last().Close;
                    var first = hist.First().Close;

                    var return1 = (last - first) / first;
                    returnPerStock.AddOrUpdate(symbol, return1);
                }
            }

            var sortedDict = from entry in returnPerStock orderby entry.Value descending select entry;

            return sortedDict.Select(entry => entry.Key).Take(this.num_stocks);
        }

        public IEnumerable<Symbol> CoarseSelectionFunction(IEnumerable<CoarseFundamental> coarse)
        {
            if (this.rebalence_flag || this.first_month_trade_flag)
            {
                var sortedByDollarVolume = coarse
                    .Where(x => x.HasFundamentalData)
                    .Where(x => x.Price > 5)
                    .OrderByDescending(x => x.DollarVolume);

                // take the top entries from our sorted collection
                var numberOfSymbolsCoarse = 200;
                var top = sortedByDollarVolume.Take(numberOfSymbolsCoarse);

                // we need to return only the symbol objects
                return top.Select(x => x.Symbol);
            }else{
                return this.symbols;
            }
        }

        public IEnumerable<Symbol> FineSelectionFunction(IEnumerable<FineFundamental> fine)
        {
            if (this.rebalence_flag || this.first_month_trade_flag)
            {
                var selected = fine
                            .Where(x => x.EarningReports.BasicAverageShares.ThreeMonths * (x.EarningReports.BasicEPS.TwelveMonths * x.ValuationRatios.PERatio) > 2e9m)
                            .OrderByDescending(x => x.ValuationRatios.EVToEBITDA).Take(this.num_screener);

                //                                .Where(x => x.CompanyReference.CountryId == "USA")
                //                                .Where(x => !x.CompanyReference.IsLimitedPartnership)
                //                               .Where(x => !x.CompanyReference.IsREIT)
                //                               .Where(x => !x.SecurityReference.IsDepositaryReceipt)
                //               .Where(x => x.SecurityReference.IsPrimaryShare)
                //               .Where(x => x.SecurityReference.ShareClassStatus == "A")
                //               .Where(x => x.SecurityReference.CurrencyId == "USD")
                //               .Where(x => x.SecurityReference.SecurityType == "ST00000001")
                //              .Where(x => x.SecurityReference.ExchangeId != "OTC")
                //             .Where(x => !x.Symbol.Value.Contains("_WI"));
                this.symbols = selected.Select(x => x.Symbol);
                this.rebalence_flag = false;
                this.first_month_trade_flag = false;
                this.trade_flag = true;

                return this.symbols;
            }else{
                return this.symbols;
            }
        }

        public override void OnData(Slice data)
        {
        }
    }
}