Merge branch 'freqtrade:develop' into develop

This commit is contained in:
hippocritical
2023-06-07 19:22:42 +02:00
committed by GitHub
31 changed files with 1064 additions and 598 deletions

View File

@@ -633,21 +633,23 @@ def test__load_markets(default_conf, mocker, caplog):
assert ex.markets == expected_return
def test_reload_markets(default_conf, mocker, caplog):
def test_reload_markets(default_conf, mocker, caplog, time_machine):
caplog.set_level(logging.DEBUG)
initial_markets = {'ETH/BTC': {}}
updated_markets = {'ETH/BTC': {}, "LTC/BTC": {}}
start_dt = dt_now()
time_machine.move_to(start_dt, tick=False)
api_mock = MagicMock()
api_mock.load_markets = MagicMock(return_value=initial_markets)
default_conf['exchange']['markets_refresh_interval'] = 10
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="binance",
mock_markets=False)
exchange._load_async_markets = MagicMock()
exchange._last_markets_refresh = dt_ts()
assert exchange._last_markets_refresh == dt_ts()
assert exchange.markets == initial_markets
time_machine.move_to(start_dt + timedelta(minutes=8), tick=False)
# less than 10 minutes have passed, no reload
exchange.reload_markets()
assert exchange.markets == initial_markets
@@ -655,12 +657,18 @@ def test_reload_markets(default_conf, mocker, caplog):
api_mock.load_markets = MagicMock(return_value=updated_markets)
# more than 10 minutes have passed, reload is executed
exchange._last_markets_refresh = dt_ts(dt_now() - timedelta(minutes=15))
time_machine.move_to(start_dt + timedelta(minutes=11), tick=False)
exchange.reload_markets()
assert exchange.markets == updated_markets
assert exchange._load_async_markets.call_count == 1
assert log_has('Performing scheduled market reload..', caplog)
# Not called again
exchange._load_async_markets.reset_mock()
exchange.reload_markets()
assert exchange._load_async_markets.call_count == 0
def test_reload_markets_exception(default_conf, mocker, caplog):
caplog.set_level(logging.DEBUG)

View File

@@ -703,15 +703,15 @@ def test_rpc_force_exit(default_conf, ticker, fee, mocker) -> None:
rpc._rpc_force_exit(None)
msg = rpc._rpc_force_exit('all')
assert msg == {'result': 'Created sell orders for all open trades.'}
assert msg == {'result': 'Created exit orders for all open trades.'}
freqtradebot.enter_positions()
msg = rpc._rpc_force_exit('all')
assert msg == {'result': 'Created sell orders for all open trades.'}
assert msg == {'result': 'Created exit orders for all open trades.'}
freqtradebot.enter_positions()
msg = rpc._rpc_force_exit('2')
assert msg == {'result': 'Created sell order for trade 2.'}
assert msg == {'result': 'Created exit order for trade 2.'}
freqtradebot.state = State.STOPPED
with pytest.raises(RPCException, match=r'.*trader is not running*'):
@@ -761,27 +761,11 @@ def test_rpc_force_exit(default_conf, ticker, fee, mocker) -> None:
freqtradebot.config['max_open_trades'] = 3
freqtradebot.enter_positions()
trade = Trade.session.scalars(select(Trade).filter(Trade.id == '2')).first()
amount = trade.amount
# make an limit-buy open trade, if there is no 'filled', don't sell it
mocker.patch(
f'{EXMS}.fetch_order',
return_value={
'status': 'open',
'type': 'limit',
'side': 'buy',
'filled': None
}
)
# check that the trade is called, which is done by ensuring exchange.cancel_order is called
msg = rpc._rpc_force_exit('4')
assert msg == {'result': 'Created sell order for trade 4.'}
assert cancel_order_mock.call_count == 2
assert trade.amount == amount
cancel_order_mock.reset_mock()
trade = Trade.session.scalars(select(Trade).filter(Trade.id == '3')).first()
# make an limit-sell open trade
amount = trade.amount
# make an limit-sell open order trade
mocker.patch(
f'{EXMS}.fetch_order',
return_value={
@@ -794,10 +778,54 @@ def test_rpc_force_exit(default_conf, ticker, fee, mocker) -> None:
'id': trade.orders[0].order_id,
}
)
cancel_order_3 = mocker.patch(
f'{EXMS}.cancel_order_with_result',
return_value={
'status': 'canceled',
'type': 'limit',
'side': 'sell',
'amount': amount,
'remaining': amount,
'filled': 0.0,
'id': trade.orders[0].order_id,
}
)
msg = rpc._rpc_force_exit('3')
assert msg == {'result': 'Created sell order for trade 3.'}
assert msg == {'result': 'Created exit order for trade 3.'}
# status quo, no exchange calls
assert cancel_order_mock.call_count == 3
assert cancel_order_3.call_count == 1
assert cancel_order_mock.call_count == 0
trade = Trade.session.scalars(select(Trade).filter(Trade.id == '2')).first()
amount = trade.amount
# make an limit-buy open trade, if there is no 'filled', don't sell it
mocker.patch(
f'{EXMS}.fetch_order',
return_value={
'status': 'open',
'type': 'limit',
'side': 'buy',
'filled': None
}
)
cancel_order_4 = mocker.patch(
f'{EXMS}.cancel_order_with_result',
return_value={
'status': 'canceled',
'type': 'limit',
'side': 'sell',
'amount': amount,
'remaining': 0.0,
'filled': amount,
'id': trade.orders[0].order_id,
}
)
# check that the trade is called, which is done by ensuring exchange.cancel_order is called
msg = rpc._rpc_force_exit('4')
assert msg == {'result': 'Created exit order for trade 4.'}
assert cancel_order_4.call_count == 1
assert cancel_order_mock.call_count == 0
assert trade.amount == amount
def test_performance_handle(default_conf_usdt, ticker, fee, mocker) -> None:

View File

@@ -1333,7 +1333,7 @@ def test_api_forceexit(botclient, mocker, ticker, fee, markets):
rc = client_post(client, f"{BASE_URI}/forceexit",
data={"tradeid": "5", "ordertype": "market", "amount": 23})
assert_response(rc)
assert rc.json() == {'result': 'Created sell order for trade 5.'}
assert rc.json() == {'result': 'Created exit order for trade 5.'}
Trade.rollback()
trade = Trade.get_trades([Trade.id == 5]).first()
@@ -1343,7 +1343,7 @@ def test_api_forceexit(botclient, mocker, ticker, fee, markets):
rc = client_post(client, f"{BASE_URI}/forceexit",
data={"tradeid": "5"})
assert_response(rc)
assert rc.json() == {'result': 'Created sell order for trade 5.'}
assert rc.json() == {'result': 'Created exit order for trade 5.'}
Trade.rollback()
trade = Trade.get_trades([Trade.id == 5]).first()
@@ -1578,6 +1578,47 @@ def test_api_strategy(botclient):
assert_response(rc, 500)
def test_api_exchanges(botclient):
ftbot, client = botclient
rc = client_get(client, f"{BASE_URI}/exchanges")
assert_response(rc)
response = rc.json()
assert isinstance(response['exchanges'], list)
assert len(response['exchanges']) > 20
okx = [x for x in response['exchanges'] if x['name'] == 'okx'][0]
assert okx == {
"name": "okx",
"valid": True,
"supported": True,
"comment": "",
"trade_modes": [
{
"trading_mode": "spot",
"margin_mode": ""
},
{
"trading_mode": "futures",
"margin_mode": "isolated"
}
]
}
mexc = [x for x in response['exchanges'] if x['name'] == 'mexc'][0]
assert mexc == {
"name": "mexc",
"valid": True,
"supported": False,
"comment": "",
"trade_modes": [
{
"trading_mode": "spot",
"margin_mode": ""
}
]
}
def test_api_freqaimodels(botclient, tmpdir, mocker):
ftbot, client = botclient
ftbot.config['user_data_dir'] = Path(tmpdir)
@@ -1673,7 +1714,8 @@ def test_api_backtesting(botclient, mocker, fee, caplog, tmpdir):
rc = client_get(client, f"{BASE_URI}/backtest")
# Backtest prevented in default mode
assert_response(rc, 502)
assert_response(rc, 503)
assert rc.json()['detail'] == 'Bot is not in the correct state.'
ftbot.config['runmode'] = RunMode.WEBSERVER
# Backtesting not started yet
@@ -1812,7 +1854,9 @@ def test_api_backtest_history(botclient, mocker, testdatadir):
])
rc = client_get(client, f"{BASE_URI}/backtest/history")
assert_response(rc, 502)
assert_response(rc, 503)
assert rc.json()['detail'] == 'Bot is not in the correct state.'
ftbot.config['user_data_dir'] = testdatadir
ftbot.config['runmode'] = RunMode.WEBSERVER
@@ -1930,3 +1974,4 @@ def test_api_ws_send_msg(default_conf, mocker, caplog):
finally:
ApiServer.shutdown()
ApiServer.shutdown()

View File

@@ -118,7 +118,7 @@ def test_main_operational_exception(mocker, default_conf, caplog) -> None:
def test_main_operational_exception1(mocker, default_conf, caplog) -> None:
patch_exchange(mocker)
mocker.patch(
'freqtrade.commands.list_commands.validate_exchanges',
'freqtrade.commands.list_commands.list_available_exchanges',
MagicMock(side_effect=ValueError('Oh snap!'))
)
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 not log_has_re(r'SIGINT.*', caplog)
mocker.patch(
'freqtrade.commands.list_commands.validate_exchanges',
'freqtrade.commands.list_commands.list_available_exchanges',
MagicMock(side_effect=KeyboardInterrupt)
)
with pytest.raises(SystemExit):