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