Overall Statistics |
Total Trades 16 Average Win 0.29% Average Loss -0.31% Compounding Annual Return -36.276% Drawdown 1.700% Expectancy -0.268 Net Profit -0.661% Sharpe Ratio -4.779 Sortino Ratio 0 Probabilistic Sharpe Ratio 8.870% Loss Rate 62% Win Rate 38% Profit-Loss Ratio 0.95 Alpha -0.393 Beta -0.369 Annual Standard Deviation 0.06 Annual Variance 0.004 Information Ratio 0.027 Tracking Error 0.168 Treynor Ratio 0.775 Total Fees $32.74 Estimated Strategy Capacity $2300000.00 Lowest Capacity Asset LTHM WYLMG5UVFEXX Portfolio Turnover 90.40% |
#region imports from AlgorithmImports import * import pandas as pd import datetime import pickle #endregion class ParticleTransdimensionalAutosequencers(QCAlgorithm): def Initialize(self): # Original variables # BASIC SETTINGS self.SetStartDate(2021, 1, 1) self.SetEndDate(2021, 1, 5) #self.SetEndDate(2023, 3, 21) self.SetCash(30000) self.cash = 30000 self.AddEquity('SPY', Resolution.Second, extendedMarketHours=True) self.market = 'SPY' self.SetUniverseSelection(ScheduledUniverseSelectionModel( self.DateRules.EveryDay(self.market), self.TimeRules.At(9, 0), self.SelectSymbols # selection function in algorithm. )) #self.AddUniverseSelection(CoarseFundamentalUniverseSelectionModel(self.CoarseSelectionFunction)) self.UniverseSettings.Resolution = Resolution.Second algorithm = self self.weight = 1 self.symbols = [] self.symbols_obj = [] self.symbolData = {} self.consol_dict = {} self.gap ={} self.gap_neg = {} self.gap_combined = {} self.pm_range = {} self.TrackOpen_hour = 9 self.TrackOpen_min = 31 self.levels_lookback_PM = 1 self.levels_lookforward_PM = 1 self.support = {} self.support_levels = {} self.resistance = {} self.resistance_levels = {} self.all_levels = {} self.rvol_PM = {} self.can_trade_flag = False self.long_trade_flag = False self.short_trade_flag = False self.min_price = 10 self.RRR = 1.0 self.min_spread = 0.1 self.min_profit = 100 self.risk_per_trade = 100 self.entry_orders = {} self.take_profit = {} self.stop_loss = {} self.SL = {} self.SL_id = {} self.LO = {} self.LO_id = {} self.TP = {} self.trades = {} self.entry_prices = {} self.theoretical_entry = {} self.daily_PL = 0 self.highest_low = {} self.lowest_high = {} self.reached_tp = {} self.winning_trades = 0 self.winning_PL = 0 self.losing_trades = 0 self.losing_PL = 0 self.min_actual_RRR = 1.0 self.no_of_trades = 5 self.daily_stop = 0.02 # for dataframe creation self.df_store = pd.DataFrame([],columns = ['time','symbol', 'gap_perc','pm_rvol','pm-low','pm_s1','pm_s2', 'pm_s3','pm_s1_rvol','pm_s2_rvol','pm_s3_rvol','pm-high','pm_r1','pm_r2','pm_r3','pm_r1_rvol','pm_r2_rvol','pm_r3_rvol']) self.df_store_day = pd.DataFrame([],columns = ['time','symbol', 'gap_perc','pm_rvol','pm-low','pm_s1','pm_s2', 'pm_s3','pm_s1_rvol','pm_s2_rvol','pm_s3_rvol','pm-high','pm_r1','pm_r2','pm_r3','pm_r1_rvol','pm_r2_rvol','pm_r3_rvol']) self.df_BS = pd.DataFrame([],columns = ['time','symbol','close', 'high', 'low','open', 'volume', 'symbol_str','gap_perc','pm_rvol','pm-low','pm_s1','pm_s2', 'pm_s3','pm_s1_rvol','pm_s2_rvol','pm_s3_rvol','pm-high','pm_r1','pm_r2','pm_r3','pm_r1_rvol','pm_r2_rvol','pm_r3_rvol','buy_sell','limit_order','bt_stop_loss','bt_take_profit','actual_entry_time','actual_entry_price','actual_exit_time','actual_exit_price','actual_PL']) self.Schedule.On(self.DateRules.EveryDay(self.market), self.TimeRules.At(8, 59), self.Clear_Consol) self.Schedule.On(self.DateRules.EveryDay(self.market), self.TimeRules.At(9, 1), self.Add_Consol) self.Schedule.On(self.DateRules.EveryDay(self.market), self.TimeRules.At(9, 31, 0), self.AtOpen) self.Schedule.On(self.DateRules.EveryDay(self.market), self.TimeRules.At(9, 45), self.ClosePositions) self.day_count = 0 self.project_key = "16565696/PM_data_2021-1-1_252 days out" # UNIVERSE SELECTION (09:30) # Create selection function which returns symbol objects. def SelectSymbols(self, dateTime): self.Debug(f"Enter Scheduled universe at {self.Time}. Universe update.") self.symbols = [] self.symbols_obj = [] self.volume_by_symbol = {} self.daily_PL = 0 adjusted_time = self.Time.replace(hour=9, minute=31, second=0) dict_of_symbols = symbol_extractor(self.project_key, self) tickers = dict_of_symbols[adjusted_time] tickers_symbol = [Symbol.Create(ticker, SecurityType.Equity, Market.USA) for ticker in tickers] self.symbols_obj = tickers_symbol self.volume_by_symbol = {c: 0 for c in tickers_symbol} self.highest_low = {c: 0 for c in tickers_symbol} self.lowest_high = {c: 10000000 for c in tickers_symbol} self.symbols = list(self.volume_by_symbol.keys()) self.Debug(f"Universe size before volume filter: {len(self.volume_by_symbol)} at {self.Time}") self.no_of_trades = max(len(self.volume_by_symbol)/10, 3) return list(self.volume_by_symbol.keys()) def OnDataConsolidated(self, sender, bar): # self.Debug(f'Information for {sender.Consolidated.Symbol} - Open: {bar.Open}, Close: {bar.Close}, Period: {bar.Period} at {self.Time}') symbol = sender.Consolidated.Symbol if sender.Consolidated.Symbol not in list(self.df_store_day.index): #start_time = self.Time.replace(hour=9, minute=31, second=0) #if self.Time >= start_time: # self.Debug(f"At {self.Time},There is no subscription of {sender.Consolidated.Symbol} after gap filter though it was in the universe filter") pass else: # Take profit or loss after stop time is reached stop_time = self.Time.replace(hour=9, minute=45, second=0) if self.Time > stop_time: return # Track if stocks are tradable at that time range start_time = self.Time.replace(hour=9, minute=31, second=0) end_time = self.Time.replace(hour=9, minute=33, second=0) if self.Time >= start_time and self.Time <= end_time : 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 if self.can_trade_flag == True: # reset trade flags before running through the trade conditions self.long_trade_flag = False self.short_trade_flag = False # CHECK FOR ENTRY CONDITIONS # Basic buy and sell condtions to meet pm_h = self.df_store_day.loc[symbol,'pm-high'] pm_l = self.df_store_day.loc[symbol,'pm-low'] up_cross = bar.High > pm_h and bar.Low < pm_h down_cross = bar.Low < pm_l and bar.High > pm_l green_bar = bar.Close > bar.Open red_bar = bar.Close < bar.Open long_condition = up_cross and green_bar short_condition = down_cross and red_bar min_price_condition = bar.Low > self.min_price # Further risk reward conditions to meet if long_condition and min_price_condition: #self.Debug(f'{symbol} met the long condition at {self.Time}') # When stocks met long criteria and min_price_condition, check if risk reward makes sense # Metrics to calculate take profit and stop loss try: start_time = self.Time.replace(hour=9, minute=30, second=0) actual_BS_time = self.Time pre_trade_history = Consolidator_2(self, symbol.Value, start_time, actual_BS_time, base_freq = Resolution.Second, consol_freq = 10) low_since_open = np.min(pre_trade_history['low']) high_since_open = np.max(pre_trade_history['high']) # Calculate atr as metric for take profit and stop loss Sym = self.AddEquity(symbol.Value) #atr_sym = self.ATR(self.symbol, 50, MovingAverageType.Simple) #atr_history = self.Indicator(atr, Sym.Symbol, actual_BS_time - timedelta(days=50), actual_BS_time, Resolution.Daily) atr = AverageTrueRange(20) history = self.History(Sym.Symbol,50,Resolution.Daily) for bar_1 in history.itertuples(): tradebar = TradeBar(bar_1.Index[1], Sym.Symbol, bar_1.open, bar_1.high, bar_1.low, bar_1.close, bar_1.volume) atr.Update(tradebar) atr_sym = atr.Current.Value range_at_open = atr_sym * 0.3 # Calculate stop-loss & risk theoretical_trade_price = pre_trade_history.iloc[-1].high self.theoretical_entry[(symbol,self.Time)] = theoretical_trade_price stop_loss_low = low_since_open stop_loss_atr = theoretical_trade_price - range_at_open * 0.5 stop_loss = max(stop_loss_low, stop_loss_atr) risk = theoretical_trade_price - stop_loss # Calculate take-profit take_profit_RRR = theoretical_trade_price + self.RRR * risk take_profit_atr = theoretical_trade_price + range_at_open * 0.5 take_profit = max(take_profit_RRR, take_profit_atr) # Calculate pre-trade volume pre_trade_volume = np.mean(pre_trade_history.volume) # Calculate spread spread = abs(take_profit - theoretical_trade_price) min_trade_volume = self.min_profit / (spread * bar.Low) if spread >= self.min_spread and pre_trade_volume * 0.25 > min_trade_volume: self.long_trade_flag = True self.Debug(f'{symbol} met the long trade condition at {self.Time}') else: self.long_trade_flag = False self.Debug(f'{symbol} met the long & min_price conditions but not long trade condtion at {self.Time}') except: self.Debug(f'Something went wrong with {symbol} at {self.Time}. Cannot check long trade RRR conditions') elif short_condition and min_price_condition: #self.Debug(f'{symbol} met the short condition at {self.Time}') try: start_time = self.Time.replace(hour=9, minute=30, second=0) actual_BS_time = self.Time pre_trade_history = Consolidator_2(self, symbol.Value, start_time, actual_BS_time, base_freq = Resolution.Second, consol_freq = 10) low_since_open = np.min(pre_trade_history['low']) high_since_open = np.max(pre_trade_history['high']) # Calculate atr as metric for take profit and stop loss Sym = self.AddEquity(symbol.Value) atr = AverageTrueRange(20) #atr_sym = self.ATR(self.symbol, 50, MovingAverageType.Simple) #atr_history = self.Indicator(atr, Sym.Symbol, actual_BS_time - timedelta(days=50), actual_BS_time, Resolution.Daily) #atr_sym = atr_history.iloc[-1]['averagetruerange'] history = self.History(Sym.Symbol,50,Resolution.Daily) for bar_1 in history.itertuples(): tradebar = TradeBar(bar_1.Index[1], Sym.Symbol, bar_1.open, bar_1.high, bar_1.low, bar_1.close, bar_1.volume) atr.Update(tradebar) atr_sym = atr.Current.Value range_at_open = atr_sym * 0.3 # Calculate stop-loss & risk theoretical_trade_price = pre_trade_history.iloc[-1].low self.theoretical_entry[(symbol,self.Time)] = theoretical_trade_price stop_loss_high = high_since_open stop_loss_atr = theoretical_trade_price + range_at_open * 0.5 stop_loss = min(stop_loss_high, stop_loss_atr) risk = stop_loss - theoretical_trade_price # Calculate take-profit take_profit_RRR = theoretical_trade_price - self.RRR * risk take_profit_atr = theoretical_trade_price - range_at_open * 0.5 take_profit = min(take_profit_RRR, take_profit_atr) # Calculate pre-trade volume pre_trade_volume = np.mean(pre_trade_history.volume) # Calculate spread spread = abs(take_profit - theoretical_trade_price) min_trade_volume = self.min_profit / (spread * bar.Low) if spread >= self.min_spread and pre_trade_volume * 0.25 > min_trade_volume: self.short_trade_flag = True #self.Debug(f'{symbol} met the trade condition at {self.Time}') self.Debug(f'{symbol} met the short trade condition at {self.Time}') else: self.short_trade_flag = False self.Debug(f'{symbol} met the short & min_price conditions but not short trade condtion at {self.Time}') except: self.Debug(f'Something went wrong with {symbol} at {self.Time}. Cannot check short trade RRR conditions') # If the instance meets all criteria, build the feature dataframe, run it through the ML model, if the ML model returns positive, trade, if not, don't trade if (self.long_trade_flag == True) or (self.short_trade_flag == True): feature_df_long = pd.DataFrame([],columns = ['gap_pct', 'pm_rvol', 'pm_range', 'time_of_trade', 'bid_ask_latest', 'max_vol_pre_trade', 'latest_vol_pre_trade', 'volume_pre_trade', 'volume_pre_trade_pct', 'volume_trend', 'open_above_r3', 'open_above_pm_high', 'pct_green_vol', 'volume_intensity', 'pct_volume_above_pm_high', 'dist_r3_high']) feature_df_short = pd.DataFrame([],columns = ['gap_pct', 'pm_rvol', 'pm_range', 'time_of_trade', 'bid_ask_latest','max_vol_pre_trade', 'latest_vol_pre_trade', 'volume_pre_trade', 'volume_pre_trade_pct', 'volume_trend', 'open_below_s3', 'pct_green_vol', 'volume_intensity', 'pct_volume_below_pm_low', 'dist_s3_low']) # Gap Percentage gap_pct = self.gap_combined[symbol] # Relative volume in PM (PM volume as percentage of average daily volume in the past 20 days) pm_rvol = self.rvol_PM[symbol] # Relative Volitilty in PM (PM range as percentage of ATR) pm_range = self.pm_range[symbol] pm_range_as_pct = pm_range / atr_sym # time of trade. How many bars has gone by before meeting the trade criteria BS_time = bar.Time BS_min = BS_time.minute BS_sec = BS_time.second no_of_bar = (BS_min - 30) * 6 + BS_sec / 10 # latest bid ask spread before having a trade ask_close = pre_trade_history['askclose'] bid_close = pre_trade_history['bidclose'] bid_ask_spread = ask_close - bid_close latest_bid_ask = bid_ask_spread.iloc[-1] # Maximum volatility and latest volatility pre trade volatility = pre_trade_history['high'] - pre_trade_history['low'] latest_vol = volatility.iloc[-1] / atr_sym max_vol = max(volatility) / atr_sym # volume after open before trade as % of avg daily volume start_d = BS_time - timedelta(days=20) stop_d = BS_time history_d = self.History(Sym.Symbol, start_d, stop_d, Resolution.Daily, extendedMarketHours=False) symbol_sma = np.mean(history_d['volume'].values) volume = pre_trade_history['volume'] pre_trade_vol = sum(volume) pre_trade_vol_as_pct = pre_trade_vol / symbol_sma # volume trend after open before trade volume_trend = find_slope(volume) # stock open above r3 and below pm-high stock_open = pre_trade_history['open'][0] pm_high = self.df_store_day.loc[symbol,'pm-high'] pm_low = self.df_store_day.loc[symbol,'pm-low'] pm_r3 = max(self.df_store_day.loc[symbol,'pm_r1'], self.df_store_day.loc[symbol,'pm_r2'], self.df_store_day.loc[symbol,'pm_r3']) if (stock_open > pm_r3) and (stock_open < pm_high): above_r3 = 1 else: above_r3 = 0 # stock open above pm-high if stock_open > pm_high: above_pm_high = 1 else: above_pm_high = 0 # stock open below pm-low if stock_open < pm_low: below_pm_low = 1 else: below_pm_low = 0 # stock open below s3 and above pm-low pm_support = [self.df_store_day.loc[symbol,'pm_s1'], self.df_store_day.loc[symbol,'pm_s2'], self.df_store_day.loc[symbol,'pm_s3']] pm_suppport_filtered = [x for x in pm_support if x !=0] try: pm_s3 = min(pm_suppport_filtered) if (stock_open < pm_s3) and (stock_open > pm_low): below_s3 = 1 else: below_s3 = 0 except: below_s3 = 0 # total green volume vs red volume condition_g = (pre_trade_history['close'] > pre_trade_history['open']) total_green_volumes = pre_trade_history.loc[condition_g, 'volume'].sum() condition_r = (pre_trade_history['close'] < pre_trade_history['open']) total_red_volumes = pre_trade_history.loc[condition_r, 'volume'].sum() pct_green_vol = total_green_volumes / (total_green_volumes + total_red_volumes) # The most recent volume bar as % of all volume since open most_recent_two_bar_volume = pre_trade_history.volume.iloc[-1] + pre_trade_history.volume.iloc[-2] total_vol_since_open = pre_trade_history.volume.sum() pct_recent_bars_volume = most_recent_two_bar_volume / total_vol_since_open # % of volume below PM-low (for sell) and above PM-high (for buy) before trade condition_pm_high = (pre_trade_history['close'] > self.df_store_day.loc[symbol,'pm-high']) total_pm_high_volumes = pre_trade_history.loc[condition_pm_high, 'volume'].sum() pct_pm_high_vol = total_pm_high_volumes / total_vol_since_open condition_pm_low = (pre_trade_history['close'] < self.df_store_day.loc[symbol,'pm-low']) total_pm_low_volumes = pre_trade_history.loc[condition_pm_low, 'volume'].sum() pct_pm_low_vol = total_pm_low_volumes / total_vol_since_open # distance between r3 vs high and s3 vs low as % of atr distance_r3_high = pm_high - pm_r3 distance_s3_low = pm_s3 - pm_low pct_dist_r3_high = distance_r3_high / atr_sym pct_dist_s3_low = distance_s3_low / atr_sym # Feed into pre-trained model to see what is the prediction Long_serialized_obj = bytes(self.ObjectStore.ReadBytes("OD_Long_AB_model")) Long_model = pickle.loads(Long_serialized_obj) Short_serialized_obj = bytes(self.ObjectStore.ReadBytes("OD_Short_GB_model")) Short_model = pickle.loads(Short_serialized_obj) if self.long_trade_flag == True: # Enter that into the dataframe feature_df_long.loc[len(feature_df_long)] = [gap_pct, pm_rvol, pm_range_as_pct, no_of_bar, latest_bid_ask, max_vol, latest_vol, pre_trade_vol, pre_trade_vol_as_pct, volume_trend, above_r3, above_pm_high, pct_green_vol, pct_recent_bars_volume, pct_pm_high_vol, pct_dist_r3_high] feature_df_long_no_na = feature_df_long.dropna() if feature_df_long_no_na.empty: yhat_pred = 0 else: yhat_pred = Long_model.predict(feature_df_long) if yhat_pred > 0: self.Debug(f'yhat for {symbol} is {yhat_pred}, Buy the stock.') # Quantity risk_per_trade = self.daily_stop * self.Portfolio.TotalPortfolioValue * 1 / self.no_of_trades theoretical_quantity = risk_per_trade / risk buying_power = self.Portfolio.GetBuyingPower(symbol, OrderDirection.Buy) binding_quantity = buying_power / (bar.High * 1.02) quantity = min(theoretical_quantity, binding_quantity) # Market order self.entry_orders[(symbol,self.Time)] = (True,"Buy") self.take_profit[(symbol,self.Time)] = take_profit self.stop_loss[(symbol,self.Time)] = stop_loss if symbol in self.trades: self.trades[symbol].append((symbol,self.Time)) else: self.trades[symbol] = [(symbol,self.Time)] #self.MarketOrder(symbol, quantity) limitPrice = (take_profit + self.min_actual_RRR * stop_loss) / (1 + self.min_actual_RRR) theoretical_profit = (take_profit - limitPrice) * quantity fees = max(1, 0.005*quantity) * 2 if theoretical_profit >= fees * 10: self.LO[(symbol, self.Time)] = self.LimitOrder(symbol, quantity, limitPrice) order_id = self.LO[(symbol, self.Time)].OrderId self.LO_id[order_id] = (symbol,self.Time) self.reached_tp[(symbol,self.Time)] = False bs_tag = f"{symbol.Value}_{self.Time}" self.df_BS.loc[bs_tag] = [self.Time.strftime("%Y-%m-%d %H:%M:%S"), symbol, bar.Close, bar.High, bar.Low, bar.Open, bar.Volume, symbol.Value, self.gap_combined[symbol], self.rvol_PM[symbol], self.df_store_day.loc[symbol,'pm-low'], self.df_store_day.loc[symbol,'pm_s1'], self.df_store_day.loc[symbol,'pm_s2'], self.df_store_day.loc[symbol,'pm_s3'], self.df_store_day.loc[symbol,'pm_s1_rvol'], self.df_store_day.loc[symbol,'pm_s2_rvol'], self.df_store_day.loc[symbol,'pm_s3_rvol'], self.df_store_day.loc[symbol,'pm-high'], self.df_store_day.loc[symbol,'pm_r1'], self.df_store_day.loc[symbol,'pm_r2'], self.df_store_day.loc[symbol,'pm_r3'], self.df_store_day.loc[symbol,'pm_r1_rvol'], self.df_store_day.loc[symbol,'pm_r2_rvol'], self.df_store_day.loc[symbol,'pm_r3_rvol'],"buy",limitPrice, stop_loss, take_profit, 0, 0, 0, 0, 0] else: self.Debug(f'{symbol} passes all requirements but fees doesnt make sense theoretical profit is {theoretical_profit} and fees is {fees}, Dont short the stock even if it meets all the criteria.') else: self.Debug(f'yhat for {symbol} is {yhat_pred}, Dont buy the stock even if it meets all the criteria.') elif self.short_trade_flag == True: feature_df_short.loc[len(feature_df_short)] = [gap_pct, pm_rvol, pm_range_as_pct, no_of_bar, latest_bid_ask, max_vol, latest_vol, pre_trade_vol, pre_trade_vol_as_pct, volume_trend, below_s3, pct_green_vol, pct_recent_bars_volume, pct_pm_low_vol, pct_dist_s3_low] feature_df_short_no_na = feature_df_short.dropna() if feature_df_short_no_na.empty: yhat_pred = 0 else: yhat_pred = Short_model.predict(feature_df_short) if yhat_pred > 0: self.Debug(f'yhat for {symbol} is {yhat_pred}, Short the stock.') # Quantity risk_per_trade = self.daily_stop * self.Portfolio.TotalPortfolioValue * 1 / self.no_of_trades theoretical_quantity = risk_per_trade / risk buying_power = self.Portfolio.GetBuyingPower(symbol, OrderDirection.Sell) binding_quantity = buying_power / (bar.Low * 1.02) quantity = min(theoretical_quantity, binding_quantity) # Market order self.entry_orders[(symbol,self.Time)] = (True, "Sell") self.take_profit[(symbol,self.Time)] = take_profit self.stop_loss[(symbol,self.Time)] = stop_loss if symbol in self.trades: self.trades[symbol].append((symbol,self.Time)) else: self.trades[symbol] = [(symbol,self.Time)] #self.MarketOrder(symbol, -quantity) limitPrice = (take_profit + self.min_actual_RRR * stop_loss) / (1 + self.min_actual_RRR) theoretical_profit = (limitPrice - take_profit) * quantity fees = max(1, 0.005*quantity) * 2 if theoretical_profit >= fees * 10: self.LO[(symbol, self.Time)] = self.LimitOrder(symbol, -quantity, limitPrice) order_id = self.LO[(symbol, self.Time)].OrderId self.LO_id[order_id] = (symbol,self.Time) self.reached_tp[(symbol,self.Time)] = False bs_tag = f"{symbol.Value}_{self.Time}" self.df_BS.loc[bs_tag] = [self.Time.strftime("%Y-%m-%d %H:%M:%S"), symbol, bar.Close, bar.High, bar.Low, bar.Open, bar.Volume, symbol.Value, self.gap_combined[symbol], self.rvol_PM[symbol], self.df_store_day.loc[symbol,'pm-low'], self.df_store_day.loc[symbol,'pm_s1'], self.df_store_day.loc[symbol,'pm_s2'], self.df_store_day.loc[symbol,'pm_s3'], self.df_store_day.loc[symbol,'pm_s1_rvol'], self.df_store_day.loc[symbol,'pm_s2_rvol'], self.df_store_day.loc[symbol,'pm_s3_rvol'], self.df_store_day.loc[symbol,'pm-high'], self.df_store_day.loc[symbol,'pm_r1'], self.df_store_day.loc[symbol,'pm_r2'], self.df_store_day.loc[symbol,'pm_r3'], self.df_store_day.loc[symbol,'pm_r1_rvol'], self.df_store_day.loc[symbol,'pm_r2_rvol'], self.df_store_day.loc[symbol,'pm_r3_rvol'],"sell",limitPrice, stop_loss, take_profit, 0, 0, 0, 0, 0] else: self.Debug(f'{symbol} passes all requirements but fees doesnt make sense theoretical profit is {theoretical_profit} and fees is {fees}, Dont short the stock even if it meets all the criteria.') else: self.Debug(f'yhat for {symbol} is {yhat_pred}, Dont short the stock even if it meets all the criteria.') # MONITOR ALL OPEN TRADES BETWEEN 9:30 - 9:45 TO UPDATE ITS STOP PRICE AT THE RIGHT TIME start_time = self.Time.replace(hour=9, minute=31, second=0) liquidate_time = self.Time.replace(hour=9, minute=45, second=0) if self.Time >= start_time and self.Time <= liquidate_time: # Update highest_low and lowest_high self.highest_low[symbol] = max(bar.Low, self.highest_low[symbol]) self.lowest_high[symbol] = min(bar.High, self.lowest_high[symbol]) ls_of_trades = list(self.SL.keys()) ls_of_relevant_trades = [] for trade in ls_of_trades: if symbol == trade[0]: ls_of_relevant_trades.append(trade) for trade in ls_of_relevant_trades: buy_sell = self.entry_orders[trade][1] if buy_sell == "Buy": TP = self.take_profit[trade] latest_high = bar.High highest_low = self.highest_low[symbol] if latest_high >= TP and self.reached_tp[trade] == False: self.reached_tp[trade] = True self.Debug(f"{symbol} reached target price at {self.Time}") if self.reached_tp[trade] == True: revised_stop = max(TP, highest_low) self.Debug(f"checking if there is a SL trade for {symbol} at {self.Time}") ticket = self.SL[trade] update_settings = UpdateOrderFields() update_settings.StopPrice = revised_stop response = ticket.Update(update_settings) self.Debug(f"Stop Loss update attempted for {symbol} long trade {trade}") if response.IsSuccess: self.Debug(f"Order updated successfully. {symbol} long trade {trade} stop loss updated to {revised_stop}") elif buy_sell == "Sell": TP = self.take_profit[trade] latest_low = bar.Low lowest_high = self.lowest_high[symbol] if latest_low <= TP and self.reached_tp[trade] == False: self.reached_tp[trade] = True self.Debug(f"{symbol} reached target price at {self.Time}") if self.reached_tp[trade] == True: revised_stop = min(TP, lowest_high) ticket = self.SL[trade] update_settings = UpdateOrderFields() update_settings.StopPrice = revised_stop response = ticket.Update(update_settings) self.Debug(f"Stop Loss update attempted for {symbol} short trade {trade}") if response.IsSuccess: self.Debug(f"Order updated successfully. {symbol} short trade {trade} stop loss updated to {revised_stop}") def OnOrderEvent(self, orderEvent): if orderEvent.Status == OrderStatus.Filled: if orderEvent.OrderId in list(self.LO_id.keys()): trade = self.LO_id[orderEvent.OrderId] stop_loss = self.stop_loss[trade] take_profit = self.take_profit[trade] # For buy orders if orderEvent.FillQuantity > 0: profit_potential = take_profit - orderEvent.FillPrice risk = orderEvent.FillPrice - stop_loss implied_RRR = profit_potential / risk self.Debug(f"BUY {orderEvent.Symbol} at {self.Time} with market entry at {orderEvent.FillPrice} vs theoretical entry of {self.theoretical_entry[trade]}, stop loss at {stop_loss}, RRR at {implied_RRR}, risk at {risk}") # For sell orders elif orderEvent.FillQuantity < 0: profit_potential = orderEvent.FillPrice - take_profit risk = stop_loss - orderEvent.FillPrice implied_RRR = profit_potential / risk self.Debug(f"SELL {orderEvent.Symbol} at {self.Time} with market entry at {orderEvent.FillPrice} vs theoretical entry of {self.theoretical_entry[trade]}, stop loss at {stop_loss}, RRR at {implied_RRR}, risk at {risk}") self.SL[trade] = self.StopMarketOrder(orderEvent.Symbol, -orderEvent.FillQuantity, stop_loss) self.SL_id[trade] = self.SL[trade].OrderId self.entry_prices[trade] = orderEvent.FillPrice del self.LO[trade] del self.LO_id[orderEvent.OrderId] symbol_str = trade[0].Value order_time = trade[1] bs_tag = f"{symbol_str}_{order_time}" self.df_BS.loc[bs_tag,'actual_entry_time'] = self.Time.strftime("%Y-%m-%d %H:%M:%S") self.df_BS.loc[bs_tag,'actual_entry_price'] = orderEvent.FillPrice else: list_of_ids = [key for key, val in self.SL_id.items() if val == orderEvent.OrderId] if len(list_of_ids) == 0: self.Debug(f"Liquidated {orderEvent.Symbol} at {self.Time}") exit_price = orderEvent.FillPrice ls_of_out_trades = list(self.SL.keys()) ls_of_outstanding_trades = [] for trade in ls_of_out_trades: if orderEvent.Symbol == trade[0]: ls_of_outstanding_trades.append(trade) #ls_of_outstanding_trades = self.trades[orderEvent.Symbol] for trade in ls_of_outstanding_trades: entry_price = self.entry_prices[trade] PL = orderEvent.FillQuantity * (entry_price - orderEvent.FillPrice) if PL >= 0: self.Debug(f"Take Profit at {orderEvent.FillPrice} and won {PL} for {orderEvent.Symbol} {trade}.") self.winning_PL += PL self.winning_trades += 1 elif PL < 0: self.Debug(f"Take Loss at {orderEvent.FillPrice} and lost {PL} for {orderEvent.Symbol} {trade}.") self.losing_PL += PL self.losing_trades += 1 self.daily_PL += PL self.Debug(f"Daily P&L is now {self.daily_PL} at {self.Time}") symbol_str = trade[0].Value order_time = trade[1] bs_tag = f"{symbol_str}_{order_time}" self.df_BS.loc[bs_tag,'actual_exit_time'] = self.Time.strftime("%Y-%m-%d %H:%M:%S") self.df_BS.loc[bs_tag,'actual_exit_price'] = orderEvent.FillPrice self.df_BS.loc[bs_tag,'actual_PL'] = PL else: trade_asso_with_SL = list_of_ids[0] entry_price = self.entry_prices[trade_asso_with_SL] PL = orderEvent.FillQuantity * (entry_price - orderEvent.FillPrice) if PL >= 0: self.Debug(f"Take Profit at {orderEvent.FillPrice} and won {PL} for {orderEvent.Symbol} {trade_asso_with_SL}.") self.winning_PL += PL self.winning_trades += 1 elif PL < 0: self.Debug(f"Take Loss at {orderEvent.FillPrice} and lost {PL} for {orderEvent.Symbol} {trade_asso_with_SL}.") self.losing_PL += PL self.losing_trades += 1 self.daily_PL += PL self.Debug(f"Daily P&L is now {self.daily_PL} at {self.Time}") symbol_str = trade_asso_with_SL[0].Value order_time = trade_asso_with_SL[1] bs_tag = f"{symbol_str}_{order_time}" self.df_BS.loc[bs_tag,'actual_exit_time'] = self.Time.strftime("%Y-%m-%d %H:%M:%S") self.df_BS.loc[bs_tag,'actual_exit_price'] = orderEvent.FillPrice self.df_BS.loc[bs_tag,'actual_PL'] = PL self.trades[orderEvent.Symbol].remove(trade_asso_with_SL) if len(self.trades[orderEvent.Symbol]) == 0: self.trades.pop(orderEvent.Symbol) del self.SL[trade_asso_with_SL] def Clear_Consol(self): self.Debug(f"Clear consol dict for previous day at {self.Time} before universe update") for removed in self.consol_dict.items(): if removed is not None: self.SubscriptionManager.RemoveConsolidator(removed[1].symbol, removed[1].TenSecConsolidator) previous_day_count = self.day_count - 1 previous_key = f"{self.ProjectId}/BT_data_2021-1-1_{previous_day_count} days out" if self.ObjectStore.ContainsKey(previous_key): self.ObjectStore.Delete(previous_key) self.Debug(f"{self.ProjectId}/BT_data_2021-1-1_{previous_day_count} days out deleted at {self.Time}") else: self.Debug(f"{self.ProjectId}/BT_data_2021-1-1_{previous_day_count} not found at {self.Time}") def Add_Consol(self): self.Debug(f"Add to consol dict for current day at {self.Time} after universe update") for symbol in self.symbols: self.AddEquity(symbol.Value, Resolution.Second, extendedMarketHours=True) self.consol_dict[symbol] = SymbolData(symbol, self) self.consol_dict[symbol].TenSecConsolidator.DataConsolidated += self.OnDataConsolidated self.SubscriptionManager.AddConsolidator(symbol, self.consol_dict[symbol].TenSecConsolidator) def AtOpen(self): self.Debug(f'Going into AtOpen at {self.Time}, active universe has {len(self.ActiveSecurities)} number of symbols') self.Debug(f'Going into AtOpen at {self.Time}, self.symbols has {len(self.symbols)} number of symbols') # Track gap% open_by_symbol = {} gap_thres = 0.03 # Empty dictionaries / dataframe from previous day self.gap.clear() self.gap_neg.clear() self.gap_combined.clear() self.rvol_PM.clear() self.support.clear() self.support_levels.clear() self.resistance.clear() self.resistance_levels.clear() self.all_levels.clear() self.pm_range.clear() self.Debug(f"dictionaries cleared before a new trading day") for symbol in self.symbols: #start = self.Time.replace(hour=9, minute=30, second=0) #stop = self.Time #historyDataMin = self.History(symbol, start, stop, Resolution.Second, extendedMarketHours=True) historyDataMin = self.History(symbol,1,Resolution.Minute) #historyDataMin = self.History(symbol,1,Resolution.Second) try: open_price_sym = historyDataMin['open'][-1] #open_price_sym = historyDataMin['open'][0] #self.Debug(f"Opening price for {symbol.Value} is {open_price_sym}") except: self.Debug(f"Opening price data for current day unavailable for {symbol.Value}") open_price_sym = 0 open_by_symbol[symbol] = open_price_sym historyData = self.History(symbol,2,Resolution.Daily) try: closeDayBefore = historyData['close'][-1] #self.Debug(f"Close price the day before for {symbol.Value} is {closeDayBefore}") except: self.Debug(f"History data unavailable for {symbol.Value}") continue priceGap = open_by_symbol[symbol] - closeDayBefore percentGap = priceGap / closeDayBefore #self.Debug(f"Percent gap for {symbol.Value} is {percentGap}") if (percentGap > gap_thres): self.gap[symbol] = percentGap if (percentGap < -gap_thres): self.gap_neg[symbol] = percentGap self.gap_combined = {**self.gap , **self.gap_neg} self.Debug(f'Universe size after gap positive filter: {len(self.gap)} and negative filter: {len(self.gap_neg)} at {self.Time}') # Track PM Rvol for symbol, gap in self.gap_combined.items(): previous_day = self.Time - datetime.timedelta(days=1) start = previous_day.replace(hour=16, minute=0, second=0) stop = self.Time.replace(hour=9, minute=30, second=0) history = self.History(symbol, start, stop, Resolution.Minute, extendedMarketHours=True) try: PM_vol_total = np.nansum(history['volume'].values) except: PM_vol_total = 0 previous_20_day = self.Time - datetime.timedelta(days=20) start_d = previous_20_day.replace(hour=9, minute=30, second=0) stop_d = self.Time history_d = self.History(symbol, start_d, stop_d, Resolution.Daily, extendedMarketHours=False) symbol_sma = np.mean(history_d['volume'].values) self.rvol_PM[symbol] = PM_vol_total / symbol_sma #self.Debug(f'{symbol} has PM rvol of {self.rvol_PM[symbol]}') # Track PM Levels for symbol, gap in self.gap_combined.items(): #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.replace(hour=9, minute=30, second=0) history = self.History(symbol, start, stop, Resolution.Minute, extendedMarketHours=True) # history = self.History(symbol, 630 , Resolution.Minute, extendedMarketHours=True) try: pm_high = history['high'].max() except: pm_high = 100000 try: pm_low = history['low'].min() except: pm_low = 0 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 if (previous_vol + new_lvl_vol) == 0: updated_walvl = (previous_walvl + new_lvl_input) / 2 else: 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 if (previous_vol + new_lvl_vol) == 0: updated_walvl = (previous_walvl + new_lvl_input) / 2 else: 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 if (previous_vol + new_lvl_vol) == 0: updated_walvl = (previous_walvl + new_lvl_input) / 2 else: 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 if (previous_vol + new_lvl_vol) == 0: updated_walvl = (previous_walvl + new_lvl_input) / 2 else: 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 # Support levels S1-S3 and their rvol try: PM_vol_total_2 = np.nansum(history['volume'].values) except: PM_vol_total_2 = 0 if PM_vol_total_2 == 0: PM_vol_total_2 = 1 sorted_s_l = sorted(self.support_levels[symbol].items(), key=lambda x: sum(x[1].values()), reverse=True) if len(sorted_s_l) >= 3: sorted_s_l_3 = sorted_s_l[:3] else: place_to_fill = 3 - len(sorted_s_l) sorted_s_l_3 = sorted_s_l for i in range(place_to_fill): sorted_s_l_3 = sorted_s_l_3 + [(0.0,{0.0:0.0})] sorted_support_levels = sorted(sorted_s_l_3, key=lambda x: x[0]) pm_s3 = list(sorted_support_levels[0][1].keys())[0] pm_s3_rvol = list(sorted_support_levels[0][1].values())[0]/PM_vol_total_2 pm_s2 = list(sorted_support_levels[1][1].keys())[0] pm_s2_rvol = list(sorted_support_levels[1][1].values())[0]/PM_vol_total_2 pm_s1 = list(sorted_support_levels[2][1].keys())[0] pm_s1_rvol = list(sorted_support_levels[2][1].values())[0]/PM_vol_total_2 # Resistance levels r1-r3 and their rvol sorted_r_l = sorted(self.resistance_levels[symbol].items(), key=lambda x: sum(x[1].values()), reverse=True) if len(sorted_r_l) >= 3: sorted_r_l_3 = sorted_r_l[:3] else: place_to_fill = 3 - len(sorted_r_l) sorted_r_l_3 = sorted_r_l for i in range(place_to_fill): sorted_r_l_3 = sorted_r_l_3 + [(0.0,{0.0:0.0})] sorted_resistance_levels = sorted(sorted_r_l_3, key=lambda x: x[0]) pm_r3 = list(sorted_resistance_levels[2][1].keys())[0] pm_r3_rvol = list(sorted_resistance_levels[2][1].values())[0]/PM_vol_total_2 pm_r2 = list(sorted_resistance_levels[1][1].keys())[0] pm_r2_rvol = list(sorted_resistance_levels[1][1].values())[0]/PM_vol_total_2 pm_r1 = list(sorted_resistance_levels[0][1].keys())[0] pm_r1_rvol = list(sorted_resistance_levels[0][1].values())[0]/PM_vol_total_2 #Record pm_range self.pm_range[symbol] = pm_high - pm_low #self.Debug(f"support and resistance levels ready for {symbol} at {self.Time}") self.df_store.loc[len(self.df_store)] = [self.Time, symbol.Value, self.gap_combined[symbol], self.rvol_PM[symbol], pm_low, pm_s1, pm_s2, pm_s3, pm_s1_rvol, pm_s2_rvol, pm_s3_rvol, pm_high, pm_r1, pm_r2, pm_r3, pm_r1_rvol, pm_r2_rvol, pm_r3_rvol] self.df_store_day.loc[symbol] = [self.Time, symbol.Value, self.gap_combined[symbol], self.rvol_PM[symbol], pm_low, pm_s1, pm_s2, pm_s3, pm_s1_rvol, pm_s2_rvol, pm_s3_rvol, pm_high, pm_r1, pm_r2, pm_r3, pm_r1_rvol, pm_r2_rvol, pm_r3_rvol] #self.Debug(f"PM data successfully saved for {symbol} at {self.Time}") self.Debug(f"All PM data successfully collected at {self.Time}") #self.day_count +=1 #self.ObjectStore.Save(f"{self.ProjectId}/PM_data_2022-1-1_{self.day_count} days out", self.df_store.reset_index().to_json(date_unit='ns')) #self.Debug(f"object store as name {self.ProjectId}/PM_data_2022-1-1_{self.day_count} days out") #previous_day_count = self.day_count - 1 #self.ObjectStore.Delete(f"{self.ProjectId}/PM_data_2022-1-1_{previous_day_count} days out") #self.Debug(f"{self.ProjectId}/PM_data_2022-1-1_{previous_day_count} days out deleted") """ # Clear all dictionary to start fresh the next day self.gap.clear() self.gap_neg.clear() self.gap_combined.clear() self.rvol_PM.clear() self.support.clear() self.support_levels.clear() self.resistance.clear() self.resistance_levels.clear() self.all_levels.clear() self.Debug(f"dictionaries cleared for the next day") """ def ClosePositions(self): self.Debug(f"Enter into ClosePositions function at {self.Time}") 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.") # empty the daily pm data tracking df self.df_store_day = self.df_store_day.drop(self.df_store_day.index) self.LO_id = {} self.Debug(f"Cumulative number of winning trades are {self.winning_trades} with {self.winning_PL} cumulative winnings") self.Debug(f"Cumulative number of losing trades are {self.losing_trades} with {self.losing_PL} cumulative winnings") for symbol in self.symbols: self.RemoveSecurity(symbol.Value) self.day_count +=1 storage = self.df_BS.reset_index().to_json(date_unit='ns', default_handler=str) self.ObjectStore.Save(f"{self.ProjectId}/BT_data_2021-1-1_{self.day_count} days out", storage) self.Debug(f"object store as name {self.ProjectId}/BT_data_2021-1-1_{self.day_count} days out at {self.Time}") 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 # Read a key string to extract dataframe in the right format def symbol_extractor(key, algorithm): Project_key = key df = pd.read_json(algorithm.ObjectStore.Read(Project_key)) # Data Cleaning # Transform time column back to datetime format df['time'] = pd.to_datetime(df['time']) # Set time and symbol column as index df.set_index(['time', 'symbol'], inplace=True) # Remove index column df = df.drop('index', axis=1) dictionary = {} for (time, symbol), _ in df.iterrows(): dictionary.setdefault(time, []).append(symbol) return dictionary # Updated version to deal wtih multi-day period. Original function cannot handle non-continuous datetime series # To extract 10 seconds trading history for a symbol 15 mins from open (9:30-9:45) def Consolidator_2(algorithm, symbol, start_time, end_time, base_freq = Resolution.Second, consol_freq = 10): Sym = algorithm.AddEquity(symbol) # symbol is a string HLGN history = algorithm.History(Sym.Symbol, start_time, end_time, base_freq, extendedMarketHours = True) history_reset = history.reset_index(level="symbol") grouped = history_reset.groupby(pd.Grouper(freq='D')) df_ls = [] for date, group in grouped: # Store each day's DataFrame in the list df_ls.append(group.copy()) df_consol_ls = [] for df in df_ls: if base_freq == Resolution.Second: str_freq = f"{consol_freq}S" elif base_freq == Resolution.Minute: str_freq = f"{consol_freq}Min" try: df_his = df.resample(str_freq).apply(lambda x: pd.Series({ 'symbol': x['symbol'].iloc[0], 'askclose': x['askclose'].iloc[-1], 'askhigh' : x['askhigh'].max(), 'asklow': x['asklow'].min(), 'askopen': x['askopen'].iloc[0], 'asksize': x['asksize'].sum(), 'bidclose': x['bidclose'].iloc[-1], 'bidhigh' : x['bidhigh'].max(), 'bidlow': x['bidlow'].min(), 'bidopen': x['bidopen'].iloc[0], 'bidsize': x['bidsize'].sum(), 'close': x['close'].iloc[-1], 'high' : x['high'].max(), 'low': x['low'].min(), 'open': x['open'].iloc[0], 'volume': x['volume'].sum() })) df_consol_ls.append(df_his) except: print(f"Consolidator function: {symbol} has incomplete info from history") df_consol_ls.append(pd.DataFrame()) ten_sec_his = pd.concat(df_consol_ls) ten_sec_his_reset = ten_sec_his.reset_index(level="time") ten_sec_his_reset.set_index(['symbol','time'], inplace=True) return ten_sec_his_reset # find the slope for a multi-index series (such as pre_trading_history['close']) def find_slope(multi_index_series): single_index_series = multi_index_series.droplevel(level=0) time_stamps = single_index_series.index.values x = pd.to_numeric(time_stamps) y = single_index_series.values m, _ = np.polyfit(x,y,1) return m # 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)