Merge branch 'develop' into data_handler
This commit is contained in:
@@ -60,8 +60,10 @@ def patch_exchange(mocker, api_mock=None, id='bittrex', mock_markets=True) -> No
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_ordertypes', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange.id', PropertyMock(return_value=id))
|
||||
mocker.patch('freqtrade.exchange.Exchange.name', PropertyMock(return_value=id.title()))
|
||||
mocker.patch('freqtrade.exchange.Exchange.precisionMode', PropertyMock(return_value=2))
|
||||
if mock_markets:
|
||||
mocker.patch('freqtrade.exchange.Exchange.markets',
|
||||
PropertyMock(return_value=get_markets()))
|
||||
@@ -1317,12 +1319,12 @@ def buy_order_fee():
|
||||
def edge_conf(default_conf):
|
||||
conf = deepcopy(default_conf)
|
||||
conf['max_open_trades'] = -1
|
||||
conf['tradable_balance_ratio'] = 0.5
|
||||
conf['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT
|
||||
conf['edge'] = {
|
||||
"enabled": True,
|
||||
"process_throttle_secs": 1800,
|
||||
"calculate_since_number_of_days": 14,
|
||||
"capital_available_percentage": 0.5,
|
||||
"allowed_risk": 0.01,
|
||||
"stoploss_range_min": -0.01,
|
||||
"stoploss_range_max": -0.1,
|
||||
|
@@ -22,8 +22,8 @@ def test_stoploss_limit_order(default_conf, mocker):
|
||||
})
|
||||
|
||||
default_conf['dry_run'] = False
|
||||
mocker.patch('freqtrade.exchange.Exchange.symbol_amount_prec', lambda s, x, y: y)
|
||||
mocker.patch('freqtrade.exchange.Exchange.symbol_price_prec', lambda s, x, y: y)
|
||||
mocker.patch('freqtrade.exchange.Exchange.amount_to_precision', lambda s, x, y: y)
|
||||
mocker.patch('freqtrade.exchange.Exchange.price_to_precision', lambda s, x, y: y)
|
||||
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance')
|
||||
|
||||
@@ -71,8 +71,8 @@ def test_stoploss_limit_order_dry_run(default_conf, mocker):
|
||||
api_mock = MagicMock()
|
||||
order_type = 'stop_loss_limit'
|
||||
default_conf['dry_run'] = True
|
||||
mocker.patch('freqtrade.exchange.Exchange.symbol_amount_prec', lambda s, x, y: y)
|
||||
mocker.patch('freqtrade.exchange.Exchange.symbol_price_prec', lambda s, x, y: y)
|
||||
mocker.patch('freqtrade.exchange.Exchange.amount_to_precision', lambda s, x, y: y)
|
||||
mocker.patch('freqtrade.exchange.Exchange.price_to_precision', lambda s, x, y: y)
|
||||
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance')
|
||||
|
||||
|
@@ -73,6 +73,7 @@ def test_init(default_conf, mocker, caplog):
|
||||
|
||||
def test_init_ccxt_kwargs(default_conf, mocker, caplog):
|
||||
mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
|
||||
caplog.set_level(logging.INFO)
|
||||
conf = copy.deepcopy(default_conf)
|
||||
conf['exchange']['ccxt_async_config'] = {'aiohttp_trust_env': True}
|
||||
@@ -121,9 +122,10 @@ def test_init_exception(default_conf, mocker):
|
||||
|
||||
def test_exchange_resolver(default_conf, mocker, caplog):
|
||||
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=MagicMock()))
|
||||
mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange._load_async_markets')
|
||||
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)
|
||||
assert isinstance(exchange, Exchange)
|
||||
assert log_has_re(r"No .* specific subclass found. Using the generic class instead.", caplog)
|
||||
@@ -173,35 +175,78 @@ def test_validate_order_time_in_force(default_conf, mocker, caplog):
|
||||
ex.validate_order_time_in_force(tif2)
|
||||
|
||||
|
||||
def test_symbol_amount_prec(default_conf, mocker):
|
||||
@pytest.mark.parametrize("amount,precision_mode,precision,expected", [
|
||||
(2.34559, 2, 4, 2.3455),
|
||||
(2.34559, 2, 5, 2.34559),
|
||||
(2.34559, 2, 3, 2.345),
|
||||
(2.9999, 2, 3, 2.999),
|
||||
(2.9909, 2, 3, 2.990),
|
||||
# Tests for Tick-size
|
||||
(2.34559, 4, 0.0001, 2.3455),
|
||||
(2.34559, 4, 0.00001, 2.34559),
|
||||
(2.34559, 4, 0.001, 2.345),
|
||||
(2.9999, 4, 0.001, 2.999),
|
||||
(2.9909, 4, 0.001, 2.990),
|
||||
(2.9909, 4, 0.005, 2.990),
|
||||
(2.9999, 4, 0.005, 2.995),
|
||||
])
|
||||
def test_amount_to_precision(default_conf, mocker, amount, precision_mode, precision, expected):
|
||||
'''
|
||||
Test rounds down to 4 Decimal places
|
||||
Test rounds down
|
||||
'''
|
||||
|
||||
markets = PropertyMock(return_value={'ETH/BTC': {'precision': {'amount': 4}}})
|
||||
markets = PropertyMock(return_value={'ETH/BTC': {'precision': {'amount': precision}}})
|
||||
|
||||
exchange = get_patched_exchange(mocker, default_conf, id="binance")
|
||||
# digits counting mode
|
||||
# DECIMAL_PLACES = 2
|
||||
# SIGNIFICANT_DIGITS = 3
|
||||
# TICK_SIZE = 4
|
||||
mocker.patch('freqtrade.exchange.Exchange.precisionMode',
|
||||
PropertyMock(return_value=precision_mode))
|
||||
mocker.patch('freqtrade.exchange.Exchange.markets', markets)
|
||||
|
||||
pair = 'ETH/BTC'
|
||||
assert exchange.amount_to_precision(pair, amount) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize("price,precision_mode,precision,expected", [
|
||||
(2.34559, 2, 4, 2.3456),
|
||||
(2.34559, 2, 5, 2.34559),
|
||||
(2.34559, 2, 3, 2.346),
|
||||
(2.9999, 2, 3, 3.000),
|
||||
(2.9909, 2, 3, 2.991),
|
||||
# Tests for Tick_size
|
||||
(2.34559, 4, 0.0001, 2.3456),
|
||||
(2.34559, 4, 0.00001, 2.34559),
|
||||
(2.34559, 4, 0.001, 2.346),
|
||||
(2.9999, 4, 0.001, 3.000),
|
||||
(2.9909, 4, 0.001, 2.991),
|
||||
(2.9909, 4, 0.005, 2.995),
|
||||
(2.9973, 4, 0.005, 3.0),
|
||||
(2.9977, 4, 0.005, 3.0),
|
||||
(234.43, 4, 0.5, 234.5),
|
||||
(234.53, 4, 0.5, 235.0),
|
||||
(0.891534, 4, 0.0001, 0.8916),
|
||||
|
||||
])
|
||||
def test_price_to_precision(default_conf, mocker, price, precision_mode, precision, expected):
|
||||
'''
|
||||
Test price to precision
|
||||
'''
|
||||
markets = PropertyMock(return_value={'ETH/BTC': {'precision': {'price': precision}}})
|
||||
|
||||
exchange = get_patched_exchange(mocker, default_conf, id="binance")
|
||||
mocker.patch('freqtrade.exchange.Exchange.markets', markets)
|
||||
# digits counting mode
|
||||
# DECIMAL_PLACES = 2
|
||||
# SIGNIFICANT_DIGITS = 3
|
||||
# TICK_SIZE = 4
|
||||
mocker.patch('freqtrade.exchange.Exchange.precisionMode',
|
||||
PropertyMock(return_value=precision_mode))
|
||||
|
||||
amount = 2.34559
|
||||
pair = 'ETH/BTC'
|
||||
amount = exchange.symbol_amount_prec(pair, amount)
|
||||
assert amount == 2.3455
|
||||
|
||||
|
||||
def test_symbol_price_prec(default_conf, mocker):
|
||||
'''
|
||||
Test rounds up to 4 decimal places
|
||||
'''
|
||||
markets = PropertyMock(return_value={'ETH/BTC': {'precision': {'price': 4}}})
|
||||
|
||||
exchange = get_patched_exchange(mocker, default_conf, id="binance")
|
||||
mocker.patch('freqtrade.exchange.Exchange.markets', markets)
|
||||
|
||||
price = 2.34559
|
||||
pair = 'ETH/BTC'
|
||||
price = exchange.symbol_price_prec(pair, price)
|
||||
assert price == 2.3456
|
||||
assert pytest.approx(exchange.price_to_precision(pair, price)) == expected
|
||||
|
||||
|
||||
def test_set_sandbox(default_conf, mocker):
|
||||
@@ -257,9 +302,10 @@ def test__load_markets(default_conf, mocker, caplog):
|
||||
api_mock = MagicMock()
|
||||
api_mock.load_markets = MagicMock(side_effect=ccxt.BaseError("SomeError"))
|
||||
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs')
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes')
|
||||
mocker.patch('freqtrade.exchange.Exchange._load_async_markets')
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
|
||||
Exchange(default_conf)
|
||||
assert log_has('Unable to initialize markets. Reason: SomeError', caplog)
|
||||
|
||||
@@ -315,6 +361,44 @@ def test__reload_markets_exception(default_conf, mocker, caplog):
|
||||
assert log_has_re(r"Could not reload markets.*", caplog)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("stake_currency", ['ETH', 'BTC', 'USDT'])
|
||||
def test_validate_stake_currency(default_conf, stake_currency, mocker, caplog):
|
||||
default_conf['stake_currency'] = stake_currency
|
||||
api_mock = MagicMock()
|
||||
type(api_mock).markets = PropertyMock(return_value={
|
||||
'ETH/BTC': {'quote': 'BTC'}, 'LTC/BTC': {'quote': 'BTC'},
|
||||
'XRP/ETH': {'quote': 'ETH'}, 'NEO/USDT': {'quote': 'USDT'},
|
||||
})
|
||||
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs')
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes')
|
||||
mocker.patch('freqtrade.exchange.Exchange._load_async_markets')
|
||||
Exchange(default_conf)
|
||||
|
||||
|
||||
def test_validate_stake_currency_error(default_conf, mocker, caplog):
|
||||
default_conf['stake_currency'] = 'XRP'
|
||||
api_mock = MagicMock()
|
||||
type(api_mock).markets = PropertyMock(return_value={
|
||||
'ETH/BTC': {'quote': 'BTC'}, 'LTC/BTC': {'quote': 'BTC'},
|
||||
'XRP/ETH': {'quote': 'ETH'}, 'NEO/USDT': {'quote': 'USDT'},
|
||||
})
|
||||
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs')
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes')
|
||||
mocker.patch('freqtrade.exchange.Exchange._load_async_markets')
|
||||
with pytest.raises(OperationalException,
|
||||
match=r'XRP is not available as stake on .*'
|
||||
'Available currencies are: BTC, ETH, USDT'):
|
||||
Exchange(default_conf)
|
||||
|
||||
|
||||
def test_get_quote_currencies(default_conf, mocker):
|
||||
ex = get_patched_exchange(mocker, default_conf)
|
||||
|
||||
assert set(ex.get_quote_currencies()) == set(['USD', 'BTC', 'USDT'])
|
||||
|
||||
|
||||
def test_validate_pairs(default_conf, mocker): # test exchange.validate_pairs directly
|
||||
api_mock = MagicMock()
|
||||
type(api_mock).markets = PropertyMock(return_value={
|
||||
@@ -324,8 +408,9 @@ def test_validate_pairs(default_conf, mocker): # test exchange.validate_pairs d
|
||||
type(api_mock).id = id_mock
|
||||
|
||||
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes')
|
||||
mocker.patch('freqtrade.exchange.Exchange._load_async_markets')
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
|
||||
Exchange(default_conf)
|
||||
|
||||
|
||||
@@ -335,8 +420,9 @@ def test_validate_pairs_not_available(default_conf, mocker):
|
||||
'XRP/BTC': {'inactive': True}
|
||||
})
|
||||
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes')
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
|
||||
mocker.patch('freqtrade.exchange.Exchange._load_async_markets')
|
||||
|
||||
with pytest.raises(OperationalException, match=r'not available'):
|
||||
Exchange(default_conf)
|
||||
@@ -349,8 +435,9 @@ def test_validate_pairs_exception(default_conf, mocker, caplog):
|
||||
|
||||
type(api_mock).markets = PropertyMock(return_value={})
|
||||
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', api_mock)
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes')
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
|
||||
mocker.patch('freqtrade.exchange.Exchange._load_async_markets')
|
||||
|
||||
with pytest.raises(OperationalException, match=r'Pair ETH/BTC is not available on Binance'):
|
||||
Exchange(default_conf)
|
||||
@@ -368,8 +455,9 @@ def test_validate_pairs_restricted(default_conf, mocker, caplog):
|
||||
'NEO/BTC': {'info': 'TestString'}, # info can also be a string ...
|
||||
})
|
||||
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes')
|
||||
mocker.patch('freqtrade.exchange.Exchange._load_async_markets')
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
|
||||
|
||||
Exchange(default_conf)
|
||||
assert log_has(f"Pair XRP/BTC is restricted for some users on this exchange."
|
||||
@@ -377,8 +465,11 @@ def test_validate_pairs_restricted(default_conf, mocker, caplog):
|
||||
f"on the exchange and eventually remove XRP/BTC from your whitelist.", caplog)
|
||||
|
||||
|
||||
def test_validate_timeframes(default_conf, mocker):
|
||||
default_conf["ticker_interval"] = "5m"
|
||||
@pytest.mark.parametrize("timeframe", [
|
||||
('5m'), ("1m"), ("15m"), ("1h")
|
||||
])
|
||||
def test_validate_timeframes(default_conf, mocker, timeframe):
|
||||
default_conf["ticker_interval"] = timeframe
|
||||
api_mock = MagicMock()
|
||||
id_mock = PropertyMock(return_value='test_exchange')
|
||||
type(api_mock).id = id_mock
|
||||
@@ -390,7 +481,8 @@ def test_validate_timeframes(default_conf, mocker):
|
||||
|
||||
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
||||
mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs')
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
|
||||
Exchange(default_conf)
|
||||
|
||||
|
||||
@@ -399,7 +491,8 @@ def test_validate_timeframes_failed(default_conf, mocker):
|
||||
api_mock = MagicMock()
|
||||
id_mock = PropertyMock(return_value='test_exchange')
|
||||
type(api_mock).id = id_mock
|
||||
timeframes = PropertyMock(return_value={'1m': '1m',
|
||||
timeframes = PropertyMock(return_value={'15s': '15s',
|
||||
'1m': '1m',
|
||||
'5m': '5m',
|
||||
'15m': '15m',
|
||||
'1h': '1h'})
|
||||
@@ -411,6 +504,11 @@ def test_validate_timeframes_failed(default_conf, mocker):
|
||||
with pytest.raises(OperationalException,
|
||||
match=r"Invalid ticker interval '3m'. This exchange supports.*"):
|
||||
Exchange(default_conf)
|
||||
default_conf["ticker_interval"] = "15s"
|
||||
|
||||
with pytest.raises(OperationalException,
|
||||
match=r"Timeframes < 1m are currently not supported by Freqtrade."):
|
||||
Exchange(default_conf)
|
||||
|
||||
|
||||
def test_validate_timeframes_emulated_ohlcv_1(default_conf, mocker):
|
||||
@@ -424,7 +522,8 @@ def test_validate_timeframes_emulated_ohlcv_1(default_conf, mocker):
|
||||
|
||||
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
||||
mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs')
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
|
||||
with pytest.raises(OperationalException,
|
||||
match=r'The ccxt library does not provide the list of timeframes '
|
||||
r'for the exchange ".*" and this exchange '
|
||||
@@ -445,6 +544,7 @@ def test_validate_timeframes_emulated_ohlcvi_2(default_conf, mocker):
|
||||
mocker.patch('freqtrade.exchange.Exchange._load_markets',
|
||||
MagicMock(return_value={'timeframes': None}))
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
|
||||
with pytest.raises(OperationalException,
|
||||
match=r'The ccxt library does not provide the list of timeframes '
|
||||
r'for the exchange ".*" and this exchange '
|
||||
@@ -465,7 +565,8 @@ def test_validate_timeframes_not_in_config(default_conf, mocker):
|
||||
|
||||
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
||||
mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs')
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
|
||||
Exchange(default_conf)
|
||||
|
||||
|
||||
@@ -475,8 +576,9 @@ def test_validate_order_types(default_conf, mocker):
|
||||
type(api_mock).has = PropertyMock(return_value={'createMarketOrder': True})
|
||||
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
||||
mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs')
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes')
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
|
||||
mocker.patch('freqtrade.exchange.Exchange.name', 'Bittrex')
|
||||
default_conf['order_types'] = {
|
||||
'buy': 'limit',
|
||||
@@ -517,8 +619,9 @@ def test_validate_order_types_not_in_config(default_conf, mocker):
|
||||
api_mock = MagicMock()
|
||||
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
||||
mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs')
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes')
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
|
||||
|
||||
conf = copy.deepcopy(default_conf)
|
||||
Exchange(conf)
|
||||
@@ -529,9 +632,10 @@ def test_validate_required_startup_candles(default_conf, mocker, caplog):
|
||||
mocker.patch('freqtrade.exchange.Exchange.name', PropertyMock(return_value='Binance'))
|
||||
|
||||
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', api_mock)
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes')
|
||||
mocker.patch('freqtrade.exchange.Exchange._load_async_markets')
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs')
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
|
||||
|
||||
default_conf['startup_candle_count'] = 20
|
||||
ex = Exchange(default_conf)
|
||||
@@ -596,8 +700,8 @@ def test_create_order(default_conf, mocker, side, ordertype, rate, marketprice,
|
||||
}
|
||||
})
|
||||
default_conf['dry_run'] = False
|
||||
mocker.patch('freqtrade.exchange.Exchange.symbol_amount_prec', lambda s, x, y: y)
|
||||
mocker.patch('freqtrade.exchange.Exchange.symbol_price_prec', lambda s, x, y: y)
|
||||
mocker.patch('freqtrade.exchange.Exchange.amount_to_precision', lambda s, x, y: y)
|
||||
mocker.patch('freqtrade.exchange.Exchange.price_to_precision', lambda s, x, y: y)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
|
||||
order = exchange.create_order(
|
||||
@@ -637,8 +741,8 @@ def test_buy_prod(default_conf, mocker, exchange_name):
|
||||
}
|
||||
})
|
||||
default_conf['dry_run'] = False
|
||||
mocker.patch('freqtrade.exchange.Exchange.symbol_amount_prec', lambda s, x, y: y)
|
||||
mocker.patch('freqtrade.exchange.Exchange.symbol_price_prec', lambda s, x, y: y)
|
||||
mocker.patch('freqtrade.exchange.Exchange.amount_to_precision', lambda s, x, y: y)
|
||||
mocker.patch('freqtrade.exchange.Exchange.price_to_precision', lambda s, x, y: y)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
|
||||
order = exchange.buy(pair='ETH/BTC', ordertype=order_type,
|
||||
@@ -711,8 +815,8 @@ def test_buy_considers_time_in_force(default_conf, mocker, exchange_name):
|
||||
}
|
||||
})
|
||||
default_conf['dry_run'] = False
|
||||
mocker.patch('freqtrade.exchange.Exchange.symbol_amount_prec', lambda s, x, y: y)
|
||||
mocker.patch('freqtrade.exchange.Exchange.symbol_price_prec', lambda s, x, y: y)
|
||||
mocker.patch('freqtrade.exchange.Exchange.amount_to_precision', lambda s, x, y: y)
|
||||
mocker.patch('freqtrade.exchange.Exchange.price_to_precision', lambda s, x, y: y)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
|
||||
order_type = 'limit'
|
||||
@@ -773,8 +877,8 @@ def test_sell_prod(default_conf, mocker, exchange_name):
|
||||
})
|
||||
default_conf['dry_run'] = False
|
||||
|
||||
mocker.patch('freqtrade.exchange.Exchange.symbol_amount_prec', lambda s, x, y: y)
|
||||
mocker.patch('freqtrade.exchange.Exchange.symbol_price_prec', lambda s, x, y: y)
|
||||
mocker.patch('freqtrade.exchange.Exchange.amount_to_precision', lambda s, x, y: y)
|
||||
mocker.patch('freqtrade.exchange.Exchange.price_to_precision', lambda s, x, y: y)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
|
||||
order = exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
|
||||
@@ -837,8 +941,8 @@ def test_sell_considers_time_in_force(default_conf, mocker, exchange_name):
|
||||
})
|
||||
api_mock.options = {}
|
||||
default_conf['dry_run'] = False
|
||||
mocker.patch('freqtrade.exchange.Exchange.symbol_amount_prec', lambda s, x, y: y)
|
||||
mocker.patch('freqtrade.exchange.Exchange.symbol_price_prec', lambda s, x, y: y)
|
||||
mocker.patch('freqtrade.exchange.Exchange.amount_to_precision', lambda s, x, y: y)
|
||||
mocker.patch('freqtrade.exchange.Exchange.price_to_precision', lambda s, x, y: y)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
|
||||
order_type = 'limit'
|
||||
@@ -1665,7 +1769,9 @@ def test_merge_ft_has_dict(default_conf, mocker):
|
||||
_init_ccxt=MagicMock(return_value=MagicMock()),
|
||||
_load_async_markets=MagicMock(),
|
||||
validate_pairs=MagicMock(),
|
||||
validate_timeframes=MagicMock())
|
||||
validate_timeframes=MagicMock(),
|
||||
validate_stakecurrency=MagicMock()
|
||||
)
|
||||
ex = Exchange(default_conf)
|
||||
assert ex._ft_has == Exchange._ft_has_default
|
||||
|
||||
|
@@ -21,8 +21,8 @@ def test_buy_kraken_trading_agreement(default_conf, mocker):
|
||||
})
|
||||
default_conf['dry_run'] = False
|
||||
|
||||
mocker.patch('freqtrade.exchange.Exchange.symbol_amount_prec', lambda s, x, y: y)
|
||||
mocker.patch('freqtrade.exchange.Exchange.symbol_price_prec', lambda s, x, y: y)
|
||||
mocker.patch('freqtrade.exchange.Exchange.amount_to_precision', lambda s, x, y: y)
|
||||
mocker.patch('freqtrade.exchange.Exchange.price_to_precision', lambda s, x, y: y)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="kraken")
|
||||
|
||||
order = exchange.buy(pair='ETH/BTC', ordertype=order_type,
|
||||
@@ -53,8 +53,8 @@ def test_sell_kraken_trading_agreement(default_conf, mocker):
|
||||
})
|
||||
default_conf['dry_run'] = False
|
||||
|
||||
mocker.patch('freqtrade.exchange.Exchange.symbol_amount_prec', lambda s, x, y: y)
|
||||
mocker.patch('freqtrade.exchange.Exchange.symbol_price_prec', lambda s, x, y: y)
|
||||
mocker.patch('freqtrade.exchange.Exchange.amount_to_precision', lambda s, x, y: y)
|
||||
mocker.patch('freqtrade.exchange.Exchange.price_to_precision', lambda s, x, y: y)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="kraken")
|
||||
|
||||
order = exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
|
||||
|
@@ -325,105 +325,6 @@ def test_tickerdata_to_dataframe_bt(default_conf, mocker, testdatadir) -> None:
|
||||
assert data['UNITTEST/BTC'].equals(data2['UNITTEST/BTC'])
|
||||
|
||||
|
||||
def test_generate_text_table(default_conf, mocker):
|
||||
patch_exchange(mocker)
|
||||
default_conf['max_open_trades'] = 2
|
||||
backtesting = Backtesting(default_conf)
|
||||
|
||||
results = pd.DataFrame(
|
||||
{
|
||||
'pair': ['ETH/BTC', 'ETH/BTC'],
|
||||
'profit_percent': [0.1, 0.2],
|
||||
'profit_abs': [0.2, 0.4],
|
||||
'trade_duration': [10, 30],
|
||||
'profit': [2, 0],
|
||||
'loss': [0, 0]
|
||||
}
|
||||
)
|
||||
|
||||
result_str = (
|
||||
'| pair | buy count | avg profit % | cum profit % | '
|
||||
'tot profit BTC | tot profit % | avg duration | profit | loss |\n'
|
||||
'|:--------|------------:|---------------:|---------------:|'
|
||||
'-----------------:|---------------:|:---------------|---------:|-------:|\n'
|
||||
'| ETH/BTC | 2 | 15.00 | 30.00 | '
|
||||
'0.60000000 | 15.00 | 0:20:00 | 2 | 0 |\n'
|
||||
'| TOTAL | 2 | 15.00 | 30.00 | '
|
||||
'0.60000000 | 15.00 | 0:20:00 | 2 | 0 |'
|
||||
)
|
||||
assert backtesting._generate_text_table(data={'ETH/BTC': {}}, results=results) == result_str
|
||||
|
||||
|
||||
def test_generate_text_table_sell_reason(default_conf, mocker):
|
||||
patch_exchange(mocker)
|
||||
backtesting = Backtesting(default_conf)
|
||||
|
||||
results = pd.DataFrame(
|
||||
{
|
||||
'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC'],
|
||||
'profit_percent': [0.1, 0.2, -0.3],
|
||||
'profit_abs': [0.2, 0.4, -0.5],
|
||||
'trade_duration': [10, 30, 10],
|
||||
'profit': [2, 0, 0],
|
||||
'loss': [0, 0, 1],
|
||||
'sell_reason': [SellType.ROI, SellType.ROI, SellType.STOP_LOSS]
|
||||
}
|
||||
)
|
||||
|
||||
result_str = (
|
||||
'| Sell Reason | Count | Profit | Loss |\n'
|
||||
'|:--------------|--------:|---------:|-------:|\n'
|
||||
'| roi | 2 | 2 | 0 |\n'
|
||||
'| stop_loss | 1 | 0 | 1 |'
|
||||
)
|
||||
assert backtesting._generate_text_table_sell_reason(
|
||||
data={'ETH/BTC': {}}, results=results) == result_str
|
||||
|
||||
|
||||
def test_generate_text_table_strategyn(default_conf, mocker):
|
||||
"""
|
||||
Test Backtesting.generate_text_table_sell_reason() method
|
||||
"""
|
||||
patch_exchange(mocker)
|
||||
default_conf['max_open_trades'] = 2
|
||||
backtesting = Backtesting(default_conf)
|
||||
results = {}
|
||||
results['ETH/BTC'] = pd.DataFrame(
|
||||
{
|
||||
'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC'],
|
||||
'profit_percent': [0.1, 0.2, 0.3],
|
||||
'profit_abs': [0.2, 0.4, 0.5],
|
||||
'trade_duration': [10, 30, 10],
|
||||
'profit': [2, 0, 0],
|
||||
'loss': [0, 0, 1],
|
||||
'sell_reason': [SellType.ROI, SellType.ROI, SellType.STOP_LOSS]
|
||||
}
|
||||
)
|
||||
results['LTC/BTC'] = pd.DataFrame(
|
||||
{
|
||||
'pair': ['LTC/BTC', 'LTC/BTC', 'LTC/BTC'],
|
||||
'profit_percent': [0.4, 0.2, 0.3],
|
||||
'profit_abs': [0.4, 0.4, 0.5],
|
||||
'trade_duration': [15, 30, 15],
|
||||
'profit': [4, 1, 0],
|
||||
'loss': [0, 0, 1],
|
||||
'sell_reason': [SellType.ROI, SellType.ROI, SellType.STOP_LOSS]
|
||||
}
|
||||
)
|
||||
|
||||
result_str = (
|
||||
'| Strategy | buy count | avg profit % | cum profit % '
|
||||
'| tot profit BTC | tot profit % | avg duration | profit | loss |\n'
|
||||
'|:-----------|------------:|---------------:|---------------:'
|
||||
'|-----------------:|---------------:|:---------------|---------:|-------:|\n'
|
||||
'| ETH/BTC | 3 | 20.00 | 60.00 '
|
||||
'| 1.10000000 | 30.00 | 0:17:00 | 3 | 0 |\n'
|
||||
'| LTC/BTC | 3 | 30.00 | 90.00 '
|
||||
'| 1.30000000 | 45.00 | 0:20:00 | 3 | 0 |'
|
||||
)
|
||||
assert backtesting._generate_text_table_strategy(all_results=results) == result_str
|
||||
|
||||
|
||||
def test_backtesting_start(default_conf, mocker, testdatadir, caplog) -> None:
|
||||
def get_timerange(input1):
|
||||
return Arrow(2017, 11, 14, 21, 17), Arrow(2017, 11, 14, 22, 59)
|
||||
@@ -431,11 +332,8 @@ def test_backtesting_start(default_conf, mocker, testdatadir, caplog) -> None:
|
||||
mocker.patch('freqtrade.data.history.get_timerange', get_timerange)
|
||||
mocker.patch('freqtrade.exchange.Exchange.refresh_latest_ohlcv', MagicMock())
|
||||
patch_exchange(mocker)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.optimize.backtesting.Backtesting',
|
||||
backtest=MagicMock(),
|
||||
_generate_text_table=MagicMock(return_value='1'),
|
||||
)
|
||||
mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', MagicMock())
|
||||
mocker.patch('freqtrade.optimize.backtesting.generate_text_table', MagicMock(return_value=1))
|
||||
|
||||
default_conf['exchange']['pair_whitelist'] = ['UNITTEST/BTC']
|
||||
default_conf['ticker_interval'] = '1m'
|
||||
@@ -465,11 +363,8 @@ def test_backtesting_start_no_data(default_conf, mocker, caplog, testdatadir) ->
|
||||
mocker.patch('freqtrade.data.history.get_timerange', get_timerange)
|
||||
mocker.patch('freqtrade.exchange.Exchange.refresh_latest_ohlcv', MagicMock())
|
||||
patch_exchange(mocker)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.optimize.backtesting.Backtesting',
|
||||
backtest=MagicMock(),
|
||||
_generate_text_table=MagicMock(return_value='1'),
|
||||
)
|
||||
mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', MagicMock())
|
||||
mocker.patch('freqtrade.optimize.backtesting.generate_text_table', MagicMock(return_value=1))
|
||||
|
||||
default_conf['exchange']['pair_whitelist'] = ['UNITTEST/BTC']
|
||||
default_conf['ticker_interval'] = "1m"
|
||||
@@ -774,7 +669,8 @@ def test_backtest_start_timerange(default_conf, mocker, caplog, testdatadir):
|
||||
|
||||
patch_exchange(mocker)
|
||||
mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', MagicMock())
|
||||
mocker.patch('freqtrade.optimize.backtesting.Backtesting._generate_text_table', MagicMock())
|
||||
mocker.patch('freqtrade.optimize.backtesting.generate_text_table', MagicMock())
|
||||
|
||||
patched_configuration_load_config_file(mocker, default_conf)
|
||||
|
||||
args = [
|
||||
@@ -815,10 +711,9 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
|
||||
backtestmock = MagicMock()
|
||||
mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', backtestmock)
|
||||
gen_table_mock = MagicMock()
|
||||
mocker.patch('freqtrade.optimize.backtesting.Backtesting._generate_text_table', gen_table_mock)
|
||||
mocker.patch('freqtrade.optimize.backtesting.generate_text_table', gen_table_mock)
|
||||
gen_strattable_mock = MagicMock()
|
||||
mocker.patch('freqtrade.optimize.backtesting.Backtesting._generate_text_table_strategy',
|
||||
gen_strattable_mock)
|
||||
mocker.patch('freqtrade.optimize.backtesting.generate_text_table_strategy', gen_strattable_mock)
|
||||
patched_configuration_load_config_file(mocker, default_conf)
|
||||
|
||||
args = [
|
||||
|
@@ -3,7 +3,6 @@
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from freqtrade.edge import PairInfo
|
||||
from freqtrade.optimize import setup_configuration, start_edge
|
||||
from freqtrade.optimize.edge_cli import EdgeCli
|
||||
from freqtrade.state import RunMode
|
||||
@@ -106,16 +105,3 @@ def test_edge_init_fee(mocker, edge_conf) -> None:
|
||||
edge_cli = EdgeCli(edge_conf)
|
||||
assert edge_cli.edge.fee == 0.1234
|
||||
assert fee_mock.call_count == 0
|
||||
|
||||
|
||||
def test_generate_edge_table(edge_conf, mocker):
|
||||
patch_exchange(mocker)
|
||||
edge_cli = EdgeCli(edge_conf)
|
||||
|
||||
results = {}
|
||||
results['ETH/BTC'] = PairInfo(-0.01, 0.60, 2, 1, 3, 10, 60)
|
||||
|
||||
assert edge_cli._generate_edge_table(results).count(':|') == 7
|
||||
assert edge_cli._generate_edge_table(results).count('| ETH/BTC |') == 1
|
||||
assert edge_cli._generate_edge_table(results).count(
|
||||
'| risk reward ratio | required risk reward | expectancy |') == 1
|
||||
|
108
tests/optimize/test_optimize_reports.py
Normal file
108
tests/optimize/test_optimize_reports.py
Normal file
@@ -0,0 +1,108 @@
|
||||
import pandas as pd
|
||||
|
||||
from freqtrade.edge import PairInfo
|
||||
from freqtrade.optimize.optimize_reports import (
|
||||
generate_edge_table, generate_text_table, generate_text_table_sell_reason,
|
||||
generate_text_table_strategy)
|
||||
from freqtrade.strategy.interface import SellType
|
||||
|
||||
|
||||
def test_generate_text_table(default_conf, mocker):
|
||||
|
||||
results = pd.DataFrame(
|
||||
{
|
||||
'pair': ['ETH/BTC', 'ETH/BTC'],
|
||||
'profit_percent': [0.1, 0.2],
|
||||
'profit_abs': [0.2, 0.4],
|
||||
'trade_duration': [10, 30],
|
||||
'profit': [2, 0],
|
||||
'loss': [0, 0]
|
||||
}
|
||||
)
|
||||
|
||||
result_str = (
|
||||
'| pair | buy count | avg profit % | cum profit % | '
|
||||
'tot profit BTC | tot profit % | avg duration | profit | loss |\n'
|
||||
'|:--------|------------:|---------------:|---------------:|'
|
||||
'-----------------:|---------------:|:---------------|---------:|-------:|\n'
|
||||
'| ETH/BTC | 2 | 15.00 | 30.00 | '
|
||||
'0.60000000 | 15.00 | 0:20:00 | 2 | 0 |\n'
|
||||
'| TOTAL | 2 | 15.00 | 30.00 | '
|
||||
'0.60000000 | 15.00 | 0:20:00 | 2 | 0 |'
|
||||
)
|
||||
assert generate_text_table(data={'ETH/BTC': {}},
|
||||
stake_currency='BTC', max_open_trades=2,
|
||||
results=results) == result_str
|
||||
|
||||
|
||||
def test_generate_text_table_sell_reason(default_conf, mocker):
|
||||
|
||||
results = pd.DataFrame(
|
||||
{
|
||||
'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC'],
|
||||
'profit_percent': [0.1, 0.2, -0.1],
|
||||
'profit_abs': [0.2, 0.4, -0.2],
|
||||
'trade_duration': [10, 30, 10],
|
||||
'profit': [2, 0, 0],
|
||||
'loss': [0, 0, 1],
|
||||
'sell_reason': [SellType.ROI, SellType.ROI, SellType.STOP_LOSS]
|
||||
}
|
||||
)
|
||||
|
||||
result_str = (
|
||||
'| Sell Reason | Count | Profit | Loss | Profit % |\n'
|
||||
'|:--------------|--------:|---------:|-------:|-----------:|\n'
|
||||
'| roi | 2 | 2 | 0 | 15 |\n'
|
||||
'| stop_loss | 1 | 0 | 1 | -10 |'
|
||||
)
|
||||
assert generate_text_table_sell_reason(
|
||||
data={'ETH/BTC': {}}, results=results) == result_str
|
||||
|
||||
|
||||
def test_generate_text_table_strategy(default_conf, mocker):
|
||||
results = {}
|
||||
results['ETH/BTC'] = pd.DataFrame(
|
||||
{
|
||||
'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC'],
|
||||
'profit_percent': [0.1, 0.2, 0.3],
|
||||
'profit_abs': [0.2, 0.4, 0.5],
|
||||
'trade_duration': [10, 30, 10],
|
||||
'profit': [2, 0, 0],
|
||||
'loss': [0, 0, 1],
|
||||
'sell_reason': [SellType.ROI, SellType.ROI, SellType.STOP_LOSS]
|
||||
}
|
||||
)
|
||||
results['LTC/BTC'] = pd.DataFrame(
|
||||
{
|
||||
'pair': ['LTC/BTC', 'LTC/BTC', 'LTC/BTC'],
|
||||
'profit_percent': [0.4, 0.2, 0.3],
|
||||
'profit_abs': [0.4, 0.4, 0.5],
|
||||
'trade_duration': [15, 30, 15],
|
||||
'profit': [4, 1, 0],
|
||||
'loss': [0, 0, 1],
|
||||
'sell_reason': [SellType.ROI, SellType.ROI, SellType.STOP_LOSS]
|
||||
}
|
||||
)
|
||||
|
||||
result_str = (
|
||||
'| Strategy | buy count | avg profit % | cum profit % '
|
||||
'| tot profit BTC | tot profit % | avg duration | profit | loss |\n'
|
||||
'|:-----------|------------:|---------------:|---------------:'
|
||||
'|-----------------:|---------------:|:---------------|---------:|-------:|\n'
|
||||
'| ETH/BTC | 3 | 20.00 | 60.00 '
|
||||
'| 1.10000000 | 30.00 | 0:17:00 | 3 | 0 |\n'
|
||||
'| LTC/BTC | 3 | 30.00 | 90.00 '
|
||||
'| 1.30000000 | 45.00 | 0:20:00 | 3 | 0 |'
|
||||
)
|
||||
assert generate_text_table_strategy('BTC', 2, all_results=results) == result_str
|
||||
|
||||
|
||||
def test_generate_edge_table(edge_conf, mocker):
|
||||
|
||||
results = {}
|
||||
results['ETH/BTC'] = PairInfo(-0.01, 0.60, 2, 1, 3, 10, 60)
|
||||
|
||||
assert generate_edge_table(results).count(':|') == 7
|
||||
assert generate_edge_table(results).count('| ETH/BTC |') == 1
|
||||
assert generate_edge_table(results).count(
|
||||
'| risk reward ratio | required risk reward | expectancy |') == 1
|
@@ -513,6 +513,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None:
|
||||
),
|
||||
get_fee=fee,
|
||||
)
|
||||
mocker.patch('freqtrade.wallets.Wallets.get_free', return_value=1000)
|
||||
|
||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||
patch_get_signal(freqtradebot, (True, False))
|
||||
|
@@ -323,7 +323,7 @@ def test_load_dry_run(default_conf, mocker, config_value, expected, arglist) ->
|
||||
configuration = Configuration(Arguments(arglist).get_parsed_arg())
|
||||
validated_conf = configuration.load_config()
|
||||
|
||||
assert validated_conf.get('dry_run') is expected
|
||||
assert validated_conf['dry_run'] is expected
|
||||
|
||||
|
||||
def test_load_custom_strategy(default_conf, mocker) -> None:
|
||||
@@ -723,6 +723,14 @@ def test_validate_default_conf(default_conf) -> None:
|
||||
validate_config_schema(default_conf)
|
||||
|
||||
|
||||
def test_validate_max_open_trades(default_conf):
|
||||
default_conf['max_open_trades'] = float('inf')
|
||||
default_conf['stake_amount'] = 'unlimited'
|
||||
with pytest.raises(OperationalException, match='`max_open_trades` and `stake_amount` '
|
||||
'cannot both be unlimited.'):
|
||||
validate_config_consistency(default_conf)
|
||||
|
||||
|
||||
def test_validate_tsl(default_conf):
|
||||
default_conf['stoploss'] = 0.0
|
||||
with pytest.raises(OperationalException, match='The config stoploss needs to be different '
|
||||
@@ -1029,6 +1037,17 @@ def test_process_deprecated_setting_pairlists(mocker, default_conf, caplog):
|
||||
assert log_has_re(r'DEPRECATED.*in pairlist is deprecated and must be moved*', caplog)
|
||||
|
||||
|
||||
def test_process_deprecated_setting_edge(mocker, edge_conf, caplog):
|
||||
patched_configuration_load_config_file(mocker, edge_conf)
|
||||
edge_conf.update({'edge': {
|
||||
'enabled': True,
|
||||
'capital_available_percentage': 0.5,
|
||||
}})
|
||||
|
||||
process_temporary_deprecated_settings(edge_conf)
|
||||
assert log_has_re(r"DEPRECATED.*Using 'edge.capital_available_percentage'*", caplog)
|
||||
|
||||
|
||||
def test_check_conflicting_settings(mocker, default_conf, caplog):
|
||||
patched_configuration_load_config_file(mocker, default_conf)
|
||||
|
||||
|
@@ -140,21 +140,65 @@ def test_get_trade_stake_amount(default_conf, ticker, mocker) -> None:
|
||||
assert result == default_conf['stake_amount']
|
||||
|
||||
|
||||
@pytest.mark.parametrize("amend_last,wallet,max_open,lsamr,expected", [
|
||||
(False, 0.002, 2, 0.5, [0.001, None]),
|
||||
(True, 0.002, 2, 0.5, [0.001, 0.00098]),
|
||||
(False, 0.003, 3, 0.5, [0.001, 0.001, None]),
|
||||
(True, 0.003, 3, 0.5, [0.001, 0.001, 0.00097]),
|
||||
(False, 0.0022, 3, 0.5, [0.001, 0.001, None]),
|
||||
(True, 0.0022, 3, 0.5, [0.001, 0.001, 0.0]),
|
||||
(True, 0.0027, 3, 0.5, [0.001, 0.001, 0.000673]),
|
||||
(True, 0.0022, 3, 1, [0.001, 0.001, 0.0]),
|
||||
])
|
||||
def test_check_available_stake_amount(default_conf, ticker, mocker, fee, limit_buy_order,
|
||||
amend_last, wallet, max_open, lsamr, expected) -> None:
|
||||
patch_RPCManager(mocker)
|
||||
patch_exchange(mocker)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
fetch_ticker=ticker,
|
||||
get_balance=MagicMock(return_value=default_conf['stake_amount'] * 2),
|
||||
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
||||
get_fee=fee
|
||||
)
|
||||
default_conf['dry_run_wallet'] = wallet
|
||||
|
||||
default_conf['amend_last_stake_amount'] = amend_last
|
||||
default_conf['last_stake_amount_min_ratio'] = lsamr
|
||||
|
||||
freqtrade = FreqtradeBot(default_conf)
|
||||
|
||||
for i in range(0, max_open):
|
||||
|
||||
if expected[i] is not None:
|
||||
result = freqtrade.get_trade_stake_amount('ETH/BTC')
|
||||
assert pytest.approx(result) == expected[i]
|
||||
freqtrade.execute_buy('ETH/BTC', result)
|
||||
else:
|
||||
with pytest.raises(DependencyException):
|
||||
freqtrade.get_trade_stake_amount('ETH/BTC')
|
||||
|
||||
|
||||
def test_get_trade_stake_amount_no_stake_amount(default_conf, mocker) -> None:
|
||||
patch_RPCManager(mocker)
|
||||
patch_exchange(mocker)
|
||||
patch_wallet(mocker, free=default_conf['stake_amount'] * 0.5)
|
||||
freqtrade = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtrade)
|
||||
|
||||
with pytest.raises(DependencyException, match=r'.*stake amount.*'):
|
||||
freqtrade.get_trade_stake_amount('ETH/BTC')
|
||||
|
||||
|
||||
def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker,
|
||||
@pytest.mark.parametrize("balance_ratio,result1", [
|
||||
(1, 0.005),
|
||||
(0.99, 0.00495),
|
||||
(0.50, 0.0025),
|
||||
])
|
||||
def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker, balance_ratio, result1,
|
||||
limit_buy_order, fee, mocker) -> None:
|
||||
patch_RPCManager(mocker)
|
||||
patch_exchange(mocker)
|
||||
patch_wallet(mocker, free=default_conf['stake_amount'])
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
fetch_ticker=ticker,
|
||||
@@ -164,32 +208,34 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker,
|
||||
|
||||
conf = deepcopy(default_conf)
|
||||
conf['stake_amount'] = UNLIMITED_STAKE_AMOUNT
|
||||
conf['dry_run_wallet'] = 0.01
|
||||
conf['max_open_trades'] = 2
|
||||
conf['tradable_balance_ratio'] = balance_ratio
|
||||
|
||||
freqtrade = FreqtradeBot(conf)
|
||||
patch_get_signal(freqtrade)
|
||||
|
||||
# no open trades, order amount should be 'balance / max_open_trades'
|
||||
result = freqtrade.get_trade_stake_amount('ETH/BTC')
|
||||
assert result == default_conf['stake_amount'] / conf['max_open_trades']
|
||||
assert result == result1
|
||||
|
||||
# create one trade, order amount should be 'balance / (max_open_trades - num_open_trades)'
|
||||
freqtrade.execute_buy('ETH/BTC', result)
|
||||
|
||||
result = freqtrade.get_trade_stake_amount('LTC/BTC')
|
||||
assert result == default_conf['stake_amount'] / (conf['max_open_trades'] - 1)
|
||||
assert result == result1
|
||||
|
||||
# create 2 trades, order amount should be None
|
||||
freqtrade.execute_buy('LTC/BTC', result)
|
||||
|
||||
result = freqtrade.get_trade_stake_amount('XRP/BTC')
|
||||
assert result is None
|
||||
assert result == 0
|
||||
|
||||
# set max_open_trades = None, so do not trade
|
||||
conf['max_open_trades'] = 0
|
||||
freqtrade = FreqtradeBot(conf)
|
||||
result = freqtrade.get_trade_stake_amount('NEO/BTC')
|
||||
assert result is None
|
||||
assert result == 0
|
||||
|
||||
|
||||
def test_edge_called_in_process(mocker, edge_conf) -> None:
|
||||
@@ -570,7 +616,7 @@ def test_create_trade_limit_reached(default_conf, ticker, limit_buy_order,
|
||||
patch_get_signal(freqtrade)
|
||||
|
||||
assert not freqtrade.create_trade('ETH/BTC')
|
||||
assert freqtrade.get_trade_stake_amount('ETH/BTC') is None
|
||||
assert freqtrade.get_trade_stake_amount('ETH/BTC') == 0
|
||||
|
||||
|
||||
def test_enter_positions_no_pairs_left(default_conf, ticker, limit_buy_order, fee,
|
||||
@@ -635,11 +681,15 @@ def test_create_trade_no_signal(default_conf, fee, mocker) -> None:
|
||||
|
||||
|
||||
@pytest.mark.parametrize("max_open", range(0, 5))
|
||||
def test_create_trades_multiple_trades(default_conf, ticker,
|
||||
fee, mocker, max_open) -> None:
|
||||
@pytest.mark.parametrize("tradable_balance_ratio,modifier", [(1.0, 1), (0.99, 0.8), (0.5, 0.5)])
|
||||
def test_create_trades_multiple_trades(default_conf, ticker, fee, mocker,
|
||||
max_open, tradable_balance_ratio, modifier) -> None:
|
||||
patch_RPCManager(mocker)
|
||||
patch_exchange(mocker)
|
||||
default_conf['max_open_trades'] = max_open
|
||||
default_conf['tradable_balance_ratio'] = tradable_balance_ratio
|
||||
default_conf['dry_run_wallet'] = 0.001 * max_open
|
||||
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
fetch_ticker=ticker,
|
||||
@@ -650,10 +700,11 @@ def test_create_trades_multiple_trades(default_conf, ticker,
|
||||
patch_get_signal(freqtrade)
|
||||
|
||||
n = freqtrade.enter_positions()
|
||||
assert n == max_open
|
||||
|
||||
trades = Trade.get_open_trades()
|
||||
assert len(trades) == max_open
|
||||
# Expected trades should be max_open * a modified value
|
||||
# depending on the configured tradable_balance
|
||||
assert n == max(int(max_open * modifier), 0)
|
||||
assert len(trades) == max(int(max_open * modifier), 0)
|
||||
|
||||
|
||||
def test_create_trades_preopen(default_conf, ticker, fee, mocker) -> None:
|
||||
@@ -1267,6 +1318,14 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog,
|
||||
rate=0.00002344 * 0.95 * 0.99,
|
||||
stop_price=0.00002344 * 0.95)
|
||||
|
||||
# price fell below stoploss, so dry-run sells trade.
|
||||
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={
|
||||
'bid': 0.00002144,
|
||||
'ask': 0.00002146,
|
||||
'last': 0.00002144
|
||||
}))
|
||||
assert freqtrade.handle_trade(trade) is True
|
||||
|
||||
|
||||
def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, caplog,
|
||||
limit_buy_order, limit_sell_order) -> None:
|
||||
@@ -2384,8 +2443,8 @@ def test_execute_sell_with_stoploss_on_exchange(default_conf, ticker, fee, ticke
|
||||
'freqtrade.exchange.Exchange',
|
||||
fetch_ticker=ticker,
|
||||
get_fee=fee,
|
||||
symbol_amount_prec=lambda s, x, y: y,
|
||||
symbol_price_prec=lambda s, x, y: y,
|
||||
amount_to_precision=lambda s, x, y: y,
|
||||
price_to_precision=lambda s, x, y: y,
|
||||
stoploss_limit=stoploss_limit,
|
||||
cancel_order=cancel_order,
|
||||
)
|
||||
@@ -2427,8 +2486,8 @@ def test_may_execute_sell_after_stoploss_on_exchange_hit(default_conf, ticker, f
|
||||
'freqtrade.exchange.Exchange',
|
||||
fetch_ticker=ticker,
|
||||
get_fee=fee,
|
||||
symbol_amount_prec=lambda s, x, y: y,
|
||||
symbol_price_prec=lambda s, x, y: y,
|
||||
amount_to_precision=lambda s, x, y: y,
|
||||
price_to_precision=lambda s, x, y: y,
|
||||
)
|
||||
|
||||
stoploss_limit = MagicMock(return_value={
|
||||
@@ -2699,6 +2758,7 @@ def test__safe_sell_amount(default_conf, fee, caplog, mocker):
|
||||
amount = 95.33
|
||||
amount_wallet = 95.29
|
||||
mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=amount_wallet))
|
||||
wallet_update = mocker.patch('freqtrade.wallets.Wallets.update')
|
||||
trade = Trade(
|
||||
pair='LTC/ETH',
|
||||
amount=amount,
|
||||
@@ -2711,8 +2771,15 @@ def test__safe_sell_amount(default_conf, fee, caplog, mocker):
|
||||
freqtrade = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtrade)
|
||||
|
||||
wallet_update.reset_mock()
|
||||
assert freqtrade._safe_sell_amount(trade.pair, trade.amount) == amount_wallet
|
||||
assert log_has_re(r'.*Falling back to wallet-amount.', caplog)
|
||||
assert wallet_update.call_count == 1
|
||||
caplog.clear()
|
||||
wallet_update.reset_mock()
|
||||
assert freqtrade._safe_sell_amount(trade.pair, amount_wallet) == amount_wallet
|
||||
assert not log_has_re(r'.*Falling back to wallet-amount.', caplog)
|
||||
assert wallet_update.call_count == 1
|
||||
|
||||
|
||||
def test__safe_sell_amount_error(default_conf, fee, caplog, mocker):
|
||||
@@ -3622,6 +3689,7 @@ def test_sync_wallet_dry_run(mocker, default_conf, ticker, fee, limit_buy_order,
|
||||
# Initialize to 2 times stake amount
|
||||
default_conf['dry_run_wallet'] = 0.002
|
||||
default_conf['max_open_trades'] = 2
|
||||
default_conf['tradable_balance_ratio'] = 1.0
|
||||
patch_exchange(mocker)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
@@ -3643,5 +3711,5 @@ def test_sync_wallet_dry_run(mocker, default_conf, ticker, fee, limit_buy_order,
|
||||
n = bot.enter_positions()
|
||||
assert n == 0
|
||||
assert log_has_re(r"Unable to create trade for XRP/BTC: "
|
||||
r"Available balance \(0 BTC\) is lower than stake amount \(0.001 BTC\)",
|
||||
r"Available balance \(0.0 BTC\) is lower than stake amount \(0.001 BTC\)",
|
||||
caplog)
|
||||
|
@@ -1,10 +1,11 @@
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from freqtrade.persistence import Trade
|
||||
from freqtrade.rpc.rpc import RPC
|
||||
from freqtrade.strategy.interface import SellCheckTuple, SellType
|
||||
from tests.conftest import get_patched_freqtradebot, patch_get_signal
|
||||
from freqtrade.rpc.rpc import RPC
|
||||
|
||||
|
||||
def test_may_execute_sell_stoploss_on_exchange_multi(default_conf, ticker, fee,
|
||||
@@ -57,8 +58,8 @@ def test_may_execute_sell_stoploss_on_exchange_multi(default_conf, ticker, fee,
|
||||
'freqtrade.exchange.Exchange',
|
||||
fetch_ticker=ticker,
|
||||
get_fee=fee,
|
||||
symbol_amount_prec=lambda s, x, y: y,
|
||||
symbol_price_prec=lambda s, x, y: y,
|
||||
amount_to_precision=lambda s, x, y: y,
|
||||
price_to_precision=lambda s, x, y: y,
|
||||
get_order=stoploss_order_mock,
|
||||
cancel_order=cancel_order_mock,
|
||||
)
|
||||
@@ -96,8 +97,8 @@ def test_may_execute_sell_stoploss_on_exchange_multi(default_conf, ticker, fee,
|
||||
|
||||
# Only order for 3rd trade needs to be cancelled
|
||||
assert cancel_order_mock.call_count == 1
|
||||
# Wallets should only be called once per sell cycle
|
||||
assert wallets_mock.call_count == 1
|
||||
# Wallets must be updated between stoploss cancellation and selling.
|
||||
assert wallets_mock.call_count == 2
|
||||
|
||||
trade = trades[0]
|
||||
assert trade.sell_reason == SellType.STOPLOSS_ON_EXCHANGE.value
|
||||
@@ -112,13 +113,22 @@ def test_may_execute_sell_stoploss_on_exchange_multi(default_conf, ticker, fee,
|
||||
assert not trade.is_open
|
||||
|
||||
|
||||
def test_forcebuy_last_unlimited(default_conf, ticker, fee, limit_buy_order, mocker) -> None:
|
||||
@pytest.mark.parametrize("balance_ratio,result1", [
|
||||
(1, 200),
|
||||
(0.99, 198),
|
||||
])
|
||||
def test_forcebuy_last_unlimited(default_conf, ticker, fee, limit_buy_order, mocker, balance_ratio,
|
||||
result1) -> None:
|
||||
"""
|
||||
Tests workflow
|
||||
Tests workflow unlimited stake-amount
|
||||
Buy 4 trades, forcebuy a 5th trade
|
||||
Sell one trade, calculated stake amount should now be lower than before since
|
||||
one trade was sold at a loss.
|
||||
"""
|
||||
default_conf['max_open_trades'] = 5
|
||||
default_conf['forcebuy_enable'] = True
|
||||
default_conf['stake_amount'] = 'unlimited'
|
||||
default_conf['tradable_balance_ratio'] = balance_ratio
|
||||
default_conf['dry_run_wallet'] = 1000
|
||||
default_conf['exchange']['name'] = 'binance'
|
||||
default_conf['telegram']['enabled'] = True
|
||||
@@ -127,8 +137,8 @@ def test_forcebuy_last_unlimited(default_conf, ticker, fee, limit_buy_order, moc
|
||||
'freqtrade.exchange.Exchange',
|
||||
fetch_ticker=ticker,
|
||||
get_fee=fee,
|
||||
symbol_amount_prec=lambda s, x, y: y,
|
||||
symbol_price_prec=lambda s, x, y: y,
|
||||
amount_to_precision=lambda s, x, y: y,
|
||||
price_to_precision=lambda s, x, y: y,
|
||||
)
|
||||
|
||||
mocker.patch.multiple(
|
||||
@@ -159,13 +169,15 @@ def test_forcebuy_last_unlimited(default_conf, ticker, fee, limit_buy_order, moc
|
||||
|
||||
trades = Trade.query.all()
|
||||
assert len(trades) == 4
|
||||
assert freqtrade.get_trade_stake_amount('XRP/BTC') == result1
|
||||
|
||||
rpc._rpc_forcebuy('TKN/BTC', None)
|
||||
|
||||
trades = Trade.query.all()
|
||||
assert len(trades) == 5
|
||||
|
||||
for trade in trades:
|
||||
assert trade.stake_amount == 200
|
||||
assert trade.stake_amount == result1
|
||||
# Reset trade open order id's
|
||||
trade.open_order_id = None
|
||||
trades = Trade.get_open_trades()
|
||||
@@ -177,6 +189,8 @@ def test_forcebuy_last_unlimited(default_conf, ticker, fee, limit_buy_order, moc
|
||||
trades = Trade.get_open_trades()
|
||||
# One trade sold
|
||||
assert len(trades) == 4
|
||||
# stake-amount should now be reduced, since one trade was sold at a loss.
|
||||
assert freqtrade.get_trade_stake_amount('XRP/BTC') < result1
|
||||
# Validate that balance of sold trade is not in dry-run balances anymore.
|
||||
bals2 = freqtrade.wallets.get_all_balances()
|
||||
assert bals != bals2
|
||||
|
@@ -13,6 +13,7 @@ from freqtrade.data.btanalysis import create_cum_profit, load_backtest_data
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.plot.plot_utils import start_plot_dataframe, start_plot_profit
|
||||
from freqtrade.plot.plotting import (add_indicators, add_profit,
|
||||
create_plotconfig,
|
||||
generate_candlestick_graph,
|
||||
generate_plot_filename,
|
||||
generate_profit_graph, init_plotscript,
|
||||
@@ -66,8 +67,8 @@ def test_add_indicators(default_conf, testdatadir, caplog):
|
||||
|
||||
data = history.load_pair_history(pair=pair, timeframe='1m',
|
||||
datadir=testdatadir, timerange=timerange)
|
||||
indicators1 = ["ema10"]
|
||||
indicators2 = ["macd"]
|
||||
indicators1 = {"ema10": {}}
|
||||
indicators2 = {"macd": {"color": "red"}}
|
||||
|
||||
# Generate buy/sell signals and indicators
|
||||
strat = DefaultStrategy(default_conf)
|
||||
@@ -86,9 +87,10 @@ def test_add_indicators(default_conf, testdatadir, caplog):
|
||||
macd = find_trace_in_fig_data(figure.data, "macd")
|
||||
assert isinstance(macd, go.Scatter)
|
||||
assert macd.yaxis == "y3"
|
||||
assert macd.line.color == "red"
|
||||
|
||||
# No indicator found
|
||||
fig3 = add_indicators(fig=deepcopy(fig), row=3, indicators=['no_indicator'], data=data)
|
||||
fig3 = add_indicators(fig=deepcopy(fig), row=3, indicators={'no_indicator': {}}, data=data)
|
||||
assert fig == fig3
|
||||
assert log_has_re(r'Indicator "no_indicator" ignored\..*', caplog)
|
||||
|
||||
@@ -108,18 +110,29 @@ def test_plot_trades(testdatadir, caplog):
|
||||
figure = fig1.layout.figure
|
||||
|
||||
# Check buys - color, should be in first graph, ...
|
||||
trade_buy = find_trace_in_fig_data(figure.data, "trade_buy")
|
||||
trade_buy = find_trace_in_fig_data(figure.data, 'Trade buy')
|
||||
assert isinstance(trade_buy, go.Scatter)
|
||||
assert trade_buy.yaxis == 'y'
|
||||
assert len(trades) == len(trade_buy.x)
|
||||
assert trade_buy.marker.color == 'green'
|
||||
assert trade_buy.marker.color == 'cyan'
|
||||
assert trade_buy.marker.symbol == 'circle-open'
|
||||
assert trade_buy.text[0] == '4.0%, roi, 15 min'
|
||||
|
||||
trade_sell = find_trace_in_fig_data(figure.data, "trade_sell")
|
||||
trade_sell = find_trace_in_fig_data(figure.data, 'Sell - Profit')
|
||||
assert isinstance(trade_sell, go.Scatter)
|
||||
assert trade_sell.yaxis == 'y'
|
||||
assert len(trades) == len(trade_sell.x)
|
||||
assert trade_sell.marker.color == 'red'
|
||||
assert trade_sell.text[0] == "4.0%, roi, 15 min"
|
||||
assert len(trades.loc[trades['profitperc'] > 0]) == len(trade_sell.x)
|
||||
assert trade_sell.marker.color == 'green'
|
||||
assert trade_sell.marker.symbol == 'square-open'
|
||||
assert trade_sell.text[0] == '4.0%, roi, 15 min'
|
||||
|
||||
trade_sell_loss = find_trace_in_fig_data(figure.data, 'Sell - Loss')
|
||||
assert isinstance(trade_sell_loss, go.Scatter)
|
||||
assert trade_sell_loss.yaxis == 'y'
|
||||
assert len(trades.loc[trades['profitperc'] <= 0]) == len(trade_sell_loss.x)
|
||||
assert trade_sell_loss.marker.color == 'red'
|
||||
assert trade_sell_loss.marker.symbol == 'square-open'
|
||||
assert trade_sell_loss.text[5] == '-10.4%, stop_loss, 720 min'
|
||||
|
||||
|
||||
def test_generate_candlestick_graph_no_signals_no_trades(default_conf, mocker, testdatadir, caplog):
|
||||
@@ -308,7 +321,7 @@ def test_load_and_plot_trades(default_conf, mocker, caplog, testdatadir):
|
||||
"freqtrade.plot.plotting",
|
||||
generate_candlestick_graph=candle_mock,
|
||||
store_plot_file=store_mock
|
||||
)
|
||||
)
|
||||
load_and_plot_trades(default_conf)
|
||||
|
||||
# Both mocks should be called once per pair
|
||||
@@ -371,3 +384,47 @@ def test_plot_profit(default_conf, mocker, testdatadir, caplog):
|
||||
|
||||
assert profit_mock.call_args_list[0][0][0] == default_conf['pairs']
|
||||
assert store_mock.call_args_list[0][1]['auto_open'] is True
|
||||
|
||||
|
||||
@pytest.mark.parametrize("ind1,ind2,plot_conf,exp", [
|
||||
# No indicators, use plot_conf
|
||||
([], [], {},
|
||||
{'main_plot': {'sma': {}, 'ema3': {}, 'ema5': {}},
|
||||
'subplots': {'Other': {'macd': {}, 'macdsignal': {}}}}),
|
||||
# use indicators
|
||||
(['sma', 'ema3'], ['macd'], {},
|
||||
{'main_plot': {'sma': {}, 'ema3': {}}, 'subplots': {'Other': {'macd': {}}}}),
|
||||
# only main_plot - adds empty subplots
|
||||
([], [], {'main_plot': {'sma': {}}},
|
||||
{'main_plot': {'sma': {}}, 'subplots': {}}),
|
||||
# Main and subplots
|
||||
([], [], {'main_plot': {'sma': {}}, 'subplots': {'RSI': {'rsi': {'color': 'red'}}}},
|
||||
{'main_plot': {'sma': {}}, 'subplots': {'RSI': {'rsi': {'color': 'red'}}}}),
|
||||
# no main_plot, adds empty main_plot
|
||||
([], [], {'subplots': {'RSI': {'rsi': {'color': 'red'}}}},
|
||||
{'main_plot': {}, 'subplots': {'RSI': {'rsi': {'color': 'red'}}}}),
|
||||
# indicator 1 / 2 should have prevelance
|
||||
(['sma', 'ema3'], ['macd'],
|
||||
{'main_plot': {'sma': {}}, 'subplots': {'RSI': {'rsi': {'color': 'red'}}}},
|
||||
{'main_plot': {'sma': {}, 'ema3': {}}, 'subplots': {'Other': {'macd': {}}}}
|
||||
),
|
||||
# indicator 1 - overrides plot_config main_plot
|
||||
(['sma', 'ema3'], [],
|
||||
{'main_plot': {'sma': {}}, 'subplots': {'RSI': {'rsi': {'color': 'red'}}}},
|
||||
{'main_plot': {'sma': {}, 'ema3': {}}, 'subplots': {'RSI': {'rsi': {'color': 'red'}}}}
|
||||
),
|
||||
# indicator 2 - overrides plot_config subplots
|
||||
([], ['macd', 'macd_signal'],
|
||||
{'main_plot': {'sma': {}}, 'subplots': {'RSI': {'rsi': {'color': 'red'}}}},
|
||||
{'main_plot': {'sma': {}}, 'subplots': {'Other': {'macd': {}, 'macd_signal': {}}}}
|
||||
),
|
||||
])
|
||||
def test_create_plotconfig(ind1, ind2, plot_conf, exp):
|
||||
|
||||
res = create_plotconfig(ind1, ind2, plot_conf)
|
||||
assert 'main_plot' in res
|
||||
assert 'subplots' in res
|
||||
assert isinstance(res['main_plot'], dict)
|
||||
assert isinstance(res['subplots'], dict)
|
||||
|
||||
assert res == exp
|
||||
|
@@ -32,7 +32,7 @@ def test_sync_wallet_at_boot(mocker, default_conf):
|
||||
assert freqtrade.wallets._wallets['GAS'].used == 0.0
|
||||
assert freqtrade.wallets._wallets['GAS'].total == 0.260739
|
||||
assert freqtrade.wallets.get_free('BNT') == 1.0
|
||||
|
||||
assert freqtrade.wallets._last_wallet_refresh > 0
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
get_balances=MagicMock(return_value={
|
||||
@@ -61,6 +61,11 @@ def test_sync_wallet_at_boot(mocker, default_conf):
|
||||
assert freqtrade.wallets.get_free('GAS') == 0.270739
|
||||
assert freqtrade.wallets.get_used('GAS') == 0.1
|
||||
assert freqtrade.wallets.get_total('GAS') == 0.260439
|
||||
update_mock = mocker.patch('freqtrade.wallets.Wallets._update_live')
|
||||
freqtrade.wallets.update(False)
|
||||
assert update_mock.call_count == 0
|
||||
freqtrade.wallets.update()
|
||||
assert update_mock.call_count == 1
|
||||
|
||||
|
||||
def test_sync_wallet_missing_data(mocker, default_conf):
|
||||
|
Reference in New Issue
Block a user