| Overall Statistics |
|
Total Trades 1 Average Win 0% Average Loss 0% Compounding Annual Return -95.059% Drawdown 3.600% Expectancy 0 Net Profit -1.904% Sharpe Ratio -6.603 Probabilistic Sharpe Ratio 0% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0.076 Beta 2.613 Annual Standard Deviation 0.121 Annual Variance 0.015 Information Ratio -5.853 Tracking Error 0.079 Treynor Ratio -0.306 Total Fees $3.66 Estimated Strategy Capacity $5000000.00 Lowest Capacity Asset TQQQ UK280CGTCB51 |
"""
SEL(stock selection part)
Based on the 'Momentum Strategy with Market Cap and EV/EBITDA' strategy introduced by Jing Wu, 6 Feb 2018
adapted and recoded by Jack Simonson, Goldie Yalamanchi, Vladimir, Peter Guenther, Leandro Maia and Simone Pantaleoni
https://www.quantconnect.com/forum/discussion/3377/momentum-strategy-with-market-cap-and-ev-ebitda/p1
https://www.quantconnect.com/forum/discussion/9678/quality-companies-in-an-uptrend/p1
https://www.quantconnect.com/forum/discussion/9632/amazing-returns-superior-stock-selection-strategy-superior-in-amp-out-strategy/p1
https://www.quantconnect.com/terminal/#live-view-tab
I/O(in & out part)
The Distilled Bear in & out algo
Dan's original notes:
"This is based on Peter Guenther great “In & Out” algo.
Included Tentor Testivis recommendation to use volatility adaptive calculation of WAIT_DAYS and RET.
Included Vladimir's ideas to eliminate fixed constants
Help from 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/
"""
from QuantConnect.Data.UniverseSelection import *
import math
import numpy as np
import pandas as pdv
import scipy as sp
class EarningsFactorWithMomentum_InOut(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2021, 7, 29)
self.SetEndDate(2021, 7, 30) #Set End Date
self.cap = 100000
self.SetCash(self.cap)
self.DISTILLED_BEAR_Init = 0
res = Resolution.Minute
# Holdings
### 'Out' holdings and weights
self.BND1 = self.AddEquity('TMF', res).Symbol #TLT; TMF for 3xlev
self.TMF = self.AddEquity('TMF', res).Symbol
self.BOND = self.AddEquity('TLT', res).Symbol
self.quantity = {self.BND1: 0, self.BOND:0}
self.TQQQ = self.AddEquity('TQQQ', res).Symbol
##### In & Out parameters #####
# Feed-in constants
self.INI_WAIT_DAYS = 15 # out for 3 trading weeks
self.wait_days = self.INI_WAIT_DAYS
# Market and list of signals based on ETFs
self.MRKT = self.AddEquity('SPY', res).Symbol # market
self.QQQ = self.AddEquity('QQQ', res).Symbol # tech market
self.TQQQ = self.AddEquity('TQQQ', res).Symbol
self.GOLD = self.AddEquity('GLD', res).Symbol # gold
self.SLVA = self.AddEquity('SLV', res).Symbol # vs silver
self.UTIL = self.AddEquity('XLU', res).Symbol # utilities
self.INDU = self.AddEquity('XLI', res).Symbol # vs industrials
self.METL = self.AddEquity('DBB', res).Symbol # input prices (metals)
self.USDX = self.AddEquity('UUP', res).Symbol # safe haven (USD)
self.FORPAIRS = [self.GOLD, self.SLVA, self.UTIL, self.INDU, self.METL, self.USDX]
# Specific variables
self.DISTILLED_BEAR = 1#999
self.BE_IN = 1#999
self.BE_IN_PRIOR = 0
self.VOLA_LOOKBACK = 126
self.WAITD_CONSTANT = 85
self.DCOUNT = 0 # count of total days since start
self.OUTDAY = (-self.INI_WAIT_DAYS+1) # dcount when self.be_in=0, initial setting ensures trading right away
self.fill_act = 0
# Variables for charts
self.spy = []
self.qqq = []
self.benchmark = []
self.portfolio_value = [self.cap] * 60
self.year = self.Time.year
self.saw_qqq_base = []
self.saw_portfolio_base = []
# set a warm-up period to initialize the indicator
self.SetWarmUp(timedelta(350))
##### Momentum & fundamentals strategy parameters #####
self.UniverseSettings.Resolution = res
self.AddUniverse(self.UniverseCoarseFilter, self.UniverseFundamentalsFilter)
self.num_coarse = 100 #200
self.num_screener = 30 #100 # changed from 15
self.num_stocks = 4 # lowered from 10
self.formation_days = 126 #126 was original value
self.lowmom = False
self.data = {}
self.setrebalancefreq = 60 # X days, update universe and momentum calculation
self.updatefinefilter = 0
self.symbols = None
self.reb_count = 0
self.fill_act_symbol = None
self.Schedule.On(
self.DateRules.EveryDay(),
self.TimeRules.AfterMarketOpen('SPY', 30), # reduced time
self.rebalance_when_out_of_the_market)
self.Schedule.On(
self.DateRules.EveryDay(),
self.TimeRules.BeforeMarketClose('SPY', 0),
self.record_vars)
self.Schedule.On(
self.DateRules.WeekEnd(),
self.TimeRules.AfterMarketOpen('SPY', 29),
self.Weeklydeposit)
self.Schedule.On(
self.DateRules.EveryDay(),
self.TimeRules.AfterMarketOpen('SPY', 25), # reduced time
self.first_day_liquidation)
#Calculate the share per min based on the trading volume per min
fill_time = []
p = 1 #interval(minutues)
y = int((390-30)/p) # total market open hour (minutes) (390(Total minute)-Dealying 30mins after open )
for i in range(1, y):
fill_time.append(i*p + 30)
for x in fill_time:
self.Schedule.On(
self.DateRules.EveryDay(),
self.TimeRules.AfterMarketOpen('SPY', x), # reduced time
self.fill)
self.vollookback = 30 #volume calculation lookback (15mins)
self.volcalsymbols = [self.BND1] + [self.TQQQ]
# Setup daily consolidation
symbols = [self.MRKT] + [self.QQQ] + self.FORPAIRS
for symbol in symbols:
self.consolidator = TradeBarConsolidator(timedelta(days=1))
self.consolidator.DataConsolidated += self.consolidation_handler
self.SubscriptionManager.AddConsolidator(symbol, self.consolidator)
# Warm up history
self.lookback = 126
self.history = self.History(symbols, self.lookback, Resolution.Daily)
#calculate close price
if self.history.empty or 'close' not in self.history.columns:
return
self.history = self.history['close'].unstack(level=0).dropna()
#EMA data
self.averages = {}
self.buy = 3
def UniverseCoarseFilter(self, coarse):
if not (((self.DCOUNT-self.reb_count)==self.setrebalancefreq) or (self.DCOUNT == self.OUTDAY + self.wait_days - 1)):
self.updatefinefilter = 0
return Universe.Unchanged
self.updatefinefilter = 1
# drop stocks which have no fundamental data or have too low prices
selected = [x for x in coarse if (x.HasFundamentalData) and (float(x.Price) > 5)]
# rank the stocks by dollar volume
filtered = sorted(selected, key=lambda x: x.DollarVolume, reverse=True)
return [x.Symbol for x in filtered[:self.num_coarse]]
def UniverseFundamentalsFilter(self, fundamental):
if self.updatefinefilter == 0:
return Universe.Unchanged
filtered_fundamental = [x for x in fundamental if (x.ValuationRatios.EVToEBITDA > 0)
and x.OperationRatios.OperationRevenueGrowth3MonthAvg.Value > 0
and (x.EarningReports.BasicAverageShares.ThreeMonths > 0)
and float(x.EarningReports.BasicAverageShares.ThreeMonths) * x.Price > 6e9
and x.SecurityReference.IsPrimaryShare
and x.SecurityReference.SecurityType == "ST00000001"
and x.SecurityReference.IsDepositaryReceipt == 0
and x.CompanyReference.IsLimitedPartnership == 0]
top = sorted(filtered_fundamental, key = lambda x: x.ValuationRatios.EVToEBITDA, reverse=True)[:self.num_screener]
self.symbols = [x.Symbol for x in top]
self.updatefinefilter = 0
self.reb_count = self.DCOUNT
# fine_series = pd.Series(self.symbols)
# self.Log('EV/EBITDA Rank: ' + str(fine_series))
# self.Notify.Sms("+18605138853", 'EV/EBITDA Rank: ' + str(fine_series))
return self.symbols
def OnSecuritiesChanged(self, changes):
#for security in changes.AddedSecurities:
# if security.Symbol not in self.data:
# self.data[security.Symbol] = SymbolData(security.Symbol, self.formation_days, self)
addedSymbols = []
for security in changes.AddedSecurities:
addedSymbols.append(security.Symbol)
if security.Symbol not in self.data:
self.data[security.Symbol] = SymbolData(security.Symbol, self.formation_days, self)
if len(addedSymbols) > 0:
history = self.History(addedSymbols, 1 + self.formation_days, Resolution.Daily).loc[addedSymbols]
for symbol in addedSymbols:
try:
self.data[symbol].Warmup(history.loc[symbol])
except:
self.Debug("not added in Universe :" + str(symbol))
continue
def consolidation_handler(self, sender, consolidated):
self.history.loc[consolidated.EndTime, consolidated.Symbol] = consolidated.Close
self.history = self.history.iloc[-self.lookback:]
def derive_vola_waitdays(self):
volatility = 0.6 * np.log1p(self.history[[self.MRKT]].pct_change()).std() * np.sqrt(252)
wait_days = int(volatility * self.WAITD_CONSTANT)
returns_lookback = int((1.0 - volatility) * self.WAITD_CONSTANT)
return wait_days, returns_lookback
def first_day_liquidation(self):
if self.DCOUNT == 0:
self.Liquidate()
self.Log("first day liquidation")
def rebalance_when_out_of_the_market(self):
if self.Portfolio.TotalPortfolioValue > 10000000:
if self.fill_act == 0:
del self.quantity[self.BND1]
self.BND1 = self.BOND
self.quantity = {self.BND1 : 0}
self.volcalsymbols = [self.BND1] + [self.TQQQ]
elif self.Portfolio.TotalPortfolioValue <= 10000000 :
if self.fill_act == 0:
del self.quantity[self.BND1]
self.BND1 = self.TMF
self.quantity = {self.BND1 : 0}
self.volcalsymbols = [self.BND1] + [self.TQQQ]
self.vol_history = self.History(self.volcalsymbols, 150, Resolution.Minute)
self.volumes = self.vol_history['volume'].unstack(level = 0).dropna()
self.TQQQsharepertick = self.volumes[self.TQQQ].rolling(window=self.vollookback).mean().iloc[-1]*0.3
self.BND1sharepertick = self.volumes[self.BND1].rolling(window=self.vollookback).mean().iloc[-1]
self.TQQQsharepertick = math.floor(self.TQQQsharepertick)
self.BND1sharepertick = math.floor(self.BND1sharepertick)
self.wait_days, returns_lookback = self.derive_vola_waitdays()
## Check for Bears
returns = self.history.pct_change(returns_lookback).iloc[-1]
silver_returns = returns[self.SLVA]
gold_returns = returns[self.GOLD]
industrials_returns = returns[self.INDU]
utilities_returns = returns[self.UTIL]
metals_returns = returns[self.METL]
dollar_returns = returns[self.USDX]
self.DISTILLED_BEAR = (((gold_returns > silver_returns) and
(utilities_returns > industrials_returns)) and
(metals_returns < dollar_returns)
)
if self.DISTILLED_BEAR_Init == 0:
if self.DISTILLED_BEAR:
self.DISTILLED_BEAR_Init = 1
self.Debug("Market Sentiment : Distilled Bear - Initialized")
self.Notify.Sms("+18605138853", "Market Sentiment : Distilled Bear - Initialized")
if self.DISTILLED_BEAR:
SPY_hist = self.History(self.Symbol("SPY"), 200, Resolution.Daily)
SPY_hist_price = SPY_hist.close
if (SPY_hist_price.rolling(window = 3).mean()[-1] - SPY_hist_price.rolling(window = 30).mean()[-1])/SPY_hist_price.rolling(window = 30).mean()[-1] > 0.025:
self.DISTILLED_BEAR = False
self.BE_IN = True
self.Debug("Market Sentiment : Distilled Bear - In the market")
self.Notify.Sms("+18605138853", "Market Sentiment : Distilled Bear - In the market")
self.DISTILLED_BEAR_Init = 0
# elif SPY_hist_price.rolling(window = 3).mean()[-1] > SPY_hist_price.rolling(window = 9).mean()[-1] > SPY_hist_price.rolling(window = 32).mean()[-1] > SPY_hist_price.rolling(window = 72).mean()[-1] > SPY_hist_price.rolling(window = 180).mean()[-1] :
# self.DISTILLED_BEAR = False
# self.BE_IN = True
# self.Debug("Market Sentiment : Distilled Bear - In the market")
# self.Notify.Sms("+18605138853", "Market Sentiment : Distilled Bear - In the market")
# self.DISTILLED_BEAR_Init = 0
else:
self.Debug("Market Sentiment : Distilled Bear - Out of the market")
self.Notify.Sms("+18605138853", "Market Sentiment : Distilled Bear - Out of the market")
elif self.DISTILLED_BEAR == False and self.BE_IN == True:
self.Debug("Market Sentiment : Bull")
self.Notify.Sms("+18605138853", "Market Sentiment : Bull")
elif self.DISTILLED_BEAR == False and self.BE_IN == False:
self.Debug("Market Sentiment : Distilled Bear - Wait for the confirmation (15 trading days)")
self.Notify.Sms("+18605138853", "Market Sentiment : Wait for the confirmation (15 trading days)")
# Determine whether 'in' or 'out' of the market
if self.DISTILLED_BEAR:
self.BE_IN = False
self.OUTDAY = self.DCOUNT
# if self.quantity[self.BND1] == 0:
for symbol in self.quantity.copy().keys():
if symbol == self.BND1: continue
elif self.quantity[symbol] <= self.TQQQsharepertick:
self.Order(symbol, - self.quantity[symbol])
self.Debug([str(self.Time), str(symbol), str(-self.quantity[symbol])])
self.quantity[symbol] = 0
del self.quantity[symbol]
self.fill_act = 0
elif self.quantity[symbol] > self.TQQQsharepertick:
if self.fill_act == 3:
self.Order(symbol, - self.TQQQsharepertick)
self.Debug([str(self.Time), str(symbol), str(-self.TQQQsharepertick)])
self.quantity[symbol] -= self.TQQQsharepertick
self.fill_act = 2
self.fill_act_symbol = symbol
quantity = self.Portfolio.TotalPortfolioValue * 0.99 / self.Securities[self.BND1].Close
history_BND1 = self.History(self.BOND, 300, Resolution.Daily)
Open = history_BND1.open.unstack(0)[self.BOND][-1]
Close = history_BND1.close.unstack(0)[self.BOND][-1]
Stable_PriceAction = Open < Close * 1.02
hlc3 = (history_BND1.close.unstack(0)[self.BOND][-1] + history_BND1.open.unstack(0)[self.BOND][-1] + history_BND1.high.unstack(0)[self.BOND][-1]) /3
Close_history = history_BND1.close.unstack(0)[self.BOND]
self.averages[self.BND1] = SelectionDataShort(Close_history)
if ((hlc3 > self.averages[self.BND1].MA13.Current.Value and
self.averages[self.BND1].MA5.Current.Value > self.averages[self.BND1].MA13.Current.Value and
Close_history.pct_change(1)[-1] > -0.015) or
Stable_PriceAction):
self.buy = 1
else:
self.buy = 0
if self.buy == 1:
if self.fill_act == 0 and self.quantity[self.BND1] == 0:
if math.floor(quantity) <= self.BND1sharepertick:
self.quantity[self.BND1] = math.floor(quantity)
self.Order(self.BND1, self.quantity[self.BND1])
self.Debug([str(self.Time), str(self.BND1), str(self.quantity[self.BND1])])
self.fill_act = 3
elif math.floor(quantity) > self.BND1sharepertick:
sym = self.BND1
self.Order(sym, self.BND1sharepertick)
self.Debug([str(self.Time), str(sym), self.BND1sharepertick])
self.quantity[sym] = self.BND1sharepertick
self.fill_act = 1
self.fill_act_symbol = self.BND1
elif self.buy == 0:
if self.fill_act == 3 and self.quantity[self.BND1] > 0:
if self.quantity[self.BND1] <= self.BND1sharepertick:
self.Order(self.BND1, - self.quantity[self.BND1])
self.Debug([str(self.Time), str(self.BND1), str(-self.quantity[self.BND1])])
self.quantity[self.BND1] = 0
self.fill_act = 0
elif self.quantity[self.BND1] > self.BND1sharepertick:
self.Order(self.BND1, - self.BND1sharepertick)
self.Debug([str(self.Time), str(self.BND1), str(-self.BND1sharepertick)])
self.quantity[self.BND1] -= self.BND1sharepertick
self.fill_act = 2
self.fill_act_symbol = self.BND1
if (self.DCOUNT >= self.OUTDAY + self.wait_days):
self.BE_IN = True
# Update stock ranking/holdings, when swithing from 'out' to 'in' plus every X days when 'in' (set rebalance frequency)
if (self.BE_IN and not self.BE_IN_PRIOR) or (self.BE_IN and (self.DCOUNT==self.reb_count)):
chosen_df = self.calc_return(self.symbols)
self.rebalance()
self.Debug('Best momentum (EV/EBITDA): ' + str(chosen_df))
self.Notify.Sms("+18605138853", 'Best momentum (EV/EBITDA): ' + str(chosen_df))
self.BE_IN_PRIOR = self.BE_IN
self.DCOUNT += 1
self.Notify.Sms("+18605138853", "Net LIQ :" + str(self.Portfolio.TotalPortfolioValue))
self.create_charts()
def rebalance(self):
# chosen_df = self.calc_return(self.symbols)
# self.Log('Best momentum (EV/EBITDA): ' + str(chosen_df))
# self.Notify.Sms("+18605138853", 'Best momentum (EV/EBITDA): ' + str(chosen_df))
# self.chosen_df = chosen_df.iloc[:self.num_stocks]
# self.Log("Chosen 4 tickers :" + str(self.chosen_df))
if self.quantity[self.BND1] > 0 :
if self.quantity[self.BND1] <= self.BND1sharepertick:
self.Order(self.BND1, - self.quantity[self.BND1])
self.Debug([str(self.Time), str(self.BND1), str(-self.quantity[self.BND1])])
self.quantity[self.BND1] = 0
self.fill_act = 0
elif self.quantity[self.BND1] > self.BND1sharepertick:
if self.fill_act == 3 :
self.Order(self.BND1, - self.BND1sharepertick)
self.Debug([str(self.Time), str(self.BND1), str(-self.BND1sharepertick)])
self.quantity[self.BND1] -= self.BND1sharepertick
self.fill_act = 2
self.fill_act_symbol = self.BND1
# weight = 1 / self.num_stocks
for symbol in self.quantity.copy().keys():
if symbol == self.BND1 and self.quantity[symbol] > 0:
pass
# # elif symbol != self.TQQQ:
# # self.Order(symbol, - self.quantity[symbol])
# # self.Debug([str(self.Time), str(symbol), str(-self.quantity[symbol])])
# # del self.quantity[symbol]
# # self.fill_act = 0
# elif symbol == self.TQQQ:
# # self.Debug("Availible funds :" + str(self.Portfolio.TotalPortfolioValue * 0.99))
# quantity = self.Portfolio.TotalPortfolioValue * 0.1 / self.Securities[symbol].Close
# # self.Log(f'Quantity for {symbol} is {quantity} (line 338)...')
# if self.fill_act == 3:
# if math.floor(quantity) != self.quantity[symbol]:
# if (math.floor(quantity) - self.quantity[symbol]) > 0:
# if (math.floor(quantity) - self.quantity[symbol]) > self.TQQQsharepertick:
# self.Order(symbol, (math.floor(quantity) - self.quantity[symbol]))
# self.Debug([str(self.Time), str(symbol), (math.floor(quantity) - self.quantity[symbol])])
# self.quantity[symbol] += (math.floor(quantity) - self.quantity[symbol])
# self.fill_act = 1
# self.fill_act_symbol = self.TQQQ
# elif (math.floor(quantity) - self.quantity[symbol]) <= self.TQQQsharepertick:
# self.Order(symbol, math.floor(quantity) - self.quantity[symbol])
# self.Debug([str(self.Time), str(symbol), str(math.floor(quantity) -self.quantity[symbol])])
# self.quantity[symbol] += (math.floor(quantity) - self.quantity[symbol])
# self.fill_act = 3
if self.TQQQ not in self.quantity.keys():
quantity = self.Portfolio.TotalPortfolioValue * 0.99 / self.Securities[self.TQQQ].Close
if self.fill_act == 0 :
if math.floor(quantity) <= self.TQQQsharepertick:
self.quantity[self.TQQQ] = math.floor(quantity)
self.Order(self.TQQQ, self.quantity[self.TQQQ])
self.Debug([str(self.Time), str(self.TQQQ), str(self.quantity[self.TQQQ])])
self.fill_act = 3
elif math.floor(quantity) > self.TQQQsharepertick:
sym = self.TQQQ
self.Order(sym, self.TQQQsharepertick)
self.Debug([str(self.Time), str(sym), self.TQQQsharepertick])
self.quantity[sym] = self.TQQQsharepertick
self.fill_act = 1
self.fill_act_symbol = self.TQQQ
def Weeklydeposit(self):
if self.fill_act == 3: #Only when all buying fill activity is finished, also you can avoid first day error since some self. are not identified.
for symbol in self.quantity.copy().keys():
if symbol == self.BND1 and self.quantity[symbol] > 0:
quantity = self.Portfolio.TotalPortfolioValue * 0.99 / self.Securities[symbol].Close
if math.floor(quantity) != self.quantity[symbol]:
if (math.floor(quantity) - self.quantity[symbol]) > 0:
if (math.floor(quantity) - self.quantity[symbol]) > self.BND1sharepertick:
self.Order(symbol, (math.floor(quantity) - self.quantity[symbol]))
self.Debug([str(self.Time), str(symbol), (math.floor(quantity) - self.quantity[symbol]), "Weekly Rebalance"])
self.quantity[symbol] += (math.floor(quantity) - self.quantity[symbol])
self.fill_act = 1
self.fill_act_symbol = self.BND1
elif (math.floor(quantity) - self.quantity[symbol]) <= self.BND1sharepertick:
self.Order(symbol, math.floor(quantity) - self.quantity[symbol])
self.Debug([str(self.Time), str(symbol), str(math.floor(quantity) -self.quantity[symbol]), "Weekly Rebalance"])
self.quantity[symbol] += math.floor(quantity) - self.quantity[symbol]
self.fill_act = 3
elif symbol == self.TQQQ and self.quantity[symbol] > 0:
quantity = self.Portfolio.TotalPortfolioValue * 0.99 / self.Securities[symbol].Close
if math.floor(quantity) != self.quantity[symbol]:
if (math.floor(quantity) - self.quantity[symbol]) > 0:
if (math.floor(quantity) - self.quantity[symbol]) > self.TQQQsharepertick:
self.Order(symbol, (math.floor(quantity) - self.quantity[symbol]))
self.Debug([str(self.Time), str(symbol), (math.floor(quantity) - self.quantity[symbol]), "Weekly Rebalance"])
self.quantity[symbol] += (math.floor(quantity) - self.quantity[symbol])
self.fill_act = 1
self.fill_act_symbol = self.TQQQ
elif (math.floor(quantity) - self.quantity[symbol]) <= self.TQQQsharepertick:
self.Order(symbol, math.floor(quantity) - self.quantity[symbol])
self.Debug([str(self.Time), str(symbol), str(math.floor(quantity) -self.quantity[symbol]), "Weekly Rebalance"])
self.quantity[symbol] += (math.floor(quantity) - self.quantity[symbol])
self.fill_act = 3
def fill(self):
if self.fill_act_symbol :
if self.fill_act ==1 or self.fill_act ==2:
sym = self.fill_act_symbol
# self.Debug(["ongoing fill activity symbol", str(sym)])
# algorithm.Portfolio[security.Symbol].Invested:
if self.fill_act == 2: #Sell fill
if sym == self.TQQQ:
sharepertick = self.TQQQsharepertick
elif sym == self.BND1:
sharepertick = self.BND1sharepertick
if self.quantity[sym] > sharepertick:
self.Order(sym, - sharepertick)
self.Debug([str(self.Time), str(sym), str(-sharepertick)])
self.quantity[sym] -= sharepertick
self.fill_act = 2
elif self.quantity[sym] <= sharepertick:
self.Order(sym, - self.quantity[sym])
self.Debug([str(self.Time), str(sym), str(-self.quantity[sym])])
self.quantity[sym] = 0
self.fill_act = 0
if sym != self.BND1:
if sym != self.BOND:
del self.quantity[sym]
if self.BE_IN:
self.rebalance()
elif self.BE_IN == False and self.quantity[self.BND1] == 0:
self.rebalance_when_out_of_the_market()
elif self.fill_act == 1:
if sym == self.BND1:
sharepertick = self.BND1sharepertick
elif sym == self.TQQQ:
sharepertick = self.TQQQsharepertick
quantity = self.Portfolio.TotalPortfolioValue * 0.99 / self.Securities[sym].Close
if (math.floor(quantity) - self.quantity[sym]) > 0:
if math.floor(quantity) != self.quantity[sym] and self.quantity[sym] != 0:
if (math.floor(quantity) - self.quantity[sym]) > sharepertick:
self.Order(sym, sharepertick)
self.Debug([str(self.Time), str(sym), sharepertick])
self.quantity[sym] += sharepertick
self.fill_act = 1
elif (math.floor(quantity) - self.quantity[sym]) <= sharepertick:
self.Order(sym, math.floor(quantity) - self.quantity[sym])
self.Debug([str(self.Time), str(sym), str(math.floor(quantity) - self.quantity[sym])])
self.quantity[sym] += math.floor(quantity) - self.quantity[sym]
self.fill_act = 3
else:
self.fill_act = 3
def calc_return(self, stocks):
ret = {}
for symbol in stocks:
try:
ret[symbol] = self.data[symbol].Roc.Current.Value
except:
self.Debug(str(symbol))
continue
df_ret = pd.DataFrame.from_dict(ret, orient='index')
df_ret.columns = ['return']
sort_return = df_ret.sort_values(by = ['return'], ascending = self.lowmom)
return sort_return
def record_vars(self):
pass
def create_charts(self):
# Record variables
### SPY benchmark
self.spy.append(self.history[self.MRKT].iloc[-1])
spy_perf = self.spy[-1] / self.spy[0] * self.cap
self.Plot('Strategy Equity', 'SPY', spy_perf)
### Annual saw tooth return comparison: Portfolio VS QQQ
self.portfolio_value.append(self.Portfolio.TotalPortfolioValue)
if (self.DCOUNT==1) or (self.Time.year!=self.year):
self.saw_qqq_base = self.history[self.QQQ].iloc[-1]
self.saw_portfolio_base = self.Portfolio.TotalPortfolioValue
self.benchmark = self.history[self.QQQ].iloc[-1]
saw_qqq_return = (self.benchmark / self.saw_qqq_base -1)
saw_portfolio_return = self.portfolio_value[-1] / self.saw_portfolio_base - 1
self.Plot('Annual Saw Tooth Returns: Portfolio VS QQQ', 'Annual portfolio return', round(saw_portfolio_return, 4))
self.Plot('Annual Saw Tooth Returns: Portfolio VS QQQ', 'Annual QQQ return', round(float(saw_qqq_return), 4))
### IN/Out indicator and wait days
self.Plot("In Out", "in_market", int(self.BE_IN))
self.Plot("Wait Days", "waitdays", self.wait_days)
### Leverage
account_leverage = self.Portfolio.TotalHoldingsValue / self.Portfolio.TotalPortfolioValue
self.Plot('Leverage', 'leverage', round(account_leverage, 4))
self.year = self.Time.year
class SymbolData(object):
def __init__(self, symbol, roc, algorithm):
self.Symbol = symbol
self.Roc = RateOfChange(roc)
self.algorithm = algorithm
self.consolidator = algorithm.ResolveConsolidator(symbol, Resolution.Daily)
algorithm.RegisterIndicator(symbol, self.Roc, self.consolidator)
def Warmup(self, history):
for index, row in history.iterrows():
self.Roc.Update(index, row['close'])
class SelectionDataShort():
#3. Update the constructor to accept a history array
def __init__(self, Close_history):
self.MA1 = ExponentialMovingAverage(1)
self.MA3 = ExponentialMovingAverage(3)
self.MA5 = ExponentialMovingAverage(7)
self.MA9 = ExponentialMovingAverage(9)
self.MA13 = ExponentialMovingAverage(13)
self.MA20 = ExponentialMovingAverage(20)
self.MA25 = ExponentialMovingAverage(25)
self.MA34 = ExponentialMovingAverage(34)
self.MA50 = ExponentialMovingAverage(50)
self.MA55 = ExponentialMovingAverage(55)
self.MA72 = ExponentialMovingAverage(72)
self.MA89 = ExponentialMovingAverage(89)
self.MA100 = ExponentialMovingAverage(100)
self.MA200 = ExponentialMovingAverage(200)
self.MA75 = ExponentialMovingAverage(75)
self.RSI = RelativeStrengthIndex(14, MovingAverageType.Simple)
#4. Loop over the history data and update the indicators
for time, price in Close_history.items():
self.update(time, price)
def is_ready(self):
return self.MA9.IsReady and self.MA1.IsReady and self.MA3.IsReady and self.MA5.IsReady and self.MA13.IsReady and self.MA20.IsReady and self.MA25.IsReady and self.MA34.IsReady and self.MA50.IsReady and self.MA55.IsReady and self.MA72.IsReady and self.MA89.IsReady and self.MA75.IsReady and self.MA100.IsReady and self.MA200.IsReady and self.RSI.IsReady
def update(self, time, price):
self.MA1.Update(time, price)
self.MA3.Update(time, price)
self.MA5.Update(time, price)
self.MA9.Update(time, price)
self.MA13.Update(time, price)
self.MA20.Update(time, price)
self.MA25.Update(time, price)
self.MA34.Update(time, price)
self.MA55.Update(time, price)
self.MA72.Update(time, price)
self.MA89.Update(time, price)
self.MA75.Update(time, price)
self.MA20.Update(time, price)
self.MA50.Update(time, price)
self.MA100.Update(time, price)
self.MA200.Update(time, price)
self.RSI.Update(time, price)