| 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']