From 8aee2c440a2bb62d5560dc7b5f1f9e6a8dc82dca Mon Sep 17 00:00:00 2001 From: "Gavin.Chan" Date: Sun, 26 Nov 2017 16:26:10 +0000 Subject: [PATCH] [#4] Added Poloniex order book query. --- libcryptomarket/api/poloniex_api.py | 30 +++++- libcryptomarket/price.py | 43 +++++++++ tests/test_poloniex_api.py | 136 ++++++++++++++++++++++------ 3 files changed, 180 insertions(+), 29 deletions(-) diff --git a/libcryptomarket/api/poloniex_api.py b/libcryptomarket/api/poloniex_api.py index 899c9da..1b9c883 100644 --- a/libcryptomarket/api/poloniex_api.py +++ b/libcryptomarket/api/poloniex_api.py @@ -14,8 +14,9 @@ def get_return_chart_data(currency_pair, period, start, end=None): :param end: End time in unix timestamp. Optional. """ - assert period in VALID_PERIODS, ( - "Period is not in the valid periods (%s)" % VALID_PERIODS) + if period not in VALID_PERIODS: + raise ValueError( + "Period is not in the valid periods (%s)" % VALID_PERIODS) url = (API_URL + "returnChartData" + "¤cyPair={0}".format(currency_pair) + @@ -32,3 +33,28 @@ def get_return_chart_data(currency_pair, period, start, end=None): raise ValueError("Query error from Poloniex API ({0})".format(rjson)) return r.json() + + +def get_return_order_book(currency_pair, depth=10): + """Return returnOrderBook. + + :param currency_pair: Currency pair. Specify "all" if requesting for all + symbols. + :param depth: Number of depth. Default is 10. + """ + if currency_pair is None: + raise ValueError("Currency pair cannot be None.") + + params = {} + params["currencyPair"] = currency_pair + params["depth"] = depth + + url = API_URL + "returnOrderBook" + + r = requests.get(url, params=params) + r.raise_for_status() + rjson = r.json() + if isinstance(rjson, dict) and 'error' in rjson.keys(): + raise ValueError("Query error from Poloniex API ({0})".format(rjson)) + + return r.json() diff --git a/libcryptomarket/price.py b/libcryptomarket/price.py index e69de29..e2ed4aa 100644 --- a/libcryptomarket/price.py +++ b/libcryptomarket/price.py @@ -0,0 +1,43 @@ +import pandas as pd + + +def get_order_book(source, symbol, depth=5): + """Return the order book. + + :param source: Data source name. + :param symbol: Symbol. + :param depth: Depth of the order book. + """ + source = source.lower() + + if source == 'poloniex': + from libcryptomarket.api.poloniex_api import get_return_order_book + + # Get the raw data + symbol = symbol.replace("/", "_") + data = get_return_order_book(currency_pair=symbol, depth=depth) + + # Align the format as symbol="all" + if symbol == "all": + # Convert it into a multiindex dataframe with symbol at the first + # level of the columns + data = [[pd.DataFrame( + data[symbol][side], + columns=pd.MultiIndex.from_product( + [[symbol], [side], ['price', 'quantity']]), + index=range(1, depth + 1)) for side in ['bids', 'asks']] + for symbol, prices in data.items()] + data = pd.concat(sum(data, []), axis=1) + else: + # Convert it into a dataframe where bid and ask at the columns + data = [pd.DataFrame( + data[side], + columns=pd.MultiIndex.from_product( + [[side], ['price', 'quantity']]), + index=range(1, depth + 1)) for side in ['bids', 'asks']] + data = pd.concat(data, axis=1) + + return data.astype('float64') + else: + raise ValueError("Source ({0}) is not yet implemented.".format( + source)) diff --git a/tests/test_poloniex_api.py b/tests/test_poloniex_api.py index 5965bd5..62e5f8f 100644 --- a/tests/test_poloniex_api.py +++ b/tests/test_poloniex_api.py @@ -6,6 +6,7 @@ from pandas.util.testing import assert_frame_equal import pytest from libcryptomarket.historical import get_historical_prices +from libcryptomarket.price import get_order_book def test_get_historical_prices_poloniex(monkeypatch): @@ -24,7 +25,7 @@ def test_get_historical_prices_poloniex(monkeypatch): "close": 0.00435873, "volume": 44.34555992, "quoteVolume": 10311.88079097, - "weightedAverage":0.00430043 + "weightedAverage": 0.00430043 }, { "date": 1405713600, @@ -52,27 +53,27 @@ def test_get_historical_prices_poloniex(monkeypatch): from_time=datetime(2014, 7, 18, 16, 0, 0)) expected_result = pd.DataFrame([ - { - "r_date": datetime(2014, 7, 18, 16, 0, 0), - "r_high": 0.0045388, - "r_low": 0.00403001, - "r_open": 0.00404545, - "r_close": 0.00435873, - "r_volume": 44.34555992, - "r_quotevolume": 10311.88079097, - "r_weightedaverage":0.00430043 - }, - { - "r_date": datetime(2014, 7, 18, 20, 0, 0), - "r_high": 0.00435, - "r_low": 0.00412, - "r_open": 0.00428012, - "r_close": 0.00412, - "r_volume": 19.12271662, - "r_quotevolume": 4531.85801066, - "r_weightedaverage": 0.00421961 - } - ]).set_index(['r_date']) + { + "r_date": datetime(2014, 7, 18, 16, 0, 0), + "r_high": 0.0045388, + "r_low": 0.00403001, + "r_open": 0.00404545, + "r_close": 0.00435873, + "r_volume": 44.34555992, + "r_quotevolume": 10311.88079097, + "r_weightedaverage": 0.00430043 + }, + { + "r_date": datetime(2014, 7, 18, 20, 0, 0), + "r_high": 0.00435, + "r_low": 0.00412, + "r_open": 0.00428012, + "r_close": 0.00412, + "r_volume": 19.12271662, + "r_quotevolume": 4531.85801066, + "r_weightedaverage": 0.00421961 + } + ]).set_index(['r_date']) expected_result.index.name = 'datetime' assert_frame_equal(result, expected_result) @@ -80,12 +81,12 @@ def test_get_historical_prices_poloniex(monkeypatch): def test_get_historical_prices_poloniex_invalid_period(monkeypatch): with pytest.raises(ValueError): - result = get_historical_prices( + get_historical_prices( source='Poloniex', period='29m', symbol='BTC/XMR', from_time=datetime(2014, 7, 18, 16, 0, 0)) - + def test_get_historical_prices_poloniex_invalid_instrument(monkeypatch): def mockreturn(url, *args, **kwargs): @@ -94,7 +95,7 @@ def test_get_historical_prices_poloniex_invalid_instrument(monkeypatch): @classmethod def json(cls): # Query all symbols - return {"error":"Invalid currency pair."} + return {"error": "Invalid currency pair."} @classmethod def raise_for_status(cls): @@ -106,9 +107,90 @@ def test_get_historical_prices_poloniex_invalid_instrument(monkeypatch): # Test to get historical prices with pytest.raises(ValueError): - result = get_historical_prices( + get_historical_prices( source='Poloniex', period='4h', symbol='BTC/XXX', from_time=datetime(2014, 7, 18, 16, 0, 0)) - \ No newline at end of file + + +def test_get_order_book_poloniex_all_symbols(monkeypatch): + def mockreturn(url, *args, **kwargs): + # The result is from request.get(...).json() + class MockReturnClass: + @classmethod + def json(cls): + # Query all symbols + import json + return json.loads( + """ +{"BTC_AMP": {"asks": [["0.00002755", 16.03393139], + ["0.00002756", 20.24556409]], + "bids": [["0.00002739", 20.23199043], ["0.00002723", 20.30358806]], + "isFrozen": "0", + "seq": 44422443}, + "BTC_ARDR": {"asks": [["0.00003331", 62.49465571], ["0.00003332", 6567.629]], + "bids": [["0.00003285", 25], ["0.00003277", 44.83974499]], + "isFrozen": "0", + "seq": 27008837}} +""" + ) + + @classmethod + def raise_for_status(cls): + pass + + return MockReturnClass() + + monkeypatch.setattr(requests, 'get', mockreturn) + + expected_df = pd.DataFrame([ + [0.00002739, 20.231990, 0.00002755, 16.033931, + 0.00003285, 25.000000, 0.00003331, 62.494656], + [0.00002723, 20.303588, 0.00002756, 20.245564, + 0.00003277, 44.839745, 0.00003332, 6567.629000]], + columns=pd.MultiIndex.from_product( + [['BTC_AMP', 'BTC_ARDR'], + ['bids', 'asks'], + ['price', 'quantity']]), + index=[1, 2]) + + df = get_order_book(source="Poloniex", symbol="all", depth=2) + assert_frame_equal(expected_df.sort_index(axis=1), + df.sort_index(axis=1)) + + +def test_get_order_book_poloniex_one_symbol(monkeypatch): + def mockreturn(url, *args, **kwargs): + # The result is from request.get(...).json() + class MockReturnClass: + @classmethod + def json(cls): + # Query all symbols + import json + return json.loads( + """ +{"asks": [["0.00002755", 16.03393139], + ["0.00002756", 20.24556409]], + "bids": [["0.00002739", 20.23199043], ["0.00002723", 20.30358806]]} +""" + ) + + @classmethod + def raise_for_status(cls): + pass + + return MockReturnClass() + + monkeypatch.setattr(requests, 'get', mockreturn) + + expected_df = pd.DataFrame([ + [0.00002739, 20.231990, 0.00002755, 16.033931], + [0.00002723, 20.303588, 0.00002756, 20.245564]], + columns=pd.MultiIndex.from_product([['bids', 'asks'], + ['price', 'quantity']]), + index=[1, 2]) + + df = get_order_book(source="Poloniex", symbol="BTC_AMP", depth=2) + assert_frame_equal(expected_df, + df)