Overall Statistics |
Total Trades 43 Average Win 5.67% Average Loss -3.88% Compounding Annual Return 25.393% Drawdown 14.700% Expectancy 0.716 Net Profit 203.658% Sharpe Ratio 1.033 Loss Rate 30% Win Rate 70% Profit-Loss Ratio 1.46 Alpha 0.261 Beta -0.021 Annual Standard Deviation 0.249 Annual Variance 0.062 Information Ratio 0.344 Tracking Error 0.297 Treynor Ratio -12.42 |
using System; using System.Collections.Generic; using System.Linq; using QuantConnect.Models; 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(2008, 1, 1); SetStartDate(2010, 1, 2); foreach (var symbol in GrowthSymbols.Union(SafetySymbols)) { // ideally we would use daily data AddSecurity(SecurityType.Equity, symbol, Resolution.Minute); SymbolData.Add(new SymbolData { Symbol = symbol }); } } private bool first = true; public void OnData(TradeBars data) { try { TradeBar bar = data.First().Value; // 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 = bar.Time; InitializeSymbolDataAndHoldings(data); return; } var delta = bar.Time.Subtract(LastRotationTime); if (delta > RotationInterval) { LastRotationTime = bar.Time; // compute performance for each symbol UpdateSymbolData(data); // 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.LastNMonthPerformance(1)).ToString("00.00")); decimal qty = Portfolio.Cash/Securities[bestGrowth.Symbol].Close; Order(bestGrowth.Symbol, qty); } 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); } } private void UpdateSymbolData(TradeBars data) { foreach (var symbolData in SymbolData) { TradeBar bar; if (data.TryGetValue(symbolData.Symbol, out bar)) { symbolData.AddClosingPrice(bar.Close); } } } private void InitializeSymbolDataAndHoldings(TradeBars data) { var symbolsToAllocate = new List<string>(); foreach (SymbolData symbolData in SymbolData) { TradeBar bar; if (data.TryGetValue(symbolData.Symbol, out bar)) { Log(">>SEEDED>>"+symbolData.Symbol); symbolData.InitializeWith(bar.Close); symbolsToAllocate.Add(symbolData.Symbol); } } return; // we could optionally decide an intelligent way to start, but with no back // data its hard to make good guesses, we can just let it run for an interval // before initial seeding double percent = 1/(double) symbolsToAllocate.Count; foreach (var symbol in symbolsToAllocate) { SetHoldings(symbol, percent); } } } class SymbolData { private const int maxMonths = 6; public string Symbol; private List<decimal> PreviousMonths = new List<decimal>(); public void AddClosingPrice(decimal value) { PreviousMonths.Insert(0, value); if (PreviousMonths.Count > maxMonths) { PreviousMonths.RemoveAt(PreviousMonths.Count - 1); } } public void InitializeWith(decimal value) { for (int i = 0; i < maxMonths; i++) { AddClosingPrice(value); } } public decimal LastNMonthPerformance(int n) { decimal delta = PreviousMonths[n] - PreviousMonths[0]; return delta/PreviousMonths[n]; } public decimal ObjectiveScore { get { // there's a few categories for scoring with different weights // first we look at the 1 month performance decimal weight1 = 100; decimal value1 = LastNMonthPerformance(1)*100; // look at the sum of the last three months performance, that is, SUM (1mp, 2mp, 3mp) decimal weight2 = 75; decimal value2 = Enumerable.Range(0, 3).Select(LastNMonthPerformance).Sum()*33; return (weight1*value1 + weight2*value2)/(weight1 + weight2); } } } }