diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index e2fd30575..4dba9ad4a 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -523,7 +523,7 @@ class AwesomeStrategy(IStrategy): ### Stake size management -It is possible to manage your risk by reducing or increasing or reducing stake amount when placing a new trade. +It is possible to manage your risk by reducing or increasing stake amount when placing a new trade. ```python class AwesomeStrategy(IStrategy): @@ -546,8 +546,10 @@ class AwesomeStrategy(IStrategy): return proposed_stake ``` +Freqtrade will fall back to the `proposed_stake` value should your code raise an exception. The exception itself will be logged. + !!! Tip - You do not _have_ to ensure that `min_stake <= returned_value <= max_stake`. Trades will succeed, as returned value will be clamped to supported range and this acton will be logged. + You do not _have_ to ensure that `min_stake <= returned_value <= max_stake`. Trades will succeed as the returned value will be clamped to supported range and this acton will be logged. !!! Tip Returning `0` or `None` will prevent trades from being placed. diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index efbf15fca..5ef109387 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -486,7 +486,7 @@ class FreqtradeBot(LoggingMixin): if not self.edge: max_stake_amount = self.wallets.get_available_stake_amount() stake_amount = strategy_safe_wrapper(self.strategy.custom_stake_amount, - default_retval=None)( + default_retval=stake_amount)( pair=pair, current_time=datetime.now(timezone.utc), current_rate=buy_limit_requested, proposed_stake=stake_amount, min_stake=min_stake_amount, max_stake=max_stake_amount) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 885e67e0b..83cd8c2bb 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -319,7 +319,7 @@ class Backtesting: max_stake_amount = self.wallets.get_available_stake_amount() stake_amount = strategy_safe_wrapper(self.strategy.custom_stake_amount, - default_retval=None)( + default_retval=stake_amount)( pair=pair, current_time=row[DATE_IDX].to_pydatetime(), current_rate=row[OPEN_IDX], proposed_stake=stake_amount, min_stake=min_stake_amount, max_stake=max_stake_amount) stake_amount = self.wallets._validate_stake_amount(pair, stake_amount, min_stake_amount) diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 30d86f979..a1881c306 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -496,6 +496,17 @@ def test_backtest__enter_trade(default_conf, fee, mocker) -> None: trade = backtesting._enter_trade(pair, row=row) assert trade is not None + backtesting.strategy.custom_stake_amount = lambda **kwargs: 123.5 + trade = backtesting._enter_trade(pair, row=row) + assert trade + assert trade.stake_amount == 123.5 + + # In case of error - use proposed stake + backtesting.strategy.custom_stake_amount = lambda **kwargs: 20 / 0 + trade = backtesting._enter_trade(pair, row=row) + assert trade + assert trade.stake_amount == 495 + # Stake-amount too high! mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=600.0) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 117589b12..addf72bbb 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -397,7 +397,7 @@ def test_create_trade_minimal_amount(default_conf, ticker, limit_buy_order_open, def test_create_trade_too_small_stake_amount(default_conf, ticker, limit_buy_order_open, - fee, mocker) -> None: + fee, mocker, caplog) -> None: patch_RPCManager(mocker) patch_exchange(mocker) buy_mock = MagicMock(return_value=limit_buy_order_open) @@ -414,6 +414,7 @@ def test_create_trade_too_small_stake_amount(default_conf, ticker, limit_buy_ord patch_get_signal(freqtrade) assert freqtrade.create_trade('ETH/BTC') + assert log_has_re(r"Stake amount for pair .* is too small.*", caplog) def test_create_trade_zero_stake_amount(default_conf, ticker, limit_buy_order_open, @@ -862,6 +863,24 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order assert trade.open_rate == 0.5 assert trade.stake_amount == 40.495905365 + # Test with custom stake + limit_buy_order['status'] = 'open' + limit_buy_order['id'] = '556' + + freqtrade.strategy.custom_stake_amount = lambda **kwargs: 150.0 + assert freqtrade.execute_buy(pair, stake_amount) + trade = Trade.query.all()[4] + assert trade + assert trade.stake_amount == 150 + + # Exception case + limit_buy_order['id'] = '557' + freqtrade.strategy.custom_stake_amount = lambda **kwargs: 20 / 0 + assert freqtrade.execute_buy(pair, stake_amount) + trade = Trade.query.all()[5] + assert trade + assert trade.stake_amount == 2.0 + # In case of the order is rejected and not filled at all limit_buy_order['status'] = 'rejected' limit_buy_order['amount'] = 90.99181073