From 8c59aa9e58b5181122b34e3b7bf9864b7c9d1795 Mon Sep 17 00:00:00 2001 From: MJ Rossetti Date: Sat, 19 Feb 2022 10:58:13 -0500 Subject: [PATCH 1/9] Optional pandas dependency --- README.rst | 22 ++++++++++++++++++---- setup.py | 3 +++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index bfbd96d85..275ddaccf 100755 --- a/README.rst +++ b/README.rst @@ -83,17 +83,31 @@ Quick Start `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 pandas dependencies, to use pandas functionality like the _____________: + +.. code:: bash + + pip install python-binance[pandas] + + +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 diff --git a/setup.py b/setup.py index 869a8a606..4d496f51a 100644 --- a/setup.py +++ b/setup.py @@ -33,6 +33,9 @@ install_requires=[ 'requests', 'six', 'dateparser', 'aiohttp', 'ujson', 'websockets==9.1' ], + extras_require = { + 'pandas': ['pandas'] + }, keywords='binance exchange rest api bitcoin ethereum btc eth neo', classifiers=[ 'Intended Audience :: Developers', From e95d6394501f4f0b4a0f910df00aac68d930971a Mon Sep 17 00:00:00 2001 From: MJ Rossetti Date: Sat, 19 Feb 2022 11:06:59 -0500 Subject: [PATCH 2/9] Optional dependency --- README.rst | 4 ++-- extra-requirements.txt | 1 + setup.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 extra-requirements.txt diff --git a/README.rst b/README.rst index 275ddaccf..543fe9ff3 100755 --- a/README.rst +++ b/README.rst @@ -93,11 +93,11 @@ Install the package: pip install python-binance -Optionally install with pandas dependencies, to use pandas functionality like the _____________: +Optionally install with extra dependencies, for example to interface with pandas dataframes: .. code:: bash - pip install python-binance[pandas] + pip install python-binance[extras] Code Example diff --git a/extra-requirements.txt b/extra-requirements.txt new file mode 100644 index 000000000..fb6c7ed7e --- /dev/null +++ b/extra-requirements.txt @@ -0,0 +1 @@ +pandas diff --git a/setup.py b/setup.py index 4d496f51a..d2a6ee53f 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,7 @@ 'requests', 'six', 'dateparser', 'aiohttp', 'ujson', 'websockets==9.1' ], extras_require = { - 'pandas': ['pandas'] + 'extras': ['pandas'] }, keywords='binance exchange rest api bitcoin ethereum btc eth neo', classifiers=[ From 1de6d0fe914a325414b86501e055c3e2b9f2eaa5 Mon Sep 17 00:00:00 2001 From: MJ Rossetti Date: Sat, 19 Feb 2022 11:49:04 -0500 Subject: [PATCH 3/9] Refactor mock klines response --- conftest.py | 65 +++++++++++++++++++++++++++++++++ tests/__init__.py | 0 tests/test_historical_klines.py | 60 ++---------------------------- 3 files changed, 68 insertions(+), 57 deletions(-) create mode 100644 conftest.py delete mode 100644 tests/__init__.py diff --git a/conftest.py b/conftest.py new file mode 100644 index 000000000..b9b47e814 --- /dev/null +++ b/conftest.py @@ -0,0 +1,65 @@ + +import pytest +import requests_mock + +from binance.client import Client + + +@pytest.fixture(scope="module") +def test_client(): + return Client("api_key", "api_secret") + + +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", +] + + +@pytest.fixture(scope="module") +def historical_klines_response(test_client): + 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 = [] + + 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 = test_client.get_historical_klines( + symbol="BNBBTC", + interval=Client.KLINE_INTERVAL_1MINUTE, + start_str="1st March 2018" + ) + #print(klines) + return klines diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/test_historical_klines.py b/tests/test_historical_klines.py index 397db4f2f..d5365b09b 100644 --- a/tests/test_historical_klines.py +++ b/tests/test_historical_klines.py @@ -9,64 +9,10 @@ client = Client("api_key", "api_secret") -def test_exact_amount(): +def test_exact_amount(historical_klines_response): """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 + klines = historical_klines_response + assert len(klines) == 500 def test_start_and_end_str(): From eb915fc329733bb8a237fc1e7c73aeb2de89b190 Mon Sep 17 00:00:00 2001 From: MJ Rossetti Date: Sat, 19 Feb 2022 12:21:33 -0500 Subject: [PATCH 4/9] Inherit methods from client --- binance/data_client.py | 10 ++++++++++ binance/enums.py | 8 ++++++++ conftest.py | 39 +++++++++++++++++++++++++++++++++++---- tests/test_data_client.py | 11 +++++++++++ 4 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 binance/data_client.py create mode 100644 tests/test_data_client.py 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 index b9b47e814..516490284 100644 --- a/conftest.py +++ b/conftest.py @@ -3,12 +3,20 @@ 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 test_client(): +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_row_1 = [ 1500004800000, @@ -42,7 +50,30 @@ def test_client(): @pytest.fixture(scope="module") -def historical_klines_response(test_client): +def historical_klines_response(client): + 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 = [] + + 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): 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" @@ -56,9 +87,9 @@ def historical_klines_response(test_client): mock.get(url_2, json=response_2) mock.get(url_3, json=response_3) - klines = test_client.get_historical_klines( + klines = data_client.get_historical_klines( symbol="BNBBTC", - interval=Client.KLINE_INTERVAL_1MINUTE, + interval=KLINE_INTERVAL_1MINUTE, start_str="1st March 2018" ) #print(klines) diff --git a/tests/test_data_client.py b/tests/test_data_client.py new file mode 100644 index 000000000..69759a094 --- /dev/null +++ b/tests/test_data_client.py @@ -0,0 +1,11 @@ + +from pandas import DataFrame + +from binance.enums import KLINES_RESPONSE_COLUMNS + + +def test_historical_klines(historical_klines_response_df): + klines_df = historical_klines_response_df + assert len(klines_df) == 500 + assert isinstance(klines_df, DataFrame) + assert klines_df.columns.tolist() == KLINES_RESPONSE_COLUMNS From 4e63679c321083a2455b19bcb4be948d1b306875 Mon Sep 17 00:00:00 2001 From: MJ Rossetti Date: Sat, 19 Feb 2022 12:31:35 -0500 Subject: [PATCH 5/9] README example --- README.rst | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/README.rst b/README.rst index 543fe9ff3..abfcc98c9 100755 --- a/README.rst +++ b/README.rst @@ -272,6 +272,35 @@ for more information. loop.run_until_complete(main()) + + + + +DataFrame Example +------------- + +If you would like to work with response data in pandas DataFrame format, use the `DataClient` class instead of the `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 ------ From bf6c866a9a3eea177050d76774449ccf5ec19507 Mon Sep 17 00:00:00 2001 From: MJ Rossetti Date: Sat, 19 Feb 2022 12:38:43 -0500 Subject: [PATCH 6/9] README --- README.rst | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/README.rst b/README.rst index abfcc98c9..49ce16257 100755 --- a/README.rst +++ b/README.rst @@ -77,7 +77,7 @@ converted to use Asynchronous Context Managers. See examples in the Async sectio `depth cache `_ docs. Quick Start ------------ +-------------------- `Register an account with Binance `_. @@ -85,7 +85,7 @@ Quick Start Installation ------------ +-------------------- Install the package: @@ -101,7 +101,7 @@ Optionally install with extra dependencies, for example to interface with pandas 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. @@ -195,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. @@ -277,16 +277,15 @@ for more information. DataFrame Example -------------- +-------------------- -If you would like to work with response data in pandas DataFrame format, use the `DataClient` class instead of the `Client`, and invoke the desired method as you would normally do: +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( @@ -302,7 +301,7 @@ Note: this requires you to install with the "extras" option. Donate ------- +-------------------- If this library helped you out feel free to donate. @@ -312,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. From 820becdc54e003b794f5389da6034fcaa914e291 Mon Sep 17 00:00:00 2001 From: MJ Rossetti Date: Sat, 19 Feb 2022 12:41:16 -0500 Subject: [PATCH 7/9] Refactor mock data --- conftest.py | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/conftest.py b/conftest.py index 516490284..ddac4a461 100644 --- a/conftest.py +++ b/conftest.py @@ -18,6 +18,11 @@ def data_client(): return DataClient("api_key", "api_secret") +# +# KLINES RESPONSE DATA +# + + klines_row_1 = [ 1500004800000, "0.00005000", @@ -48,17 +53,16 @@ def data_client(): "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): - 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 = [] - with requests_mock.mock() as mock: mock.get(url_1, json=response_1) mock.get(url_2, json=response_2) @@ -74,14 +78,6 @@ def historical_klines_response(client): @pytest.fixture(scope="module") def historical_klines_response_df(data_client): - 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 = [] - with requests_mock.mock() as mock: mock.get(url_1, json=response_1) mock.get(url_2, json=response_2) From bacc32a07f0355d3118e64133ea6cbb83c38298e Mon Sep 17 00:00:00 2001 From: MJ Rossetti Date: Sun, 20 Feb 2022 10:40:27 -0500 Subject: [PATCH 8/9] Alias methods for readability --- conftest.py | 10 ++++++++++ tests/test_data_client.py | 3 +-- tests/test_historical_klines.py | 3 +-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/conftest.py b/conftest.py index ddac4a461..2d1803ce4 100644 --- a/conftest.py +++ b/conftest.py @@ -90,3 +90,13 @@ def historical_klines_response_df(data_client): ) #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/tests/test_data_client.py b/tests/test_data_client.py index 69759a094..e38f1328e 100644 --- a/tests/test_data_client.py +++ b/tests/test_data_client.py @@ -4,8 +4,7 @@ from binance.enums import KLINES_RESPONSE_COLUMNS -def test_historical_klines(historical_klines_response_df): - klines_df = historical_klines_response_df +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 d5365b09b..f0496dd07 100644 --- a/tests/test_historical_klines.py +++ b/tests/test_historical_klines.py @@ -9,9 +9,8 @@ client = Client("api_key", "api_secret") -def test_exact_amount(historical_klines_response): +def test_exact_amount(klines): """Test Exact amount returned""" - klines = historical_klines_response assert len(klines) == 500 From 4ecba53d5865b4413a1c200c02f3e44e6f9221d7 Mon Sep 17 00:00:00 2001 From: Mike Rossetti Date: Sat, 9 Jul 2022 14:58:57 -0400 Subject: [PATCH 9/9] Lock pandas version --- extra-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extra-requirements.txt b/extra-requirements.txt index fb6c7ed7e..efd4927ba 100644 --- a/extra-requirements.txt +++ b/extra-requirements.txt @@ -1 +1 @@ -pandas +pandas==1.4.3