Implement order_types validation
This commit is contained in:
parent
e492bf3159
commit
5d4386f037
@ -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')
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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'
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user