Example #1
0
def test_book_delta_simple():
    a = {BID: {1.0: 1, 0.9: 0.5, 0.8: 2}, ASK: {1.1: 1.1, 1.2: 0.6, 1.3: 2.1}}

    b = {BID: {0.9: 0.5, 0.8: 2}, ASK: {1.1: 1.1, 1.2: 0.6, 1.3: 2.1}}

    assert book_delta(a, b) == {'bid': [(1.0, 0)], 'ask': []}
    assert book_delta(b, a) == {'bid': [(1.0, 1)], 'ask': []}
Example #2
0
def test_book_delta_empty():
    a = {BID: {1.0: 1, 0.9: 0.5, 0.8: 2}, ASK: {1.1: 1.1, 1.2: 0.6, 1.3: 2.1}}
    b = {BID: {}, ASK: {}}

    assert book_delta(a, b) == {
        'bid': [(0.9, 0), (1.0, 0), (0.8, 0)],
        'ask': [(1.2, 0), (1.1, 0), (1.3, 0)]
    }
    assert book_delta(b, a) == {
        'ask': [(1.2, 0.6), (1.1, 1.1), (1.3, 2.1)],
        'bid': [(0.9, 0.5), (1.0, 1), (0.8, 2)]
    }
Example #3
0
    async def book_callback(self, book, book_type, pair, forced, delta,
                            timestamp):
        """
        Three cases we need to handle here

        1.  Book deltas are enabled (application of max depth here is trivial)
        1a. Book deltas are enabled, max depth is not, and exchange does not support deltas. Rare
        2.  Book deltas not enabled, but max depth is enabled
        3.  Neither deltas nor max depth enabled

        2 and 3 can be combined into a single block as long as application of depth modification
        happens first

        For 1, need to handle separate cases where a full book is returned vs a delta
        """
        if self.do_deltas:
            if not forced and self.updates[pair] < self.book_update_interval:
                if self.max_depth:
                    delta, book = await self.apply_depth(book, True, pair)
                    if not (delta[BID] or delta[ASK]):
                        return
                elif not delta:
                    # this will only happen in cases where an exchange does not support deltas and max depth is not enabled.
                    # this is an uncommon situation. Exchanges that do not support deltas will need
                    # to populate self.previous internally to avoid the unncesessary book copy on all other exchanges
                    delta = book_delta(self.previous_book[pair],
                                       book,
                                       book_type=book_type)
                    if not (delta[BID] or delta[ASK]):
                        return
                self.updates[pair] += 1
                await self.callback(BOOK_DELTA,
                                    feed=self.id,
                                    pair=pair,
                                    delta=delta,
                                    timestamp=timestamp)
                if self.updates[pair] != self.book_update_interval:
                    return
            elif forced and self.max_depth:
                # We want to send a full book update but need to apply max depth first
                _, book = await self.apply_depth(book, False, pair)
        elif self.max_depth:
            changed, book = await self.apply_depth(book, False, pair)
            if not changed:
                return
        if book_type == L2_BOOK:
            await self.callback(L2_BOOK,
                                feed=self.id,
                                pair=pair,
                                book=book,
                                timestamp=timestamp)
        else:
            await self.callback(L3_BOOK,
                                feed=self.id,
                                pair=pair,
                                book=book,
                                timestamp=timestamp)
        self.updates[pair] = 0
Example #4
0
    async def apply_depth(self, book: dict, do_delta: bool, symbol: str):
        ret = depth(book, self.max_depth)
        if not do_delta:
            delta = self.previous_book[symbol] != ret
            self.previous_book[symbol] = ret
            return delta, ret

        delta = book_delta(self.previous_book[symbol], ret)
        self.previous_book[symbol] = ret
        return delta, ret
Example #5
0
    async def apply_depth(self, book: dict, do_delta: bool, pair: str):
        ret = depth(book, self.max_depth)
        if not do_delta:
            delta = self.previous_book[pair] != ret
            self.previous_book[pair] = ret
            return delta, ret

        delta = []
        delta = book_delta(self.previous_book[pair], ret)
        self.previous_book[pair] = ret
        return delta, ret
Example #6
0
    async def apply_depth(self, book: dict,
                          do_delta: bool) -> Tuple[list, dict]:
        ret = depth(book, self.max_depth)
        if not do_delta:
            self.previous_book = ret
            return {BID: [], ASK: []}, ret

        delta = []
        delta = book_delta(self.previous_book, ret)
        self.previous_book = ret
        return delta, ret
Example #7
0
    async def book_callback(self, book: dict, book_type: str, pair: str, forced: bool, delta: dict, timestamp: float, receipt_timestamp: float):
        """
        Three cases we need to handle here

        1.  Book deltas are enabled (application of max depth here is trivial)
        1a. Book deltas are enabled, max depth is not, and exchange does not support deltas. Rare
        2.  Book deltas not enabled, but max depth is enabled
        3.  Neither deltas nor max depth enabled
        4.  Book deltas disabled and snapshot intervals enabled (with/without max depth)

        2 and 3 can be combined into a single block as long as application of depth modification
        happens first

        For 1, need to handle separate cases where a full book is returned vs a delta
        """
        if self.do_deltas:
            if not forced and self.updates[pair] < self.book_update_interval:
                if self.max_depth:
                    delta, book = await self.apply_depth(book, True, pair)
                    if not (delta[BID] or delta[ASK]):
                        return
                elif not delta:
                    # this will only happen in cases where an exchange does not support deltas and max depth is not enabled.
                    # this is an uncommon situation. Exchanges that do not support deltas will need
                    # to populate self.previous internally to avoid the unncesessary book copy on all other exchanges
                    delta = book_delta(self.previous_book[pair], book, book_type=book_type)
                    if not (delta[BID] or delta[ASK]):
                        return
                self.updates[pair] += 1
                if self.cross_check:
                    self.check_bid_ask_overlapping(book, pair)
                await self.callback(BOOK_DELTA, feed=self.id, pair=pair, delta=delta, timestamp=timestamp, receipt_timestamp=receipt_timestamp)
                if self.updates[pair] != self.book_update_interval:
                    return
            elif forced and self.max_depth:
                # We want to send a full book update but need to apply max depth first
                _, book = await self.apply_depth(book, False, pair)
        elif self.max_depth:
            if not self.snapshot_interval or (self.snapshot_interval and self.updates[pair] >= self.snapshot_interval):
                changed, book = await self.apply_depth(book, False, pair)
                if not changed:
                    return
        # case 4 - incremement skiped update, and exit
        if self.snapshot_interval and self.updates[pair] < self.snapshot_interval:
            self.updates[pair] += 1
            return

        if self.cross_check:
            self.check_bid_ask_overlapping(book, pair)
        if book_type == L2_BOOK:
            await self.callback(L2_BOOK, feed=self.id, pair=pair, book=book, timestamp=timestamp, receipt_timestamp=receipt_timestamp)
        else:
            await self.callback(L3_BOOK, feed=self.id, pair=pair, book=book, timestamp=timestamp, receipt_timestamp=receipt_timestamp)
        self.updates[pair] = 0