| 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;
}
}
}