Overall Statistics
Total Trades
996
Average Win
0.40%
Average Loss
-0.44%
Compounding Annual Return
11.138%
Drawdown
16.700%
Expectancy
0.478
Net Profit
187.754%
Sharpe Ratio
0.88
Probabilistic Sharpe Ratio
28.989%
Loss Rate
22%
Win Rate
78%
Profit-Loss Ratio
0.89
Alpha
0.103
Beta
-0.045
Annual Standard Deviation
0.111
Annual Variance
0.012
Information Ratio
-0.142
Tracking Error
0.197
Treynor Ratio
-2.145
Total Fees
$1150.26
using QuantConnect.Algorithm.Framework.Alphas;
using QuantConnect.Algorithm.Framework.Execution;
using QuantConnect.Algorithm.Framework.Portfolio;
using QuantConnect.Algorithm.Framework.Selection;
using QuantConnect.Data;
using QuantConnect.Data.Fundamental;
using QuantConnect.Data.UniverseSelection;
using System;
using System.Collections.Generic;
using System.Linq;

namespace QuantConnect.Algorithm.CSharp
{
    public class MagicFormula : QCAlgorithm
    {
        private DateTime _lastRebalance;
        private bool _rebalance = true;

        private bool _isLiquidating = false;
        private DateTime _liquidationDate;
        private DateTime _purchaseDate;

        public override void Initialize()
        {
            SetStartDate(2011, 1, 1);
            SetEndDate(2021, 1, 1);
            SetCash(100000);
            SetExecution(new ImmediateExecutionModel());
            SetAlpha(new ConstantAlphaModel(InsightType.Price, InsightDirection.Up, TimeSpan.FromDays(365)));
            UniverseSettings.Resolution = Resolution.Daily;
            SetPortfolioConstruction(new ConstantMaximumEqualWeightPortfolio());
            AddUniverseSelection(new FineFundamentalUniverseSelectionModel(SelectCoarse, SelectFine));
            _liquidationDate = new DateTime(Time.Year, 12, 10);
            _purchaseDate = new DateTime(Time.Year + 1, 1, 10);
            Schedule.On(DateRules.EveryDay(), TimeRules.At(3, 30, 1), Update);
        }

        public IEnumerable<Symbol> SelectCoarse(IEnumerable<CoarseFundamental> coarse)
        {
            if (_isLiquidating)
                return Enumerable.Empty<Symbol>();

            if (!_rebalance)
                return Universe.Unchanged;

            return from x in coarse
                   where x.HasFundamentalData
                   where x.Price > 5
                   where x.DollarVolume > 300000
                   select x.Symbol;
        }
        public IEnumerable<Symbol> SelectFine(IEnumerable<FineFundamental> fine)
        {
            if (_isLiquidating)
                return Enumerable.Empty<Symbol>();

            if (!_rebalance)
                return Universe.Unchanged;

            var candidates = from x in fine
                             let ebit = x.FinancialStatements.IncomeStatement.EBIT
                             let workingCapital = x.FinancialStatements.BalanceSheet.WorkingCapital
                             let netAssets = x.FinancialStatements.BalanceSheet.NetTangibleAssets
                             where workingCapital != 0 || netAssets != 0
                             let roc = ebit / (workingCapital + netAssets)
                             where x.OperationRatios.ROIC.OneYear > 0.25m
                             //where roc > 2.0m

                             where x.CompanyProfile.HeadquarterCountry == "USA"
                             where x.AssetClassification.MorningstarSectorCode != MorningstarSectorCode.Utilities
                                 && x.AssetClassification.MorningstarSectorCode != MorningstarSectorCode.FinancialServices

                             where x.MarketCap > 0 && x.FinancialStatements.BalanceSheet.TotalDebt != 0

                             //let earningsYield = ebit /
                                //(x.MarketCap + x.FinancialStatements.BalanceSheet.TotalDebt - 
                                    //x.FinancialStatements.BalanceSheet.CashAndCashEquivalents)
                             let earningsYield = x.ValuationRatios.EarningYield

                             where earningsYield > 0.05m
                             orderby earningsYield
                             select x;

            _rebalance = false;
            _lastRebalance = Time;
            var newSymbols = from x in candidates.Take(10)
                             where !ActiveSecurities.ContainsKey(x.Symbol)
                             select x.Symbol;
            var previousSecurities = ActiveSecurities.Values.ToList();
            var allSymbols = (from x in previousSecurities
                    select x.Symbol).ToList();
            allSymbols.AddRange(newSymbols);
            return allSymbols;
        }

        private bool ShouldLiquidate()
        {
            return (Time > _liquidationDate && !_isLiquidating);
        }

        private void ReinvestOnNewYear()
        {
            _isLiquidating = false;
            _rebalance = true;
            _liquidationDate = new DateTime(Time.Year, _liquidationDate.Month, _liquidationDate.Day);
            _purchaseDate = new DateTime(Time.Year + 1, _purchaseDate.Month, _purchaseDate.Day);
            Debug("Starting investing for the year: " + Time.Year);
        }

        public void Update()
        {
            if (ShouldLiquidate())
            {
                Debug("Liquidating For End Of Year: " + Time);
                _isLiquidating = true;
                Schedule.On(DateRules.On(_purchaseDate), TimeRules.At(11, 0), () =>
                {
                    ReinvestOnNewYear();
                });
                return;
            }
            if (Time > _lastRebalance.AddMonths(3) && !_isLiquidating)
            {
                Debug(Time + ": Adding more stocks!");
                _rebalance = true;
                return;
            }
        }

        public class ConstantMaximumEqualWeightPortfolio : PortfolioConstructionModel
        {
            private int _maximumPositions;

            public ConstantMaximumEqualWeightPortfolio(int maximumPositions = 30) : base(Time => null) 
            {
                _maximumPositions = maximumPositions;
            }

            protected override Dictionary<Insight, double> DetermineTargetPercent(List<Insight> activeInsights)
            {
                var result = new Dictionary<Insight, double>();
                var percentPerPosition = 1m / _maximumPositions;

                foreach (var insight in activeInsights)
                {
                	result[insight] =
                    (double)((int)(insight.Direction) * percentPerPosition);
                }
                return result;
            }

            protected override bool ShouldCreateTargetForInsight(Insight insight)
            {
                return !Algorithm.Portfolio[insight.Symbol].Invested;
            }
        }
    }
}