def __init__(self, mode='full_history', verbose=False): # os.environ['TZ'] = 'America/New_York' # tzset() self._dt_wallstreet_ = get_wallstreet_time( )['datetime'] - datetime.datetime.now() self._dir_out_ = os.path.join(DIR_DATA, mode) self._date_format_ = '%Y-%m-%d' self._date_time_format_ = '%Y-%m-%d-%H-%M-%S' self._max_connection_attempts_ = 20 self.rexp_live_dollar = re.compile(r'starQuote.*?<') self.rexp_live_volume = re.compile(r'olume<.+?</td>') self.rexp_yahoo_prices_list = re.compile(r'"prices":\[.*?\]') self.n_cpu = os.cpu_count() self.verbose = verbose self.symbs = list() self.live_now = False self.dfs = None self.alpha_vantage_state = 'ok' if mode and not os.path.isdir(self._dir_out_): os.makedirs(self._dir_out_) print('Created: %s' % self._dir_out_)
def run(self): wst = get_wallstreet_time() if not wst['is_market_open']: print("Waiting {:.2f} hours for market to open".format( wst['open_in'] / 3600)) sleep(wst['open_in']) print('Market now open') if self.trader is not None: self.begin_trade() else: print('No trader')
def sort_buyables(self): wst = get_wallstreet_time(offset_close=self.trade_end_offset) if not wst['is_market_open']: # prevent buy when near end of day return None rvalues = [ self.stocks.get_stock(s).price_trend(k=self.__trend_window).rvalue for s in self.stocks.symbols ] idx = np.argsort(rvalues)[::-1] symbs = np.array(self.stocks.symbols)[idx] buy_symbs = [] for s in symbs: if s not in self.holding \ and s not in self.pending_buy \ and s not in self.pending_sell \ and s not in self.pending_cancel: buy_symbs.append(s) return buy_symbs
def sell_criteria(self, symbol): wst = get_wallstreet_time(offset_close=self.trade_end_offset) if wst['is_market_open']: # sell everything by end of day return True if symbol not in self.holding or symbol in self.pending_sell: return False stat = self.stocks.price_trend(symbol, k=self.__trend_window) stock = self.stocks.get_stock(symbol) # diff is relative to previous cached price diff = stock.price - self.cached_price[symbol] lb = self.lb_ratio * stock.open_price print(': {} rval={:.5f} pval={:.5f} diff={:.2f} lb={:.2f}'.format( symbol, stat.rvalue, stat.pvalue, diff, lb)) if diff <= lb: print('sell criteria ({}): below lower bound'.format(symbol)) return True # elif stat.pvalue <= 0.1 and stat.rvalue < 0: # # Too sensitive at the moment # print('sell criteria ({}): trending down'.format(symbol)) # return True return False
def trade_sequence(self): print('\n___ sequence {} ______'.format(datetime.now())) # check pending status self.get_all_pending_status() # get updates on qualified stocks # don't update cached_price yet, # need the previous cached_price # to determine sell criteria self.stocks.update() # check if there are stocks that should be # sold off for s in self.stocks.symbols: # sell stocks that should be dumped off if self.sell_criteria(s) and self.sell(s): self.pending_sell.append(s) # check global trend and make sure still in trade period # TODO - sell/buy criteria are bad! Need to figure out # a decent strategy stat = self.stocks.price_trend(k=self.__trend_window) global_statistic = stat.statistic global_pvalue = stat.pvalue print(': Global stat={} pval={}'.format(global_statistic, global_pvalue)) wst = get_wallstreet_time(offset_close=self.trade_end_offset) if wst['is_market_open'] and global_statistic > 4 and global_pvalue < 1e-4: # see if there are partitions available # to buy stocks that are worth it if self.partitions_remain > 0: # get all the symbols worth investing buyable_symbols = self.sort_buyables() # this tracks N x parition for each symbol # so if there are more partitions left than # buyable_symbols, same symbol can be assigned # more than 1 partition stock_partitions = defaultdict(int) if buyable_symbols: for i in range(self.partitions_remain): symb = buyable_symbols[i % len(buyable_symbols)] stock_partitions[symb] += 1 for symb, p in stock_partitions.items(): if self.buy(symb, p * self.partition_size): self.pending_buy.append(symb) elif not wst['is_market_open'] or (global_statistic < -4 and global_pvalue < 1e-4): if not wst['is_market_open']: print('! End of day soon, selling everything...') # sell all and cancel all buy orders if self.pending_buy: self.cancel_all_pending('buy') if self.holding: self.batch_order(list(self.holding.keys()), 'sell') else: print( '! Does not meet buy or sell criteria, continue watching market ...' ) # update cached_price for s in self.cached_price: if self.stocks.get_stock(s).price: self.cached_price[s] = self.stocks.get_stock(s).price
def begin_trade(self): """ Call this at market open to find stocks to scalp. Watch the first 10 iterations before calculating trend. :return: """ print('\nBegin trading ...\n') # 1. scan for open-high stocks # Take 60 - 75 sec for 342 stocks # About 5 sec for 30 print('Scanning %d stocks for open-high ...' % self.stocks.n_stocks) t0 = time() self.stocks.update() remove_stocks = [] for symb, stock in self.stocks.stocks.items(): if stock.open_close_change is None or stock.open_close_change <= 0: remove_stocks.append(symb) for s in remove_stocks: self.stocks.remove(s) print('|- scan took {:.2f} sec'.format(time() - t0)) # 2. Sort open-close ratio from low to high # take the first N-split X 3 to watch for # It seems like the ones that open too high do not growth much # but the ones the open slighly high are more likely to grow symbs = np.array(self.stocks.symbols) changes = np.array( [self.stocks.get_stock(s).open_close_change for s in symbs]) idx = np.argsort(changes) n_track = 3 * self.n_splits if len(symbs) > n_track: remove_stocks = symbs[idx][n_track:] for s in remove_stocks: self.stocks.remove(s) self.symbols_qualified = self.stocks.symbols print('Tracking %d qualifying stocks' % self.stocks.n_stocks) # 3. Conitnue to monitor the qualifying stocks for # more iterations for i_iter in range(self.__watch_iters): sleep(self.__watch_interval_sec) self.stocks.update() print('|- watch iter {} / {}'.format(i_iter + 1, self.__watch_iters)) # 4. run sequence until trade end or if there are pendings wst = get_wallstreet_time(offset_close=self.trade_end_offset) while wst['is_market_open'] or self.has_pending: self.trade_sequence() if self.net_worth <= self.max_loss: print('! Reach max loss, selling/cancelling everything.') if self.pending_buy: self.cancel_all_pending(method='buy') if self.holding: self.batch_order(list(self.holding.keys()), 'sell') break sleep(self.__watch_interval_sec) # 5. close trader self.trader.quit() print('\nHappy trade day!') print('${:,.2f} ===> ${:,.2f}'.format(self.allowance, self.net_worth))