Overall Statistics
Total Trades
1953
Average Win
0.81%
Average Loss
-0.57%
Compounding Annual Return
436.757%
Drawdown
16.300%
Expectancy
0.325
Net Profit
436.757%
Sharpe Ratio
5.951
Probabilistic Sharpe Ratio
98.757%
Loss Rate
45%
Win Rate
55%
Profit-Loss Ratio
1.41
Alpha
3.845
Beta
-0.162
Annual Standard Deviation
0.637
Annual Variance
0.406
Information Ratio
5.244
Tracking Error
0.662
Treynor Ratio
-23.356
Total Fees
$7980.44
Estimated Strategy Capacity
$160000000.00
Lowest Capacity Asset
PANW V8EJVK11FRZ9
//Copyright HardingSoftware.com. Granted to the public domain.
//Use entirely at your own risk.
//This algorithm contains open source code from other sources,
//no claim is being made to such code.
//Do not remove this copyright notice.
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Data.Market;
using QuantConnect.Data.UniverseSelection;

namespace QuantConnect.Algorithm.CSharp
{
    public class UniverseTemplate : QCAlgorithm
    {
		List<StockData> HighDollarVolumeStocks = new List<StockData>();
		int TotalHighDollarVolumeStocks = 50;
		int TotalStocksToHold = 5;
		Resolution Resolution = Resolution.Daily;
		int Period = 10;
		decimal Leverage = 0.99m;
		
        public override void Initialize()
        {
            UniverseSettings.Resolution = Resolution;

            SetStartDate(2020, 6, 02);
            SetCash(100000);

            AddUniverse(coarse =>
            {
            	return (from stock in coarse
            			orderby stock.DollarVolume descending  
            			select stock.Symbol).Take(TotalHighDollarVolumeStocks);
            });
        }

        public void OnData(TradeBars data)
        {
            foreach (StockData stockData in HighDollarVolumeStocks)
            {
            	if (data.ContainsKey(stockData.Symbol))
            	{
	            	TradeBar bar = data[stockData.Symbol];
	            	stockData.Candles.Add(bar);
	            	if (stockData.Candles.Count > Period)
	            	{
	            		stockData.Candles.RemoveAt(0);
	            	}
	            	if (stockData.Candles.Count == Period)
	            	{
						stockData.Stochastic = CalculateStochastic(stockData.Candles);
	            	}
            	}
            }
            
            List<StockData> stocksToHold = HighDollarVolumeStocks.OrderByDescending(x => x.Stochastic).Take(TotalStocksToHold).ToList();
            
            foreach (var security in Portfolio.Values)
            {
            	if (Portfolio[security.Symbol].Invested)
            	{
	                if (stocksToHold.Exists(x => x.Symbol == security.Symbol) == false)
	                {
	                    Liquidate(security.Symbol);
	                }
            	}
            }
            
            foreach (var security in stocksToHold)
            {
        		if (Portfolio[security.Symbol].Quantity >= 0)
        		{
					SetHoldings(security.Symbol, -Leverage / (decimal)TotalStocksToHold);
        		}
            }

        }

		public class StockData
		{
			public Symbol Symbol;
			public List<TradeBar> Candles = new List<TradeBar>();
        	public decimal Stochastic = 0;
		}

        public override void OnSecuritiesChanged(SecurityChanges changes)
        {
            foreach (var security in changes.RemovedSecurities)
            {
            	StockData stockData = HighDollarVolumeStocks.Find(x => x.Symbol == security.Symbol);
            	if (stockData != null)
            	{
                	HighDollarVolumeStocks.Remove(stockData);
            	}
            }
            foreach (var security in changes.AddedSecurities)
            {
            	StockData stockData = new StockData();
            	stockData.Symbol = security.Symbol;
            	stockData.Candles = History(stockData.Symbol, Period, Resolution).ToList();
                HighDollarVolumeStocks.Add(stockData);
            }
        }
        
        public static decimal CalculateStochastic(List<TradeBar> candles)
        {
            decimal low = candles.Select(x => x.Low).Min();
            decimal high = candles.Select(x => x.High).Max();
            decimal close = candles.Last().Close;
            return 100m * (close - low) / (high - low);
        }
    }
}