update unfilledtimeout settings to entry/exit
This commit is contained in:
parent
6f1b14c013
commit
0624817242
@ -8,8 +8,8 @@
|
||||
"dry_run": true,
|
||||
"cancel_open_orders_on_exit": false,
|
||||
"unfilledtimeout": {
|
||||
"buy": 10,
|
||||
"sell": 10,
|
||||
"entry": 10,
|
||||
"exit": 10,
|
||||
"exit_timeout_count": 0,
|
||||
"unit": "minutes"
|
||||
},
|
||||
|
@ -8,8 +8,8 @@
|
||||
"dry_run": true,
|
||||
"cancel_open_orders_on_exit": false,
|
||||
"unfilledtimeout": {
|
||||
"buy": 10,
|
||||
"sell": 10,
|
||||
"entry": 10,
|
||||
"exit": 10,
|
||||
"exit_timeout_count": 0,
|
||||
"unit": "minutes"
|
||||
},
|
||||
|
@ -8,8 +8,8 @@
|
||||
"dry_run": true,
|
||||
"cancel_open_orders_on_exit": false,
|
||||
"unfilledtimeout": {
|
||||
"buy": 10,
|
||||
"sell": 10,
|
||||
"entry": 10,
|
||||
"exit": 10,
|
||||
"exit_timeout_count": 0,
|
||||
"unit": "minutes"
|
||||
},
|
||||
|
@ -30,8 +30,8 @@
|
||||
},
|
||||
"stoploss": -0.10,
|
||||
"unfilledtimeout": {
|
||||
"buy": 10,
|
||||
"sell": 10,
|
||||
"entry": 10,
|
||||
"exit": 10,
|
||||
"exit_timeout_count": 0,
|
||||
"unit": "minutes"
|
||||
},
|
||||
|
@ -8,8 +8,8 @@
|
||||
"dry_run": true,
|
||||
"cancel_open_orders_on_exit": false,
|
||||
"unfilledtimeout": {
|
||||
"buy": 10,
|
||||
"sell": 10,
|
||||
"entry": 10,
|
||||
"exit": 10,
|
||||
"exit_timeout_count": 0,
|
||||
"unit": "minutes"
|
||||
},
|
||||
|
@ -102,8 +102,8 @@ Mandatory parameters are marked as **Required**, which means that they are requi
|
||||
| `trading_mode` | Specifies if you want to trade regularly, trade with leverage, or trade contracts whose prices are derived from matching cryptocurrency prices. [leverage documentation](leverage.md). <br>*Defaults to `"spot"`.* <br> **Datatype:** String
|
||||
| `margin_mode` | When trading with leverage, this determines if the collateral owned by the trader will be shared or isolated to each trading pair [leverage documentation](leverage.md). <br> **Datatype:** String
|
||||
| `liquidation_buffer` | A ratio specifying how large of a safety net to place between the liquidation price and the stoploss to prevent a position from reaching the liquidation price [leverage documentation](leverage.md). <br>*Defaults to `0.05`.* <br> **Datatype:** Float
|
||||
| `unfilledtimeout.buy` | **Required.** How long (in minutes or seconds) the bot will wait for an unfilled buy order to complete, after which the order will be cancelled and repeated at current (new) price, as long as there is a signal. [Strategy Override](#parameters-in-the-strategy).<br> **Datatype:** Integer
|
||||
| `unfilledtimeout.sell` | **Required.** How long (in minutes or seconds) the bot will wait for an unfilled sell order to complete, after which the order will be cancelled and repeated at current (new) price, as long as there is a signal. [Strategy Override](#parameters-in-the-strategy).<br> **Datatype:** Integer
|
||||
| `unfilledtimeout.entry` | **Required.** How long (in minutes or seconds) the bot will wait for an unfilled entry order to complete, after which the order will be cancelled and repeated at current (new) price, as long as there is a signal. [Strategy Override](#parameters-in-the-strategy).<br> **Datatype:** Integer
|
||||
| `unfilledtimeout.exit` | **Required.** How long (in minutes or seconds) the bot will wait for an unfilled exit order to complete, after which the order will be cancelled and repeated at current (new) price, as long as there is a signal. [Strategy Override](#parameters-in-the-strategy).<br> **Datatype:** Integer
|
||||
| `unfilledtimeout.unit` | Unit to use in unfilledtimeout setting. Note: If you set unfilledtimeout.unit to "seconds", "internals.process_throttle_secs" must be inferior or equal to timeout [Strategy Override](#parameters-in-the-strategy). <br> *Defaults to `minutes`.* <br> **Datatype:** String
|
||||
| `unfilledtimeout.exit_timeout_count` | How many times can exit orders time out. Once this number of timeouts is reached, an emergency sell is triggered. 0 to disable and allow unlimited order cancels. [Strategy Override](#parameters-in-the-strategy).<br>*Defaults to `0`.* <br> **Datatype:** Integer
|
||||
| `bid_strategy.price_side` | Select the side of the spread the bot should look at to get the buy rate. [More information below](#buy-price-side).<br> *Defaults to `bid`.* <br> **Datatype:** String (either `ask` or `bid`).
|
||||
|
@ -425,8 +425,8 @@ class AwesomeStrategy(IStrategy):
|
||||
|
||||
# Set unfilledtimeout to 25 hours, since the maximum timeout from below is 24 hours.
|
||||
unfilledtimeout = {
|
||||
'buy': 60 * 25,
|
||||
'sell': 60 * 25
|
||||
'entry': 60 * 25,
|
||||
'exit': 60 * 25
|
||||
}
|
||||
|
||||
def check_entry_timeout(self, pair: str, trade: 'Trade', order: dict,
|
||||
@ -466,8 +466,8 @@ class AwesomeStrategy(IStrategy):
|
||||
|
||||
# Set unfilledtimeout to 25 hours, since the maximum timeout from below is 24 hours.
|
||||
unfilledtimeout = {
|
||||
'buy': 60 * 25,
|
||||
'sell': 60 * 25
|
||||
'entry': 60 * 25,
|
||||
'exit': 60 * 25
|
||||
}
|
||||
|
||||
def check_entry_timeout(self, pair: str, trade: Trade, order: dict,
|
||||
|
@ -32,6 +32,7 @@ If you intend on using markets other than spot markets, please migrate your stra
|
||||
* Strategy/Configuration settings.
|
||||
* `order_time_in_force` buy -> entry, sell -> exit.
|
||||
* `order_types` buy -> entry, sell -> exit.
|
||||
* `unfilledtimeout` buy -> entry, sell -> exit.
|
||||
|
||||
## Extensive explanation
|
||||
|
||||
@ -287,6 +288,7 @@ This should be given the value of `trade.is_short`.
|
||||
"stoploss": "market",
|
||||
"stoploss_on_exchange": false,
|
||||
"stoploss_on_exchange_interval": 60
|
||||
}
|
||||
```
|
||||
|
||||
``` python hl_lines="2-6"
|
||||
@ -299,4 +301,27 @@ This should be given the value of `trade.is_short`.
|
||||
"stoploss": "market",
|
||||
"stoploss_on_exchange": false,
|
||||
"stoploss_on_exchange_interval": 60
|
||||
}
|
||||
```
|
||||
|
||||
#### `unfilledtimeout`
|
||||
|
||||
`unfilledtimeout` have changed all wordings from `buy` to `entry` - and `sell` to `exit`.
|
||||
|
||||
``` python hl_lines="2-3"
|
||||
unfilledtimeout = {
|
||||
"buy": 10,
|
||||
"sell": 10,
|
||||
"exit_timeout_count": 0,
|
||||
"unit": "minutes"
|
||||
}
|
||||
```
|
||||
|
||||
``` python hl_lines="2-3"
|
||||
unfilledtimeout = {
|
||||
"entry": 10,
|
||||
"exit": 10,
|
||||
"exit_timeout_count": 0,
|
||||
"unit": "minutes"
|
||||
}
|
||||
```
|
||||
|
@ -216,6 +216,7 @@ def validate_migrated_strategy_settings(conf: Dict[str, Any]) -> None:
|
||||
|
||||
_validate_time_in_force(conf)
|
||||
_validate_order_types(conf)
|
||||
_validate_unfilledtimeout(conf)
|
||||
|
||||
|
||||
def _validate_time_in_force(conf: Dict[str, Any]) -> None:
|
||||
@ -258,3 +259,23 @@ def _validate_order_types(conf: Dict[str, Any]) -> None:
|
||||
]:
|
||||
|
||||
process_deprecated_setting(conf, 'order_types', o, 'order_types', n)
|
||||
|
||||
|
||||
def _validate_unfilledtimeout(conf: Dict[str, Any]) -> None:
|
||||
unfilledtimeout = conf.get('unfilledtimeout', {})
|
||||
if any(x in unfilledtimeout for x in ['buy', 'sell']):
|
||||
if conf.get('trading_mode', TradingMode.SPOT) != TradingMode.SPOT:
|
||||
raise OperationalException(
|
||||
"Please migrate your unfilledtimeout settings to use the new wording.")
|
||||
else:
|
||||
|
||||
logger.warning(
|
||||
"DEPRECATED: Using 'buy' and 'sell' for unfilledtimeout is deprecated."
|
||||
"Please migrate your unfilledtimeout settings to use 'entry' and 'exit' wording."
|
||||
)
|
||||
for o, n in [
|
||||
('buy', 'entry'),
|
||||
('sell', 'exit'),
|
||||
]:
|
||||
|
||||
process_deprecated_setting(conf, 'unfilledtimeout', o, 'unfilledtimeout', n)
|
||||
|
@ -165,8 +165,8 @@ CONF_SCHEMA = {
|
||||
'unfilledtimeout': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'buy': {'type': 'number', 'minimum': 1},
|
||||
'sell': {'type': 'number', 'minimum': 1},
|
||||
'entry': {'type': 'number', 'minimum': 1},
|
||||
'exit': {'type': 'number', 'minimum': 1},
|
||||
'exit_timeout_count': {'type': 'number', 'minimum': 0, 'default': 0},
|
||||
'unit': {'type': 'string', 'enum': TIMEOUT_UNITS, 'default': 'minutes'}
|
||||
}
|
||||
|
@ -131,8 +131,8 @@ class Daily(BaseModel):
|
||||
|
||||
|
||||
class UnfilledTimeout(BaseModel):
|
||||
buy: Optional[int]
|
||||
sell: Optional[int]
|
||||
entry: Optional[int]
|
||||
exit: Optional[int]
|
||||
unit: Optional[str]
|
||||
exit_timeout_count: Optional[int]
|
||||
|
||||
|
@ -259,7 +259,7 @@ class IStrategy(ABC, HyperStrategyMixin):
|
||||
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
|
||||
:return bool: When True is returned, then the (long)sell/(short)buy-order is cancelled.
|
||||
"""
|
||||
return self.check_exit_timeout(
|
||||
return self.check_sell_timeout(
|
||||
pair=pair, trade=trade, order=order, current_time=current_time)
|
||||
|
||||
def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float,
|
||||
@ -1045,14 +1045,14 @@ class IStrategy(ABC, HyperStrategyMixin):
|
||||
FT Internal method.
|
||||
Check if timeout is active, and if the order is still open and timed out
|
||||
"""
|
||||
side = 'buy' if order.side == 'buy' else 'sell'
|
||||
side = 'entry' if order.ft_order_side == trade.enter_side else 'exit'
|
||||
|
||||
timeout = self.config.get('unfilledtimeout', {}).get(side)
|
||||
if timeout is not None:
|
||||
timeout_unit = self.config.get('unfilledtimeout', {}).get('unit', 'minutes')
|
||||
timeout_kwargs = {timeout_unit: -timeout}
|
||||
timeout_threshold = current_time + timedelta(**timeout_kwargs)
|
||||
timedout = (order.status == 'open' and order.side == side
|
||||
and order.order_date_utc < timeout_threshold)
|
||||
timedout = (order.status == 'open' and order.order_date_utc < timeout_threshold)
|
||||
if timedout:
|
||||
return True
|
||||
time_method = (self.check_exit_timeout if order.side == trade.exit_side
|
||||
|
@ -16,8 +16,8 @@
|
||||
"trading_mode": "{{ trading_mode }}",
|
||||
"margin_mode": "{{ margin_mode }}",
|
||||
"unfilledtimeout": {
|
||||
"buy": 10,
|
||||
"sell": 10,
|
||||
"entry": 10,
|
||||
"exit": 10,
|
||||
"exit_timeout_count": 0,
|
||||
"unit": "minutes"
|
||||
},
|
||||
|
@ -416,8 +416,8 @@ def get_default_conf(testdatadir):
|
||||
"dry_run_wallet": 1000,
|
||||
"stoploss": -0.10,
|
||||
"unfilledtimeout": {
|
||||
"buy": 10,
|
||||
"sell": 30
|
||||
"entry": 10,
|
||||
"exit": 30
|
||||
},
|
||||
"bid_strategy": {
|
||||
"ask_last_balance": 0.0,
|
||||
|
@ -963,7 +963,7 @@ def test_validate_time_in_force(default_conf, caplog) -> None:
|
||||
validate_config_consistency(conf)
|
||||
|
||||
|
||||
def test_validate_order_types(default_conf, caplog) -> None:
|
||||
def test__validate_order_types(default_conf, caplog) -> None:
|
||||
conf = deepcopy(default_conf)
|
||||
conf['order_types'] = {
|
||||
'buy': 'limit',
|
||||
@ -998,6 +998,31 @@ def test_validate_order_types(default_conf, caplog) -> None:
|
||||
validate_config_consistency(conf)
|
||||
|
||||
|
||||
def test__validate_unfilledtimeout(default_conf, caplog) -> None:
|
||||
conf = deepcopy(default_conf)
|
||||
conf['unfilledtimeout'] = {
|
||||
'buy': 30,
|
||||
'sell': 35,
|
||||
}
|
||||
validate_config_consistency(conf)
|
||||
assert log_has_re(r"DEPRECATED: Using 'buy' and 'sell' for unfilledtimeout is.*", caplog)
|
||||
assert conf['unfilledtimeout']['entry'] == 30
|
||||
assert conf['unfilledtimeout']['exit'] == 35
|
||||
assert 'buy' not in conf['unfilledtimeout']
|
||||
assert 'sell' not in conf['unfilledtimeout']
|
||||
|
||||
conf = deepcopy(default_conf)
|
||||
conf['unfilledtimeout'] = {
|
||||
'buy': 30,
|
||||
'sell': 35,
|
||||
}
|
||||
conf['trading_mode'] = 'futures'
|
||||
with pytest.raises(
|
||||
OperationalException,
|
||||
match=r"Please migrate your unfilledtimeout settings to use the new wording\."):
|
||||
validate_config_consistency(conf)
|
||||
|
||||
|
||||
def test_load_config_test_comments() -> None:
|
||||
"""
|
||||
Load config with comments
|
||||
|
@ -2378,8 +2378,8 @@ def test_check_handle_timedout_entry_usercustom(
|
||||
old_order = limit_sell_order_old if is_short else limit_buy_order_old
|
||||
old_order['id'] = open_trade.open_order_id
|
||||
|
||||
default_conf_usdt["unfilledtimeout"] = {"buy": 30,
|
||||
"sell": 1400} if is_short else {"buy": 1400, "sell": 30}
|
||||
default_conf_usdt["unfilledtimeout"] = {"entry": 30,
|
||||
"exit": 1400} if is_short else {"entry": 1400, "exit": 30}
|
||||
|
||||
rpc_mock = patch_RPCManager(mocker)
|
||||
cancel_order_mock = MagicMock(return_value=old_order)
|
||||
@ -2543,7 +2543,7 @@ def test_check_handle_timedout_exit_usercustom(
|
||||
default_conf_usdt, ticker_usdt, limit_sell_order_old, mocker,
|
||||
is_short, open_trade_usdt, caplog
|
||||
) -> None:
|
||||
default_conf_usdt["unfilledtimeout"] = {"buy": 1440, "sell": 1440, "exit_timeout_count": 1}
|
||||
default_conf_usdt["unfilledtimeout"] = {"entry": 1440, "exit": 1440, "exit_timeout_count": 1}
|
||||
limit_sell_order_old['id'] = open_trade_usdt.open_order_id
|
||||
if is_short:
|
||||
limit_sell_order_old['side'] = 'buy'
|
||||
|
Loading…
Reference in New Issue
Block a user