Giter VIP home page Giter VIP logo

binance.py's Introduction


Binance.py logo

๐Ÿฆพ A python3 binance API wrapper powered by asyncio and python Decimals.

Language grade: Python

Get binance.py

To install the library, you can just run the following command:

# Linux/macOS
python3 -m pip install -U binance.py

# Windows
py -3 -m pip install -U binance.py

Alternatively you can build the package and install it manually:

python setup.py sdist bdist_wheel
python -m pip install dist/binance.py-X.X.X-py3-none-any.whl

Why binance.py?

The binance api is complex to grasp and using a wrapper saves time but also ensures that the right practices are adopted. Binance.py offers a modern and asynchronous solution.

Features

  • Covers general endpoints (test connectivity and get exchange informations)
  • Covers market data endpoints
  • Covers Account endpoints (create and manage orders)
  • Covers user data stream (receive real time user updates)
  • Covers web socket streams (receive real time market updates)
  • Async support
  • Completely free and without limitations

What it does not

  • Binance.py does not cover the withdraw API
  • Binance.py does not cover the margin trading API

If you need these features, don't open an issue to ask me to implement them

Get started

  • Generate an API Key and assign relevant permissions.
  • import binance, create a client and send your first test order:
import binance

client = binance.Client(API_KEY, API_SECRET)
await client.load()

order = await client.create_order(
    "ETHPAX", binance.Side.BUY.value, binance.OrderType.MARKET.value, quantity="1", test=True,
)
print(order)
await client.close()

License

FOSSA Status

Donate

  • ETH (ENS): thomas.ethers.xyz
  • ETH (legacy address): 0x54c5a92c57A07f33500Ec9977797219D70D506C9
  • BTC: bc1qm9g2k3fznl2a9vghnpnwem87p03txl4y5lahyu

Powered by binance.py

Free trading system for Binance SPOT market. Adaptive customizable reverse grid strategy based on martingale.

binance.py's People

Contributors

dependabot[bot] avatar dgmaxime avatar dogstailfarmer avatar ionsome avatar mrchuckomo avatar th0rgal avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

binance.py's Issues

exception=AttributeError("'MarketEventsDataStream' object has no attribute 'connect'")>

Hi th0rgal!

I'm not sure, but this seems to be a bug in the code. This situation is very rare, so it could go unnoticed.

ERROR:root:Trying to receive something while the websocket is closed! Trying to reconnect.
ERROR:asyncio:Task exception was never retrieved
future: <Task finished name='Task-30' coro=<Client.start_market_events_listener() done, defined at /home/ubuntu/PycharmProjects/martin-binance/venv/lib/python3.8/site-packages/binance/client.py:80> exception=AttributeError("'MarketEventsDataStream' object has no attribute 'connect'")>
AttributeError: 'MarketEventsDataStream' object has no attribute 'connect'

I think that instead of calling self.connect () one should use self.start () in this snippet:

    async def _handle_messages(self, web_socket):
        while True:
            msg = await web_socket.receive()

            if msg.type in (aiohttp.WSMsgType.CLOSED, aiohttp.WSMsgType.CLOSE):
                logging.error(
                    "Trying to receive something while the websocket is closed! Trying to reconnect."
                )
                #
                # asyncio.ensure_future(self.connect())
                asyncio.ensure_future(self.start())
                #
            elif msg.type is aiohttp.WSMsgType.ERROR:
                logging.error(
                    f"Something went wrong with the websocket, reconnecting..."
                )
                asyncio.ensure_future(self.connect())
            elif msg.type == aiohttp.WSMsgType.CLOSING:
                logging.info(
                    "_handle_messages loop is stopped"
                )
                break
            await self._handle_event(json.loads(msg.data))

Regard's, Jerry.

Try to start user_event_listener

HI!

I'm trying to start user event listener and can't get any result. Can you help me for some example, please? My code below. Whats wrong? For public events all work - on_price_change(event) for example.

Thank's, Jerry.

# v 0.1a

import setup
import asyncio
# import json
import binance
from datetime import datetime
import time
import functools
import math
import signal
import martin_scale as ms
# from margin_strategy_sdk import *

SYMBOL = 'BTCUSDT'


def heartbeat():
    while True:
        # last_state = _strategy.save_strategy_state()
        # print(last_state)
        print('tik-tak ', datetime.utcnow())
        time.sleep(2)


async def on_price_change(event):
    await asyncio.sleep(5)
    print(
        f"symbol: {event.symbol},"
        f" best bid: {event.best_bid_quantity}ร—{event.best_bid_price},"
        f" best ask: {event.best_ask_quantity}ร—{event.best_ask_price}"
    )
    asyncio.create_task(foo())


async def foo() -> None:
    # await asyncio.sleep(5)
    print(datetime.utcnow())
    print("this can contain await instructions without blocking the code")


async def on_funds_update(wrapped_event):
    print(wrapped_event)


def ask_exit(sig_name, _loop):
    print(f"Got signal {sig_name}: exit")
    _loop.stop()


async def refresh(_client, _base_asset, _quote_asset):
    while True:
        print('Time to refresh')
        await asyncio.sleep(3)


async def main():
    client = binance.Client(setup.api_key, setup.api_secret, endpoint='https://testnet.binance.vision')
    # we load the client (this is not mandatory but it allows binance.py
    # to prevent you if you entered a wrong token name for example)
    await client.load()

    # Init section
    # account_info = await client.fetch_account_information(receive_window=None)
    # print(account_info)

    exchange_info = await client.fetch_exchange_info()
    exchange_info_symbol = next(item for item in exchange_info.get('symbols') if item["symbol"] == SYMBOL)
    # print(exchange_info_symbol)
    base_asset = exchange_info_symbol.get('baseAsset')
    quote_asset = exchange_info_symbol.get('quoteAsset')

    # loop.create_task(refresh(client, base_asset, quote_asset))

    # we register the events client.events.register_event(on_price_update, "ethbtc@bookTicker")
    # client.events.register_event(on_price_change, SYMBOL.lower()+"@bookTicker")

    client.events.register_user_event(on_funds_update, "outboundAccountPosition")

    # Start section
    # Start the data stream
    # loop.create_task(client.start_market_events_listener())
    loop.create_task(client.start_user_events_listener())

    for sig_name in {'SIGINT', 'SIGTERM'}:
        loop.add_signal_handler(getattr(signal, sig_name), functools.partial(ask_exit, sig_name, loop))

    print('End main')


if __name__ == "__main__":

    loop = asyncio.get_event_loop()
    loop.create_task(main())
    # loop.run_in_executor(None, functools.partial(heartbeat, m_strategy))
    loop.run_in_executor(None, heartbeat)
    # because we don't want to exit but wait for events
    loop.run_forever()
    loop.close()
    print('End __main__')

unregister method for class Events

Hi! And one more - in unregister method also remove {event_type} like this:

self.registered_streams.discard(event_type)

Regard's, Jerry.

Issue with create_order

I tried the create_order method with LIMIT order, and encounter a potential issue with the price parameter.
I tried the following cases:

  1. price=9000, (fail)
  2. price=9000.0, (fail)
  3. price=9000.00, (fail)
  4. price=9000.01, (success)

Dependencies were not installed

Unfortunately the dependencies were not installed by following the pip installation procedure on pypi:
pip install binance.py

After the installation aiohttp was missing - which you defined in your requirements.txt

Looks like the install_requires keyword is missing in the setup.py

๐Ÿ’ก Maybe you can dynamically read out the requirements.txt in your setup.py and attach a list of required packages. Then you still have just one source for maintaining all your required package.

error: invalid quantity when using quote_order_quantity in market orders

Description of the error:
For some pairs and specific amounts (e.g. FTM_BTC with 0.004 BTC).

Cause(s) of the error:
stepSize if wrong in LOT_SIZE filter for quote_order_quantity (this should only be used for quantity). I'm going to contact binance in order to know what I should use instead.

Wrong quantity

Hello @Ma007ks,

I have an issue with your commits. I'm using the testnet and I want to buy ETH with 10% of my BTC balance.
With your commits (1.8.1):

>> {'symbol': 'ETHBTC', 'side': <Side.BUY: 'BUY'>, 'order_type': <OrderType.MARKET: 'MARKET'>, 'quote_order_quantity': Decimal('0.090569718')}
Task exception was never retrieved
future: <Task finished name='Task-11' coro=<BotsPooler.send_order() done, defined at ./botspooler.py:176> exception=BinanceError('Invalid quantity.')>
Traceback (most recent call last):
  File "./botspooler.py", line 178, in send_order
    order = await user.client.create_order(**kwargs)
  File "/nix/store/bg993hfs0dw6vqq1qfci9241950qgwn8-python3-3.8.9-env/lib/python3.8/site-packages/binance/client.py", line 368, in create_order
    return await self.http.send_api_call(route, "POST", data=params, signed=True)
  File "/nix/store/bg993hfs0dw6vqq1qfci9241950qgwn8-python3-3.8.9-env/lib/python3.8/site-packages/binance/http.py", line 84, in send_api_call
    return await self.handle_errors(response)
  File "/nix/store/bg993hfs0dw6vqq1qfci9241950qgwn8-python3-3.8.9-env/lib/python3.8/site-packages/binance/http.py", line 47, in handle_errors
    raise BinanceError(payload["msg"])
binance.errors.BinanceError: Invalid quantity.

With the previous build (1.8.0), I tried with even 1%:
{'symbol': 'ETHBTC', 'side': <Side.BUY: 'BUY'>, 'order_type': <OrderType.MARKET: 'MARKET'>, 'quote_order_quantity': Decimal('0.0090569718')}

and it worked perfectly.
Do you have any idea of what causes that issue? I will probably revert you commits for now.

Thomas

KeyboardInterrupt catch?

i've tried catch it with :

except KeyboardInterrupt as e:
        loop.stop()
        print("Received KeyboardInterrupt, shutting down...")

But its working so slowly...
maybe another, more simple way to do this..?

Error in code: OCO orders

There is some problem in code. It is impossible to create OCO order:
in client.py:
if stop_client_order_id: params["stopLimitPrice"] = self.refine_price(symbol, stop_client_order_id)
need to be changed to:
if stop_limit_price: params["stopLimitPrice"] = self.refine_price(symbol, stop_limit_price)
or we gat an error when creating an order:
Stop loss orders are not supported for this symbol.

aiohttp slower than requests ?

I am experiencing higher latency VS python-binance lib that uses requests (https://github.com/sammchardy/python-binance)

Here is my test code:

Test1:

import os
import sys
import time
import asyncio
import binancepy
from binance.client import Client

API_KEY = "XX"
API_SECRET = "XX"

async def get_binancepy_latency(client):
    machineTime = time.time()
    serverTime = await client.fetch_server_time()
    latency = int(serverTime["serverTime"]) - int(machineTime * 1000)
    return latency

def get_python_binance_latency(client):
    machineTime = time.time()
    serverTime = client.get_server_time()
    latency = int(serverTime["serverTime"]) - int(machineTime * 1000)
    return latency

async def main(loop):
    client = binancepy.Client(API_KEY, API_SECRET)
    client2 =  Client(API_KEY, API_SECRET)

    print("binancepy latency", await get_binancepy_latency(client))
    print("python-binance latency", get_python_binance_latency(client2))

    binancepy_latencies = []
    python_binance_latencies = []
    for i in range(100):
        latency = await get_binancepy_latency(client)
        binancepy_latencies.append(latency)

        latency = get_python_binance_latency(client2)
        python_binance_latencies.append(latency)

    print("binancepy average %f min %d max %d" % (sum(binancepy_latencies)/len(binancepy_latencies), min(binancepy_latencies), max(binancepy_latencies)))
    print("python-binance average %f min %d max %d" % (sum(python_binance_latencies)/len(python_binance_latencies), min(python_binance_latencies), max(python_binance_latencies)))

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main(loop))

Output:

binancepy latency 25
python-binance latency 11
binancepy average 21.290000 min 18 max 49
python-binance average 9.780000 min 8 max 26

Test 2:

import os
import sys
import time
import asyncio
import binancepy
from binance.client import Client

API_KEY = "XX"
API_SECRET = "XX"

def user_order_callback(event):
    print(event.event_time, event.side)

async def main(loop):
    client = binancepy.Client(API_KEY, API_SECRET)
    client2 =  Client(API_KEY, API_SECRET)

    # we register the events
    client.events.register_user_event(user_order_callback, "executionReport")
    loop.create_task(client.start_user_events_listener())

    await asyncio.sleep(10)

    print(int(time.time() * 1000), "binancepy create_order")
    await client.create_order(
        symbol="BNBUSDT",
        side=binancepy.Side.BUY,
        order_type=binancepy.OrderType.LIMIT,
        time_in_force=binancepy.TimeInForce.GTC,
        quantity="0.035",  # you can use Decimals
        price="350.0",  # or directly a string
        test=False,
    )

    print(int(time.time() * 1000), "python-biance create_order")
    client2.create_order(symbol="BNBUSDT", side='SELL', type='LIMIT', timeInForce='GTC', quantity="0.035", price="750.0")

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.create_task(main(loop))
    # because we don't want to exit but wait for events
    loop.run_forever()

Output:

1618261495647 binancepy create_order
1618261495670 python-biance create_order
1618261495668 BUY
1618261495682 SELL

I am running the code in an aws in Tokyo.

aiohttp version: 3.7.4.post0

Edit: Test 2 was not fair, updated

Error with param response_type=binance.ResponseType.RESULT

Thomas, hi!

When create order with param response_type=binance.ResponseType.RESULT get error:

Exception in callback limit_order_callback(<Task finishe...Z]{1,36}$'.")>) at /home/ubuntu/PycharmProjects/Binance/martin_scale_binance.py:349
handle: <Handle limit_order_callback(<Task finishe...Z]{1,36}$'.")>) at /home/ubuntu/PycharmProjects/Binance/martin_scale_binance.py:349>
Traceback (most recent call last):
  File "/usr/lib/python3.8/asyncio/events.py", line 81, in _run
    self._context.run(self._callback, *self._args)
  File "/home/ubuntu/PycharmProjects/Binance/martin_scale_binance.py", line 350, in limit_order_callback
    result = fut.result()
  File "/home/ubuntu/PycharmProjects/Binance/venv/lib/python3.8/site-packages/binance/client.py", line 379, in create_order
    return await self.http.send_api_call(route, "POST", data=params, signed=True)
  File "/home/ubuntu/PycharmProjects/Binance/venv/lib/python3.8/site-packages/binance/http.py", line 84, in send_api_call
    return await self.handle_errors(response)
  File "/home/ubuntu/PycharmProjects/Binance/venv/lib/python3.8/site-packages/binance/http.py", line 47, in handle_errors
    raise BinanceError(payload["msg"])
binance.errors.BinanceError: Illegal characters found in parameter 'newOrderRespType'; legal range is '^[a-zA-Z]{1,36}$'.
Exception in callback limit_order_callback(<Task finishe...Z]{1,36}$'.")>) at /home/ubuntu/PycharmProjects/Binance/martin_scale_binance.py:349
handle: <Handle limit_order_callback(<Task finishe...Z]{1,36}$'.")>) at /home/ubuntu/PycharmProjects/Binance/martin_scale_binance.py:349>

Code is:

    @classmethod
    def place_limit_order(cls, buy: bool, amount: float, price: float) -> int:
        cls.order_id += 1
        side = binance.Side.BUY if buy else binance.Side.SELL
        asyncio.ensure_future(cls.client.create_order(
            SYMBOL,
            side,
            order_type=binance.OrderType.LIMIT,
            time_in_force=binance.TimeInForce.GTC,
            quantity=str(amount),
            quote_order_quantity=None,
            price=str(price),
            new_client_order_id=cls.order_id,
            stop_price=None,
            iceberg_quantity=None,
            response_type=binance.ResponseType.RESULT,
            receive_window=None,
            test=False)).add_done_callback(limit_order_callback)
        print(f"place_limit_order.order_id: {cls.order_id}")
        return cls.order_id

And no error if response_type=None

limit_order.result: {'symbol': 'BTCUSDT', 'orderId': 7981419, 'orderListId': -1, 'clientOrderId': '1', 'transactTime': 1629979423445, 'price': '46757.82000000', 'origQty': '0.01161000', 'executedQty': '0.00000000', 'cummulativeQuoteQty': '0.00000000', 'status': 'NEW', 'timeInForce': 'GTC', 'type': 'LIMIT', 'side': 'SELL', 'fills': []}

Regard's, Jerry.

Get Unexpected Error in version 1.8.0

After updating to the latest version 1.8.0 I get some unexpected RuntimeError which didn't happen before with existing code.

Probably an issue with the new session handling. Looks like the session isn't getting closed properly.

File "/opt/miniconda3/lib/python3.8/site-packages/binance/client.py", line 35, in load
    infos = await self.fetch_exchange_info()
  File "/opt/miniconda3/lib/python3.8/site-packages/binance/client.py", line 150, in fetch_exchange_info
    return await self.http.send_api_call("/api/v3/exchangeInfo", send_api_key=False)
  File "/opt/miniconda3/lib/python3.8/site-packages/binance/http.py", line 81, in send_api_call
    async with self.session.request(
  File "/opt/miniconda3/lib/python3.8/site-packages/aiohttp/client.py", line 1117, in __aenter__
    self._resp = await self._coro
  File "/opt/miniconda3/lib/python3.8/site-packages/aiohttp/client.py", line 448, in _request
    with timer:
  File "/opt/miniconda3/lib/python3.8/site-packages/aiohttp/helpers.py", line 635, in __enter__
    raise RuntimeError(
RuntimeError: Timeout context manager should be used inside a task
Unclosed client session
client_session: <aiohttp.client.ClientSession object at 0x7ffaad5b99a0>

Place an order in a bookTicker callback

First, thank you for this lib !

I want to place an order in an event callback for example

def on_price_change(event):
    print(
        f"symbol: {event.symbol}, best bid: {event.best_bid_quantity}ร—{event.best_bid_price}, best ask: {event.best_ask_quantity}ร—{event.best_ask_price}"
    )
    order = await client.create_order(
        symbol="BTCUSDT",
        side=binancepy.Side.SELL,
        order_type=binancepy.OrderType.LIMIT,
        time_in_force=binancepy.TimeInForce.GTC,
        quantity=Decimal("0.0012"),  # you can use Decimals
        price="15100.00",  # or directly a string
        test=False,
    )
    ....

async def main(loop):
    client.events.register_event(on_price_change, "ethbtc@bookTicker")

Of course this is a SyntaxError: 'await' outside function.

Can you propose any implementation to achieve this ?

Need correct stop or restart web_socket stream

Thomas, hi! I almost finished a multiplexer server (based on gRPC) for your wrapper, to handle many trading pairs on one connection and different connection for different (sub)account. Now I have one non-critical request.

For correct start/stop/add client I need break while loop in async def _handle_messages(self, web_socket): and restart stream web_socket session. If don't it, when changing the list self.client.events.registered_streams a combined stream that has already been started retains its content.

Now I do it as

        # Restart stream
        if hasattr(open_client.client, 'market_data_stream'):
            await open_client.client.market_data_stream.web_socket.close()

and then restart:

    async def market_events_listener(self):
        while True:
            try:
                await asyncio.sleep(HEARTBEAT)
                if self.client.events.registered_streams:
                    logging.info(f"Trying to start market events listener")
                    # For market events use default ws
                    await self.client.start_market_events_listener()
            except asyncio.CancelledError:
                print(f"market_events.Cancelled")
            except Exception as exc:
                logging.info(f"Exception market_events: {exc}")
                await asyncio.sleep(HEARTBEAT * 5)

This work, but it's not very pretty and I get an exception:

INFO:root:Exception market_events: the JSON object must be str, bytes or bytearray, not NoneType

If you can add stop or restart method for
class MarketEventsDataStream(EventsDataStream):
and
class UserEventsDataStream(EventsDataStream):

it would be great.

Regard's, Jerry.

In events.py Class declaration duplicated

Hi!

class SymbolBookTickerWrapper(BinanceEventWrapper):
    def __init__(self, event_data, handlers):
        super().__init__(event_data, handlers)
        self.order_book_updated = event_data["u"]
        self.symbol = event_data["s"]
        self.best_bid_price = event_data["b"]
        self.best_bid_quantity = event_data["B"]
        self.best_ask_price = event_data["a"]
        self.best_ask_quantity = event_data["A"]


class SymbolBookTickerWrapper(BinanceEventWrapper):
    def __init__(self, event_data, handlers):
        super().__init__(event_data, handlers)
        self.order_book_updated = event_data["u"]
        self.symbol = event_data["s"]
        self.best_bid_price = event_data["b"]
        self.best_bid_quantity = event_data["B"]
        self.best_ask_price = event_data["a"]
        self.best_ask_quantity = event_data["A"]

Regard's, Jerry.

package name conflict

I have https://github.com/sammchardy/python-binance installed on my machine. Recently I installed your package as well.
Now my previous code utilizing the old package fail to run, because both use 'import binance'.
I don't know how do deal with the conflict, so I end up uninstall your package (more recent installation) in order to use the previous package, and reinstall the new one while testing the new package.
Any suggestions for an easier co-existence solution?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.