Overall Statistics |
Total Trades 115 Average Win 0.11% Average Loss -0.12% Compounding Annual Return 37.221% Drawdown 16.200% Expectancy 0.673 Net Profit 56.543% Sharpe Ratio 1.919 Probabilistic Sharpe Ratio 80.828% Loss Rate 10% Win Rate 90% Profit-Loss Ratio 0.86 Alpha 0.057 Beta 1.353 Annual Standard Deviation 0.165 Annual Variance 0.027 Information Ratio 1.703 Tracking Error 0.073 Treynor Ratio 0.234 Total Fees $131.06 |
import pandas as pd import numpy as np from datetime import datetime, timedelta from QuantConnect.Data.UniverseSelection import * from scipy.stats import linregress class Momentum(QCAlgorithm): def __init__(self): self.reb1 = 1 # set the flag for rebalance self.reb2 = 12 # set the flag for rebalance self.num_coarse = 20 # Number of stocks to pass CoarseSelection process self.num_fine = 20 # Number of stocks to long self.symbols = None def Initialize(self): self.SetStartDate(2012, 1, 1) # Set Start Date self.SetEndDate(2013,6, 1) # Set End Date self.SetCash(150000) # Set Strategy Cash self.btal = self.AddEquity("BTAL", Resolution.Minute).Symbol # BTAL hedge self.iei = self.AddEquity("IEI", Resolution.Minute).Symbol # bond hedge self.tlt = self.AddEquity("TLT", Resolution.Minute).Symbol # bond hedge self.list = [self.btal, self.iei, self.tlt] self.reb1 = 1 # set the flag for momentum stock rebalancement self.reb2 = 1 # set the flag for rebalance self.AddUniverse(self.CoarseSelectionFunction,self.FineSelectionFunction) self.spy = self.AddEquity("SPY", Resolution.Daily).Symbol self.Schedule.On(self.DateRules.MonthStart(self.spy), self.TimeRules.AfterMarketOpen(self.spy,5), Action(self.rebalance)) self.SetSecurityInitializer(self.CustomSecurityInitializer) self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Cash) def CustomSecurityInitializer(self, security): security.SetDataNormalizationMode(DataNormalizationMode.SplitAdjusted) def CoarseSelectionFunction(self, coarse): # if the rebalance flag is not 1, return null list to save time. if self.reb1 != 1: return Universe.Unchanged # make universe selection once a month sortedByDollarVolume = sorted(coarse, key=lambda x: x.DollarVolume, reverse=True) filtered = [x.Symbol for x in sortedByDollarVolume if x.HasFundamentalData] # filtered down to the 500 most liquid stocks return filtered[:self.num_coarse] def FineSelectionFunction(self, fine): # return null list if it's not time to rebalance if self.reb1 != 1: return Universe.Unchanged # drop counter (will update back to 1 after rebalancement has occurred) self.reb1 = 0 #will not include this portion of code - essentially filters for top momentum stocks # self.long = sorted_symbolLong[:self.num_fine] # return self.long return [f.Symbol for f in fine] def OnData(self, data): pass def rebalance(self): long_list = [x for x in self.ActiveSecurities.Keys] # to update the momentum stocks only monthly for i in self.Portfolio.Values: if i.Invested and i.Symbol not in long_list and i.Symbol not in self.list: self.Liquidate(i.Symbol) # annual portfolio rebalancement if self.reb2 % 12 == 0: self.SetHoldings(self.iei, 0.15) self.SetHoldings(self.tlt, 0.15) self.SetHoldings(self.btal, 0.10) for i in long_list: self.SetHoldings(i, 0.6/self.num_fine) else: # monthly rebalancement of momentum stocks only, leaving bonds and market neutral etfs untouched for i in long_list: if not self.Securities[i].Invested: self.SetHoldings(i, 0.6/self.num_fine) self.reb1 = 1 self.reb2 += 1