class Streamer:
    conn = None

    def __init__(self,
                 q,
                 api_key='',
                 api_secret='',
                 instrument='',
                 method: StreamingMethod = StreamingMethod.AccountUpdate,
                 base_url='',
                 data_url='',
                 data_feed='iex',
                 *args,
                 **kwargs):
        try:
            # make sure we have an event loop, if not create a new one
            asyncio.get_event_loop()
        except RuntimeError:
            asyncio.set_event_loop(asyncio.new_event_loop())

        self.conn = Stream(api_key,
                           api_secret,
                           base_url,
                           data_stream_url=data_url,
                           data_feed=data_feed)
        self.instrument = instrument
        self.method = method
        self.q = q

    def run(self):
        if self.method == StreamingMethod.AccountUpdate:
            self.conn.subscribe_trade_updates(self.on_trade)
        elif self.method == StreamingMethod.MinuteAgg:
            self.conn.subscribe_bars(self.on_agg_min, self.instrument)
        elif self.method == StreamingMethod.Quote:
            self.conn.subscribe_quotes(self.on_quotes, self.instrument)

        # this code runs in a new thread. we need to set the loop for it
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        self.conn.run()

    async def on_listen(self, conn, stream, msg):
        pass

    async def on_quotes(self, msg):
        msg._raw['time'] = msg.timestamp
        self.q.put(msg._raw)

    async def on_agg_min(self, msg):
        msg._raw['time'] = msg.timestamp
        self.q.put(msg._raw)

    async def on_account(self, msg):
        self.q.put(msg)

    async def on_trade(self, msg):
        self.q.put(msg)
class AlpacaStream(StreamingAPI):
    def __init__(self, queues: QueueMapper):
        self.alpaca_ws_client = Stream(
            key_id=config.alpaca_api_key, secret_key=config.alpaca_api_secret
        )

        if not self.alpaca_ws_client:
            raise AssertionError(
                "Failed to authenticate Alpaca web_socket client"
            )
        self.alpaca_ws_client.subscribe_trade_updates(
            AlpacaStream.trade_update_handler
        )
        self.alpaca_ws_client.run()

        super().__init__(queues)

    @classmethod
    async def trade_update_handler(cls, msg):
        print(f"trade_update_handler:{msg}")

    @classmethod
    async def bar_handler(cls, msg):
        print(f"bar_handler:{msg}")

    @classmethod
    async def trades_handler(cls, msg):
        print(f"trades_handler:{msg}")

    @classmethod
    async def quotes_handler(cls, msg):
        print(f"quotes_handler:{msg}")

    async def subscribe(
        self, symbols: List[str], events: List[WSEventType]
    ) -> bool:
        for event in events:
            if event == WSEventType.SEC_AGG:
                raise NotImplementedError(
                    f"event {event} not implemente in Alpaca"
                )
            elif event == WSEventType.MIN_AGG:
                self.alpaca_ws_client.subscribe_bars(
                    AlpacaStream.bar_handler, symbols
                )
            elif event == WSEventType.TRADE:
                self.alpaca_ws_client.subscribe_trades(
                    AlpacaStream.trades_handler, symbols
                )
            elif event == WSEventType.QUOTE:
                self.alpaca_ws_client.subscribe_quotes(
                    AlpacaStream.quotes_handler, symbols
                )
        return True
示例#3
0
class AlpacaStream(StreamingAPI):
    def __init__(self, queues: QueueMapper):
        self.alpaca_ws_client = Stream(
            base_url=URL(config.alpaca_base_url),
            key_id=config.alpaca_api_key,
            secret_key=config.alpaca_api_secret,
            data_feed="sip",  # config.alpaca_data_feed,
        )

        if not self.alpaca_ws_client:
            raise AssertionError(
                "Failed to authenticate Alpaca web_socket client")

        self.running = False
        super().__init__(queues)

    async def run(self):
        if not self.running:
            if not self.queues:
                raise AssertionError(
                    "can't call `AlpacaStream.run()` without queues")

            asyncio.create_task(self.alpaca_ws_client._run_forever())
            self.running = True

    @classmethod
    async def bar_handler(cls, msg):
        try:
            event = {
                "symbol": msg.symbol,
                "open": msg.open,
                "close": msg.close,
                "high": msg.high,
                "low": msg.low,
                "start": int(msg.timestamp // 1000000),
                "volume": msg.volume,
                "count": 0.0,
                "vwap": 0.0,
                "average": 0.0,
                "totalvolume": None,
                "EV": "AM",
            }
            cls.get_instance().queues[msg.symbol].put(event, timeout=1)
        except queue.Full as f:
            tlog(
                f"[EXCEPTION] process_message(): queue for {event['sym']} is FULL:{f}, sleeping for 2 seconds and re-trying."
            )
            raise
        except Exception as e:
            tlog(
                f"[EXCEPTION] process_message(): exception of type {type(e).__name__} with args {e.args}"
            )
            traceback.print_exc()

    @classmethod
    async def trades_handler(cls, msg):
        try:
            event = {
                "symbol": msg.symbol,
                "price": msg.price,
                "timestamp": pd.to_datetime(msg.timestamp),
                "volume": msg.size,
                "exchange": msg.exchange,
                "conditions": msg.conditions,
                "tape": msg.tape,
                "EV": "T",
            }
            cls.get_instance().queues[msg.symbol].put(event, timeout=1)
        except queue.Full as f:
            tlog(
                f"[EXCEPTION] process_message(): queue for {event['sym']} is FULL:{f}, sleeping for 2 seconds and re-trying."
            )
            raise
        except Exception as e:
            tlog(
                f"[EXCEPTION] process_message(): exception of type {type(e).__name__} with args {e.args}"
            )
            traceback.print_exc()

    @classmethod
    async def quotes_handler(cls, msg):
        print(f"quotes_handler:{msg}")

    async def subscribe(self, symbols: List[str],
                        events: List[WSEventType]) -> bool:
        for event in events:
            if event == WSEventType.SEC_AGG:
                tlog(f"event {event} not implemented in Alpaca")
            elif event == WSEventType.MIN_AGG:
                self.alpaca_ws_client._data_ws._running = False
                self.alpaca_ws_client.subscribe_bars(AlpacaStream.bar_handler,
                                                     *symbols)
            elif event == WSEventType.TRADE:
                self.alpaca_ws_client.subscribe_trades(
                    AlpacaStream.trades_handler, *symbols)
            elif event == WSEventType.QUOTE:
                self.alpaca_ws_client.subscribe_quotes(
                    AlpacaStream.quotes_handler, *symbols)
        return True
示例#4
0
    def start_trading(self):
        conn = Stream(
            self.key_id,
            self.secret_key,
            base_url=self.base_url,
            data_feed='iex')  # <- replace to SIP if you have PRO subscription

        # Listen for second aggregates and perform trading logic
        async def handle_bar(bar):
            self.tick_index = (self.tick_index + 1) % self.tick_size
            if self.tick_index == 0:
                # It's time to update

                # Update price info
                tick_open = self.last_price
                tick_close = bar.close
                self.last_price = tick_close

                self.process_current_tick(tick_open, tick_close)

        conn.subscribe_bars(handle_bar, self.symbol)

        # Listen for quote data and perform trading logic
        async def handle_trades(trade):
            now = datetime.datetime.utcnow()
            if now - self.last_trade_time < datetime.timedelta(seconds=1):
                # don't react every tick unless at least 1 second past
                return
            self.last_trade_time = now
            self.tick_index = (self.tick_index + 1) % self.tick_size
            if self.tick_index == 0:
                # It's time to update

                # Update price info
                tick_open = self.last_price
                tick_close = trade.price
                self.last_price = tick_close

                self.process_current_tick(tick_open, tick_close)

        conn.subscribe_trades(handle_trades, self.symbol)

        # Listen for updates to our orders
        async def handle_trade_updates(data):
            symbol = data.order['symbol']
            if symbol != self.symbol:
                # The order was for a position unrelated to this script
                return

            event_type = data.event
            qty = int(data.order['filled_qty'])
            side = data.order['side']
            oid = data.order['id']

            if event_type == 'fill' or event_type == 'partial_fill':
                # Our position size has changed
                self.position = int(data.position_qty)
                print(f'New position size due to order fill: {self.position}')
                if (event_type == 'fill' and self.current_order
                    and self.current_order.id == oid):
                    self.current_order = None
            elif event_type == 'rejected' or event_type == 'canceled':
                if self.current_order and self.current_order.id == oid:
                    # Our last order should be removed
                    self.current_order = None
            elif event_type != 'new':
                print(f'Unexpected order event type {event_type} received')

        conn.subscribe_trade_updates(handle_trade_updates)

        conn.run()