| Overall Statistics |
|
Total Trades 143 Average Win 7.98% Average Loss -4.28% Compounding Annual Return 16.878% Drawdown 36.000% Expectancy 0.817 Net Profit 785.662% Sharpe Ratio 0.81 Loss Rate 37% Win Rate 63% Profit-Loss Ratio 1.87 Alpha 0.092 Beta 4.406 Annual Standard Deviation 0.222 Annual Variance 0.049 Information Ratio 0.72 Tracking Error 0.222 Treynor Ratio 0.041 Total Fees $2086.99 |
namespace QuantConnect
{
/*
* Author: Tomer Borenstein
* My attempt at whipping up an implementation of the "Global Market
* Rotation" strategy. Original description of strategy is linked to
* in the project notes.
*/
public class SGMR : QCAlgorithm
{
private string currentMonth;
private DateTime currentDay;
private MyMomentum momentumMDY;
private MyMomentum momentumILF;
private MyMomentum momentumFEZ;
private MyMomentum momentumEEM;
private MyMomentum momentumEPP;
private MyMomentum momentumTLT;
string currentETF;
public override void Initialize(){
// start and End Date range for the backtest:
SetStartDate(2004, 1, 1);
SetEndDate(DateTime.Now.Date.AddDays(-1));
currentMonth = Time.ToString("MMM");
currentETF = "";
// cash allocation
SetCash(25000);
// 5 ETFs and a negatively correlated treasury ETF
AddSecurity(SecurityType.Equity, "MDY", Resolution.Minute);
AddSecurity(SecurityType.Equity, "ILF", Resolution.Minute);
AddSecurity(SecurityType.Equity, "FEZ", Resolution.Minute);
AddSecurity(SecurityType.Equity, "EEM", Resolution.Minute);
AddSecurity(SecurityType.Equity, "EPP", Resolution.Minute);
AddSecurity(SecurityType.Equity, "TLT", Resolution.Minute);
// momentum over roughly 3 month period
momentumMDY = new MyMomentum("MDY", 90);
momentumILF = new MyMomentum("ILF", 90);
momentumFEZ = new MyMomentum("FEZ", 90);
momentumEEM = new MyMomentum("EEM", 90);
momentumEPP = new MyMomentum("EPP", 90);
momentumTLT = new MyMomentum("TLT", 90);
}
public void OnData(TradeBars data){
// day tick - add data to MyMomentum objects
if(currentDay.Date != data.Time.Date){
currentDay = data.Time;
if(data.ContainsKey("MDY")){
momentumMDY.add(data["MDY"].Close);
}
if(data.ContainsKey("ILF")){
momentumILF.add(data["ILF"].Close);
}
if(data.ContainsKey("FEZ")){
momentumFEZ.add(data["FEZ"].Close);
}
if(data.ContainsKey("EEM")){
momentumEEM.add(data["EEM"].Close);
}
if(data.ContainsKey("EPP")){
momentumEPP.add(data["EPP"].Close);
}
if(data.ContainsKey("TLT")){
momentumTLT.add(data["TLT"].Close);
}
}
// month tick - use momentum to determine which ETF to invest in
if(Time.ToString("MMM") != currentMonth){
currentMonth = Time.ToString("MMM");
string ETF = bestETF();
if(currentETF != ETF){
Liquidate(); // cash out
// fully invest in best ETF if possible
if(data.ContainsKey(ETF)){
decimal cash = Portfolio.Cash;
decimal price = data[ETF].Close;
int quantity = (int)Math.Floor(cash/price);
Order(ETF, quantity);
Debug(Time.ToString("MMM") + ": invested in " + ETF + ".");
} else {
Debug(Time.ToString("MMM") + ": could not invest in " + ETF + ".");
}
currentETF = ETF;
}
}
}
// returns the name of the ETF with the best return
private string bestETF(){
MyMomentum[] ETFs = new MyMomentum[]{momentumMDY,
momentumILF,
momentumFEZ,
momentumEEM,
momentumEPP,
momentumTLT};
string bestFund = "";
decimal bestReturn = 0m;
for(int i = 0; i < ETFs.Length; i++){
MyMomentum mETF = ETFs[i];
string fund = mETF.getName();
decimal fundReturn = mETF.getReturn();
if(bestFund == "" || bestReturn < fundReturn){
bestFund = fund;
bestReturn = fundReturn;
}
}
return bestFund;
}
}
}using System;
using System.Collections;
using System.Collections.Generic;
using QuantConnect.Securities;
using QuantConnect.Models;
namespace QuantConnect {
// Author: Tomer Borenstein
// MyMomentum - calculates a crude measure of an asset's return
public class MyMomentum {
private List<decimal> prices;
private string name;
private int window;
public MyMomentum(string name, int window){
this.prices = new List<decimal>();
this.name = name;
this.window = window;
}
public void add(decimal price){
prices.Add(price);
if(prices.Count > window){
prices.RemoveAt(0);
}
}
public string getName(){
return name;
}
public decimal getReturn(){
decimal start = prices[0];
decimal end = prices[prices.Count - 1];
return (end-start)/start;
}
}
}