Esempio n. 1
0
    async def _trade(self, msg: dict, timestamp: float):
        """
        example message:

        {"channel": "trades", "market": "BTC-PERP", "type": "update", "data": [{"id": null, "price": 10738.75,
        "size": 0.3616, "side": "buy", "liquidation": false, "time": "2019-08-03T12:20:19.170586+00:00"}]}
        """
        for trade in msg['data']:
            await self.callback(TRADES, feed=self.id,
                                symbol=symbol_exchange_to_std(msg['market']),
                                side=BUY if trade['side'] == 'buy' else SELL,
                                amount=Decimal(trade['size']),
                                price=Decimal(trade['price']),
                                order_id=None,
                                timestamp=float(timestamp_normalize(self.id, trade['time'])),
                                receipt_timestamp=timestamp)
            if bool(trade['liquidation']):
                await self.callback(LIQUIDATIONS,
                                    feed=self.id,
                                    symbol=symbol_exchange_to_std(msg['market']),
                                    side=BUY if trade['side'] == 'buy' else SELL,
                                    leaves_qty=Decimal(trade['size']),
                                    price=Decimal(trade['price']),
                                    order_id=None,
                                    timestamp=float(timestamp_normalize(self.id, trade['time'])),
                                    receipt_timestamp=timestamp
                                    )
Esempio n. 2
0
    async def _trade(self, msg: dict, timestamp: float):
        """
        example message:

        {"channel": "trades", "market": "BTC-PERP", "type": "update", "data": [{"id": null, "price": 10738.75,
        "size": 0.3616, "side": "buy", "liquidation": false, "time": "2019-08-03T12:20:19.170586+00:00"}]}
        """

        # THis part is for merging trades which are supposed to be from same taker order
        reconst_trades = []
        for trade in msg['data']:

            inl = in_dictlist('time', trade['time'], reconst_trades)
            if inl is None:
                trade['fills'] = 1
                reconst_trades.append(trade)
            else:
                reconst_trades[inl]['size'] = Decimal(
                    reconst_trades[inl]['size']) + Decimal(trade['size'])
                reconst_trades[inl]['fills'] = reconst_trades[inl]['fills'] + 1
                # reconst_trades[inl]['amount'] = reconst_trades[inl]['amount'] + trade['amount']

        for trade in reconst_trades:
            await self.callback(
                TRADES,
                feed=self.id,
                pair=symbol_exchange_to_std(msg['market']),
                side=BUY if trade['side'] == 'buy' else SELL,
                amount=Decimal(trade['size']),
                price=Decimal(trade['price']),
                order_id=None,
                timestamp=float(timestamp_normalize(self.id, trade['time'])),
                fills=trade['fills'],
                recons=True,
                receipt_timestamp=timestamp)
            '''
            await self.callback(TRADES, feed=self.id,
                                pair=pair_exchange_to_std(msg['market']),
                                side=BUY if trade['side'] == 'buy' else SELL,
                                amount=Decimal(trade['size']),
                                price=Decimal(trade['price']),
                                order_id=None,
                                timestamp=float(timestamp_normalize(self.id, trade['time'])),
                                receipt_timestamp=timestamp)'''

            if bool(trade['liquidation']):
                await self.callback(
                    LIQUIDATIONS,
                    feed=self.id,
                    pair=symbol_exchange_to_std(msg['market']),
                    side=BUY if trade['side'] == 'buy' else SELL,
                    leaves_qty=Decimal(trade['size']),
                    price=Decimal(trade['price']),
                    order_id=None,
                    timestamp=float(timestamp_normalize(
                        self.id, trade['time'])),
                    receipt_timestamp=timestamp)
Esempio n. 3
0
    async def _book(self, msg: dict, timestamp: float):
        """
        example messages:

        snapshot:
        {"channel": "orderbook", "market": "BTC/USD", "type": "partial", "data": {"time": 1564834586.3382702,
        "checksum": 427503966, "bids": [[10717.5, 4.092], ...], "asks": [[10720.5, 15.3458], ...], "action": "partial"}}

        update:
        {"channel": "orderbook", "market": "BTC/USD", "type": "update", "data": {"time": 1564834587.1299787,
        "checksum": 3115602423, "bids": [], "asks": [[10719.0, 14.7461]], "action": "update"}}
        """
        check = msg['data']['checksum']
        if msg['type'] == 'partial':
            # snapshot
            pair = symbol_exchange_to_std(msg['market'])
            self.l2_book[pair] = {
                BID:
                sd({
                    Decimal(price): Decimal(amount)
                    for price, amount in msg['data']['bids']
                }),
                ASK:
                sd({
                    Decimal(price): Decimal(amount)
                    for price, amount in msg['data']['asks']
                })
            }
            if self.checksum_validation and self.__calc_checksum(
                    pair) != check:
                raise BadChecksum
            await self.book_callback(self.l2_book[pair], L2_BOOK,
                                     pair, True, None,
                                     float(msg['data']['time']), timestamp)
        else:
            # update
            delta = {BID: [], ASK: []}
            pair = symbol_exchange_to_std(msg['market'])
            for side in ('bids', 'asks'):
                s = BID if side == 'bids' else ASK
                for price, amount in msg['data'][side]:
                    price = Decimal(price)
                    amount = Decimal(amount)
                    if amount == 0:
                        delta[s].append((price, 0))
                        del self.l2_book[pair][s][price]
                    else:
                        delta[s].append((price, amount))
                        self.l2_book[pair][s][price] = amount
            if self.checksum_validation and self.__calc_checksum(
                    pair) != check:
                raise BadChecksum
            await self.book_callback(self.l2_book[pair], L2_BOOK,
                                     pair, False, delta,
                                     float(msg['data']['time']), timestamp)
Esempio n. 4
0
    async def _book_update(self, msg: dict, timestamp: float):
        '''
        {
            'type': 'match', or last_match
            'trade_id': 43736593
            'maker_order_id': '2663b65f-b74e-4513-909d-975e3910cf22',
            'taker_order_id': 'd058d737-87f1-4763-bbb4-c2ccf2a40bde',
            'side': 'buy',
            'size': '0.01235647',
            'price': '8506.26000000',
            'product_id': 'BTC-USD',
            'sequence': 5928276661,
            'time': '2018-05-21T00:26:05.585000Z'
        }
        '''
        pair = symbol_exchange_to_std(msg['product_id'])
        ts = timestamp_normalize(self.id, msg['time'])

        if self.keep_l3_book and ('full' in self.channels or ('full' in self.subscription and pair in self.subscription['full'])):
            delta = {BID: [], ASK: []}
            price = Decimal(msg['price'])
            side = ASK if msg['side'] == 'sell' else BID
            size = Decimal(msg['size'])
            maker_order_id = msg['maker_order_id']

            _, new_size = self.order_map[maker_order_id]
            new_size -= size
            if new_size <= 0:
                del self.order_map[maker_order_id]
                self.order_type_map.pop(maker_order_id, None)
                delta[side].append((maker_order_id, price, 0))
                del self.l3_book[pair][side][price][maker_order_id]
                if len(self.l3_book[pair][side][price]) == 0:
                    del self.l3_book[pair][side][price]
            else:
                self.order_map[maker_order_id] = (price, new_size)
                self.l3_book[pair][side][price][maker_order_id] = new_size
                delta[side].append((maker_order_id, price, new_size))

            await self.book_callback(self.l3_book[pair], L3_BOOK, pair, False, delta, ts, timestamp)

        order_type = self.order_type_map.get(msg['taker_order_id'])
        await self.callback(TRADES,
                            feed=self.id,
                            symbol=symbol_exchange_to_std(msg['product_id']),
                            order_id=msg['trade_id'],
                            side=SELL if msg['side'] == 'buy' else BUY,
                            amount=Decimal(msg['size']),
                            price=Decimal(msg['price']),
                            timestamp=ts,
                            receipt_timestamp=timestamp,
                            order_type=order_type
                            )
Esempio n. 5
0
    async def _book(self, msg: dict, timestamp: float):
        if msg['action'] == 'partial':
            # snapshot
            for update in msg['data']:
                pair = symbol_exchange_to_std(update['instrument_id'])
                self.l2_book[pair] = {
                    BID:
                    sd({
                        Decimal(price): Decimal(amount)
                        for price, amount, *_ in update['bids']
                    }),
                    ASK:
                    sd({
                        Decimal(price): Decimal(amount)
                        for price, amount, *_ in update['asks']
                    })
                }

                if self.checksum_validation and self.__calc_checksum(pair) != (
                        update['checksum'] & 0xFFFFFFFF):
                    raise BadChecksum
                await self.book_callback(
                    self.l2_book[pair], L2_BOOK, pair, True, None,
                    timestamp_normalize(self.id, update['timestamp']),
                    timestamp)
        else:
            # update
            for update in msg['data']:
                delta = {BID: [], ASK: []}
                pair = symbol_exchange_to_std(update['instrument_id'])
                for side in ('bids', 'asks'):
                    s = BID if side == 'bids' else ASK
                    for price, amount, *_ in update[side]:
                        price = Decimal(price)
                        amount = Decimal(amount)
                        if amount == 0:
                            if price in self.l2_book[pair][s]:
                                delta[s].append((price, 0))
                                del self.l2_book[pair][s][price]
                        else:
                            delta[s].append((price, amount))
                            self.l2_book[pair][s][price] = amount
                if self.checksum_validation and self.__calc_checksum(pair) != (
                        update['checksum'] & 0xFFFFFFFF):
                    raise BadChecksum
                await self.book_callback(
                    self.l2_book[pair], L2_BOOK, pair, False, delta,
                    timestamp_normalize(self.id, update['timestamp']),
                    timestamp)
Esempio n. 6
0
 async def _trade(self, msg: dict, timestamp: float):
     """
     {
         "params":
         {
             "data":
             [
                 {
                     "trade_seq": 933,
                     "trade_id": "9178",
                     "timestamp": 1550736299753,
                     "tick_direction": 3,
                     "price": 3948.69,
                     "instrument_name": "BTC-PERPETUAL",
                     "index_price": 3930.73,
                     "direction": "sell",
                     "amount": 10
                 }
             ],
             "channel": "trades.BTC-PERPETUAL.raw"
         },
         "method": "subscription",
         "jsonrpc": "2.0"
     }
     """
     for trade in msg["params"]["data"]:
         await self.callback(
             TRADES,
             feed=self.id,
             symbol=symbol_exchange_to_std(trade["instrument_name"]),
             order_id=trade['trade_id'],
             side=BUY if trade['direction'] == 'buy' else SELL,
             amount=Decimal(trade['amount']),
             price=Decimal(trade['price']),
             timestamp=timestamp_normalize(self.id, trade['timestamp']),
             receipt_timestamp=timestamp,
         )
         if 'liquidation' in trade:
             await self.callback(
                 LIQUIDATIONS,
                 feed=self.id,
                 symbol=symbol_exchange_to_std(trade["instrument_name"]),
                 side=BUY if trade['direction'] == 'buy' else SELL,
                 leaves_qty=Decimal(trade['amount']),
                 price=Decimal(trade['price']),
                 order_id=trade['trade_id'],
                 timestamp=timestamp_normalize(self.id, trade['timestamp']),
                 receipt_timestamp=timestamp)
Esempio n. 7
0
    async def _trade(self, msg: dict, timestamp: float):
        """
        Doc : https://docs.upbit.com/v1.0.7/reference#시세-체결-조회

        {
            'ty': 'trade'             // Event type
            'cd': 'KRW-BTC',          // Symbol
            'tp': 6759000.0,          // Trade Price
            'tv': 0.03243003,         // Trade volume(amount)
            'tms': 1584257228806,     // Timestamp
            'ttms': 1584257228000,    // Trade Timestamp
            'ab': 'BID',              // 'BID' or 'ASK'
            'cp': 64000.0,            // Change of price
            'pcp': 6823000.0,         // Previous closing price
            'sid': 1584257228000000,  // Sequential ID
            'st': 'SNAPSHOT',         // 'SNAPSHOT' or 'REALTIME'
            'td': '2020-03-15',       // Trade date utc
            'ttm': '07:27:08',        // Trade time utc
            'c': 'FALL',              // Change - 'FALL' / 'RISE' / 'EVEN'
        }
        """

        price = Decimal(msg['tp'])
        amount = Decimal(msg['tv'])
        await self.callback(TRADES, feed=self.id,
                            order_id=msg['sid'],
                            symbol=symbol_exchange_to_std(msg['cd']),
                            side=BUY if msg['ab'] == 'BID' else SELL,
                            amount=amount,
                            price=price,
                            timestamp=timestamp_normalize(self.id, msg['ttms']),
                            receipt_timestamp=timestamp)
Esempio n. 8
0
    async def _snapshot(self, pairs: list):
        await asyncio.sleep(5)
        urls = [
            f'https://www.bitstamp.net/api/v2/order_book/{sym}'
            for sym in pairs
        ]

        async def fetch(session, url):
            async with session.get(url) as response:
                response.raise_for_status()
                return await response.json()

        async with aiohttp.ClientSession() as session:
            results = await asyncio.gather(
                *[fetch(session, url) for url in urls])

        for r, pair in zip(results, pairs):
            std_pair = symbol_exchange_to_std(pair) if pair else 'BTC-USD'
            self.last_update_id[std_pair] = r['timestamp']
            self.l2_book[std_pair] = {BID: sd(), ASK: sd()}
            for s, side in (('bids', BID), ('asks', ASK)):
                for update in r[s]:
                    price = Decimal(update[0])
                    amount = Decimal(update[1])
                    self.l2_book[std_pair][side][price] = amount
 async def _ticker(self, msg: dict, timestamp: float):
     """
     https://binance-docs.github.io/apidocs/delivery/cn/#symbol
     {
       "e":"bookTicker",         // 事件类型
       "u":17242169,             // 更新ID
       "s":"BTCUSD_200626",      // 交易对
       "ps":"BTCUSD",                 // 标的交易对
       "b":"9548.1",             // 买单最优挂单价格
       "B":"52",                 // 买单最优挂单数量
       "a":"9548.5",             // 卖单最优挂单价格
       "A":"11",                 // 卖单最优挂单数量
       "T":1591268628155,        // 撮合时间
       "E":1591268628166         // 事件时间
     }
     """
     pair = symbol_exchange_to_std(msg['s'])
     bid = Decimal(msg['b'])
     ask = Decimal(msg['a'])
     await self.callback(TICKER, feed=self.id,
                         symbol=pair,
                         bid=bid,
                         ask=ask,
                         bid_amount=Decimal(msg["B"]),
                         ask_amount=Decimal(msg["A"]),
                         timestamp=timestamp_normalize(self.id, msg['E']),
                         receipt_timestamp=timestamp)
Esempio n. 10
0
 def _reset(self):
     self.partial_received = defaultdict(bool)
     self.order_id = {}
     for pair in self.normalized_symbols:
         pair = symbol_exchange_to_std(pair)
         self.l2_book[pair] = {BID: sd(), ASK: sd()}
         self.order_id[pair] = defaultdict(dict)
Esempio n. 11
0
    async def _ticker(self, msg: dict, timestamp: float):
        """
        {
            'u': 382569232,
            's': 'FETUSDT',
            'b': '0.36031000',
            'B': '1500.00000000',
            'a': '0.36092000',
            'A': '176.40000000'
        }
        """
        pair = symbol_exchange_to_std(msg['s'])
        bid = Decimal(msg['b'])
        ask = Decimal(msg['a'])

        # Binance does not have a timestamp in this update, but the two futures APIs do
        if 'E' in msg:
            ts = timestamp_normalize(self.id, msg['E'])
        else:
            ts = timestamp

        await self.callback(TICKER,
                            feed=self.id,
                            symbol=pair,
                            bid=bid,
                            ask=ask,
                            timestamp=ts,
                            receipt_timestamp=timestamp)
Esempio n. 12
0
    async def _book(self, msg: dict, timestamp: float):
        pair = symbol_exchange_to_std(msg['symbol'])
        # Gemini sends ALL data for the symbol, so if we don't actually want
        # the book data, bail before parsing
        if self.channels and L2_BOOK not in self.channels:
            return
        if self.subscription and ((L2_BOOK in self.subscription and msg['symbol'] not in self.subscription[L2_BOOK]) or L2_BOOK not in self.subscription):
            return

        data = msg['changes']
        forced = not len(self.l2_book[pair][BID])
        delta = {BID: [], ASK: []}
        for entry in data:
            side = ASK if entry[0] == 'sell' else BID
            price = Decimal(entry[1])
            amount = Decimal(entry[2])
            if amount == 0:
                if price in self.l2_book[pair][side]:
                    del self.l2_book[pair][side][price]
                    delta[side].append((price, 0))
            else:
                self.l2_book[pair][side][price] = amount
                delta[side].append((price, amount))

        await self.book_callback(self.l2_book[pair], L2_BOOK, pair, forced, delta, timestamp, timestamp)
Esempio n. 13
0
    async def _book_update(self, msg: dict, timestamp: float):
        ts = msg["params"]["data"]["timestamp"]
        pair = symbol_exchange_to_std(msg["params"]["data"]["instrument_name"])

        if msg['params']['data']['prev_change_id'] != self.seq_no[pair]:
            LOG.warning("%s: Missing sequence number detected for %s", self.id,
                        pair)
            LOG.warning("%s: Requesting book snapshot", self.id)
            raise MissingSequenceNumber

        self.seq_no[pair] = msg['params']['data']['change_id']

        delta = {BID: [], ASK: []}

        for action, price, amount in msg["params"]["data"]["bids"]:
            bidask = self.l2_book[pair][BID]
            if action != "delete":
                bidask[price] = Decimal(amount)
                delta[BID].append((Decimal(price), Decimal(amount)))
            else:
                del bidask[price]
                delta[BID].append((Decimal(price), Decimal(amount)))

        for action, price, amount in msg["params"]["data"]["asks"]:
            bidask = self.l2_book[pair][ASK]
            if action != "delete":
                bidask[price] = amount
                delta[ASK].append((Decimal(price), Decimal(amount)))
            else:
                del bidask[price]
                delta[ASK].append((Decimal(price), Decimal(amount)))
        await self.book_callback(self.l2_book[pair], L2_BOOK,
                                 pair, False, delta,
                                 timestamp_normalize(self.id, ts), timestamp)
Esempio n. 14
0
 async def _trade(self, msg: dict, timestamp: float):
     """
     {
         "jsonrpc":"2.0",
         "method":"channelMessage",
         "params":{
             "channel":"lightning_executions_BTC_JPY",
             "message":[
                 {
                     "id":2084881071,
                     "side":"BUY",
                     "price":2509125.0,
                     "size":0.005,
                     "exec_date":"2020-12-25T21:36:22.8840579Z",
                     "buy_child_order_acceptance_id":"JRF20201225-213620-004123",
                     "sell_child_order_acceptance_id":"JRF20201225-213620-133314"
                 }
             ]
         }
     }
     """
     pair = msg['params']['channel'][21:]
     pair = symbol_exchange_to_std(pair)
     for update in msg['params']['message']:
         await self.callback(TRADES,
                             feed=self.id,
                             order_id=update['id'],
                             symbol=pair,
                             side=BUY if update['side'] == 'BUY' else SELL,
                             amount=update['size'],
                             price=update['price'],
                             timestamp=timestamp_normalize(
                                 self.id, update['exec_date']),
                             receipt_timestamp=timestamp)
Esempio n. 15
0
    async def _pair_l3_update(self, msg: str, timestamp: float):
        delta = {BID: [], ASK: []}
        pair = symbol_exchange_to_std(msg['symbol'])

        if msg['event'] == 'snapshot':
            # Reset the book
            self.l3_book[pair] = {BID: sd(), ASK: sd()}

        book = self.l3_book[pair]

        for side in (BID, ASK):
            for update in msg[side + 's']:
                price = update['px']
                qty = update['qty']
                order_id = update['id']

                p_orders = book[side].get(price, sd())
                p_orders[order_id] = qty
                if qty <= 0:
                    del p_orders[order_id]

                book[side][price] = p_orders
                if len(book[side][price]) == 0:
                    del book[side][price]

                delta[side].append((order_id, price, qty))

        self.l3_book[pair] = book

        await self.book_callback(self.l3_book[pair], L3_BOOK, pair, False, delta, timestamp, timestamp)
Esempio n. 16
0
 async def _trade(self, msg: dict, timestamp: float):
     """
     {
         'ch': 'market.adausdt.trade.detail',
         'ts': 1597792835344,
         'tick': {
             'id': 101801945127,
             'ts': 1597792835336,
             'data': [
                 {
                     'id': Decimal('10180194512782291967181675'),   <- per docs this is deprecated
                     'ts': 1597792835336,
                     'tradeId': 100341530602,
                     'amount': Decimal('0.1'),
                     'price': Decimal('0.137031'),
                     'direction': 'sell'
                 }
             ]
         }
     }
     """
     for trade in msg['tick']['data']:
         await self.callback(
             TRADES,
             feed=self.id,
             symbol=symbol_exchange_to_std(msg['ch'].split('.')[1]),
             order_id=trade['tradeId'],
             side=BUY if trade['direction'] == 'buy' else SELL,
             amount=Decimal(trade['amount']),
             price=Decimal(trade['price']),
             timestamp=timestamp_normalize(self.id, trade['ts']),
             receipt_timestamp=timestamp)
Esempio n. 17
0
 async def _ticker(self, msg: dict, timestamp: float):
     '''
     https://huobiapi.github.io/docs/spot/v1/cn/#9d97b30872
     {
       "ch": "market.btcusdt.bbo",
       "ts": 1489474082831, //system update time
       "tick": {
         "symbol": "btcusdt",
         "quoteTime": "1489474082811",
         "bid": "10008.31",
         "bidSize": "0.01",
         "ask": "10009.54",
         "askSize": "0.3",
         "seqId":"10242474683"
       }
     }
     '''
     await self.callback(TICKER,
                         feed=self.id,
                         symbol=symbol_exchange_to_std(
                             msg['ch'].split('.')[1]),
                         bid=Decimal(msg['tick']['bid']),
                         bid_amount=Decimal(msg['tick']['bidSize']),
                         ask=Decimal(msg['tick']['ask']),
                         ask_amount=Decimal(msg['tick']['askSize']),
                         timestamp=timestamp_normalize(self.id, msg['ts']),
                         receipt_timestamp=timestamp)
Esempio n. 18
0
    async def _book_snapshot(self, pairs: list):
        # Coinbase needs some time to send messages to us
        # before we request the snapshot. If we don't sleep
        # the snapshot seq no could be much earlier than
        # the subsequent messages, causing a seq no mismatch.
        await asyncio.sleep(2)

        url = 'https://api.pro.coinbase.com/products/{}/book?level=3'
        urls = [url.format(pair) for pair in pairs]

        results = []
        for url in urls:
            ret = requests.get(url)
            results.append(ret)
            # rate limit - 3 per second
            await asyncio.sleep(0.3)

        timestamp = time.time()
        for res, pair in zip(results, pairs):
            orders = res.json()
            npair = symbol_exchange_to_std(pair)
            self.l3_book[npair] = {BID: sd(), ASK: sd()}
            self.seq_no[npair] = orders['sequence']
            for side in (BID, ASK):
                for price, size, order_id in orders[side + 's']:
                    price = Decimal(price)
                    size = Decimal(size)
                    if price in self.l3_book[npair][side]:
                        self.l3_book[npair][side][price][order_id] = size
                    else:
                        self.l3_book[npair][side][price] = {order_id: size}
                    self.order_map[order_id] = (price, size)
            await self.book_callback(self.l3_book[npair], L3_BOOK, npair, True, None, timestamp, timestamp)
Esempio n. 19
0
 async def _trade(self, msg: dict, timestamp: float):
     """
     {
         "e": "aggTrade",  // Event type
         "E": 123456789,   // Event time
         "s": "BNBBTC",    // Symbol
         "a": 12345,       // Aggregate trade ID
         "p": "0.001",     // Price
         "q": "100",       // Quantity
         "f": 100,         // First trade ID
         "l": 105,         // Last trade ID
         "T": 123456785,   // Trade time
         "m": true,        // Is the buyer the market maker?
         "M": true         // Ignore
     }
     """
     price = Decimal(msg['p'])
     amount = Decimal(msg['q'])
     await self.callback(TRADES,
                         feed=self.id,
                         order_id=msg['a'],
                         symbol=symbol_exchange_to_std(msg['s']),
                         side=SELL if msg['m'] else BUY,
                         amount=amount,
                         price=price,
                         timestamp=timestamp_normalize(self.id, msg['E']),
                         receipt_timestamp=timestamp)
Esempio n. 20
0
    async def _done(self, msg: dict, timestamp: float):
        """
        per Coinbase API Docs:

        A done message will be sent for received orders which are fully filled or canceled due
        to self-trade prevention. There will be no open message for such orders. Done messages
        for orders which are not on the book should be ignored when maintaining a real-time order book.
        """
        if 'price' not in msg:
            return

        order_id = msg['order_id']
        self.order_type_map.pop(order_id, None)
        if order_id not in self.order_map:
            return

        del self.order_map[order_id]
        if self.keep_l3_book:
            delta = {BID: [], ASK: []}

            price = Decimal(msg['price'])
            side = ASK if msg['side'] == 'sell' else BID
            pair = symbol_exchange_to_std(msg['product_id'])
            ts = timestamp_normalize(self.id, msg['time'])

            del self.l3_book[pair][side][price][order_id]
            if len(self.l3_book[pair][side][price]) == 0:
                del self.l3_book[pair][side][price]
            delta[side].append((order_id, price, 0))

            await self.book_callback(self.l3_book[pair], L3_BOOK, pair, False, delta, ts, timestamp)
Esempio n. 21
0
 async def _liquidations(self, msg: dict, timestamp: float):
     """
     {
     "e":"forceOrder",       // Event Type
     "E":1568014460893,      // Event Time
     "o":{
         "s":"BTCUSDT",      // Symbol
         "S":"SELL",         // Side
         "o":"LIMIT",        // Order Type
         "f":"IOC",          // Time in Force
         "q":"0.014",        // Original Quantity
         "p":"9910",         // Price
         "ap":"9910",        // Average Price
         "X":"FILLED",       // Order Status
         "l":"0.014",        // Order Last Filled Quantity
         "z":"0.014",        // Order Filled Accumulated Quantity
         "T":1568014460893,  // Order Trade Time
         }
     }
     """
     pair = symbol_exchange_to_std(msg['o']['s'])
     await self.callback(LIQUIDATIONS,
                         feed=self.id,
                         symbol=pair,
                         side=msg['o']['S'],
                         leaves_qty=Decimal(msg['o']['q']),
                         price=Decimal(msg['o']['p']),
                         order_id=None,
                         timestamp=timestamp_normalize(self.id, msg['E']),
                         receipt_timestamp=timestamp)
Esempio n. 22
0
    async def _change(self, msg: dict, timestamp: float):
        """
        Like done, these updates can be sent for orders that are not in the book. Per the docs:

        Not all done or change messages will result in changing the order book. These messages will
        be sent for received orders which are not yet on the order book. Do not alter
        the order book for such messages, otherwise your order book will be incorrect.
        """
        if not self.keep_l3_book:
            return

        delta = {BID: [], ASK: []}

        if 'price' not in msg or not msg['price']:
            return

        order_id = msg['order_id']
        if order_id not in self.order_map:
            return

        ts = timestamp_normalize(self.id, msg['time'])
        price = Decimal(msg['price'])
        side = ASK if msg['side'] == 'sell' else BID
        new_size = Decimal(msg['new_size'])
        pair = symbol_exchange_to_std(msg['product_id'])

        self.l3_book[pair][side][price][order_id] = new_size
        self.order_map[order_id] = (price, new_size)

        delta[side].append((order_id, price, new_size))

        await self.book_callback(self.l3_book, L3_BOOK, pair, False, delta, ts, timestamp)
Esempio n. 23
0
    async def _trade(self, msg: dict, timestamp: float):
        """
        trade msg example

        {
            'timestamp': '2018-05-19T12:25:26.632Z',
            'symbol': 'XBTUSD',
            'side': 'Buy',
            'size': 40,
            'price': 8335,
            'tickDirection': 'PlusTick',
            'trdMatchID': '5f4ecd49-f87f-41c0-06e3-4a9405b9cdde',
            'grossValue': 479920,
            'homeNotional': Decimal('0.0047992'),
            'foreignNotional': 40
        }
        """
        for data in msg['data']:
            ts = timestamp_normalize(self.id, data['timestamp'])
            await self.callback(TRADES,
                                feed=self.id,
                                symbol=symbol_exchange_to_std(data['symbol']),
                                side=BUY if data['side'] == 'Buy' else SELL,
                                amount=Decimal(data['size']),
                                price=Decimal(data['price']),
                                order_id=data['trdMatchID'],
                                timestamp=ts,
                                receipt_timestamp=timestamp)
Esempio n. 24
0
    async def message_handler(self, msg: str, conn, timestamp: float):

        msg = json.loads(msg, parse_float=Decimal)

        if 'event' in msg:
            if msg['event'] == 'info':
                return
            elif msg['event'] == 'subscribed':
                return
            else:
                LOG.warning("%s: Invalid message type %s", self.id, msg)
        else:
            pair = symbol_exchange_to_std(msg['product_id'])
            if msg['feed'] == 'trade':
                await self._trade(msg, pair, timestamp)
            elif msg['feed'] == 'trade_snapshot':
                return
            elif msg['feed'] == 'ticker_lite':
                await self._ticker(msg, pair, timestamp)
            elif msg['feed'] == 'ticker':
                await self._funding(msg, pair, timestamp)
            elif msg['feed'] == 'book_snapshot':
                await self._book_snapshot(msg, pair, timestamp)
            elif msg['feed'] == 'book':
                await self._book(msg, pair, timestamp)
            else:
                LOG.warning("%s: Invalid message type %s", self.id, msg)
Esempio n. 25
0
    async def _book(self, msg: dict, timestamp: float):
        pair = symbol_exchange_to_std(msg['ch'].split('.')[1])
        data = msg['tick']
        forced = pair not in self.l2_book

        update = {
            BID:
            sd({
                Decimal(price): Decimal(amount)
                for price, amount in data['bids']
            }),
            ASK:
            sd({
                Decimal(price): Decimal(amount)
                for price, amount in data['asks']
            })
        }

        if not forced:
            self.previous_book[pair] = self.l2_book[pair]
        self.l2_book[pair] = update

        await self.book_callback(self.l2_book[pair], L2_BOOK, pair,
                                 forced, False,
                                 timestamp_normalize(self.id,
                                                     msg['ts']), timestamp)
Esempio n. 26
0
    async def _open_interest(self, pairs: Iterable):
        """
        {
            "openInterest": "10659.509",
            "symbol": "BTCUSDT",
            "time": 1589437530011   // Transaction time
        }
        """

        rate_limiter = 2  # don't fetch too many pairs too fast
        async with aiohttp.ClientSession() as session:
            while True:
                for pair in pairs:
                    end_point = f"{self.rest_endpoint}/openInterest?symbol={pair}"
                    async with session.get(end_point) as response:
                        data = await response.text()
                        data = json.loads(data, parse_float=Decimal)

                        oi = data['openInterest']
                        if oi != self.open_interest.get(pair, None):
                            await self.callback(
                                OPEN_INTEREST,
                                feed=self.id,
                                symbol=symbol_exchange_to_std(pair),
                                open_interest=oi,
                                timestamp=timestamp_normalize(
                                    self.id, data['time']),
                                receipt_timestamp=time())
                            self.open_interest[pair] = oi
                            await asyncio.sleep(rate_limiter)
                # Binance updates OI every 15 minutes, however not all pairs are ready exactly at :15 :30 :45 :00
                wait_time = (17 - (datetime.now().minute % 15)) * 60
                await asyncio.sleep(wait_time)
Esempio n. 27
0
    def register_channel_handler(self, msg: dict, conn: AsyncConnection):
        symbol = msg['symbol']
        is_funding = (symbol[0] == 'f')
        pair = symbol_exchange_to_std(symbol)

        if msg['channel'] == 'ticker':
            if is_funding:
                LOG.warning('%s %s: Ticker funding not implemented - set _do_nothing() for %s', conn.uuid, pair, msg)
                handler = self._do_nothing
            else:
                handler = partial(self._ticker, pair)
        elif msg['channel'] == 'trades':
            if is_funding:
                handler = partial(self._funding, pair)
            else:
                handler = partial(self._trades, pair)
        elif msg['channel'] == 'book':
            if msg['prec'] == 'R0':
                handler = partial(self._raw_book, pair, self.l3_book[pair], self.order_map[pair])
            elif is_funding:
                LOG.warning('%s %s: Book funding not implemented - set _do_nothing() for %s', conn.uuid, pair, msg)
                handler = self._do_nothing
            else:
                handler = partial(self._book, pair, self.l2_book[pair])
        else:
            LOG.warning('%s %s: Unexpected message %s', conn.uuid, pair, msg)
            return

        LOG.debug('%s: Register channel=%s pair=%s funding=%s %s -> %s()', conn.uuid, msg['channel'], pair, is_funding,
                  '='.join(list(msg.items())[-1]), handler.__name__ if hasattr(handler, '__name__') else handler.func.__name__)
        self.handlers[msg['chanId']] = handler
Esempio n. 28
0
    async def message_handler(self, msg: str, conn, timestamp: float):

        msg = json.loads(msg, parse_float=Decimal)

        if isinstance(msg, list):
            channel_id = msg[0]
            if channel_id not in self.channel_map:
                LOG.warning("%s: Invalid channel id received %d", self.id,
                            channel_id)
                LOG.warning("%s: channel map: %s", self.id, self.channel_map)
            else:
                channel, pair = self.channel_map[channel_id]
                if channel == 'trade':
                    await self._trade(msg, pair, timestamp)
                elif channel == 'ticker':
                    await self._ticker(msg, pair, timestamp)
                elif channel == 'book':
                    await self._book(msg, pair, timestamp)
                else:
                    LOG.warning("%s: No mapping for message %s", self.id, msg)
                    LOG.warning("%s: channel map: %s", self.id,
                                self.channel_map)
        else:
            if msg['event'] == 'heartbeat':
                return
            elif msg['event'] == 'systemStatus':
                return
            elif msg['event'] == 'subscriptionStatus' and msg[
                    'status'] == 'subscribed':
                self.channel_map[msg['channelID']] = (
                    msg['subscription']['name'],
                    symbol_exchange_to_std(msg['pair']))
            else:
                LOG.warning("%s: Invalid message type %s", self.id, msg)
Esempio n. 29
0
    async def _l2_book(self, msg: dict, timestamp: float):
        data = msg['data']
        chan = msg['channel']
        ts = int(data['microtimestamp'])
        pair = symbol_exchange_to_std(chan.split('_')[-1])
        forced = False
        delta = {BID: [], ASK: []}

        if pair in self.last_update_id:
            if data['timestamp'] < self.last_update_id[pair]:
                return
            else:
                forced = True
                del self.last_update_id[pair]

        for side in (BID, ASK):
            for update in data[side + 's']:
                price = Decimal(update[0])
                size = Decimal(update[1])

                if size == 0:
                    if price in self.l2_book[pair][side]:
                        del self.l2_book[pair][side][price]
                        delta[side].append((price, size))
                else:
                    self.l2_book[pair][side][price] = size
                    delta[side].append((price, size))

        await self.book_callback(self.l2_book[pair], L2_BOOK,
                                 pair, forced, delta,
                                 timestamp_normalize(self.id, ts), timestamp)
Esempio n. 30
0
    async def _book(self, msg: dict, timestamp: float):
        sequence_number = msg['data']['seqnum']
        pair = symbol_exchange_to_std(msg['symbol'])
        delta = {BID: [], ASK: []}
        forced = False

        if msg['m'] == 'depth-snapshot':
            forced = True
            self.seq_no[pair] = sequence_number
            self.l2_book[pair] = {BID: sd(), ASK: sd()}
        else:
            # ignore messages while we wait for the snapshot
            if self.seq_no[pair] is None:
                return
            if self.seq_no[pair] + 1 != sequence_number:
                raise MissingSequenceNumber
            self.seq_no[pair] = sequence_number

        for side in ('bids', 'asks'):
            for price, amount in msg['data'][side]:
                s = BID if side == 'bids' else ASK
                price = Decimal(price)
                size = Decimal(amount)
                if size == 0:
                    delta[s].append((price, 0))
                    if price in self.l2_book[pair][s]:
                        del self.l2_book[pair][s][price]
                else:
                    delta[s].append((price, size))
                    self.l2_book[pair][s][price] = size

        await self.book_callback(
            self.l2_book[pair], L2_BOOK, pair, forced, delta,
            timestamp_normalize(self.id, msg['data']['ts']), timestamp)