Merge pull request #4685 from freqtrade/ft_has
Use ccxt's has to check which exchanges can work with freqtrade
This commit is contained in:
commit
c36051be56
197
docs/utils.md
197
docs/utils.md
@ -253,13 +253,206 @@ optional arguments:
|
|||||||
* Example: see exchanges available for the bot:
|
* Example: see exchanges available for the bot:
|
||||||
```
|
```
|
||||||
$ freqtrade list-exchanges
|
$ freqtrade list-exchanges
|
||||||
Exchanges available for Freqtrade: _1btcxe, acx, allcoin, bequant, bibox, binance, binanceje, binanceus, bitbank, bitfinex, bitfinex2, bitkk, bitlish, bitmart, bittrex, bitz, bleutrade, btcalpha, btcmarkets, btcturk, buda, cex, cobinhood, coinbaseprime, coinbasepro, coinex, cointiger, coss, crex24, digifinex, dsx, dx, ethfinex, fcoin, fcoinjp, gateio, gdax, gemini, hitbtc2, huobipro, huobiru, idex, kkex, kraken, kucoin, kucoin2, kuna, lbank, mandala, mercado, oceanex, okcoincny, okcoinusd, okex, okex3, poloniex, rightbtc, theocean, tidebit, upbit, zb
|
Exchanges available for Freqtrade:
|
||||||
|
Exchange name Valid reason
|
||||||
|
--------------- ------- --------------------------------------------
|
||||||
|
aax True
|
||||||
|
ascendex True missing opt: fetchMyTrades
|
||||||
|
bequant True
|
||||||
|
bibox True
|
||||||
|
bigone True
|
||||||
|
binance True
|
||||||
|
binanceus True
|
||||||
|
bitbank True missing opt: fetchTickers
|
||||||
|
bitcoincom True
|
||||||
|
bitfinex True
|
||||||
|
bitforex True missing opt: fetchMyTrades, fetchTickers
|
||||||
|
bitget True
|
||||||
|
bithumb True missing opt: fetchMyTrades
|
||||||
|
bitkk True missing opt: fetchMyTrades
|
||||||
|
bitmart True
|
||||||
|
bitmax True missing opt: fetchMyTrades
|
||||||
|
bitpanda True
|
||||||
|
bittrex True
|
||||||
|
bitvavo True
|
||||||
|
bitz True missing opt: fetchMyTrades
|
||||||
|
btcalpha True missing opt: fetchTicker, fetchTickers
|
||||||
|
btcmarkets True missing opt: fetchTickers
|
||||||
|
buda True missing opt: fetchMyTrades, fetchTickers
|
||||||
|
bw True missing opt: fetchMyTrades, fetchL2OrderBook
|
||||||
|
bybit True
|
||||||
|
bytetrade True
|
||||||
|
cdax True
|
||||||
|
cex True missing opt: fetchMyTrades
|
||||||
|
coinbaseprime True missing opt: fetchTickers
|
||||||
|
coinbasepro True missing opt: fetchTickers
|
||||||
|
coinex True
|
||||||
|
crex24 True
|
||||||
|
deribit True
|
||||||
|
digifinex True
|
||||||
|
equos True missing opt: fetchTicker, fetchTickers
|
||||||
|
eterbase True
|
||||||
|
fcoin True missing opt: fetchMyTrades, fetchTickers
|
||||||
|
fcoinjp True missing opt: fetchMyTrades, fetchTickers
|
||||||
|
ftx True
|
||||||
|
gateio True
|
||||||
|
gemini True
|
||||||
|
gopax True
|
||||||
|
hbtc True
|
||||||
|
hitbtc True
|
||||||
|
huobijp True
|
||||||
|
huobipro True
|
||||||
|
idex True
|
||||||
|
kraken True
|
||||||
|
kucoin True
|
||||||
|
lbank True missing opt: fetchMyTrades
|
||||||
|
mercado True missing opt: fetchTickers
|
||||||
|
ndax True missing opt: fetchTickers
|
||||||
|
novadax True
|
||||||
|
okcoin True
|
||||||
|
okex True
|
||||||
|
probit True
|
||||||
|
qtrade True
|
||||||
|
stex True
|
||||||
|
timex True
|
||||||
|
upbit True missing opt: fetchMyTrades
|
||||||
|
vcc True
|
||||||
|
zb True missing opt: fetchMyTrades
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! Note "missing opt exchanges"
|
||||||
|
Values with "missing opt:" might need special configuration (e.g. using orderbook if `fetchTickers` is missing) - but should in theory work (although we cannot guarantee they will).
|
||||||
|
|
||||||
* Example: see all exchanges supported by the ccxt library (including 'bad' ones, i.e. those that are known to not work with Freqtrade):
|
* Example: see all exchanges supported by the ccxt library (including 'bad' ones, i.e. those that are known to not work with Freqtrade):
|
||||||
```
|
```
|
||||||
$ freqtrade list-exchanges -a
|
$ freqtrade list-exchanges -a
|
||||||
All exchanges supported by the ccxt library: _1btcxe, acx, adara, allcoin, anxpro, bcex, bequant, bibox, bigone, binance, binanceje, binanceus, bit2c, bitbank, bitbay, bitfinex, bitfinex2, bitflyer, bitforex, bithumb, bitkk, bitlish, bitmart, bitmex, bitso, bitstamp, bitstamp1, bittrex, bitz, bl3p, bleutrade, braziliex, btcalpha, btcbox, btcchina, btcmarkets, btctradeim, btctradeua, btcturk, buda, bxinth, cex, chilebit, cobinhood, coinbase, coinbaseprime, coinbasepro, coincheck, coinegg, coinex, coinexchange, coinfalcon, coinfloor, coingi, coinmarketcap, coinmate, coinone, coinspot, cointiger, coolcoin, coss, crex24, crypton, deribit, digifinex, dsx, dx, ethfinex, exmo, exx, fcoin, fcoinjp, flowbtc, foxbit, fybse, gateio, gdax, gemini, hitbtc, hitbtc2, huobipro, huobiru, ice3x, idex, independentreserve, indodax, itbit, kkex, kraken, kucoin, kucoin2, kuna, lakebtc, latoken, lbank, liquid, livecoin, luno, lykke, mandala, mercado, mixcoins, negociecoins, nova, oceanex, okcoincny, okcoinusd, okex, okex3, paymium, poloniex, rightbtc, southxchange, stronghold, surbitcoin, theocean, therock, tidebit, tidex, upbit, vaultoro, vbtc, virwox, xbtce, yobit, zaif, zb
|
All exchanges supported by the ccxt library:
|
||||||
|
Exchange name Valid reason
|
||||||
|
------------------ ------- ---------------------------------------------------------------------------------------
|
||||||
|
aax True
|
||||||
|
aofex False missing: fetchOrder
|
||||||
|
ascendex True missing opt: fetchMyTrades
|
||||||
|
bequant True
|
||||||
|
bibox True
|
||||||
|
bigone True
|
||||||
|
binance True
|
||||||
|
binanceus True
|
||||||
|
bit2c False missing: fetchOrder, fetchOHLCV
|
||||||
|
bitbank True missing opt: fetchTickers
|
||||||
|
bitbay False missing: fetchOrder
|
||||||
|
bitcoincom True
|
||||||
|
bitfinex True
|
||||||
|
bitfinex2 False missing: fetchOrder
|
||||||
|
bitflyer False missing: fetchOrder, fetchOHLCV
|
||||||
|
bitforex True missing opt: fetchMyTrades, fetchTickers
|
||||||
|
bitget True
|
||||||
|
bithumb True missing opt: fetchMyTrades
|
||||||
|
bitkk True missing opt: fetchMyTrades
|
||||||
|
bitmart True
|
||||||
|
bitmax True missing opt: fetchMyTrades
|
||||||
|
bitmex False Various reasons.
|
||||||
|
bitpanda True
|
||||||
|
bitso False missing: fetchOHLCV
|
||||||
|
bitstamp False Does not provide history. Details in https://github.com/freqtrade/freqtrade/issues/1983
|
||||||
|
bitstamp1 False missing: fetchOrder, fetchOHLCV
|
||||||
|
bittrex True
|
||||||
|
bitvavo True
|
||||||
|
bitz True missing opt: fetchMyTrades
|
||||||
|
bl3p False missing: fetchOrder, fetchOHLCV
|
||||||
|
bleutrade False missing: fetchOrder
|
||||||
|
braziliex False missing: fetchOHLCV
|
||||||
|
btcalpha True missing opt: fetchTicker, fetchTickers
|
||||||
|
btcbox False missing: fetchOHLCV
|
||||||
|
btcmarkets True missing opt: fetchTickers
|
||||||
|
btctradeua False missing: fetchOrder, fetchOHLCV
|
||||||
|
btcturk False missing: fetchOrder
|
||||||
|
buda True missing opt: fetchMyTrades, fetchTickers
|
||||||
|
bw True missing opt: fetchMyTrades, fetchL2OrderBook
|
||||||
|
bybit True
|
||||||
|
bytetrade True
|
||||||
|
cdax True
|
||||||
|
cex True missing opt: fetchMyTrades
|
||||||
|
chilebit False missing: fetchOrder, fetchOHLCV
|
||||||
|
coinbase False missing: fetchOrder, cancelOrder, createOrder, fetchOHLCV
|
||||||
|
coinbaseprime True missing opt: fetchTickers
|
||||||
|
coinbasepro True missing opt: fetchTickers
|
||||||
|
coincheck False missing: fetchOrder, fetchOHLCV
|
||||||
|
coinegg False missing: fetchOHLCV
|
||||||
|
coinex True
|
||||||
|
coinfalcon False missing: fetchOHLCV
|
||||||
|
coinfloor False missing: fetchOrder, fetchOHLCV
|
||||||
|
coingi False missing: fetchOrder, fetchOHLCV
|
||||||
|
coinmarketcap False missing: fetchOrder, cancelOrder, createOrder, fetchBalance, fetchOHLCV
|
||||||
|
coinmate False missing: fetchOHLCV
|
||||||
|
coinone False missing: fetchOHLCV
|
||||||
|
coinspot False missing: fetchOrder, cancelOrder, fetchOHLCV
|
||||||
|
crex24 True
|
||||||
|
currencycom False missing: fetchOrder
|
||||||
|
delta False missing: fetchOrder
|
||||||
|
deribit True
|
||||||
|
digifinex True
|
||||||
|
equos True missing opt: fetchTicker, fetchTickers
|
||||||
|
eterbase True
|
||||||
|
exmo False missing: fetchOrder
|
||||||
|
exx False missing: fetchOHLCV
|
||||||
|
fcoin True missing opt: fetchMyTrades, fetchTickers
|
||||||
|
fcoinjp True missing opt: fetchMyTrades, fetchTickers
|
||||||
|
flowbtc False missing: fetchOrder, fetchOHLCV
|
||||||
|
foxbit False missing: fetchOrder, fetchOHLCV
|
||||||
|
ftx True
|
||||||
|
gateio True
|
||||||
|
gemini True
|
||||||
|
gopax True
|
||||||
|
hbtc True
|
||||||
|
hitbtc True
|
||||||
|
hollaex False missing: fetchOrder
|
||||||
|
huobijp True
|
||||||
|
huobipro True
|
||||||
|
idex True
|
||||||
|
independentreserve False missing: fetchOHLCV
|
||||||
|
indodax False missing: fetchOHLCV
|
||||||
|
itbit False missing: fetchOHLCV
|
||||||
|
kraken True
|
||||||
|
kucoin True
|
||||||
|
kuna False missing: fetchOHLCV
|
||||||
|
lakebtc False missing: fetchOrder, fetchOHLCV
|
||||||
|
latoken False missing: fetchOrder, fetchOHLCV
|
||||||
|
lbank True missing opt: fetchMyTrades
|
||||||
|
liquid False missing: fetchOHLCV
|
||||||
|
luno False missing: fetchOHLCV
|
||||||
|
lykke False missing: fetchOHLCV
|
||||||
|
mercado True missing opt: fetchTickers
|
||||||
|
mixcoins False missing: fetchOrder, fetchOHLCV
|
||||||
|
ndax True missing opt: fetchTickers
|
||||||
|
novadax True
|
||||||
|
oceanex False missing: fetchOHLCV
|
||||||
|
okcoin True
|
||||||
|
okex True
|
||||||
|
paymium False missing: fetchOrder, fetchOHLCV
|
||||||
|
phemex False Does not provide history.
|
||||||
|
poloniex False missing: fetchOrder
|
||||||
|
probit True
|
||||||
|
qtrade True
|
||||||
|
rightbtc False missing: fetchOrder
|
||||||
|
ripio False missing: fetchOHLCV
|
||||||
|
southxchange False missing: fetchOrder, fetchOHLCV
|
||||||
|
stex True
|
||||||
|
surbitcoin False missing: fetchOrder, fetchOHLCV
|
||||||
|
therock False missing: fetchOHLCV
|
||||||
|
tidebit False missing: fetchOrder
|
||||||
|
tidex False missing: fetchOHLCV
|
||||||
|
timex True
|
||||||
|
upbit True missing opt: fetchMyTrades
|
||||||
|
vbtc False missing: fetchOrder, fetchOHLCV
|
||||||
|
vcc True
|
||||||
|
wavesexchange False missing: fetchOrder
|
||||||
|
whitebit False missing: fetchOrder, cancelOrder, createOrder, fetchBalance
|
||||||
|
xbtce False missing: fetchOrder, fetchOHLCV
|
||||||
|
xena False missing: fetchOrder
|
||||||
|
yobit False missing: fetchOHLCV
|
||||||
|
zaif False missing: fetchOrder, fetchOHLCV
|
||||||
|
zb True missing opt: fetchMyTrades
|
||||||
```
|
```
|
||||||
|
|
||||||
## List Timeframes
|
## List Timeframes
|
||||||
|
@ -13,7 +13,7 @@ from tabulate import tabulate
|
|||||||
from freqtrade.configuration import setup_utils_configuration
|
from freqtrade.configuration import setup_utils_configuration
|
||||||
from freqtrade.constants import USERPATH_HYPEROPTS, USERPATH_STRATEGIES
|
from freqtrade.constants import USERPATH_HYPEROPTS, USERPATH_STRATEGIES
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.exchange import available_exchanges, ccxt_exchanges, market_is_active
|
from freqtrade.exchange import market_is_active, validate_exchanges
|
||||||
from freqtrade.misc import plural
|
from freqtrade.misc import plural
|
||||||
from freqtrade.resolvers import ExchangeResolver, StrategyResolver
|
from freqtrade.resolvers import ExchangeResolver, StrategyResolver
|
||||||
from freqtrade.state import RunMode
|
from freqtrade.state import RunMode
|
||||||
@ -28,14 +28,18 @@ def start_list_exchanges(args: Dict[str, Any]) -> None:
|
|||||||
:param args: Cli args from Arguments()
|
:param args: Cli args from Arguments()
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
exchanges = ccxt_exchanges() if args['list_exchanges_all'] else available_exchanges()
|
exchanges = validate_exchanges(args['list_exchanges_all'])
|
||||||
|
|
||||||
if args['print_one_column']:
|
if args['print_one_column']:
|
||||||
print('\n'.join(exchanges))
|
print('\n'.join([e[0] for e in exchanges]))
|
||||||
else:
|
else:
|
||||||
if args['list_exchanges_all']:
|
if args['list_exchanges_all']:
|
||||||
print(f"All exchanges supported by the ccxt library: {', '.join(exchanges)}")
|
print("All exchanges supported by the ccxt library:")
|
||||||
else:
|
else:
|
||||||
print(f"Exchanges available for Freqtrade: {', '.join(exchanges)}")
|
print("Exchanges available for Freqtrade:")
|
||||||
|
exchanges = [e for e in exchanges if e[1] is not False]
|
||||||
|
|
||||||
|
print(tabulate(exchanges, headers=['Exchange name', 'Valid', 'reason']))
|
||||||
|
|
||||||
|
|
||||||
def _print_objs_tabular(objs: List, print_colorized: bool) -> None:
|
def _print_objs_tabular(objs: List, print_colorized: bool) -> None:
|
||||||
|
@ -2,8 +2,8 @@ import logging
|
|||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
|
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.exchange import (available_exchanges, get_exchange_bad_reason, is_exchange_bad,
|
from freqtrade.exchange import (available_exchanges, is_exchange_known_ccxt,
|
||||||
is_exchange_known_ccxt, is_exchange_officially_supported)
|
is_exchange_officially_supported, validate_exchange)
|
||||||
from freqtrade.state import RunMode
|
from freqtrade.state import RunMode
|
||||||
|
|
||||||
|
|
||||||
@ -57,9 +57,13 @@ def check_exchange(config: Dict[str, Any], check_for_bad: bool = True) -> bool:
|
|||||||
f'{", ".join(available_exchanges())}'
|
f'{", ".join(available_exchanges())}'
|
||||||
)
|
)
|
||||||
|
|
||||||
if check_for_bad and is_exchange_bad(exchange):
|
valid, reason = validate_exchange(exchange)
|
||||||
raise OperationalException(f'Exchange "{exchange}" is known to not work with the bot yet. '
|
if not valid:
|
||||||
f'Reason: {get_exchange_bad_reason(exchange)}')
|
if check_for_bad:
|
||||||
|
raise OperationalException(f'Exchange "{exchange}" will not work with Freqtrade. '
|
||||||
|
f'Reason: {reason}')
|
||||||
|
else:
|
||||||
|
logger.warning(f'Exchange "{exchange}" will not work with Freqtrade. Reason: {reason}')
|
||||||
|
|
||||||
if is_exchange_officially_supported(exchange):
|
if is_exchange_officially_supported(exchange):
|
||||||
logger.info(f'Exchange "{exchange}" is officially supported '
|
logger.info(f'Exchange "{exchange}" is officially supported '
|
||||||
|
@ -8,10 +8,10 @@ from freqtrade.exchange.binance import Binance
|
|||||||
from freqtrade.exchange.bittrex import Bittrex
|
from freqtrade.exchange.bittrex import Bittrex
|
||||||
from freqtrade.exchange.bybit import Bybit
|
from freqtrade.exchange.bybit import Bybit
|
||||||
from freqtrade.exchange.exchange import (available_exchanges, ccxt_exchanges,
|
from freqtrade.exchange.exchange import (available_exchanges, ccxt_exchanges,
|
||||||
get_exchange_bad_reason, is_exchange_bad,
|
|
||||||
is_exchange_known_ccxt, is_exchange_officially_supported,
|
is_exchange_known_ccxt, is_exchange_officially_supported,
|
||||||
market_is_active, timeframe_to_minutes, timeframe_to_msecs,
|
market_is_active, timeframe_to_minutes, timeframe_to_msecs,
|
||||||
timeframe_to_next_date, timeframe_to_prev_date,
|
timeframe_to_next_date, timeframe_to_prev_date,
|
||||||
timeframe_to_seconds)
|
timeframe_to_seconds, validate_exchange,
|
||||||
|
validate_exchanges)
|
||||||
from freqtrade.exchange.ftx import Ftx
|
from freqtrade.exchange.ftx import Ftx
|
||||||
from freqtrade.exchange.kraken import Kraken
|
from freqtrade.exchange.kraken import Kraken
|
||||||
|
@ -18,78 +18,8 @@ BAD_EXCHANGES = {
|
|||||||
"bitmex": "Various reasons.",
|
"bitmex": "Various reasons.",
|
||||||
"bitstamp": "Does not provide history. "
|
"bitstamp": "Does not provide history. "
|
||||||
"Details in https://github.com/freqtrade/freqtrade/issues/1983",
|
"Details in https://github.com/freqtrade/freqtrade/issues/1983",
|
||||||
"hitbtc": "This API cannot be used with Freqtrade. "
|
|
||||||
"Use `hitbtc2` exchange id to access this exchange.",
|
|
||||||
"phemex": "Does not provide history. ",
|
"phemex": "Does not provide history. ",
|
||||||
"poloniex": "Does not provide fetch_order endpoint to fetch both open and closed orders.",
|
"poloniex": "Does not provide fetch_order endpoint to fetch both open and closed orders.",
|
||||||
**dict.fromkeys([
|
|
||||||
'adara',
|
|
||||||
'anxpro',
|
|
||||||
'bigone',
|
|
||||||
'coinbase',
|
|
||||||
'coinexchange',
|
|
||||||
'coinmarketcap',
|
|
||||||
'lykke',
|
|
||||||
'xbtce',
|
|
||||||
], "Does not provide timeframes. ccxt fetchOHLCV: False"),
|
|
||||||
**dict.fromkeys([
|
|
||||||
'bcex',
|
|
||||||
'bit2c',
|
|
||||||
'bitbay',
|
|
||||||
'bitflyer',
|
|
||||||
'bitforex',
|
|
||||||
'bithumb',
|
|
||||||
'bitso',
|
|
||||||
'bitstamp1',
|
|
||||||
'bl3p',
|
|
||||||
'braziliex',
|
|
||||||
'btcbox',
|
|
||||||
'btcchina',
|
|
||||||
'btctradeim',
|
|
||||||
'btctradeua',
|
|
||||||
'bxinth',
|
|
||||||
'chilebit',
|
|
||||||
'coincheck',
|
|
||||||
'coinegg',
|
|
||||||
'coinfalcon',
|
|
||||||
'coinfloor',
|
|
||||||
'coingi',
|
|
||||||
'coinmate',
|
|
||||||
'coinone',
|
|
||||||
'coinspot',
|
|
||||||
'coolcoin',
|
|
||||||
'crypton',
|
|
||||||
'deribit',
|
|
||||||
'exmo',
|
|
||||||
'exx',
|
|
||||||
'flowbtc',
|
|
||||||
'foxbit',
|
|
||||||
'fybse',
|
|
||||||
# 'hitbtc',
|
|
||||||
'ice3x',
|
|
||||||
'independentreserve',
|
|
||||||
'indodax',
|
|
||||||
'itbit',
|
|
||||||
'lakebtc',
|
|
||||||
'latoken',
|
|
||||||
'liquid',
|
|
||||||
'livecoin',
|
|
||||||
'luno',
|
|
||||||
'mixcoins',
|
|
||||||
'negociecoins',
|
|
||||||
'nova',
|
|
||||||
'paymium',
|
|
||||||
'southxchange',
|
|
||||||
'stronghold',
|
|
||||||
'surbitcoin',
|
|
||||||
'therock',
|
|
||||||
'tidex',
|
|
||||||
'vaultoro',
|
|
||||||
'vbtc',
|
|
||||||
'virwox',
|
|
||||||
'yobit',
|
|
||||||
'zaif',
|
|
||||||
], "Does not provide timeframes. ccxt fetchOHLCV: emulated"),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MAP_EXCHANGE_CHILDCLASS = {
|
MAP_EXCHANGE_CHILDCLASS = {
|
||||||
@ -98,6 +28,29 @@ MAP_EXCHANGE_CHILDCLASS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
EXCHANGE_HAS_REQUIRED = [
|
||||||
|
# Required / private
|
||||||
|
'fetchOrder',
|
||||||
|
'cancelOrder',
|
||||||
|
'createOrder',
|
||||||
|
# 'createLimitOrder', 'createMarketOrder',
|
||||||
|
'fetchBalance',
|
||||||
|
|
||||||
|
# Public endpoints
|
||||||
|
'loadMarkets',
|
||||||
|
'fetchOHLCV',
|
||||||
|
]
|
||||||
|
|
||||||
|
EXCHANGE_HAS_OPTIONAL = [
|
||||||
|
# Private
|
||||||
|
'fetchMyTrades', # Trades for order - fee detection
|
||||||
|
# Public
|
||||||
|
'fetchOrderBook', 'fetchL2OrderBook', 'fetchTicker', # OR for pricing
|
||||||
|
'fetchTickers', # For volumepairlist?
|
||||||
|
'fetchTrades', # Downloading trades data
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def calculate_backoff(retrycount, max_retries):
|
def calculate_backoff(retrycount, max_retries):
|
||||||
"""
|
"""
|
||||||
Calculate backoff
|
Calculate backoff
|
||||||
|
@ -23,7 +23,8 @@ from freqtrade.data.converter import ohlcv_to_dataframe, trades_dict_to_list
|
|||||||
from freqtrade.exceptions import (DDosProtection, ExchangeError, InsufficientFundsError,
|
from freqtrade.exceptions import (DDosProtection, ExchangeError, InsufficientFundsError,
|
||||||
InvalidOrderException, OperationalException, RetryableOrderError,
|
InvalidOrderException, OperationalException, RetryableOrderError,
|
||||||
TemporaryError)
|
TemporaryError)
|
||||||
from freqtrade.exchange.common import (API_FETCH_ORDER_RETRY_COUNT, BAD_EXCHANGES, retrier,
|
from freqtrade.exchange.common import (API_FETCH_ORDER_RETRY_COUNT, BAD_EXCHANGES,
|
||||||
|
EXCHANGE_HAS_OPTIONAL, EXCHANGE_HAS_REQUIRED, retrier,
|
||||||
retrier_async)
|
retrier_async)
|
||||||
from freqtrade.misc import deep_merge_dicts, safe_value_fallback2
|
from freqtrade.misc import deep_merge_dicts, safe_value_fallback2
|
||||||
from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist
|
from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist
|
||||||
@ -1306,14 +1307,6 @@ class Exchange:
|
|||||||
self.calculate_fee_rate(order))
|
self.calculate_fee_rate(order))
|
||||||
|
|
||||||
|
|
||||||
def is_exchange_bad(exchange_name: str) -> bool:
|
|
||||||
return exchange_name in BAD_EXCHANGES
|
|
||||||
|
|
||||||
|
|
||||||
def get_exchange_bad_reason(exchange_name: str) -> str:
|
|
||||||
return BAD_EXCHANGES.get(exchange_name, "")
|
|
||||||
|
|
||||||
|
|
||||||
def is_exchange_known_ccxt(exchange_name: str, ccxt_module: CcxtModuleType = None) -> bool:
|
def is_exchange_known_ccxt(exchange_name: str, ccxt_module: CcxtModuleType = None) -> bool:
|
||||||
return exchange_name in ccxt_exchanges(ccxt_module)
|
return exchange_name in ccxt_exchanges(ccxt_module)
|
||||||
|
|
||||||
@ -1334,7 +1327,36 @@ def available_exchanges(ccxt_module: CcxtModuleType = None) -> List[str]:
|
|||||||
Return exchanges available to the bot, i.e. non-bad exchanges in the ccxt list
|
Return exchanges available to the bot, i.e. non-bad exchanges in the ccxt list
|
||||||
"""
|
"""
|
||||||
exchanges = ccxt_exchanges(ccxt_module)
|
exchanges = ccxt_exchanges(ccxt_module)
|
||||||
return [x for x in exchanges if not is_exchange_bad(x)]
|
return [x for x in exchanges if validate_exchange(x)[0]]
|
||||||
|
|
||||||
|
|
||||||
|
def validate_exchange(exchange: str) -> Tuple[bool, str]:
|
||||||
|
ex_mod = getattr(ccxt, exchange.lower())()
|
||||||
|
if not ex_mod or not ex_mod.has:
|
||||||
|
return False, ''
|
||||||
|
missing = [k for k in EXCHANGE_HAS_REQUIRED if ex_mod.has.get(k) is not True]
|
||||||
|
if missing:
|
||||||
|
return False, f"missing: {', '.join(missing)}"
|
||||||
|
|
||||||
|
missing_opt = [k for k in EXCHANGE_HAS_OPTIONAL if not ex_mod.has.get(k)]
|
||||||
|
|
||||||
|
if exchange.lower() in BAD_EXCHANGES:
|
||||||
|
return False, BAD_EXCHANGES.get(exchange.lower(), '')
|
||||||
|
if missing_opt:
|
||||||
|
return True, f"missing opt: {', '.join(missing_opt)}"
|
||||||
|
|
||||||
|
return True, ''
|
||||||
|
|
||||||
|
|
||||||
|
def validate_exchanges(all_exchanges: bool) -> List[Tuple[str, bool, str]]:
|
||||||
|
"""
|
||||||
|
:return: List of tuples with exchangename, valid, reason.
|
||||||
|
"""
|
||||||
|
exchanges = ccxt_exchanges() if all_exchanges else available_exchanges()
|
||||||
|
exchanges_valid = [
|
||||||
|
(e, *validate_exchange(e)) for e in exchanges
|
||||||
|
]
|
||||||
|
return exchanges_valid
|
||||||
|
|
||||||
|
|
||||||
def timeframe_to_seconds(timeframe: str) -> int:
|
def timeframe_to_seconds(timeframe: str) -> int:
|
||||||
|
@ -66,8 +66,8 @@ def test_list_exchanges(capsys):
|
|||||||
start_list_exchanges(get_args(args))
|
start_list_exchanges(get_args(args))
|
||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
assert re.match(r"Exchanges available for Freqtrade.*", captured.out)
|
assert re.match(r"Exchanges available for Freqtrade.*", captured.out)
|
||||||
assert re.match(r".*binance,.*", captured.out)
|
assert re.search(r".*binance.*", captured.out)
|
||||||
assert re.match(r".*bittrex,.*", captured.out)
|
assert re.search(r".*bittrex.*", captured.out)
|
||||||
|
|
||||||
# Test with --one-column
|
# Test with --one-column
|
||||||
args = [
|
args = [
|
||||||
@ -89,9 +89,9 @@ def test_list_exchanges(capsys):
|
|||||||
start_list_exchanges(get_args(args))
|
start_list_exchanges(get_args(args))
|
||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
assert re.match(r"All exchanges supported by the ccxt library.*", captured.out)
|
assert re.match(r"All exchanges supported by the ccxt library.*", captured.out)
|
||||||
assert re.match(r".*binance,.*", captured.out)
|
assert re.search(r".*binance.*", captured.out)
|
||||||
assert re.match(r".*bittrex,.*", captured.out)
|
assert re.search(r".*bittrex.*", captured.out)
|
||||||
assert re.match(r".*bitmex,.*", captured.out)
|
assert re.search(r".*bitmex.*", captured.out)
|
||||||
|
|
||||||
# Test with --one-column --all
|
# Test with --one-column --all
|
||||||
args = [
|
args = [
|
||||||
|
@ -565,7 +565,7 @@ def test_check_exchange(default_conf, caplog) -> None:
|
|||||||
# Test a 'bad' exchange, which known to have serious problems
|
# Test a 'bad' exchange, which known to have serious problems
|
||||||
default_conf.get('exchange').update({'name': 'bitmex'})
|
default_conf.get('exchange').update({'name': 'bitmex'})
|
||||||
with pytest.raises(OperationalException,
|
with pytest.raises(OperationalException,
|
||||||
match=r"Exchange .* is known to not work with the bot yet.*"):
|
match=r"Exchange .* will not work with Freqtrade\..*"):
|
||||||
check_exchange(default_conf)
|
check_exchange(default_conf)
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@ def test_main_operational_exception(mocker, default_conf, caplog) -> None:
|
|||||||
def test_main_operational_exception1(mocker, default_conf, caplog) -> None:
|
def test_main_operational_exception1(mocker, default_conf, caplog) -> None:
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
'freqtrade.commands.list_commands.available_exchanges',
|
'freqtrade.commands.list_commands.validate_exchanges',
|
||||||
MagicMock(side_effect=ValueError('Oh snap!'))
|
MagicMock(side_effect=ValueError('Oh snap!'))
|
||||||
)
|
)
|
||||||
patched_configuration_load_config_file(mocker, default_conf)
|
patched_configuration_load_config_file(mocker, default_conf)
|
||||||
@ -132,7 +132,7 @@ def test_main_operational_exception1(mocker, default_conf, caplog) -> None:
|
|||||||
assert log_has('Fatal exception!', caplog)
|
assert log_has('Fatal exception!', caplog)
|
||||||
assert not log_has_re(r'SIGINT.*', caplog)
|
assert not log_has_re(r'SIGINT.*', caplog)
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
'freqtrade.commands.list_commands.available_exchanges',
|
'freqtrade.commands.list_commands.validate_exchanges',
|
||||||
MagicMock(side_effect=KeyboardInterrupt)
|
MagicMock(side_effect=KeyboardInterrupt)
|
||||||
)
|
)
|
||||||
with pytest.raises(SystemExit):
|
with pytest.raises(SystemExit):
|
||||||
|
Loading…
Reference in New Issue
Block a user