Overall Statistics
Total Trades
90
Average Win
0.63%
Average Loss
-0.74%
Compounding Annual Return
41.182%
Drawdown
9.800%
Expectancy
0.543
Net Profit
41.182%
Sharpe Ratio
2.715
Probabilistic Sharpe Ratio
93.223%
Loss Rate
17%
Win Rate
83%
Profit-Loss Ratio
0.85
Alpha
0.345
Beta
-0.049
Annual Standard Deviation
0.125
Annual Variance
0.016
Information Ratio
1.323
Tracking Error
0.177
Treynor Ratio
-7.01
Total Fees
$125.95
using QuantConnect.Algorithm.Framework.Alphas;
using QuantConnect.Algorithm.Framework.Execution;
using QuantConnect.Algorithm.Framework.Portfolio;
using QuantConnect.Algorithm.Framework.Risk;
using QuantConnect.Algorithm.Framework.Selection;
using QuantConnect.Data;
using QuantConnect.Data.Fundamental;
using QuantConnect.Data.Market;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Indicators;
using QuantConnect.Orders;
using QuantConnect.Securities;
using System;
using System.Collections.Generic;
using System.Linq;

namespace QuantConnect.Algorithm.CSharp
{
    public class TachyonOptimizedRegulators : QCAlgorithm
    {
        private PriceIndexUniverseSelection _universeSelection;

        private DateTime lastRebalance;
        public List<Symbol> symbols = new List<Symbol>();

        public override void Initialize()
        {
            SetStartDate(2016, 1, 1);
            SetEndDate(2017, 1, 1);
            SetCash(100000);
            UniverseSettings.Resolution = Resolution.Daily;
            SetUniverseSelection(_universeSelection = new PriceIndexUniverseSelection());
            AddAlpha(new PriceIndexAlphaModel());
            SetPortfolioConstruction(new PriceIndexPortfolioConstruction());
            SetExecution(new PriceIndexExecutionModel());
            AddRiskManagement(new SpyRedCrossRiskManagementModel(this));
            Rebalance();
        }

        public override void OnData(Slice slice)
        {
            if (Time > lastRebalance.AddMonths(6))
            {
                Rebalance();
            }
        }

        public void Rebalance() 
        {
            lastRebalance = Time;
            if (Portfolio.Invested)
            {
                Debug(Time + ": Liquidating portfolio!");
                Liquidate();
            }
            _universeSelection.Rebalance();
        }

        public override void OnEndOfAlgorithm()
        {
            Liquidate();
        }

        public class PriceIndexUniverseSelection : FundamentalUniverseSelectionModel
        {
            private bool rebalance = false;

            public PriceIndexUniverseSelection() : base(true, null, null)
            { }

            public override IEnumerable<Symbol> SelectCoarse(QCAlgorithm algo, IEnumerable<CoarseFundamental> coarse)
            {
                TachyonOptimizedRegulators myAlgo = (TachyonOptimizedRegulators)algo;
                if (!rebalance)
                    return algo.Universe.Unchanged;
                var stocks = (from c in coarse
                              where c.DollarVolume > 300000 &&
                                    c.DollarVolume < 2000000 &&
                                    c.Price > 5
                              where c.HasFundamentalData
                              orderby c.DollarVolume descending
                              select c.Symbol).Take(500);
                return stocks;
            }

            public override IEnumerable<Symbol> SelectFine(QCAlgorithm algo, IEnumerable<FineFundamental> fine)
            {
                TachyonOptimizedRegulators myAlgo = (TachyonOptimizedRegulators)algo;
                if (!rebalance)
                    return algo.Universe.Unchanged;
                myAlgo.symbols.Clear();
                var sortedByPriceIndex = from stock in fine
                                         where stock.MarketCap < 1000000000
                                         let history = algo.History<TradeBar>(stock.Symbol, new TimeSpan(180, 0, 0), Resolution.Daily)
                                         where history.Count() > 0
                                         let priceIndex = history.Last().Close - history.First().Close
                                         orderby priceIndex
                                         descending
                                         select stock;
                int numberOfStocksToPick = (int)Math.Round(sortedByPriceIndex.Count() * 0.2);
                var highestPriceIndexStocks = sortedByPriceIndex.Take(numberOfStocksToPick);
                rebalance = false;
                myAlgo.symbols = (from x in highestPriceIndexStocks
                        orderby x.ValuationRatios.PBRatio
                        select x.Symbol).Take(30).ToList();
                algo.Debug(myAlgo.Time + ": Succesfully chose " + myAlgo.symbols.Count + " new stocks!");
                return myAlgo.symbols;
            }

            public void Rebalance()
            {
                rebalance = true;
            }
        }

        public class PriceIndexAlphaModel : IAlphaModel
        {
            public void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes) { }

            public IEnumerable<Insight> Update(QCAlgorithm algorithm, Slice data)
            {
                List<Insight> insights = new List<Insight>();
                TachyonOptimizedRegulators myAlgo = (TachyonOptimizedRegulators)algorithm;
                foreach (var symbol in myAlgo.symbols)
                {
                    insights.Add(new Insight(symbol, new TimeSpan(1, 0, 0, 0, 0), InsightType.Price, InsightDirection.Up));
                }
                return insights;
            }
        }

        public class PriceIndexPortfolioConstruction : PortfolioConstructionModel
        {
            public override IEnumerable<IPortfolioTarget> CreateTargets(QCAlgorithm algorithm, Insight[] insights)
            {
                TachyonOptimizedRegulators myAlgo = (TachyonOptimizedRegulators)algorithm;
                List<IPortfolioTarget> targets = new List<IPortfolioTarget>();
                int stocksToBuy = (from x in insights
                                   where x.Direction == InsightDirection.Up
                                   select x).Count();
                if (stocksToBuy > 0)
                {
                    decimal percentToBuy = Convert.ToDecimal((float)1 / (float)stocksToBuy);
                    foreach (var insight in insights)
                    {
                        if (insight.Direction == InsightDirection.Up && !myAlgo.Portfolio[insight.Symbol].Invested)
                        {
                            //Indicated to buy
                            targets.Add(PortfolioTarget.Percent(algorithm, insight.Symbol, percentToBuy));
                        }
                        // This will be triggered by risk management
                        else if (insight.Direction == InsightDirection.Down && myAlgo.Portfolio[insight.Symbol].Invested)
                        {
                            //Indicated to liquidate
                            targets.Add(new PortfolioTarget(insight.Symbol, 0));
                        }
                    }
                }
                return targets;
            }
        }

        public class PriceIndexExecutionModel : ExecutionModel
        {
            public override void Execute(QCAlgorithm algorithm, IPortfolioTarget[] targets)
            {
                foreach (var target in targets)
                {
                    if (target.Quantity > 0)
                    {
                        if (algorithm.Transactions.GetOpenOrders(target.Symbol).Count == 0 && !algorithm.Portfolio[target.Symbol].Invested)
                        {
                            algorithm.MarketOrder(target.Symbol, target.Quantity);
                        }
                    }
                    else
                    {
                        if (algorithm.Portfolio[target.Symbol].Invested)
                        {
                            algorithm.Liquidate(target.Symbol);
                        }
                    }
                }
            }
        }

        public class SpyRedCrossRiskManagementModel : RiskManagementModel
        {
            private ExponentialMovingAverage _signal;
            private Security _spy;

            public SpyRedCrossRiskManagementModel(QCAlgorithm algo)
            {
                _spy = algo.AddEquity("SPY", Resolution.Daily);
                _signal = new ExponentialMovingAverage(200);
                algo.RegisterIndicator(_spy.Symbol, _signal, Resolution.Daily);
            }

            public override IEnumerable<IPortfolioTarget> ManageRisk(QCAlgorithm algo, IPortfolioTarget[] targets)
            {
                List<IPortfolioTarget> newTargets = new List<IPortfolioTarget>();
                var spyClose = algo.Securities["SPY"].Close;
                if (_signal.IsReady && spyClose < _signal)
                {
                    foreach (var target in targets)
                    {
                        newTargets.Add(new PortfolioTarget(target.Symbol, 0));
                    }
                     algo.Debug(algo.Time + ": SPY is below minimum price signal - restricting trade!");
                }
                return newTargets;
            }
        }
    }
}