| Overall Statistics |
|
Total Trades 43 Average Win 0% Average Loss 0% Compounding Annual Return 0% Drawdown 0% Expectancy 0 Net Profit 0% Sharpe Ratio 0 Probabilistic Sharpe Ratio 0% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0 Beta 0 Annual Standard Deviation 0 Annual Variance 0 Information Ratio 0 Tracking Error 0 Treynor Ratio 0 Total Fees $43.00 Estimated Strategy Capacity $6100000.00 Lowest Capacity Asset DRI R735QTJ8XC9X Portfolio Turnover 450.72% |
#region imports
from AlgorithmImports import *
import pandas as pd
import datetime
#endregion
class ParticleTransdimensionalAutosequencers(QCAlgorithm):
def Initialize(self):
# BASIC SETTINGS
self.SetStartDate(2023, 6, 22)
self.SetEndDate(2023, 6, 22)
self.SetCash(50000)
self.cash = 50000
self.equityls = ['SPY']
for symbol in self.equityls:
self.AddEquity(symbol, Resolution.Second, extendedMarketHours=False)
self.AddUniverseSelection(CoarseFundamentalUniverseSelectionModel(self.CoarseSelectionFunction))
self.UniverseSettings.Resolution = Resolution.Second
self.UniverseSettings.extendedMarketHours = True
# SCHEDULED EVENTS
self.TrackOpen_hour = 9
self.TrackOpen_min = 29
self.TrackOpen_sec = 55
self.SelectUniverse_hour = 10
self.SelectUniverse_min = 0
# Track open price of the day
self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.At(self.TrackOpen_hour, self.TrackOpen_min, self.TrackOpen_sec), self.TrackOpen)
# Select stocks with highest RVOL in the first 15 mins of trading
# self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.At(self.SelectUniverse_hour , self.SelectUniverse_min), self.SelectUniverse)
# Track PM levels + 9:30 - selectUniverse levels
self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.At(self.TrackOpen_hour, self.TrackOpen_min, self.TrackOpen_sec +1), self.DetectLevels)
# Liquidate all position before market close
self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.At(9, 45), self.ClosePositions)
# Cancel all open orders before market close
self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.At(9, 30), self.CancelOpenOrders)
# METRICS TO TRACK
# List or dictionary to keep track of values for the universe selection
self.universe = []
self.universe_neg = []
self.universe_combined = []
self.volume_by_symbol = {}
self.open_by_symbol = {}
self.gap = {}
self.gap_neg = {}
self.vol_after_gap = {}
self.vol_after_gap_neg = {}
self.rvol_by_symbol = {}
self.rvol_by_symbol_neg = {}
self.logged = False
# Essential to have this automatic warmup, otherwise indicators are all zero and indicator.is_ready = False
self.EnableAutomaticIndicatorWarmUp = True
# List of dictionary to keep track of indicators that determines entry and exit criteria
self.green_vol = {}
self.red_vol = {}
self.green_red_df = {}
self.vwap_dict = {}
self.consol_dict = {}
self.vwap_df = {}
self.vwap_cross_flag = {}
self.PM_rs_cross_flag = {}
self.PM_sp_cross_flag = {}
self.vwap_above_flag = {}
self.vwap_above_flag_20_bars = {}
self.vwap_high_flag = {}
self.vwap_low_flag = {}
self.high_vol_flag = {}
self.green_red_flag = {}
self.can_trade_flag = False
self.atr_dict = {}
self.HoDay = {}
self.LoDay = {}
self.entry_orders = {}
self.order_type = {}
self.SL = {}
self.SL_id = {}
self.TP = {}
self.TP_id = {}
self.entry_prices = {}
self.ST_range = {}
self.sma = {}
self.support = {}
self.support_levels = {}
self.resistance = {}
self.resistance_levels = {}
self.all_levels = {}
# PARAMETERS TO ADJUST
self.PT_Pct = 0.2
self.bars_tracked = 20
self.gap_thres = 0.03
self.high_vol_thres = 1.3
self.rvol_threshold = 0.0
self.stop_thres_atr = 0.1
self.RRR = 1.0
self.actual_RRR_cap_reversal_1 = 0.25
self.actual_RRR_cap_reversal_2 = 0.35
self.actual_RRR_cap_reversal_3 = 0.5
self.actual_RRR_cap_reversal_4 = 0.75
self.actual_RRR_cap_reversal_5 = 1.0
self.take_profit_quantity_1 = 0.5
self.take_profit_quantity_2 = 0.2
self.take_profit_quantity_3 = 0.15
self.take_profit_quantity_4 = 0.1
self.take_profit_quantity_5 = 0.05
self.actual_RRR_cap_breakout = 1.0
self.daily_stop = 0.02
self.daily_PL = 0
self.no_of_trades = 10
self.bars_apart = 0
self.levels_lookback = 2
self.levels_lookforward = 2
self.levels_lookback_PM = 1
self.levels_lookforward_PM = 1
# UNIVERSE SELECTION (00:00)
# Coarse Selection Filter executed at 00:00 on every day (once only) based on data of the previous trading day
def CoarseSelectionFunction(self, coarse):
self.volume_by_symbol = {c.Symbol: 0 for c in coarse if c.Price > 5 and c.Price < 10000 and c.Volume > 1000000 and c.HasFundamentalData}
self.Debug(f"Universe size before volume filter: {len(self.volume_by_symbol)} at {self.Time}")
self.green_vol = self.volume_by_symbol.copy()
self.red_vol = self.volume_by_symbol.copy()
self.daily_PL = 0
# initialize df and various flags
self.can_trade_flag = False
for symbol in self.volume_by_symbol.keys():
self.green_red_df[symbol] = pd.DataFrame([],columns = ['volume', 'green_red'])
self.vwap_df[symbol] = pd.DataFrame([],columns = ['vwap', 'close','high','low','vwap_high', 'vwap_low','range','sma_fast','sma_slow','trend','volume'])
self.vwap_cross_flag[symbol] = False
self.PM_rs_cross_flag[symbol] = False
self.PM_sp_cross_flag[symbol] = False
self.green_red_flag[symbol] = 0
self.high_vol_flag[symbol] = False
self.vwap_above_flag[symbol] = 0.0
self.vwap_high_flag[symbol] = False
self.vwap_low_flag[symbol] = False
try:
self.vwap_dict[symbol] = SymbolData(symbol, self)
self.sma[symbol] = SMAData(symbol, self)
except:
#self.Debug(f"{symbol} data not available. Need to be added using AddSecurity()")
self.AddEquity(symbol, Resolution.Second, extendedMarketHours=False)
self.vwap_dict[symbol] = SymbolData(symbol, self)
self.sma[symbol] = SMAData(symbol, self)
#self.Debug(f"Added {symbol} data using AddSecurity()")
return list(self.volume_by_symbol.keys())
# DATA INFLOW (09:30)
# OnData executed whenever there is new data arriving. In this case, new data arrive at 9:30 every day at customised interval (due to Resolution.Second and consolidator)
def OnDataConsolidated(self, sender, bar):
symbol = sender.Consolidated.Symbol
stop_time = self.Time.replace(hour=9, minute=45, second=0)
if self.Time > stop_time:
return
# register VWAP indicator at 9:30
if self.Time.hour == 9 and self.Time.minute == 30:
for symbol in self.universe_combined:
self.vwap_dict[symbol] = SymbolData(symbol, self)
self.sma[symbol] = SMAData(symbol, self)
# Track if stocks are tradable at that time range
if (self.Time.hour == 9 and self.Time.minute <=35):
self.can_trade_flag = True
else:
self.can_trade_flag = False
if self.daily_PL < - self.Portfolio.TotalPortfolioValue * self.daily_stop:
self.can_trade_flag = False
# Every bar, data got pumped in for many securities. We record the volume data for stocks that passes our coarse selection filter
try:
self.volume_by_symbol[symbol] += bar.Volume
# if the bar is green, record the volume in a seperate dictionary
if bar.Close > bar.Open:
self.green_vol[symbol] += bar.Volume
self.green_red_df[symbol].loc[self.Time] = [bar.Volume, "Green"]
if bar.Close < bar.Open:
self.red_vol[symbol] += bar.Volume
self.green_red_df[symbol].loc[self.Time] = [bar.Volume, "Red"]
except:
self.Log(f'{symbol} has data[symbol] as non-type')
# Every bar, data got pumped in for many securities. We record the volume data for stocks that passes our coarse selection filter
try:
self.vwap_dict[symbol].Update(bar)
vwap = self.vwap_dict[symbol].vwap.Current.Value
self.sma[symbol].Update(self.Time, bar.Close)
sma_fast = round(self.sma[symbol].sma5.Current.Value,1)
sma_slow = round(self.sma[symbol].sma10.Current.Value,1)
if self.Time.hour == 9 and self.Time.minute <= 29:
trend = "none"
if sma_fast > sma_slow and sma_fast > self.vwap_df[symbol].iloc[-1]['sma_fast'] and self.vwap_df[symbol].iloc[-1]['sma_fast'] > self.vwap_df[symbol].iloc[-2]['sma_fast']:
trend = "uptrend"
elif sma_fast < sma_slow and sma_fast < self.vwap_df[symbol].iloc[-1]['sma_fast'] and self.vwap_df[symbol].iloc[-1]['sma_fast'] < self.vwap_df[symbol].iloc[-2]['sma_fast']:
trend = "downtrend"
else:
trend = "none"
self.vwap_df[symbol].loc[self.Time] = [vwap, bar.Close, bar.High, bar.Low, bar.High - vwap, vwap - bar.Low, bar.High - bar.Low, sma_fast, sma_slow, trend, bar.Volume]
except:
self.Log(f'{symbol} has data[symbol] as non-type')
# When volume_by_symbol is cleared (it is cleared by a scheduled event), it will print the length of the final universe to be traded
if len(self.volume_by_symbol) == 0:
if not self.logged:
self.logged = True
self.Debug(f"Universe size for gap up stocks after volume filter: {len(self.universe)} at {self.Time}")
self.Debug(f"Universe size for gap down stocks after volume filter: {len(self.universe_neg)} at {self.Time}")
self.Debug(f"Combined universe size after volume filter: {len(self.universe_combined)} at {self.Time}")
self.Debug(f"Number of trades: {self.no_of_trades} at {self.Time}")
#return
try:
data_symbol = self.green_red_df[symbol]
length_of_data = data_symbol.shape[0]
if length_of_data <= self.bars_tracked:
last10barvol = data_symbol[-length_of_data:]
else:
last10barvol = data_symbol[-self.bars_tracked:]
green_vol_last10 = last10barvol.loc[last10barvol['green_red'] == "Green", "volume"].sum()
red_vol_last10 = last10barvol.loc[last10barvol['green_red'] == "Red", "volume"].sum()
try:
bullishness_last10 = green_vol_last10 / (green_vol_last10 + red_vol_last10)
except:
#self.Debug(f'{symbol} has green vol of {green_vol_last10} and red vol of {red_vol_last10}')
#if self.Time.hour == 9 and self.Time.minute == 32:
#self.Debug(f'it is {self.Time} now')
bullishness_last10 = 0.5
if bullishness_last10 >= 0.75:
self.green_red_flag[symbol] = bullishness_last10
elif bullishness_last10 <= 0.25:
self.green_red_flag[symbol] = bullishness_last10
else:
self.green_red_flag[symbol] = bullishness_last10
except:
self.Log(f'{symbol} fail to register near term bullishness')
try:
self.vwap_cross_flag[symbol] = False # reset flag to 0 every bar
self.PM_rs_cross_flag[symbol] = False
self.PM_sp_cross_flag[symbol] = False
self.vwap_above_flag[symbol] = 0.0 # reset flag to 0 every bar
try:
PM_rs_max_key = max(list(self.resistance_levels[symbol].keys()))
PM_rs_max = list(self.resistance_levels[symbol][PM_rs_max_key].keys())[0]
PM_sp_min_key = min(list(self.support_levels[symbol].keys()))
PM_sp_min = list(self.support_levels[symbol][PM_sp_min_key].keys())[0]
#self.Debug(f"{symbol} has PM min support level of {PM_sp_min} and max resistance level of {PM_rs_max} at {self.Time}")
except:
PM_rs_max = 1000000
PM_sp_min = -100000
# self.Debug(f"{symbol} has no PM levels")
self.vwap_dict[symbol].Update(bar)
vwap = self.vwap_dict[symbol].vwap.Current.Value
self.sma[symbol].Update(self.Time, bar.Close)
# vwap cross flag
if (bar.High > vwap and bar.Low < vwap) and (bar.Close > bar.Open):
self.vwap_cross_flag[symbol] = 1
elif (bar.High > vwap and bar.Low < vwap) and (bar.Close < bar.Open):
self.vwap_cross_flag[symbol] = -1
else:
self.vwap_cross_flag[symbol] = False
# PM resistance cross flag
if (bar.High > PM_rs_max and bar.Low < PM_rs_max) and (bar.Close > bar.Open):
self.PM_rs_cross_flag[symbol] = 1
elif (bar.High > PM_rs_max and bar.Low < PM_rs_max) and (bar.Close < bar.Open):
self.PM_rs_cross_flag[symbol] = -1
else:
self.PM_rs_cross_flag[symbol] = False
# PM support cross flag
if (bar.High > PM_sp_min and bar.Low < PM_sp_min) and (bar.Close > bar.Open):
self.PM_sp_cross_flag[symbol] = 1
elif (bar.High > PM_sp_min and bar.Low < PM_sp_min) and (bar.Close < bar.Open):
self.PM_sp_cross_flag[symbol] = -1
else:
self.PM_sp_cross_flag[symbol] = False
# vwap df tracking
sma_fast = round(self.sma[symbol].sma5.Current.Value,1)
sma_slow = round(self.sma[symbol].sma10.Current.Value,1)
if sma_fast > sma_slow and sma_fast > self.vwap_df[symbol].iloc[-1]['sma_fast'] and self.vwap_df[symbol].iloc[-1]['sma_fast'] > self.vwap_df[symbol].iloc[-2]['sma_fast']:
trend = "uptrend"
elif sma_fast < sma_slow and sma_fast < self.vwap_df[symbol].iloc[-1]['sma_fast'] and self.vwap_df[symbol].iloc[-1]['sma_fast'] < self.vwap_df[symbol].iloc[-2]['sma_fast']:
trend = "downtrend"
else:
trend = "none"
self.vwap_df[symbol].loc[self.Time] = [vwap, bar.Close, bar.High, bar.Low, bar.High - vwap, vwap - bar.Low, bar.High - bar.Low, sma_fast , sma_slow, trend, bar.Volume]
no_of_bars = self.vwap_df[symbol].shape[0]
no_of_bars_above_vwap = len(self.vwap_df[symbol][self.vwap_df[symbol]['close'] > self.vwap_df[symbol]['vwap']])
perc_of_bars_above_vwap = no_of_bars_above_vwap / no_of_bars
self.vwap_above_flag[symbol] = perc_of_bars_above_vwap
# vwap above for last 20 bars
if no_of_bars >= self.bars_tracked:
vwap_last_20_bars = self.vwap_df[symbol][-self.bars_tracked:]
no_of_bars_above_vwap_20_bars = len(vwap_last_20_bars[self.vwap_df[symbol]['close'] > self.vwap_df[symbol]['vwap']])
self.vwap_above_flag_20_bars[symbol] = no_of_bars_above_vwap_20_bars / self.bars_tracked
else:
self.vwap_above_flag_20_bars[symbol] = self.vwap_above_flag[symbol]
# High of Day tracking
self.HoDay[symbol] = self.vwap_df[symbol]['high'].max()
# Low of Day tracking
self.LoDay[symbol] = self.vwap_df[symbol]['low'].min()
# Track vwap distance (long)
if self.vwap_high_flag[symbol] == False:
vwap_high = max(self.vwap_df[symbol]['vwap_high'])
if vwap_high > self.atr_dict[symbol].atr.Current.Value * 1:
self.vwap_high_flag[symbol] = True
# Track vwap distance (short)
if self.vwap_low_flag[symbol] == False:
vwap_low = max(self.vwap_df[symbol]['vwap_low'])
if vwap_low > self.atr_dict[symbol].atr.Current.Value * 1:
self.vwap_low_flag[symbol] = True
# Latest range tracking
vwap_last_5_bars = self.vwap_df[symbol][-5:]
self.ST_range[symbol] = vwap_last_5_bars['range'].mean()
except:
self.Log(f'{symbol} fail to register near VWAP, resistance and support levels cross')
#except:
# self.vwap_cross_flag[symbol] = False
# self.vwap_above_flag[symbol] = 0.0
try:
# Track Levels
if self.Time.hour >= self.SelectUniverse_hour and self.Time.minute >= self.SelectUniverse_min + 2:
n1 = self.levels_lookback
n2 = self.levels_lookforward
history = self.vwap_df[symbol].tail(n1 + n2 + 1)
l = len(history)
for row in range(n1, l-n2):
if support(history,row,n1,n2):
self.support[symbol][history.index[row-1]] = history.low[row]
rounded_level = round(history.low[row],0)
if rounded_level not in list(self.support_levels[symbol].keys()):
self.support_levels[symbol][rounded_level] = {history.low[row]: history.volume[row]}
else:
previous_walvl = list(self.support_levels[symbol][rounded_level].keys())[0]
previous_vol = list(self.support_levels[symbol][rounded_level].values())[0]
new_lvl_input = history.low[row]
new_lvl_vol = history.volume[row]
updated_walvl = (previous_walvl * previous_vol + new_lvl_input * new_lvl_vol) / (previous_vol + new_lvl_vol)
updated_vol = previous_vol + new_lvl_vol
self.support_levels[symbol][rounded_level] = {updated_walvl: updated_vol}
if rounded_level not in list(self.all_levels[symbol].keys()):
self.all_levels[symbol][rounded_level] = {history.low[row]: history.volume[row]}
else:
previous_walvl = list(self.all_levels[symbol][rounded_level].keys())[0]
previous_vol = list(self.all_levels[symbol][rounded_level].values())[0]
new_lvl_input = history.low[row]
new_lvl_vol = history.volume[row]
updated_walvl = (previous_walvl * previous_vol + new_lvl_input * new_lvl_vol) / (previous_vol + new_lvl_vol)
updated_vol = previous_vol + new_lvl_vol
self.all_levels[symbol][rounded_level] = {updated_walvl: updated_vol}
if resistance(history,row,n1,n2):
self.resistance[symbol][history.index[row-1]] = history.high[row]
rounded_level = round(history.high[row],0)
if rounded_level not in list(self.resistance_levels[symbol].keys()):
self.resistance_levels[symbol][rounded_level] = {history.high[row]: history.volume[row]}
else:
previous_walvl = list(self.resistance_levels[symbol][rounded_level].keys())[0]
previous_vol = list(self.resistance_levels[symbol][rounded_level].values())[0]
new_lvl_input = history.high[row]
new_lvl_vol = history.volume[row]
updated_walvl = (previous_walvl * previous_vol + new_lvl_input * new_lvl_vol) / (previous_vol + new_lvl_vol)
updated_vol = previous_vol + new_lvl_vol
self.resistance_levels[symbol][rounded_level] = {updated_walvl: updated_vol}
if rounded_level not in list(self.all_levels[symbol].keys()):
self.all_levels[symbol][rounded_level] = {history.high[row]: history.volume[row]}
else:
previous_walvl = list(self.all_levels[symbol][rounded_level].keys())[0]
previous_vol = list(self.all_levels[symbol][rounded_level].values())[0]
new_lvl_input = history.high[row]
new_lvl_vol = history.volume[row]
updated_walvl = (previous_walvl * previous_vol + new_lvl_input * new_lvl_vol) / (previous_vol + new_lvl_vol)
updated_vol = previous_vol + new_lvl_vol
self.all_levels[symbol][rounded_level] = {updated_walvl: updated_vol}
except:
self.Log(f'{symbol} fail to track levels')
# Track volume of current bar vs the past 20 bars
try:
self.high_vol_flag[symbol] = False
try:
vol_last_20_bar = self.SMA(symbol, self.bars_tracked, Resolution.Second, Field.Volume)
if bar.Volume > self.high_vol_thres * vol_last_20_bar.Current.Value:
self.high_vol_flag[symbol] = True
except:
self.high_vol_flag[symbol] = False
self.Log(f'{symbol} fail to high vol flag')
except:
self.Log(f'{symbol} fail to high vol flag')
# Buying criteria
try:
invested = [x.Symbol for x in self.Portfolio.Values if x.Invested]
try:
if (bar.Close - self.LoDay[symbol]) > 0:
day_range_RRR = (self.HoDay[symbol] - bar.Close)/(bar.Close - self.LoDay[symbol])
else:
day_range_RRR = 0
except:
day_range_RRR = 0
try:
last_support_time = list(self.support[symbol].keys())[-1]
if self.Time - last_support_time <= datetime.timedelta(minutes=5):
just_support = True
else:
just_support = False
except:
just_support = False
long_reversal_condition = (symbol not in invested and self.PM_rs_cross_flag[symbol] == 1 and self.can_trade_flag == True and self.vwap_df[symbol].iloc[-1]['trend'] == "uptrend")
long_breakout_condition = (symbol not in invested and self.vwap_high_flag[symbol] == True and self.vwap_cross_flag[symbol] == 1 and self.can_trade_flag == True and self.green_red_flag[symbol] >= 0.6 and self.high_vol_flag[symbol] == True and self.Time.hour < 14 and self.vwap_above_flag[symbol] >= 0.75 and self.vwap_df[symbol].iloc[-1]['trend'] == "uptrend")
if long_reversal_condition:
# if self.high_vol_flag[symbol] == True and self.vwap_cross_flag[symbol] == 1 and self.green_red_flag[symbol] >= 0.75 and symbol not in invested and self.can_trade_flag == True and self.vwap_above_flag[symbol] >= 0.5 and self.vwap_above_flag_20_bars[symbol] >= 0.75 and self.vwap_high_flag[symbol] == True:
# Bracket
take_profit = data[symbol].Close * 20
profit_potential = take_profit - bar.Close
stop_loss_support = list(self.support[symbol].values())[-1] - self.ST_range[symbol] * 0.5
stop_loss_latest_atr = self.vwap_dict[symbol].vwap.Current.Value - self.ST_range[symbol] * 1
# stop_loss = max(stop_loss_day_range , stop_loss_latest_atr)
stop_loss = stop_loss_support
risk = bar.Close - stop_loss
# Quantity
risk_quantum = self.daily_stop * self.Portfolio.TotalPortfolioValue * 1 / self.no_of_trades
theoretical_quantity = risk_quantum / risk
buying_power = self.Portfolio.GetBuyingPower(symbol, OrderDirection.Buy)
binding_quantity = buying_power / (self.vwap_dict[symbol].vwap.Current.Value * 1.02)
quantity = min(theoretical_quantity, binding_quantity)
# Entry criteria
if profit_potential / risk >= self.RRR:
# Debug on conditions
self.Debug(f"BUY {symbol} at {self.Time} meet long_reversal_condition: {long_reversal_condition}, long_breakout_condition: {long_breakout_condition}")
self.Debug(f"BUY {symbol} at {self.Time} with implied RRR {profit_potential / risk}")
try:
PM_rs_max_key = max(list(self.resistance_levels[symbol].keys()))
PM_rs_max = list(self.resistance_levels[symbol][PM_rs_max_key].keys())[0]
PM_sp_min_key = min(list(self.support_levels[symbol].keys()))
PM_sp_min = list(self.support_levels[symbol][PM_sp_min_key].keys())[0]
except:
PM_rs_max = 1000000
PM_sp_min = -100000
self.Debug(f"PM resistance at {PM_rs_max}")
self.Debug(f"PM support at {PM_sp_min}")
self.Debug(f"self.PM_rs_cross_flag[symbol] at {self.PM_rs_cross_flag[symbol]}")
self.Debug(f"self.vwap_df[symbol].iloc[-1]['trend'] at {self.vwap_df[symbol].iloc[-1]['trend']}")
self.entry_orders[symbol] = True
self.MarketOrder(symbol, quantity)
# Liquidate all stocks without stoploss
try:
if symbol in invested and self.SL[symbol] is None:
self.Liquidate(symbol)
except:
pass
except:
self.Log(f'{symbol} fail to execute buy criteria')
# Short Sell criteria
try:
invested = [x.Symbol for x in self.Portfolio.Values if x.Invested]
try:
if (bar.Close - self.LoDay[symbol]) > 0:
day_range_RRR = (self.HoDay[symbol] - bar.Close)/(bar.Close - self.LoDay[symbol])
else:
day_range_RRR = 5
except:
day_range_RRR = 5
try:
last_resistance_time = list(self.resistance[symbol].keys())[-1]
if self.Time - last_resistance_time < datetime.timedelta(minutes=5):
just_resistance = True
else:
just_resistance = False
except:
just_resistance = False
short_reversal_condition = (symbol not in invested and self.PM_sp_cross_flag[symbol] == -1 and self.can_trade_flag == True and self.vwap_df[symbol].iloc[-1]['trend'] == "downtrend")
short_breakout_condition = (symbol not in invested and self.vwap_low_flag[symbol] == True and self.vwap_cross_flag[symbol] == -1 and self.can_trade_flag == True and self.green_red_flag[symbol] <= 0.4 and self.high_vol_flag[symbol] == True and self.Time.hour < 14 and self.vwap_above_flag[symbol] <= 0.25 and self.vwap_df[symbol].iloc[-1]['trend'] == "downtrend")
if short_reversal_condition:
# if self.high_vol_flag[symbol] == True and self.vwap_cross_flag[symbol] == -1 and self.green_red_flag[symbol] <= 0.25 and symbol not in invested and self.can_trade_flag == True and self.vwap_above_flag[symbol] <= 0.5 and self.vwap_above_flag_20_bars[symbol] <= 0.25 and self.vwap_low_flag[symbol] == True:
# Bracket
take_profit = self.LoDay[symbol] * 0.005
profit_potential = bar.Close - take_profit
stop_loss_resistance = list(self.resistance[symbol].values())[-1] + self.ST_range[symbol] * 0.5
stop_loss_latest_atr = self.vwap_dict[symbol].vwap.Current.Value + self.ST_range[symbol] * 1
stop_loss = stop_loss_resistance
risk = stop_loss - bar.Close
# Quantity
risk_quantum = self.daily_stop * self.Portfolio.TotalPortfolioValue * 1/ self.no_of_trades
theoretical_quantity = risk_quantum / risk
buying_power = self.Portfolio.GetBuyingPower(symbol, OrderDirection.Sell)
binding_quantity = buying_power / (self.vwap_dict[symbol].vwap.Current.Value * 1.02)
quantity = min(theoretical_quantity, binding_quantity)
# Entry criteria
if profit_potential / risk >= self.RRR:
# Debug on conditions
self.Debug(f"SELL {symbol} at {self.Time} meet short_reversal_condition: {short_reversal_condition}, short_breakout_condition: {short_breakout_condition}")
self.Debug(f"SELL {symbol} at {self.Time} with implied RRR of {profit_potential / risk}")
try:
PM_rs_max_key = max(list(self.resistance_levels[symbol].keys()))
PM_rs_max = list(self.resistance_levels[symbol][PM_rs_max_key].keys())[0]
PM_sp_min_key = min(list(self.support_levels[symbol].keys()))
PM_sp_min = list(self.support_levels[symbol][PM_sp_min_key].keys())[0]
except:
PM_rs_max = 1000000
PM_sp_min = -100000
self.Debug(f"PM resistance at {PM_rs_max}")
self.Debug(f"PM support at {PM_sp_min}")
self.Debug(f"self.PM_sp_cross_flag[symbol] at {self.PM_sp_cross_flag[symbol]}")
self.Debug(f"self.vwap_df[symbol].iloc[-1]['trend'] at {self.vwap_df[symbol].iloc[-1]['trend']}")
self.entry_orders[symbol] = True
self.MarketOrder(symbol, -quantity)
# Liquidate all stocks without stoploss
try:
if symbol in invested and self.SL[symbol] is None:
self.Liquidate(symbol)
except:
pass
except:
self.Log(f'{symbol} fail to execute short sell criteria')
# OnData executed whenever there is new data arriving. In this case, new data arrive at 9:30 every day at minute interval (due to Resolution.Minute)
def OnData(self, data):
pass
# WHEN ORDER IS MADE, ATTACHED A BRACKET
# Orderevent: This function is triggered automatically every time an order evenet occurs
def OnOrderEvent(self, orderEvent):
if orderEvent.Status == OrderStatus.Filled:
if self.entry_orders[orderEvent.Symbol] == True:
# For buy orders
if orderEvent.FillQuantity > 0:
#stop_loss_price = self.vwap_dict[orderEvent.Symbol].vwap.Current.Value - self.atr_dict[orderEvent.Symbol].atr.Current.Value* self.stop_thres_atr
stop_loss_day_range = self.vwap_dict[orderEvent.Symbol].vwap.Current.Value - (self.HoDay[orderEvent.Symbol] - self.LoDay[orderEvent.Symbol]) * self.stop_thres_atr
stop_loss_latest_atr = self.vwap_dict[orderEvent.Symbol].vwap.Current.Value - self.ST_range[orderEvent.Symbol] * 1
# stop_loss_price = max(stop_loss_day_range , stop_loss_latest_atr)
PM_rs_max_key = max(list(self.resistance_levels[orderEvent.Symbol].keys()))
PM_rs_max = list(self.resistance_levels[orderEvent.Symbol][PM_rs_max_key].keys())[0]
stop_loss_price = PM_rs_max - self.ST_range[orderEvent.Symbol] * 0.5
risk = orderEvent.FillPrice - stop_loss_price
#stop_loss_price = self.vwap_dict[orderEvent.Symbol].vwap.Current.Value - (self.HoDay[orderEvent.Symbol] - self.LoDay[orderEvent.Symbol]) * self.stop_thres_atr
#risk = min(orderEvent.FillPrice - stop_loss_price , self.ST_range[orderEvent.Symbol] * 2)
actual_RRR_cap = self.actual_RRR_cap_reversal_1
#take_profit_price = min(self.HoDay[orderEvent.Symbol] * 0.995, orderEvent.FillPrice + risk * actual_RRR_cap)
take_profit_price_1 = orderEvent.FillPrice + risk * self.actual_RRR_cap_reversal_1
take_profit_price_2 = orderEvent.FillPrice + risk * self.actual_RRR_cap_reversal_2
take_profit_price_3 = orderEvent.FillPrice + risk * self.actual_RRR_cap_reversal_3
take_profit_price_4 = orderEvent.FillPrice + risk * self.actual_RRR_cap_reversal_4
take_profit_price_5 = orderEvent.FillPrice + risk * self.actual_RRR_cap_reversal_5
take_profit_quantity_1 = orderEvent.FillQuantity * self.take_profit_quantity_1
take_profit_quantity_2 = orderEvent.FillQuantity * self.take_profit_quantity_2
take_profit_quantity_3 = orderEvent.FillQuantity * self.take_profit_quantity_3
take_profit_quantity_4 = orderEvent.FillQuantity * self.take_profit_quantity_4
take_profit_quantity_5 = orderEvent.FillQuantity * self.take_profit_quantity_5
#take_profit_price = min(self.HoDay[orderEvent.Symbol] * 0.995, orderEvent.FillPrice + risk * actual_RRR_cap)
profit_potential = take_profit_price_1 - orderEvent.FillPrice
implied_RRR = profit_potential / risk
self.Debug(f"BUY {orderEvent.Symbol} at {self.Time} with market entry at {orderEvent.FillPrice}, stop loss at {stop_loss_price}, RRR at {implied_RRR}, risk at {risk} atr at {self.atr_dict[orderEvent.Symbol].atr}")
self.Debug(f"VWAP at {self.vwap_dict[orderEvent.Symbol].vwap.Current.Value}, day range at {self.HoDay[orderEvent.Symbol] - self.LoDay[orderEvent.Symbol]} and take profit at {take_profit_price_1}")
self.Debug(f"Day high is at {self.HoDay[orderEvent.Symbol]} and Day low is at {self.LoDay[orderEvent.Symbol]}")
self.SL[orderEvent.Symbol] = [self.StopMarketOrder(orderEvent.Symbol, -take_profit_quantity_1, stop_loss_price),
self.StopMarketOrder(orderEvent.Symbol, -take_profit_quantity_2, stop_loss_price),
self.StopMarketOrder(orderEvent.Symbol, -take_profit_quantity_3, stop_loss_price),
self.StopMarketOrder(orderEvent.Symbol, -take_profit_quantity_4, stop_loss_price),
self.StopMarketOrder(orderEvent.Symbol, -take_profit_quantity_5, stop_loss_price)]
self.SL_id[orderEvent.Symbol] = [self.SL[orderEvent.Symbol][0].OrderId,
self.SL[orderEvent.Symbol][1].OrderId,
self.SL[orderEvent.Symbol][2].OrderId,
self.SL[orderEvent.Symbol][3].OrderId,
self.SL[orderEvent.Symbol][4].OrderId]
self.TP[orderEvent.Symbol] = [self.LimitOrder(orderEvent.Symbol, -take_profit_quantity_1, take_profit_price_1),
self.LimitOrder(orderEvent.Symbol, -take_profit_quantity_2, take_profit_price_2),
self.LimitOrder(orderEvent.Symbol, -take_profit_quantity_3, take_profit_price_3),
self.LimitOrder(orderEvent.Symbol, -take_profit_quantity_4, take_profit_price_4),
self.LimitOrder(orderEvent.Symbol, -take_profit_quantity_5, take_profit_price_5)]
self.TP_id[orderEvent.Symbol] = [self.TP[orderEvent.Symbol][0].OrderId,
self.TP[orderEvent.Symbol][1].OrderId,
self.TP[orderEvent.Symbol][2].OrderId,
self.TP[orderEvent.Symbol][3].OrderId,
self.TP[orderEvent.Symbol][4].OrderId]
self.entry_prices[orderEvent.Symbol] = orderEvent.FillPrice
# For sell orders
elif orderEvent.FillQuantity < 0:
#stop_loss_price = self.vwap_dict[orderEvent.Symbol].vwap.Current.Value + self.atr_dict[orderEvent.Symbol].atr.Current.Value* self.stop_thres_atr
stop_loss_day_range = self.vwap_dict[orderEvent.Symbol].vwap.Current.Value + (self.HoDay[orderEvent.Symbol] - self.LoDay[orderEvent.Symbol]) * self.stop_thres_atr
stop_loss_latest_atr = self.vwap_dict[orderEvent.Symbol].vwap.Current.Value + self.ST_range[orderEvent.Symbol] * 1
# stop_loss_price = min(stop_loss_day_range , stop_loss_latest_atr)
PM_sp_min_key = min(list(self.support_levels[orderEvent.Symbol].keys()))
PM_sp_min = list(self.support_levels[orderEvent.Symbol][PM_sp_min_key].keys())[0]
stop_loss_price = PM_sp_min + self.ST_range[orderEvent.Symbol] * 0.5
risk = stop_loss_price - orderEvent.FillPrice
#stop_loss_price = self.vwap_dict[orderEvent.Symbol].vwap.Current.Value + (self.HoDay[orderEvent.Symbol] - self.LoDay[orderEvent.Symbol])* self.stop_thres_atr
#risk = min(stop_loss_price - orderEvent.FillPrice, self.ST_range[orderEvent.Symbol] * 2)
actual_RRR_cap = self.actual_RRR_cap_reversal_1
# take_profit_price = max(self.LoDay[orderEvent.Symbol] * 1.005, orderEvent.FillPrice - risk * actual_RRR_cap)
take_profit_price_1 = orderEvent.FillPrice - risk * self.actual_RRR_cap_reversal_1
take_profit_price_2 = orderEvent.FillPrice - risk * self.actual_RRR_cap_reversal_2
take_profit_price_3 = orderEvent.FillPrice - risk * self.actual_RRR_cap_reversal_3
take_profit_price_4 = orderEvent.FillPrice - risk * self.actual_RRR_cap_reversal_4
take_profit_price_5 = orderEvent.FillPrice - risk * self.actual_RRR_cap_reversal_5
take_profit_quantity_1 = orderEvent.FillQuantity * self.take_profit_quantity_1
take_profit_quantity_2 = orderEvent.FillQuantity * self.take_profit_quantity_2
take_profit_quantity_3 = orderEvent.FillQuantity * self.take_profit_quantity_3
take_profit_quantity_4 = orderEvent.FillQuantity * self.take_profit_quantity_4
take_profit_quantity_5 = orderEvent.FillQuantity * self.take_profit_quantity_5
profit_potential = orderEvent.FillPrice - take_profit_price_1
implied_RRR = profit_potential / risk
self.Debug(f"SELL {orderEvent.Symbol} at {self.Time} with market entry at {orderEvent.FillPrice}, stop loss at {stop_loss_price}, RRR at {implied_RRR}, risk at {risk} atr at {self.atr_dict[orderEvent.Symbol].atr}")
self.Debug(f"VWAP at {self.vwap_dict[orderEvent.Symbol].vwap.Current.Value}, day range at {self.HoDay[orderEvent.Symbol] - self.LoDay[orderEvent.Symbol]} and take profit at {take_profit_price_1}")
self.Debug(f"Day high is at {self.HoDay[orderEvent.Symbol]} and Day low is at {self.LoDay[orderEvent.Symbol]}")
self.SL[orderEvent.Symbol] = [self.StopMarketOrder(orderEvent.Symbol, -take_profit_quantity_1, stop_loss_price),
self.StopMarketOrder(orderEvent.Symbol, -take_profit_quantity_2, stop_loss_price),
self.StopMarketOrder(orderEvent.Symbol, -take_profit_quantity_3, stop_loss_price),
self.StopMarketOrder(orderEvent.Symbol, -take_profit_quantity_4, stop_loss_price),
self.StopMarketOrder(orderEvent.Symbol, -take_profit_quantity_5, stop_loss_price)]
self.SL_id[orderEvent.Symbol] = [self.SL[orderEvent.Symbol][0].OrderId,
self.SL[orderEvent.Symbol][1].OrderId,
self.SL[orderEvent.Symbol][2].OrderId,
self.SL[orderEvent.Symbol][3].OrderId,
self.SL[orderEvent.Symbol][4].OrderId]
self.TP[orderEvent.Symbol] = [self.LimitOrder(orderEvent.Symbol, -take_profit_quantity_1, take_profit_price_1),
self.LimitOrder(orderEvent.Symbol, -take_profit_quantity_2, take_profit_price_2),
self.LimitOrder(orderEvent.Symbol, -take_profit_quantity_3, take_profit_price_3),
self.LimitOrder(orderEvent.Symbol, -take_profit_quantity_4, take_profit_price_4),
self.LimitOrder(orderEvent.Symbol, -take_profit_quantity_5, take_profit_price_5)]
self.TP_id[orderEvent.Symbol] = [self.TP[orderEvent.Symbol][0].OrderId,
self.TP[orderEvent.Symbol][1].OrderId,
self.TP[orderEvent.Symbol][2].OrderId,
self.TP[orderEvent.Symbol][3].OrderId,
self.TP[orderEvent.Symbol][4].OrderId]
#self.SL[orderEvent.Symbol] = self.StopMarketOrder(orderEvent.Symbol, -orderEvent.FillQuantity, stop_loss_price)
#self.TP[orderEvent.Symbol] = self.LimitOrder(orderEvent.Symbol, -orderEvent.FillQuantity, take_profit_price)
self.entry_prices[orderEvent.Symbol] = orderEvent.FillPrice
self.entry_orders[orderEvent.Symbol] = False
elif (self.SL[orderEvent.Symbol] is not None) and (self.TP[orderEvent.Symbol] is not None):
if self.SL[orderEvent.Symbol] is not None and orderEvent.OrderId in self.SL_id[orderEvent.Symbol]:
# self.TP[orderEvent.Symbol].Cancel()
position_of_order = self.SL_id[orderEvent.Symbol].index(orderEvent.OrderId)
self.TP[orderEvent.Symbol][position_of_order].Cancel()
self.TP_id[orderEvent.Symbol].remove(self.TP_id[orderEvent.Symbol][position_of_order])
self.SL_id[orderEvent.Symbol].remove(self.SL_id[orderEvent.Symbol][position_of_order])
if self.SL_id[orderEvent.Symbol] == []:
self.SL[orderEvent.Symbol] = None
self.Debug(f"Take loss at {orderEvent.FillPrice} and cancelled {orderEvent.Symbol} take profit. {len(self.TP_id[orderEvent.Symbol])} of take profit left at {self.Time}")
self.daily_PL += orderEvent.FillQuantity * (self.entry_prices[orderEvent.Symbol] - orderEvent.FillPrice)
self.Debug(f"Daily P&L is now {self.daily_PL}")
if self.SL[orderEvent.Symbol] == []:
self.entry_prices[orderEvent.Symbol] = None
elif self.TP[orderEvent.Symbol] is not None and orderEvent.OrderId in self.TP_id[orderEvent.Symbol]:
position_of_order = self.TP_id[orderEvent.Symbol].index(orderEvent.OrderId)
self.SL[orderEvent.Symbol][position_of_order].Cancel()
self.TP_id[orderEvent.Symbol].remove(self.TP_id[orderEvent.Symbol][position_of_order])
self.SL_id[orderEvent.Symbol].remove(self.SL_id[orderEvent.Symbol][position_of_order])
if self.SL_id[orderEvent.Symbol] == []:
self.SL[orderEvent.Symbol] = None
self.Debug(f"Take profit at {orderEvent.FillPrice} and cancelled {orderEvent.Symbol} stop loss. {len(self.SL_id[orderEvent.Symbol])} of stop loss left at {self.Time}")
self.daily_PL += orderEvent.FillQuantity * (self.entry_prices[orderEvent.Symbol] - orderEvent.FillPrice)
self.Debug(f"Daily P&L is now {self.daily_PL}")
if self.SL_id[orderEvent.Symbol] == []:
self.entry_prices[orderEvent.Symbol] = None
# SCHEDULED EVENTS
# TrackOpen is executed everyday at 9:31 and track the opening price of the current trading day. Slight descrepency with actual opening price but close enough
def TrackOpen(self):
for symbol, volume in self.volume_by_symbol.items():
previous_day = self.Time - datetime.timedelta(days=1)
start = self.Time.replace(hour=9, minute=29, second=0)
stop = self.Time
historyDataMin = self.History(symbol, start, stop, Resolution.Second, extendedMarketHours=True)
try:
open_price_sym = historyDataMin['open'][-1]
except:
open_price_sym = 0
self.open_by_symbol[symbol] = open_price_sym
# Calculate gap%
for symbol, volume in self.volume_by_symbol.items():
historyData = self.History(symbol,2,Resolution.Daily)
try:
today_open = self.open_by_symbol[symbol]
ytd_close = historyData['close'][-1]
except:
self.Debug(f"History data unavailable for {symbol.Value}")
continue
priceGap = today_open - ytd_close
percentGap = priceGap / ytd_close
if (percentGap > self.gap_thres):
self.gap[symbol] = percentGap
self.Debug(f'Gap up stock {symbol} closes at {ytd_close} previously and opens at {today_open} now, it has gap up percentage of {percentGap} at {self.Time}')
self.vol_after_gap[symbol] = volume
if (percentGap < -self.gap_thres) and (percentGap > -1):
self.gap_neg[symbol] = percentGap
self.Debug(f'Gap down stock {symbol} closes at {ytd_close} previously and opens at {today_open} now, it has gap down percentage of {percentGap} at {self.Time}')
self.vol_after_gap_neg[symbol] = volume
self.Debug(f'Universe size after gap positive filter: {len(self.gap)} and negative filter: {len(self.gap_neg)} at {self.Time}')
# SelectUniverse is executed to calculate gap %, then calculate rvol, sort the narrowed list after gap filter by rvol, then output the top few
def SelectUniverse(self):
self.universe = []
self.universe_neg = []
self.universe_combined = []
# Calculate rvol for gap up stocks and sort that
for symbol, gap in self.gap.items():
volume = self.vol_after_gap[symbol]
symbol_sma = self.SMA(symbol, 20, Resolution.Daily, Field.Volume)
symbol_rvol = self.volume_by_symbol[symbol] / self.SMA(symbol, 20, Resolution.Daily, Field.Volume).Current.Value
if symbol_sma.Current.Value >= 0:
self.rvol_by_symbol[symbol] = symbol_rvol
sorted_rvol_ls = sorted(self.rvol_by_symbol.items(), key=lambda x:x[1], reverse=True)
temp_ls = []
for tuple in sorted_rvol_ls:
if tuple[1] > self.rvol_threshold: # A stock has to trade at least 30% of average daily volume to be qualified as in-play stocks
temp_ls.append(tuple[0])
self.universe = temp_ls[:300]
for symbol in self.universe:
self.atr_dict[symbol] = AtrData(symbol, self)
atr = self.atr_dict[symbol].atr
self.Debug(f'Gap up stock {symbol} has avg daily ATR of {atr}, avg daily volume of {self.SMA(symbol, 20, Resolution.Daily, Field.Volume)} and intraday volume of {self.volume_by_symbol[symbol]} and rvol of {self.rvol_by_symbol[symbol]} and gap of {self.gap[symbol]} and green vol of {self.green_vol[symbol]} and red vol of {self.red_vol[symbol]} at {self.Time}')
# Calculate rvol for gap down stocks and sort that
for symbol, gap in self.gap_neg.items():
volume = self.vol_after_gap_neg[symbol]
symbol_sma = self.SMA(symbol, 20, Resolution.Daily, Field.Volume)
symbol_rvol = self.volume_by_symbol[symbol] / self.SMA(symbol, 20, Resolution.Daily, Field.Volume).Current.Value
if symbol_sma.Current.Value >= 0:
self.rvol_by_symbol_neg[symbol] = symbol_rvol
sorted_rvol_ls_neg = sorted(self.rvol_by_symbol_neg.items(), key=lambda x:x[1], reverse=True)
temp_ls = []
for tuple in sorted_rvol_ls_neg:
if tuple[1] > self.rvol_threshold: # A stock has to trade at least 30% of average daily volume to be qualified as in-play stocks
temp_ls.append(tuple[0])
self.universe_neg = temp_ls[:300]
for symbol in self.universe_neg:
self.atr_dict[symbol] = AtrData(symbol, self)
atr = self.atr_dict[symbol].atr
self.Debug(f'Gap down stock {symbol} has avg daily ATR of {atr}, avg daily volume of {self.SMA(symbol, 20, Resolution.Daily, Field.Volume)} and intraday volume of {self.volume_by_symbol[symbol]} and rvol of {self.rvol_by_symbol_neg[symbol]} and gap of {self.gap_neg[symbol]} and green vol of {self.green_vol[symbol]} and red vol of {self.red_vol[symbol]} at {self.Time}')
self.universe_combined = self.universe + self.universe_neg
# Clear all dictionary to start fresh the next day
self.volume_by_symbol.clear()
self.rvol_by_symbol.clear()
self.rvol_by_symbol_neg.clear()
self.open_by_symbol.clear()
self.gap.clear()
self.gap_neg.clear()
self.vol_after_gap.clear()
self.vol_after_gap_neg.clear()
self.logged = False
self.can_trade_flag = True
def ClosePositions(self):
if self.Portfolio.Invested:
self.Liquidate()
self.Debug(f"All stocks liquidated. Daily P&L at {self.daily_PL} at {self.Time}")
openOrders = self.Transactions.GetOpenOrders()
if len(openOrders)> 0:
for x in openOrders:
self.Transactions.CancelOrder(x.Id)
self.Debug(f"Open order for {x} cancelled. Daily P&L at {self.daily_PL} at {self.Time}")
def CancelOpenOrders(self):
openOrders = self.Transactions.GetOpenOrders()
if len(openOrders)> 0:
for x in openOrders:
self.Transactions.CancelOrder(x.Id)
def DetectLevels(self):
def support(df1, l, n1, n2): #n1 n2 before and after candle l
for i in range(l-n1+1, l+1):
if(df1.low[i]>df1.low[i-1]):
return 0
for i in range(l+1,l+n2+1):
if(df1.low[i]<df1.low[i-1]):
return 0
return 1
def resistance(df1, l, n1, n2): #n1 n2 before and after candle l
for i in range(l-n1+1, l+1):
if(df1.high[i]<df1.high[i-1]):
return 0
for i in range(l+1,l+n2+1):
if(df1.high[i]>df1.high[i-1]):
return 0
return 1
self.universe = []
self.universe_neg = []
self.universe_combined = []
gap_combined = []
for symbol, gap in self.gap.items():
gap_combined.append(symbol)
self.universe.append(symbol)
self.universe_combined.append(symbol)
for symbol, gap in self.gap_neg.items():
gap_combined.append(symbol)
self.universe_neg.append(symbol)
self.universe_combined.append(symbol)
for symbol in self.universe_neg:
self.atr_dict[symbol] = AtrData(symbol, self)
for symbol in self.universe:
self.atr_dict[symbol] = AtrData(symbol, self)
atr = self.atr_dict[symbol].atr
for removed in self.consol_dict.items():
if removed is not None:
self.SubscriptionManager.RemoveConsolidator(removed[1].symbol, removed[1].TenSecConsolidator)
self.consol_dict = {}
for symbol in self.universe_combined:
self.consol_dict[symbol] = SymbolData(symbol, self)
self.consol_dict[symbol].TenSecConsolidator.DataConsolidated += self.OnDataConsolidated
self.SubscriptionManager.AddConsolidator(symbol, self.consol_dict[symbol].TenSecConsolidator)
#self.Debug(f'Reg {symbol} in consolidator at {self.Time}')
for symbol in gap_combined:
#try:
ss = {}
ss_levels = {}
rr = {}
rr_levels = {}
all_levels = {}
# previous_day = self.Time - datetime.timedelta(days=1)
# start = previous_day.replace(hour=16, minute=0, second=0)
start = self.Time.replace(hour=8, minute=30, second=0)
stop = self.Time
history = self.History(symbol, start, stop, Resolution.Minute, extendedMarketHours=True)
# history = self.History(symbol, 630 , Resolution.Minute, extendedMarketHours=True)
n1 = self.levels_lookback_PM
n2 = self.levels_lookforward_PM
l = len(history)
for row in range(n1, l-n2):
if support(history,row,n1,n2):
ss[history.index[row-1][1]] = history.low[row]
rounded_level = round(history.low[row],0)
if rounded_level not in list(ss_levels.keys()):
try:
ss_levels[rounded_level] = {history.low[row]: history.volume[row]}
except:
self.Debug(f"volume not available for {symbol} at {self.Time}")
ss_levels[rounded_level] = {history.low[row]: 0}
else:
previous_walvl = list(ss_levels[rounded_level].keys())[0]
previous_vol = list(ss_levels[rounded_level].values())[0]
new_lvl_input = history.low[row]
try:
new_lvl_vol = history.volume[row]
except:
new_lvl_vol = 0
updated_walvl = (previous_walvl * previous_vol + new_lvl_input * new_lvl_vol) / (previous_vol + new_lvl_vol)
updated_vol = previous_vol + new_lvl_vol
ss_levels[rounded_level] = {updated_walvl: updated_vol}
if rounded_level not in list(all_levels.keys()):
try:
all_levels[rounded_level] = {history.low[row]: history.volume[row]}
except:
self.Debug(f"volume not available for {symbol} at {self.Time}")
all_levels[rounded_level] = {history.low[row]: 0}
else:
previous_walvl = list(all_levels[rounded_level].keys())[0]
previous_vol = list(all_levels[rounded_level].values())[0]
new_lvl_input = history.low[row]
try:
new_lvl_vol = history.volume[row]
except:
new_lvl_vol = 0
updated_walvl = (previous_walvl * previous_vol + new_lvl_input * new_lvl_vol) / (previous_vol + new_lvl_vol)
updated_vol = previous_vol + new_lvl_vol
all_levels[rounded_level] = {updated_walvl: updated_vol}
if resistance(history,row,n1,n2):
rr[history.index[row-1][1]] = history.high[row]
rounded_level = round(history.high[row],0)
if rounded_level not in list(rr_levels.keys()):
try:
rr_levels[rounded_level] = {history.high[row]: history.volume[row]}
except:
self.Debug(f"volume not available for {symbol} at {self.Time}")
rr_levels[rounded_level] = {history.high[row]: 0}
else:
previous_walvl = list(rr_levels[rounded_level].keys())[0]
previous_vol = list(rr_levels[rounded_level].values())[0]
new_lvl_input = history.high[row]
try:
new_lvl_vol = history.volume[row]
except:
new_lvl_vol = 0
updated_walvl = (previous_walvl * previous_vol + new_lvl_input * new_lvl_vol) / (previous_vol + new_lvl_vol)
updated_vol = previous_vol + new_lvl_vol
rr_levels[rounded_level] = {updated_walvl: updated_vol}
if rounded_level not in list(all_levels.keys()):
try:
all_levels[rounded_level] = {history.high[row]: history.volume[row]}
except:
all_levels[rounded_level] = {history.high[row]: 0}
else:
previous_walvl = list(all_levels[rounded_level].keys())[0]
previous_vol = list(all_levels[rounded_level].values())[0]
new_lvl_input = history.high[row]
try:
new_lvl_vol = history.volume[row]
except:
new_lvl_vol = 0
updated_walvl = (previous_walvl * previous_vol + new_lvl_input * new_lvl_vol) / (previous_vol + new_lvl_vol)
updated_vol = previous_vol + new_lvl_vol
all_levels[rounded_level] = {updated_walvl: updated_vol}
self.support[symbol] = ss
self.resistance[symbol] = rr
self.support_levels[symbol] = ss_levels
self.resistance_levels[symbol] = rr_levels
self.all_levels[symbol] = all_levels
self.Debug(f"support and resistance levels ready for {symbol} at {self.Time}")
# Clear all dictionary to start fresh the next day
self.volume_by_symbol.clear()
self.rvol_by_symbol.clear()
self.rvol_by_symbol_neg.clear()
self.open_by_symbol.clear()
self.gap.clear()
self.gap_neg.clear()
self.vol_after_gap.clear()
self.vol_after_gap_neg.clear()
self.logged = False
self.can_trade_flag = True
#except:
# self.Debug(f"Can't detect levels for {symbol}")
# DATA STRCUTRE TO STORE VWAP AND ATR
class SymbolData:
def __init__(self,symbol,algo):
self.algo = algo
self.symbol = symbol
self.vwap = algo.VWAP(self.symbol)
self.TenSecConsolidator = TradeBarConsolidator(timedelta(seconds=10))
def Update(self,bar):
self.vwap.Update(bar)
class SMAData:
def __init__(self,symbol,algo):
self.algo = algo
self.symbol = symbol
self.sma5 = SimpleMovingAverage(3)
self.sma10 = SimpleMovingAverage(5)
def Update(self,time,close):
self.sma5.Update(time,close)
self.sma10.Update(time,close)
class AtrData:
def __init__(self,symbol,algo):
self.algo = algo
self.symbol = symbol
self.atr = AverageTrueRange(self.symbol, 20)
history = algo.History(symbol,20,Resolution.Daily)
for bar in history.itertuples():
tradebar = TradeBar(bar.Index[1], self.symbol, bar.open, bar.high, bar.low, bar.close, bar.volume)
self.atr.Update(tradebar)
def Update(self,time,close):
history = algo.History(symbol,1,Resolution.Daily)
for bar in history.itertuples():
tradebar = TradeBar(bar.Index[1], self.symbol, bar.open, bar.high, bar.low, bar.close, bar.volume)
self.atr.Update(tradebar)
def support(df1, l, n1, n2): #n1 n2 before and after candle l
for i in range(l-n1+1, l+1):
if(df1.low[i]>df1.low[i-1]):
return 0
for i in range(l+1,l+n2+1):
if(df1.low[i]<df1.low[i-1]):
return 0
return 1
def resistance(df1, l, n1, n2): #n1 n2 before and after candle l
for i in range(l-n1+1, l+1):
if(df1.high[i]<df1.high[i-1]):
return 0
for i in range(l+1,l+n2+1):
if(df1.high[i]>df1.high[i-1]):
return 0
return 1