| Overall Statistics |
|
Total Orders 9 Average Win 73.66% Average Loss -5.64% Compounding Annual Return 31.221% Drawdown 56.900% Expectancy 9.545 Start Equity 100000 End Equity 511290.73 Net Profit 411.291% Sharpe Ratio 0.706 Sortino Ratio 0.816 Probabilistic Sharpe Ratio 18.567% Loss Rate 25% Win Rate 75% Profit-Loss Ratio 13.06 Alpha 0.162 Beta 1.124 Annual Standard Deviation 0.366 Annual Variance 0.134 Information Ratio 0.554 Tracking Error 0.312 Treynor Ratio 0.23 Total Fees $250.33 Estimated Strategy Capacity $61000.00 Lowest Capacity Asset BIGT Y7JDXRV74ZMT Portfolio Turnover 0.41% Drawdown Recovery 1383 Avg. Lost% Per Losser -5.61% Avg. Win% Per Winner 73.69% Max Win% 147.22% Max Loss% -5.61% *Profit Ratio 23.93 |
'''
Usage:
def Initialize(self):
self.log = Log(self)
# code xxxxxx
self.log.log("---->1")
'''
from AlgorithmImports import *
import time
class Log():
def __init__(self, algo):
self.timer = round(time.time() * 1000)
self.algo = algo
self.maxLine = 400
self.count = 0
self.debug(f"Live mode={self.algo.live_mode}.....Log Initialized")
def log(self, message):
self.algo.Log(f"[LOG] {message}")
def info(self, message):
now = round(time.time() * 1000)
timer = (now - self.timer) / 1000
self.timer = now
if (self.algo.Time <= self.algo.Time.replace(hour=9, minute=35)):
self.algo.Log(f"[INFO] {message}")
def debug(self, message):
if (self.count < self.maxLine or self.algo.live_mode):
self.algo.Log(f"[DEUBG] {message}")
self.count += 1
def live(self, message):
if self.algo.live_mode:
self.algo.Log(f"[DEUBG] {message}")
# ================================================================================
# DISCLAIMER
# ================================================================================
# This code is provided free of charge for EDUCATIONAL PURPOSES ONLY. Users are
# granted permission to study, modify, and redistribute this script for non-
# commercial learning and research.
#
# The author and developers of this code assume NO LIABILITY for any financial
# losses, trading damages, or missed opportunities resulting from the use or
# misuse of this algorithm. Quantitative trading involves significant risk,
# and past performance is not indicative of future results
#
# This code is provided "AS IS" without any warranties. The author does not
# provide technical support, bug fixes, or maintenance for this script.
# Users are responsible for their own due diligence and backtesting before
# considering any live deployment
from AlgorithmImports import *
from datetime import timedelta, datetime
from security_initializer import CustomSecurityInitializer
from utils import Utils
from log import Log
'''
Scope: All in best performance ETF except cryto
'''
# lean project-create --language python "ALLIN03"
# lean cloud backtest "ALLIN03" --push --open
class ALLIN03(QCAlgorithm):
def initialize(self):
# Set backtest range and initial capital
self.set_start_date(2020, 1, 1)
self.set_end_date(2025, 12, 31)
self.init_cash = 100000
self.set_cash(self.init_cash) # Set Strategy Cash
# Universe settings: Resolution.HOUR is used for trading, though selection is annual
self.universe_settings.resolution = Resolution.HOUR
# Define the benchmark and the base ETF for constituent selection (SPY)
self._symbol = self.add_equity("SPY", Resolution.HOUR).Symbol
# Add a universe that tracks the constituents of the SPY ETF
self.add_universe_selection(ETFTopPickUniverseSelectionModel(self))
# Alpha Model: Emits a daily "UP" insight for all symbols currently in the universe
self.add_alpha(NonBenchmarkConstantAlphaModel(self, InsightType.PRICE, InsightDirection.UP, timedelta(days=1)))
# Security Initializer: Seeds new securities with historical price data to prevent trade delays
self.set_security_initializer(CustomSecurityInitializer(InteractiveBrokersBrokerageModel(AccountType.CASH), FuncSecuritySeeder(self.get_last_known_prices)))
# Use Interactive Brokers brokerage model with a Cash account (no margin)
self.set_brokerage_model(BrokerageName.INTERACTIVE_BROKERS_BROKERAGE, AccountType.CASH)
# Portfolio settings: Fully invest, rebalance when insights change, and set margin buffer
self.settings.free_portfolio_value_percentage = 0.00
self.settings.rebalance_portfolio_on_insight_changes = True
self.settings.minimum_order_margin_portfolio_percentage = 0.5
# Construction Model: Distributes portfolio value equally among all symbols with active insights
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
# Dictionary to cache the selected symbols per year to avoid redundant heavy calculations
self.picked = {}
self.log = Log(self)
self.utils = Utils(self, self._symbol)
self.schedule.on(self.date_rules.every_day(), self.time_rules.before_market_close(self._symbol, 0), self.utils.plot)
def on_end_of_algorithm(self):
self.utils.stats()
class ETFTopPickUniverseSelectionModel(FundamentalUniverseSelectionModel):
def __init__(self, algo):
self.whitelist_etfs = [
'DAPP','HIBS','AAAU','AADR','AAXJ','ACES','ACIO','ACSI','ACWI','ACWV','ACWX','ADME','AESR','AFK','AFLG','AFMC','AFSM','AGG','AGGY','AGZD','AIA','AIEQ','AIRR','ALFA','ALTS','ALTY','AMOM','AMU','AMUB','ANGL','AOA','AOK','AOM','AOR','ARCM','ARGT','ARKQ','ARKW','ASEA','ASET','ASHR','ASHS','ATMP','AUSF','AVDE','AVDV','AVEM','AVUS','AVUV','BAB','BAPR','BAR','BATT','BAUG','BBAX','BBC','BBCA','BBEU','BBH','BBIN','BBJP','BBP','BBRE','BCD','BCI','BDCZ','BDEC','BFIT','BFOR','BIBL','BIL','BIV','BIZD','BJAN','BJK','BJUL','BJUN','BKF','BKLN','BLCN','BLES','BLOK','BNO','BNOV','BOCT','BOSS','BOTZ','BOUT','BRF','BSDE','BSEP','BTAL','BTEC','BUG','BUL','BUY','BWX','BWZ','CALF','CARZ','CATH','CCOR','CDC','CDL','CEFS','CEMB','CEW','CEZ','CFA','CFO','CGW','CHIQ','CIBR','CID','CIL','CLIX','CLOU','CMBS','CMDY','CN','CNBS','CNRG','CNXT','CNYA','COM','COMB','COMT','COPX','CORN','CORP','COW','COWZ','CPER','CPI','CQQQ','CRAK','CRBN','CSA','CSB','CSD','CSM','CUT','CVY','CWB','CWI','CWS','CXSE','CYB','CZA','DALI','DAUG','DAX','DBA','DBAW','DBB','DBC','DBE','DBEF','DBEM','DBEU','DBEZ','DBJP','DBMF','DBO','DBP','DBV','DDIV','DDLS','DDWM','DEEF','DEF','DEM','DES','DEUS','DEW','DFE','DFJ','DFNL','DGRE','DGRO','DGRS','DGRW','DGS','DGT','DGZ','DHS','DIA','DIAL','DIM','DINT','DIVB','DIVO','DJCI','DJD','DJP','DLN','DLS','DMRE','DNL','DNOV','DOG','DOL','DON','DOO','DRIP','DRIV','DRSK','DSI','DSTL','DTD','DTEC','DTH','DURA','DUSA','DVLU','DVOL','DVP','DVY','DVYA','DVYE','DWAS','DWAW','DWM','DWMF','DWSH','DWUS','DWX','DXJ','DYNF','EAGG','EASG','EBIZ','ECNS','ECON','ECOW','EDEN','EDIV','EDOG','EDOW','EDV','EELV','EEM','EEMA','EEMO','EEMS','EEMV','EEMX','EES','EET','EEV','EFA','EFAD','EFAS','EFAV','EFAX','EFG','EFNL','EFU','EFV','EFZ','EIDO','EINC','EIRL','EIS','EJUL','ELD','EMB','EMDV','EMGF','GFOF','EMHY','EMIF','EMLC','EMLP','EMMF','EMNT','EMQQ','EMTL','EMTY','EMXC','ENFR','ENOR','ENZL','EPHE','EPI','EPOL','EPP','EPRF','EPS','EPU','EQAL','EQL','EQWL','ERM','ESGD','ESGE','ESGU','ESGV','ESML','ESPO','EUDG','EUDV','EUFN','EUM','EUSA','SATO','EVX','EWA','EWC','EWD','EWG','EWH','EWI','EWJ','EWJV','EWK','EWL','EWM','EWN','EWO','EWP','EWQ','EWS','EWT','EWU','EWUS','EWV','EWW','EWX','EWY','EWZ','EWZS','EXI','EXT','EYLD','EZA','EZM','EZU','FAAR','FAB','FAD','FALN','FAN','FAUG','FBT','FCA','FCAL','FCEF','FCG','FCOM','FCPI','FCTR','FCVT','FDD','FDEM','FDEV','FDHY','FDIS','FDL','FDLO','FDM','FDMO','FDN','FDNI','FDRR','FDT','FDVV','FEM','FEMB','FEMS','FENY','FEP','FEUZ','FEX','FEZ','FFIU','FFR','FFTY','FGD','FGM','FHLC','FID','FIDI','FIDU','FILL','FINX','FISR','FITE','FIVA','FIW','FIXD','FJP','FKU','FLAU','FLAX','FLBL','FLBR','FLCA','FLCH','FLCO','FLDR','FLEE','FLGB','FLGR','FLHY','FLIA','FLIN','FLJH','FLJP','FLKR','FLLA','FLMB','FLMI','FLMX','FLN','FLQL','FLQM','FLQS','FLRN','FLRT','FLSA','FLSP','FLSW','FLTB','FLTR','FLTW','FM','FMAT','FMB','FMF','FMHI','FMK','FNCL','FNDA','FNDB','FNDC','FNDE','FNDF','FNDX','FNGS','FNK','FNOV','FNX','FNY','FPA','FPE','FPEI','FPX','FPXE','FPXI','FQAL','FRDM','FREL','FRI','FSMB','FSMD','FSTA','FSZ','FTA','FTAG','FTC','FTCS','FTEC','FTGC','FTHI','FTLS','FTRI','FTSD','FTSL','FTSM','FTXG','FTXH','FTXL','FTXN','FTXO','FUD','FUTY','FV','FVAL','FVC','FVD','FVL','FXA','FXB','FXC','FXD','FXE','FXF','FXG','FXH','FXI','FXL','FXN','FXO','FXR','FXU','FXY','FXZ','FYC','FYLD','FYT','FYX','GAA','GAL','GAMR','GBDV','GBF','GBIL','GCC','GDMA','GDX','GDXJ','GEM','GFIN','GHYB','GHYG','GIGB','GII','GLD','GLDM','GLTR','GMF','GMOM','GNOM','GNR','GOAU','GOEX','GQRE','GREK','GRID','GRN','GSEU','GSEW','GSG','GSIE','GSJY','GSLC','GSP','GSSC','GTIP','GUNR','GURU','GVAL','GVI','GVIP','GWX','GXC','GYLD','HACK','HAIL','HAP','HAUZ','HAWX','HDEF','HDG','HDMV','HDV','HEDJ','HEEM','HEFA','HERD','HERO','HEWJ','HEZU','HFXI','HIBL','HIPS','HLAL','HMOP','HNDL','HOMZ','HSCZ','HSPX','HTAB','HTEC','HTRB','HTUS','HUSV','HYD','HYDB','HYDW','HYEM','HYG','HYGH','HYGV','HYHG','HYLB','HYLD','HYLS','HYMB','HYUP','HYXU','HYZD','IAI','IAK','IAT','IAU','IBB','IBDQ','IBDR','IBDS','IBDT','IBDU','IBHE','IBMN','IBMO','IBMP','IBMQ','IBND','IBUY','ICF','ICLN','ICOW','IDEV','IDHQ','IDIV','IDLV','IDMO','IDNA','IDOG','IDRV','IDU','IDV','IDX','IEDI','IEF','IEFA','IEI','IEMG','IEO','IETC','IEUR','IEUS','IEV','IEZ','IFGL','IFRA','IFV','IG','IGBH','IGEB','IGF','IGHG','IGIB','IGLB','IGM','IGOV','IGSB','IGV','IHAK','IHDG','IHE','IHF','IHI','IIGD','IJH','IJJ','IJK','IJR','IJS','IJT','IJUL','ILF','ILTB','IMOM','IMTB','IMTM','INCO','INDA','INDL','INDS','INDY','INFR','INKM','INTF','IOO','IPAC','IPAY','IPKW','IPO','IPOS','IQDF','IQDG','IQDY','IQLT','IQSI','IQSU','ISCF','ISHG','ISMD','ISRA','ISTB','ITA','ITB','ITEQ','ITM','ITOT','IUS','IUSG','IUSV','IVAL','IVE','IVLU','IVOG','IVOL','IVOO','IVOV','IVV','IVW','IWB','IWC','IWD','IWF','IWL','IWM','IWN','IWO','IWP','IWR','IWS','IWV','IWX','IWY','IXC','IXG','IXJ','IXN','IXP','IXUS','IYC','IYE','IYF','IYG','IYH','IYJ','IYK','IYM','IYR','IYT','IYW','IYY','IYZ','IZRL','JETS','JHEM','JHMD','JHML','JHMM','JHSC','JMBS','JMIN','JMOM','JMUB','JOYY','JPEM','JPIN','JPMB','JPME','JPN','JPSE','JPUS','JPXN','JQUA','JSMD','JSML','JUST','JVAL','JXI','KARS','KBA','KBE','KBWB','KBWD','KBWP','KBWR','KBWY','KCE','KEMQ','KEMX','KGRN','KIE','KNG','KOCT','KOIN','KOMP','KORP','KRE','KRMA','KSA','KURE','KWEB','KXI','LDSF','LDUR','LEGR','LEMB','LFEQ','LGH','LGLV','LGOV','LIT','LKOR','LMBS','LOUP','LQD','LQDH','LQDI','LRGE','LRGF','LSAF','LTPZ','LVHD','LVHI','MAGA','MAGS','MBB','MBSD','MCHI','MDIV','MDY','MDYG','MDYV','MEAR','MFDX','MFEM','MFUS','MGC','MGK','MGV','MILN','MINT','MJ','MLN','MLPB','MLPX','MMIN','MMIT','MMTM','MNA','MOAT','MOM','MOO','MORT','MOTI','MOTO','MRGR','MUNI','MUST','MVIN','MXI','MYY','MZZ','NACP','NANR','NERD','NETL','NFLT','NFRA','NFTY','NIB','NLR','NOCT','NTSX','NUAG','NUDM','NUEM','NUHY','NULC','NULG','NULV','NUMG','NUMV','NURE','NUSA','NUSC','NXTG','OBOR','OCIO','OEF','OGIG','OIH','OIL','OILK','OLD','OMFL','OMFS','ONEO','ONEQ','ONEV','ONEY','ONLN','ORG','OSCV','OUNZ','OUSA','OUSM','OVF','OVL','OVM','OVS','PALL','PAPR','PAUG','PAWZ','PBD','PBE','PBJ','PBP','PBTP','PBUS','PBW','PCEF','PCY','PDBC','PDEC','PDN','PDP','PEJ','PEK','PEX','PEXL','PEY','PEZ','PFF','PFFD','PFFR','PFI','PFIG','PFLD','PFM','PFXF','PGF','PGHY','PGJ','PGM','PGX','PHB','PHDG','PHO','PICB','PICK','PID','PIE','PIN','PIO','PIZ','PJAN','PJP','PJUL','PJUN','PKB','PKW','PLW','PNOV','PNQI','POCT','PPA','PPH','PPLN','PPLT','PPTY','PREF','PRF','PRFZ','PRN','PRNT','PSCC','PSCD','PSCF','PSCH','PSCI','PSCT','PSCU','PSEP','PSET','PSI','PSK','PSL','PSM','PSP','PSQ','PSR','PTEU','PTF','PTH','PTIN','PTLC','PTMC','PTNQ','PUI','PVAL','PVI','PWB','PWC','PWS','PWV','PWZ','PXE','PXF','PXH','PXI','PYZ','PZA','PZT','QABA','QAI','QARP','QAT','QCLN','QDEF','QDF','QDIV','QED','QEFA','QEMM','QGRO','QINT','QLC','QLTA','QLV','QLVD','QLVE','QMOM','QQEW','QQH','QQQ','QQQE','QQXT','QRFT','QTEC','QTUM','QUAL','QUS','QVAL','QWLD','QYLD','RAAX','RAFE','RALS','RDVY','RECS','REET','REGL','REK','REMX','RESI','REVS','REZ','RFCI','RFDA','RFDI','RFEM','RFEU','RFFC','RFG','RFV','RIGS','RINF','RING','RISE','RLY','RNEM','ROAM','ROBO','ROBT','RODM','ROKT','ROSC','ROUS','RPAR','RPG','RPV','RSP','RSX','RTH','RVNU','RWJ','RWK','RWL','RWM','RWO','RWR','RWX','RXI','RYLD','RYU','RZG','RZV','SBB','SBIO','SBM','SCHA','SCHB','SCHC','SCHD','SCHE','SCHF','SCHG','SCHH','SCHI','SCHJ','SCHK','SCHM','SCHO','SCHP','SCHQ','SCHR','SCHV','SCHX','SCJ','SCZ','SDCI','SDEM','SDG','SDIV','SDOG','SDP','SDVY','SDY','SECT','SEF','SEIX','SFY','SFYF','SFYX','SGDJ','SGDM','SGG','SGOL','SH','SHAG','SHE','SHM','SHY','SHYD','SHYL','SIJ','SIL','SILJ','SIMS','SIVR','SIZE','SJB','SJNK','SKF','SKOR','SKYY','SLQD','SLV','SLVP','SLX','SLYG','SLYV','SMB','SMCP','SMDV','SMH','SMIN','SMLF','SMLV','SMMD','SMMU','SMMV','SMOG','SNPE','SNSR','SOCL','SOXX','SOYB','SPAB','SPBO','SPDV','SPDW','SPEM','SPEU','SPFF','SPGM','SPGP','SPHB','SPHD','SPHQ','SPHY','SPIP','SPLB','SPLG','SPLV','SPMB','SPMD','SPMO','SPMV','SPSB','SPSK','SPSM','SPTI','SPTL','SPTM','SPTS','SPUS','SPVM','SPVU','SPXE','SPXN','SPXT','SPXV','SPY','SPYD','SPYG','SPYV','SPYX','SQLV','SRET','SRLN','SRVR','SSPY','STOT','STPZ','SUB','SUSA','SUSB','SUSC','SUSL','SWAN','SYV','SZNE','TAGS','TAIL','TAN','TAO','TAXF','TBF','TBLU','TBX','TDIV','TDTF','TDTT','TDV','TFI','TFLO','THD','TILT','TIPX','TIPZ','TLH','TLT','TLTD','TLTE','TMDV','TMFC','TOK','TOKE','TOLZ','TOTL','TPHD','TPIF','TPLC','TPSC','TPYP','TRTY','UAE','UBOT','UCIB','UCON','UDN','UEVM','UFO','UGA','UITB','UIVM','ULVM','UNG','URA','URNM','URTH','USCI','USFR','USIG','USL','USMC','USMF','USMV','USO','USOI','USRT','USSG','USTB','USVM','UTES','UTRN','UUP','VALQ','VAMO','VAW','VB','VBK','VBR','VCIT','VCR','VDC','VDE','VEA','VEGA','VEGI','VEGN','VEU','VFH','VFMF','VFMO','VFMV','VFQY','VFVA','VGIT','VGK','VGLT','VGSH','VGT','VHT','VIDI','VIG','VIGI','VIOG','VIOO','VIOV','VIS','VIXM','VIXY','VLU','VMBS','VNLA','VNM','VNQ','VNQI','VO','VOE','VONE','VONG','VONV','VOO','VOOG','VOOV','VOT','VOX','VPC','VPL','VPU','VRAI','VRIG','VRP','VSDA','VSGX','VSL','VSMV','VSS','VT','VTC','VTHR','VTI','VTIP','VTV','VTWG','VTWO','VTWV','VUG','VUSE','VV','VWO','VWOB','VXF','VXUS','VXZ','VYM','VYMI','WBIY','WCLD','WDIV','WIL','WIP','WLDR','WOMN','WOOD','WPS','WTMF','XAR','XBI','XCEM','XDIV','XHB','XHE','XHS','XITK','XLB','XLC','XLE','XLF','XLG','XLI','XLK','XLP','XLRE','XLSR','XLU','XLV','XLY','XME','XMHQ','XMLV','XMMO','XMPT','XMVM','XNTK','XPH','XRLV','XRT','XSD','XSHD','XSHQ','XSLV','XSMO','XSOE','XSVM','XSW','XT','XTL','XTN','YLD','YOLO','YXI','YYY','ZIG','ZROZ','EJAN','IJAN','KJAN','NJAN','RDOG','JDST','GXG','PLTM','IBTE','AFIF','SMDD','DUST','XES','AMZA','MLPA','GLIN','GLDI','AMLP','JPIB','HYXF','TDSA','BTEK','TGIF','LIV','DOCT','INDF','VPN','MOON','DEFN','FEVR','BNE','QQD','QPT','OVT','EPRE','PVAL','CRUZ','UNL','SMI','CYA','HHH','INNO','WTAI','BKES','RJA','IUSA','CWC','CSH','WEAT','DAM','PSCE','NBCC','CANE','WEED','UAV','ORFN','SESG','EMCA','SLVO','INC','TGN','INTL','DIP','PBL','ENAV','BIGT','CETF','TUNE','PXJ','KEM','ALUM','INOV','SOF','CHAI','PSH','ISPY','RTRE','AUGM','LIAB','GLBL','TRSY','INFO','SCY','EDGE','EGLE','FLXN','XDIV','BITK','ESK',
'EDGI','EDGH','EDGU','BDIV','SAWS','LODI','TRFM','FWD','ILOW','TAFM','TAFL','LRGC','LOWV','BCIM','ABEQ','XVOL','ADPV','AGOX','RULE','JSTC','SCAP','GK','PSIL','MSOS','QPX','ACT','LEXI','ATFV','ALAI','CNEQ','APLU','AINP','ABCS','CCNR','GRI','SMTH','DIVD','FUSI','MGNR','AHLT','MUSI','CATF','FDG','FLV','ESGA','ESGY','MID','ISWN','HCOW','COWS','QDVO','IDVO','SMAP','THNR','CARY','NDOW','AOTG','VSLU','UPSD','ARKG','ARKK','ARMR','ROE','PPI','GQQQ','AZNH','ASPY','RORO','USAF','AVGV','AVGE','AVNV','AVNM','AVEE','AVES','AVXC','AVIV','AVDS','AVMA','AVRE','AVSD','AVSE','AVSU','AVLV','AVLC','AVMC','AVMV','AVSC','CHGX','FOMO','NXTE','KNO','RINC','BGDV','BGIG','SMIG','SCDV','MGMT','BCIL','PCMM','BSR','LCTU','LCTD','TFPN','BKCI','BKDV','BKEM','BKGI','BKIV','BKIE','BKLC','BKMC','BKSE','BINV','BUSA','BSMC','DVAL','BWET','BWTG','BDGS','TBFC','BAMA','BAMD','BAMG','BAMO','BAMV','BAFE','BRNY','SMRI','CVIE','CPNQ','CPSN','CVRT','CANQ','CPNS','CPNJ','CPSO','CPST','CPSY','CPSJ','CPSM','CPSA','CPSL','SROI','CPRJ','CPRO','CVLC','CVMC','CVSE','CAMX','MFUT','BLDG','LYLD','CGCV','CGBL','CGUS','CGCP','CGDV','CGGE','CGGO','CGGR','CGIC','CGIE','CGXU','CGHM','CGMU','CGNG','CCSB','PUTD','CPRY','CPSD','CBLS','CBSE','ROPE','ESGW','ESGS','CGV','CAML','CSMD','CGRO','FUNL','CPAI','CCSO','SPC','DIVP','DWLD','SSUS','SSFI','XMAG','QQQT','QQQY','BIGY','USOY','IWMY','SPYT','HF','DFGR','DFLV','DFVX','DFCA','DEXC','DFSE','DFAE','DEHP','DFEM','DFEV','DFGP','DGCB','DFIP','DFSI','DFAI','DFIC','DIHP','DFIS','DISV','DFIV','DXIV','DFNM','DCOR','DFAC','DFAU','DFUS','DUHP','DFUV','DFAR','DFAS','DFSV','DFSU','DFAT','DXUV','DFAX','DFAW','KNOW','QQQD','HIPR','WFH','DSTX','DSMC','DCMT','DFVE','DMX','DRAI','GCAL','BBLU','BSVO','EAGL','EVLN','EVIM','EVTR','EMPB','LUXE','REC','CHRG','TERM','WUGI','ESN','SIXH','SIXL','SIXA','SXQG','SIXS','TDSB','RITA','AWAY','AMNA','ELCV','TDSC','CLSM','LFSC','TACK','FLCG','FLCV','FSCC','FTRB','FDV','FBCG','FBCV','FRNW','FCLD','FDHT','FBOT','FDFF','FMED','FDTX','FDCF','FDIF','FDRV','FEMR','FELC','FELG','FELV','FMDE','FESM','FEAC','FENI','FFDI','FFGX','FFLV','FHEQ','FSEC','FLDB','FMET','FSST','FLRG','FDWM','FIRI','FIRS','FEGE','FEOE','AGQI','FDIV','CAAA','FLM','FNI','FDTS','XPND','FTGS','HSMV','MISL','ILDR','MMLG','MMSC','FTXR','FMNY','EMOT','DEED','BNGE','BRIF','PRAY','FEDM','FEUS','FEIG','ESGG','ESG','FMCE','FMCX','FMQQ','GRNY','FORH','KONG','FPAG','YLDE','BUYZ','XDAT','FFOG','HELX','INCM','IQM','MCSE','MBOX','FLCE','FARX','FOPC','FINT','FGSM','FTIF','RND','FAI','EIPX','XIJN','XIMR','QCOC','QMAG','QMMY','QMNV','FTCE','QCJL','QCAP','RJMG','SCIO','XMAY','UXOC','DOGG','XFEB','BGLD','YJUN','YDEC','QSPT','RDVI','SDVD','TDVI','FSEP','FDEC','DFEB','DSEP','DDEC','XJUN','XSEP','GMAY','SMAY','NOVM','RSJN','RSSE','FJUN','XISE','XIDE','FFEB','FMAY','FMAR','DMAY','DJUN','DMAR','XMAR','XAUG','XNOV','GMAR','GJUN','GSEP','GNOV','GDEC','SFEB','SAUG','SNOV','YSEP','YMAR','WCMI','FHDG','FDND','TSEP','MARM','RSDE','KNOW','XRLX','XCOR','XFLX','GCAD','GABF','GMMA','EMM','IPAV','GENM','GENT','EMC','GPIX','ONOF','KROP','BRAZ','SHLD','DMAT','DYLG','DJIA','EDOC','HYDR','NDIA','NGE','QCLR','QYLG','QRMI','QTR','RYLG','RSSL','XCLR','XYLG','XRMI','XTR','CEFA','RAYS','FLOW','PAVE','WNDY','QLTI','GMOI','GMOV','YALL','GSIG','GCOR','GDOC','GINN','GSEE','GSID','GUSA','GSC','GGUS','GHTA','GVLU','GSPY','DARP','GLOV','GSC','GTEK','GPIQ','RCD','RHS','SEA','GFGF','ZAP','EBIT','HGER','GDIV','RENW','HAPY','HAPI','OSEA','WINN','MAPP','HDUS','HFGO','QUVU','SMCO','BCDF','NVIR','INFL','MEDX','HSBH','BDVG','OWNS','NDEC','RILA','QIDX','ICAP','SCAP','BALT','QTJL','XBJL','XTJL','KDEC','KNOV','ZDEK','IDEC','ZAUG','NAUG','IAUG','IJUN','IMAY','BSTP','JAJL','SFLR','NJUN','NNOV','IGTR','QTJA','QTOC','QHDG','IMAR','ISEP','QFLR','PSTP','BFEB','BMAR','BMAY','PFEB','PMAY','XUSP','ZALT','XBJA','XTJA','EALT','XDOC','XBOC','XBAP','XTOC','XDSQ','RFLR','INQQ','APOC','PTL','GLRY','RISN','KAUG','HAO','DGL','PMR','IEMD','QQMG','QQJG','RSPE','GBLD','EFAA','KLMT','KLMN','QOWZ','QQQM','IBBQ','QQQS','QQQJ','SOXQ','PBEE','QQA','IVRA','RSPA','QVML','QVMM','QVMS','PSCM','DIVG','QBIG','KJUN','ZNOV','CROP','IRET','CORO','IVVM','XJH','BRLN','BALI','ETEC','USCL','ICOP','IBAT','CSHP','ERET','EMXF','DMXF','USXF','EVUS','EAOK','EAOM','EAOR','EAOA','EGUS','LDEM','ESMV','XVV','IAUM','BMED','IBTM','AGRH','ISVL','BIDD','IGRO','BEMB','SMAX','IVVB','ITDD','DMAX','MAXJ','IRTR','ITDJ','ITDB','ITDC','ITDI','ITDE','ITDF','ITDG','ILIT','BGRO','BLCR','BLCV','IYLD','REM','ECH','TCHI','AQLT','KWT','MTUM','QNXT','QTOP','IWMW','IVVW','NEAR','ITDH','TMET','TEK','TOPT','BRTR','BELT','INRO','MADE','TECB','BPAY','XJR','EVLU','PABU','BAI','JRE','JGRW','JDVI','JSI','JIII','JHMB','JHMU','JHPI','JHDV','BBIB','BBMC','JIVE','JEPQ','JMEE','JMSI','JCHI','JGRO','JAVA','JPSV','BBEM','BBUS','BBSC','JCTR','JDIV','JPEF','JEPI','LCDS','JGLO','HELO','JPIE','JIRE','JIG','JPLD','JPRE','CIRC','JDIV','JTEK','KHPI','DVDN','EQTY','KLIP','AGIX','KCCA','KEUA','KLXY','KSPY','KMLM','KSTR','KVLE','KRBN','KQQQ','AAPY','TGLR','LGHT','SQEW','LCR','LGRO','LQAI','RMIF','LIFT','LCLG','LSVD','MSMR','EMEQ','LRGG','BILD','CVRD','DIVL','BUYW','FDIV','ADVE','MINV','MCH','MCHS','MEMX','MEM','EMSF','JPAN','MKOR','ASIA','MFSG','MFSI','MFSV','MVPA','MVPL','MFUL','CNAV','MAMB','MBCC','MDPL','MPRO','MSSS','MVFG','MVFD','MDLV','TMFE','TMFG','TMFM','TMFX','MSLC','MSSM','MUSQ','NSI','GQI','LSGR','NBCM','NBDS','CSHI','QQQI','IWMI','SPYI','EGGY','NBCR','NBGX','NBOS','NBSM','NBTR','GIAX','KOOL','QTPI','NCLO','NDVG','NUDV','NUGO','NSCR','IQSM','ROOF','IQRA','MMCA','SECR','IWLG','OAKM','OGSP','DUKZ','DUKQ','DUKX','FFND','OAEM','OAIM','OALC','OASC','DIVZ','ESGL','CRIT','OPTZ','OCFS','ODDS','BULD','TRFK','EAFG','SHPP','ALTL','PALC','PAMC','QDPL','QQQG','FOWF','PSCQ','PSCW','PSCX','PSCJ','PSFM','PSFD','PSFJ','PSFO','PSFF','PSMO','PSMR','PSMD','PSMJ','COWG','QSIX','PAPI','PHEQ','PRCS','PRVS','PSTR','PAAA','PFRL','PJBF','PJFG','PJFV','PBFR','PMIO','PQJA','PBDE','MAYP','JUNP','PBSE','PBOC','PBNV','PMJA','PBJA','PBJN','FCUS','PRAE','PSQO','ARP','PCEM','PCCE','PCGG','PCIG','PPEM','BCHP','PIEQ','BYRE','PQDI','PY','DAT','VERS','ANEW','TINY','IQQQ','QQQA','ITWO','RB','ION','CTEX','ZBIO','PBDC','PEMX','PGRO','PPIE','PFUT','PLDR','QVOY','NVQ','HDIV','NIXT','COAL','NUKZ','RSEE','RTAI','RAYC','RAYJ','RPHS','MOOD','HAUS','RSBA','RSSY','RSBY','AIPI','FEPI','RHRX','FLDZ','THNQ','RMCA','RGEF','RMOP','RSMC','XPAY','XDTE','CHAT','OZEM','QDTE','RDTE','LUXX','BETZ','RUNN','SAEF','SCHY','SMBS','SELV','SEIM','SEIQ','SEIV','SEEM','SEIE','SEIS','EUAD','DYTA','GINX','USDX','SGLC','SHOC','HARD','QQC','GAEM','YGLD','PINK','HEQT','QIS','NMB','SURI','IOPP','SPYC','SPD','SPUC','SPQ','SVOL','ADIV','DIVS','FCTE','DIVY','SHDG','SOVF','SPTE','SPWO','SPRE','SPCX','ITAN','DTAN','CERY','DECO','HECO','TEKX','LQIG','SPDG','EFIV','XCNY','ESIX','ZJPN','SPRX','COPP','SETM','COPJ','URNJ','LITP','NIKL','SRHR','NEWZ','LCG','TUG','TUGN','SAGP','SAMM','SAMT','HECO','STXD','STXG','STXV','STRV','STXE','STXI','STXM','FTWO','STXK','DRLL','HEGD','SHUS','TCAF','TOUS','TCHP','TDVG','TEQI','TFLR','TGRW','TAXE','TTEQ','TOTR','TSPA','TGRT','TVAL','TMSL','FDAT','TSPY','ACLO','GRW','FLXR','SLNZ','VOTE','SUPP','RSHO','VOLT','HRTS','TILL','TXS','BEEX','NITE','IDVZ','SHEH','AIRL','CLOD','COPA','SPAM','FINE','WISE','GSIB','AUMI','CZAR','NATO','HWAY','USRD','SMCF','THLV','THIR','TSME','TPHE','TPLE','THY','MRSK','HEAT','DVND','TSEC','SIO','LCF','TOGA','RVER','RNWZ','QBER','AUGZ','DECZ','SEPZ','FEBZ','JUNZ','LRNZ','COPY','HFND','NANC','WAR','UDI','GLDX','UMI','ZSB','CLOB','PIT','SMHX','GMET','MIG','SMOT','MGRO','DESK','IBOT','VGSR','CIZ','CEY','VFLO','SHLD','MODL','VEMY','ASMF','KMID','PCLO','JOET','AIS','GFLW','SFLO','UMMA','WLTG','DVQQ','DVSP','WINC','MDST','LBO','WDNA','WCBR','GDMN','GDE','DXGE','INDH','DHDG','JAMF','GTR','XC','QSML','QGRW','WGLD','WEEI','USIN','USCA','XAIX','PSWD','KOKU','NRES','SNPG','SMLE','CHPS','CRTC','SMCY','ZECP','GROZ','SMIZ','ZHDG'
]
self.algo = algo
def select(self, algo: QCAlgorithm, fundamental: List[Fundamental]) -> List[Symbol]:
if self.algo.time.year not in self.algo.picked:
# Filter out constituents without weight and sort by weight (optional step for cleanliness)
selected = [x for x in fundamental if not x.has_fundamental_data and x.price > 10 and x.dollar_volume > 500_000 and x.market_cap == 0 and x.symbol.value in self.whitelist_etfs]
# Request daily historical data for the entire previous year for all ~500 SPY constituents
history = self.algo.history([s.symbol for s in selected],
datetime(self.algo.time.year-2, 12, 1),
datetime(self.algo.time.year-1, 12, 31),
Resolution.DAILY)
# Reset index to make 'symbol' and 'time' accessible columns in the DataFrame
df = history.reset_index()
# Extract the year from the timestamp
df['year'] = df['time'].dt.year
# 1. Group by symbol and year to find ONLY the 'last' close price of each year
# We don't need the open price anymore for this specific calculation
yearly_df = df.groupby(['symbol', 'year'])['close'].last().reset_index()
# 2. Sort by symbol and year to ensure 2018 comes before 2019
yearly_df = yearly_df.sort_values(['symbol', 'year'])
# 3. Calculate the percentage price change: (Current Year Close - Previous Year Close) / Previous Year Close
# We group by symbol again so we don't calculate change across different stocks
yearly_df['change'] = yearly_df.groupby('symbol')['close'].pct_change()
yearly_df = yearly_df[yearly_df['year'] == (self.algo.time.year-1)]
# Select the ticker with the highest percentage change and store it in the 'picked' cache
# Returns a list containing the Symbol of the #1 top performer
self.algo.picked[self.algo.time.year] = [c for c in yearly_df.sort_values('change', ascending=False).head(1)['symbol'].values]
self.algo.log.debug(f"{self.algo.time.year}: {yearly_df.sort_values('change', ascending=False).head(20)['symbol']}")
# Return the cached list of symbols for the current year to the universe selection model
return self.algo.picked[self.algo.time.year]
class NonBenchmarkConstantAlphaModel(AlphaModel):
''' Provides an implementation of IAlphaModel that always returns the same insight for each security'''
def __init__(self, algo, type, direction, period, magnitude = None, confidence = None, weight = None):
'''Initializes a new instance of the ConstantAlphaModel class
Args:
type: The type of insight
direction: The direction of the insight
period: The period over which the insight with come to fruition
magnitude: The predicted change in magnitude as a +- percentage
confidence: The confidence in the insight
weight: The portfolio weight of the insights'''
self.algo = algo
self.type = type
self.direction = direction
self.period = period
self.magnitude = magnitude
self.confidence = confidence
self.weight = weight
self.securities = []
self.insights_time_by_symbol = {}
self.Name = '{}({},{},{}'.format(self.__class__.__name__, type, direction, strfdelta(period))
if magnitude is not None:
self.Name += ',{}'.format(magnitude)
if confidence is not None:
self.Name += ',{}'.format(confidence)
self.Name += ')'
def update(self, algorithm, data):
''' Creates a constant insight for each security as specified via the constructor
Args:
algorithm: The algorithm instance
data: The new data available
Returns:
The new insights generated'''
insights = []
for security in self.securities:
# security price could be zero until we get the first data point. e.g. this could happen
# when adding both forex and equities, we will first get a forex data point
if security.price != 0 and self.should_emit_insight(algorithm.utc_time, security.symbol) and security.symbol != self.algo._symbol:
insights.append(Insight(security.symbol, self.period, self.type, self.direction, self.magnitude, self.confidence, weight = self.weight))
return insights
def on_securities_changed(self, algorithm, changes):
''' Event fired each time the we add/remove securities from the data feed
Args:
algorithm: The algorithm instance that experienced the change in securities
changes: The security additions and removals from the algorithm'''
for added in changes.added_securities:
self.securities.append(added)
# this will allow the insight to be re-sent when the security re-joins the universe
for removed in changes.removed_securities:
if removed in self.securities:
self.securities.remove(removed)
if removed.symbol in self.insights_time_by_symbol:
self.insights_time_by_symbol.pop(removed.symbol)
def should_emit_insight(self, utc_time, symbol):
if symbol.is_canonical():
# canonical futures & options are none tradable
return False
generated_time_utc = self.insights_time_by_symbol.get(symbol)
if generated_time_utc is not None:
# we previously emitted a insight for this symbol, check it's period to see
# if we should emit another insight
if utc_time - generated_time_utc < self.period:
return False
# we either haven't emitted a insight for this symbol or the previous
# insight's period has expired, so emit a new insight now for this symbol
self.insights_time_by_symbol[symbol] = utc_time
return True
def strfdelta(tdelta):
d = tdelta.days
h, rem = divmod(tdelta.seconds, 3600)
m, s = divmod(rem, 60)
return "{}.{:02d}:{:02d}:{:02d}".format(d,h,m,s)# region imports
from AlgorithmImports import *
# endregion
class CustomSecurityInitializer(BrokerageModelSecurityInitializer):
def __init__(self, brokerage_model: IBrokerageModel, security_seeder: ISecuritySeeder) -> None:
super().__init__(brokerage_model, security_seeder)
def initialize(self, security: Security) -> None:
super().initialize(security)
security.set_slippage_model(VolumeShareSlippageModel())
security.set_settlement_model(ImmediateSettlementModel())
security.set_leverage(1.0)
security.set_buying_power_model(CashBuyingPowerModel())
security.set_fee_model(InteractiveBrokersFeeModel())
security.set_margin_model(SecurityMarginModel.NULL)
from AlgorithmImports import *
from Newtonsoft.Json import JsonConvert
import System
import psutil
class Utils():
def __init__(self, algo, ticker):
self.algo = algo
self.ticker = ticker
self.mkt = []
self.insights_key = f"{self.algo.project_id}/Live_{self.algo.live_mode}_insights"
self.algo.set_benchmark(ticker)
self._initial_portfolio_value = self.algo.init_cash
self._initial_benchmark_price = 0
self._portfolio_high_watermark = 0
self.init_chart()
def init_chart(self):
chart_name = "Strategy Performance"
chart = Chart(chart_name)
strategy_series = Series("Strategy", SeriesType.LINE, 0, "$")
strategy_series.color = Color.ORANGE
chart.add_series(strategy_series)
benchmark_series = Series("Benchmark", SeriesType.LINE, 0, "$")
benchmark_series.color = Color.LIGHT_GRAY
chart.add_series(benchmark_series)
drawdown_series = Series("Drawdown", SeriesType.LINE, 1, "%")
drawdown_series.color = Color.INDIAN_RED
chart.add_series(drawdown_series)
allocation_series = Series("Allocation", SeriesType.LINE, 2, "%")
allocation_series.color = Color.CORNFLOWER_BLUE
chart.add_series(allocation_series)
holding_series = Series("Holdings", SeriesType.LINE, 3, "")
holding_series.color = Color.YELLOW_GREEN
chart.add_series(holding_series)
self.algo.add_chart(chart)
def plot(self):
if self.algo.live_mode or self.algo.is_warming_up:
return
# Capture initial reference values
if self._initial_portfolio_value == 0.0:
self._initial_portfolio_value = float(self.algo.portfolio.total_portfolio_value)
benchmark_price = float(self.algo.securities[self.algo._symbol].price)
if self._initial_benchmark_price == 0.0 and benchmark_price > 0.0:
self._initial_benchmark_price = benchmark_price
# Ensure both initial values are set
if self._initial_portfolio_value == 0.0 or self._initial_benchmark_price == 0.0:
return
# Current values
current_portfolio_value = float(self.algo.portfolio.total_portfolio_value)
# Defensive check (avoid division by zero)
if self._initial_portfolio_value == 0.0 or self._initial_benchmark_price == 0.0:
return
# Normalize (start at 1.0)
normalized_portfolio = current_portfolio_value / self._initial_portfolio_value
normalized_benchmark = benchmark_price / self._initial_benchmark_price
current_value = self.algo.portfolio.total_portfolio_value
if current_value > self._portfolio_high_watermark:
self._portfolio_high_watermark = current_value
drawdown = 0.0
if self._portfolio_high_watermark != 0.0:
drawdown = (current_value - self._portfolio_high_watermark) / self._portfolio_high_watermark * 100.0
holding_count = 0
for symbol in list(self.algo.securities.keys()):
if symbol is None:
continue
holding = self.algo.portfolio[symbol]
if holding is None or not holding.invested:
continue
holding_count += 1
chart_name = "Strategy Performance"
self.algo.plot(chart_name, "Drawdown", drawdown)
self.algo.plot(chart_name, "Strategy", normalized_portfolio*self.algo.init_cash)
self.algo.plot(chart_name, "Benchmark", normalized_benchmark*self.algo.init_cash)
self.algo.plot(chart_name, "Allocation", round(self.algo.portfolio.total_holdings_value / self.algo.portfolio.total_portfolio_value,2)*100)
self.algo.plot(chart_name, "Holdings", holding_count)
self.algo.plot('Strategy Equity', self.ticker, normalized_benchmark*self.algo.init_cash)
def pctc(no1, no2):
return((float(str(no2))-float(str(no1)))/float(str(no1)))
def stats(self):
df = None
trades = self.algo.trade_builder.closed_trades
for trade in trades:
data = {
'symbol': trade.symbol,
'time': trade.entry_time,
'entry_price': trade.entry_price,
'exit_price': trade.exit_price,
'pnl': trade.profit_loss,
'pnl_pct': (trade.exit_price - trade.entry_price)/trade.entry_price,
}
df = pd.concat([pd.DataFrame(data=data, index=[0]), df])
if df is not None:
profit = df.query('pnl >= 0')['pnl'].sum()
loss = df.query('pnl < 0')['pnl'].sum()
avgWinPercentPerWin = "{0:.2%}".format(df.query('pnl >= 0')['pnl_pct'].mean())
avgLostPercentPerLost = "{0:.2%}".format(df.query('pnl < 0')['pnl_pct'].mean())
maxLost = "{0:.2%}".format(df.query('pnl < 0')['pnl_pct'].min())
maxWin = "{0:.2%}".format(df.query('pnl > 0')['pnl_pct'].max())
self.algo.set_summary_statistic("*Profit Ratio", round(profit / abs(loss),2))
self.algo.set_summary_statistic("Avg. Win% Per Winner", avgWinPercentPerWin)
self.algo.set_summary_statistic("Avg. Lost% Per Losser", avgLostPercentPerLost)
self.algo.set_summary_statistic("Max Loss%", maxLost)
self.algo.set_summary_statistic("Max Win%", maxWin)
def read_insight(self):
if self.algo.object_store.contains_key(self.insights_key) and self.algo.live_mode:
insights = self.algo.object_store.read_json[System.Collections.Generic.List[Insight]](self.insights_key)
self.algo.log.debug(f"Read {len(insights)} insight(s) from the Object Store")
self.algo.insights.add_range(insights)
#self.algo.object_store.delete(self.insights_key)
def store_insight(self):
if self.algo.live_mode:
insights = self.algo.insights.get_insights(lambda x: x.is_active(self.algo.utc_time))
# If we want to save all insights (expired and active), we can use
# insights = self.insights.get_insights(lambda x: True)
self.algo.log.debug(f"Save {len(insights)} insight(s) to the Object Store.")
content = ','.join([JsonConvert.SerializeObject(x) for x in insights])
self.algo.object_store.save(self.insights_key, f'[{content}]')
def trace_memory(self, name):
self.algo.log.debug(f"[{name}] RAM memory % used: {psutil.virtual_memory()[2]} / RAM Used (GB): {round(psutil.virtual_memory()[3]/1000000000,2)}")