Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Data Client #1147

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 50 additions & 8 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -77,23 +77,37 @@ converted to use Asynchronous Context Managers. See examples in the Async sectio
`depth cache <https://python-binance.readthedocs.io/en/latest/depth_cache.html>`_ docs.

Quick Start
-----------
--------------------

`Register an account with Binance <https://accounts.binance.com/en/register?ref=10099792>`_.

`Generate an API Key <https://www.binance.com/en/my/settings/api-management>`_ and assign relevant permissions.

If you are using an exchange from the US, Japan or other TLD then make sure pass `tld='us'` when creating the
client.

To use the `Spot <https://testnet.binance.vision/>`_ or `Vanilla Options <https://testnet.binanceops.com/>`_ Testnet,
pass `testnet=True` when creating the client.
Installation
--------------------

Install the package:

.. code:: bash

pip install python-binance

Optionally install with extra dependencies, for example to interface with pandas dataframes:

.. code:: bash

pip install python-binance[extras]


Code Example
--------------------

If you are using an exchange from the US, Japan or other TLD then make sure pass `tld='us'` when creating the
client.

To use the `Spot <https://testnet.binance.vision/>`_ or `Vanilla Options <https://testnet.binanceops.com/>`_ Testnet,
pass `testnet=True` when creating the client.

.. code:: python

Expand Down Expand Up @@ -181,7 +195,7 @@ pass `testnet=True` when creating the client.
For more `check out the documentation <https://python-binance.readthedocs.io/en/latest/>`_.

Async Example
-------------
--------------------

Read `Async basics for Binance <https://sammchardy.github.io/binance/2021/05/01/async-binance-basics.html>`_
for more information.
Expand Down Expand Up @@ -258,8 +272,36 @@ for more information.
loop.run_until_complete(main())






DataFrame Example
--------------------

If you would like to work with response data in :code:`pandas.DataFrame` format, use the :code:`DataClient` class instead of the :code:`Client`, and invoke the desired method as you would normally do:

.. code:: python

from binance.data_client import DataClient
from binance.enums import KLINE_INTERVAL_1MINUTE

client = DataClient(api_key, api_secret)

df = client.get_historical_klines(
symbol="BNBBTC",
interval=KLINE_INTERVAL_1MINUTE,
start_str="1st March 2018"
)
print(type(df)) #> <class 'pandas.core.frame.DataFrame'>
print(df.columns.tolist()) #> ['open_time', 'open', 'high', 'low', 'close', 'volume', 'close_time', 'quote_asset_volume', 'number_of_trades', 'taker_buy_base_asset_volume', 'taker_buy_quote_asset_volume', 'ignore']


Note: this requires you to install with the "extras" option.


Donate
------
--------------------

If this library helped you out feel free to donate.

Expand All @@ -269,7 +311,7 @@ If this library helped you out feel free to donate.
- BTC: 1Dknp6L6oRZrHDECRedihPzx2sSfmvEBys

Other Exchanges
---------------
--------------------

If you use `Binance Chain <https://testnet.binance.org/>`_ check out my `python-binance-chain <https://github.com/sammchardy/python-binance-chain>`_ library.

Expand Down
10 changes: 10 additions & 0 deletions binance/data_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from pandas import DataFrame

from binance.client import Client
from binance.enums import KLINES_RESPONSE_COLUMNS

class DataClient(Client):

def get_historical_klines(self, **args):
klines = super().get_historical_klines(**args)
return DataFrame(klines, columns=KLINES_RESPONSE_COLUMNS)
8 changes: 8 additions & 0 deletions binance/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@
MARGIN_BUY_TYPE = 'MARGIN_BUY'
AUTO_REPAY_TYPE = 'AUTO_REPAY'

# the names of klines response columns, in exact order
# https://github.com/binance-us/binance-official-api-docs/blob/master/rest-api.md#klinecandlestick-data
KLINES_RESPONSE_COLUMNS = [
'open_time', 'open', 'high', 'low', 'close', 'volume', 'close_time',
'quote_asset_volume', 'number_of_trades',
'taker_buy_base_asset_volume', 'taker_buy_quote_asset_volume',
'ignore'
]

class HistoricalKlinesType(Enum):
SPOT = 1
Expand Down
102 changes: 102 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@

import pytest
import requests_mock

Comment on lines +1 to +4

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you shure, that this file should be stored in project root?
And add module's description

Copy link
Contributor Author

@s2t2 s2t2 Mar 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confest in the root directory is a best practice to help us avoid adding init files to the test directory. It facilities local imports, especially as they pertain to testing.

https://stackoverflow.com/questions/34466027/in-pytest-what-is-the-use-of-conftest-py-files

Test root path: This is a bit of a hidden feature. By defining conftest.py in your root path, you will have pytest recognizing your application modules without specifying PYTHONPATH. In the background, py.test modifies your sys.path by including all submodules which are found from the root path.

from binance.client import Client
from binance.data_client import DataClient
from binance.enums import KLINE_INTERVAL_1MINUTE


@pytest.fixture(scope="module")
def client():
"""Returns a client to use for testing purposes"""
return Client("api_key", "api_secret")

@pytest.fixture(scope="module")
def data_client():
"""Returns a data client to use for testing purposes"""
return DataClient("api_key", "api_secret")


#
# KLINES RESPONSE DATA
#


klines_row_1 = [
1500004800000,
"0.00005000",
"0.00005300",
"0.00001000",
"0.00004790",
"663152.00000000",
1500004859999,
"30.55108144",
43,
"559224.00000000",
"25.65468144",
"83431971.04346950",
]

klines_row_2 = [
1519892340000,
"0.00099400",
"0.00099810",
"0.00099400",
"0.00099810",
"4806.04000000",
1519892399999,
"4.78553253",
154,
"1785.14000000",
"1.77837524",
"0",
]

url_1 = "https://api.binance.com/api/v3/klines?interval=1m&limit=1&startTime=0&symbol=BNBBTC"
url_2 = "https://api.binance.com/api/v3/klines?interval=1m&limit=1000&startTime=1519862400000&symbol=BNBBTC"
url_3 = "https://api.binance.com/api/v3/klines?interval=1m&limit=1000&startTime=1519892400000&symbol=BNBBTC"

response_1 = [klines_row_1]
response_2 = [klines_row_2 for _ in range(0,500)]
response_3 = []

@pytest.fixture(scope="module")
def historical_klines_response(client):
with requests_mock.mock() as mock:
mock.get(url_1, json=response_1)
mock.get(url_2, json=response_2)
mock.get(url_3, json=response_3)

klines = client.get_historical_klines(
symbol="BNBBTC",
interval=KLINE_INTERVAL_1MINUTE,
start_str="1st March 2018"
)
#print(klines)
return klines

@pytest.fixture(scope="module")
def historical_klines_response_df(data_client):
with requests_mock.mock() as mock:
mock.get(url_1, json=response_1)
mock.get(url_2, json=response_2)
mock.get(url_3, json=response_3)

klines = data_client.get_historical_klines(
symbol="BNBBTC",
interval=KLINE_INTERVAL_1MINUTE,
start_str="1st March 2018"
)
#print(klines)
return klines

@pytest.fixture(scope="module")
def klines(historical_klines_response):
"""Alias method"""
return historical_klines_response

@pytest.fixture(scope="module")
def klines_df(historical_klines_response_df):
"""Alias method"""
return historical_klines_response_df
1 change: 1 addition & 0 deletions extra-requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pandas

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, you should define version range: pandas>=n.n.n<n+1

This comment was marked as outdated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

3 changes: 3 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
install_requires=[
'requests', 'six', 'dateparser', 'aiohttp', 'ujson', 'websockets==9.1'
],
extras_require = {
'extras': ['pandas']
},
keywords='binance exchange rest api bitcoin ethereum btc eth neo',
classifiers=[
'Intended Audience :: Developers',
Expand Down
Empty file removed tests/__init__.py
Empty file.
10 changes: 10 additions & 0 deletions tests/test_data_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

from pandas import DataFrame

from binance.enums import KLINES_RESPONSE_COLUMNS


def test_historical_klines(klines_df):
assert len(klines_df) == 500
assert isinstance(klines_df, DataFrame)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

assert klines_df.columns.tolist() == KLINES_RESPONSE_COLUMNS
59 changes: 2 additions & 57 deletions tests/test_historical_klines.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,64 +9,9 @@
client = Client("api_key", "api_secret")


def test_exact_amount():
def test_exact_amount(klines):
"""Test Exact amount returned"""

first_available_res = [
[
1500004800000,
"0.00005000",
"0.00005300",
"0.00001000",
"0.00004790",
"663152.00000000",
1500004859999,
"30.55108144",
43,
"559224.00000000",
"25.65468144",
"83431971.04346950",
]
]

first_res = []
row = [
1519892340000,
"0.00099400",
"0.00099810",
"0.00099400",
"0.00099810",
"4806.04000000",
1519892399999,
"4.78553253",
154,
"1785.14000000",
"1.77837524",
"0",
]

for i in range(0, 500):
first_res.append(row)

second_res = []

with requests_mock.mock() as m:
m.get(
"https://api.binance.com/api/v3/klines?interval=1m&limit=1&startTime=0&symbol=BNBBTC",
json=first_available_res,
)
m.get(
"https://api.binance.com/api/v3/klines?interval=1m&limit=1000&startTime=1519862400000&symbol=BNBBTC",
json=first_res,
)
m.get(
"https://api.binance.com/api/v3/klines?interval=1m&limit=1000&startTime=1519892400000&symbol=BNBBTC",
json=second_res,
)
klines = client.get_historical_klines(
symbol="BNBBTC", interval=Client.KLINE_INTERVAL_1MINUTE, start_str="1st March 2018"
)
assert len(klines) == 500
assert len(klines) == 500


def test_start_and_end_str():
Expand Down