class TickerFilter: def __init__(self, key, secret): self.api = Alpaca(key, secret, 'https://paper-api.alpaca.markets') self.cached_tickers = {} def ticker_exists(self, ticker): try: if ticker in self.cached_tickers: return self.cached_tickers[ticker] asset = self.api.get_asset(ticker) self.cached_tickers[ticker] = asset.tradable return asset.tradable except: self.cached_tickers[ticker] = False return False def filter_pass(self, comment): words = alnum.sub('', comment).split() tickers = [word for word in words if word.isupper() and len(word) in RedditSentimentSource._VALID_TICKER_LENGTHS] for word in words: if self.ticker_exists(word): return True return False
class AlpacaTradeInterface(TradeInterface): def __init__(self, key, secret, endpoint): self.api = Alpaca(key, secret, endpoint) self.pending_orders = {} self.cached_tickers = {} def initialize(self, env: Environment): try: # Load cash balance account = self.api.get_account() env.get_portfolio().cash = float( account.cash) # We have margin but shouldn't use it # Load open orders orders = self.api.list_orders(status='open', limit=500) self.pending_orders = { order.id: Order(order.symbol, OrderType(order.order_type), order.qty, order.side == 'buy', order_id=order.id, limit_price=order.limit_price, stop_price=order.stop_price) for order in orders } # Load open positions positions = self.api.list_positions() for pos in positions: env.get_portfolio()._add_position( Position(pos.symbol, pos.qty, pos.avg_entry_price, pos.current_price)) except Exception: logger.exception('Failed to pull current orders and cash balance') return False return True def update(self, env: Environment): orders = self.api.list_orders(status='closed', after=datetime.now() - timedelta(days=2), limit=500) for order in orders: if order.id in self.pending_orders: logger.info(f'Order {order.id} executed') env.notify_order_completed( ExecutedOrder(order.symbol, order.filled_qty, order.filled_avg_price, order.side == 'buy')) def place_order(self, order: Order): logger.info(f'Placing order for {order.quantity} {order.ticker}') try: result = self.api.submit_order(order.ticker, order.quantity, 'buy' if order.is_buy else 'sell', order.order_type.value, 'day', limit_price=order.limit_price, stop_price=order.stop_price) order.order_id = result.id self.pending_orders[result.id] = order return True except Exception: logger.exception( f'Failed to place order for {order.quantity} {order.ticker}') return False def cancel_order(self, order_id): logger.info(f'Canceling order {order_id}') try: self.api.cancel_order(order_id) self.pending_orders.pop(order_id, None) return True except Exception: logger.exception(f'Failed to cancel order {order_id}') return False def market_open(self): clock = self.api.get_clock() return clock.is_open # TODO - we also have open/close times. May want to avoid holding overnight def open_orders(self): return [order for order in self.pending_orders.values()] def ticker_exists(self, ticker): try: if ticker in self.cached_tickers: return self.cached_tickers[ticker] asset = self.api.get_asset(ticker) self.cached_tickers[ticker] = asset.tradable return asset.tradable except: self.cached_tickers[ticker] = False return False