Merge pull request #5188 from freqtrade/move_config_settings
Move ask_strategy config settings to root level
This commit is contained in:
@@ -150,7 +150,7 @@ def _validate_edge(conf: Dict[str, Any]) -> None:
|
||||
if not conf.get('edge', {}).get('enabled'):
|
||||
return
|
||||
|
||||
if not conf.get('ask_strategy', {}).get('use_sell_signal', True):
|
||||
if not conf.get('use_sell_signal', True):
|
||||
raise OperationalException(
|
||||
"Edge requires `use_sell_signal` to be True, otherwise no sells will happen."
|
||||
)
|
||||
|
@@ -3,7 +3,7 @@ Functions to handle deprecated settings
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Any, Dict
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from freqtrade.exceptions import OperationalException
|
||||
|
||||
@@ -12,23 +12,24 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def check_conflicting_settings(config: Dict[str, Any],
|
||||
section1: str, name1: str,
|
||||
section2: str, name2: str) -> None:
|
||||
section1_config = config.get(section1, {})
|
||||
section2_config = config.get(section2, {})
|
||||
if name1 in section1_config and name2 in section2_config:
|
||||
section_old: 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, {})
|
||||
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}"
|
||||
raise OperationalException(
|
||||
f"Conflicting settings `{section1}.{name1}` and `{section2}.{name2}` "
|
||||
f"Conflicting settings `{new_name}` and `{section_old}.{name_old}` "
|
||||
"(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 `{section1}.{name1}` "
|
||||
f"Please delete it from your configuration and use the `{new_name}` "
|
||||
"setting instead."
|
||||
)
|
||||
|
||||
|
||||
def process_removed_setting(config: Dict[str, Any],
|
||||
section1: str, name1: str,
|
||||
section2: str, name2: str) -> None:
|
||||
section2: Optional[str], name2: str) -> None:
|
||||
"""
|
||||
:param section1: Removed section
|
||||
:param name1: Removed setting name
|
||||
@@ -37,27 +38,32 @@ def process_removed_setting(config: Dict[str, Any],
|
||||
"""
|
||||
section1_config = config.get(section1, {})
|
||||
if name1 in section1_config:
|
||||
section_2 = f"{section2}.{name2}" if section2 else f"{name2}"
|
||||
raise OperationalException(
|
||||
f"Setting `{section1}.{name1}` has been moved to `{section2}.{name2}. "
|
||||
f"Please delete it from your configuration and use the `{section2}.{name2}` "
|
||||
f"Setting `{section1}.{name1}` has been moved to `{section_2}. "
|
||||
f"Please delete it from your configuration and use the `{section_2}` "
|
||||
"setting instead."
|
||||
)
|
||||
|
||||
|
||||
def process_deprecated_setting(config: Dict[str, Any],
|
||||
section1: str, name1: str,
|
||||
section2: str, name2: str) -> None:
|
||||
section2_config = config.get(section2, {})
|
||||
section_old: 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, {})
|
||||
|
||||
if name2 in section2_config:
|
||||
if name_old in section_old_config:
|
||||
section_2 = f"{section_new}.{name_new}" if section_new else f"{name_new}"
|
||||
logger.warning(
|
||||
"DEPRECATED: "
|
||||
f"The `{section2}.{name2}` setting is deprecated and "
|
||||
f"The `{section_old}.{name_old}` setting is deprecated and "
|
||||
"will be removed in the next versions of Freqtrade. "
|
||||
f"Please use the `{section1}.{name1}` setting in your configuration instead."
|
||||
f"Please use the `{section_2}` setting in your configuration instead."
|
||||
)
|
||||
section1_config = config.get(section1, {})
|
||||
section1_config[name1] = section2_config[name2]
|
||||
|
||||
section_new_config = config.get(section_new, {}) if section_new else config
|
||||
section_new_config[name_new] = section_old_config[name_old]
|
||||
|
||||
|
||||
def process_temporary_deprecated_settings(config: Dict[str, Any]) -> None:
|
||||
@@ -65,15 +71,24 @@ 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',
|
||||
# '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')
|
||||
|
||||
# Legacy way - having them in experimental ...
|
||||
process_removed_setting(config, 'experimental', 'use_sell_signal',
|
||||
'ask_strategy', 'use_sell_signal')
|
||||
None, 'use_sell_signal')
|
||||
process_removed_setting(config, 'experimental', 'sell_profit_only',
|
||||
'ask_strategy', 'sell_profit_only')
|
||||
None, 'sell_profit_only')
|
||||
process_removed_setting(config, 'experimental', 'ignore_roi_if_buy_signal',
|
||||
'ask_strategy', 'ignore_roi_if_buy_signal')
|
||||
None, 'ignore_roi_if_buy_signal')
|
||||
|
||||
if (config.get('edge', {}).get('enabled', False)
|
||||
and 'capital_available_percentage' in config.get('edge', {})):
|
||||
|
@@ -134,6 +134,11 @@ 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'},
|
||||
'ignore_buying_expired_candle_after': {'type': 'number'},
|
||||
'bot_name': {'type': 'string'},
|
||||
'unfilledtimeout': {
|
||||
'type': 'object',
|
||||
@@ -177,10 +182,6 @@ CONF_SCHEMA = {
|
||||
},
|
||||
'use_order_book': {'type': 'boolean'},
|
||||
'order_book_top': {'type': 'integer', 'minimum': 1, 'maximum': 50, },
|
||||
'use_sell_signal': {'type': 'boolean'},
|
||||
'sell_profit_only': {'type': 'boolean'},
|
||||
'sell_profit_offset': {'type': 'number'},
|
||||
'ignore_roi_if_buy_signal': {'type': 'boolean'}
|
||||
},
|
||||
'required': ['price_side']
|
||||
},
|
||||
|
@@ -684,10 +684,8 @@ class FreqtradeBot(LoggingMixin):
|
||||
|
||||
(buy, sell) = (False, False)
|
||||
|
||||
config_ask_strategy = self.config.get('ask_strategy', {})
|
||||
|
||||
if (config_ask_strategy.get('use_sell_signal', True) or
|
||||
config_ask_strategy.get('ignore_roi_if_buy_signal', False)):
|
||||
if (self.config.get('use_sell_signal', True) or
|
||||
self.config.get('ignore_roi_if_buy_signal', False)):
|
||||
analyzed_df, _ = self.dataprovider.get_analyzed_dataframe(trade.pair,
|
||||
self.strategy.timeframe)
|
||||
|
||||
|
@@ -378,10 +378,10 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame],
|
||||
'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['ask_strategy']['use_sell_signal'],
|
||||
'sell_profit_only': config['ask_strategy']['sell_profit_only'],
|
||||
'sell_profit_offset': config['ask_strategy']['sell_profit_offset'],
|
||||
'ignore_roi_if_buy_signal': config['ask_strategy']['ignore_roi_if_buy_signal'],
|
||||
'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'],
|
||||
**daily_stats,
|
||||
**trade_stats
|
||||
}
|
||||
|
@@ -45,10 +45,6 @@ class StrategyResolver(IResolver):
|
||||
strategy_name, config=config,
|
||||
extra_dir=config.get('strategy_path'))
|
||||
|
||||
# make sure ask_strategy dict is available
|
||||
if 'ask_strategy' not in config:
|
||||
config['ask_strategy'] = {}
|
||||
|
||||
if hasattr(strategy, 'ticker_interval') and not hasattr(strategy, 'timeframe'):
|
||||
# Assign ticker_interval to timeframe to keep compatibility
|
||||
if 'timeframe' not in config:
|
||||
@@ -60,42 +56,36 @@ class StrategyResolver(IResolver):
|
||||
# Set attributes
|
||||
# Check if we need to override configuration
|
||||
# (Attribute name, default, subkey)
|
||||
attributes = [("minimal_roi", {"0": 10.0}, None),
|
||||
("timeframe", None, None),
|
||||
("stoploss", None, None),
|
||||
("trailing_stop", None, None),
|
||||
("trailing_stop_positive", None, None),
|
||||
("trailing_stop_positive_offset", 0.0, None),
|
||||
("trailing_only_offset_is_reached", None, None),
|
||||
("use_custom_stoploss", None, None),
|
||||
("process_only_new_candles", None, None),
|
||||
("order_types", None, None),
|
||||
("order_time_in_force", None, None),
|
||||
("stake_currency", None, None),
|
||||
("stake_amount", None, None),
|
||||
("protections", None, None),
|
||||
("startup_candle_count", None, None),
|
||||
("unfilledtimeout", None, None),
|
||||
("use_sell_signal", True, 'ask_strategy'),
|
||||
("sell_profit_only", False, 'ask_strategy'),
|
||||
("ignore_roi_if_buy_signal", False, 'ask_strategy'),
|
||||
("sell_profit_offset", 0.0, 'ask_strategy'),
|
||||
("disable_dataframe_checks", False, None),
|
||||
("ignore_buying_expired_candle_after", 0, 'ask_strategy')
|
||||
attributes = [("minimal_roi", {"0": 10.0}),
|
||||
("timeframe", None),
|
||||
("stoploss", None),
|
||||
("trailing_stop", None),
|
||||
("trailing_stop_positive", None),
|
||||
("trailing_stop_positive_offset", 0.0),
|
||||
("trailing_only_offset_is_reached", None),
|
||||
("use_custom_stoploss", None),
|
||||
("process_only_new_candles", None),
|
||||
("order_types", None),
|
||||
("order_time_in_force", None),
|
||||
("stake_currency", None),
|
||||
("stake_amount", None),
|
||||
("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),
|
||||
("disable_dataframe_checks", False),
|
||||
("ignore_buying_expired_candle_after", 0)
|
||||
]
|
||||
for attribute, default, subkey in attributes:
|
||||
if subkey:
|
||||
StrategyResolver._override_attribute_helper(strategy, config.get(subkey, {}),
|
||||
attribute, default)
|
||||
else:
|
||||
StrategyResolver._override_attribute_helper(strategy, config,
|
||||
attribute, default)
|
||||
for attribute, default in attributes:
|
||||
StrategyResolver._override_attribute_helper(strategy, config,
|
||||
attribute, default)
|
||||
|
||||
# Loop this list again to have output combined
|
||||
for attribute, _, subkey in attributes:
|
||||
if subkey and attribute in config[subkey]:
|
||||
logger.info("Strategy using %s: %s", attribute, config[subkey][attribute])
|
||||
elif attribute in config:
|
||||
for attribute, _ in attributes:
|
||||
if attribute in config:
|
||||
logger.info("Strategy using %s: %s", attribute, config[attribute])
|
||||
|
||||
StrategyResolver._normalize_attributes(strategy)
|
||||
|
@@ -97,6 +97,11 @@ 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
|
||||
|
||||
# Number of seconds after which the candle will no longer result in a buy on expired candles
|
||||
ignore_buying_expired_candle_after: int = 0
|
||||
|
||||
@@ -543,10 +548,9 @@ class IStrategy(ABC, HyperStrategyMixin):
|
||||
# Set current rate to high for backtesting sell
|
||||
current_rate = high or rate
|
||||
current_profit = trade.calc_profit_ratio(current_rate)
|
||||
ask_strategy = self.config.get('ask_strategy', {})
|
||||
|
||||
# if buy signal and ignore_roi is set, we don't need to evaluate min_roi.
|
||||
roi_reached = (not (buy and ask_strategy.get('ignore_roi_if_buy_signal', False))
|
||||
roi_reached = (not (buy and self.ignore_roi_if_buy_signal)
|
||||
and self.min_roi_reached(trade=trade, current_profit=current_profit,
|
||||
current_time=date))
|
||||
|
||||
@@ -556,11 +560,10 @@ class IStrategy(ABC, HyperStrategyMixin):
|
||||
current_rate = rate
|
||||
current_profit = trade.calc_profit_ratio(current_rate)
|
||||
|
||||
if (ask_strategy.get('sell_profit_only', False)
|
||||
and current_profit <= ask_strategy.get('sell_profit_offset', 0)):
|
||||
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
|
||||
pass
|
||||
elif ask_strategy.get('use_sell_signal', True) and not buy:
|
||||
elif self.use_sell_signal and not buy:
|
||||
if sell:
|
||||
sell_signal = SellType.SELL_SIGNAL
|
||||
else:
|
||||
|
@@ -26,9 +26,6 @@
|
||||
"price_side": "ask",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"use_sell_signal": true,
|
||||
"sell_profit_only": false,
|
||||
"ignore_roi_if_buy_signal": false
|
||||
},
|
||||
{{ exchange | indent(4) }},
|
||||
"pairlists": [
|
||||
|
Reference in New Issue
Block a user