Overall Statistics
Total Trades
18
Average Win
0.40%
Average Loss
-2.33%
Compounding Annual Return
-92.488%
Drawdown
5.400%
Expectancy
-0.532
Net Profit
-3.484%
Sharpe Ratio
-3.905
Loss Rate
60%
Win Rate
40%
Profit-Loss Ratio
0.17
Alpha
-1.695
Beta
-0.586
Annual Standard Deviation
0.436
Annual Variance
0.19
Information Ratio
-3.937
Tracking Error
0.436
Treynor Ratio
2.906
Total Fees
$18.00
''' Set the starting cash amount '''
starting_cash = 1000

''' Set True to recieve email notifications '''
email_notify = True

''' Set the email address to recieve notifications '''
email = "jmoore@trifectaky.com"

''' Set True to enable logging '''
log = True

''' Set True for Daily resolution | False for Minute resolution '''
resolution_daily = True

'''
These filters the stock based on a lower % change and higher % change so only stock between this lower limit and upper limit will pass
(i.e -10 and 0 will find a price drop of at least 10% and a maximum of 0% gain)
'''
day_price_change_percent = -7

maximum_stocks = 5

TargetProfit = 3.0
StopLoss = 1.5

'''
Specify the timing of the functions x time before close 
'''
entry_time_beforeclose = 1
screen_time_beforeclose = 2
exit_time_beforeclose = 3

'''

'''
from Risk.NullRiskManagementModel import NullRiskManagementModel

from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Indicators")
AddReference("QuantConnect.Common")

import decimal
import math
import numpy as np
from datetime import datetime, timedelta

import json
import pandas as pd

from custom_tickers import custom_tickers
from filters import *

class CustomAlgorithm(QCAlgorithmFramework):

    def Initialize(self):

        # Set requested data resolution
        if resolution_daily:
            self.UniverseSettings.Resolution = Resolution.Daily
        else:
            self.UniverseSettings.Resolution = Resolution.Minute

        self.SetStartDate(2019, 1, 6)
        #self.SetEndDate(2018, 12, 21)
        self.SetCash(starting_cash)

        self.day_price_change_percent = day_price_change_percent

        self.maximum_stocks = maximum_stocks
        self.TargetProfit = TargetProfit
        self.StopLoss = StopLoss 

        self.filtered_symbols = []
        self.coarse_universe = []
        self.daily_open_price = {}
        self.universe = []
        self.equities = []
        self.screened = False
        
        if resolution_daily:
            self.AddEquity("SPY", Resolution.Daily)
        else:
            self.AddEquity("SPY", Resolution.Minute)
        
        self.SetUniverseSelection(FineFundamentalUniverseSelectionModel(self.CoarseSelectionFunction, self.FineSelectionFunction))
        
        self.SetAlpha(NullAlphaModel())
        self.SetPortfolioConstruction(NullPortfolioConstructionModel()) 
        self.SetExecution(NullExecutionModel())
        self.SetRiskManagement(NullRiskManagementModel())
        
        self.log = log
        self.email_notify = email_notify
        self.email=email
        if email_notify:
            self.Notify.Email(email, "Live!", "Algorithm is now live.")
        
        '''
        buy filtered stocks
        at min before market close
        '''
        self.Schedule.On(self.DateRules.EveryDay("SPY"), 
                         self.TimeRules.BeforeMarketClose("SPY", entry_time_beforeclose), 
                         Action(self.long_entry))
        '''
        liquidate all holdings before market close
        '''
        self.Schedule.On(self.DateRules.EveryDay("SPY"), 
                         self.TimeRules.BeforeMarketClose("SPY", exit_time_beforeclose+5), 
                         Action(self.long_exit))
        '''
        create new filtered universe based on various price conditions
        2 min before market close
        '''
        self.Schedule.On(self.DateRules.EveryDay("SPY"), 
                         self.TimeRules.BeforeMarketClose("SPY", screen_time_beforeclose+10), 
                         Action(self.screen))
        
    def CoarseSelectionFunction(self, coarse):
        custom_coarse = [x for x in coarse if x.Symbol.Value in custom_tickers]
        result = [ x.Symbol for x in custom_coarse ]
        self.Debug('Coarse length: {0}'.format(len(result)))
        return result
    
    def FineSelectionFunction(self, fine):
        for fine in fine:
            try:
                if fine.Symbol.Value not in self.equities:
                    self.AddEquity(fine.Symbol.Value, Resolution.Daily)
                    self.equities.append(fine.Symbol.Value)
                self.universe.append(fine.Symbol)
            except:
                self.Debug('Exception: {0}'.format(fine.Symbol.Value))
                continue
        return self.universe
    
    '''
    filter stock using various price conditions
    '''
    def screen(self):
        self.screened = True

    def OnData(self, data):
        '''
        check for target and stop loss price hit
        for holdings
        '''
        for symbol in self.filtered_symbols:
            if not self.Securities.ContainsKey(symbol): continue
            if not abs(self.Securities[symbol].Holdings.Quantity) > 0: continue
            
            '''
            calculation of cost basis (average holding cost)
            '''
            abs_hold_cost = self.Portfolio[symbol].AbsoluteHoldingsCost
            abs_hold_q = self.Portfolio[symbol].AbsoluteQuantity
            Cost_Basis = float(abs_hold_cost/abs_hold_q) # Determine cost basis of each stock in the portfolio
            
            Current_Price = self.Securities[symbol].Price # Determine current price of each stock
            
            '''
            stop loss check
            '''
            StopLossCondition = Current_Price <= 0.01*(100.0-self.StopLoss)*Cost_Basis
            
            '''
            target profit check
            '''
            TargetProfitCondition = Current_Price >= 0.01*(100.0+self.TargetProfit)*Cost_Basis
            
            if (StopLossCondition):
                self.Liquidate(symbol,"Liquidated: Stop Loss")
            if (TargetProfitCondition):
                self.Liquidate(symbol,"Liquidated: Target Profit")

        if self.screened:
            self.filtered_symbols = []
            if self.log:
                self.Log("Universe Length: "+str(len(self.universe)))
            
      
            for symbol in self.universe:
                if not self.Securities.ContainsKey(symbol): continue
                if not self.Securities[symbol].IsTradable: continue
                if not data.ContainsKey(symbol.Value): continue
                price = data[symbol.Value].Price
                
                '''
                check current price is between range
                '''
                #price_condition = price > self.price_lower_limit and price < self.price_higher_limit
                '''
                get daily history
                '''
                #hist = self.History(symbol, 2, Resolution.Daily)
                hist = self.History(symbol, 3)
                if hist.empty: continue
                
                if self.Log:
                    self.Log(hist['close'])
                
                self.daily_open_price[symbol] = hist["close"][-2]
                prev_close = float(self.daily_open_price[symbol])
                day_close = float(price)
                
                day_change_percent = 100*(day_close-prev_close)/prev_close
            
                
                '''
                check day price change is between range
                '''
                day_change_condition = ( day_change_percent <= self.day_price_change_percent)
                
                '''
                check if all above conditions are met
                '''                            
                condition_check = day_change_condition
                
                if condition_check: 
                    self.filtered_symbols.append(symbol)
                    if self.Log:
                        self.Log('Symbol: {2} Prev Close: {0} Current: {1} Change: {3}'.format(prev_close, day_close, symbol.Value, day_change_percent))
    
            if self.Log:
                self.Log("Filtered Universe Length: {0}".format(len(self.filtered_symbols)))
            self.screened = False

    '''
    take long on stocks in list of self.filtered_symbols limited
    to self.maximum_stocks
    '''
    def long_entry(self):
        
        num_stocks = 0
        percent = 0.0
        
        num_stocks_today = len(self.filtered_symbols)
        
        if self.maximum_stocks < 1: return
        if num_stocks_today < 1: return
        else: percent = 1.0/min(num_stocks_today, self.maximum_stocks)

        temp = 'List of chosen stocks: \n'
        for symbol in self.filtered_symbols:
            if not self.Securities.ContainsKey(symbol): continue
            if not self.Securities[symbol].IsTradable: continue
            if self.Portfolio[symbol].Invested: continue
            if num_stocks >= self.maximum_stocks: return
            
            self.SetHoldings(symbol, percent)
            temp = temp + 'Stock: {0} TargetPercent: {1} \n'.format(symbol.Value, str(round(percent,2)))
            num_stocks += 1

        if self.email_notify:
            self.Notify.Email(self.email, 'Update', temp)
    '''
    liquidate all holdings
    '''
    def long_exit(self):
        for symbol in self.filtered_symbols:
            if not self.Securities.ContainsKey(symbol): continue
        
            if not abs(self.Securities[symbol].Holdings.Quantity) > 0: continue
            self.Liquidate(symbol,"Liquidated: End Of Day")
            
    def OnOrderEvent(self, orderEvent):
        s = orderEvent.Symbol
        if orderEvent.FillQuantity < 0:
            if self.email_notify:
                self.Notify.Email(self.email, 'New Order', 'Sold {0} FillQuantity: {1} FillPrice: {2}'.format(s.Value, orderEvent.FillQuantity, orderEvent.FillPrice))
        elif self.email_notify:
            self.Notify.Email(self.email, 'New Order', 'Bought: {0} FillQuantity: {1} FillPrice: {2}'.format(s.Value, orderEvent.FillQuantity, orderEvent.FillPrice))
        if self.log:
            self.Log(str(orderEvent))
    #END
'''filters sales 1yr change percent greater than 22%, prince greater than 1'''
custom_tickers=['SGH','TLND','AM','AMGP','DLR','PLUG','MDB','AKBA','ASMB','DRYS','CYTK','TRXC','MGNX','CNSL','ADMS','TPL','HRTX','CDXS','NVAX','CTL','PRQR','BPT','SNAP','SALT','KRNY','CLVS','ATNX','DSX','SECO','SRCI','GNK','HOME','TWLO','QNST','SEND','EQT','CVNA','YEXT','SPKE','RDFN','FLXN','FSCT','XXII','RDUS','GTT','SIEB','EPD','EVI','SBLK','SENS','DERM','DGII','ICHR','ANGI','EGLE','SSTI','HQY','LSTR','NYMT','RIGL','SKY','NEWR','THR','DE','SBRA','RVNC','TBPH','ENTA','GCAP','GRUB','NFX','JHG','EVRG','SMHI','RNG','INVH','UPLD','PGNX','CHCT','REXR','MKL','OLBK','IIPR','RESI','SASR','VCEL','CSWC','EQM','PEN','MCB','COUP','ETSY','FIVE','AKCA','ERI','BOMN','ALBO','NFLX','PCH','HLNE','GDEN','ZEN','SE','INST','GOGL','SAFE','MB','RILY','CODI','MIME','WTM','TRC','NTNX','UBSH','VST','BL','TTD','RGEN','LBRDA','KREF','PBYI','GLIBA','ONCE','PVAC','QDEL','KPTI','BXMT','EQBK','BPMC','NCOM','STWD','ESIO','MRUS','LADR','ARRY','SSP','PUB','HPR','TNK','PPBI','SCACU','AGNC','CALM','ICPT','IRMD','SGMO','DX','FBNC','EQGP','LOXO','TWO','IAC','OKTA','HTGC','ADAP','CSFL','RBBN','CRM','LHCG','SYNH','NBHC','BMTC','NBLX','LPI','FFBC','KNOP','AMZN','TDW','WEN','PLT','CAMT','JAG','AVD','GLNG','MTCH','SFLY','MITK','IMMR','WDAY','APTS','DM','ATTU','OMP','VYGR','NNI','TBBK','HUBS','FBMS','DMLP','NLY','MUX','TBK','NVTR','STL','CIR','OCFC','ERIE','GLP','GWRE','JCAP','SFNC','USAC','YY','SCHN','MDR','HLX','DWDP','NOW','PKE','SAIL','GLUU','AYX','EFC','LND','ANET','CLR','CNX','FMC','MOS','CARG','NKTR','VLO','QTRX','IART','EXTR','EDN','WUBA','NMIH','NOG','CARO','BCEI','TTI','WB','CKH','FHN','DISCA','AVAV','MBUU','SPLK','ETM','REI','CARA','GPRK','QRTEA','RYAM','CQP','LABL','RCM','PETX','OPI','VICI','SSW','MCFT','BOOM','APPN','XNCR','PARR','IRTC','FTAI','CORT','LGND','BHGE','ENLC','QIWI','KRP','GFF','QD','SUN','MSBI','ASR','TRHC','PLNT','NVDA','TEAM','AVX','PAYC','FUL','NGL','MGLN','COWN','DLTH','GHL','TWOU','CTSO','DSKE','NANO','WNC','FB','TGS','ABEO','KALV','ATRO','RP','SUPN','PNFP','SSNC','WVE','JT','RLJ','MOMO','AMTD','EXPI','OXY','MSB','VRTS','LNG','PSTG','EVBG','FOLD','ENLK','KMPR','GPK','KNX','PGTI','SHI','EBS','ET','KKR','FPRX','KDP','CRY','WIX','TNDM','WPX','PAA','GRBK','SRPT','PE','PFPT','PRLB','MPLX','JEC','ICLR','EXEL','EXAS','SEDG','SNHY','YNDX','SANW','NBIX','SONA','EBIX','DK','AMWD','DNR','MDP','LFUS','PANW','RBNC','ROKU','GPOR','MTLS','GEL','STAA','XOG','SGRY','SUPV','PETQ','TSLA','TDOC','VBTX','YPF','HCLP','OAS','ECHO','BCML','SBGL','FOXF','BFR','CDEV','CLDR','MTDR','SRLP','INGN','VECO','SMPL','APPF','PAGP','VEEV','PRAH','BDX','PXD','PBF','W','GGB','VRAY','SIVB','AGIO','CERS','TOL','HBMD','EVH','GRVY','FANG','RARE','MEET','PAM','GLPG','TUSK','AXDX','PTLA','BECN','VIRT','KEX','MRO','HBCP','TELL','JOBS','SINA','RGNX','DVAX','TECD','CRZO','BEAT','CZR','BSTI','DRNA','PTCT','EOG','REDU','APA','ZTO','BABA','MEDP','CDNA','OIS','SGEN','EDIT','FET','PATK','NYNY','SQ','TEO','COLL','POPE','CYRX','SUZ','RRC','GNMK','NVTA','AXGN','CJ','CXO','HP','LIVX','CPE','KOS','LMRK','SFIX','MAXR','MED','LOMA','SM','NVCR','ENVA','DXCM','ALGN','CCS','SIEN','SLCA','FRAC','ESTE','GGAL','GDS','WLL','RUN','TAL','SIGA','XELA','EDU','PTEN','BRY','SOI','MU','VNOM','WBAI','PUMP','LEN','ABMD','ACAD','GSM','MYOK','WTTR','WRD','GOGO']