예제 #1
0
    async def _subscribe_trades_and_quotes(self):
        network = self.scheduler.get_network()

        for instrument in self.get_instruments():
            symbol = instrument.get_exchange_instrument_code()

            self.instrument_trades[symbol] = MutableSignal()
            self.instrument_quotes[symbol] = MutableSignal()

            # magic: inject the bare Signal into the graph so we can
            # fire events on it without any downstream connections
            # yet made
            network.attach(self.instrument_trades[symbol])
            network.attach(self.instrument_quotes[symbol])

            subscribe_msg = {
                'id': 1,
                'method': 'trade.subscribe',
                'params': [symbol]
            }

            messages = MutableSignal()
            json_messages = Map(network, messages, lambda x: json.loads(x))
            incr_messages = Filter(network, json_messages,
                                   lambda x: x.get('type', None) == 'incremental')
            trade_lists = Map(network, incr_messages, lambda x: self.__extract_trades(x))
            trades = FlatMap(self.scheduler, trade_lists)

            class TradeScheduler(Event):
                # noinspection PyShadowingNames
                def __init__(self, fh: PhemexFeedHandler, trades: Signal):
                    self.fh = fh
                    self.trades = trades

                def on_activate(self) -> bool:
                    if self.trades.is_valid():
                        trade = self.trades.get_value()
                        trade_symbol = trade.get_instrument().get_exchange_instrument_code()
                        trade_signal = self.fh.instrument_trades[trade_symbol]
                        self.fh.scheduler.schedule_update(trade_signal, trade)
                        return True
                    else:
                        return False

            network.connect(trades, TradeScheduler(self, trades))

            # noinspection PyShadowingNames
            async def do_subscribe(instrument, subscribe_msg, messages):
                async with websockets.connect(self.ws_uri) as sock:
                    subscribe_msg_txt = json.dumps(subscribe_msg)
                    self.logger.info(f'sending subscription request for {instrument.get_exchange_instrument_code()}')
                    await sock.send(subscribe_msg_txt)
                    while True:
                        self.scheduler.schedule_update(messages, await sock.recv())

            asyncio.ensure_future(do_subscribe(instrument, subscribe_msg, messages))

        # we are now live
        self.scheduler.schedule_update(self.state, FeedHandlerState.LIVE)
    async def _subscribe_trades_and_quotes(self):
        network = self.scheduler.get_network()

        symbols = []
        for instrument in self.get_instruments():
            symbol = instrument.get_exchange_instrument_code()
            symbols.append(f'{symbol}')

            self.instrument_trades[symbol] = MutableSignal()
            self.instrument_quotes[symbol] = MutableSignal()
            self.instrument_order_books[symbol] = MutableSignal()

            # magic: inject the bare Signal into the graph so we can
            # fire events on it without any downstream connections
            # yet made
            network.attach(self.instrument_trades[symbol])
            network.attach(self.instrument_quotes[symbol])
            network.attach(self.instrument_order_books[symbol])

        subscribe_msg = {
            'type': 'subscribe',
            'product_ids': symbols,
            'channels': ['matches', 'heartbeat']
        }

        messages = MutableSignal()
        json_messages = Map(network, messages, lambda x: json.loads(x))
        match_messages = Filter(network, json_messages,
                                lambda x: x.get('type', None) == 'match')
        trades = Map(network, match_messages,
                     lambda x: self.__extract_trade(x))

        class TradeScheduler(Event):
            def __init__(self, fh: CoinbaseProFeedHandler):
                self.fh = fh

            def on_activate(self) -> bool:
                if trades.is_valid():
                    trade = trades.get_value()
                    trade_symbol = trade.get_instrument(
                    ).get_exchange_instrument_code()
                    trade_signal = self.fh.instrument_trades[trade_symbol]
                    self.fh.scheduler.schedule_update(trade_signal, trade)
                    return True
                else:
                    return False

        network.connect(trades, TradeScheduler(self))

        async with websockets.connect(self.ws_uri) as sock:
            self.logger.info('Sending subscription request for all products')
            await sock.send(json.dumps(subscribe_msg))
            self.scheduler.schedule_update(self.state, FeedHandlerState.LIVE)
            while True:
                self.scheduler.schedule_update(messages, await sock.recv())
예제 #3
0
    def get_order_books(self, instrument: ExchangeInstrument) -> Signal:
        symbol = instrument.get_exchange_instrument_code()
        exchange_code_upper = instrument.get_exchange().get_exchange_code().upper()

        order_books = self.book_signal_by_symbol.get(symbol, None)
        if order_books is None:
            self._maybe_notify_subscription(instrument)

            order_books = MutableSignal()
            self.scheduler.network.attach(order_books)
            self.book_signal_by_symbol[symbol] = order_books

            class OrderBookGenerator(SignalGenerator):
                def __init__(self, mds):
                    self.mds = mds

                def generate(self, scheduler: NetworkScheduler):
                    tickstore = AzureBlobTickstore(self.mds.credential, f'{exchange_code_upper}_BOOKS',
                                                   timestamp_column='time')
                    books_df = tickstore.select(symbol, self.mds.start_time, self.mds.end_time)
                    for row in books_df.itertuples():
                        at_time = row[0].asm8.astype('int64') / 10**6
                        best_bid = BookLevel(row[2], row[1])
                        best_ask = BookLevel(row[4], row[3])
                        book = OrderBook(bids=[best_bid], asks=[best_ask])
                        scheduler.schedule_update_at(order_books, book, at_time)
                    tickstore.close()

            self.scheduler.add_generator(OrderBookGenerator(self))

            # workaround: force scheduling of all historic trade events
            self.get_trades(instrument)

        return order_books
예제 #4
0
 def __init__(self, scheduler: NetworkScheduler, registry: FeedHandlerRegistry, instance_id: str = 'prod'):
     self.scheduler = scheduler
     self.registry = registry
     self.instance_id = instance_id
     self.subscribed_instruments = MutableSignal()
     self.notified_instruments = set()
     scheduler.get_network().attach(self.subscribed_instruments)
예제 #5
0
 def get_position(self, account: str,
                  instrument: ExchangeInstrument) -> MutableSignal:
     position = self.positions.get((account, instrument), None)
     if position is None:
         position = MutableSignal(Position(account, instrument))
         self.positions[(account, instrument)] = position
         self.scheduler.get_network().attach(position)
     return position
    def __init__(self, scheduler: NetworkScheduler, mds: MarketdataService):
        super().__init__(OrderFactory())
        self.scheduler = scheduler
        self.mds = mds

        self.order_events = MutableSignal()
        self.scheduler.get_network().attach(self.order_events)

        self.orders = {}
    def start(self):
        network = self.scheduler.get_network()
        messages = MutableSignal()
        json_messages = Map(network, messages, lambda x: json.loads(x))
        json_messages = Filter(
            network, json_messages,
            lambda x: x.get('type', None) in ['incremental', 'snapshot'])

        class OrderEventScheduler(Event):
            # noinspection PyShadowingNames
            def __init__(self, sub: AccountOrderPositionSubscriber,
                         json_messages: Signal):
                self.sub = sub
                self.json_messages = json_messages

            def on_activate(self) -> bool:
                if self.json_messages.is_valid():
                    msg = self.json_messages.get_value()
                    accounts = msg['accounts']
                    for account in accounts:
                        orders = account['orders']
                        for order in orders:
                            order_event = OrderEvent()
                            self.sub.scheduler.schedule_update(
                                self.sub.order_events, order_event)
                    return True
                else:
                    return False

        network.connect(json_messages,
                        OrderEventScheduler(self, json_messages))

        # noinspection PyShadowingNames
        async def do_subscribe():
            async with websockets.connect(self.ws_uri) as sock:
                self.logger.info(
                    f'sending Account-Order-Position subscription request')
                auth_msg = self.auth.get_user_auth_message()
                await sock.send(auth_msg)
                error_msg = await sock.recv()
                error_struct = json.loads(error_msg)
                if error_struct['error'] is not None:
                    raise ConnectionError(
                        f'Unable to authenticate: {error_msg}')

                aop_sub_msg = {
                    'id': 2,
                    'method': 'aop.subscribe',
                    'params': []
                }
                await sock.send(json.dumps(aop_sub_msg))
                while True:
                    self.scheduler.schedule_update(messages, await sock.recv())

        asyncio.ensure_future(do_subscribe())
예제 #8
0
    def get_marks(self, instrument: ExchangeInstrument) -> Signal:
        economics = instrument.get_instrument().get_economics()
        if isinstance(economics, FutureContract):
            economics = economics.get_underlier().get_economics()
        if isinstance(economics, CurrencyPair):
            ccy_code = economics.get_base_currency().get_currency_code()
            symbol = f'.M{ccy_code}'
            cash_instrument = self.instrument_cache.get_or_create_cash_instrument(
                ccy_code)
        else:
            raise ValueError(
                'Unable to get marks: expected CurrencyPair or FutureContract on CurrencyPair'
            )

        network = self.scheduler.get_network()
        marks = self.instrument_marks.get(symbol)
        if marks is None:
            marks = MutableSignal()
            network.attach(marks)
            self.instrument_marks[symbol] = marks

            subscribe_msg = {
                'id': 1,
                'method': 'tick.subscribe',
                'params': [symbol]
            }

            messages = MutableSignal()
            json_messages = Map(network, messages, lambda x: json.loads(x))
            tick_messages = Filter(network, json_messages,
                                   lambda x: 'tick' in x)
            ticks = Map(
                network, tick_messages, lambda x: self.__extract_tick(
                    x, cash_instrument.get_instrument()))
            Pipe(self.scheduler, ticks, marks)

            asyncio.ensure_future(
                websocket_subscribe_with_retry(self.ws_uri, self.timeout,
                                               self.logger, subscribe_msg,
                                               self.scheduler, messages,
                                               symbol, 'ticks'))
        return marks
예제 #9
0
파일: azure.py 프로젝트: trxw/serenity
    def __init__(self, scheduler: HistoricNetworkScheduler,
                 azure_connect_str: str):
        self.scheduler = scheduler
        self.azure_connect_str = azure_connect_str
        self.subscribed_instruments = MutableSignal()
        self.scheduler.get_network().attach(self.subscribed_instruments)

        self.start_time = scheduler.get_clock().get_start_time()
        self.end_time = scheduler.get_clock().get_end_time()

        self.all_subscribed = set()
        self.book_signal_by_symbol = {}
        self.trade_signal_by_symbol = {}
예제 #10
0
    def __init__(self, scheduler: NetworkScheduler, instrument_cache: InstrumentCache,
                 instance_id: str):
        self.scheduler = scheduler
        self.instrument_cache = instrument_cache
        self.type_code_cache = instrument_cache.get_type_code_cache()
        self.instance_id = instance_id

        self.instruments = []
        self.known_instrument_ids = {}
        self.price_scaling = {}
        self._load_instruments()

        self.state = MutableSignal(FeedHandlerState.INITIALIZING)
        self.scheduler.get_network().attach(self.state)
예제 #11
0
def test_buffer_with_time_historic():
    scheduler = HistoricNetworkScheduler(0, 30 * 1000)
    network = scheduler.get_network()
    values = MutableSignal()
    scheduler.schedule_update_at(values, 1.0, 1000)
    scheduler.schedule_update_at(values, -3.2, 2000)
    scheduler.schedule_update_at(values, 2.1, 10000)
    scheduler.schedule_update_at(values, -2.9, 15000)
    scheduler.schedule_update_at(values, 8.3, 25000)
    scheduler.schedule_update_at(values, -5.7, 30000)

    buffer = BufferWithTime(scheduler, values, timedelta(seconds=5))
    Do(network, buffer, lambda: print(f'{buffer.get_value()}'))
    scheduler.run()
예제 #12
0
    def __init__(self, scheduler: HistoricNetworkScheduler):
        self.scheduler = scheduler
        self.subscribed_instruments = MutableSignal()
        self.scheduler.get_network().attach(self.subscribed_instruments)

        self.start_time = scheduler.get_clock().get_start_time()
        self.end_time = scheduler.get_clock().get_end_time()

        self.all_subscribed = set()
        self.book_signal_by_symbol = {}
        self.trade_signal_by_symbol = {}

        self.credential = DeviceCodeCredential(client_id=get_global_defaults()['azure']['client_id'],
                                               tenant_id=get_global_defaults()['azure']['tenant_id'])
예제 #13
0
    def __init__(self, scheduler: NetworkScheduler, booker: TradeBookingService = NullTradeBookingService()):
        self.scheduler = scheduler
        self.order_state_by_cl_ord_id = {}
        self.order_by_cl_ord_id = {}
        self.order_events = MutableSignal()
        self.orders = MutableSignal()

        self.scheduler.get_network().attach(self.orders)
        self.scheduler.get_network().attach(self.order_events)

        class OnBookingEvent(Event):
            def __init__(self, oms: OrderManagerService):
                self.oms = oms

            def on_activate(self) -> bool:
                if self.oms.order_events.is_valid():
                    order_event = self.oms.order_events.get_value()
                    if isinstance(order_event, ExecutionReport):
                        cl_ord_id = order_event.get_cl_ord_id()
                        order = self.oms.get_order_by_cl_ord_id(cl_ord_id)
                        booker.book(order, order_event)
                return False

        self.scheduler.get_network().connect(self.order_events, OnBookingEvent(self))
예제 #14
0
    def __init__(self,
                 credentials: AuthCredentials,
                 scheduler: NetworkScheduler,
                 oms: OrderManagerService,
                 instance_id: str = 'prod'):
        self.auth = PhemexWebsocketAuthenticator(credentials)
        self.scheduler = scheduler
        self.oms = oms

        # timeout in seconds
        self.timeout = 60

        self.order_events = MutableSignal()
        self.scheduler.network.attach(self.order_events)

        (self.phemex,
         self.ws_uri) = get_phemex_connection(credentials, instance_id)
예제 #15
0
파일: azure.py 프로젝트: trxw/serenity
    def get_trades(self, instrument: ExchangeInstrument) -> Signal:
        trade_symbol = instrument.get_exchange_instrument_code()
        exchange_code_upper = instrument.get_exchange().get_exchange_code(
        ).upper()

        self._maybe_notify_subscription(instrument)

        trades = self.trade_signal_by_symbol.get(trade_symbol, None)
        if trades is None:
            trades = MutableSignal()
            self.scheduler.network.attach(trades)
            self.trade_signal_by_symbol[trade_symbol] = trades

            class TradeGenerator(SignalGenerator):
                def __init__(self, mds):
                    self.mds = mds

                def generate(self, scheduler: NetworkScheduler):
                    tickstore = AzureBlobTickstore(
                        self.mds.azure_connect_str,
                        f'{exchange_code_upper}_TRADES',
                        timestamp_column='time')
                    trades_df = tickstore.select(trade_symbol,
                                                 self.mds.start_time,
                                                 self.mds.end_time)
                    for row in trades_df.itertuples():
                        at_time = row[0].asm8.astype('int64') / 10**6
                        sequence = row[1]
                        trade_id = row[2]
                        side = Side.SELL if row[4] == 'sell' else Side.BUY
                        qty = row[5]
                        price = row[6]
                        trade = Trade(instrument, sequence, trade_id, side,
                                      qty, price)
                        scheduler.schedule_update_at(trades, trade, at_time)

                    tickstore.close()

            self.scheduler.add_generator(TradeGenerator(self))

            # workaround: force scheduling of all historic order book events
            self.get_order_books(instrument)

        return trades
    def __init__(self,
                 auth: WebsocketAuthenticator,
                 scheduler: NetworkScheduler,
                 instance_id: str = 'prod'):
        self.auth = auth
        self.scheduler = scheduler

        self.order_events = MutableSignal()
        self.scheduler.network.attach(self.order_events)

        if instance_id == 'prod':
            self.ws_uri = 'wss://phemex.com/ws'
            self.phemex = PhemexConnection()
        elif instance_id == 'test':
            self.ws_uri = 'wss://testnet.phemex.com/ws'
            self.phemex = PhemexConnection(
                api_url='https://testnet-api.phemex.com')
        else:
            raise ValueError(f'Unknown instance_id: {instance_id}')
예제 #17
0
    def __init__(self,
                 credentials: AuthCredentials,
                 scheduler: NetworkScheduler,
                 instrument_cache: InstrumentCache,
                 account: str,
                 instance_id: str = 'prod'):
        super().__init__(scheduler)
        self.auth = PhemexWebsocketAuthenticator(credentials)
        self.scheduler = scheduler
        self.instrument_cache = instrument_cache
        self.account = account

        # timeout in seconds
        self.timeout = 60

        self.order_events = MutableSignal()
        self.scheduler.network.attach(self.order_events)

        (self.phemex,
         self.ws_uri) = get_phemex_connection(credentials, instance_id)
예제 #18
0
 def __init__(self, scheduler: NetworkScheduler):
     self.scheduler = scheduler
     self.exchange_positions = MutableSignal()
     self.scheduler.get_network().attach(self.exchange_positions)
    async def _subscribe_trades_and_quotes(self):
        network = self.scheduler.get_network()

        symbols = []
        for instrument in self.get_instruments():
            symbol = instrument.get_exchange_instrument_code()
            symbols.append(f'{symbol.lower()}@aggTrade')

            self.instrument_trades[symbol] = MutableSignal()
            self.instrument_quotes[symbol] = MutableSignal()

            # magic: inject the bare Signal into the graph so we can
            # fire events on it without any downstream connections
            # yet made
            network.attach(self.instrument_trades[symbol])
            network.attach(self.instrument_quotes[symbol])

        messages = MutableSignal()
        json_messages = Map(network, messages, lambda x: json.loads(x))
        trade_messages = Filter(network, json_messages, lambda x: 'data' in x)
        trades = Map(network, trade_messages,
                     lambda x: self.__extract_trade(x))

        class TradeScheduler(Event):
            def __init__(self, fh: BinanceFeedHandler):
                self.fh = fh

            def on_activate(self) -> bool:
                if trades.is_valid():
                    trade = trades.get_value()
                    trade_symbol = trade.get_instrument(
                    ).get_exchange_instrument_code()
                    trade_signal = self.fh.instrument_trades[trade_symbol]
                    self.fh.scheduler.schedule_update(trade_signal, trade)
                    return True
                else:
                    return False

        network.connect(trades, TradeScheduler(self))

        async with websockets.connect(self.ws_uri) as sock:
            ndx = 1
            n = 250
            symbols_chunked = [
                symbols[i:i + n] for i in range(0, len(symbols), n)
            ]

            for symbols in symbols_chunked:
                self.logger.info(
                    f'Sending subscription request for {len(symbols)} symbols: {symbols}'
                )
                subscribe_msg = {
                    "method": "SUBSCRIBE",
                    "params": symbols,
                    "id": ndx
                }
                await sock.send(json.dumps(subscribe_msg))
                ndx = ndx + 1

            self.scheduler.schedule_update(self.state, FeedHandlerState.LIVE)
            while True:
                self.scheduler.schedule_update(messages, await sock.recv())
예제 #20
0
    async def _subscribe_trades_and_quotes(self):
        network = self.scheduler.get_network()

        for instrument in self.get_instruments():
            symbol = instrument.get_exchange_instrument_code()
            if symbol == self.include_symbol or self.include_symbol == '*':
                self.instrument_trades[symbol] = MutableSignal()
                self.instrument_order_book_events[symbol] = MutableSignal()
                self.instrument_order_books[symbol] = OrderBookBuilder(
                    network, self.instrument_order_book_events[symbol])

                # magic: inject the bare Signal into the graph so we can
                # fire events on it without any downstream connections
                network.attach(self.instrument_trades[symbol])
                network.attach(self.instrument_order_book_events[symbol])
                network.attach(self.instrument_order_books[symbol])

                trade_subscribe_msg = {
                    'id': 1,
                    'method': 'trade.subscribe',
                    'params': [symbol]
                }

                trade_messages = MutableSignal()
                trade_json_messages = Map(network, trade_messages,
                                          lambda x: json.loads(x))
                trade_incr_messages = Filter(
                    network, trade_json_messages,
                    lambda x: x.get('type', None) == 'incremental')
                trade_lists = Map(network, trade_incr_messages,
                                  lambda x: self.__extract_trades(x))
                trades = FlatMap(self.scheduler, trade_lists)

                class TradeScheduler(Event):
                    # noinspection PyShadowingNames
                    def __init__(self, fh: PhemexFeedHandler, trades: Signal):
                        self.fh = fh
                        self.trades = trades

                    def on_activate(self) -> bool:
                        if self.trades.is_valid():
                            trade = self.trades.get_value()
                            trade_symbol = trade.get_instrument(
                            ).get_exchange_instrument_code()
                            trade_signal = self.fh.instrument_trades[
                                trade_symbol]
                            self.fh.scheduler.schedule_update(
                                trade_signal, trade)
                            return True
                        else:
                            return False

                network.connect(trades, TradeScheduler(self, trades))

                orderbook_subscribe_msg = {
                    'id': 2,
                    'method': 'orderbook.subscribe',
                    'params': [symbol]
                }

                obe_messages = MutableSignal()
                obe_json_messages = Map(network, obe_messages,
                                        lambda x: json.loads(x))
                obe_json_messages = Filter(
                    network, obe_json_messages, lambda x: x.get('type', None)
                    in ['incremental', 'snapshot'])
                order_book_events = Map(
                    network, obe_json_messages,
                    lambda x: self.__extract_order_book_event(x))

                class OrderBookEventScheduler(Event):
                    # noinspection PyShadowingNames
                    def __init__(self, fh: PhemexFeedHandler,
                                 order_book_events: Signal):
                        self.fh = fh
                        self.order_book_events = order_book_events

                    def on_activate(self) -> bool:
                        if self.order_book_events.is_valid():
                            obe = self.order_book_events.get_value()
                            obe_symbol = obe.get_instrument(
                            ).get_exchange_instrument_code()
                            obe_signal = self.fh.instrument_order_book_events[
                                obe_symbol]
                            self.fh.scheduler.schedule_update(obe_signal, obe)
                            return True
                        else:
                            return False

                network.connect(
                    order_book_events,
                    OrderBookEventScheduler(self, order_book_events))

                asyncio.ensure_future(
                    websocket_subscribe_with_retry(self.ws_uri, self.timeout,
                                                   self.logger,
                                                   trade_subscribe_msg,
                                                   self.scheduler,
                                                   trade_messages, symbol,
                                                   'trade'))
                asyncio.ensure_future(
                    websocket_subscribe_with_retry(self.ws_uri, self.timeout,
                                                   self.logger,
                                                   orderbook_subscribe_msg,
                                                   self.scheduler,
                                                   obe_messages, symbol,
                                                   'order book'))

        # we are now live
        self.scheduler.schedule_update(self.state, FeedHandlerState.LIVE)
예제 #21
0
    def subscribe(self):
        network = self.scheduler.get_network()
        messages = MutableSignal()
        json_messages = Map(network, messages, lambda x: json.loads(x))

        class PositionUpdateScheduler(Event):
            # noinspection PyShadowingNames
            def __init__(self, sub: PhemexExchangePositionService,
                         json_messages: Signal):
                self.sub = sub
                self.json_messages = json_messages

            def on_activate(self) -> bool:
                if self.json_messages.is_valid():
                    msg = self.json_messages.get_value()
                    if 'positions' in msg:
                        for position in msg['positions']:
                            if position['accountID'] == self.sub.account:
                                qty = (position['crossSharedBalanceEv'] /
                                       100_000_000)
                                ccy_symbol = position['currency']
                                ccy = self.sub.instrument_cache.get_or_create_currency(
                                    ccy_symbol)
                                xp = ExchangePosition(self.sub.account, ccy,
                                                      qty)
                                self.sub.scheduler.schedule_update(
                                    self.sub.exchange_positions, xp)

                    return True
                else:
                    return False

        network.connect(json_messages,
                        PositionUpdateScheduler(self, json_messages))

        # noinspection PyShadowingNames,PyBroadException
        async def do_subscribe():
            while True:
                try:
                    async with websockets.connect(self.ws_uri) as sock:
                        self.logger.info(
                            'sending Account-Order-Position subscription request for positions'
                        )
                        auth_msg = self.auth.get_user_auth_message(2)
                        await sock.send(auth_msg)
                        error_msg = await sock.recv()
                        error_struct = json.loads(error_msg)
                        if error_struct['error'] is not None:
                            raise ConnectionError(
                                f'Unable to authenticate: {error_msg}')

                        aop_sub_msg = {
                            'id': 3,
                            'method': 'aop.subscribe',
                            'params': []
                        }
                        await sock.send(json.dumps(aop_sub_msg))
                        while True:
                            try:
                                self.scheduler.schedule_update(
                                    messages, await sock.recv())
                            except BaseException as error:
                                self.logger.error(
                                    f'disconnected; attempting to reconnect after {self.timeout} '
                                    f'seconds: {error}')
                                await asyncio.sleep(self.timeout)

                                # exit inner loop
                                break

                except socket.gaierror as error:
                    self.logger.error(
                        f'failed with socket error; attempting to reconnect after {self.timeout} '
                        f'seconds: {error}')
                    await asyncio.sleep(self.timeout)
                    continue
                except ConnectionRefusedError as error:
                    self.logger.error(
                        f'connection refused; attempting to reconnect after {self.timeout} '
                        f'seconds: {error}')
                    await asyncio.sleep(self.timeout)
                    continue
                except BaseException as error:
                    self.logger.error(
                        f'unknown connection error; attempting to reconnect after {self.timeout} '
                        f'seconds: {error}')
                    await asyncio.sleep(self.timeout)
                    continue

        asyncio.ensure_future(do_subscribe())
예제 #22
0
    async def _subscribe_trades_and_quotes(self):
        network = self.scheduler.get_network()

        symbols = []
        for instrument in self.get_instruments():
            symbol = instrument.get_exchange_instrument_code()
            if symbol == self.include_symbol or self.include_symbol == '*':
                symbols.append(f'{symbol}')

                self.instrument_trades[symbol] = MutableSignal()
                self.instrument_order_book_events[symbol] = MutableSignal()
                self.instrument_order_books[symbol] = OrderBookBuilder(
                    network, self.instrument_order_book_events[symbol])

                # magic: inject the bare Signal into the graph so we can
                # fire events on it without any downstream connections
                # yet made
                network.attach(self.instrument_trades[symbol])
                network.attach(self.instrument_order_book_events[symbol])
                network.attach(self.instrument_order_books[symbol])

        subscribe_msg = {
            'type': 'subscribe',
            'product_ids': symbols,
            'channels': ['matches', 'level2', 'heartbeat']
        }

        messages = MutableSignal()
        json_messages = Map(network, messages, lambda x: json.loads(x))
        match_messages = Filter(network, json_messages,
                                lambda x: x.get('type', None) == 'match')
        book_messages = Filter(
            network, json_messages,
            lambda x: x.get('type', None) in {'snapshot', 'l2update'})
        trades = Map(network, match_messages,
                     lambda x: self.__extract_trade(x))
        books = Map(network, book_messages,
                    lambda x: self.__extract_order_book_event(x))

        class TradeScheduler(Event):
            def __init__(self, fh: CoinbaseProFeedHandler):
                self.fh = fh

            def on_activate(self) -> bool:
                if trades.is_valid():
                    trade = trades.get_value()
                    trade_symbol = trade.get_instrument(
                    ).get_exchange_instrument_code()
                    trade_signal = self.fh.instrument_trades[trade_symbol]
                    self.fh.scheduler.schedule_update(trade_signal, trade)
                    return True
                else:
                    return False

        network.connect(trades, TradeScheduler(self))

        class OrderBookScheduler(Event):
            def __init__(self, fh: CoinbaseProFeedHandler):
                self.fh = fh

            def on_activate(self) -> bool:
                if books.is_valid():
                    obe = books.get_value()
                    obe_symbol = obe.get_instrument(
                    ).get_exchange_instrument_code()
                    obe_signal = self.fh.instrument_order_book_events[
                        obe_symbol]
                    self.fh.scheduler.schedule_update(obe_signal, obe)
                    return True
                else:
                    return False

        network.connect(books, OrderBookScheduler(self))
        asyncio.ensure_future(
            websocket_subscribe_with_retry(self.ws_uri, self.timeout,
                                           self.logger, subscribe_msg,
                                           self.scheduler, messages,
                                           'all products', 'global'))

        # we are now live
        self.scheduler.schedule_update(self.state, FeedHandlerState.LIVE)
예제 #23
0
    def start(self):
        network = self.scheduler.get_network()
        messages = MutableSignal()
        json_messages = Map(network, messages, lambda x: json.loads(x))
        json_messages = Filter(network, json_messages,
                               lambda x: x.get('type', None) == 'incremental')

        class OrderEventScheduler(Event):
            # noinspection PyShadowingNames
            def __init__(self, sub: OrderEventSubscriber,
                         json_messages: Signal):
                self.sub = sub
                self.json_messages = json_messages

            def on_activate(self) -> bool:
                if self.json_messages.is_valid():
                    msg = self.json_messages.get_value()
                    orders = msg['orders']
                    for order_msg in orders:
                        order_id = order_msg['orderID']
                        cl_ord_id = order_msg['clOrdID']
                        exec_id = order_msg['execID']
                        last_px = order_msg['execPriceEp'] / 10000
                        last_qty = order_msg['execQty']

                        order = self.sub.oms.get_order_by_cl_ord_id(cl_ord_id)
                        if order is None:
                            if cl_ord_id is None or cl_ord_id == '':
                                self.sub.logger.warning(
                                    f'Received message from exchange with missing clOrdID, '
                                    f'orderID={order_id}')
                            else:
                                self.sub.logger.warning(
                                    f'Received message from exchange for unknown '
                                    f'clOrdID={cl_ord_id}, orderID={order_id}')
                            return False
                        elif order.get_order_id() is None:
                            self.sub.logger.info(
                                f'OMS order missing orderID; patching from clOrdID={cl_ord_id}'
                            )
                            order.set_order_id(order_id)

                        if order_msg['ordStatus'] == 'New':
                            self.sub.oms.new(order, exec_id)
                        elif order_msg['ordStatus'] == 'Canceled':
                            self.sub.oms.apply_cancel(order, exec_id)
                        elif order_msg[
                                'ordStatus'] == 'PartiallyFilled' or order_msg[
                                    'ordStatus'] == 'Filled':
                            self.sub.oms.apply_fill(order, last_qty, last_px,
                                                    exec_id)

                    return True
                else:
                    return False

        network.connect(json_messages,
                        OrderEventScheduler(self, json_messages))

        # noinspection PyShadowingNames,PyBroadException
        async def do_subscribe():
            while True:
                try:
                    async with websockets.connect(self.ws_uri) as sock:
                        self.logger.info(
                            'sending Account-Order-Position subscription request for orders'
                        )
                        auth_msg = self.auth.get_user_auth_message(1)
                        await sock.send(auth_msg)
                        error_msg = await sock.recv()
                        error_struct = json.loads(error_msg)
                        if error_struct['error'] is not None:
                            raise ConnectionError(
                                f'Unable to authenticate: {error_msg}')

                        aop_sub_msg = {
                            'id': 2,
                            'method': 'aop.subscribe',
                            'params': []
                        }
                        await sock.send(json.dumps(aop_sub_msg))
                        while True:
                            try:
                                self.scheduler.schedule_update(
                                    messages, await sock.recv())
                            except BaseException as error:
                                self.logger.error(
                                    f'disconnected; attempting to reconnect after {self.timeout} '
                                    f'seconds: {error}')
                                await asyncio.sleep(self.timeout)

                                # exit inner loop
                                break

                except socket.gaierror as error:
                    self.logger.error(
                        f'failed with socket error; attempting to reconnect after {self.timeout} '
                        f'seconds: {error}')
                    await asyncio.sleep(self.timeout)
                    continue
                except ConnectionRefusedError as error:
                    self.logger.error(
                        f'connection refused; attempting to reconnect after {self.timeout} '
                        f'seconds: {error}')
                    await asyncio.sleep(self.timeout)
                    continue
                except BaseException as error:
                    self.logger.error(
                        f'unknown connection error; attempting to reconnect after {self.timeout} '
                        f'seconds: {error}')
                    await asyncio.sleep(self.timeout)
                    continue

        asyncio.ensure_future(do_subscribe())