Merge pull request #3868 from freqtrade/fix/3865
bittrex fetch_orderbook API change.
This commit is contained in:
commit
8ae193f638
@ -5,15 +5,15 @@
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"timeframe": "5m",
|
||||
"dry_run": false,
|
||||
"dry_run": true,
|
||||
"cancel_open_orders_on_exit": false,
|
||||
"unfilledtimeout": {
|
||||
"buy": 10,
|
||||
"sell": 30
|
||||
},
|
||||
"bid_strategy": {
|
||||
"ask_last_balance": 0.0,
|
||||
"use_order_book": false,
|
||||
"ask_last_balance": 0.0,
|
||||
"order_book_top": 1,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
|
@ -7,7 +7,7 @@
|
||||
"amount_reserve_percent": 0.05,
|
||||
"amend_last_stake_amount": false,
|
||||
"last_stake_amount_min_ratio": 0.5,
|
||||
"dry_run": false,
|
||||
"dry_run": true,
|
||||
"cancel_open_orders_on_exit": false,
|
||||
"timeframe": "5m",
|
||||
"trailing_stop": false,
|
||||
|
@ -27,12 +27,11 @@
|
||||
"use_sell_signal": true,
|
||||
"sell_profit_only": false,
|
||||
"ignore_roi_if_buy_signal": false
|
||||
|
||||
},
|
||||
"exchange": {
|
||||
"name": "kraken",
|
||||
"key": "",
|
||||
"secret": "",
|
||||
"key": "your_exchange_key",
|
||||
"secret": "your_exchange_key",
|
||||
"ccxt_config": {"enableRateLimit": true},
|
||||
"ccxt_async_config": {
|
||||
"enableRateLimit": true,
|
||||
|
@ -5,6 +5,7 @@ from freqtrade.exchange.exchange import Exchange
|
||||
# isort: on
|
||||
from freqtrade.exchange.bibox import Bibox
|
||||
from freqtrade.exchange.binance import Binance
|
||||
from freqtrade.exchange.bittrex import Bittrex
|
||||
from freqtrade.exchange.exchange import (available_exchanges, ccxt_exchanges,
|
||||
get_exchange_bad_reason, is_exchange_bad,
|
||||
is_exchange_known_ccxt, is_exchange_officially_supported,
|
||||
|
@ -20,20 +20,9 @@ class Binance(Exchange):
|
||||
"order_time_in_force": ['gtc', 'fok', 'ioc'],
|
||||
"trades_pagination": "id",
|
||||
"trades_pagination_arg": "fromId",
|
||||
"l2_limit_range": [5, 10, 20, 50, 100, 500, 1000],
|
||||
}
|
||||
|
||||
def fetch_l2_order_book(self, pair: str, limit: int = 100) -> dict:
|
||||
"""
|
||||
get order book level 2 from exchange
|
||||
|
||||
20180619: binance support limits but only on specific range
|
||||
"""
|
||||
limit_range = [5, 10, 20, 50, 100, 500, 1000]
|
||||
# get next-higher step in the limit_range list
|
||||
limit = min(list(filter(lambda x: limit <= x, limit_range)))
|
||||
|
||||
return super().fetch_l2_order_book(pair, limit)
|
||||
|
||||
def stoploss_adjust(self, stop_loss: float, order: Dict) -> bool:
|
||||
"""
|
||||
Verify stop_loss against stoploss-order value (limit or price)
|
||||
|
23
freqtrade/exchange/bittrex.py
Normal file
23
freqtrade/exchange/bittrex.py
Normal file
@ -0,0 +1,23 @@
|
||||
""" Bittrex exchange subclass """
|
||||
import logging
|
||||
from typing import Dict
|
||||
|
||||
from freqtrade.exchange import Exchange
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Bittrex(Exchange):
|
||||
"""
|
||||
Bittrex exchange class. Contains adjustments needed for Freqtrade to work
|
||||
with this exchange.
|
||||
|
||||
Please note that this exchange is not included in the list of exchanges
|
||||
officially supported by the Freqtrade development team. So some features
|
||||
may still not work as expected.
|
||||
"""
|
||||
|
||||
_ft_has: Dict = {
|
||||
"l2_limit_range": [1, 25, 500],
|
||||
}
|
@ -53,7 +53,7 @@ class Exchange:
|
||||
"ohlcv_partial_candle": True,
|
||||
"trades_pagination": "time", # Possible are "time" or "id"
|
||||
"trades_pagination_arg": "since",
|
||||
|
||||
"l2_limit_range": None,
|
||||
}
|
||||
_ft_has: Dict = {}
|
||||
|
||||
@ -1069,6 +1069,16 @@ class Exchange:
|
||||
return self.fetch_stoploss_order(order_id, pair)
|
||||
return self.fetch_order(order_id, pair)
|
||||
|
||||
@staticmethod
|
||||
def get_next_limit_in_list(limit: int, limit_range: Optional[List[int]]):
|
||||
"""
|
||||
Get next greater value in the list.
|
||||
Used by fetch_l2_order_book if the api only supports a limited range
|
||||
"""
|
||||
if not limit_range:
|
||||
return limit
|
||||
return min([x for x in limit_range if limit <= x] + [max(limit_range)])
|
||||
|
||||
@retrier
|
||||
def fetch_l2_order_book(self, pair: str, limit: int = 100) -> dict:
|
||||
"""
|
||||
@ -1077,9 +1087,10 @@ class Exchange:
|
||||
Returns a dict in the format
|
||||
{'asks': [price, volume], 'bids': [price, volume]}
|
||||
"""
|
||||
limit1 = self.get_next_limit_in_list(limit, self._ft_has['l2_limit_range'])
|
||||
try:
|
||||
|
||||
return self._api.fetch_l2_order_book(pair, limit)
|
||||
return self._api.fetch_l2_order_book(pair, limit1)
|
||||
except ccxt.NotSupported as e:
|
||||
raise OperationalException(
|
||||
f'Exchange {self._api.name} does not support fetching order book.'
|
||||
|
@ -132,7 +132,7 @@ def test_orderbook(mocker, default_conf, order_book_l2):
|
||||
res = dp.orderbook('ETH/BTC', 5)
|
||||
assert order_book_l2.call_count == 1
|
||||
assert order_book_l2.call_args_list[0][0][0] == 'ETH/BTC'
|
||||
assert order_book_l2.call_args_list[0][0][1] == 5
|
||||
assert order_book_l2.call_args_list[0][0][1] >= 5
|
||||
|
||||
assert type(res) is dict
|
||||
assert 'bids' in res
|
||||
|
@ -11,7 +11,7 @@ from pandas import DataFrame
|
||||
|
||||
from freqtrade.exceptions import (DDosProtection, DependencyException, InvalidOrderException,
|
||||
OperationalException, TemporaryError)
|
||||
from freqtrade.exchange import Binance, Exchange, Kraken
|
||||
from freqtrade.exchange import Binance, Bittrex, Exchange, Kraken
|
||||
from freqtrade.exchange.common import (API_FETCH_ORDER_RETRY_COUNT, API_RETRY_COUNT,
|
||||
calculate_backoff)
|
||||
from freqtrade.exchange.exchange import (market_is_active, timeframe_to_minutes, timeframe_to_msecs,
|
||||
@ -148,11 +148,19 @@ def test_exchange_resolver(default_conf, mocker, caplog):
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs')
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes')
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
|
||||
exchange = ExchangeResolver.load_exchange('Bittrex', default_conf)
|
||||
|
||||
exchange = ExchangeResolver.load_exchange('huobi', default_conf)
|
||||
assert isinstance(exchange, Exchange)
|
||||
assert log_has_re(r"No .* specific subclass found. Using the generic class instead.", caplog)
|
||||
caplog.clear()
|
||||
|
||||
exchange = ExchangeResolver.load_exchange('Bittrex', default_conf)
|
||||
assert isinstance(exchange, Exchange)
|
||||
assert isinstance(exchange, Bittrex)
|
||||
assert not log_has_re(r"No .* specific subclass found. Using the generic class instead.",
|
||||
caplog)
|
||||
caplog.clear()
|
||||
|
||||
exchange = ExchangeResolver.load_exchange('kraken', default_conf)
|
||||
assert isinstance(exchange, Exchange)
|
||||
assert isinstance(exchange, Kraken)
|
||||
@ -1438,6 +1446,27 @@ def test_refresh_latest_ohlcv_inv_result(default_conf, mocker, caplog):
|
||||
assert log_has("Async code raised an exception: TypeError", caplog)
|
||||
|
||||
|
||||
def test_get_next_limit_in_list():
|
||||
limit_range = [5, 10, 20, 50, 100, 500, 1000]
|
||||
assert Exchange.get_next_limit_in_list(1, limit_range) == 5
|
||||
assert Exchange.get_next_limit_in_list(5, limit_range) == 5
|
||||
assert Exchange.get_next_limit_in_list(6, limit_range) == 10
|
||||
assert Exchange.get_next_limit_in_list(9, limit_range) == 10
|
||||
assert Exchange.get_next_limit_in_list(10, limit_range) == 10
|
||||
assert Exchange.get_next_limit_in_list(11, limit_range) == 20
|
||||
assert Exchange.get_next_limit_in_list(19, limit_range) == 20
|
||||
assert Exchange.get_next_limit_in_list(21, limit_range) == 50
|
||||
assert Exchange.get_next_limit_in_list(51, limit_range) == 100
|
||||
assert Exchange.get_next_limit_in_list(1000, limit_range) == 1000
|
||||
# Going over the limit ...
|
||||
assert Exchange.get_next_limit_in_list(1001, limit_range) == 1000
|
||||
assert Exchange.get_next_limit_in_list(2000, limit_range) == 1000
|
||||
|
||||
assert Exchange.get_next_limit_in_list(21, None) == 21
|
||||
assert Exchange.get_next_limit_in_list(100, None) == 100
|
||||
assert Exchange.get_next_limit_in_list(1000, None) == 1000
|
||||
|
||||
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
def test_fetch_l2_order_book(default_conf, mocker, order_book_l2, exchange_name):
|
||||
default_conf['exchange']['name'] = exchange_name
|
||||
@ -1450,6 +1479,19 @@ def test_fetch_l2_order_book(default_conf, mocker, order_book_l2, exchange_name)
|
||||
assert 'asks' in order_book
|
||||
assert len(order_book['bids']) == 10
|
||||
assert len(order_book['asks']) == 10
|
||||
assert api_mock.fetch_l2_order_book.call_args_list[0][0][0] == 'ETH/BTC'
|
||||
|
||||
for val in [1, 5, 10, 12, 20, 50, 100]:
|
||||
api_mock.fetch_l2_order_book.reset_mock()
|
||||
|
||||
order_book = exchange.fetch_l2_order_book(pair='ETH/BTC', limit=val)
|
||||
assert api_mock.fetch_l2_order_book.call_args_list[0][0][0] == 'ETH/BTC'
|
||||
# Not all exchanges support all limits for orderbook
|
||||
if not exchange._ft_has['l2_limit_range'] or val in exchange._ft_has['l2_limit_range']:
|
||||
assert api_mock.fetch_l2_order_book.call_args_list[0][0][1] == val
|
||||
else:
|
||||
next_limit = exchange.get_next_limit_in_list(val, exchange._ft_has['l2_limit_range'])
|
||||
assert api_mock.fetch_l2_order_book.call_args_list[0][0][1] == next_limit
|
||||
|
||||
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
|
Loading…
Reference in New Issue
Block a user