Overall Statistics |
Total Trades 666 Average Win 1.67% Average Loss -1.27% Compounding Annual Return 63.965% Drawdown 19.200% Expectancy 0.258 Net Profit 168.480% Sharpe Ratio 1.64 Loss Rate 46% Win Rate 54% Profit-Loss Ratio 1.32 Alpha -0.214 Beta 38.199 Annual Standard Deviation 0.333 Annual Variance 0.111 Information Ratio 1.581 Tracking Error 0.333 Treynor Ratio 0.014 Total Fees $3.33 |
/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System.Collections.Generic; using System.Linq; using QuantConnect.Data.Fundamental; using QuantConnect.Data.Market; using QuantConnect.Data.UniverseSelection; namespace QuantConnect.Algorithm.CSharp { /// <summary> /// Demonstration how to group stocks by Morningstar Asset Classification and then compare fundamental factors to select the best stock. /// This was inspired by the Fundamental Factor Long Short Strategy: /// - https://www.quantconnect.com/tutorials/strategy-library/fundamental-factor-long-short-strategy /// </summary> /// <meta name="tag" content="using data" /> /// <meta name="tag" content="universes" /> /// <meta name="tag" content="coarse universes" /> /// <meta name="tag" content="fine universes" /> public class FundamentalFactorLongUniverse : QCAlgorithm { private const int _numberOfSymbolsCoarse = 1000; private const int _numberOfSymbolsFine = 2; // initialize our changes to nothing private SecurityChanges _changes = SecurityChanges.None; private readonly Dictionary<Symbol, decimal> _dollarVolumeBySymbol = new Dictionary<Symbol, decimal>(); public override void Initialize() { UniverseSettings.Resolution = Resolution.Daily; SetSecurityInitializer(s => s.SetFeeModel(new ConstantFeeModel(.005m))); SetStartDate(2010, 01, 01); SetEndDate(2012, 01, 01); SetCash(50000); // This add universe method accepts two parameters: // - coarse selection function: accepts an IEnumerable<CoarseFundamental> and returns an IEnumerable<Symbol> // - fine selection function: accepts an IEnumerable<FineFundamental> and returns an IEnumerable<Symbol> AddUniverse(CoarseSelectionFunction, FineSelectionFunction); } // sort the data by daily dollar volume and take the top 'NumberOfSymbolsCoarse' public IEnumerable<Symbol> CoarseSelectionFunction(IEnumerable<CoarseFundamental> coarse) { // Coarse Filtering: // - the stocks must have fundamental data // - volume must be greater than 0 // - price must be greater than 0 // - avoids Nasdaq test symbols // - ordered by DollarVolume descending var sortedByDollarVolume = (from x in coarse where x.Symbol != "ZWZZT" || // Nasdaq test symbol x.Symbol != "ZXZZT" || // Nasdaq test symbol x.Symbol != "ZVZZT" // Nasdaq test symbol where x.HasFundamentalData && x.Volume > 0 && x.Price > 0 orderby x.DollarVolume descending select x).Take(_numberOfSymbolsCoarse).ToList(); // Add coarse symbols to the dollarVolumeBySymbol dictionary for use later in the fine selctor _dollarVolumeBySymbol.Clear(); foreach (var i in sortedByDollarVolume) { _dollarVolumeBySymbol[i.Symbol] = i.DollarVolume; } return _dollarVolumeBySymbol.Keys; } /// <summary> /// /// </summary> /// <param name="fine"></param> /// <returns></returns> public IEnumerable<Symbol> FineSelectionFunction(IEnumerable<FineFundamental> fine) { // Split stocks into groups by Asset Classification - MorningstarIndustryCode) // - splitting the stocks up by 148 classifications alows us to compare stocks in the same industry (apples to apples) // - only selects US stocks // - only selects stocks from the New York Stock Exchange or Nasdaq // - will not select stocks within 180 days of IPO date // - only selects Grade A growth grade stocks // - order by dollar volume from the dollarVolumeBySector dictionary from the coarse selector Dictionary<int, IOrderedEnumerable<FineFundamental>> topFineBySector = (from x in fine // Group by MorningstarIndustryCode group x by x.AssetClassification.MorningstarIndustryCode into g let y = from item in g where item.CompanyReference.CountryId == "USA" && (item.CompanyReference.PrimaryExchangeID == "NYS" || item.CompanyReference.PrimaryExchangeID == "NAS") && (Time - item.SecurityReference.IPODate).Days > 180 && item.AssetClassification.GrowthGrade == "A" orderby _dollarVolumeBySymbol[item.Symbol] descending select item select new { g.Key, Value = y } ).ToDictionary(x => x.Key, x => x.Value); // Collections to order sectors by different valuations Dictionary<int, List<FineFundamental>> sectorByBookValuePerShare = new Dictionary<int, List<FineFundamental>>(); Dictionary<int, List<FineFundamental>> sectorByOperationMargin = new Dictionary<int, List<FineFundamental>>(); Dictionary<int, List<FineFundamental>> sectorByEvToEbit = new Dictionary<int, List<FineFundamental>>(); // Order each sector by 3 valuations foreach (var sector in topFineBySector) { var sectorNumber = sector.Key; var sectorFine = sector.Value; // Order each sector by book value per share ratio var sectorFilteredByBookValuePerShare = sectorFine.Select(x => x) .OrderBy(y => y.ValuationRatios.BookValuePerShare) .Select(f => f).ToList(); // Order each sector by OperationMargin var sectorFilteredByOperationMargin = sectorFine.Select(x => x) .OrderByDescending(z => z.OperationRatios.OperationMargin.OneYear) .Select(f => f).ToList(); // Order each sector by EvToEbit ratio. var sectorFilteredByEvToEbit = sectorFine.Select(x => x) .OrderBy(r => r.ValuationRatios.EVtoEBIT) .Select(f => f).ToList(); // Add ordered secotrs to the sector valuation collections sectorByBookValuePerShare.Add(sectorNumber, sectorFilteredByBookValuePerShare); sectorByOperationMargin.Add(sectorNumber, sectorFilteredByOperationMargin); sectorByEvToEbit.Add(sectorNumber, sectorFilteredByEvToEbit); } // Put all the sectors in the collections in list's so we can use the index to calculate a score var bookValuePershareList = sectorByBookValuePerShare.SelectMany(x => x.Value) .OrderBy(y => y.ValuationRatios.BookValuePerShare) .Select(x => x.Symbol).Take(25).ToList(); var OperationMarginList = sectorByOperationMargin.SelectMany(x => x.Value) .OrderByDescending(z => z.OperationRatios.OperationMargin.OneYear) .Select(x => x.Symbol).Take(25).ToList(); var EvToEbitList = sectorByEvToEbit.SelectMany(x => x.Value) .OrderBy(r => r.ValuationRatios.EVtoEBIT) .Select(x => x.Symbol).Take(25).ToList(); Dictionary<Symbol, double> symbolDic = new Dictionary<Symbol, double>(); foreach (var symbol in bookValuePershareList) { // Get the index of each symbol in each valuation list. We'll use this value multiplied by a multiplier to get a score. var value = bookValuePershareList.FindIndex(a => a == symbol); var quality = EvToEbitList.FindIndex(a => a == symbol); var momentum = OperationMarginList.FindIndex(a => a == symbol); // Multiply each valuation by a weight: // - Value: Multiplied by 40% (.4) // - Quality: Multiplied by 40% (.4) // - Momentum: Multiplied by 20% (.2) var finalValue = (value * .4) + (quality * .4) + (momentum * .2); symbolDic.Add(symbol, finalValue); } // Return the symbol (key) ordered by score (value) ascending and take the defined number of symbols fine return symbolDic.Select(x => x) .OrderBy(x => x.Value) .Select(x => x.Key).Take(_numberOfSymbolsFine); } //Data Event Handler: New data arrives here. "TradeBars" type is a dictionary of strings so you can access it by symbol. public void OnData(TradeBars data) { // if we have no changes, do nothing if (_changes == SecurityChanges.None) return; // liquidate removed securities foreach (var security in _changes.RemovedSecurities) { if (security.Invested) { Liquidate(security.Symbol); Debug("Liquidated Stock: " + security.Symbol.Value); } } // we want 20% allocation in each security in our universe foreach (var security in _changes.AddedSecurities) { SetHoldings(security.Symbol, 0.5m); Debug("Purchased Stock: " + security.Symbol.Value); } _changes = SecurityChanges.None; } // this event fires whenever we have changes to our universe public override void OnSecuritiesChanged(SecurityChanges changes) { _changes = changes; if (changes.AddedSecurities.Count > 0) { Debug("Securities added: " + string.Join(",", changes.AddedSecurities.Select(x => x.Symbol.Value))); } if (changes.RemovedSecurities.Count > 0) { Debug("Securities removed: " + string.Join(",", changes.RemovedSecurities.Select(x => x.Symbol.Value))); } } } }