diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 17e9649af..fbf8f38b2 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -85,6 +85,10 @@ def get_balance(currency: str) -> float: return EXCHANGE.get_balance(currency) +def get_ticker(pair: str) -> Dict[str, float]: + return EXCHANGE.get_ticker(pair) + + def get_orderbook(pair: str, top_most: Optional[int] = None) -> Dict[str, List[Dict]]: return EXCHANGE.get_orderbook(pair, top_most) diff --git a/freqtrade/exchange/bittrex.py b/freqtrade/exchange/bittrex.py index c686cd8c1..d45028588 100644 --- a/freqtrade/exchange/bittrex.py +++ b/freqtrade/exchange/bittrex.py @@ -69,6 +69,13 @@ class Bittrex(Exchange): raise RuntimeError('{}: {}'.format(self.name.upper(), data['message'])) return float(data['result']['Balance'] or 0.0) + def get_ticker(self, pair: str) -> Dict[str, float]: + data = self.get_orderbook(pair, top_most=1) + return { + 'bid': data['bid'][0]['Rate'], + 'ask': data['ask'][0]['Rate'], + } + def get_orderbook(self, pair: str, top_most: Optional[int] = None) -> Dict[str, List[Dict]]: data = _API.get_orderbook(pair.replace('_', '-')) if not data['success']: diff --git a/freqtrade/exchange/interface.py b/freqtrade/exchange/interface.py index 67c910504..56629c5b4 100644 --- a/freqtrade/exchange/interface.py +++ b/freqtrade/exchange/interface.py @@ -49,6 +49,17 @@ class Exchange(ABC): :return: float """ + @abstractmethod + def get_ticker(self, pair: str) -> Dict[str, float]: + """ + Gets ticker for given pair. + :param pair: Pair as str, format: BTC_ETC + :return: dict, format: { + 'bid': float, + 'ask': float + } + """ + @abstractmethod def get_orderbook(self, pair: str, top_most: Optional[int] = None) -> Dict[str, List[Dict]]: """ diff --git a/freqtrade/main.py b/freqtrade/main.py index 9fe817db4..7cb88cffe 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -134,7 +134,7 @@ def handle_trade(trade: Trade) -> None: logger.debug('Handling open trade %s ...', trade) - current_rate = exchange.get_orderbook(trade.pair, top_most=1)['bid'][0]['Rate'] + current_rate = exchange.get_ticker(trade.pair)['bid'] if should_sell(trade, current_rate, datetime.utcnow()): execute_sell(trade, current_rate) return @@ -143,15 +143,14 @@ def handle_trade(trade: Trade) -> None: logger.exception('Unable to handle open order') -def get_target_bid(orderbook: Dict[str, List[Dict]]) -> float: +def get_target_bid(ticker: Dict[str, float]) -> float: """ Calculates bid target between bid and ask prices from the given orderbook - :param orderbook: + :param ticker: ticker data :return: target bit as float """ - ask = orderbook['ask'][0]['Rate'] # Get lowest ask - bid = orderbook['bid'][0]['Rate'] # Get highest bid + ask, bid = ticker['ask'], ticker['bid'] balance = _CONF['bid_strategy']['bid_ask_balance'] return bid + balance * (ask - bid) @@ -186,7 +185,7 @@ def create_trade(stake_amount: float) -> Optional[Trade]: else: return None - open_rate = get_target_bid(exchange.get_orderbook(pair, top_most=1)) + open_rate = get_target_bid(exchange.get_ticker(pair)) amount = stake_amount / open_rate order_id = exchange.buy(pair, open_rate, amount) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index a09e6087e..5312125ed 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -99,7 +99,7 @@ def _status(bot: Bot, update: Update) -> None: else: for trade in trades: # calculate profit and send message to user - current_rate = exchange.get_orderbook(trade.pair, top_most=1)['bid'][0]['Rate'] + current_rate = exchange.get_ticker(trade.pair)['bid'] current_profit = 100 * ((current_rate - trade.open_rate) / trade.open_rate) orders = exchange.get_open_orders(trade.pair) orders = [o for o in orders if o['id'] == trade.open_order_id] @@ -156,7 +156,7 @@ def _profit(bot: Bot, update: Update) -> None: profit = trade.close_profit else: # Get current rate - current_rate = exchange.get_orderbook(trade.pair, top_most=1)['bid'][0]['Rate'] + current_rate = exchange.get_ticker(trade.pair)['bid'] profit = 100 * ((current_rate - trade.open_rate) / trade.open_rate) profit_amounts.append((profit / 100) * trade.stake_amount) @@ -250,7 +250,7 @@ def _forcesell(bot: Bot, update: Update) -> None: send_msg('There is no open trade with ID: `{}`'.format(trade_id)) return # Get current rate - current_rate = exchange.get_orderbook(trade.pair, top_most=1)['bid'][0]['Rate'] + current_rate = exchange.get_ticker(trade.pair)['bid'] # Get available balance currency = trade.pair.split('_')[1] balance = exchange.get_balance(currency) diff --git a/freqtrade/tests/test_main.py b/freqtrade/tests/test_main.py index 3479ba898..a0907b8a8 100644 --- a/freqtrade/tests/test_main.py +++ b/freqtrade/tests/test_main.py @@ -55,15 +55,9 @@ def test_create_trade(conf, mocker): mocker.patch.multiple('freqtrade.main.telegram', init=MagicMock(), send_msg=MagicMock()) mocker.patch.multiple('freqtrade.main.exchange', validate_pairs=MagicMock(), - get_orderbook=MagicMock(return_value={ - 'bid': [{ - 'Quantity': 1, - 'Rate': 0.07256061 - }], - 'ask': [{ - 'Quantity': 1, - 'Rate': 0.072661 - }] + get_ticker=MagicMock(return_value={ + 'bid': 0.07256061, + 'ask': 0.072661, }), buy=MagicMock(return_value='mocked_order_id')) # Save state of current whitelist @@ -94,15 +88,9 @@ def test_handle_trade(conf, mocker): mocker.patch.multiple('freqtrade.main.telegram', init=MagicMock(), send_msg=MagicMock()) mocker.patch.multiple('freqtrade.main.exchange', validate_pairs=MagicMock(), - get_orderbook=MagicMock(return_value={ - 'bid': [{ - 'Quantity': 1, - 'Rate': 0.17256061 - }], - 'ask': [{ - 'Quantity': 1, - 'Rate': 0.172661 - }] + get_ticker=MagicMock(return_value={ + 'bid': 0.17256061, + 'ask': 0.172661, }), buy=MagicMock(return_value='mocked_order_id')) trade = Trade.query.filter(Trade.is_open.is_(True)).first() @@ -129,44 +117,14 @@ def test_close_trade(conf, mocker): def test_balance_fully_bid_side(mocker): mocker.patch.dict('freqtrade.main._CONF', {'bid_strategy': {'bid_ask_balance': 0.0}}) - orderbook = { - 'bid': [{ - 'Quantity': 10, - 'Rate': 10 - }], - 'ask': [{ - 'Quantity': 20, - 'Rate': 20 - }] - } - assert get_target_bid(orderbook) == 10 + assert get_target_bid({'bid': 10, 'ask': 20}) == 10 def test_balance_fully_ask_side(mocker): mocker.patch.dict('freqtrade.main._CONF', {'bid_strategy': {'bid_ask_balance': 1.0}}) - orderbook = { - 'bid': [{ - 'Quantity': 10, - 'Rate': 10 - }], - 'ask': [{ - 'Quantity': 20, - 'Rate': 20 - }] - } - assert get_target_bid(orderbook) == 20 + assert get_target_bid({'bid': 10, 'ask': 20}) == 20 def test_balance_half(mocker): mocker.patch.dict('freqtrade.main._CONF', {'bid_strategy': {'bid_ask_balance': 0.5}}) - orderbook = { - 'bid': [{ - 'Quantity': 10, - 'Rate': 10 - }], - 'ask': [{ - 'Quantity': 20, - 'Rate': 20 - }] - } - assert get_target_bid(orderbook) == 15 + assert get_target_bid({'bid': 10, 'ask': 20}) == 15 diff --git a/freqtrade/tests/test_telegram.py b/freqtrade/tests/test_telegram.py index d3d54197d..2d1f4f201 100644 --- a/freqtrade/tests/test_telegram.py +++ b/freqtrade/tests/test_telegram.py @@ -65,15 +65,9 @@ def test_status_handle(conf, update, mocker): mocker.patch.multiple('freqtrade.main.telegram', _CONF=conf, init=MagicMock(), send_msg=msg_mock) mocker.patch.multiple('freqtrade.main.exchange', validate_pairs=MagicMock(), - get_orderbook=MagicMock(return_value={ - 'bid': [{ - 'Quantity': 1, - 'Rate': 0.07256061 - }], - 'ask': [{ - 'Quantity': 1, - 'Rate': 0.072661 - }] + get_ticker=MagicMock(return_value={ + 'bid': 0.07256061, + 'ask': 0.072661, }), buy=MagicMock(return_value='mocked_order_id')) init(conf, 'sqlite://') @@ -96,15 +90,9 @@ def test_profit_handle(conf, update, mocker): mocker.patch.multiple('freqtrade.main.telegram', _CONF=conf, init=MagicMock(), send_msg=msg_mock) mocker.patch.multiple('freqtrade.main.exchange', validate_pairs=MagicMock(), - get_orderbook=MagicMock(return_value={ - 'bid': [{ - 'Quantity': 1, - 'Rate': 0.07256061 - }], - 'ask': [{ - 'Quantity': 1, - 'Rate': 0.072661 - }] + get_ticker=MagicMock(return_value={ + 'bid': 0.07256061, + 'ask': 0.072661, }), buy=MagicMock(return_value='mocked_order_id')) init(conf, 'sqlite://') @@ -132,15 +120,9 @@ def test_forcesell_handle(conf, update, mocker): mocker.patch.multiple('freqtrade.main.telegram', _CONF=conf, init=MagicMock(), send_msg=msg_mock) mocker.patch.multiple('freqtrade.main.exchange', validate_pairs=MagicMock(), - get_orderbook=MagicMock(return_value={ - 'bid': [{ - 'Quantity': 1, - 'Rate': 0.07256061 - }], - 'ask': [{ - 'Quantity': 1, - 'Rate': 0.072661 - }] + get_ticker=MagicMock(return_value={ + 'bid': 0.07256061, + 'ask': 0.072661, }), buy=MagicMock(return_value='mocked_order_id')) init(conf, 'sqlite://') @@ -166,15 +148,9 @@ def test_performance_handle(conf, update, mocker): mocker.patch.multiple('freqtrade.main.telegram', _CONF=conf, init=MagicMock(), send_msg=msg_mock) mocker.patch.multiple('freqtrade.main.exchange', validate_pairs=MagicMock(), - get_orderbook=MagicMock(return_value={ - 'bid': [{ - 'Quantity': 1, - 'Rate': 0.07256061 - }], - 'ask': [{ - 'Quantity': 1, - 'Rate': 0.072661 - }] + get_ticker=MagicMock(return_value={ + 'bid': 0.07256061, + 'ask': 0.072661, }), buy=MagicMock(return_value='mocked_order_id')) init(conf, 'sqlite://')