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