| Overall Statistics |
|
Total Trades 452 Average Win 0.59% Average Loss -0.74% Compounding Annual Return -8.318% Drawdown 31.000% Expectancy -0.120 Net Profit -16.463% Sharpe Ratio -0.318 Probabilistic Sharpe Ratio 1.657% Loss Rate 51% Win Rate 49% Profit-Loss Ratio 0.80 Alpha 0 Beta 0 Annual Standard Deviation 0.175 Annual Variance 0.031 Information Ratio -0.318 Tracking Error 0.175 Treynor Ratio 0 Total Fees $686.89 |
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):
# set the flag for rebalance
self.reb = 1
# Number of stocks to pass CoarseSelection process
self.num_coarse = 500
# Number of stocks to long
self.num_fine = 10
self.symbols = None
def Initialize(self):
self.SetStartDate(2018, 8, 1) # Set Start Date
self.SetCash(100000) # Set Strategy Cash
# set the flag for rebalance
self.reb = 1
self.AddUniverse(self.CoarseSelectionFunction,self.FineSelectionFunction)
# for market state filter
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))
def CoarseSelectionFunction(self, coarse):
# if the rebalance flag is not 1, return null list to save time.
if self.reb != 1:
return self.long
# 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.reb != 1:
return self.long
# drop counter (will update back to 1 after rebalancement has occurred)
self.reb = 0
# create dictionaries to store the indicator values
stock_filter1 = {}
# sort the fine list by their momentum
for security in fine:
hist = self.History(security.Symbol, timedelta(days=365), Resolution.Daily)
if "close" in hist.columns and len(hist) > 239:
hist["log"] = np.log(hist["close"])
x1 = np.arange(hist["close"].count())
slope, _, rvalue, _, _ = linregress(x1, hist["log"])
coeff = slope*252*(rvalue**2)
# we now have a dictionary storing the values
stock_filter1[security.Symbol] = coeff
# we only want the highest values for the coeff
self.sorted1 = sorted(stock_filter1.items(), key=lambda d:d[1],reverse=True)
sorted1_symbol = [x[0] for x in self.sorted1]
# long the top 10
self.long = sorted1_symbol[:self.num_fine]
return self.long
def OnData(self, data):
pass
def rebalance(self):
# at the start of rebalancement, if the stock is no longer in the long list, liquidate
long_list = self.long
for i in self.Portfolio.Values:
if (i.Invested):
self.Liquidate(i.Symbol)
# Assign each stock equally. Alternatively you can design your own portfolio construction method
for i in self.long:
self.SetHoldings(i, 1/self.num_fine)
self.reb = 1