Merge branch 'develop' into align_utils
This commit is contained in:
@@ -318,7 +318,8 @@ def markets():
|
||||
'symbol': 'TKN/BTC',
|
||||
'base': 'TKN',
|
||||
'quote': 'BTC',
|
||||
'active': True,
|
||||
# According to ccxt, markets without active item set are also active
|
||||
# 'active': True,
|
||||
'precision': {
|
||||
'price': 8,
|
||||
'amount': 8,
|
||||
@@ -509,6 +510,50 @@ def markets():
|
||||
}
|
||||
},
|
||||
'info': {},
|
||||
},
|
||||
'LTC/USD': {
|
||||
'id': 'USD-LTC',
|
||||
'symbol': 'LTC/USD',
|
||||
'base': 'LTC',
|
||||
'quote': 'USD',
|
||||
'active': True,
|
||||
'precision': {
|
||||
'amount': 8,
|
||||
'price': 8
|
||||
},
|
||||
'limits': {
|
||||
'amount': {
|
||||
'min': 0.06646786,
|
||||
'max': None
|
||||
},
|
||||
'price': {
|
||||
'min': 1e-08,
|
||||
'max': None
|
||||
}
|
||||
},
|
||||
'info': {},
|
||||
},
|
||||
'XLTCUSDT': {
|
||||
'id': 'xLTCUSDT',
|
||||
'symbol': 'XLTCUSDT',
|
||||
'base': 'LTC',
|
||||
'quote': 'USDT',
|
||||
'active': True,
|
||||
'precision': {
|
||||
'amount': 8,
|
||||
'price': 8
|
||||
},
|
||||
'limits': {
|
||||
'amount': {
|
||||
'min': 0.06646786,
|
||||
'max': None
|
||||
},
|
||||
'price': {
|
||||
'min': 1e-08,
|
||||
'max': None
|
||||
}
|
||||
},
|
||||
'info': {},
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -18,7 +18,9 @@ from freqtrade.exchange.exchange import (API_RETRY_COUNT, timeframe_to_minutes,
|
||||
timeframe_to_msecs,
|
||||
timeframe_to_next_date,
|
||||
timeframe_to_prev_date,
|
||||
timeframe_to_seconds)
|
||||
timeframe_to_seconds,
|
||||
symbol_is_pair,
|
||||
market_is_active)
|
||||
from freqtrade.resolvers.exchange_resolver import ExchangeResolver
|
||||
from tests.conftest import get_patched_exchange, log_has, log_has_re
|
||||
|
||||
@@ -929,17 +931,17 @@ def test_get_balances_prod(default_conf, mocker, exchange_name):
|
||||
def test_get_tickers(default_conf, mocker, exchange_name):
|
||||
api_mock = MagicMock()
|
||||
tick = {'ETH/BTC': {
|
||||
'symbol': 'ETH/BTC',
|
||||
'bid': 0.5,
|
||||
'ask': 1,
|
||||
'last': 42,
|
||||
}, 'BCH/BTC': {
|
||||
'symbol': 'BCH/BTC',
|
||||
'bid': 0.6,
|
||||
'ask': 0.5,
|
||||
'last': 41,
|
||||
}
|
||||
}
|
||||
'symbol': 'ETH/BTC',
|
||||
'bid': 0.5,
|
||||
'ask': 1,
|
||||
'last': 42,
|
||||
}, 'BCH/BTC': {
|
||||
'symbol': 'BCH/BTC',
|
||||
'bid': 0.6,
|
||||
'ask': 0.5,
|
||||
'last': 41,
|
||||
}
|
||||
}
|
||||
api_mock.fetch_tickers = MagicMock(return_value=tick)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
# retrieve original ticker
|
||||
@@ -1693,6 +1695,74 @@ def test_get_valid_pair_combination(default_conf, mocker, markets):
|
||||
ex.get_valid_pair_combination("NOPAIR", "ETH")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"base_currencies, quote_currencies, pairs_only, active_only, expected_keys", [
|
||||
# Testing markets (in conftest.py):
|
||||
# 'BLK/BTC': 'active': True
|
||||
# 'BTT/BTC': 'active': True
|
||||
# 'ETH/BTC': 'active': True
|
||||
# 'ETH/USDT': 'active': True
|
||||
# 'LTC/BTC': 'active': False
|
||||
# 'LTC/USD': 'active': True
|
||||
# 'LTC/USDT': 'active': True
|
||||
# 'NEO/BTC': 'active': False
|
||||
# 'TKN/BTC': 'active' not set
|
||||
# 'XLTCUSDT': 'active': True, not a pair
|
||||
# 'XRP/BTC': 'active': False
|
||||
# all markets
|
||||
([], [], False, False,
|
||||
['BLK/BTC', 'BTT/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/USD',
|
||||
'LTC/USDT', 'NEO/BTC', 'TKN/BTC', 'XLTCUSDT', 'XRP/BTC']),
|
||||
# active markets
|
||||
([], [], False, True,
|
||||
['BLK/BTC', 'BTT/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/USD', 'LTC/USDT',
|
||||
'TKN/BTC', 'XLTCUSDT']),
|
||||
# all pairs
|
||||
([], [], True, False,
|
||||
['BLK/BTC', 'BTT/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/USD',
|
||||
'LTC/USDT', 'NEO/BTC', 'TKN/BTC', 'XRP/BTC']),
|
||||
# active pairs
|
||||
([], [], True, True,
|
||||
['BLK/BTC', 'BTT/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/USD', 'LTC/USDT', 'TKN/BTC']),
|
||||
# all markets, base=ETH, LTC
|
||||
(['ETH', 'LTC'], [], False, False,
|
||||
['ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/USD', 'LTC/USDT', 'XLTCUSDT']),
|
||||
# all markets, base=LTC
|
||||
(['LTC'], [], False, False,
|
||||
['LTC/BTC', 'LTC/USD', 'LTC/USDT', 'XLTCUSDT']),
|
||||
# all markets, quote=USDT
|
||||
([], ['USDT'], False, False,
|
||||
['ETH/USDT', 'LTC/USDT', 'XLTCUSDT']),
|
||||
# all markets, quote=USDT, USD
|
||||
([], ['USDT', 'USD'], False, False,
|
||||
['ETH/USDT', 'LTC/USD', 'LTC/USDT', 'XLTCUSDT']),
|
||||
# all markets, base=LTC, quote=USDT
|
||||
(['LTC'], ['USDT'], False, False,
|
||||
['LTC/USDT', 'XLTCUSDT']),
|
||||
# all pairs, base=LTC, quote=USDT
|
||||
(['LTC'], ['USDT'], True, False,
|
||||
['LTC/USDT']),
|
||||
# all markets, base=LTC, quote=USDT, NONEXISTENT
|
||||
(['LTC'], ['USDT', 'NONEXISTENT'], False, False,
|
||||
['LTC/USDT', 'XLTCUSDT']),
|
||||
# all markets, base=LTC, quote=NONEXISTENT
|
||||
(['LTC'], ['NONEXISTENT'], False, False,
|
||||
[]),
|
||||
])
|
||||
def test_get_markets(default_conf, mocker, markets,
|
||||
base_currencies, quote_currencies, pairs_only, active_only,
|
||||
expected_keys):
|
||||
mocker.patch.multiple('freqtrade.exchange.Exchange',
|
||||
_init_ccxt=MagicMock(return_value=MagicMock()),
|
||||
_load_async_markets=MagicMock(),
|
||||
validate_pairs=MagicMock(),
|
||||
validate_timeframes=MagicMock(),
|
||||
markets=PropertyMock(return_value=markets))
|
||||
ex = Exchange(default_conf)
|
||||
pairs = ex.get_markets(base_currencies, quote_currencies, pairs_only, active_only)
|
||||
assert sorted(pairs.keys()) == sorted(expected_keys)
|
||||
|
||||
|
||||
def test_timeframe_to_minutes():
|
||||
assert timeframe_to_minutes("5m") == 5
|
||||
assert timeframe_to_minutes("10m") == 10
|
||||
@@ -1762,3 +1832,33 @@ def test_timeframe_to_next_date():
|
||||
|
||||
date = datetime.now(tz=timezone.utc)
|
||||
assert timeframe_to_next_date("5m") > date
|
||||
|
||||
|
||||
@pytest.mark.parametrize("market_symbol,base_currency,quote_currency,expected_result", [
|
||||
("BTC/USDT", None, None, True),
|
||||
("USDT/BTC", None, None, True),
|
||||
("BTCUSDT", None, None, False),
|
||||
("BTC/USDT", None, "USDT", True),
|
||||
("USDT/BTC", None, "USDT", False),
|
||||
("BTCUSDT", None, "USDT", False),
|
||||
("BTC/USDT", "BTC", None, True),
|
||||
("USDT/BTC", "BTC", None, False),
|
||||
("BTCUSDT", "BTC", None, False),
|
||||
("BTC/USDT", "BTC", "USDT", True),
|
||||
("BTC/USDT", "USDT", "BTC", False),
|
||||
("BTC/USDT", "BTC", "USD", False),
|
||||
("BTCUSDT", "BTC", "USDT", False),
|
||||
("BTC/", None, None, False),
|
||||
("/USDT", None, None, False),
|
||||
])
|
||||
def test_symbol_is_pair(market_symbol, base_currency, quote_currency, expected_result) -> None:
|
||||
assert symbol_is_pair(market_symbol, base_currency, quote_currency) == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize("market,expected_result", [
|
||||
({'symbol': 'ETH/BTC', 'active': True}, True),
|
||||
({'symbol': 'ETH/BTC', 'active': False}, False),
|
||||
({'symbol': 'ETH/BTC', }, True),
|
||||
])
|
||||
def test_market_is_active(market, expected_result) -> None:
|
||||
assert market_is_active(market) == expected_result
|
||||
|
@@ -7,7 +7,7 @@ from unittest.mock import MagicMock
|
||||
from freqtrade.data.converter import parse_ticker_dataframe
|
||||
from freqtrade.data.history import pair_data_filename
|
||||
from freqtrade.misc import (datesarray_to_datetimearray, file_dump_json,
|
||||
file_load_json, format_ms_time, shorten_date)
|
||||
file_load_json, format_ms_time, plural, shorten_date)
|
||||
|
||||
|
||||
def test_shorten_date() -> None:
|
||||
@@ -69,3 +69,35 @@ def test_format_ms_time() -> None:
|
||||
# Date 2017-12-13 08:02:01
|
||||
date_in_epoch_ms = 1513152121000
|
||||
assert format_ms_time(date_in_epoch_ms) == res.astimezone(None).strftime('%Y-%m-%dT%H:%M:%S')
|
||||
|
||||
|
||||
def test_plural() -> None:
|
||||
assert plural(0, "page") == "pages"
|
||||
assert plural(0.0, "page") == "pages"
|
||||
assert plural(1, "page") == "page"
|
||||
assert plural(1.0, "page") == "page"
|
||||
assert plural(2, "page") == "pages"
|
||||
assert plural(2.0, "page") == "pages"
|
||||
assert plural(-1, "page") == "page"
|
||||
assert plural(-1.0, "page") == "page"
|
||||
assert plural(-2, "page") == "pages"
|
||||
assert plural(-2.0, "page") == "pages"
|
||||
assert plural(0.5, "page") == "pages"
|
||||
assert plural(1.5, "page") == "pages"
|
||||
assert plural(-0.5, "page") == "pages"
|
||||
assert plural(-1.5, "page") == "pages"
|
||||
|
||||
assert plural(0, "ox", "oxen") == "oxen"
|
||||
assert plural(0.0, "ox", "oxen") == "oxen"
|
||||
assert plural(1, "ox", "oxen") == "ox"
|
||||
assert plural(1.0, "ox", "oxen") == "ox"
|
||||
assert plural(2, "ox", "oxen") == "oxen"
|
||||
assert plural(2.0, "ox", "oxen") == "oxen"
|
||||
assert plural(-1, "ox", "oxen") == "ox"
|
||||
assert plural(-1.0, "ox", "oxen") == "ox"
|
||||
assert plural(-2, "ox", "oxen") == "oxen"
|
||||
assert plural(-2.0, "ox", "oxen") == "oxen"
|
||||
assert plural(0.5, "ox", "oxen") == "oxen"
|
||||
assert plural(1.5, "ox", "oxen") == "oxen"
|
||||
assert plural(-0.5, "ox", "oxen") == "oxen"
|
||||
assert plural(-1.5, "ox", "oxen") == "oxen"
|
||||
|
@@ -8,7 +8,7 @@ from freqtrade import OperationalException
|
||||
from freqtrade.state import RunMode
|
||||
from freqtrade.utils import (setup_utils_configuration, start_create_userdir,
|
||||
start_download_data, start_list_exchanges,
|
||||
start_list_timeframes)
|
||||
start_list_markets, start_list_timeframes)
|
||||
from tests.conftest import get_args, log_has, patch_exchange
|
||||
|
||||
|
||||
@@ -164,6 +164,247 @@ def test_list_timeframes(mocker, capsys):
|
||||
assert re.search(r"^1d$", captured.out, re.MULTILINE)
|
||||
|
||||
|
||||
def test_list_markets(mocker, markets, capsys):
|
||||
|
||||
api_mock = MagicMock()
|
||||
api_mock.markets = markets
|
||||
patch_exchange(mocker, api_mock=api_mock)
|
||||
|
||||
# Test with no --config
|
||||
args = [
|
||||
"list-markets",
|
||||
]
|
||||
pargs = get_args(args)
|
||||
pargs['config'] = None
|
||||
with pytest.raises(OperationalException,
|
||||
match=r"This command requires a configured exchange.*"):
|
||||
start_list_markets(pargs, False)
|
||||
|
||||
# Test with --config config.json.example
|
||||
args = [
|
||||
'--config', 'config.json.example',
|
||||
"list-markets",
|
||||
"--print-list",
|
||||
]
|
||||
start_list_markets(get_args(args), False)
|
||||
captured = capsys.readouterr()
|
||||
assert ("Exchange Bittrex has 8 active markets: "
|
||||
"BLK/BTC, BTT/BTC, ETH/BTC, ETH/USDT, LTC/USD, LTC/USDT, TKN/BTC, XLTCUSDT.\n"
|
||||
in captured.out)
|
||||
|
||||
patch_exchange(mocker, api_mock=api_mock, id="binance")
|
||||
# Test with --exchange
|
||||
args = [
|
||||
"list-markets",
|
||||
"--exchange", "binance"
|
||||
]
|
||||
pargs = get_args(args)
|
||||
pargs['config'] = None
|
||||
start_list_markets(pargs, False)
|
||||
captured = capsys.readouterr()
|
||||
assert re.match("\nExchange Binance has 8 active markets:\n",
|
||||
captured.out)
|
||||
|
||||
patch_exchange(mocker, api_mock=api_mock, id="bittrex")
|
||||
# Test with --all: all markets
|
||||
args = [
|
||||
'--config', 'config.json.example',
|
||||
"list-markets", "--all",
|
||||
"--print-list",
|
||||
]
|
||||
start_list_markets(get_args(args), False)
|
||||
captured = capsys.readouterr()
|
||||
assert ("Exchange Bittrex has 11 markets: "
|
||||
"BLK/BTC, BTT/BTC, ETH/BTC, ETH/USDT, LTC/BTC, LTC/USD, LTC/USDT, NEO/BTC, "
|
||||
"TKN/BTC, XLTCUSDT, XRP/BTC.\n"
|
||||
in captured.out)
|
||||
|
||||
# Test list-pairs subcommand: active pairs
|
||||
args = [
|
||||
'--config', 'config.json.example',
|
||||
"list-pairs",
|
||||
"--print-list",
|
||||
]
|
||||
start_list_markets(get_args(args), True)
|
||||
captured = capsys.readouterr()
|
||||
assert ("Exchange Bittrex has 7 active pairs: "
|
||||
"BLK/BTC, BTT/BTC, ETH/BTC, ETH/USDT, LTC/USD, LTC/USDT, TKN/BTC.\n"
|
||||
in captured.out)
|
||||
|
||||
# Test list-pairs subcommand with --all: all pairs
|
||||
args = [
|
||||
'--config', 'config.json.example',
|
||||
"list-pairs", "--all",
|
||||
"--print-list",
|
||||
]
|
||||
start_list_markets(get_args(args), True)
|
||||
captured = capsys.readouterr()
|
||||
assert ("Exchange Bittrex has 10 pairs: "
|
||||
"BLK/BTC, BTT/BTC, ETH/BTC, ETH/USDT, LTC/BTC, LTC/USD, LTC/USDT, NEO/BTC, "
|
||||
"TKN/BTC, XRP/BTC.\n"
|
||||
in captured.out)
|
||||
|
||||
# active markets, base=ETH, LTC
|
||||
args = [
|
||||
'--config', 'config.json.example',
|
||||
"list-markets",
|
||||
"--base", "ETH", "LTC",
|
||||
"--print-list",
|
||||
]
|
||||
start_list_markets(get_args(args), False)
|
||||
captured = capsys.readouterr()
|
||||
assert ("Exchange Bittrex has 5 active markets with ETH, LTC as base currencies: "
|
||||
"ETH/BTC, ETH/USDT, LTC/USD, LTC/USDT, XLTCUSDT.\n"
|
||||
in captured.out)
|
||||
|
||||
# active markets, base=LTC
|
||||
args = [
|
||||
'--config', 'config.json.example',
|
||||
"list-markets",
|
||||
"--base", "LTC",
|
||||
"--print-list",
|
||||
]
|
||||
start_list_markets(get_args(args), False)
|
||||
captured = capsys.readouterr()
|
||||
assert ("Exchange Bittrex has 3 active markets with LTC as base currency: "
|
||||
"LTC/USD, LTC/USDT, XLTCUSDT.\n"
|
||||
in captured.out)
|
||||
|
||||
# active markets, quote=USDT, USD
|
||||
args = [
|
||||
'--config', 'config.json.example',
|
||||
"list-markets",
|
||||
"--quote", "USDT", "USD",
|
||||
"--print-list",
|
||||
]
|
||||
start_list_markets(get_args(args), False)
|
||||
captured = capsys.readouterr()
|
||||
assert ("Exchange Bittrex has 4 active markets with USDT, USD as quote currencies: "
|
||||
"ETH/USDT, LTC/USD, LTC/USDT, XLTCUSDT.\n"
|
||||
in captured.out)
|
||||
|
||||
# active markets, quote=USDT
|
||||
args = [
|
||||
'--config', 'config.json.example',
|
||||
"list-markets",
|
||||
"--quote", "USDT",
|
||||
"--print-list",
|
||||
]
|
||||
start_list_markets(get_args(args), False)
|
||||
captured = capsys.readouterr()
|
||||
assert ("Exchange Bittrex has 3 active markets with USDT as quote currency: "
|
||||
"ETH/USDT, LTC/USDT, XLTCUSDT.\n"
|
||||
in captured.out)
|
||||
|
||||
# active markets, base=LTC, quote=USDT
|
||||
args = [
|
||||
'--config', 'config.json.example',
|
||||
"list-markets",
|
||||
"--base", "LTC", "--quote", "USDT",
|
||||
"--print-list",
|
||||
]
|
||||
start_list_markets(get_args(args), False)
|
||||
captured = capsys.readouterr()
|
||||
assert ("Exchange Bittrex has 2 active markets with LTC as base currency and "
|
||||
"with USDT as quote currency: LTC/USDT, XLTCUSDT.\n"
|
||||
in captured.out)
|
||||
|
||||
# active pairs, base=LTC, quote=USDT
|
||||
args = [
|
||||
'--config', 'config.json.example',
|
||||
"list-pairs",
|
||||
"--base", "LTC", "--quote", "USDT",
|
||||
"--print-list",
|
||||
]
|
||||
start_list_markets(get_args(args), True)
|
||||
captured = capsys.readouterr()
|
||||
assert ("Exchange Bittrex has 1 active pair with LTC as base currency and "
|
||||
"with USDT as quote currency: LTC/USDT.\n"
|
||||
in captured.out)
|
||||
|
||||
# active markets, base=LTC, quote=USDT, NONEXISTENT
|
||||
args = [
|
||||
'--config', 'config.json.example',
|
||||
"list-markets",
|
||||
"--base", "LTC", "--quote", "USDT", "NONEXISTENT",
|
||||
"--print-list",
|
||||
]
|
||||
start_list_markets(get_args(args), False)
|
||||
captured = capsys.readouterr()
|
||||
assert ("Exchange Bittrex has 2 active markets with LTC as base currency and "
|
||||
"with USDT, NONEXISTENT as quote currencies: LTC/USDT, XLTCUSDT.\n"
|
||||
in captured.out)
|
||||
|
||||
# active markets, base=LTC, quote=NONEXISTENT
|
||||
args = [
|
||||
'--config', 'config.json.example',
|
||||
"list-markets",
|
||||
"--base", "LTC", "--quote", "NONEXISTENT",
|
||||
"--print-list",
|
||||
]
|
||||
start_list_markets(get_args(args), False)
|
||||
captured = capsys.readouterr()
|
||||
assert ("Exchange Bittrex has 0 active markets with LTC as base currency and "
|
||||
"with NONEXISTENT as quote currency.\n"
|
||||
in captured.out)
|
||||
|
||||
# Test tabular output
|
||||
args = [
|
||||
'--config', 'config.json.example',
|
||||
"list-markets",
|
||||
]
|
||||
start_list_markets(get_args(args), False)
|
||||
captured = capsys.readouterr()
|
||||
assert ("Exchange Bittrex has 8 active markets:\n"
|
||||
in captured.out)
|
||||
|
||||
# Test tabular output, no markets found
|
||||
args = [
|
||||
'--config', 'config.json.example',
|
||||
"list-markets",
|
||||
"--base", "LTC", "--quote", "NONEXISTENT",
|
||||
]
|
||||
start_list_markets(get_args(args), False)
|
||||
captured = capsys.readouterr()
|
||||
assert ("Exchange Bittrex has 0 active markets with LTC as base currency and "
|
||||
"with NONEXISTENT as quote currency.\n"
|
||||
in captured.out)
|
||||
|
||||
# Test --print-json
|
||||
args = [
|
||||
'--config', 'config.json.example',
|
||||
"list-markets",
|
||||
"--print-json"
|
||||
]
|
||||
start_list_markets(get_args(args), False)
|
||||
captured = capsys.readouterr()
|
||||
assert ('["BLK/BTC","BTT/BTC","ETH/BTC","ETH/USDT","LTC/USD","LTC/USDT","TKN/BTC","XLTCUSDT"]'
|
||||
in captured.out)
|
||||
|
||||
# Test --print-csv
|
||||
args = [
|
||||
'--config', 'config.json.example',
|
||||
"list-markets",
|
||||
"--print-csv"
|
||||
]
|
||||
start_list_markets(get_args(args), False)
|
||||
captured = capsys.readouterr()
|
||||
assert ("Id,Symbol,Base,Quote,Active,Is pair" in captured.out)
|
||||
assert ("blkbtc,BLK/BTC,BLK,BTC,True,True" in captured.out)
|
||||
assert ("BTTBTC,BTT/BTC,BTT,BTC,True,True" in captured.out)
|
||||
|
||||
# Test --one-column
|
||||
args = [
|
||||
'--config', 'config.json.example',
|
||||
"list-markets",
|
||||
"--one-column"
|
||||
]
|
||||
start_list_markets(get_args(args), False)
|
||||
captured = capsys.readouterr()
|
||||
assert re.search(r"^BLK/BTC$", captured.out, re.MULTILINE)
|
||||
assert re.search(r"^BTT/BTC$", captured.out, re.MULTILINE)
|
||||
|
||||
|
||||
def test_create_datadir_failed(caplog):
|
||||
|
||||
args = [
|
||||
|
Reference in New Issue
Block a user