Merge branch 'develop' into feature/fetch-public-trades

This commit is contained in:
Matthias
2024-04-05 06:35:35 +02:00
99 changed files with 5849 additions and 1672 deletions
+45 -6
View File
@@ -12,9 +12,9 @@ from freqtrade.commands import (start_backtesting_show, start_convert_data, star
start_create_userdir, start_download_data, start_hyperopt_list,
start_hyperopt_show, start_install_ui, start_list_data,
start_list_exchanges, start_list_markets, start_list_strategies,
start_list_timeframes, start_new_strategy, start_show_trades,
start_strategy_update, start_test_pairlist, start_trading,
start_webserver)
start_list_timeframes, start_new_strategy, start_show_config,
start_show_trades, start_strategy_update, start_test_pairlist,
start_trading, start_webserver)
from freqtrade.commands.db_commands import start_convert_db
from freqtrade.commands.deploy_commands import (clean_ui_subdir, download_and_install_ui,
get_ui_download_url, read_ui_version)
@@ -39,6 +39,14 @@ def test_setup_utils_configuration():
assert "exchange" in config
assert config['dry_run'] is True
args = [
'list-exchanges', '--config', 'tests/testdata/testconfigs/testconfig.json',
]
config = setup_utils_configuration(get_args(args), RunMode.OTHER, set_dry=False)
assert "exchange" in config
assert config['dry_run'] is False
def test_start_trading_fail(mocker, caplog):
@@ -51,15 +59,16 @@ def test_start_trading_fail(mocker, caplog):
'trade',
'-c', 'tests/testdata/testconfigs/main_test_config.json'
]
start_trading(get_args(args))
with pytest.raises(OperationalException):
start_trading(get_args(args))
assert exitmock.call_count == 1
exitmock.reset_mock()
caplog.clear()
mocker.patch("freqtrade.worker.Worker.__init__", MagicMock(side_effect=OperationalException))
start_trading(get_args(args))
with pytest.raises(OperationalException):
start_trading(get_args(args))
assert exitmock.call_count == 0
assert log_has('Fatal exception!', caplog)
def test_start_webserver(mocker, caplog):
@@ -1571,3 +1580,33 @@ def test_start_strategy_updater(mocker, tmp_path):
start_strategy_update(pargs)
# Number of strategies in the test directory
assert sc_mock.call_count == 2
def test_start_show_config(capsys, caplog):
args = [
"show-config",
"--config",
"tests/testdata/testconfigs/main_test_config.json",
]
pargs = get_args(args)
start_show_config(pargs)
captured = capsys.readouterr()
assert "Your combined configuration is:" in captured.out
assert '"max_open_trades":' in captured.out
assert '"secret": "REDACTED"' in captured.out
args = [
"show-config",
"--config",
"tests/testdata/testconfigs/main_test_config.json",
"--show-sensitive"
]
pargs = get_args(args)
start_show_config(pargs)
captured = capsys.readouterr()
assert "Your combined configuration is:" in captured.out
assert '"max_open_trades":' in captured.out
assert '"secret": "REDACTED"' not in captured.out
assert log_has_re(r'Sensitive information will be shown in the upcomming output.*', caplog)
+8 -5
View File
@@ -11,8 +11,8 @@ from numpy import NaN
from pandas import DataFrame
from freqtrade.enums import CandleType, MarginMode, RunMode, TradingMode
from freqtrade.exceptions import (DDosProtection, DependencyException, ExchangeError,
InsufficientFundsError, InvalidOrderException,
from freqtrade.exceptions import (ConfigurationError, DDosProtection, DependencyException,
ExchangeError, InsufficientFundsError, InvalidOrderException,
OperationalException, PricingError, TemporaryError)
from freqtrade.exchange import (Binance, Bybit, Exchange, Kraken, market_is_active,
timeframe_to_prev_date)
@@ -595,7 +595,7 @@ def test_validate_stakecurrency_error(default_conf, mocker, caplog):
mocker.patch(f'{EXMS}.validate_pairs')
mocker.patch(f'{EXMS}.validate_timeframes')
mocker.patch(f'{EXMS}._load_async_markets')
with pytest.raises(OperationalException,
with pytest.raises(ConfigurationError,
match=r'XRP is not available as stake on .*'
'Available currencies are: BTC, ETH, USDT'):
Exchange(default_conf)
@@ -800,12 +800,12 @@ def test_validate_timeframes_failed(default_conf, mocker):
mocker.patch(f'{EXMS}.validate_pairs')
mocker.patch(f'{EXMS}.validate_stakecurrency')
mocker.patch(f'{EXMS}.validate_pricing')
with pytest.raises(OperationalException,
with pytest.raises(ConfigurationError,
match=r"Invalid timeframe '3m'. This exchange supports.*"):
Exchange(default_conf)
default_conf["timeframe"] = "15s"
with pytest.raises(OperationalException,
with pytest.raises(ConfigurationError,
match=r"Timeframes < 1m are currently not supported by Freqtrade."):
Exchange(default_conf)
@@ -1066,6 +1066,9 @@ def test_exchange_has(default_conf, mocker):
exchange = get_patched_exchange(mocker, default_conf, api_mock)
assert not exchange.exchange_has("deadbeef")
exchange._ft_has['exchange_has_overrides'] = {'deadbeef': True}
assert exchange.exchange_has("deadbeef")
@pytest.mark.parametrize("side,leverage", [
("buy", 1),
+7
View File
@@ -262,6 +262,13 @@ EXCHANGES = {
'leverage_tiers_public': False,
'leverage_in_spot_market': False,
},
'bingx': {
'pair': 'BTC/USDT',
'stake_currency': 'USDT',
'hasQuoteVolume': True,
'timeframe': '1h',
'futures': False,
},
}
+6 -1
View File
@@ -1233,6 +1233,7 @@ def test_update_trade_state(mocker, default_conf_usdt, limit_order, is_short, ca
order_id=order_id,
))
freqtrade.strategy.order_filled = MagicMock(return_value=None)
assert not freqtrade.update_trade_state(trade, None)
assert log_has_re(r'Orderid for trade .* is empty.', caplog)
caplog.clear()
@@ -1243,6 +1244,7 @@ def test_update_trade_state(mocker, default_conf_usdt, limit_order, is_short, ca
caplog.clear()
assert not trade.has_open_orders
assert trade.amount == order['amount']
assert freqtrade.strategy.order_filled.call_count == 1
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', return_value=0.01)
assert trade.amount == 30.0
@@ -1260,11 +1262,13 @@ def test_update_trade_state(mocker, default_conf_usdt, limit_order, is_short, ca
limit_buy_order_usdt_new['filled'] = 0.0
limit_buy_order_usdt_new['status'] = 'canceled'
freqtrade.strategy.order_filled = MagicMock(return_value=None)
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', side_effect=ValueError)
mocker.patch(f'{EXMS}.fetch_order', return_value=limit_buy_order_usdt_new)
res = freqtrade.update_trade_state(trade, order_id)
# Cancelled empty
assert res is True
assert freqtrade.strategy.order_filled.call_count == 0
@pytest.mark.parametrize("is_short", [False, True])
@@ -5460,9 +5464,10 @@ def test_check_and_call_adjust_trade_position(mocker, default_conf_usdt, fee, ca
assert freqtrade.strategy.adjust_trade_position.call_count == 1
caplog.clear()
freqtrade.strategy.adjust_trade_position = MagicMock(return_value=(-10, 'partial_exit_c'))
freqtrade.strategy.adjust_trade_position = MagicMock(return_value=(-0.0005, 'partial_exit_c'))
freqtrade.process_open_trade_positions()
assert log_has_re(r"LIMIT_SELL has been fulfilled.*", caplog)
assert freqtrade.strategy.adjust_trade_position.call_count == 1
trade = Trade.get_trades(trade_filter=[Trade.id == 5]).first()
assert trade.orders[-1].ft_order_tag == 'partial_exit_c'
assert trade.is_open
+6 -6
View File
@@ -636,12 +636,12 @@ def test_dca_exiting(default_conf_usdt, ticker_usdt, fee, mocker, caplog, levera
assert len(trade.orders) == 2
assert trade.orders[-1].ft_order_side == 'sell'
assert trade.orders[-1].ft_order_tag == 'PES'
assert pytest.approx(trade.stake_amount) == 40.198
assert pytest.approx(trade.amount) == 20.099 * leverage
assert pytest.approx(trade.stake_amount) == 40
assert pytest.approx(trade.amount) == 20 * leverage
assert trade.open_rate == 2.0
assert trade.is_open
assert trade.realized_profit > 0.098 * leverage
expected_profit = starting_amount - 40.1980 + trade.realized_profit
expected_profit = starting_amount - 40 + trade.realized_profit
assert pytest.approx(freqtrade.wallets.get_free('USDT')) == expected_profit
if spot:
@@ -667,14 +667,14 @@ def test_dca_exiting(default_conf_usdt, ticker_usdt, fee, mocker, caplog, levera
# Amount exactly comes out as exactly 0
freqtrade.strategy.adjust_trade_position = MagicMock(
return_value=-(trade.amount / trade.leverage * 2.02))
return_value=-trade.stake_amount)
freqtrade.process()
trade = Trade.get_trades().first()
assert len(trade.orders) == 3
assert trade.orders[-1].ft_order_side == 'sell'
assert pytest.approx(trade.stake_amount) == 40.198
assert pytest.approx(trade.stake_amount) == 40
assert trade.is_open is False
# use amount that would trunc to 0.0 once selling
@@ -684,7 +684,7 @@ def test_dca_exiting(default_conf_usdt, ticker_usdt, fee, mocker, caplog, levera
trade = Trade.get_trades().first()
assert len(trade.orders) == 3
assert trade.orders[-1].ft_order_side == 'sell'
assert pytest.approx(trade.stake_amount) == 40.198
assert pytest.approx(trade.stake_amount) == 40
assert trade.is_open is False
assert log_has_re('Amount to exit is 0.0 due to exchange limits - not exiting.', caplog)
expected_profit = starting_amount - 60 + trade.realized_profit
@@ -146,10 +146,12 @@ def test_handle_stoploss_on_exchange(mocker, default_conf_usdt, fee, caplog, is_
'amount': enter_order['amount'],
})
mocker.patch(f'{EXMS}.fetch_stoploss_order', stoploss_order_hit)
freqtrade.strategy.order_filled = MagicMock(return_value=None)
assert freqtrade.handle_stoploss_on_exchange(trade) is True
assert log_has_re(r'STOP_LOSS_LIMIT is hit for Trade\(id=1, .*\)\.', caplog)
assert len(trade.open_sl_orders) == 0
assert trade.is_open is False
assert freqtrade.strategy.order_filled.call_count == 1
caplog.clear()
mocker.patch(f'{EXMS}.create_stoploss', side_effect=ExchangeError())
+6 -3
View File
@@ -698,6 +698,7 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None:
data = history.load_data(datadir=testdatadir, timeframe='5m', pairs=['UNITTEST/BTC'],
timerange=timerange)
processed = backtesting.strategy.advise_all_indicators(data)
backtesting.strategy.order_filled = MagicMock()
min_date, max_date = get_timerange(processed)
result = backtesting.backtest(
@@ -760,6 +761,8 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None:
pd.testing.assert_frame_equal(results, expected)
assert 'orders' in results.columns
data_pair = processed[pair]
# Called once per order
assert backtesting.strategy.order_filled.call_count == 4
for _, t in results.iterrows():
assert len(t['orders']) == 2
ln = data_pair.loc[data_pair["date"] == t["open_date"]]
@@ -1470,7 +1473,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
PropertyMock(return_value=['UNITTEST/BTC']))
mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', backtestmock)
text_table_mock = MagicMock()
sell_reason_mock = MagicMock()
tag_metrics_mock = MagicMock()
strattable_mock = MagicMock()
strat_summary = MagicMock()
@@ -1480,7 +1483,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
)
mocker.patch.multiple('freqtrade.optimize.optimize_reports.optimize_reports',
generate_pair_metrics=MagicMock(),
generate_exit_reason_stats=sell_reason_mock,
generate_tag_metrics=tag_metrics_mock,
generate_strategy_comparison=strat_summary,
generate_daily_stats=MagicMock(),
)
@@ -1505,7 +1508,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
assert backtestmock.call_count == 2
assert text_table_mock.call_count == 4
assert strattable_mock.call_count == 1
assert sell_reason_mock.call_count == 2
assert tag_metrics_mock.call_count == 4
assert strat_summary.call_count == 1
# check the logs, that will contain the backtest result
+29 -28
View File
@@ -15,16 +15,16 @@ from freqtrade.data.btanalysis import (get_latest_backtest_filename, load_backte
from freqtrade.edge import PairInfo
from freqtrade.enums import ExitType
from freqtrade.optimize.optimize_reports import (generate_backtest_stats, generate_daily_stats,
generate_edge_table, generate_exit_reason_stats,
generate_pair_metrics,
generate_edge_table, generate_pair_metrics,
generate_periodic_breakdown_stats,
generate_strategy_comparison,
generate_trading_stats, show_sorted_pairlist,
store_backtest_analysis_results,
store_backtest_stats, text_table_bt_results,
text_table_exit_reason, text_table_strategy)
text_table_strategy)
from freqtrade.optimize.optimize_reports.bt_output import text_table_tags
from freqtrade.optimize.optimize_reports.optimize_reports import (_get_resample_from_period,
calc_streak)
calc_streak, generate_tag_metrics)
from freqtrade.resolvers.strategy_resolver import StrategyResolver
from freqtrade.util import dt_ts
from freqtrade.util.datetime_helpers import dt_from_ts, dt_utc
@@ -59,13 +59,13 @@ def test_text_table_bt_results():
)
result_str = (
'| Pair | Entries | Avg Profit % | Cum Profit % | Tot Profit BTC | '
'| Pair | Entries | Avg Profit % | Tot Profit BTC | '
'Tot Profit % | Avg Duration | Win Draw Loss Win% |\n'
'|---------+-----------+----------------+----------------+------------------+'
'|---------+-----------+----------------+------------------+'
'----------------+----------------+-------------------------|\n'
'| ETH/BTC | 3 | 8.33 | 25.00 | 0.50000000 | '
'| ETH/BTC | 3 | 8.33 | 0.50000000 | '
'12.50 | 0:20:00 | 2 0 1 66.7 |\n'
'| TOTAL | 3 | 8.33 | 25.00 | 0.50000000 | '
'| TOTAL | 3 | 8.33 | 0.50000000 | '
'12.50 | 0:20:00 | 2 0 1 66.7 |'
)
@@ -392,20 +392,21 @@ def test_text_table_exit_reason():
)
result_str = (
'| Exit Reason | Exits | Win Draws Loss Win% | Avg Profit % | Cum Profit % |'
' Tot Profit BTC | Tot Profit % |\n'
'|---------------+---------+--------------------------+----------------+----------------+'
'------------------+----------------|\n'
'| roi | 2 | 2 0 0 100 | 15 | 30 |'
' 0.6 | 15 |\n'
'| stop_loss | 1 | 0 0 1 0 | -10 | -10 |'
' -0.2 | -5 |'
'| Exit Reason | Exits | Avg Profit % | Tot Profit BTC | Tot Profit % |'
' Avg Duration | Win Draw Loss Win% |\n'
'|---------------+---------+----------------+------------------+----------------+'
'----------------+-------------------------|\n'
'| roi | 2 | 15.00 | 0.60000000 | 2.73 |'
' 0:20:00 | 2 0 0 100 |\n'
'| stop_loss | 1 | -10.00 | -0.20000000 | -0.91 |'
' 0:10:00 | 0 0 1 0 |\n'
'| TOTAL | 3 | 6.67 | 0.40000000 | 1.82 |'
' 0:17:00 | 2 0 1 66.7 |'
)
exit_reason_stats = generate_exit_reason_stats(max_open_trades=2,
results=results)
assert text_table_exit_reason(exit_reason_stats=exit_reason_stats,
stake_currency='BTC') == result_str
exit_reason_stats = generate_tag_metrics('exit_reason', starting_balance=22,
results=results, skip_nan=False)
assert text_table_tags('exit_tag', exit_reason_stats, 'BTC') == result_str
def test_generate_sell_reason_stats():
@@ -423,10 +424,10 @@ def test_generate_sell_reason_stats():
}
)
exit_reason_stats = generate_exit_reason_stats(max_open_trades=2,
results=results)
exit_reason_stats = generate_tag_metrics('exit_reason', starting_balance=22,
results=results, skip_nan=False)
roi_result = exit_reason_stats[0]
assert roi_result['exit_reason'] == 'roi'
assert roi_result['key'] == 'roi'
assert roi_result['trades'] == 2
assert pytest.approx(roi_result['profit_mean']) == 0.15
assert roi_result['profit_mean_pct'] == round(roi_result['profit_mean'] * 100, 2)
@@ -435,7 +436,7 @@ def test_generate_sell_reason_stats():
stop_result = exit_reason_stats[1]
assert stop_result['exit_reason'] == 'stop_loss'
assert stop_result['key'] == 'stop_loss'
assert stop_result['trades'] == 1
assert pytest.approx(stop_result['profit_mean']) == -0.1
assert stop_result['profit_mean_pct'] == round(stop_result['profit_mean'] * 100, 2)
@@ -450,13 +451,13 @@ def test_text_table_strategy(testdatadir):
bt_res_data_comparison = bt_res_data.pop('strategy_comparison')
result_str = (
'| Strategy | Entries | Avg Profit % | Cum Profit % | Tot Profit BTC |'
'| Strategy | Entries | Avg Profit % | Tot Profit BTC |'
' Tot Profit % | Avg Duration | Win Draw Loss Win% | Drawdown |\n'
'|----------------+-----------+----------------+----------------+------------------+'
'|----------------+-----------+----------------+------------------+'
'----------------+----------------+-------------------------+-----------------------|\n'
'| StrategyTestV2 | 179 | 0.08 | 14.39 | 0.02608550 |'
'| StrategyTestV2 | 179 | 0.08 | 0.02608550 |'
' 260.85 | 3:40:00 | 170 0 9 95.0 | 0.00308222 BTC 8.67% |\n'
'| TestStrategy | 179 | 0.08 | 14.39 | 0.02608550 |'
'| TestStrategy | 179 | 0.08 | 0.02608550 |'
' 260.85 | 3:40:00 | 170 0 9 95.0 | 0.00308222 BTC 8.67% |'
)
+8
View File
@@ -406,6 +406,14 @@ def test_VolumePairList_refresh_empty(mocker, markets_empty, whitelist_conf):
([{"method": "VolumePairList", "number_assets": 5,
"sort_key": "quoteVolume", "min_value": 1250}],
"BTC", ['ETH/BTC', 'TKN/BTC', 'LTC/BTC']),
# HOT, XRP and FUEL whitelisted because they are below 1300 quoteVolume.
([{"method": "VolumePairList", "number_assets": 5,
"sort_key": "quoteVolume", "max_value": 1300}],
"BTC", ['XRP/BTC', 'HOT/BTC', 'FUEL/BTC']),
# HOT, XRP whitelisted because they are between 100 and 1300 quoteVolume.
([{"method": "VolumePairList", "number_assets": 5,
"sort_key": "quoteVolume", "min_value": 100, "max_value": 1300}],
"BTC", ['XRP/BTC', 'HOT/BTC']),
# StaticPairlist only
([{"method": "StaticPairList"}],
"BTC", ['ETH/BTC', 'TKN/BTC', 'HOT/BTC']),
+65 -65
View File
@@ -11,7 +11,6 @@ from freqtrade.enums import SignalDirection, State, TradingMode
from freqtrade.exceptions import ExchangeError, InvalidOrderException, TemporaryError
from freqtrade.persistence import Order, Trade
from freqtrade.persistence.key_value_store import set_startup_time
from freqtrade.persistence.pairlock_middleware import PairLocks
from freqtrade.rpc import RPC, RPCException
from freqtrade.rpc.fiat_convert import CryptoToFiatConverter
from tests.conftest import (EXMS, create_mock_trades, create_mock_trades_usdt,
@@ -491,12 +490,12 @@ def test_rpc_balance_handle_error(default_conf, mocker):
rpc._rpc_balance(default_conf['stake_currency'], default_conf['fiat_display_currency'])
def test_rpc_balance_handle(default_conf, mocker, tickers):
def test_rpc_balance_handle(default_conf_usdt, mocker, tickers):
mock_balance = {
'BTC': {
'free': 10.0,
'total': 12.0,
'used': 2.0,
'free': 0.01,
'total': 0.012,
'used': 0.002,
},
'ETH': {
'free': 1.0,
@@ -504,8 +503,8 @@ def test_rpc_balance_handle(default_conf, mocker, tickers):
'used': 4.0,
},
'USDT': {
'free': 5.0,
'total': 10.0,
'free': 50.0,
'total': 100.0,
'used': 5.0,
}
}
@@ -519,10 +518,10 @@ def test_rpc_balance_handle(default_conf, mocker, tickers):
"maintenanceMargin": 0.0,
"maintenanceMarginPercentage": 0.005,
"entryPrice": 0.0,
"notional": 100.0,
"notional": 10.0,
"leverage": 5.0,
"unrealizedPnl": 0.0,
"contracts": 100.0,
"contracts": 1.0,
"contractSize": 1,
"marginRatio": None,
"liquidationPrice": 0.0,
@@ -536,9 +535,9 @@ def test_rpc_balance_handle(default_conf, mocker, tickers):
mocker.patch.multiple(
'freqtrade.rpc.fiat_convert.CoinGeckoAPI',
get_price=MagicMock(return_value={'bitcoin': {'usd': 15000.0}}),
get_price=MagicMock(return_value={'bitcoin': {'usd': 1.2}}),
)
mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=1.2)
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
mocker.patch.multiple(
EXMS,
@@ -549,86 +548,86 @@ def test_rpc_balance_handle(default_conf, mocker, tickers):
get_valid_pair_combination=MagicMock(
side_effect=lambda a, b: f"{b}/{a}" if a == "USDT" else f"{a}/{b}")
)
default_conf['dry_run'] = False
default_conf['trading_mode'] = 'futures'
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
default_conf_usdt['dry_run'] = False
default_conf_usdt['trading_mode'] = 'futures'
freqtradebot = get_patched_freqtradebot(mocker, default_conf_usdt)
patch_get_signal(freqtradebot)
rpc = RPC(freqtradebot)
rpc._fiat_converter = CryptoToFiatConverter()
result = rpc._rpc_balance(default_conf['stake_currency'], default_conf['fiat_display_currency'])
assert pytest.approx(result['total']) == 30.30909624
assert pytest.approx(result['value']) == 454636.44360691
result = rpc._rpc_balance(
default_conf_usdt['stake_currency'], default_conf_usdt['fiat_display_currency'])
assert pytest.approx(result['total']) == 2824.83464
assert pytest.approx(result['value']) == 2824.83464 * 1.2
assert tickers.call_count == 1
assert tickers.call_args_list[0][1]['cached'] is True
assert 'USD' == result['symbol']
assert result['currencies'] == [
{
'currency': 'BTC',
'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,
'free': 0.01,
'balance': 0.012,
'used': 0.002,
'bot_owned': 0,
'est_stake': 103.78464,
'est_stake_bot': 0,
'stake': 'USDT',
'side': 'long',
'is_bot_managed': True,
'leverage': 1,
'position': 0,
'is_bot_managed': False,
'is_position': False
},
{
'currency': 'ETH',
'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': 2651.05,
'est_stake_bot': 0,
'used': 5.0,
'stake': 'BTC',
'is_position': False,
'leverage': 1.0,
'position': 0.0,
'stake': 'USDT',
'side': 'long',
'leverage': 1,
'position': 0,
'is_bot_managed': False,
'is_position': False
},
{
'currency': 'USDT',
'free': 50.0,
'balance': 100.0,
'used': 5.0,
'bot_owned': 49.5,
'est_stake': 50.0,
'est_stake_bot': 49.5,
'stake': 'USDT',
'side': 'long',
'leverage': 1,
'position': 0,
'is_bot_managed': True,
'is_position': False
},
{
'free': 0.0,
'balance': 0.0,
'currency': 'ETH/USDT:USDT',
'free': 0,
'balance': 0,
'used': 0,
'position': 10.0,
'est_stake': 20,
'est_stake_bot': 20,
'used': 0,
'stake': 'BTC',
'is_position': True,
'stake': 'USDT',
'leverage': 5.0,
'position': 1000.0,
'side': 'short',
'is_bot_managed': True,
'is_position': 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)
assert pytest.approx(result['total_bot']) == 69.5
assert pytest.approx(result['total']) == 2824.83464 # ETH stake is missing.
assert result['starting_capital'] == 50
assert result['starting_capital_ratio'] == pytest.approx(0.3899999)
def test_rpc_start(mocker, default_conf) -> None:
@@ -1171,14 +1170,15 @@ def test_rpc_force_entry_wrong_mode(mocker, default_conf) -> None:
@pytest.mark.usefixtures("init_persistence")
def test_rpc_delete_lock(mocker, default_conf):
def test_rpc_add_and_delete_lock(mocker, default_conf):
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
rpc = RPC(freqtradebot)
pair = 'ETH/BTC'
PairLocks.lock_pair(pair, datetime.now(timezone.utc) + timedelta(minutes=4))
PairLocks.lock_pair(pair, datetime.now(timezone.utc) + timedelta(minutes=5))
PairLocks.lock_pair(pair, datetime.now(timezone.utc) + timedelta(minutes=10))
rpc._rpc_add_lock(pair, datetime.now(timezone.utc) + timedelta(minutes=4), '', '*')
rpc._rpc_add_lock(pair, datetime.now(timezone.utc) + timedelta(minutes=5), '', '*')
rpc._rpc_add_lock(pair, datetime.now(timezone.utc) + timedelta(minutes=10), '', '*')
locks = rpc._rpc_locks()
assert locks['lock_count'] == 3
locks1 = rpc._rpc_delete_lock(lockid=locks['locks'][0]['id'])
+15 -3
View File
@@ -23,12 +23,13 @@ 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.persistence import 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 freqtrade.util.datetime_helpers import format_date
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)
@@ -553,8 +554,19 @@ def test_api_locks(botclient):
assert rc.json()['lock_count'] == 0
assert rc.json()['lock_count'] == len(rc.json()['locks'])
PairLocks.lock_pair('ETH/BTC', datetime.now(timezone.utc) + timedelta(minutes=4), 'randreason')
PairLocks.lock_pair('XRP/BTC', datetime.now(timezone.utc) + timedelta(minutes=20), 'deadbeef')
rc = client_post(client, f"{BASE_URI}/locks", [
{
"pair": "ETH/BTC",
"until": f"{format_date(datetime.now(timezone.utc) + timedelta(minutes=4))}Z",
"reason": "randreason"
}, {
"pair": "XRP/BTC",
"until": f"{format_date(datetime.now(timezone.utc) + timedelta(minutes=20))}Z",
"reason": "deadbeef"
}
])
assert_response(rc)
assert rc.json()['lock_count'] == 2
rc = client_get(client, f"{BASE_URI}/locks")
assert_response(rc)
-3
View File
@@ -795,9 +795,6 @@ def test_strategy_safe_wrapper_error(caplog, error):
def failing_method():
raise error('This is an error.')
def working_method(argumentpassedin):
return argumentpassedin
with pytest.raises(StrategyError, match=r'This is an error.'):
strategy_safe_wrapper(failing_method, message='DeadBeef')()
+17 -2
View File
@@ -10,6 +10,7 @@ from jsonschema import ValidationError
from freqtrade.commands import Arguments
from freqtrade.configuration import Configuration, validate_config_consistency
from freqtrade.configuration.config_secrets import sanitize_config
from freqtrade.configuration.config_validation import validate_config_schema
from freqtrade.configuration.deprecated_settings import (check_conflicting_settings,
process_deprecated_setting,
@@ -1440,7 +1441,7 @@ def test_flat_vars_to_nested_dict(caplog):
assert not log_has("Loading variable 'NOT_RELEVANT'", caplog)
def test_setup_hyperopt_freqai(mocker, default_conf, caplog) -> None:
def test_setup_hyperopt_freqai(mocker, default_conf) -> None:
patched_configuration_load_config_file(mocker, default_conf)
mocker.patch(
'freqtrade.configuration.configuration.create_datadir',
@@ -1473,7 +1474,7 @@ def test_setup_hyperopt_freqai(mocker, default_conf, caplog) -> None:
validate_config_consistency(config)
def test_setup_freqai_backtesting(mocker, default_conf, caplog) -> None:
def test_setup_freqai_backtesting(mocker, default_conf) -> None:
patched_configuration_load_config_file(mocker, default_conf)
mocker.patch(
'freqtrade.configuration.configuration.create_datadir',
@@ -1520,3 +1521,17 @@ def test_setup_freqai_backtesting(mocker, default_conf, caplog) -> None:
OperationalException, match=r".* pass --timerange if you intend to use FreqAI .*"
):
validate_config_consistency(conf)
def test_sanitize_config(default_conf_usdt):
assert default_conf_usdt['exchange']['key'] != 'REDACTED'
res = sanitize_config(default_conf_usdt)
# Didn't modify original dict
assert default_conf_usdt['exchange']['key'] != 'REDACTED'
assert res['exchange']['key'] == 'REDACTED'
assert res['exchange']['secret'] == 'REDACTED'
res = sanitize_config(default_conf_usdt, show_sensitive=True)
assert res['exchange']['key'] == default_conf_usdt['exchange']['key']
assert res['exchange']['secret'] == default_conf_usdt['exchange']['secret']
+17 -1
View File
@@ -8,7 +8,7 @@ import pytest
from freqtrade.commands import Arguments
from freqtrade.enums import State
from freqtrade.exceptions import FreqtradeException, OperationalException
from freqtrade.exceptions import ConfigurationError, FreqtradeException, OperationalException
from freqtrade.freqtradebot import FreqtradeBot
from freqtrade.main import main
from freqtrade.worker import Worker
@@ -141,6 +141,22 @@ def test_main_operational_exception1(mocker, default_conf, caplog) -> None:
assert log_has_re(r'SIGINT.*', caplog)
def test_main_ConfigurationError(mocker, default_conf, caplog) -> None:
patch_exchange(mocker)
mocker.patch(
'freqtrade.commands.list_commands.list_available_exchanges',
MagicMock(side_effect=ConfigurationError('Oh snap!'))
)
patched_configuration_load_config_file(mocker, default_conf)
args = ['list-exchanges']
# Test Main + the KeyboardInterrupt exception
with pytest.raises(SystemExit):
main(args)
assert log_has_re('Configuration error: Oh snap!', caplog)
def test_main_reload_config(mocker, default_conf, caplog) -> None:
patch_exchange(mocker)
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.cleanup', MagicMock())
+1 -1
View File
@@ -1,6 +1,6 @@
{
"stake_currency": "",
"dry_run": true,
"dry_run": false,
"exchange": {
"name": "",
"key": "",