From e10a3d1f9dca03d74c889a4065355f4a94afa727 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste LE STANG Date: Tue, 2 Jan 2018 10:56:42 +0100 Subject: [PATCH 01/14] get_ticker can return a cached value --- freqtrade/exchange/__init__.py | 4 +-- freqtrade/exchange/bittrex.py | 44 ++++++++++++++++++--------------- freqtrade/exchange/interface.py | 5 ++-- freqtrade/rpc/telegram.py | 8 +++--- 4 files changed, 33 insertions(+), 28 deletions(-) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 7b5c0c753..d41c78921 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -134,8 +134,8 @@ def get_balances(): return _API.get_balances() -def get_ticker(pair: str) -> dict: - return _API.get_ticker(pair) +def get_ticker(pair: str, refresh: Optional[bool] = True) -> dict: + return _API.get_ticker(pair, refresh) @cached(TTLCache(maxsize=100, ttl=30)) diff --git a/freqtrade/exchange/bittrex.py b/freqtrade/exchange/bittrex.py index 3714de070..c153091d0 100644 --- a/freqtrade/exchange/bittrex.py +++ b/freqtrade/exchange/bittrex.py @@ -1,5 +1,5 @@ import logging -from typing import List, Dict +from typing import List, Dict, Optional from bittrex.bittrex import Bittrex as _Bittrex, API_V2_0, API_V1_1 from requests.exceptions import ContentDecodingError @@ -38,6 +38,7 @@ class Bittrex(Exchange): calls_per_second=1, api_version=API_V2_0, ) + self.cached_ticker = {} @staticmethod def _validate_response(response) -> None: @@ -95,26 +96,29 @@ class Bittrex(Exchange): raise OperationalException('{message}'.format(message=data['message'])) return data['result'] - def get_ticker(self, pair: str) -> dict: - data = _API.get_ticker(pair.replace('_', '-')) - if not data['success']: - Bittrex._validate_response(data) - raise OperationalException('{message} params=({pair})'.format( - message=data['message'], - pair=pair)) + def get_ticker(self, pair: str, refresh: Optional[bool] = True) -> dict: + data = _API.get_ticker(pair.replace('_', '-'), refresh) + if refresh: + if not data['success']: + Bittrex._validate_response(data) + raise OperationalException('{message} params=({pair})'.format( + message=data['message'], + pair=pair)) - if not data.get('result') \ - or not data['result'].get('Bid') \ - or not data['result'].get('Ask') \ - or not data['result'].get('Last'): - raise ContentDecodingError('{message} params=({pair})'.format( - message='Got invalid response from bittrex', - pair=pair)) - return { - 'bid': float(data['result']['Bid']), - 'ask': float(data['result']['Ask']), - 'last': float(data['result']['Last']), - } + if not data.get('result') \ + or not data['result'].get('Bid') \ + or not data['result'].get('Ask') \ + or not data['result'].get('Last'): + raise ContentDecodingError('{message} params=({pair})'.format( + message='Got invalid response from bittrex', + pair=pair)) + # Update the pair + self.cached_ticker[pair] = { + 'bid': float(data['result']['Bid']), + 'ask': float(data['result']['Ask']), + 'last': float(data['result']['Last']), + } + return self.cached_ticker[pair] def get_ticker_history(self, pair: str, tick_interval: int) -> List[Dict]: if tick_interval == 1: diff --git a/freqtrade/exchange/interface.py b/freqtrade/exchange/interface.py index a46b3c054..167a824d0 100644 --- a/freqtrade/exchange/interface.py +++ b/freqtrade/exchange/interface.py @@ -1,5 +1,5 @@ from abc import ABC, abstractmethod -from typing import List, Dict +from typing import List, Dict, Optional class Exchange(ABC): @@ -62,10 +62,11 @@ class Exchange(ABC): """ @abstractmethod - def get_ticker(self, pair: str) -> dict: + def get_ticker(self, pair: str, refresh: Optional[bool] = True) -> dict: """ Gets ticker for given pair. :param pair: Pair as str, format: BTC_ETC + :param refresh: Shall we query a new value or a cached value is enought :return: dict, format: { 'bid': float, 'ask': float, diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 7636c2b8a..ba3dd3c3e 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -140,7 +140,7 @@ def _status(bot: Bot, update: Update) -> None: if trade.open_order_id: order = exchange.get_order(trade.open_order_id) # calculate profit and send message to user - current_rate = exchange.get_ticker(trade.pair)['bid'] + current_rate = exchange.get_ticker(trade.pair, False)['bid'] current_profit = trade.calc_profit_percent(current_rate) fmt_close_profit = '{:.2f}%'.format( round(trade.close_profit * 100, 2) @@ -193,7 +193,7 @@ def _status_table(bot: Bot, update: Update) -> None: trades_list = [] for trade in trades: # calculate profit and send message to user - current_rate = exchange.get_ticker(trade.pair)['bid'] + current_rate = exchange.get_ticker(trade.pair, False)['bid'] trades_list.append([ trade.id, trade.pair, @@ -301,7 +301,7 @@ def _profit(bot: Bot, update: Update) -> None: profit_closed_percent.append(profit_percent) else: # Get current rate - current_rate = exchange.get_ticker(trade.pair)['bid'] + current_rate = exchange.get_ticker(trade.pair, False)['bid'] profit_percent = trade.calc_profit_percent(rate=current_rate) profit_all_coin.append(trade.calc_profit(rate=Decimal(trade.close_rate or current_rate))) @@ -577,7 +577,7 @@ def _exec_forcesell(trade: Trade) -> None: return # Get current rate and execute sell - current_rate = exchange.get_ticker(trade.pair)['bid'] + current_rate = exchange.get_ticker(trade.pair, False)['bid'] from freqtrade.main import execute_sell execute_sell(trade, current_rate) From 165781a5459e01fbd03b02749ad1874dc85dd64e Mon Sep 17 00:00:00 2001 From: Jean-Baptiste LE STANG Date: Tue, 2 Jan 2018 11:00:22 +0100 Subject: [PATCH 02/14] force refresh is the value has never been set --- freqtrade/exchange/bittrex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/exchange/bittrex.py b/freqtrade/exchange/bittrex.py index c153091d0..c1ea0608f 100644 --- a/freqtrade/exchange/bittrex.py +++ b/freqtrade/exchange/bittrex.py @@ -98,7 +98,7 @@ class Bittrex(Exchange): def get_ticker(self, pair: str, refresh: Optional[bool] = True) -> dict: data = _API.get_ticker(pair.replace('_', '-'), refresh) - if refresh: + if refresh or pair not in self.cached_ticker.keys(): if not data['success']: Bittrex._validate_response(data) raise OperationalException('{message} params=({pair})'.format( From 90d3c0953685ba2c73c371a2bdc95a97251685e7 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste LE STANG Date: Tue, 2 Jan 2018 14:05:27 +0100 Subject: [PATCH 03/14] fixing refresh argument ... --- freqtrade/exchange/bittrex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/exchange/bittrex.py b/freqtrade/exchange/bittrex.py index c1ea0608f..139f2896f 100644 --- a/freqtrade/exchange/bittrex.py +++ b/freqtrade/exchange/bittrex.py @@ -97,7 +97,7 @@ class Bittrex(Exchange): return data['result'] def get_ticker(self, pair: str, refresh: Optional[bool] = True) -> dict: - data = _API.get_ticker(pair.replace('_', '-'), refresh) + data = _API.get_ticker(pair.replace('_', '-')) if refresh or pair not in self.cached_ticker.keys(): if not data['success']: Bittrex._validate_response(data) From 5f696a0cce492fce1d565acda233a5b83f9b25b6 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste LE STANG Date: Tue, 2 Jan 2018 14:09:56 +0100 Subject: [PATCH 04/14] really fixing --- freqtrade/exchange/bittrex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/exchange/bittrex.py b/freqtrade/exchange/bittrex.py index 139f2896f..4883db037 100644 --- a/freqtrade/exchange/bittrex.py +++ b/freqtrade/exchange/bittrex.py @@ -97,8 +97,8 @@ class Bittrex(Exchange): return data['result'] def get_ticker(self, pair: str, refresh: Optional[bool] = True) -> dict: - data = _API.get_ticker(pair.replace('_', '-')) if refresh or pair not in self.cached_ticker.keys(): + data = _API.get_ticker(pair.replace('_', '-')) if not data['success']: Bittrex._validate_response(data) raise OperationalException('{message} params=({pair})'.format( From 050e73d96086626961085475b1b310bdd681ae54 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste LE STANG Date: Wed, 3 Jan 2018 17:51:01 +0100 Subject: [PATCH 05/14] fix a typo in the description of get_ticker --- freqtrade/exchange/interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/exchange/interface.py b/freqtrade/exchange/interface.py index 167a824d0..1be84abe5 100644 --- a/freqtrade/exchange/interface.py +++ b/freqtrade/exchange/interface.py @@ -66,7 +66,7 @@ class Exchange(ABC): """ Gets ticker for given pair. :param pair: Pair as str, format: BTC_ETC - :param refresh: Shall we query a new value or a cached value is enought + :param refresh: Shall we query a new value or a cached value is enough :return: dict, format: { 'bid': float, 'ask': float, From 75955fcc04b9b4d4ca4127085c67b59132dece39 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste LE STANG Date: Wed, 3 Jan 2018 17:58:08 +0100 Subject: [PATCH 06/14] Add a unitest and fix pep8 --- freqtrade/tests/exchange/test_exchange.py | 35 +++++++++++++++++------ 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index 2da657642..e47bb56eb 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -11,7 +11,8 @@ from freqtrade.exchange import init, validate_pairs, buy, sell, get_balance, get def test_init(default_conf, mocker, caplog): - mocker.patch('freqtrade.exchange.validate_pairs', side_effect=lambda s: True) + mocker.patch('freqtrade.exchange.validate_pairs', + side_effect=lambda s: True) init(config=default_conf) assert ('freqtrade.exchange', logging.INFO, @@ -25,7 +26,7 @@ def test_init_exception(default_conf, mocker): with pytest.raises( OperationalException, match='Exchange {} is not supported'.format(default_conf['exchange']['name'])): - init(config=default_conf) + init(config=default_conf) def test_validate_pairs(default_conf, mocker): @@ -49,7 +50,8 @@ def test_validate_pairs_not_available(default_conf, mocker): def test_validate_pairs_not_compatible(default_conf, mocker): api_mock = MagicMock() - api_mock.get_markets = MagicMock(return_value=['BTC_ETH', 'BTC_TKN', 'BTC_TRST', 'BTC_SWT']) + api_mock.get_markets = MagicMock( + return_value=['BTC_ETH', 'BTC_TKN', 'BTC_TRST', 'BTC_SWT']) default_conf['stake_currency'] = 'ETH' mocker.patch('freqtrade.exchange._API', api_mock) mocker.patch.dict('freqtrade.exchange._CONF', default_conf) @@ -79,7 +81,8 @@ def test_buy_dry_run(default_conf, mocker): def test_buy_prod(default_conf, mocker): api_mock = MagicMock() - api_mock.buy = MagicMock(return_value='dry_run_buy_{}'.format(randint(0, 10**6))) + api_mock.buy = MagicMock( + return_value='dry_run_buy_{}'.format(randint(0, 10**6))) mocker.patch('freqtrade.exchange._API', api_mock) default_conf['dry_run'] = False @@ -97,7 +100,8 @@ def test_sell_dry_run(default_conf, mocker): def test_sell_prod(default_conf, mocker): api_mock = MagicMock() - api_mock.sell = MagicMock(return_value='dry_run_sell_{}'.format(randint(0, 10**6))) + api_mock.sell = MagicMock( + return_value='dry_run_sell_{}'.format(randint(0, 10**6))) mocker.patch('freqtrade.exchange._API', api_mock) default_conf['dry_run'] = False @@ -141,7 +145,8 @@ def test_get_balances_prod(default_conf, mocker): } api_mock = MagicMock() - api_mock.get_balances = MagicMock(return_value=[balance_item, balance_item, balance_item]) + api_mock.get_balances = MagicMock( + return_value=[balance_item, balance_item, balance_item]) mocker.patch('freqtrade.exchange._API', api_mock) default_conf['dry_run'] = False @@ -163,7 +168,19 @@ def test_get_ticker(mocker, ticker): ticker = get_ticker(pair='BTC_ETH') assert ticker['bid'] == 0.00001098 assert ticker['ask'] == 0.00001099 + + # if not caching the result we should get the same ticker + ticker = get_ticker(pair='BTC_ETH', refresh=False) assert ticker['bid'] == 0.00001098 + assert ticker['ask'] == 0.00001099 + + # change the ticker + api_mock.get_ticker = MagicMock(return_value={"bid": 0, "ask": 1}) + mocker.patch('freqtrade.exchange._API', api_mock) + + ticker = get_ticker(pair='BTC_ETH', refresh=True) + assert ticker['bid'] == 0 + assert ticker['ask'] == 1 def test_cancel_order_dry_run(default_conf, mocker): @@ -174,7 +191,8 @@ def test_cancel_order_dry_run(default_conf, mocker): def test_get_name(default_conf, mocker): - mocker.patch('freqtrade.exchange.validate_pairs', side_effect=lambda s: True) + mocker.patch('freqtrade.exchange.validate_pairs', + side_effect=lambda s: True) default_conf['exchange']['name'] = 'bittrex' init(default_conf) @@ -182,7 +200,8 @@ def test_get_name(default_conf, mocker): def test_get_fee(default_conf, mocker): - mocker.patch('freqtrade.exchange.validate_pairs', side_effect=lambda s: True) + mocker.patch('freqtrade.exchange.validate_pairs', + side_effect=lambda s: True) init(default_conf) assert get_fee() == 0.0025 From 8175eaa48ab946e24ddfba8602d0a320d2f97bc1 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste LE STANG Date: Tue, 2 Jan 2018 10:56:42 +0100 Subject: [PATCH 07/14] get_ticker can return a cached value --- freqtrade/exchange/__init__.py | 4 +-- freqtrade/exchange/bittrex.py | 44 ++++++++++++++++++--------------- freqtrade/exchange/interface.py | 5 ++-- freqtrade/rpc/telegram.py | 8 +++--- 4 files changed, 33 insertions(+), 28 deletions(-) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 7b5c0c753..d41c78921 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -134,8 +134,8 @@ def get_balances(): return _API.get_balances() -def get_ticker(pair: str) -> dict: - return _API.get_ticker(pair) +def get_ticker(pair: str, refresh: Optional[bool] = True) -> dict: + return _API.get_ticker(pair, refresh) @cached(TTLCache(maxsize=100, ttl=30)) diff --git a/freqtrade/exchange/bittrex.py b/freqtrade/exchange/bittrex.py index 3714de070..c153091d0 100644 --- a/freqtrade/exchange/bittrex.py +++ b/freqtrade/exchange/bittrex.py @@ -1,5 +1,5 @@ import logging -from typing import List, Dict +from typing import List, Dict, Optional from bittrex.bittrex import Bittrex as _Bittrex, API_V2_0, API_V1_1 from requests.exceptions import ContentDecodingError @@ -38,6 +38,7 @@ class Bittrex(Exchange): calls_per_second=1, api_version=API_V2_0, ) + self.cached_ticker = {} @staticmethod def _validate_response(response) -> None: @@ -95,26 +96,29 @@ class Bittrex(Exchange): raise OperationalException('{message}'.format(message=data['message'])) return data['result'] - def get_ticker(self, pair: str) -> dict: - data = _API.get_ticker(pair.replace('_', '-')) - if not data['success']: - Bittrex._validate_response(data) - raise OperationalException('{message} params=({pair})'.format( - message=data['message'], - pair=pair)) + def get_ticker(self, pair: str, refresh: Optional[bool] = True) -> dict: + data = _API.get_ticker(pair.replace('_', '-'), refresh) + if refresh: + if not data['success']: + Bittrex._validate_response(data) + raise OperationalException('{message} params=({pair})'.format( + message=data['message'], + pair=pair)) - if not data.get('result') \ - or not data['result'].get('Bid') \ - or not data['result'].get('Ask') \ - or not data['result'].get('Last'): - raise ContentDecodingError('{message} params=({pair})'.format( - message='Got invalid response from bittrex', - pair=pair)) - return { - 'bid': float(data['result']['Bid']), - 'ask': float(data['result']['Ask']), - 'last': float(data['result']['Last']), - } + if not data.get('result') \ + or not data['result'].get('Bid') \ + or not data['result'].get('Ask') \ + or not data['result'].get('Last'): + raise ContentDecodingError('{message} params=({pair})'.format( + message='Got invalid response from bittrex', + pair=pair)) + # Update the pair + self.cached_ticker[pair] = { + 'bid': float(data['result']['Bid']), + 'ask': float(data['result']['Ask']), + 'last': float(data['result']['Last']), + } + return self.cached_ticker[pair] def get_ticker_history(self, pair: str, tick_interval: int) -> List[Dict]: if tick_interval == 1: diff --git a/freqtrade/exchange/interface.py b/freqtrade/exchange/interface.py index a46b3c054..167a824d0 100644 --- a/freqtrade/exchange/interface.py +++ b/freqtrade/exchange/interface.py @@ -1,5 +1,5 @@ from abc import ABC, abstractmethod -from typing import List, Dict +from typing import List, Dict, Optional class Exchange(ABC): @@ -62,10 +62,11 @@ class Exchange(ABC): """ @abstractmethod - def get_ticker(self, pair: str) -> dict: + def get_ticker(self, pair: str, refresh: Optional[bool] = True) -> dict: """ Gets ticker for given pair. :param pair: Pair as str, format: BTC_ETC + :param refresh: Shall we query a new value or a cached value is enought :return: dict, format: { 'bid': float, 'ask': float, diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index e5e18ad5b..4d7957f59 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -140,7 +140,7 @@ def _status(bot: Bot, update: Update) -> None: if trade.open_order_id: order = exchange.get_order(trade.open_order_id) # calculate profit and send message to user - current_rate = exchange.get_ticker(trade.pair)['bid'] + current_rate = exchange.get_ticker(trade.pair, False)['bid'] current_profit = trade.calc_profit_percent(current_rate) fmt_close_profit = '{:.2f}%'.format( round(trade.close_profit * 100, 2) @@ -193,7 +193,7 @@ def _status_table(bot: Bot, update: Update) -> None: trades_list = [] for trade in trades: # calculate profit and send message to user - current_rate = exchange.get_ticker(trade.pair)['bid'] + current_rate = exchange.get_ticker(trade.pair, False)['bid'] trades_list.append([ trade.id, trade.pair, @@ -301,7 +301,7 @@ def _profit(bot: Bot, update: Update) -> None: profit_closed_percent.append(profit_percent) else: # Get current rate - current_rate = exchange.get_ticker(trade.pair)['bid'] + current_rate = exchange.get_ticker(trade.pair, False)['bid'] profit_percent = trade.calc_profit_percent(rate=current_rate) profit_all_coin.append(trade.calc_profit(rate=Decimal(trade.close_rate or current_rate))) @@ -579,7 +579,7 @@ def _exec_forcesell(trade: Trade) -> None: return # Get current rate and execute sell - current_rate = exchange.get_ticker(trade.pair)['bid'] + current_rate = exchange.get_ticker(trade.pair, False)['bid'] from freqtrade.main import execute_sell execute_sell(trade, current_rate) From c72e9c3ceff827caf95a7ea6a1a15027a650147a Mon Sep 17 00:00:00 2001 From: Jean-Baptiste LE STANG Date: Tue, 2 Jan 2018 11:00:22 +0100 Subject: [PATCH 08/14] force refresh is the value has never been set --- freqtrade/exchange/bittrex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/exchange/bittrex.py b/freqtrade/exchange/bittrex.py index c153091d0..c1ea0608f 100644 --- a/freqtrade/exchange/bittrex.py +++ b/freqtrade/exchange/bittrex.py @@ -98,7 +98,7 @@ class Bittrex(Exchange): def get_ticker(self, pair: str, refresh: Optional[bool] = True) -> dict: data = _API.get_ticker(pair.replace('_', '-'), refresh) - if refresh: + if refresh or pair not in self.cached_ticker.keys(): if not data['success']: Bittrex._validate_response(data) raise OperationalException('{message} params=({pair})'.format( From 9e7a4c3717088d9cc8da0a937259c8fc8b49033b Mon Sep 17 00:00:00 2001 From: Jean-Baptiste LE STANG Date: Tue, 2 Jan 2018 14:05:27 +0100 Subject: [PATCH 09/14] fixing refresh argument ... --- freqtrade/exchange/bittrex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/exchange/bittrex.py b/freqtrade/exchange/bittrex.py index c1ea0608f..139f2896f 100644 --- a/freqtrade/exchange/bittrex.py +++ b/freqtrade/exchange/bittrex.py @@ -97,7 +97,7 @@ class Bittrex(Exchange): return data['result'] def get_ticker(self, pair: str, refresh: Optional[bool] = True) -> dict: - data = _API.get_ticker(pair.replace('_', '-'), refresh) + data = _API.get_ticker(pair.replace('_', '-')) if refresh or pair not in self.cached_ticker.keys(): if not data['success']: Bittrex._validate_response(data) From 80c4dea875911b6780d802b1eca1ec559eded584 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste LE STANG Date: Tue, 2 Jan 2018 14:09:56 +0100 Subject: [PATCH 10/14] really fixing --- freqtrade/exchange/bittrex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/exchange/bittrex.py b/freqtrade/exchange/bittrex.py index 139f2896f..4883db037 100644 --- a/freqtrade/exchange/bittrex.py +++ b/freqtrade/exchange/bittrex.py @@ -97,8 +97,8 @@ class Bittrex(Exchange): return data['result'] def get_ticker(self, pair: str, refresh: Optional[bool] = True) -> dict: - data = _API.get_ticker(pair.replace('_', '-')) if refresh or pair not in self.cached_ticker.keys(): + data = _API.get_ticker(pair.replace('_', '-')) if not data['success']: Bittrex._validate_response(data) raise OperationalException('{message} params=({pair})'.format( From 6be607e528a8b8737646c3fc16543d6da3aad1f3 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste LE STANG Date: Wed, 3 Jan 2018 17:51:01 +0100 Subject: [PATCH 11/14] fix a typo in the description of get_ticker --- freqtrade/exchange/interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/exchange/interface.py b/freqtrade/exchange/interface.py index 167a824d0..1be84abe5 100644 --- a/freqtrade/exchange/interface.py +++ b/freqtrade/exchange/interface.py @@ -66,7 +66,7 @@ class Exchange(ABC): """ Gets ticker for given pair. :param pair: Pair as str, format: BTC_ETC - :param refresh: Shall we query a new value or a cached value is enought + :param refresh: Shall we query a new value or a cached value is enough :return: dict, format: { 'bid': float, 'ask': float, From 975a785e683795e99c8558af9c41df589501ccd8 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste LE STANG Date: Wed, 3 Jan 2018 17:58:08 +0100 Subject: [PATCH 12/14] Add a unitest and fix pep8 --- freqtrade/tests/exchange/test_exchange.py | 35 +++++++++++++++++------ 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index 2da657642..e47bb56eb 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -11,7 +11,8 @@ from freqtrade.exchange import init, validate_pairs, buy, sell, get_balance, get def test_init(default_conf, mocker, caplog): - mocker.patch('freqtrade.exchange.validate_pairs', side_effect=lambda s: True) + mocker.patch('freqtrade.exchange.validate_pairs', + side_effect=lambda s: True) init(config=default_conf) assert ('freqtrade.exchange', logging.INFO, @@ -25,7 +26,7 @@ def test_init_exception(default_conf, mocker): with pytest.raises( OperationalException, match='Exchange {} is not supported'.format(default_conf['exchange']['name'])): - init(config=default_conf) + init(config=default_conf) def test_validate_pairs(default_conf, mocker): @@ -49,7 +50,8 @@ def test_validate_pairs_not_available(default_conf, mocker): def test_validate_pairs_not_compatible(default_conf, mocker): api_mock = MagicMock() - api_mock.get_markets = MagicMock(return_value=['BTC_ETH', 'BTC_TKN', 'BTC_TRST', 'BTC_SWT']) + api_mock.get_markets = MagicMock( + return_value=['BTC_ETH', 'BTC_TKN', 'BTC_TRST', 'BTC_SWT']) default_conf['stake_currency'] = 'ETH' mocker.patch('freqtrade.exchange._API', api_mock) mocker.patch.dict('freqtrade.exchange._CONF', default_conf) @@ -79,7 +81,8 @@ def test_buy_dry_run(default_conf, mocker): def test_buy_prod(default_conf, mocker): api_mock = MagicMock() - api_mock.buy = MagicMock(return_value='dry_run_buy_{}'.format(randint(0, 10**6))) + api_mock.buy = MagicMock( + return_value='dry_run_buy_{}'.format(randint(0, 10**6))) mocker.patch('freqtrade.exchange._API', api_mock) default_conf['dry_run'] = False @@ -97,7 +100,8 @@ def test_sell_dry_run(default_conf, mocker): def test_sell_prod(default_conf, mocker): api_mock = MagicMock() - api_mock.sell = MagicMock(return_value='dry_run_sell_{}'.format(randint(0, 10**6))) + api_mock.sell = MagicMock( + return_value='dry_run_sell_{}'.format(randint(0, 10**6))) mocker.patch('freqtrade.exchange._API', api_mock) default_conf['dry_run'] = False @@ -141,7 +145,8 @@ def test_get_balances_prod(default_conf, mocker): } api_mock = MagicMock() - api_mock.get_balances = MagicMock(return_value=[balance_item, balance_item, balance_item]) + api_mock.get_balances = MagicMock( + return_value=[balance_item, balance_item, balance_item]) mocker.patch('freqtrade.exchange._API', api_mock) default_conf['dry_run'] = False @@ -163,7 +168,19 @@ def test_get_ticker(mocker, ticker): ticker = get_ticker(pair='BTC_ETH') assert ticker['bid'] == 0.00001098 assert ticker['ask'] == 0.00001099 + + # if not caching the result we should get the same ticker + ticker = get_ticker(pair='BTC_ETH', refresh=False) assert ticker['bid'] == 0.00001098 + assert ticker['ask'] == 0.00001099 + + # change the ticker + api_mock.get_ticker = MagicMock(return_value={"bid": 0, "ask": 1}) + mocker.patch('freqtrade.exchange._API', api_mock) + + ticker = get_ticker(pair='BTC_ETH', refresh=True) + assert ticker['bid'] == 0 + assert ticker['ask'] == 1 def test_cancel_order_dry_run(default_conf, mocker): @@ -174,7 +191,8 @@ def test_cancel_order_dry_run(default_conf, mocker): def test_get_name(default_conf, mocker): - mocker.patch('freqtrade.exchange.validate_pairs', side_effect=lambda s: True) + mocker.patch('freqtrade.exchange.validate_pairs', + side_effect=lambda s: True) default_conf['exchange']['name'] = 'bittrex' init(default_conf) @@ -182,7 +200,8 @@ def test_get_name(default_conf, mocker): def test_get_fee(default_conf, mocker): - mocker.patch('freqtrade.exchange.validate_pairs', side_effect=lambda s: True) + mocker.patch('freqtrade.exchange.validate_pairs', + side_effect=lambda s: True) init(default_conf) assert get_fee() == 0.0025 From b722a89276cd38c36609e8ad8dabfe96fb31fba0 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste LE STANG Date: Sun, 7 Jan 2018 21:24:17 +0100 Subject: [PATCH 13/14] fixing unittest --- freqtrade/tests/exchange/test_exchange.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index e47bb56eb..e7914466e 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -162,24 +162,27 @@ def test_get_balances_prod(default_conf, mocker): def test_get_ticker(mocker, ticker): api_mock = MagicMock() - api_mock.get_ticker = MagicMock(return_value=ticker()) - mocker.patch('freqtrade.exchange._API', api_mock) + tick = {"success": True, 'result': {'Bid': 0.00001098, 'Ask': 0.00001099, 'Last': 0.0001}} + api_mock.get_ticker = MagicMock(return_value=tick) + mocker.patch('freqtrade.exchange.bittrex._API', api_mock) ticker = get_ticker(pair='BTC_ETH') assert ticker['bid'] == 0.00001098 assert ticker['ask'] == 0.00001099 + # change the ticker + tick = {"success": True, 'result': {"Bid": 0.5, "Ask": 1, "Last": 42}} + api_mock.get_ticker = MagicMock(return_value=tick) + mocker.patch('freqtrade.exchange.bittrex._API', api_mock) + # if not caching the result we should get the same ticker ticker = get_ticker(pair='BTC_ETH', refresh=False) + print(str(ticker)) assert ticker['bid'] == 0.00001098 assert ticker['ask'] == 0.00001099 - # change the ticker - api_mock.get_ticker = MagicMock(return_value={"bid": 0, "ask": 1}) - mocker.patch('freqtrade.exchange._API', api_mock) - ticker = get_ticker(pair='BTC_ETH', refresh=True) - assert ticker['bid'] == 0 + assert ticker['bid'] == 0.5 assert ticker['ask'] == 1 From 4c8ae3a7afa4f9aba9835a9640ce14fd72bfdc0f Mon Sep 17 00:00:00 2001 From: Jean-Baptiste LE STANG Date: Sun, 7 Jan 2018 23:15:33 +0100 Subject: [PATCH 14/14] without debug print --- freqtrade/tests/exchange/test_exchange.py | 1 - 1 file changed, 1 deletion(-) diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index e9a7f8aa5..56218b2bc 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -178,7 +178,6 @@ def test_get_ticker(mocker, ticker): # if not caching the result we should get the same ticker ticker = get_ticker(pair='BTC_ETH', refresh=False) - print(str(ticker)) assert ticker['bid'] == 0.00001098 assert ticker['ask'] == 0.00001099