sammchardy / python-binance

Binance Exchange API python implementation for automated trading
https://python-binance.readthedocs.io/en/latest/
MIT License
5.97k stars 2.19k forks source link

symbol_book_ticker_socket() & symbol_ticker_futures_socket() are subscribing to the same thing? #854

Closed mchangun closed 3 years ago

mchangun commented 3 years ago

Describe the bug I suspect that symbol_book_ticker_socket() & symbol_ticker_futures_socket() from BinanceSocketManager are subscribing to the same books. If I subscribe to both the future and the coin book and print out the bid and offers of each, they look suspiciously similar:

======================
ALICEUSDT
bids: 11.17660000 11.17670000
asks: 11.19030000 11.19030000
update id: 173124777 | 173124778
======================
ALICEUSDT
bids: 11.17660000 11.17670000
asks: 11.19030000 11.19030000
update id: 173124777 | 173124778
======================
ALICEUSDT
bids: 11.17630000 11.17630000
asks: 11.19340000 11.19350000
update id: 173124869 | 173124865
======================
ALICEUSDT
bids: 11.17610000 11.17610000
asks: 11.19040000 11.19030000
update id: 173124952 | 173124955
======================
ALICEUSDT
bids: 11.18010000 11.18010000
asks: 11.19780000 11.19760000
update id: 173125074 | 173125079
======================

Also look at the update ids for both the coin and future book, they look suspiciously close together too.

I've looked at my code a few times already, but I can't see where I am going wrong so I'm suspecting it's the library.

To Reproduce Code snippet to reproduce the behavior:

import asyncio

from binance import BinanceSocketManager
from binance.client import AsyncClient, Client
from decimal import Decimal as D

coin_book_top_level: dict[str, tuple[D, D, int]] = {}
fut_book_top_level: dict[str, tuple[D, D, int]] = {}

async def display():
    while True:
        for sym in sorted(coin_book_top_level):
            try:
                print(sym)
                print(f"bids: {fut_book_top_level[sym][0]} {coin_book_top_level[sym][0]}")
                print(f"asks: {fut_book_top_level[sym][1]} { coin_book_top_level[sym][1]}")
                print(f"update id: {fut_book_top_level[sym][2]} | {coin_book_top_level[sym][2]}")
            except KeyError:
                pass

        print("======================")
        await asyncio.sleep(5)

async def coin_book_listener(bsm, symbol):
    global coin_book_top_level
    async with bsm.symbol_book_ticker_socket(symbol) as book_ticker:
        while True:
            msg = await book_ticker.recv()
            coin_book_top_level[msg['s']] = (D(msg['b']), D(msg['a']), msg['u'])

async def fut_book_listener(bsm: BinanceSocketManager, symbol):
    global fut_book_top_level
    async with bsm.symbol_ticker_futures_socket(symbol) as fut_ticker:
        while True:
            msg = await fut_ticker.recv()
            fut_book_top_level[msg['s']] = (D(msg['b']), D(msg['a']), msg['u'])

async def main():
    async_client = await AsyncClient.create()
    bsm = BinanceSocketManager(async_client)
    syms = ['ALICEUSDT']

    for sym in syms:
        asyncio.create_task(coin_book_listener(bsm, sym))
        asyncio.create_task(fut_book_listener(bsm, sym))

    await display()

if __name__ == '__main__':
    asyncio.run(main())

Expected behavior I expect that symbol_book_ticker_socket gives me the coin book and symbol_ticker_futures_socket gives me the perpetual USD future book.

Environment (please complete the following information):

mchangun commented 3 years ago

They both seem to be the coin book.

mchangun commented 3 years ago

Here's an even simpler example to illustrate my point

import asyncio

from binance import BinanceSocketManager
from binance.client import AsyncClient
from decimal import Decimal as D

async def coin_book_listener(bsm, symbol):
    async with bsm.symbol_book_ticker_socket(symbol) as book_ticker:
        while True:
            msg = await book_ticker.recv()
            print("coin:", D(msg['b']), D(msg['a']), msg['u'])

async def fut_book_listener(bsm: BinanceSocketManager, symbol):
    async with bsm.symbol_ticker_futures_socket(symbol) as fut_ticker:
        while True:
            msg = await fut_ticker.recv()
            print("futs:", D(msg['b']), D(msg['a']), msg['u'])

async def main():
    async_client = await AsyncClient.create()
    bsm = BinanceSocketManager(async_client)
    syms = ['ALICEUSDT']

    for sym in syms:
        asyncio.create_task(coin_book_listener(bsm, sym))
        asyncio.create_task(fut_book_listener(bsm, sym))

    await asyncio.sleep(5)

if __name__ == '__main__':
    asyncio.run(main())

It just runs for 5 seconds and prints the best bid / offer updates from the coin book and the futures book. Here are the results I get:

coin: 10.95000000 10.96150000 177508567
futs: 10.95000000 10.96150000 177508567
coin: 10.95000000 10.95770000 177508571
coin: 10.95000000 10.95770000 177508580
coin: 10.95000000 10.95760000 177508600
coin: 10.95000000 10.95760000 177508644
coin: 10.95000000 10.95760000 177508649
futs: 10.94760000 10.95760000 177508650
coin: 10.94760000 10.95750000 177508652
coin: 10.94760000 10.95540000 177508669
coin: 10.94750000 10.95540000 177508676
coin: 10.94370000 10.95540000 177508680
coin: 10.94370000 10.95530000 177508699
coin: 10.94370000 10.95530000 177508715
coin: 10.94370000 10.95530000 177508718
coin: 10.94370000 10.95540000 177508719
coin: 10.94370000 10.95240000 177508720

Look at when the lines where it goes from coin->futs or futs->coin. The best bid/offers look almost identical (they're not if u check on the website). Furthermore, the first two lines share the exact same update ID.

mchangun commented 3 years ago

There was a bug in my code.