Merge pull request #6656 from freqtrade/use_sell_signal

Use sell signal -> use_exit_signal
This commit is contained in:
Matthias 2022-04-06 19:46:46 +02:00 committed by GitHub
commit 299dd84cfe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 216 additions and 156 deletions

View File

@ -15,10 +15,10 @@
"trailing_stop_positive": 0.005,
"trailing_stop_positive_offset": 0.0051,
"trailing_only_offset_is_reached": false,
"use_sell_signal": true,
"sell_profit_only": false,
"sell_profit_offset": 0.0,
"ignore_roi_if_buy_signal": false,
"use_exit_signal": true,
"exit_profit_only": false,
"exit_profit_offset": 0.0,
"ignore_roi_if_entry_signal": false,
"ignore_buying_expired_candle_after": 300,
"trading_mode": "spot",
"margin_mode": "",

View File

@ -116,10 +116,10 @@ Mandatory parameters are marked as **Required**, which means that they are requi
| `exit_pricing.price_last_balance` | Interpolate the exiting price. More information [below](#exit-price-without-orderbook-enabled).
| `exit_pricing.use_order_book` | Enable exiting of open trades using [Order Book Exit](#exit-price-with-orderbook-enabled). <br> *Defaults to `True`.*<br> **Datatype:** Boolean
| `exit_pricing.order_book_top` | Bot will use the top N rate in Order Book "price_side" to sell. I.e. a value of 2 will allow the bot to pick the 2nd ask rate in [Order Book Exit](#exit-price-with-orderbook-enabled)<br>*Defaults to `1`.* <br> **Datatype:** Positive Integer
| `use_sell_signal` | Use sell signals produced by the strategy in addition to the `minimal_roi`. [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `true`.* <br> **Datatype:** Boolean
| `sell_profit_only` | Wait until the bot reaches `sell_profit_offset` before taking a sell decision. [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `false`.* <br> **Datatype:** Boolean
| `sell_profit_offset` | Sell-signal is only active above this value. Only active in combination with `sell_profit_only=True`. [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `0.0`.* <br> **Datatype:** Float (as ratio)
| `ignore_roi_if_buy_signal` | Do not sell if the buy signal is still active. This setting takes preference over `minimal_roi` and `use_sell_signal`. [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `false`.* <br> **Datatype:** Boolean
| `use_exit_signal` | Use sell signals produced by the strategy in addition to the `minimal_roi`. [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `true`.* <br> **Datatype:** Boolean
| `exit_profit_only` | Wait until the bot reaches `exit_profit_offset` before taking an exit decision. [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `false`.* <br> **Datatype:** Boolean
| `exit_profit_offset` | Sell-signal is only active above this value. Only active in combination with `exit_profit_only=True`. [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `0.0`.* <br> **Datatype:** Float (as ratio)
| `ignore_roi_if_entry_signal` | Do not sell if the buy signal is still active. This setting takes preference over `minimal_roi` and `use_exit_signal`. [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `false`.* <br> **Datatype:** Boolean
| `ignore_buying_expired_candle_after` | Specifies the number of seconds until a buy signal is no longer used. <br> **Datatype:** Integer
| `order_types` | Configure order-types depending on the action (`"entry"`, `"exit"`, `"stoploss"`, `"stoploss_on_exchange"`). [More information below](#understand-order_types). [Strategy Override](#parameters-in-the-strategy).<br> **Datatype:** Dict
| `order_time_in_force` | Configure time in force for entry and exit orders. [More information below](#understand-order_time_in_force). [Strategy Override](#parameters-in-the-strategy). <br> **Datatype:** Dict
@ -198,10 +198,10 @@ Values set in the configuration file always overwrite values set in the strategy
* `order_time_in_force`
* `unfilledtimeout`
* `disable_dataframe_checks`
* `use_sell_signal`
* `sell_profit_only`
* `sell_profit_offset`
* `ignore_roi_if_buy_signal`
- `use_exit_signal`
* `exit_profit_only`
- `exit_profit_offset`
- `ignore_roi_if_entry_signal`
* `ignore_buying_expired_candle_after`
* `position_adjustment_enable`
* `max_entry_position_adjustment`

View File

@ -91,7 +91,7 @@ For example you could implement a 1:2 risk-reward ROI with `custom_exit()`.
Using custom_exit() signals in place of stoploss though *is not recommended*. It is a inferior method to using `custom_stoploss()` in this regard - which also allows you to keep the stoploss on exchange.
!!! Note
Returning a (none-empty) `string` or `True` from this method is equal to setting sell signal on a candle at specified time. This method is not called when sell signal is set already, or if sell signals are disabled (`use_sell_signal=False` or `sell_profit_only=True` while profit is below `sell_profit_offset`). `string` max length is 64 characters. Exceeding this limit will cause the message to be truncated to 64 characters.
Returning a (none-empty) `string` or `True` from this method is equal to setting sell signal on a candle at specified time. This method is not called when sell signal is set already, or if sell signals are disabled (`use_exit_signal=False` or `exit_profit_only=True` while profit is below `exit_profit_offset`). `string` max length is 64 characters. Exceeding this limit will cause the message to be truncated to 64 characters.
An example of how we can use different indicators depending on the current profit and also sell trades that were open longer than one day:

View File

@ -264,7 +264,7 @@ def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFram
### Exit signal rules
Edit the method `populate_exit_trend()` into your strategy file to update your sell strategy.
Please note that the sell-signal is only used if `use_sell_signal` is set to true in the configuration.
Please note that the exit-signal is only used if `use_exit_signal` is set to true in the configuration.
It's important to always return the dataframe without removing/modifying the columns `"open", "high", "low", "close", "volume"`, otherwise these fields would contain something unexpected.

View File

@ -61,8 +61,11 @@ You can use the quick summary as checklist. Please refer to the detailed section
* `sell` -> `exit`
* `sell_fill` -> `exit_fill`
* `sell_cancel` -> `exit_cancel`
* Strategy/config settings:
* `use_sell_signal` -> `use_exit_signal`
* `sell_profit_only` -> `exit_profit_only`
* `sell_profit_offset` -> `exit_profit_offset`
* `ignore_roi_if_buy_signal` -> `ignore_roi_if_entry_signal`
## Extensive explanation
@ -360,6 +363,31 @@ After:
}
```
#### Strategy level settings
* `use_sell_signal` -> `use_exit_signal`
* `sell_profit_only` -> `exit_profit_only`
* `sell_profit_offset` -> `exit_profit_offset`
* `ignore_roi_if_buy_signal` -> `ignore_roi_if_entry_signal`
``` python hl_lines="2-5"
# These values can be overridden in the config.
use_sell_signal = True
sell_profit_only = True
sell_profit_offset: 0.01
ignore_roi_if_buy_signal = False
```
After:
``` python hl_lines="2-5"
# These values can be overridden in the config.
use_exit_signal = True
exit_profit_only = True
exit_profit_offset: 0.01
ignore_roi_if_entry_signal = False
```
#### `unfilledtimeout`
`unfilledtimeout` have changed all wordings from `buy` to `entry` - and `sell` to `exit`.

View File

@ -154,9 +154,9 @@ def _validate_edge(conf: Dict[str, Any]) -> None:
if not conf.get('edge', {}).get('enabled'):
return
if not conf.get('use_sell_signal', True):
if not conf.get('use_exit_signal', True):
raise OperationalException(
"Edge requires `use_sell_signal` to be True, otherwise no sells will happen."
"Edge requires `use_exit_signal` to be True, otherwise no sells will happen."
)
@ -219,6 +219,7 @@ def validate_migrated_strategy_settings(conf: Dict[str, Any]) -> None:
_validate_order_types(conf)
_validate_unfilledtimeout(conf)
_validate_pricing_rules(conf)
_strategy_settings(conf)
def _validate_time_in_force(conf: Dict[str, Any]) -> None:
@ -312,3 +313,12 @@ def _validate_pricing_rules(conf: Dict[str, Any]) -> None:
else:
process_deprecated_setting(conf, 'ask_strategy', obj, 'exit_pricing', obj)
del conf['ask_strategy']
def _strategy_settings(conf: Dict[str, Any]) -> None:
process_deprecated_setting(conf, None, 'use_sell_signal', None, 'use_exit_signal')
process_deprecated_setting(conf, None, 'sell_profit_only', None, 'exit_profit_only')
process_deprecated_setting(conf, None, 'sell_profit_offset', None, 'exit_profit_offset')
process_deprecated_setting(conf, None, 'ignore_roi_if_buy_signal',
None, 'ignore_roi_if_entry_signal')

View File

@ -12,14 +12,15 @@ logger = logging.getLogger(__name__)
def check_conflicting_settings(config: Dict[str, Any],
section_old: str, name_old: str,
section_old: Optional[str], name_old: str,
section_new: Optional[str], name_new: str) -> None:
section_new_config = config.get(section_new, {}) if section_new else config
section_old_config = config.get(section_old, {})
section_old_config = config.get(section_old, {}) if section_old else config
if name_new in section_new_config and name_old in section_old_config:
new_name = f"{section_new}.{name_new}" if section_new else f"{name_new}"
old_name = f"{section_old}.{name_old}" if section_old else f"{name_old}"
raise OperationalException(
f"Conflicting settings `{new_name}` and `{section_old}.{name_old}` "
f"Conflicting settings `{new_name}` and `{old_name}` "
"(DEPRECATED) detected in the configuration file. "
"This deprecated setting will be removed in the next versions of Freqtrade. "
f"Please delete it from your configuration and use the `{new_name}` "
@ -47,11 +48,11 @@ def process_removed_setting(config: Dict[str, Any],
def process_deprecated_setting(config: Dict[str, Any],
section_old: str, name_old: str,
section_old: Optional[str], name_old: str,
section_new: Optional[str], name_new: str
) -> None:
check_conflicting_settings(config, section_old, name_old, section_new, name_new)
section_old_config = config.get(section_old, {})
section_old_config = config.get(section_old, {}) if section_old else config
if name_old in section_old_config:
section_2 = f"{section_new}.{name_new}" if section_new else f"{name_new}"
@ -72,14 +73,7 @@ def process_temporary_deprecated_settings(config: Dict[str, Any]) -> None:
# Kept for future deprecated / moved settings
# check_conflicting_settings(config, 'ask_strategy', 'use_sell_signal',
# 'experimental', 'use_sell_signal')
process_deprecated_setting(config, 'ask_strategy', 'use_sell_signal',
None, 'use_sell_signal')
process_deprecated_setting(config, 'ask_strategy', 'sell_profit_only',
None, 'sell_profit_only')
process_deprecated_setting(config, 'ask_strategy', 'sell_profit_offset',
None, 'sell_profit_offset')
process_deprecated_setting(config, 'ask_strategy', 'ignore_roi_if_buy_signal',
None, 'ignore_roi_if_buy_signal')
process_deprecated_setting(config, 'ask_strategy', 'ignore_buying_expired_candle_after',
None, 'ignore_buying_expired_candle_after')
# New settings
@ -109,13 +103,18 @@ def process_temporary_deprecated_settings(config: Dict[str, Any]) -> None:
'webhook', 'webhookexitfill')
# Legacy way - having them in experimental ...
process_removed_setting(config, 'experimental', 'use_sell_signal',
None, 'use_sell_signal')
process_removed_setting(config, 'experimental', 'sell_profit_only',
None, 'sell_profit_only')
process_removed_setting(config, 'experimental', 'ignore_roi_if_buy_signal',
None, 'ignore_roi_if_buy_signal')
process_removed_setting(config, 'experimental', 'use_sell_signal', None, 'use_exit_signal')
process_removed_setting(config, 'experimental', 'sell_profit_only', None, 'exit_profit_only')
process_removed_setting(config, 'experimental', 'ignore_roi_if_buy_signal',
None, 'ignore_roi_if_entry_signal')
process_removed_setting(config, 'ask_strategy', 'use_sell_signal', None, 'exit_sell_signal')
process_removed_setting(config, 'ask_strategy', 'sell_profit_only', None, 'exit_profit_only')
process_removed_setting(config, 'ask_strategy', 'sell_profit_offset',
None, 'exit_profit_offset')
process_removed_setting(config, 'ask_strategy', 'ignore_roi_if_buy_signal',
None, 'ignore_roi_if_entry_signal')
if (config.get('edge', {}).get('enabled', False)
and 'capital_available_percentage' in config.get('edge', {})):
raise OperationalException(

View File

@ -149,10 +149,10 @@ CONF_SCHEMA = {
'trailing_stop_positive': {'type': 'number', 'minimum': 0, 'maximum': 1},
'trailing_stop_positive_offset': {'type': 'number', 'minimum': 0, 'maximum': 1},
'trailing_only_offset_is_reached': {'type': 'boolean'},
'use_sell_signal': {'type': 'boolean'},
'sell_profit_only': {'type': 'boolean'},
'sell_profit_offset': {'type': 'number'},
'ignore_roi_if_buy_signal': {'type': 'boolean'},
'use_exit_signal': {'type': 'boolean'},
'exit_profit_only': {'type': 'boolean'},
'exit_profit_offset': {'type': 'number'},
'ignore_roi_if_entry_signal': {'type': 'boolean'},
'ignore_buying_expired_candle_after': {'type': 'number'},
'trading_mode': {'type': 'string', 'enum': TRADING_MODES},
'margin_mode': {'type': 'string', 'enum': MARGIN_MODES},

View File

@ -926,8 +926,8 @@ class FreqtradeBot(LoggingMixin):
exit_tag = None
exit_signal_type = "exit_short" if trade.is_short else "exit_long"
if (self.config.get('use_sell_signal', True) or
self.config.get('ignore_roi_if_buy_signal', False)):
if (self.config.get('use_exit_signal', True) or
self.config.get('ignore_roi_if_entry_signal', False)):
analyzed_df, _ = self.dataprovider.get_analyzed_dataframe(trade.pair,
self.strategy.timeframe)

View File

@ -114,8 +114,8 @@ class Hyperopt:
self.position_stacking = self.config.get('position_stacking', False)
if HyperoptTools.has_space(self.config, 'sell'):
# Make sure use_sell_signal is enabled
self.config['use_sell_signal'] = True
# Make sure use_exit_signal is enabled
self.config['use_exit_signal'] = True
self.print_all = self.config.get('print_all', False)
self.hyperopt_table_header = 0

View File

@ -460,10 +460,10 @@ def generate_strategy_stats(pairlist: List[str],
'trailing_only_offset_is_reached': config.get('trailing_only_offset_is_reached', False),
'use_custom_stoploss': config.get('use_custom_stoploss', False),
'minimal_roi': config['minimal_roi'],
'use_sell_signal': config['use_sell_signal'],
'sell_profit_only': config['sell_profit_only'],
'sell_profit_offset': config['sell_profit_offset'],
'ignore_roi_if_buy_signal': config['ignore_roi_if_buy_signal'],
'use_exit_signal': config['use_exit_signal'],
'exit_profit_only': config['exit_profit_only'],
'exit_profit_offset': config['exit_profit_offset'],
'ignore_roi_if_entry_signal': config['ignore_roi_if_entry_signal'],
**daily_stats,
**trade_stats
}

View File

@ -85,10 +85,10 @@ class StrategyResolver(IResolver):
("protections", None),
("startup_candle_count", None),
("unfilledtimeout", None),
("use_sell_signal", True),
("sell_profit_only", False),
("ignore_roi_if_buy_signal", False),
("sell_profit_offset", 0.0),
("use_exit_signal", True),
("exit_profit_only", False),
("ignore_roi_if_entry_signal", False),
("exit_profit_offset", 0.0),
("disable_dataframe_checks", False),
("ignore_buying_expired_candle_after", 0),
("position_adjustment_enable", False),
@ -173,6 +173,12 @@ class StrategyResolver(IResolver):
def validate_strategy(strategy: IStrategy) -> IStrategy:
if strategy.config.get('trading_mode', TradingMode.SPOT) != TradingMode.SPOT:
# Require new method
warn_deprecated_setting(strategy, 'sell_profit_only', 'exit_profit_only', True)
warn_deprecated_setting(strategy, 'sell_profit_offset', 'exit_profit_offset', True)
warn_deprecated_setting(strategy, 'use_sell_signal', 'use_exit_signal', True)
warn_deprecated_setting(strategy, 'ignore_roi_if_buy_signal',
'ignore_roi_if_entry_signal', True)
if not check_override(strategy, IStrategy, 'populate_entry_trend'):
raise OperationalException("`populate_entry_trend` must be implemented.")
if not check_override(strategy, IStrategy, 'populate_exit_trend'):
@ -187,9 +193,16 @@ class StrategyResolver(IResolver):
if check_override(strategy, IStrategy, 'custom_sell'):
raise OperationalException(
"Please migrate your implementation of `custom_sell` to `custom_exit`.")
else:
# TODO: Implementing one of the following methods should show a deprecation warning
# buy_trend and sell_trend, custom_sell
warn_deprecated_setting(strategy, 'sell_profit_only', 'exit_profit_only')
warn_deprecated_setting(strategy, 'sell_profit_offset', 'exit_profit_offset')
warn_deprecated_setting(strategy, 'use_sell_signal', 'use_exit_signal')
warn_deprecated_setting(strategy, 'ignore_roi_if_buy_signal',
'ignore_roi_if_entry_signal')
if (
not check_override(strategy, IStrategy, 'populate_buy_trend')
and not check_override(strategy, IStrategy, 'populate_entry_trend')
@ -262,6 +275,15 @@ class StrategyResolver(IResolver):
)
def warn_deprecated_setting(strategy: IStrategy, old: str, new: str, error=False):
if hasattr(strategy, old):
errormsg = f"DEPRECATED: Using '{old}' moved to '{new}'."
if error:
raise OperationalException(errormsg)
logger.warning(errormsg)
setattr(strategy, new, getattr(strategy, f'{old}'))
def check_override(object, parentclass, attribute):
"""
Checks if a object overrides the parent class attribute.

View File

@ -90,10 +90,10 @@ class IStrategy(ABC, HyperStrategyMixin):
# run "populate_indicators" only for new candle
process_only_new_candles: bool = False
use_sell_signal: bool
sell_profit_only: bool
sell_profit_offset: float
ignore_roi_if_buy_signal: bool
use_exit_signal: bool
exit_profit_only: bool
exit_profit_offset: float
ignore_roi_if_entry_signal: bool
# Position adjustment is disabled by default
position_adjustment_enable: bool = False
@ -871,7 +871,7 @@ class IStrategy(ABC, HyperStrategyMixin):
current_profit = trade.calc_profit_ratio(current_rate)
# if enter signal and ignore_roi is set, we don't need to evaluate min_roi.
roi_reached = (not (enter and self.ignore_roi_if_buy_signal)
roi_reached = (not (enter and self.ignore_roi_if_entry_signal)
and self.min_roi_reached(trade=trade, current_profit=current_profit,
current_time=current_time))
@ -881,10 +881,10 @@ class IStrategy(ABC, HyperStrategyMixin):
current_rate = rate
current_profit = trade.calc_profit_ratio(current_rate)
if (self.sell_profit_only and current_profit <= self.sell_profit_offset):
# sell_profit_only and profit doesn't reach the offset - ignore sell signal
if (self.exit_profit_only and current_profit <= self.exit_profit_offset):
# exit_profit_only and profit doesn't reach the offset - ignore sell signal
pass
elif self.use_sell_signal and not enter:
elif self.use_exit_signal and not enter:
if exit_:
exit_signal = ExitType.EXIT_SIGNAL
else:

View File

@ -65,9 +65,9 @@ class {{ strategy }}(IStrategy):
process_only_new_candles = False
# These values can be overridden in the config.
use_sell_signal = True
sell_profit_only = False
ignore_roi_if_buy_signal = False
use_exit_signal = True
exit_profit_only = False
ignore_roi_if_entry_signal = False
# Number of candles the strategy requires before producing valid signals
startup_candle_count: int = 30

View File

@ -65,9 +65,9 @@ class SampleStrategy(IStrategy):
process_only_new_candles = False
# These values can be overridden in the config.
use_sell_signal = True
sell_profit_only = False
ignore_roi_if_buy_signal = False
use_exit_signal = True
exit_profit_only = False
ignore_roi_if_entry_signal = False
# Hyperoptable parameters
buy_rsi = IntParameter(low=1, high=50, default=30, space='buy', optimize=True, load=True)

View File

@ -821,7 +821,7 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data: BTContainer)
if data.trailing_stop_positive is not None:
default_conf["trailing_stop_positive"] = data.trailing_stop_positive
default_conf["trailing_stop_positive_offset"] = data.trailing_stop_positive_offset
default_conf["use_sell_signal"] = data.use_exit_signal
default_conf["use_exit_signal"] = data.use_exit_signal
mocker.patch("freqtrade.exchange.Exchange.get_fee", return_value=0.0)
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)

View File

@ -504,7 +504,7 @@ def test_backtesting_pairlist_list(default_conf, mocker, caplog, testdatadir, ti
def test_backtest__enter_trade(default_conf, fee, mocker) -> None:
default_conf['use_sell_signal'] = False
default_conf['use_exit_signal'] = False
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf'))
@ -563,7 +563,7 @@ def test_backtest__enter_trade(default_conf, fee, mocker) -> None:
def test_backtest__enter_trade_futures(default_conf_usdt, fee, mocker) -> None:
default_conf_usdt['use_sell_signal'] = False
default_conf_usdt['use_exit_signal'] = False
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf'))
@ -645,7 +645,7 @@ def test_backtest__enter_trade_futures(default_conf_usdt, fee, mocker) -> None:
def test_backtest__get_sell_trade_entry(default_conf, fee, mocker) -> None:
default_conf['use_sell_signal'] = False
default_conf['use_exit_signal'] = False
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf'))
@ -740,7 +740,7 @@ def test_backtest__get_sell_trade_entry(default_conf, fee, mocker) -> None:
def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None:
default_conf['use_sell_signal'] = False
default_conf['use_exit_signal'] = False
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf'))
@ -807,7 +807,7 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None:
def test_backtest_1min_timeframe(default_conf, fee, mocker, testdatadir) -> None:
default_conf['use_sell_signal'] = False
default_conf['use_exit_signal'] = False
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf'))
@ -833,7 +833,7 @@ def test_backtest_1min_timeframe(default_conf, fee, mocker, testdatadir) -> None
def test_backtest_trim_no_data_left(default_conf, fee, mocker, testdatadir) -> None:
default_conf['use_sell_signal'] = False
default_conf['use_exit_signal'] = False
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf'))
@ -878,7 +878,7 @@ def test_processed(default_conf, mocker, testdatadir) -> None:
def test_backtest_dataprovider_analyzed_df(default_conf, fee, mocker, testdatadir) -> None:
default_conf['use_sell_signal'] = False
default_conf['use_exit_signal'] = False
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=100000)
@ -1151,10 +1151,10 @@ def test_backtest_start_timerange(default_conf, mocker, caplog, testdatadir):
def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
default_conf.update({
"use_sell_signal": True,
"sell_profit_only": False,
"sell_profit_offset": 0.0,
"ignore_roi_if_buy_signal": False,
"use_exit_signal": True,
"exit_profit_only": False,
"exit_profit_offset": 0.0,
"ignore_roi_if_entry_signal": False,
})
patch_exchange(mocker)
backtestmock = MagicMock(return_value={
@ -1228,10 +1228,10 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
@pytest.mark.filterwarnings("ignore:deprecated")
def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdatadir, capsys):
default_conf.update({
"use_sell_signal": True,
"sell_profit_only": False,
"sell_profit_offset": 0.0,
"ignore_roi_if_buy_signal": False,
"use_exit_signal": True,
"exit_profit_only": False,
"exit_profit_offset": 0.0,
"ignore_roi_if_entry_signal": False,
})
patch_exchange(mocker)
result1 = pd.DataFrame({'pair': ['XRP/BTC', 'LTC/BTC'],
@ -1346,10 +1346,10 @@ def test_backtest_start_nomock_futures(default_conf_usdt, mocker,
default_conf_usdt.update({
"trading_mode": "futures",
"margin_mode": "isolated",
"use_sell_signal": True,
"sell_profit_only": False,
"sell_profit_offset": 0.0,
"ignore_roi_if_buy_signal": False,
"use_exit_signal": True,
"exit_profit_only": False,
"exit_profit_offset": 0.0,
"ignore_roi_if_entry_signal": False,
"strategy": CURRENT_TEST_STRATEGY,
})
patch_exchange(mocker)
@ -1450,10 +1450,10 @@ def test_backtest_start_multi_strat_nomock_detail(default_conf, mocker,
caplog, testdatadir, capsys):
# Tests detail-data loading
default_conf.update({
"use_sell_signal": True,
"sell_profit_only": False,
"sell_profit_offset": 0.0,
"ignore_roi_if_buy_signal": False,
"use_exit_signal": True,
"exit_profit_only": False,
"exit_profit_offset": 0.0,
"ignore_roi_if_entry_signal": False,
})
patch_exchange(mocker)
result1 = pd.DataFrame({'pair': ['XRP/BTC', 'LTC/BTC'],
@ -1557,10 +1557,10 @@ def test_backtest_start_multi_strat_nomock_detail(default_conf, mocker,
def test_backtest_start_multi_strat_caching(default_conf, mocker, caplog, testdatadir, run_id,
start_delta, cache):
default_conf.update({
"use_sell_signal": True,
"sell_profit_only": False,
"sell_profit_offset": 0.0,
"ignore_roi_if_buy_signal": False,
"use_exit_signal": True,
"exit_profit_only": False,
"exit_profit_offset": 0.0,
"ignore_roi_if_entry_signal": False,
})
patch_exchange(mocker)
backtestmock = MagicMock(return_value={

View File

@ -14,7 +14,7 @@ from tests.conftest import patch_exchange
def test_backtest_position_adjustment(default_conf, fee, mocker, testdatadir) -> None:
default_conf['use_sell_signal'] = False
default_conf['use_exit_signal'] = False
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf'))

View File

@ -50,6 +50,8 @@ class StrategyTestV2(IStrategy):
'entry': 'gtc',
'exit': 'gtc',
}
# Test legacy use_sell_signal definition
use_sell_signal = False
# By default this strategy does not use Position Adjustments
position_adjustment_enable = False

View File

@ -143,16 +143,6 @@ def test_strategy_can_short(caplog, default_conf):
assert isinstance(strat, IStrategy)
def test_strategy_implements_populate_entry(caplog, default_conf):
caplog.set_level(logging.INFO)
default_conf.update({
'strategy': "StrategyTestV2",
})
default_conf['trading_mode'] = 'futures'
with pytest.raises(OperationalException, match="`populate_entry_trend` must be implemented."):
StrategyResolver.load_strategy(default_conf)
def test_strategy_override_minimal_roi(caplog, default_conf):
caplog.set_level(logging.INFO)
default_conf.update({
@ -310,50 +300,50 @@ def test_strategy_override_order_tif(caplog, default_conf):
StrategyResolver.load_strategy(default_conf)
def test_strategy_override_use_sell_signal(caplog, default_conf):
def test_strategy_override_use_exit_signal(caplog, default_conf):
caplog.set_level(logging.INFO)
default_conf.update({
'strategy': CURRENT_TEST_STRATEGY,
})
strategy = StrategyResolver.load_strategy(default_conf)
assert strategy.use_sell_signal
assert isinstance(strategy.use_sell_signal, bool)
assert strategy.use_exit_signal
assert isinstance(strategy.use_exit_signal, bool)
# must be inserted to configuration
assert 'use_sell_signal' in default_conf
assert default_conf['use_sell_signal']
assert 'use_exit_signal' in default_conf
assert default_conf['use_exit_signal']
default_conf.update({
'strategy': CURRENT_TEST_STRATEGY,
'use_sell_signal': False,
'use_exit_signal': False,
})
strategy = StrategyResolver.load_strategy(default_conf)
assert not strategy.use_sell_signal
assert isinstance(strategy.use_sell_signal, bool)
assert log_has("Override strategy 'use_sell_signal' with value in config file: False.", caplog)
assert not strategy.use_exit_signal
assert isinstance(strategy.use_exit_signal, bool)
assert log_has("Override strategy 'use_exit_signal' with value in config file: False.", caplog)
def test_strategy_override_use_sell_profit_only(caplog, default_conf):
def test_strategy_override_use_exit_profit_only(caplog, default_conf):
caplog.set_level(logging.INFO)
default_conf.update({
'strategy': CURRENT_TEST_STRATEGY,
})
strategy = StrategyResolver.load_strategy(default_conf)
assert not strategy.sell_profit_only
assert isinstance(strategy.sell_profit_only, bool)
assert not strategy.exit_profit_only
assert isinstance(strategy.exit_profit_only, bool)
# must be inserted to configuration
assert 'sell_profit_only' in default_conf
assert not default_conf['sell_profit_only']
assert 'exit_profit_only' in default_conf
assert not default_conf['exit_profit_only']
default_conf.update({
'strategy': CURRENT_TEST_STRATEGY,
'sell_profit_only': True,
'exit_profit_only': True,
})
strategy = StrategyResolver.load_strategy(default_conf)
assert strategy.sell_profit_only
assert isinstance(strategy.sell_profit_only, bool)
assert log_has("Override strategy 'sell_profit_only' with value in config file: True.", caplog)
assert strategy.exit_profit_only
assert isinstance(strategy.exit_profit_only, bool)
assert log_has("Override strategy 'exit_profit_only' with value in config file: True.", caplog)
@pytest.mark.filterwarnings("ignore:deprecated")
@ -391,7 +381,22 @@ def test_deprecate_populate_indicators(result, default_conf):
@pytest.mark.filterwarnings("ignore:deprecated")
def test_missing_implements(default_conf):
def test_missing_implements(default_conf, caplog):
default_location = Path(__file__).parent / "strats"
default_conf.update({'strategy': 'StrategyTestV2',
'strategy_path': default_location})
StrategyResolver.load_strategy(default_conf)
log_has_re(r"DEPRECATED: .*use_sell_signal.*use_exit_signal.", caplog)
default_conf['trading_mode'] = 'futures'
with pytest.raises(OperationalException,
match=r"DEPRECATED: .*use_sell_signal.*use_exit_signal."):
StrategyResolver.load_strategy(default_conf)
default_conf['trading_mode'] = 'spot'
default_location = Path(__file__).parent / "strats/broken_strats"
default_conf.update({'strategy': 'TestStrategyNoImplements',
'strategy_path': default_location})

View File

@ -868,15 +868,15 @@ def test_validate_tsl(default_conf):
def test_validate_edge2(edge_conf):
edge_conf.update({
"use_sell_signal": True,
"use_exit_signal": True,
})
# Passes test
validate_config_consistency(edge_conf)
edge_conf.update({
"use_sell_signal": False,
"use_exit_signal": False,
})
with pytest.raises(OperationalException, match="Edge requires `use_sell_signal` to be True, "
with pytest.raises(OperationalException, match="Edge requires `use_exit_signal` to be True, "
"otherwise no sells will happen."):
validate_config_consistency(edge_conf)
@ -1238,14 +1238,8 @@ def test_pairlist_resolving_fallback(mocker):
@pytest.mark.parametrize("setting", [
("ask_strategy", "use_sell_signal", True,
None, "use_sell_signal", False),
("ask_strategy", "sell_profit_only", True,
None, "sell_profit_only", False),
("ask_strategy", "sell_profit_offset", 0.1,
None, "sell_profit_offset", 0.01),
("ask_strategy", "ignore_roi_if_buy_signal", True,
None, "ignore_roi_if_buy_signal", False),
("webhook", "webhookbuy", 'testWEbhook',
"webhook", "webhookentry", 'testWEbhook'),
("ask_strategy", "ignore_buying_expired_candle_after", 5,
None, "ignore_buying_expired_candle_after", 6),
])

View File

@ -2273,14 +2273,14 @@ def test_handle_trade_roi(default_conf_usdt, ticker_usdt, limit_order_open, fee,
@pytest.mark.parametrize("is_short", [False, True])
def test_handle_trade_use_sell_signal(
def test_handle_trade_use_exit_signal(
default_conf_usdt, ticker_usdt, limit_order_open, fee, mocker, caplog, is_short
) -> None:
enter_open_order = limit_order_open[exit_side(is_short)]
exit_open_order = limit_order_open[entry_side(is_short)]
# use_sell_signal is True buy default
# use_exit_signal is True buy default
caplog.set_level(logging.DEBUG)
patch_RPCManager(mocker)
mocker.patch.multiple(
@ -3637,7 +3637,7 @@ def test_execute_trade_exit_insufficient_funds_error(default_conf_usdt, ticker_u
(False, 0.10, 0.22, True, False, ExitType.EXIT_SIGNAL.value, False),
(False, 0.10, 0.22, True, False, ExitType.EXIT_SIGNAL.value, True),
])
def test_sell_profit_only(
def test_exit_profit_only(
default_conf_usdt, limit_order, limit_order_open, is_short,
fee, mocker, profit_only, bid, ask, handle_first, handle_second, exit_type) -> None:
patch_RPCManager(mocker)
@ -3657,9 +3657,9 @@ def test_sell_profit_only(
get_fee=fee,
)
default_conf_usdt.update({
'use_sell_signal': True,
'sell_profit_only': profit_only,
'sell_profit_offset': 0.1,
'use_exit_signal': True,
'exit_profit_only': profit_only,
'exit_profit_offset': 0.1,
})
freqtrade = FreqtradeBot(default_conf_usdt)
patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
@ -3679,7 +3679,7 @@ def test_sell_profit_only(
assert freqtrade.handle_trade(trade) is handle_first
if handle_second:
freqtrade.strategy.sell_profit_offset = 0.0
freqtrade.strategy.exit_profit_offset = 0.0
assert freqtrade.handle_trade(trade) is True
@ -3799,8 +3799,8 @@ def test_locked_pairs(default_conf_usdt, ticker_usdt, fee,
@pytest.mark.parametrize("is_short", [False, True])
def test_ignore_roi_if_buy_signal(default_conf_usdt, limit_order, limit_order_open, is_short,
fee, mocker) -> None:
def test_ignore_roi_if_entry_signal(default_conf_usdt, limit_order, limit_order_open, is_short,
fee, mocker) -> None:
patch_RPCManager(mocker)
patch_exchange(mocker)
eside = entry_side(is_short)
@ -3817,7 +3817,7 @@ def test_ignore_roi_if_buy_signal(default_conf_usdt, limit_order, limit_order_op
]),
get_fee=fee,
)
default_conf_usdt['ignore_roi_if_buy_signal'] = True
default_conf_usdt['ignore_roi_if_entry_signal'] = True
freqtrade = FreqtradeBot(default_conf_usdt)
patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
@ -4016,8 +4016,8 @@ def test_trailing_stop_loss_positive(
@pytest.mark.parametrize("is_short", [False, True])
def test_disable_ignore_roi_if_buy_signal(default_conf_usdt, limit_order, limit_order_open,
is_short, fee, mocker) -> None:
def test_disable_ignore_roi_if_entry_signal(default_conf_usdt, limit_order, limit_order_open,
is_short, fee, mocker) -> None:
patch_RPCManager(mocker)
patch_exchange(mocker)
eside = entry_side(is_short)
@ -4037,7 +4037,7 @@ def test_disable_ignore_roi_if_buy_signal(default_conf_usdt, limit_order, limit_
_is_dry_limit_order_filled=MagicMock(return_value=False),
)
default_conf_usdt['exit_pricing'] = {
'ignore_roi_if_buy_signal': False
'ignore_roi_if_entry_signal': False
}
freqtrade = FreqtradeBot(default_conf_usdt)
patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long