Overall Statistics |
Total Trades 67 Average Win 11.87% Average Loss -1.51% Compounding Annual Return 26.052% Drawdown 20.400% Expectancy 6.265 Net Profit 1943.236% Sharpe Ratio 1.662 Probabilistic Sharpe Ratio 95.317% Loss Rate 18% Win Rate 82% Profit-Loss Ratio 7.88 Alpha 0.259 Beta 0.106 Annual Standard Deviation 0.168 Annual Variance 0.028 Information Ratio 0.34 Tracking Error 0.26 Treynor Ratio 2.631 Total Fees $97.75 |
""" Based on 'In & Out' strategy by Peter Guenther 4 Oct 2020 expanded/inspired by Tentor Testivis, Dan Whitnable (Quantopian), Vladimir, and Thomas Chang. https://www.quantopian.com/posts/new-strategy-in-and-out https://www.quantconnect.com/forum/discussion/9597/the-in-amp-out-strategy-continued-from-quantopian/p1 """ # Import packages import numpy as np import pandas as pd import scipy as sc class InOut(QCAlgorithm): def Initialize(self): self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage) self.SetStartDate(2008, 1, 1) # Set Start Date self.SetCash(5000) # Set Strategy Cash self.UniverseSettings.Resolution = Resolution.Daily res = Resolution.Hour self.SetBenchmark("QQQ") # Feed-in constants self.INI_WAIT_DAYS = 15 # out for 3 trading weeks self.be_in = True self.last = True # Holdings ### 'Out' holdings and weights self.BND1 = self.AddEquity('TLT', res).Symbol #TLT; TMF for 3xlev self.BND2 = self.AddEquity('TMF', res).Symbol #IEF; TYD for 3xlev self.HLD_OUT = {self.BND1: 1} ### 'In' holdings and weights (static stock selection strategy) self.STKS = self.AddEquity('QQQ', res).Symbol #SPY or QQQ; TQQQ for 3xlev self.STKS2 = self.AddEquity('TQQQ', res).Symbol #SPY or QQQ; TQQQ for 3xlev self.HLD_IN = {self.STKS: 1} ### combined holdings dictionary self.wt = {**self.HLD_IN, **self.HLD_OUT} # Market and list of signals based on ETFs self.MRKT = self.AddEquity('SPY', res).Symbol # market false = False true = True self.sig = [{"date": "2008-01-02", "be_in": false}, {"date": "2008-02-19", "be_in": true}, {"date": "2008-06-02", "be_in": false}, {"date": "2008-10-13", "be_in": true}, {"date": "2008-10-16", "be_in": false}, {"date": "2009-03-23", "be_in": true}, {"date": "2010-06-09", "be_in": false}, {"date": "2010-08-31", "be_in": true}, {"date": "2011-06-14", "be_in": false}, {"date": "2012-01-04", "be_in": true}, {"date": "2012-05-04", "be_in": false}, {"date": "2012-09-11", "be_in": true}, {"date": "2013-04-03", "be_in": false}, {"date": "2013-06-05", "be_in": true}, {"date": "2014-03-17", "be_in": false}, {"date": "2014-05-30", "be_in": true}, {"date": "2014-06-06", "be_in": false}, {"date": "2014-06-30", "be_in": true}, {"date": "2014-09-23", "be_in": false}, {"date": "2015-02-27", "be_in": true}, {"date": "2015-06-18", "be_in": false}, {"date": "2015-07-01", "be_in": true}, {"date": "2015-07-08", "be_in": false}, {"date": "2015-10-23", "be_in": true}, {"date": "2015-12-21", "be_in": false}, {"date": "2016-03-09", "be_in": true}, {"date": "2017-05-10", "be_in": false}, {"date": "2017-05-26", "be_in": true}, {"date": "2017-06-05", "be_in": false}, {"date": "2017-07-03", "be_in": true}, {"date": "2018-04-10", "be_in": false}, {"date": "2018-06-05", "be_in": true}, {"date": "2018-08-01", "be_in": false}, {"date": "2019-02-19", "be_in": true}, {"date": "2019-05-09", "be_in": false}, {"date": "2019-09-11", "be_in": true}, {"date": "2020-02-03", "be_in": false}, {"date": "2020-07-09", "be_in": true}] self.Schedule.On( self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen('SPY', 1), self.generate_signal ) self.Schedule.On( self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen('SPY', 120), self.rebalance_when_out_of_the_market ) self.Schedule.On( self.DateRules.WeekEnd(), self.TimeRules.AfterMarketOpen('SPY', 121), self.rebalance_when_in_the_market ) def generate_signal(self): if(len(self.sig) > 0): currentDate = self.UtcTime.date() signalDate = datetime.strptime(self.sig[0]["date"], '%Y-%m-%d').date() if currentDate == signalDate: self.be_in = self.sig[0]["be_in"] self.sig = self.sig[1:] def rebalance_when_out_of_the_market(self): # Swap to 'out' assets if applicable if not self.be_in: self.wt = {**dict.fromkeys(self.HLD_IN, 0), **self.HLD_OUT} # Only trade when changing from in to out for sec, weight in self.wt.items(): cond1 = (self.Portfolio[sec].Quantity > 0) and (weight == 0) cond2 = (self.Portfolio[sec].Quantity == 0) and (weight > 0) if cond1 or cond2: self.SetHoldings(sec, weight) def rebalance_when_in_the_market(self): # Swap to 'in' assets if applicable if self.be_in: self.wt = {**self.HLD_IN, **dict.fromkeys(self.HLD_OUT, 0)} # Only trade when changing from out to in for sec, weight in self.wt.items(): cond1 = (self.Portfolio[sec].Quantity > 0) and (weight == 0) cond2 = (self.Portfolio[sec].Quantity == 0) and (weight > 0) if cond1 or cond2: self.SetHoldings(sec, weight)