Overall Statistics |
Total Trades 33 Average Win 15.76% Average Loss -5.23% Compounding Annual Return 29.169% Drawdown 17.100% Expectancy 1.431 Net Profit 665.057% Sharpe Ratio 0.898 Loss Rate 39% Win Rate 61% Profit-Loss Ratio 3.01 Alpha 0.324 Beta -0.081 Annual Standard Deviation 0.352 Annual Variance 0.124 Information Ratio 0.531 Tracking Error 0.425 Treynor Ratio -3.9 |
using System; using System.Collections.Generic; using System.Linq; using QuantConnect.Models; using QuantConnect.Indicators; namespace QuantConnect.Rotation { public class GlobalRotation : QCAlgorithm { // we'll use this to tell us when the month has ended DateTime LastRotationTime = DateTime.MinValue; TimeSpan RotationInterval = TimeSpan.FromDays(30); // these are the growth symbols we'll rotate through List<string> GrowthSymbols = new List<string> { "MDY", // US S&P mid cap 400 "IEV", // iShares S&P europe 350 "EEM", // iShared MSCI emerging markets "ILF", // iShares S&P latin america "EPP" // iShared MSCI Pacific ex-Japan }; // these are the safety symbols we go to when things are looking bad for growth List<string> SafetySymbols = new List<string> { "EDV", // Vangaurd TSY 25yr+ "SHY" // Barclays Low Duration TSY }; // we'll hold some computed data in these guys List<SymbolData> SymbolData = new List<SymbolData>(); public override void Initialize() { SetCash(25000); SetStartDate(2007, 1, 1); foreach (var symbol in GrowthSymbols.Union(SafetySymbols)) { // ideally we would use daily data AddSecurity(SecurityType.Equity, symbol, Resolution.Minute); var oneMonthPerformance = MOM(symbol, 30, Resolution.Daily); var threeMonthPerformance = MOM(symbol, 90, Resolution.Daily); SymbolData.Add(new SymbolData { Symbol = symbol, OneMonthPerformance = oneMonthPerformance, ThreeMonthPerformance = threeMonthPerformance }); } } private bool first = true; public void OnData(TradeBars data) { try { // the first time we come through here we'll need to do some things such as allocation // and initializing our symbol data if (first) { first = false; LastRotationTime = data.Time; return; } var delta = data.Time.Subtract(LastRotationTime); if (delta > RotationInterval) { LastRotationTime = data.Time; // pick which one is best from growth and safety symbols var orderedObjScores = SymbolData.OrderByDescending(x => x.ObjectiveScore).ToList(); foreach (var orderedObjScore in orderedObjScores) { Log(">>SCORE>>" + orderedObjScore.Symbol + ">>" + orderedObjScore.ObjectiveScore); } var bestGrowth = orderedObjScores.First(); if (bestGrowth.ObjectiveScore > 0) { if (Portfolio[bestGrowth.Symbol].Quantity == 0) { Log("PREBUY>>LIQUIDATE>>"); Liquidate(); } Log(">>BUY>>" + bestGrowth.Symbol + "@" + (100 * bestGrowth.OneMonthPerformance).ToString("00.00")); decimal qty = Portfolio.Cash / Securities[bestGrowth.Symbol].Close; Order(bestGrowth.Symbol, qty, OrderType.Market); } else { // if no one has a good objective score then let's hold cash this month to be safe Log(">>LIQUIDATE>>CASH"); Liquidate(); } } } catch (Exception ex) { Error("OnTradeBar: " + ex.Message + "\r\n\r\n" + ex.StackTrace); } } /// <summary> /// Creates a new Momentum indicator /// </summary> /// <param name="symbol"></param> /// <param name="period"></param> /// <param name="resolution"></param> /// <returns></returns> public Momentum MOM(string symbol, int period, Resolution? resolution = null) { string name = CreateIndicatorName(symbol, "MOM" + period, resolution); var momentum = new Momentum(name, period); RegisterIndicator(symbol, momentum, resolution); return momentum; } /// <summary> /// Creates a new name for an indicator created with the convenience functions (SMA, EMA, ect...) /// </summary> /// <param name="symbol">The symbol this indicator is registered to</param> /// <param name="type">The indicator type, for example, 'SMA5'</param> /// <param name="resolution">The resolution requested</param> /// <returns>A unique for the given parameters</returns> private static string CreateIndicatorName(string symbol, string type, Resolution? resolution) { string res; switch (resolution) { case Resolution.Tick: res = "_tick"; break; case Resolution.Second: res = "_sec"; break; case Resolution.Minute: res = "_min"; break; case Resolution.Hour: res = "_hr"; break; case Resolution.Daily: res = "_day"; break; case null: res = string.Empty; break; default: throw new ArgumentOutOfRangeException("resolution"); } return string.Format("{0}({1}{2})", type, symbol.ToUpper(), res); } } class SymbolData { public string Symbol; public Momentum OneMonthPerformance { get; set; } public Momentum ThreeMonthPerformance { get; set; } public decimal ObjectiveScore { get { // we weight the one month performance higher decimal weight1 = 100; decimal weight2 = 75; return (weight1 * OneMonthPerformance + weight2 * ThreeMonthPerformance) / (weight1 + weight2); } } } }
/* * 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. */ namespace QuantConnect.Indicators { /// <summary> /// This indicator computes the n-period change in a value /// </summary> public class Momentum : WindowIndicator { /// <summary> /// Creates a new Momentum indicator with the specified period /// </summary> /// <param name="period">The period over which to perform to computation</param> public Momentum(int period) : base("MOM" + period, period) { } /// <summary> /// Creates a new Momentum indicator with the specified period /// </summary> /// <param name="name">The name of this indicator</param> /// <param name="period">The period over which to perform to computation</param> public Momentum(string name, int period) : base(name, period) { } /// <summary> /// Computes the next value for this indicator from the given state. /// </summary> /// <param name="window">The window of data held in this indicator</param> /// <param name="previousValue">The previous value of this indicator</param> /// <param name="input">The input value to this indicator on this time step</param> /// <returns>A new value for this indicator</returns> protected override decimal ComputeNextValue(IReadOnlyWindow<IndicatorDataPoint> window, IndicatorDataPoint previousValue, IndicatorDataPoint input) { if (!window.IsReady) { // keep returning the delta from the first item put in there to init return input - window[window.Count - 1]; } return input - window.MostRecentlyRemoved; } } }