mirror of
https://github.com/freqtrade/freqtrade.git
synced 2026-02-11 08:40:46 +00:00
Merge branch 'freqtrade:develop' into develop
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user