Overall Statistics |
Total Trades 150 Average Win 1.67% Average Loss -2.00% Compounding Annual Return -27.100% Drawdown 27.100% Expectancy -0.218 Net Profit -26.973% Sharpe Ratio -1.023 Loss Rate 57% Win Rate 43% Profit-Loss Ratio 0.83 Alpha -0.368 Beta 0.769 Annual Standard Deviation 0.271 Annual Variance 0.073 Information Ratio -1.532 Tracking Error 0.258 Treynor Ratio -0.36 Total Fees $352.38 |
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; namespace QuantConnect.Algorithm.CSharp { /// <summary> // Select stocka with: // price > 10 dollars // fast EMA above slow EMA // EBITDAMargin.OneYear > 0.1 AND RevenueGrowth.OneYear > 0.1 AND DilutedContEPSGrowth.OneYear > 0.1 AND USA // Order by DilutedContEPSGrowth /// </summary> public class FundamentalAndMaAlgorithm : QCAlgorithm { private readonly decimal minGrossMargin = 0.4m; private readonly decimal minRevenueGrowth = 0.15m; private readonly decimal minDilutedContEPSGrowth = 0.2m; // minimal stock price in dollars decimal minStockPrice = 10; // EMA tolerance to prevent bouncing const decimal Tolerance = 0.02m; int numberOfStocks = 1; static int daysInSlowEma = 50; static int daysInFastEma = 10; private readonly int takeByDollarVolume = 200; // moving averages by stock symbol private readonly ConcurrentDictionary<Symbol, MovingAverages> averages = new ConcurrentDictionary<Symbol, MovingAverages>(); List<Symbol> selectedByFundamentals = new List<Symbol>(); private class MovingAverages { public readonly ExponentialMovingAverage Fast; public readonly ExponentialMovingAverage Slow; public MovingAverages() { Fast = new ExponentialMovingAverage(daysInFastEma); Slow = new ExponentialMovingAverage(daysInSlowEma); } // computes an object score of how much large the fast is than the slow public decimal ScaledDelta { get { return (Fast - Slow) / ((Fast + Slow) / 2m); } } // updates the EMA50 and EMA100 indicators, returning true when they're both ready public bool Update(DateTime time, decimal value) { return Fast.Update(time, value) && Slow.Update(time, value); } } Symbol benchmark = QuantConnect.Symbol.Create("SPY", SecurityType.Equity, Market.USA); Symbol bonds = QuantConnect.Symbol.Create("TLT", SecurityType.Equity, Market.USA); public override void Initialize() { UniverseSettings.Resolution = Resolution.Daily; SetStartDate(2017, 4, 1); SetEndDate(2018, 4, 1); SetCash(50000); AddUniverse(CoarseSelectionFunction, FineSelectionFunction); AddEquity(bonds.Value, Resolution.Daily); this.SetBenchmark(benchmark); this.Log("orderby DilutedContEPSGrowth"); this.Log("minGrossMargin=" + minGrossMargin); this.Log("minRevenueGrowth=" + minRevenueGrowth); this.Log("minDilutedContEPSGrowth=" + minDilutedContEPSGrowth); this.Log("minStockPrice=" + minStockPrice); this.Log("Tolerance=" + Tolerance); this.Log("numberOfStocks=" + numberOfStocks); this.Log("daysInSlowEma=" + daysInSlowEma); this.Log("daysInFastEma=" + daysInFastEma); this.Log("takeByDollarVolume=" + takeByDollarVolume); } public IEnumerable<Symbol> CoarseSelectionFunction(IEnumerable<CoarseFundamental> coarse) { var selectedByEma = SelectByEma(coarse); return selectedByEma.OrderByDescending(stock => stock.DollarVolume).Take(this.takeByDollarVolume).Select(stock => stock.Symbol); } int numberOfBonds = 0; public IEnumerable<Symbol> FineSelectionFunction(IEnumerable<FineFundamental> fine) { this.numberOfBonds = 0; this.selectedByFundamentals.Clear(); this.selectedByFundamentals.AddRange(SelectByFundamentals(fine).Select(stock => stock.Symbol)); if (selectedByFundamentals.Count() < this.numberOfStocks) { // nothing to invest in, invest in bonds this.numberOfBonds = this.numberOfStocks - selectedByFundamentals.Count(); selectedByFundamentals.Add(this.bonds); } return selectedByFundamentals; } private IEnumerable<FineFundamental> SelectByFundamentals(IEnumerable<FineFundamental> selectedByMa) { return (from stock in selectedByMa where stock.CompanyReference.CountryId == "USA" where !stock.CompanyReference.IsLimitedPartnership where !stock.CompanyReference.IsREIT where stock.OperationRatios.GrossMargin.OneYear > this.minGrossMargin where stock.OperationRatios.RevenueGrowth.OneYear > this.minRevenueGrowth where stock.EarningRatios.DilutedContEPSGrowth.OneYear > this.minDilutedContEPSGrowth where stock.OperationRatios.GrossMargin5YrAvg > this.minGrossMargin where stock.OperationRatios.GrossProfitAnnual5YrGrowth > this.minDilutedContEPSGrowth orderby stock.EarningRatios.DilutedContEPSGrowth.OneYear descending select stock).Take(this.numberOfStocks); } // select stocks with fundamental data and price > 10 dollars // fast EMA is above slow EMA private List<CoarseFundamental> SelectByEma(IEnumerable<CoarseFundamental> coarse) { List<CoarseFundamental> selected = new List<CoarseFundamental>(); // select stocks with fast EMA above slow EMA foreach (CoarseFundamental stock in coarse) { if (stock.Price > this.minStockPrice) { if (stock.HasFundamentalData) { // create or update averages for the stock MovingAverages average = null; if (this.averages.ContainsKey(stock.Symbol)) { this.averages.TryGetValue(stock.Symbol, out average); } else { average = new MovingAverages(); this.averages.TryAdd(stock.Symbol, average); } // if indicators are ready if (average.Update(stock.EndTime, stock.Price)) { // if fast EMA is above slow EMA if (stock.Price > average.Slow * (1 + Tolerance)) { selected.Add(stock); } } } } } return selected; } public void OnData(TradeBars data) { LiquidateNotSelected(); InvestSelected(); } private void InvestSelected() { // invest in securities that are in lists if (this.Portfolio.Cash > 0m) { if (this.selectedByFundamentals.Count() > 0) { foreach (Symbol security in this.selectedByFundamentals) { // do not trade benchmark and bonds here if (!security.Equals(this.benchmark) && !security.Equals(this.bonds)) { if (this.Portfolio.Securities[security].IsTradable) { decimal holdings = 1m / this.numberOfStocks; this.Log("Buy:" + security.Value + " " + holdings); this.SetHoldings(security, holdings); } } } if (this.numberOfBonds > 0) { decimal holdings = (decimal)numberOfBonds / this.numberOfStocks; this.Log("Buy bonds:" + this.bonds.Value + " " + holdings); // this.SetHoldings(this.bonds, holdings); } } } } private void LiquidateNotSelected() { // liquidate securities not in the list IEnumerable<Symbol> toLiquidate = this.Portfolio.Securities.Keys.Except(this.selectedByFundamentals); foreach (Symbol security in toLiquidate) { // do not trade benchmark if (!security.Equals(this.benchmark)) { this.Log("Liquidate:" + security.Value); Liquidate(security); } } } } }