Merge in develop changes

This commit is contained in:
froggleston
2023-07-15 16:16:08 +01:00
218 changed files with 10245 additions and 4896 deletions
+76 -33
View File
@@ -97,7 +97,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
'order_date': ANY, 'order_timestamp': ANY, 'order_filled_date': ANY,
'order_filled_timestamp': ANY, 'order_type': 'limit', 'price': 1.098e-05,
'is_open': False, 'pair': 'ETH/BTC', 'order_id': ANY,
'remaining': ANY, 'status': ANY, 'ft_is_entry': True,
'remaining': ANY, 'status': ANY, 'ft_is_entry': True, 'ft_fee_base': None,
}],
}
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
@@ -261,8 +261,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None:
assert isnan(fiat_profit_sum)
def test__rpc_timeunit_profit(default_conf_usdt, ticker, fee,
limit_buy_order, limit_sell_order, markets, mocker) -> None:
def test__rpc_timeunit_profit(default_conf_usdt, ticker, fee, markets, mocker) -> None:
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
mocker.patch.multiple(
EXMS,
@@ -295,7 +294,7 @@ def test__rpc_timeunit_profit(default_conf_usdt, ticker, fee,
assert day['starting_balance'] in (pytest.approx(1062.37), pytest.approx(1066.46))
assert day['fiat_value'] in (0.0, )
# ensure first day is current date
assert str(days['data'][0]['date']) == str(datetime.utcnow().date())
assert str(days['data'][0]['date']) == str(datetime.now(timezone.utc).date())
# Try invalid data
with pytest.raises(RPCException, match=r'.*must be an integer greater than 0*'):
@@ -418,8 +417,8 @@ def test_rpc_trade_statistics(default_conf_usdt, ticker, fee, mocker) -> None:
assert pytest.approx(stats['profit_all_fiat']) == -85.205614098
assert pytest.approx(stats['winrate']) == 66.666666667
assert stats['trade_count'] == 7
assert stats['first_trade_date'] == '2 days ago'
assert stats['latest_trade_date'] == '17 minutes ago'
assert stats['first_trade_humanized'] == '2 days ago'
assert stats['latest_trade_humanized'] == '17 minutes ago'
assert stats['avg_duration'] in ('0:17:40')
assert stats['best_pair'] == 'XRP/USDT'
assert stats['best_rate'] == 10.0
@@ -431,8 +430,8 @@ def test_rpc_trade_statistics(default_conf_usdt, ticker, fee, mocker) -> None:
MagicMock(side_effect=ExchangeError("Pair 'XRP/USDT' not available")))
stats = rpc._rpc_trade_statistics(stake_currency, fiat_display_currency)
assert stats['trade_count'] == 7
assert stats['first_trade_date'] == '2 days ago'
assert stats['latest_trade_date'] == '17 minutes ago'
assert stats['first_trade_humanized'] == '2 days ago'
assert stats['latest_trade_humanized'] == '17 minutes ago'
assert stats['avg_duration'] in ('0:17:40')
assert stats['best_pair'] == 'XRP/USDT'
assert stats['best_rate'] == 10.0
@@ -551,51 +550,67 @@ def test_rpc_balance_handle(default_conf, mocker, tickers):
'free': 10.0,
'balance': 12.0,
'used': 2.0,
'bot_owned': 9.9, # available stake - reducing by reserved amount
'est_stake': 10.0, # In futures mode, "free" is used here.
'est_stake_bot': 9.9,
'stake': 'BTC',
'is_position': False,
'leverage': 1.0,
'position': 0.0,
'side': 'long',
'is_bot_managed': True,
},
{
'free': 1.0,
'balance': 5.0,
'currency': 'ETH',
'bot_owned': 0,
'est_stake': 0.30794,
'est_stake_bot': 0,
'used': 4.0,
'stake': 'BTC',
'is_position': False,
'leverage': 1.0,
'position': 0.0,
'side': 'long',
'is_bot_managed': False,
},
{
'free': 5.0,
'balance': 10.0,
'currency': 'USDT',
'bot_owned': 0,
'est_stake': 0.0011562404610161968,
'est_stake_bot': 0,
'used': 5.0,
'stake': 'BTC',
'is_position': False,
'leverage': 1.0,
'position': 0.0,
'side': 'long',
'is_bot_managed': False,
},
{
'free': 0.0,
'balance': 0.0,
'currency': 'ETH/USDT:USDT',
'est_stake': 20,
'est_stake_bot': 20,
'used': 0,
'stake': 'BTC',
'is_position': True,
'leverage': 5.0,
'position': 1000.0,
'side': 'short',
'is_bot_managed': True,
}
]
assert pytest.approx(result['total_bot']) == 29.9
assert pytest.approx(result['total']) == 30.309096
assert result['starting_capital'] == 10
# Very high starting capital ratio, because the futures position really has the wrong unit.
# TODO: improve this test (see comment above)
assert result['starting_capital_ratio'] == pytest.approx(1.98999999)
def test_rpc_start(mocker, default_conf) -> None:
@@ -693,15 +708,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*'):
@@ -751,27 +766,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={
@@ -784,10 +783,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:
+320 -120
View File
@@ -21,11 +21,13 @@ from freqtrade.__init__ import __version__
from freqtrade.enums import CandleType, RunMode, State, TradingMode
from freqtrade.exceptions import DependencyException, ExchangeError, OperationalException
from freqtrade.loggers import setup_logging, setup_logging_pre
from freqtrade.optimize.backtesting import Backtesting
from freqtrade.persistence import PairLocks, Trade
from freqtrade.rpc import RPC
from freqtrade.rpc.api_server import ApiServer
from freqtrade.rpc.api_server.api_auth import create_token, get_user_from_token
from freqtrade.rpc.api_server.uvicorn_threaded import UvicornServer
from freqtrade.rpc.api_server.webserver_bgwork import ApiBG
from tests.conftest import (CURRENT_TEST_STRATEGY, EXMS, create_mock_trades, get_mock_coro,
get_patched_freqtradebot, log_has, log_has_re, patch_get_signal)
@@ -283,7 +285,7 @@ def test_api__init__(default_conf, mocker):
"username": "TestUser",
"password": "testPass",
}})
mocker.patch('freqtrade.rpc.telegram.Updater', MagicMock())
mocker.patch('freqtrade.rpc.telegram.Telegram._init')
mocker.patch('freqtrade.rpc.api_server.webserver.ApiServer.start_api', MagicMock())
apiserver = ApiServer(default_conf)
apiserver.add_rpc_handler(RPC(get_patched_freqtradebot(mocker, default_conf)))
@@ -341,7 +343,7 @@ def test_api_run(default_conf, mocker, caplog):
"username": "TestUser",
"password": "testPass",
}})
mocker.patch('freqtrade.rpc.telegram.Updater', MagicMock())
mocker.patch('freqtrade.rpc.telegram.Telegram._init')
server_inst_mock = MagicMock()
server_inst_mock.run_in_thread = MagicMock()
@@ -419,7 +421,7 @@ def test_api_cleanup(default_conf, mocker, caplog):
"username": "TestUser",
"password": "testPass",
}})
mocker.patch('freqtrade.rpc.telegram.Updater', MagicMock())
mocker.patch('freqtrade.rpc.telegram.Telegram._init')
server_mock = MagicMock()
server_mock.cleanup = MagicMock()
@@ -480,13 +482,18 @@ def test_api_balance(botclient, mocker, rpc_balance, tickers):
'free': 12.0,
'balance': 12.0,
'used': 0.0,
'bot_owned': pytest.approx(11.879999),
'est_stake': 12.0,
'est_stake_bot': pytest.approx(11.879999),
'stake': 'BTC',
'is_position': False,
'leverage': 1.0,
'position': 0.0,
'side': 'long',
'is_bot_managed': True,
}
assert response['total'] == 12.159513094
assert response['total_bot'] == pytest.approx(11.879999)
assert 'starting_capital' in response
assert 'starting_capital_fiat' in response
assert 'starting_capital_pct' in response
@@ -596,7 +603,7 @@ def test_api_daily(botclient, mocker, ticker, fee, markets):
assert len(rc.json()['data']) == 7
assert rc.json()['stake_currency'] == 'BTC'
assert rc.json()['fiat_display_currency'] == 'USD'
assert rc.json()['data'][0]['date'] == str(datetime.utcnow().date())
assert rc.json()['data'][0]['date'] == str(datetime.now(timezone.utc).date())
@pytest.mark.parametrize('is_short', [True, False])
@@ -735,6 +742,33 @@ def test_api_delete_open_order(botclient, mocker, fee, markets, ticker, is_short
assert cancel_mock.call_count == 1
@pytest.mark.parametrize('is_short', [True, False])
def test_api_trade_reload_trade(botclient, mocker, fee, markets, ticker, is_short):
ftbot, client = botclient
patch_get_signal(ftbot, enter_long=not is_short, enter_short=is_short)
stoploss_mock = MagicMock()
cancel_mock = MagicMock()
ftbot.handle_onexchange_order = MagicMock()
mocker.patch.multiple(
EXMS,
markets=PropertyMock(return_value=markets),
fetch_ticker=ticker,
cancel_order=cancel_mock,
cancel_stoploss_order=stoploss_mock,
)
rc = client_post(client, f"{BASE_URI}/trades/10/reload")
assert_response(rc, 502)
assert 'Could not find trade with id 10.' in rc.json()['error']
assert ftbot.handle_onexchange_order.call_count == 0
create_mock_trades(fee, is_short=is_short)
Trade.commit()
rc = client_post(client, f"{BASE_URI}/trades/5/reload")
assert ftbot.handle_onexchange_order.call_count == 1
def test_api_logs(botclient):
ftbot, client = botclient
rc = client_get(client, f"{BASE_URI}/logs")
@@ -856,8 +890,10 @@ def test_api_profit(botclient, mocker, ticker, fee, markets, is_short, expected)
'best_pair_profit_ratio': expected['best_pair_profit_ratio'],
'best_rate': expected['best_rate'],
'first_trade_date': ANY,
'first_trade_humanized': ANY,
'first_trade_timestamp': ANY,
'latest_trade_date': '5 minutes ago',
'latest_trade_date': ANY,
'latest_trade_humanized': '5 minutes ago',
'latest_trade_timestamp': ANY,
'profit_all_coin': pytest.approx(expected['profit_all_coin']),
'profit_all_fiat': pytest.approx(expected['profit_all_fiat']),
@@ -1192,7 +1228,7 @@ def test_api_force_entry(botclient, mocker, fee, endpoint):
stake_amount=1,
open_rate=0.245441,
open_order_id="123456",
open_date=datetime.utcnow(),
open_date=datetime.now(timezone.utc),
is_open=False,
is_short=False,
fee_close=fee.return_value,
@@ -1297,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()
@@ -1307,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()
@@ -1542,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)
@@ -1580,6 +1657,122 @@ def test_api_freqaimodels(botclient, tmpdir, mocker):
]}
def test_api_pairlists_available(botclient, tmpdir):
ftbot, client = botclient
ftbot.config['user_data_dir'] = Path(tmpdir)
rc = client_get(client, f"{BASE_URI}/pairlists/available")
assert_response(rc, 503)
assert rc.json()['detail'] == 'Bot is not in the correct state.'
ftbot.config['runmode'] = RunMode.WEBSERVER
rc = client_get(client, f"{BASE_URI}/pairlists/available")
assert_response(rc)
response = rc.json()
assert isinstance(response['pairlists'], list)
assert len(response['pairlists']) > 0
assert len([r for r in response['pairlists'] if r['name'] == 'AgeFilter']) == 1
assert len([r for r in response['pairlists'] if r['name'] == 'VolumePairList']) == 1
assert len([r for r in response['pairlists'] if r['name'] == 'StaticPairList']) == 1
volumepl = [r for r in response['pairlists'] if r['name'] == 'VolumePairList'][0]
assert volumepl['is_pairlist_generator'] is True
assert len(volumepl['params']) > 1
age_pl = [r for r in response['pairlists'] if r['name'] == 'AgeFilter'][0]
assert age_pl['is_pairlist_generator'] is False
assert len(volumepl['params']) > 2
def test_api_pairlists_evaluate(botclient, tmpdir, mocker):
ftbot, client = botclient
ftbot.config['user_data_dir'] = Path(tmpdir)
rc = client_get(client, f"{BASE_URI}/pairlists/evaluate/randomJob")
assert_response(rc, 503)
assert rc.json()['detail'] == 'Bot is not in the correct state.'
ftbot.config['runmode'] = RunMode.WEBSERVER
rc = client_get(client, f"{BASE_URI}/pairlists/evaluate/randomJob")
assert_response(rc, 404)
assert rc.json()['detail'] == 'Job not found.'
body = {
"pairlists": [
{"method": "StaticPairList", },
],
"blacklist": [
],
"stake_currency": "BTC"
}
# Fail, already running
ApiBG.pairlist_running = True
rc = client_post(client, f"{BASE_URI}/pairlists/evaluate", body)
assert_response(rc, 400)
assert rc.json()['detail'] == 'Pairlist evaluation is already running.'
# should start the run
ApiBG.pairlist_running = False
rc = client_post(client, f"{BASE_URI}/pairlists/evaluate", body)
assert_response(rc)
assert rc.json()['status'] == 'Pairlist evaluation started in background.'
job_id = rc.json()['job_id']
rc = client_get(client, f"{BASE_URI}/background/RandomJob")
assert_response(rc, 404)
assert rc.json()['detail'] == 'Job not found.'
rc = client_get(client, f"{BASE_URI}/background/{job_id}")
assert_response(rc)
response = rc.json()
assert response['job_id'] == job_id
assert response['job_category'] == 'pairlist'
rc = client_get(client, f"{BASE_URI}/pairlists/evaluate/{job_id}")
assert_response(rc)
response = rc.json()
assert response['result']['whitelist'] == ['ETH/BTC', 'LTC/BTC', 'XRP/BTC', 'NEO/BTC',]
assert response['result']['length'] == 4
# Restart with additional filter, reducing the list to 2
body['pairlists'].append({"method": "OffsetFilter", "number_assets": 2})
rc = client_post(client, f"{BASE_URI}/pairlists/evaluate", body)
assert_response(rc)
assert rc.json()['status'] == 'Pairlist evaluation started in background.'
job_id = rc.json()['job_id']
rc = client_get(client, f"{BASE_URI}/pairlists/evaluate/{job_id}")
assert_response(rc)
response = rc.json()
assert response['result']['whitelist'] == ['ETH/BTC', 'LTC/BTC', ]
assert response['result']['length'] == 2
# Patch __run_pairlists
plm = mocker.patch('freqtrade.rpc.api_server.api_background_tasks.__run_pairlist',
return_value=None)
body = {
"pairlists": [
{"method": "StaticPairList", },
],
"blacklist": [
],
"stake_currency": "BTC",
"exchange": "randomExchange",
"trading_mode": "futures",
"margin_mode": "isolated",
}
rc = client_post(client, f"{BASE_URI}/pairlists/evaluate", body)
assert_response(rc)
assert plm.call_count == 1
call_config = plm.call_args_list[0][0][1]
assert call_config['exchange']['name'] == 'randomExchange'
assert call_config['trading_mode'] == 'futures'
assert call_config['margin_mode'] == 'isolated'
def test_list_available_pairs(botclient):
ftbot, client = botclient
@@ -1631,137 +1824,141 @@ def test_sysinfo(botclient):
def test_api_backtesting(botclient, mocker, fee, caplog, tmpdir):
ftbot, client = botclient
mocker.patch(f'{EXMS}.get_fee', fee)
try:
ftbot, client = botclient
mocker.patch(f'{EXMS}.get_fee', fee)
rc = client_get(client, f"{BASE_URI}/backtest")
# Backtest prevented in default mode
assert_response(rc, 502)
rc = client_get(client, f"{BASE_URI}/backtest")
# Backtest prevented in default mode
assert_response(rc, 503)
assert rc.json()['detail'] == 'Bot is not in the correct state.'
ftbot.config['runmode'] = RunMode.WEBSERVER
# Backtesting not started yet
rc = client_get(client, f"{BASE_URI}/backtest")
assert_response(rc)
ftbot.config['runmode'] = RunMode.WEBSERVER
# Backtesting not started yet
rc = client_get(client, f"{BASE_URI}/backtest")
assert_response(rc)
result = rc.json()
assert result['status'] == 'not_started'
assert not result['running']
assert result['status_msg'] == 'Backtest not yet executed'
assert result['progress'] == 0
result = rc.json()
assert result['status'] == 'not_started'
assert not result['running']
assert result['status_msg'] == 'Backtest not yet executed'
assert result['progress'] == 0
# Reset backtesting
rc = client_delete(client, f"{BASE_URI}/backtest")
assert_response(rc)
result = rc.json()
assert result['status'] == 'reset'
assert not result['running']
assert result['status_msg'] == 'Backtest reset'
ftbot.config['export'] = 'trades'
ftbot.config['backtest_cache'] = 'day'
ftbot.config['user_data_dir'] = Path(tmpdir)
ftbot.config['exportfilename'] = Path(tmpdir) / "backtest_results"
ftbot.config['exportfilename'].mkdir()
# Reset backtesting
rc = client_delete(client, f"{BASE_URI}/backtest")
assert_response(rc)
result = rc.json()
assert result['status'] == 'reset'
assert not result['running']
assert result['status_msg'] == 'Backtest reset'
ftbot.config['export'] = 'trades'
ftbot.config['backtest_cache'] = 'day'
ftbot.config['user_data_dir'] = Path(tmpdir)
ftbot.config['exportfilename'] = Path(tmpdir) / "backtest_results"
ftbot.config['exportfilename'].mkdir()
# start backtesting
data = {
"strategy": CURRENT_TEST_STRATEGY,
"timeframe": "5m",
"timerange": "20180110-20180111",
"max_open_trades": 3,
"stake_amount": 100,
"dry_run_wallet": 1000,
"enable_protections": False
}
rc = client_post(client, f"{BASE_URI}/backtest", data=data)
assert_response(rc)
result = rc.json()
# start backtesting
data = {
"strategy": CURRENT_TEST_STRATEGY,
"timeframe": "5m",
"timerange": "20180110-20180111",
"max_open_trades": 3,
"stake_amount": 100,
"dry_run_wallet": 1000,
"enable_protections": False
}
rc = client_post(client, f"{BASE_URI}/backtest", data=data)
assert_response(rc)
result = rc.json()
assert result['status'] == 'running'
assert result['progress'] == 0
assert result['running']
assert result['status_msg'] == 'Backtest started'
assert result['status'] == 'running'
assert result['progress'] == 0
assert result['running']
assert result['status_msg'] == 'Backtest started'
rc = client_get(client, f"{BASE_URI}/backtest")
assert_response(rc)
rc = client_get(client, f"{BASE_URI}/backtest")
assert_response(rc)
result = rc.json()
assert result['status'] == 'ended'
assert not result['running']
assert result['status_msg'] == 'Backtest ended'
assert result['progress'] == 1
assert result['backtest_result']
result = rc.json()
assert result['status'] == 'ended'
assert not result['running']
assert result['status_msg'] == 'Backtest ended'
assert result['progress'] == 1
assert result['backtest_result']
rc = client_get(client, f"{BASE_URI}/backtest/abort")
assert_response(rc)
result = rc.json()
assert result['status'] == 'not_running'
assert not result['running']
assert result['status_msg'] == 'Backtest ended'
rc = client_get(client, f"{BASE_URI}/backtest/abort")
assert_response(rc)
result = rc.json()
assert result['status'] == 'not_running'
assert not result['running']
assert result['status_msg'] == 'Backtest ended'
# Simulate running backtest
ApiServer._bgtask_running = True
rc = client_get(client, f"{BASE_URI}/backtest/abort")
assert_response(rc)
result = rc.json()
assert result['status'] == 'stopping'
assert not result['running']
assert result['status_msg'] == 'Backtest ended'
# Simulate running backtest
ApiBG.bgtask_running = True
rc = client_get(client, f"{BASE_URI}/backtest/abort")
assert_response(rc)
result = rc.json()
assert result['status'] == 'stopping'
assert not result['running']
assert result['status_msg'] == 'Backtest ended'
# Get running backtest...
rc = client_get(client, f"{BASE_URI}/backtest")
assert_response(rc)
result = rc.json()
assert result['status'] == 'running'
assert result['running']
assert result['step'] == "backtest"
assert result['status_msg'] == "Backtest running"
# Get running backtest...
rc = client_get(client, f"{BASE_URI}/backtest")
assert_response(rc)
result = rc.json()
assert result['status'] == 'running'
assert result['running']
assert result['step'] == "backtest"
assert result['status_msg'] == "Backtest running"
# Try delete with task still running
rc = client_delete(client, f"{BASE_URI}/backtest")
assert_response(rc)
result = rc.json()
assert result['status'] == 'running'
# Try delete with task still running
rc = client_delete(client, f"{BASE_URI}/backtest")
assert_response(rc)
result = rc.json()
assert result['status'] == 'running'
# Post to backtest that's still running
rc = client_post(client, f"{BASE_URI}/backtest", data=data)
assert_response(rc, 502)
result = rc.json()
assert 'Bot Background task already running' in result['error']
# Post to backtest that's still running
rc = client_post(client, f"{BASE_URI}/backtest", data=data)
assert_response(rc, 502)
result = rc.json()
assert 'Bot Background task already running' in result['error']
ApiServer._bgtask_running = False
ApiBG.bgtask_running = False
# Rerun backtest (should get previous result)
rc = client_post(client, f"{BASE_URI}/backtest", data=data)
assert_response(rc)
result = rc.json()
assert log_has_re('Reusing result of previous backtest.*', caplog)
# Rerun backtest (should get previous result)
rc = client_post(client, f"{BASE_URI}/backtest", data=data)
assert_response(rc)
result = rc.json()
assert log_has_re('Reusing result of previous backtest.*', caplog)
data['stake_amount'] = 101
data['stake_amount'] = 101
mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest_one_strategy',
side_effect=DependencyException('DeadBeef'))
rc = client_post(client, f"{BASE_URI}/backtest", data=data)
assert log_has("Backtesting caused an error: DeadBeef", caplog)
mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest_one_strategy',
side_effect=DependencyException('DeadBeef'))
rc = client_post(client, f"{BASE_URI}/backtest", data=data)
assert log_has("Backtesting caused an error: DeadBeef", caplog)
rc = client_get(client, f"{BASE_URI}/backtest")
assert_response(rc)
result = rc.json()
assert result['status'] == 'error'
assert 'Backtest failed' in result['status_msg']
rc = client_get(client, f"{BASE_URI}/backtest")
assert_response(rc)
result = rc.json()
assert result['status'] == 'error'
assert 'Backtest failed' in result['status_msg']
# Delete backtesting to avoid leakage since the backtest-object may stick around.
rc = client_delete(client, f"{BASE_URI}/backtest")
assert_response(rc)
# Delete backtesting to avoid leakage since the backtest-object may stick around.
rc = client_delete(client, f"{BASE_URI}/backtest")
assert_response(rc)
result = rc.json()
assert result['status'] == 'reset'
assert not result['running']
assert result['status_msg'] == 'Backtest reset'
result = rc.json()
assert result['status'] == 'reset'
assert not result['running']
assert result['status_msg'] == 'Backtest reset'
# Disallow base64 strategies
data['strategy'] = "xx:cHJpbnQoImhlbGxvIHdvcmxkIik="
rc = client_post(client, f"{BASE_URI}/backtest", data=data)
assert_response(rc, 500)
# Disallow base64 strategies
data['strategy'] = "xx:cHJpbnQoImhlbGxvIHdvcmxkIik="
rc = client_post(client, f"{BASE_URI}/backtest", data=data)
assert_response(rc, 500)
finally:
Backtesting.cleanup()
def test_api_backtest_history(botclient, mocker, testdatadir):
@@ -1773,7 +1970,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
@@ -1872,7 +2071,7 @@ def test_api_ws_send_msg(default_conf, mocker, caplog):
"password": _TEST_PASS,
"ws_token": _TEST_WS_TOKEN
}})
mocker.patch('freqtrade.rpc.telegram.Updater')
mocker.patch('freqtrade.rpc.telegram.Telegram._init')
mocker.patch('freqtrade.rpc.api_server.ApiServer.start_api')
apiserver = ApiServer(default_conf)
apiserver.add_rpc_handler(RPC(get_patched_freqtradebot(mocker, default_conf)))
@@ -1891,3 +2090,4 @@ def test_api_ws_send_msg(default_conf, mocker, caplog):
finally:
ApiServer.shutdown()
ApiServer.shutdown()
File diff suppressed because it is too large Load Diff
+15 -10
View File
@@ -17,6 +17,10 @@ def get_webhook_dict() -> dict:
"enabled": True,
"url": "https://maker.ifttt.com/trigger/freqtrade_test/with/key/c764udvJ5jfSlswVRukZZ2/",
"webhookentry": {
# Intentionally broken, as "entry" should have priority.
"value1": "Buying {pair55555}",
},
"entry": {
"value1": "Buying {pair}",
"value2": "limit {limit:8f}",
"value3": "{stake_amount:8f} {stake_currency}",
@@ -89,15 +93,15 @@ def test_send_msg_webhook(default_conf, mocker):
webhook.send_msg(msg=msg)
assert msg_mock.call_count == 1
assert (msg_mock.call_args[0][0]["value1"] ==
default_conf["webhook"]["webhookentry"]["value1"].format(**msg))
default_conf["webhook"]["entry"]["value1"].format(**msg))
assert (msg_mock.call_args[0][0]["value2"] ==
default_conf["webhook"]["webhookentry"]["value2"].format(**msg))
default_conf["webhook"]["entry"]["value2"].format(**msg))
assert (msg_mock.call_args[0][0]["value3"] ==
default_conf["webhook"]["webhookentry"]["value3"].format(**msg))
default_conf["webhook"]["entry"]["value3"].format(**msg))
assert (msg_mock.call_args[0][0]["value4"] ==
default_conf["webhook"]["webhookentry"]["value4"].format(**msg))
default_conf["webhook"]["entry"]["value4"].format(**msg))
assert (msg_mock.call_args[0][0]["value5"] ==
default_conf["webhook"]["webhookentry"]["value5"].format(**msg))
default_conf["webhook"]["entry"]["value5"].format(**msg))
# Test short
msg_mock.reset_mock()
@@ -116,15 +120,15 @@ def test_send_msg_webhook(default_conf, mocker):
webhook.send_msg(msg=msg)
assert msg_mock.call_count == 1
assert (msg_mock.call_args[0][0]["value1"] ==
default_conf["webhook"]["webhookentry"]["value1"].format(**msg))
default_conf["webhook"]["entry"]["value1"].format(**msg))
assert (msg_mock.call_args[0][0]["value2"] ==
default_conf["webhook"]["webhookentry"]["value2"].format(**msg))
default_conf["webhook"]["entry"]["value2"].format(**msg))
assert (msg_mock.call_args[0][0]["value3"] ==
default_conf["webhook"]["webhookentry"]["value3"].format(**msg))
default_conf["webhook"]["entry"]["value3"].format(**msg))
assert (msg_mock.call_args[0][0]["value4"] ==
default_conf["webhook"]["webhookentry"]["value4"].format(**msg))
default_conf["webhook"]["entry"]["value4"].format(**msg))
assert (msg_mock.call_args[0][0]["value5"] ==
default_conf["webhook"]["webhookentry"]["value5"].format(**msg))
default_conf["webhook"]["entry"]["value5"].format(**msg))
# Test buy cancel
msg_mock.reset_mock()
@@ -328,6 +332,7 @@ def test_send_msg_webhook(default_conf, mocker):
def test_exception_send_msg(default_conf, mocker, caplog):
default_conf["webhook"] = get_webhook_dict()
del default_conf["webhook"]["entry"]
del default_conf["webhook"]["webhookentry"]
webhook = Webhook(RPC(get_patched_freqtradebot(mocker, default_conf)), default_conf)