Merge pull request #293 from jblestang/fix_issue_278
The /status table command was getting slower when we had multiple trades opened
This commit is contained in:
commit
fca6a09a41
@ -134,8 +134,8 @@ def get_balances():
|
|||||||
return _API.get_balances()
|
return _API.get_balances()
|
||||||
|
|
||||||
|
|
||||||
def get_ticker(pair: str) -> dict:
|
def get_ticker(pair: str, refresh: Optional[bool] = True) -> dict:
|
||||||
return _API.get_ticker(pair)
|
return _API.get_ticker(pair, refresh)
|
||||||
|
|
||||||
|
|
||||||
@cached(TTLCache(maxsize=100, ttl=30))
|
@cached(TTLCache(maxsize=100, ttl=30))
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import logging
|
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 bittrex.bittrex import Bittrex as _Bittrex, API_V2_0, API_V1_1
|
||||||
from requests.exceptions import ContentDecodingError
|
from requests.exceptions import ContentDecodingError
|
||||||
@ -38,6 +38,7 @@ class Bittrex(Exchange):
|
|||||||
calls_per_second=1,
|
calls_per_second=1,
|
||||||
api_version=API_V2_0,
|
api_version=API_V2_0,
|
||||||
)
|
)
|
||||||
|
self.cached_ticker = {}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _validate_response(response) -> None:
|
def _validate_response(response) -> None:
|
||||||
@ -95,7 +96,8 @@ class Bittrex(Exchange):
|
|||||||
raise OperationalException('{message}'.format(message=data['message']))
|
raise OperationalException('{message}'.format(message=data['message']))
|
||||||
return data['result']
|
return data['result']
|
||||||
|
|
||||||
def get_ticker(self, pair: str) -> dict:
|
def get_ticker(self, pair: str, refresh: Optional[bool] = True) -> dict:
|
||||||
|
if refresh or pair not in self.cached_ticker.keys():
|
||||||
data = _API.get_ticker(pair.replace('_', '-'))
|
data = _API.get_ticker(pair.replace('_', '-'))
|
||||||
if not data['success']:
|
if not data['success']:
|
||||||
Bittrex._validate_response(data)
|
Bittrex._validate_response(data)
|
||||||
@ -110,11 +112,13 @@ class Bittrex(Exchange):
|
|||||||
raise ContentDecodingError('{message} params=({pair})'.format(
|
raise ContentDecodingError('{message} params=({pair})'.format(
|
||||||
message='Got invalid response from bittrex',
|
message='Got invalid response from bittrex',
|
||||||
pair=pair))
|
pair=pair))
|
||||||
return {
|
# Update the pair
|
||||||
|
self.cached_ticker[pair] = {
|
||||||
'bid': float(data['result']['Bid']),
|
'bid': float(data['result']['Bid']),
|
||||||
'ask': float(data['result']['Ask']),
|
'ask': float(data['result']['Ask']),
|
||||||
'last': float(data['result']['Last']),
|
'last': float(data['result']['Last']),
|
||||||
}
|
}
|
||||||
|
return self.cached_ticker[pair]
|
||||||
|
|
||||||
def get_ticker_history(self, pair: str, tick_interval: int) -> List[Dict]:
|
def get_ticker_history(self, pair: str, tick_interval: int) -> List[Dict]:
|
||||||
if tick_interval == 1:
|
if tick_interval == 1:
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from typing import List, Dict
|
from typing import List, Dict, Optional
|
||||||
|
|
||||||
|
|
||||||
class Exchange(ABC):
|
class Exchange(ABC):
|
||||||
@ -62,10 +62,11 @@ class Exchange(ABC):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def get_ticker(self, pair: str) -> dict:
|
def get_ticker(self, pair: str, refresh: Optional[bool] = True) -> dict:
|
||||||
"""
|
"""
|
||||||
Gets ticker for given pair.
|
Gets ticker for given pair.
|
||||||
:param pair: Pair as str, format: BTC_ETC
|
:param pair: Pair as str, format: BTC_ETC
|
||||||
|
:param refresh: Shall we query a new value or a cached value is enough
|
||||||
:return: dict, format: {
|
:return: dict, format: {
|
||||||
'bid': float,
|
'bid': float,
|
||||||
'ask': float,
|
'ask': float,
|
||||||
|
@ -140,7 +140,7 @@ def _status(bot: Bot, update: Update) -> None:
|
|||||||
if trade.open_order_id:
|
if trade.open_order_id:
|
||||||
order = exchange.get_order(trade.open_order_id)
|
order = exchange.get_order(trade.open_order_id)
|
||||||
# calculate profit and send message to user
|
# 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)
|
current_profit = trade.calc_profit_percent(current_rate)
|
||||||
fmt_close_profit = '{:.2f}%'.format(
|
fmt_close_profit = '{:.2f}%'.format(
|
||||||
round(trade.close_profit * 100, 2)
|
round(trade.close_profit * 100, 2)
|
||||||
@ -193,7 +193,7 @@ def _status_table(bot: Bot, update: Update) -> None:
|
|||||||
trades_list = []
|
trades_list = []
|
||||||
for trade in trades:
|
for trade in trades:
|
||||||
# calculate profit and send message to user
|
# 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([
|
trades_list.append([
|
||||||
trade.id,
|
trade.id,
|
||||||
trade.pair,
|
trade.pair,
|
||||||
@ -301,7 +301,7 @@ def _profit(bot: Bot, update: Update) -> None:
|
|||||||
profit_closed_percent.append(profit_percent)
|
profit_closed_percent.append(profit_percent)
|
||||||
else:
|
else:
|
||||||
# Get current rate
|
# 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_percent = trade.calc_profit_percent(rate=current_rate)
|
||||||
|
|
||||||
profit_all_coin.append(trade.calc_profit(rate=Decimal(trade.close_rate or 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
|
return
|
||||||
|
|
||||||
# Get current rate and execute sell
|
# 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
|
from freqtrade.main import execute_sell
|
||||||
execute_sell(trade, current_rate)
|
execute_sell(trade, current_rate)
|
||||||
|
|
||||||
|
@ -11,7 +11,8 @@ from freqtrade.exchange import init, validate_pairs, buy, sell, get_balance, get
|
|||||||
|
|
||||||
|
|
||||||
def test_init(default_conf, mocker, caplog):
|
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)
|
init(config=default_conf)
|
||||||
assert ('freqtrade.exchange',
|
assert ('freqtrade.exchange',
|
||||||
logging.INFO,
|
logging.INFO,
|
||||||
@ -49,7 +50,8 @@ def test_validate_pairs_not_available(default_conf, mocker):
|
|||||||
|
|
||||||
def test_validate_pairs_not_compatible(default_conf, mocker):
|
def test_validate_pairs_not_compatible(default_conf, mocker):
|
||||||
api_mock = MagicMock()
|
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'
|
default_conf['stake_currency'] = 'ETH'
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
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):
|
def test_buy_prod(default_conf, mocker):
|
||||||
api_mock = MagicMock()
|
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)
|
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||||
|
|
||||||
default_conf['dry_run'] = False
|
default_conf['dry_run'] = False
|
||||||
@ -97,7 +100,8 @@ def test_sell_dry_run(default_conf, mocker):
|
|||||||
|
|
||||||
def test_sell_prod(default_conf, mocker):
|
def test_sell_prod(default_conf, mocker):
|
||||||
api_mock = MagicMock()
|
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)
|
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||||
|
|
||||||
default_conf['dry_run'] = False
|
default_conf['dry_run'] = False
|
||||||
@ -141,7 +145,8 @@ def test_get_balances_prod(default_conf, mocker):
|
|||||||
}
|
}
|
||||||
|
|
||||||
api_mock = MagicMock()
|
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)
|
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||||
|
|
||||||
default_conf['dry_run'] = False
|
default_conf['dry_run'] = False
|
||||||
@ -157,13 +162,29 @@ def test_get_balances_prod(default_conf, mocker):
|
|||||||
def test_get_ticker(mocker, ticker):
|
def test_get_ticker(mocker, ticker):
|
||||||
|
|
||||||
api_mock = MagicMock()
|
api_mock = MagicMock()
|
||||||
api_mock.get_ticker = MagicMock(return_value=ticker())
|
tick = {"success": True, 'result': {'Bid': 0.00001098, 'Ask': 0.00001099, 'Last': 0.0001}}
|
||||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
api_mock.get_ticker = MagicMock(return_value=tick)
|
||||||
|
mocker.patch('freqtrade.exchange.bittrex._API', api_mock)
|
||||||
|
|
||||||
|
# retrieve original ticker
|
||||||
ticker = get_ticker(pair='BTC_ETH')
|
ticker = get_ticker(pair='BTC_ETH')
|
||||||
assert ticker['bid'] == 0.00001098
|
assert ticker['bid'] == 0.00001098
|
||||||
assert ticker['ask'] == 0.00001099
|
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)
|
||||||
assert ticker['bid'] == 0.00001098
|
assert ticker['bid'] == 0.00001098
|
||||||
|
assert ticker['ask'] == 0.00001099
|
||||||
|
|
||||||
|
# force ticker refresh
|
||||||
|
ticker = get_ticker(pair='BTC_ETH', refresh=True)
|
||||||
|
assert ticker['bid'] == 0.5
|
||||||
|
assert ticker['ask'] == 1
|
||||||
|
|
||||||
|
|
||||||
def test_cancel_order_dry_run(default_conf, mocker):
|
def test_cancel_order_dry_run(default_conf, mocker):
|
||||||
@ -174,7 +195,8 @@ def test_cancel_order_dry_run(default_conf, mocker):
|
|||||||
|
|
||||||
|
|
||||||
def test_get_name(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'
|
default_conf['exchange']['name'] = 'bittrex'
|
||||||
init(default_conf)
|
init(default_conf)
|
||||||
|
|
||||||
@ -182,7 +204,8 @@ def test_get_name(default_conf, mocker):
|
|||||||
|
|
||||||
|
|
||||||
def test_get_fee(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)
|
init(default_conf)
|
||||||
|
|
||||||
assert get_fee() == 0.0025
|
assert get_fee() == 0.0025
|
||||||
|
Loading…
Reference in New Issue
Block a user