| Overall Statistics |
|
Total Trades 457 Average Win 1.02% Average Loss -0.41% Compounding Annual Return 10.330% Drawdown 20.900% Expectancy 1.500 Net Profit 306.582% Sharpe Ratio 0.865 Loss Rate 29% Win Rate 71% Profit-Loss Ratio 2.50 Alpha 0.027 Beta 3.921 Annual Standard Deviation 0.122 Annual Variance 0.015 Information Ratio 0.702 Tracking Error 0.122 Treynor Ratio 0.027 Total Fees $1001.90 |
import numpy as np
import pandas as pd
import operator
### <summary>
### Basic template algorithm simply initializes the date range and cash. This is a skeleton
### framework you can use for designing an algorithm.
### </summary>
class ResolveGEM(QCAlgorithm):
'''Basic template algorithm simply initializes the date range and cash'''
def Initialize(self):
'''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
self.SetStartDate(2005,1, 1) #Set Start Date
#self.SetEndDate(2013,10,11) #Set End Date
self.SetCash(100000) #Set Strategy Cash
self.equity = ['SPY', 'EFA']
self.bonds = ['TLT']
self.symbols = self.equity + self.bonds
self.leverage = 1
for symbol in self.symbols:
self.AddEquity(symbol)
#self.Securities[symbol].SetDataNormalizationMode(DataNormalizationMode.TotalReturn)
self.lookbacks = range(1, 19)
self.ma_periods = range(2, 19)
self.trend_style = ['SPY', 'Multi']
self.momentum_style = ['Price', 'MA']
columns = ['Momentum Style', 'Trend Style', 'Momentum Lookback', 'Trend Lookback', 'MA Period']
# Create multiple strategies with different settings
settings = []
for trend in self.trend_style:
for momentum in self.momentum_style:
if momentum == 'Price':
for m_look in self.lookbacks:
for t_look in self.lookbacks:
settings.append([momentum, trend, m_look, t_look, 0])
else:
for ma in self.ma_periods:
for ma_look in self.ma_periods:
settings.append([momentum, trend, 0, ma_look, ma])
self.strategies = pd.DataFrame(settings, columns=columns)
self.strategies.fillna(0)
self.allocation = {}
for sym in self.symbols:
self.allocation[sym] = 0
self.Schedule.On(self.DateRules.MonthEnd('SPY'),
self.TimeRules.BeforeMarketClose('SPY', 10),
self.signals)
def signals(self):
h = self.History(self.equity, 252 * 2, Resolution.Daily).unstack(level=0).close
for strategy in range(self.strategies.iloc[:,0].count()):
if self.strategies.iloc[strategy]['Momentum Style'] == 'Price':
spy_trend = h['SPY'].iloc[-1] / h['SPY'].iloc[-22 * self.strategies.iloc[strategy]['Trend Lookback']] - 1 > 0
efa_trend = h['EFA'].iloc[-1] / h['EFA'].iloc[-22 * self.strategies.iloc[strategy]['Trend Lookback']] - 1 > 0
# Are SPY OR EFA returns positive
if spy_trend or efa_trend:
# Calculate momentum as % change in price
ret = {}
for sym in self.equity:
ret[sym] = h[sym].iloc[-1] / h[sym].iloc[-22 * self.strategies.iloc[strategy]['Momentum Lookback']] -1
# Select highest momentum equity if SPY > Moving Average
if self.strategies.iloc[strategy]['Trend Style'] == 'SPY' and spy_trend:
self.allocation[max(ret.items(), key=operator.itemgetter(1))[0]] += 1
# Select highest momentum equity if SPY and EFA > Moving Average
elif self.strategies.iloc[strategy]['Trend Style'] == 'Multi' and spy_trend and efa_trend:
self.allocation[max(ret.items(), key=operator.itemgetter(1))[0]] += 1
else:
self.allocation['TLT'] += 1
else:
self.allocation['TLT'] += 1
else:
spy_trend = h['SPY'].iloc[-1] > h['SPY'].iloc[-22 * self.strategies.iloc[strategy]['Trend Lookback']:].mean()
efa_trend = h['EFA'].iloc[-1] > h['EFA'].iloc[-22 * self.strategies.iloc[strategy]['Trend Lookback']:].mean()
if spy_trend or efa_trend:
# Calculate momentum as % difference to moving average
ret = {}
for sym in self.equity:
ret[sym] = h[sym].iloc[-1] / h[sym].iloc[-22 * self.strategies.iloc[strategy]['MA Period']:].mean() -1
# Select highest momentum equity if SPY > Moving Average
if self.strategies.iloc[strategy]['Trend Style'] == 'SPY' and spy_trend:
self.allocation[max(ret.items(), key=operator.itemgetter(1))[0]] += 1
# Select highest momentum equity if SPY and EFA > Moving Average
elif self.strategies.iloc[strategy]['Trend Style'] == 'Multi' and spy_trend and efa_trend:
self.allocation[max(ret.items(), key=operator.itemgetter(1))[0]] += 1
else:
self.allocation['TLT'] += 1
else:
self.allocation['TLT'] += 1
self.trade()
def trade(self):
# Place sales first to ensure buying power is no exceeded in rebalance
for s in self.symbols:
current_weight = float((self.Portfolio[s].Quantity * self.Portfolio[s].Price) / self.Portfolio.TotalPortfolioValue)
weight = self.allocation[s] / sum(self.allocation.values())
if (weight * self.leverage) < current_weight:
self.SetHoldings(s, weight * self.leverage )
# Place purchases
for s in self.symbols:
current_weight = float((self.Portfolio[s].Quantity * self.Portfolio[s].Price) / self.Portfolio.TotalPortfolioValue)
weight = self.allocation[s] / sum(self.allocation.values())
if (weight * self.leverage) > current_weight:
self.SetHoldings(s, weight * self.leverage )
# Reset allocations
for sym in self.symbols:
self.allocation[sym] = 0