Implement order_types validation

This commit is contained in:
Matthias 2022-03-08 06:59:14 +01:00
parent e492bf3159
commit 5d4386f037
9 changed files with 63 additions and 32 deletions

View File

@ -6,6 +6,7 @@ from jsonschema import Draft4Validator, validators
from jsonschema.exceptions import ValidationError, best_match from jsonschema.exceptions import ValidationError, best_match
from freqtrade import constants from freqtrade import constants
from freqtrade.configuration.deprecated_settings import process_deprecated_setting
from freqtrade.enums import RunMode, TradingMode from freqtrade.enums import RunMode, TradingMode
from freqtrade.exceptions import OperationalException from freqtrade.exceptions import OperationalException
@ -102,11 +103,12 @@ def _validate_price_config(conf: Dict[str, Any]) -> None:
""" """
When using market orders, price sides must be using the "other" side of the price When using market orders, price sides must be using the "other" side of the price
""" """
if (conf.get('order_types', {}).get('buy') == 'market' # TODO-lev: check this again when determining how to migrate pricing strategies!
if (conf.get('order_types', {}).get('entry') == 'market'
and conf.get('bid_strategy', {}).get('price_side') != 'ask'): and conf.get('bid_strategy', {}).get('price_side') != 'ask'):
raise OperationalException('Market buy orders require bid_strategy.price_side = "ask".') raise OperationalException('Market buy orders require bid_strategy.price_side = "ask".')
if (conf.get('order_types', {}).get('sell') == 'market' if (conf.get('order_types', {}).get('exit') == 'market'
and conf.get('ask_strategy', {}).get('price_side') != 'bid'): and conf.get('ask_strategy', {}).get('price_side') != 'bid'):
raise OperationalException('Market sell orders require ask_strategy.price_side = "bid".') raise OperationalException('Market sell orders require ask_strategy.price_side = "bid".')
@ -213,6 +215,7 @@ def _validate_ask_orderbook(conf: Dict[str, Any]) -> None:
def validate_migrated_strategy_settings(conf: Dict[str, Any]) -> None: def validate_migrated_strategy_settings(conf: Dict[str, Any]) -> None:
_validate_time_in_force(conf) _validate_time_in_force(conf)
_validate_order_types(conf)
def _validate_time_in_force(conf: Dict[str, Any]) -> None: def _validate_time_in_force(conf: Dict[str, Any]) -> None:
@ -229,3 +232,31 @@ def _validate_time_in_force(conf: Dict[str, Any]) -> None:
) )
time_in_force['entry'] = time_in_force.pop('buy') time_in_force['entry'] = time_in_force.pop('buy')
time_in_force['exit'] = time_in_force.pop('sell') time_in_force['exit'] = time_in_force.pop('sell')
def _validate_order_types(conf: Dict[str, Any]) -> None:
order_types = conf.get('order_types', {})
if any(x in order_types for x in ['buy', 'sell', 'emergencysell', 'forcebuy', 'forcesell']):
if conf.get('trading_mode', TradingMode.SPOT) != TradingMode.SPOT:
raise OperationalException(
"Please migrate your order_types settings to use the new wording.")
else:
logger.warning(
"DEPRECATED: Using 'buy' and 'sell' for order_types is deprecated."
"Please migrate your time_in_force settings to use 'entry' and 'exit' wording."
)
for o, n in [
('buy', 'entry'),
('sell', 'exit'),
('emergencysell', 'emergencyexit'),
('forcesell', 'forceexit'),
('forcebuy', 'forceentry'),
]:
process_deprecated_setting(conf, 'order_types', o, 'order_types', n)
# order_types['entry'] = order_types.pop('buy')
# order_types['exit'] = order_types.pop('sell')
# order_types['emergencyexit'] = order_types.pop('emergencysell')
# order_types['forceexit'] = order_types.pop('forceexit')
# order_types['forceentry'] = order_types.pop('forceentry')

View File

@ -951,8 +951,8 @@ def test_validate_order_types(default_conf, mocker):
mocker.patch('freqtrade.exchange.Exchange.name', 'Bittrex') mocker.patch('freqtrade.exchange.Exchange.name', 'Bittrex')
default_conf['order_types'] = { default_conf['order_types'] = {
'buy': 'limit', 'entry': 'limit',
'sell': 'limit', 'exit': 'limit',
'stoploss': 'market', 'stoploss': 'market',
'stoploss_on_exchange': False 'stoploss_on_exchange': False
} }
@ -962,8 +962,8 @@ def test_validate_order_types(default_conf, mocker):
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
default_conf['order_types'] = { default_conf['order_types'] = {
'buy': 'limit', 'entry': 'limit',
'sell': 'limit', 'exit': 'limit',
'stoploss': 'market', 'stoploss': 'market',
'stoploss_on_exchange': False 'stoploss_on_exchange': False
} }
@ -972,8 +972,8 @@ def test_validate_order_types(default_conf, mocker):
Exchange(default_conf) Exchange(default_conf)
default_conf['order_types'] = { default_conf['order_types'] = {
'buy': 'limit', 'entry': 'limit',
'sell': 'limit', 'exit': 'limit',
'stoploss': 'limit', 'stoploss': 'limit',
'stoploss_on_exchange': True 'stoploss_on_exchange': True
} }

View File

@ -34,8 +34,8 @@ class HyperoptableStrategy(IStrategy):
# Optional order type mapping # Optional order type mapping
order_types = { order_types = {
'buy': 'limit', 'entry': 'limit',
'sell': 'limit', 'exit': 'limit',
'stoploss': 'limit', 'stoploss': 'limit',
'stoploss_on_exchange': False 'stoploss_on_exchange': False
} }

View File

@ -36,8 +36,8 @@ class StrategyTestV2(IStrategy):
# Optional order type mapping # Optional order type mapping
order_types = { order_types = {
'buy': 'limit', 'entry': 'limit',
'sell': 'limit', 'exit': 'limit',
'stoploss': 'limit', 'stoploss': 'limit',
'stoploss_on_exchange': False 'stoploss_on_exchange': False
} }

View File

@ -37,8 +37,8 @@ class StrategyTestV3(IStrategy):
# Optional order type mapping # Optional order type mapping
order_types = { order_types = {
'buy': 'limit', 'entry': 'limit',
'sell': 'limit', 'exit': 'limit',
'stoploss': 'limit', 'stoploss': 'limit',
'stoploss_on_exchange': False 'stoploss_on_exchange': False
} }

View File

@ -223,8 +223,8 @@ def test_strategy_override_order_types(caplog, default_conf):
caplog.set_level(logging.INFO) caplog.set_level(logging.INFO)
order_types = { order_types = {
'buy': 'market', 'entry': 'market',
'sell': 'limit', 'exit': 'limit',
'stoploss': 'limit', 'stoploss': 'limit',
'stoploss_on_exchange': True, 'stoploss_on_exchange': True,
} }
@ -235,16 +235,16 @@ def test_strategy_override_order_types(caplog, default_conf):
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
assert strategy.order_types assert strategy.order_types
for method in ['buy', 'sell', 'stoploss', 'stoploss_on_exchange']: for method in ['entry', 'exit', 'stoploss', 'stoploss_on_exchange']:
assert strategy.order_types[method] == order_types[method] assert strategy.order_types[method] == order_types[method]
assert log_has("Override strategy 'order_types' with value in config file:" assert log_has("Override strategy 'order_types' with value in config file:"
" {'buy': 'market', 'sell': 'limit', 'stoploss': 'limit'," " {'entry': 'market', 'exit': 'limit', 'stoploss': 'limit',"
" 'stoploss_on_exchange': True}.", caplog) " 'stoploss_on_exchange': True}.", caplog)
default_conf.update({ default_conf.update({
'strategy': CURRENT_TEST_STRATEGY, 'strategy': CURRENT_TEST_STRATEGY,
'order_types': {'buy': 'market'} 'order_types': {'exit': 'market'}
}) })
# Raise error for invalid configuration # Raise error for invalid configuration
with pytest.raises(ImportError, with pytest.raises(ImportError,

View File

@ -798,8 +798,8 @@ def test_validate_max_open_trades(default_conf):
def test_validate_price_side(default_conf): def test_validate_price_side(default_conf):
default_conf['order_types'] = { default_conf['order_types'] = {
"buy": "limit", "entry": "limit",
"sell": "limit", "exit": "limit",
"stoploss": "limit", "stoploss": "limit",
"stoploss_on_exchange": False, "stoploss_on_exchange": False,
} }
@ -807,21 +807,21 @@ def test_validate_price_side(default_conf):
validate_config_consistency(default_conf) validate_config_consistency(default_conf)
conf = deepcopy(default_conf) conf = deepcopy(default_conf)
conf['order_types']['buy'] = 'market' conf['order_types']['entry'] = 'market'
with pytest.raises(OperationalException, with pytest.raises(OperationalException,
match='Market buy orders require bid_strategy.price_side = "ask".'): match='Market buy orders require bid_strategy.price_side = "ask".'):
validate_config_consistency(conf) validate_config_consistency(conf)
conf = deepcopy(default_conf) conf = deepcopy(default_conf)
conf['order_types']['sell'] = 'market' conf['order_types']['exit'] = 'market'
with pytest.raises(OperationalException, with pytest.raises(OperationalException,
match='Market sell orders require ask_strategy.price_side = "bid".'): match='Market sell orders require ask_strategy.price_side = "bid".'):
validate_config_consistency(conf) validate_config_consistency(conf)
# Validate inversed case # Validate inversed case
conf = deepcopy(default_conf) conf = deepcopy(default_conf)
conf['order_types']['sell'] = 'market' conf['order_types']['exit'] = 'market'
conf['order_types']['buy'] = 'market' conf['order_types']['entry'] = 'market'
conf['ask_strategy']['price_side'] = 'bid' conf['ask_strategy']['price_side'] = 'bid'
conf['bid_strategy']['price_side'] = 'ask' conf['bid_strategy']['price_side'] = 'ask'

View File

@ -91,8 +91,8 @@ def test_order_dict(default_conf_usdt, mocker, runmode, caplog) -> None:
conf = default_conf_usdt.copy() conf = default_conf_usdt.copy()
conf['runmode'] = runmode conf['runmode'] = runmode
conf['order_types'] = { conf['order_types'] = {
'buy': 'market', 'entry': 'market',
'sell': 'limit', 'exit': 'limit',
'stoploss': 'limit', 'stoploss': 'limit',
'stoploss_on_exchange': True, 'stoploss_on_exchange': True,
} }
@ -108,8 +108,8 @@ def test_order_dict(default_conf_usdt, mocker, runmode, caplog) -> None:
conf = default_conf_usdt.copy() conf = default_conf_usdt.copy()
conf['runmode'] = runmode conf['runmode'] = runmode
conf['order_types'] = { conf['order_types'] = {
'buy': 'market', 'entry': 'market',
'sell': 'limit', 'exit': 'limit',
'stoploss': 'limit', 'stoploss': 'limit',
'stoploss_on_exchange': False, 'stoploss_on_exchange': False,
} }
@ -3490,7 +3490,7 @@ def test_execute_trade_exit_market_order(
'freqtrade.exchange.Exchange', 'freqtrade.exchange.Exchange',
fetch_ticker=ticker_usdt_sell_up fetch_ticker=ticker_usdt_sell_up
) )
freqtrade.config['order_types']['sell'] = 'market' freqtrade.config['order_types']['exit'] = 'market'
freqtrade.execute_trade_exit( freqtrade.execute_trade_exit(
trade=trade, trade=trade,

View File

@ -80,7 +80,7 @@ def test_may_execute_exit_stoploss_on_exchange_multi(default_conf, ticker, fee,
freqtrade = get_patched_freqtradebot(mocker, default_conf) freqtrade = get_patched_freqtradebot(mocker, default_conf)
freqtrade.strategy.order_types['stoploss_on_exchange'] = True freqtrade.strategy.order_types['stoploss_on_exchange'] = True
# Switch ordertype to market to close trade immediately # Switch ordertype to market to close trade immediately
freqtrade.strategy.order_types['sell'] = 'market' freqtrade.strategy.order_types['exit'] = 'market'
freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=True) freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=True)
freqtrade.strategy.confirm_trade_exit = MagicMock(return_value=True) freqtrade.strategy.confirm_trade_exit = MagicMock(return_value=True)
patch_get_signal(freqtrade) patch_get_signal(freqtrade)
@ -173,7 +173,7 @@ def test_forcebuy_last_unlimited(default_conf, ticker, fee, mocker, balance_rati
rpc = RPC(freqtrade) rpc = RPC(freqtrade)
freqtrade.strategy.order_types['stoploss_on_exchange'] = True freqtrade.strategy.order_types['stoploss_on_exchange'] = True
# Switch ordertype to market to close trade immediately # Switch ordertype to market to close trade immediately
freqtrade.strategy.order_types['sell'] = 'market' freqtrade.strategy.order_types['exit'] = 'market'
patch_get_signal(freqtrade) patch_get_signal(freqtrade)
# Create 4 trades # Create 4 trades