Overall Statistics |
Total Trades 1237 Average Win 1.00% Average Loss -0.90% Compounding Annual Return 28.225% Drawdown 39.400% Expectancy 0.218 Net Profit 247.573% Sharpe Ratio 0.995 Probabilistic Sharpe Ratio 38.496% Loss Rate 42% Win Rate 58% Profit-Loss Ratio 1.11 Alpha 0.118 Beta 1.246 Annual Standard Deviation 0.276 Annual Variance 0.076 Information Ratio 0.832 Tracking Error 0.179 Treynor Ratio 0.22 Total Fees $24126.68 |
//Copyright Warren Harding 2020. //Granted to the public domain. //Use at your own risk. namespace QuantConnect.Algorithm.CSharp { public class FactorOfTheDay : QCAlgorithm { int TotalHighDollarVolumeStocks = 10; int TotalStocksToHold = 5; int FactorPeriod = 20; List<Symbol> LongSymbols = new List<Symbol>(); List<decimal> FCFYieldFitnesses = new List<decimal>(); List<decimal> BookYieldFitnesses = new List<decimal>(); List<decimal> EarningYieldFitnesses = new List<decimal>(); List<decimal> SalesYieldFitnesses = new List<decimal>(); List<decimal> CFYieldFitnesses = new List<decimal>(); public override void Initialize() { SetStartDate(2015, 11, 19); SetCash(1000000); AddUniverse(CoarseSelectionFunction, FineSelectionFunction); } public IEnumerable<Symbol> CoarseSelectionFunction(IEnumerable<CoarseFundamental> coarse) { return coarse .Where(x => x.HasFundamentalData) .OrderByDescending(x => x.DollarVolume) .Take(TotalHighDollarVolumeStocks) .Select(x => x.Symbol); } public IEnumerable<Symbol> FineSelectionFunction(IEnumerable<FineFundamental> fine) { List<StockData> stockDatas = new List<StockData>(); foreach (FineFundamental f in fine) { List<TradeBar> history = History(f.Symbol, 2, Resolution.Daily).ToList(); if (history != null && history.Count == 2) { StockData stockData = new StockData(); stockData.Change = (history.Last().Close - history.First().Close) / history.First().Close; stockData.FCFYield = f.ValuationRatios.FCFYield; stockData.BookYield = f.ValuationRatios.BookValueYield; stockData.EarningYield = f.ValuationRatios.EarningYield; stockData.SalesYield = f.ValuationRatios.SalesYield; stockData.CFYield = f.ValuationRatios.CFYield; stockData.Symbol = f.Symbol; stockDatas.Add(stockData); } } decimal[] changes = stockDatas.Select(x => x.Change).ToArray(); decimal[] fcfYields = stockDatas.Select(x => x.FCFYield).ToArray(); decimal[] bookYields = stockDatas.Select(x => x.BookYield).ToArray(); decimal[] earningYields = stockDatas.Select(x => x.EarningYield).ToArray(); decimal[] salesYields = stockDatas.Select(x => x.SalesYield).ToArray(); decimal[] cfYields = stockDatas.Select(x => x.CFYield).ToArray(); decimal fcfYieldFitness = WeightedAverage(changes, fcfYields); decimal bookYieldFitness = WeightedAverage(changes, bookYields); decimal earningYieldFitness = WeightedAverage(changes, earningYields); decimal salesYieldFitness = WeightedAverage(changes, salesYields); decimal cfYieldFitness = WeightedAverage(changes, cfYields); FCFYieldFitnesses.Add(fcfYieldFitness); BookYieldFitnesses.Add(bookYieldFitness); EarningYieldFitnesses.Add(earningYieldFitness); SalesYieldFitnesses.Add(salesYieldFitness); CFYieldFitnesses.Add(cfYieldFitness); Remove0(FCFYieldFitnesses); Remove0(BookYieldFitnesses); Remove0(EarningYieldFitnesses); Remove0(SalesYieldFitnesses); Remove0(CFYieldFitnesses); decimal fcfYieldFitnessTMA = TriangularMovingAverage(FCFYieldFitnesses.ToArray()); decimal bookYieldFitnessTMA = TriangularMovingAverage(BookYieldFitnesses.ToArray()); decimal earningYieldFitnessTMA = TriangularMovingAverage(EarningYieldFitnesses.ToArray()); decimal salesYieldFitnessTMA = TriangularMovingAverage(SalesYieldFitnesses.ToArray()); decimal cfYieldFitnessTMA = TriangularMovingAverage(CFYieldFitnesses.ToArray()); foreach (StockData stockData in stockDatas) { stockData.Fitness = stockData.FCFYield * fcfYieldFitnessTMA + stockData.BookYield * bookYieldFitnessTMA + stockData.EarningYield * earningYieldFitnessTMA + stockData.SalesYield * salesYieldFitnessTMA + stockData.CFYield * cfYieldFitnessTMA; } LongSymbols = stockDatas.OrderByDescending(x => x.Fitness) .Take(TotalStocksToHold) .Select(x => x.Symbol) .ToList(); return LongSymbols; } public void Remove0(List<decimal> list) { if (list.Count > FactorPeriod) { list.RemoveAt(0); } } public override void OnData(Slice data) { if (data.Time.DayOfWeek != DayOfWeek.Wednesday) { return; } foreach (var stock in Portfolio.Values) { if (stock.Invested) { if (LongSymbols.Exists(x => x == stock.Symbol) == false) { Liquidate(stock.Symbol); } } } decimal positionSize = 1m / TotalStocksToHold; foreach (Symbol symbol in LongSymbols) { if (Portfolio[symbol].Invested == false && data.ContainsKey(symbol)) { SetHoldings(symbol, positionSize); } } } public class StockData { public Symbol Symbol; public decimal Change; public decimal FCFYield; public decimal BookYield; public decimal EarningYield; public decimal SalesYield; public decimal CFYield; public decimal Fitness; } public static decimal WeightedAverage(decimal[] values, decimal[] weights) { decimal weightsSum = weights.Sum(); if (weightsSum != 0) { return values.Zip(weights, (x, y) => x * y).Sum() / weights.Sum(); } else { return 0; } } public static decimal TriangularMovingAverage(decimal[] values) { return WeightedAverage(values, TriangularWeightsDecimal(values.Length)); } public static decimal[] TriangularWeightsDecimal(int length) { int[] intWeights = Enumerable.Range(1, length).ToArray(); return intWeights.Select(x => Convert.ToDecimal(x)).ToArray(); } } }