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.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

# 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```