diff --git a/README.rst b/README.rst index bfbd96d85..49ce16257 100755 --- a/README.rst +++ b/README.rst @@ -77,23 +77,37 @@ converted to use Asynchronous Context Managers. See examples in the Async sectio `depth cache `_ docs. Quick Start ------------ +-------------------- `Register an account with Binance `_. `Generate an API Key `_ 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 `_ or `Vanilla Options `_ 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 `_ or `Vanilla Options `_ Testnet, +pass `testnet=True` when creating the client. .. code:: python @@ -181,7 +195,7 @@ pass `testnet=True` when creating the client. For more `check out the documentation `_. Async Example -------------- +-------------------- Read `Async basics for Binance `_ for more information. @@ -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)) #> + 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. @@ -269,7 +311,7 @@ If this library helped you out feel free to donate. - BTC: 1Dknp6L6oRZrHDECRedihPzx2sSfmvEBys Other Exchanges ---------------- +-------------------- If you use `Binance Chain `_ check out my `python-binance-chain `_ library. diff --git a/binance/data_client.py b/binance/data_client.py new file mode 100644 index 000000000..5487b3bfa --- /dev/null +++ b/binance/data_client.py @@ -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) diff --git a/binance/enums.py b/binance/enums.py index cbb65aff1..bd77cc2df 100644 --- a/binance/enums.py +++ b/binance/enums.py @@ -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 diff --git a/conftest.py b/conftest.py new file mode 100644 index 000000000..2d1803ce4 --- /dev/null +++ b/conftest.py @@ -0,0 +1,102 @@ + +import pytest +import requests_mock + +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 diff --git a/extra-requirements.txt b/extra-requirements.txt new file mode 100644 index 000000000..efd4927ba --- /dev/null +++ b/extra-requirements.txt @@ -0,0 +1 @@ +pandas==1.4.3 diff --git a/setup.py b/setup.py index 869a8a606..d2a6ee53f 100644 --- a/setup.py +++ b/setup.py @@ -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', diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/test_data_client.py b/tests/test_data_client.py new file mode 100644 index 000000000..e38f1328e --- /dev/null +++ b/tests/test_data_client.py @@ -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) + assert klines_df.columns.tolist() == KLINES_RESPONSE_COLUMNS diff --git a/tests/test_historical_klines.py b/tests/test_historical_klines.py index 397db4f2f..f0496dd07 100644 --- a/tests/test_historical_klines.py +++ b/tests/test_historical_klines.py @@ -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():