| Overall Statistics |
|
Total Trades 1138 Average Win 0.41% Average Loss -0.41% Compounding Annual Return -7.970% Drawdown 20.600% Expectancy -0.047 Net Profit -11.208% Sharpe Ratio -0.435 Probabilistic Sharpe Ratio 2.920% Loss Rate 53% Win Rate 47% Profit-Loss Ratio 1.01 Alpha -0.053 Beta -0.106 Annual Standard Deviation 0.132 Annual Variance 0.017 Information Ratio -0.473 Tracking Error 0.203 Treynor Ratio 0.539 Total Fees $1244.57 |
from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Algorithm.Framework")
AddReference("QuantConnect.Common")
import pandas as pd
import numpy as np
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
import talib as ta
from sklearn.svm import SVC
from sklearn import metrics
from QuantConnect.Indicators import *
from QuantConnect.Algorithm import *
from QuantConnect import *
from QuantConnect.Algorithm.Framework import *
from QuantConnect.Algorithm.Framework.Alphas import *
from QuantConnect.Algorithm.Framework.Execution import *
class myAlgo(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2015, 1, 1) # Set Start Date
self.SetEndDate(2020, 1, 1)
self.SetCash(100000) # Set Strategy Cash
## Helper variables for universe selection and checking for conditions to update only at the
## beginning of each month
self.num_coarse = 60
self.curmonth = self.Time.month
# spy = self.AddEquity("SPY", Resolution.Daily)
# aapl = self.AddEquity("AAPL", Resolution.Daily)
# msft = self.AddEquity("MSFT", Resolution.Daily)
# amzn = self.AddEquity("AMZN", Resolution.Daily)
# googl = self.AddEquity("GOOGL", Resolution.Daily)
# self.symbols = [spy.Symbol, aapl.Symbol, msft.Symbol, amzn.Symbol, googl.Symbol]
self.AddUniverse(self.CoarseSelectionFunction)
self.spy = self.AddEquity("SPY", Resolution.Daily).Symbol
# self.SetAlpha(ConstantAlphaModel(InsightType.Price, InsightDirection.Up, TimeSpan.FromMinutes(20), 0.025, None))
# self.SetExecution(ImmediateExecutionModel())
self.Schedule.On(self.DateRules.Every(DayOfWeek.Monday), self.TimeRules.AfterMarketOpen(self.spy, 60), Action(self.trade))
# self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen(self.spy, 5), Action(self.trade))
self.historical_data = 276
# self.feature_window = 250
self.long_pos = 5
self.short_pos = 5
self.short_period = 20
self.long_period = 200
self.symbols = []
def CoarseSelectionFunction(self, coarse):
sym = sorted([x for x in coarse if x.HasFundamentalData and x.Price>20],
key = lambda x: x.DollarVolume, reverse = True)
syms = sym[:self.num_coarse]
for i in syms:
if not self.Securities.ContainsKey(i.Symbol):
# j = self.AddEquity(i.Symbol, Resolution.Daily)
self.symbols.append(i.Symbol)
return self.symbols
def OnData(self, data):
'''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
Arguments:
data: Slice object keyed by symbol containing the stock data
'''
pass
def trade(self):
# prices = self.History(self.symbols, self.historical_data, Resolution.Daily)
longs = []
shorts = []
thismonth = self.Time.month
this = 'this month: ' + str(thismonth)
self.Debug(this)
if self.curmonth != thismonth:
self.Liquidate()
cur = 'cur_month: ' + str(self.curmonth)
self.Debug(cur)
self.curmonth = thismonth
for symbol in self.symbols:
security = self.Securities[symbol]
if security.IsTradable:
# price = prices.loc[symbol.Value]
price = self.History(symbol, self.historical_data, Resolution.Daily)
# s_sma = self.History(symbol, self.short_period, Resolution.Daily)
# l_sma = self.History(symbol, self.long_period, Resolution.Daily)
# price_hist = self.History(symbol, 10, Resolution.Daily)
if (not price.empty): #and (not s_sma.empty) and (not l_sma.empty) and (not price_hist.empty):
price_list = list(price['close'])
short_sma = price['close'][-self.short_period:].mean()
long_sma = price['close'][-self.long_period:].mean()
close_10_days = price_list[-10:-9]
current_close = price_list[-1:]
# price_list = list(prices.loc[symbol.Value]['close'])
price_df = pd.DataFrame(price_list)
price_df = price_df.rename(columns = {0:'close'})
price_df['sma_5'] = price_df['close'].rolling(window = 5).mean()
price_df['rsi'] = self.rsi(price_df, 14)
price_df['ema_12'] = self.ema(price_df['close'], 12)
price_df['ema_26'] = self.ema(price_df['close'], 26)
price_df['macd'] = price_df['ema_12'] - price_df['ema_26']
price_df['macd_signal_line'] = self.ema(price_df['macd'], 9) #macd signal line
price_df['macd_crossover'] = price_df['macd'] - price_df['macd_signal_line'] #diff between macd and its signal line
price_df.dropna(inplace=True)
if len(price_df) != 250:
continue
price_df['label'] = price_df.apply(self.label, axis=1)
X = price_df[['rsi', 'macd', 'macd_crossover']]
y = price_df['label']
#train for the first 249 days
X_train = X[:-1]
y_train = y[:-1]
#predict the 250th day
X_test = X[-1:]
y_test = y[-1:]
ss = StandardScaler()
#usig principal component analysis machine learning to ensure no overfitting of variables
pca = PCA(n_components = 2)
X_train = ss.fit_transform(X_train)
X_test = ss.transform(X_test)
X_train = pca.fit_transform(X_train)
X_test = pca.transform(X_test)
#fitting the trainset to support vector classifier machine learning
clf = SVC(kernel = 'rbf', C=10, max_iter = 5, random_state = 42)
clf.fit(X_train, y_train)
#predict the 250th day (whether to buy or sell on the last day)
y_pred = clf.predict(X_test)
train_r2 = clf.score(X_train, y_train)
test_r2 = metrics.accuracy_score(y_test, y_pred)
pred = "prediction: " + str(y_pred)
train_str = "train r^2: " + str(train_r2)
test_str = "test r^2: " + str(test_r2)
self.Debug(pred)
self.Debug(train_str)
self.Debug(test_str)
#momentum used to rank the stocks
#so that the top 5 will be will enter long while the bottom 5 will enter short
momentum = current_close[0] - close_10_days[0]
if y_pred == 1 and short_sma>long_sma:
longs.append([symbol, momentum])
i = "long: " + str(symbol) + " " + str(momentum)
self.Debug(i)
elif y_pred == -1 and short_sma<long_sma:
shorts.append([symbol, momentum])
i = "short: " + str(symbol) + " " + str(momentum)
self.Debug(i)
if longs!=[]:
long_df = self.list_(longs)
long_list = long_df[:self.long_pos]['symbol'].tolist()
else:
long_list = longs
if shorts!=[]:
short_df = self.list_(shorts)
short_list = short_df[-self.short_pos:]['symbol'].tolist()
else:
short_list = shorts
for i in self.Portfolio.Values:
# check if stock is in portfolio
# exit/liquidate if it is currently in holding but not found in our long/short list
if i.Invested:
self.Debug(i.Price)
if i.IsLong:
if (i.Symbol not in long_list):
self.Liquidate(i.Symbol)
ii = "liquidate: " + str(i.Symbol)
self.Debug(ii)
elif i.IsShort:
if (i.Symbol not in short_list):
self.Liquidate(i.Symbol)
ii = "liquidate: " + str(i.Symbol)
self.Debug(ii)
for l in long_list:
if self.Portfolio[l].Quantity == 0:
#buy if stock is not in holding but found in our long list
self.SetHoldings(l,1.0/(len(long_list)+len(short_list)))
ll = "buy: " + str(l)
self.Debug(ll)
for s in short_list:
if self.Portfolio[s].Quantity == 0:
#sell if stock is not in holding but found in our short list
self.SetHoldings(s,-1.0/(len(long_list)+len(short_list)))
ss = "sell: " + str(s)
self.Debug(ss)
def ema(self, price, window):
return pd.Series.ewm(price, span=window).mean()[window-1:]
def rsi(self, df, window):
i = 1
pos_period = []
neg_period = []
close = list(df['close'])
while i <= (len(df)-1):
diff = close[i] - close[i-1]
if diff>0:
pos_period.append(diff)
neg_period.append(0)
else:
pos_period.append(0)
neg_period.append(abs(diff))
i = i+1
pos_period = pd.Series(pos_period)
neg_period = pd.Series(neg_period)
ema_u = self.ema(pos_period, window)
ema_d = self.ema(neg_period, window)
rsi = 100 - (100/(1+(ema_u/ema_d)))
return rsi
def label(self, df):
if df['close'] > df['sma_5']:
return 1 #buy
else:
return -1 #sell
def list_(self, lists):
df = pd.DataFrame(lists)
df = df.rename(columns = {0:'symbol', 1:'momentum'})
df['rank'] = df['momentum'].rank(ascending=False)
df = df.sort_values('rank')
return df