Merge pull request #6512 from freqtrade/short_order_types

Short order types renamal
This commit is contained in:
Matthias 2022-03-12 09:32:16 +01:00 committed by GitHub
commit efc313b28b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 198 additions and 131 deletions

View File

@ -128,7 +128,7 @@ Telegram is not mandatory. However, this is a great way to control your bot. Mor
- `/stopbuy`: Stop entering new trades. - `/stopbuy`: Stop entering new trades.
- `/status <trade_id>|[table]`: Lists all or specific open trades. - `/status <trade_id>|[table]`: Lists all or specific open trades.
- `/profit [<n>]`: Lists cumulative profit from all finished trades, over the last n days. - `/profit [<n>]`: Lists cumulative profit from all finished trades, over the last n days.
- `/forcesell <trade_id>|all`: Instantly sells the given trade (Ignoring `minimum_roi`). - `/forceexit <trade_id>|all`: Instantly exits the given trade (Ignoring `minimum_roi`).
- `/performance`: Show performance of each finished trade grouped by pair - `/performance`: Show performance of each finished trade grouped by pair
- `/balance`: Show account balance per currency. - `/balance`: Show account balance per currency.
- `/daily <n>`: Shows profit or loss per day, over the last n days. - `/daily <n>`: Shows profit or loss per day, over the last n days.

View File

@ -51,11 +51,11 @@
"order_book_top": 1 "order_book_top": 1
}, },
"order_types": { "order_types": {
"buy": "limit", "entry": "limit",
"sell": "limit", "exit": "limit",
"emergencysell": "market", "emergencyexit": "market",
"forcesell": "market", "forceexit": "market",
"forcebuy": "market", "forceentry": "market",
"stoploss": "market", "stoploss": "market",
"stoploss_on_exchange": false, "stoploss_on_exchange": false,
"stoploss_on_exchange_interval": 60, "stoploss_on_exchange_interval": 60,

View File

@ -121,7 +121,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi
| `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) | `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 | `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
| `ignore_buying_expired_candle_after` | Specifies the number of seconds until a buy signal is no longer used. <br> **Datatype:** Integer | `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 (`"buy"`, `"sell"`, `"stoploss"`, `"stoploss_on_exchange"`). [More information below](#understand-order_types). [Strategy Override](#parameters-in-the-strategy).<br> **Datatype:** Dict | `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 | `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
| `custom_price_max_distance_ratio` | Configure maximum distance ratio between current and custom entry or exit price. <br>*Defaults to `0.02` 2%).*<br> **Datatype:** Positive float | `custom_price_max_distance_ratio` | Configure maximum distance ratio between current and custom entry or exit price. <br>*Defaults to `0.02` 2%).*<br> **Datatype:** Positive float
| `exchange.name` | **Required.** Name of the exchange class to use. [List below](#user-content-what-values-for-exchangename). <br> **Datatype:** String | `exchange.name` | **Required.** Name of the exchange class to use. [List below](#user-content-what-values-for-exchangename). <br> **Datatype:** String
@ -374,7 +374,7 @@ For example, if your strategy is using a 1h timeframe, and you only want to buy
### Understand order_types ### Understand order_types
The `order_types` configuration parameter maps actions (`buy`, `sell`, `stoploss`, `emergencysell`, `forcesell`, `forcebuy`) to order-types (`market`, `limit`, ...) as well as configures stoploss to be on the exchange and defines stoploss on exchange update interval in seconds. The `order_types` configuration parameter maps actions (`entry`, `exit`, `stoploss`, `emergencyexit`, `forceexit`, `forceentry`) to order-types (`market`, `limit`, ...) as well as configures stoploss to be on the exchange and defines stoploss on exchange update interval in seconds.
This allows to buy using limit orders, sell using This allows to buy using limit orders, sell using
limit-orders, and create stoplosses using market orders. It also allows to set the limit-orders, and create stoplosses using market orders. It also allows to set the
@ -383,20 +383,19 @@ the buy order is fulfilled.
`order_types` set in the configuration file overwrites values set in the strategy as a whole, so you need to configure the whole `order_types` dictionary in one place. `order_types` set in the configuration file overwrites values set in the strategy as a whole, so you need to configure the whole `order_types` dictionary in one place.
If this is configured, the following 4 values (`buy`, `sell`, `stoploss` and If this is configured, the following 4 values (`entry`, `exit`, `stoploss` and `stoploss_on_exchange`) need to be present, otherwise, the bot will fail to start.
`stoploss_on_exchange`) need to be present, otherwise, the bot will fail to start.
For information on (`emergencysell`,`forcesell`, `forcebuy`, `stoploss_on_exchange`,`stoploss_on_exchange_interval`,`stoploss_on_exchange_limit_ratio`) please see stop loss documentation [stop loss on exchange](stoploss.md) For information on (`emergencyexit`,`forceexit`, `forceentry`, `stoploss_on_exchange`,`stoploss_on_exchange_interval`,`stoploss_on_exchange_limit_ratio`) please see stop loss documentation [stop loss on exchange](stoploss.md)
Syntax for Strategy: Syntax for Strategy:
```python ```python
order_types = { order_types = {
"buy": "limit", "entry": "limit",
"sell": "limit", "exit": "limit",
"emergencysell": "market", "emergencyexit": "market",
"forcebuy": "market", "forceentry": "market",
"forcesell": "market", "forceexit": "market",
"stoploss": "market", "stoploss": "market",
"stoploss_on_exchange": False, "stoploss_on_exchange": False,
"stoploss_on_exchange_interval": 60, "stoploss_on_exchange_interval": 60,
@ -408,11 +407,11 @@ Configuration:
```json ```json
"order_types": { "order_types": {
"buy": "limit", "entry": "limit",
"sell": "limit", "exit": "limit",
"emergencysell": "market", "emergencyexit": "market",
"forcebuy": "market", "forceentry": "market",
"forcesell": "market", "forceexit": "market",
"stoploss": "market", "stoploss": "market",
"stoploss_on_exchange": false, "stoploss_on_exchange": false,
"stoploss_on_exchange_interval": 60 "stoploss_on_exchange_interval": 60
@ -435,7 +434,7 @@ Configuration:
If `stoploss_on_exchange` is enabled and the stoploss is cancelled manually on the exchange, then the bot will create a new stoploss order. If `stoploss_on_exchange` is enabled and the stoploss is cancelled manually on the exchange, then the bot will create a new stoploss order.
!!! Warning "Warning: stoploss_on_exchange failures" !!! Warning "Warning: stoploss_on_exchange failures"
If stoploss on exchange creation fails for some reason, then an "emergency sell" is initiated. By default, this will sell the asset using a market order. The order-type for the emergency-sell can be changed by setting the `emergencysell` value in the `order_types` dictionary - however, this is not advised. If stoploss on exchange creation fails for some reason, then an "emergency exit" is initiated. By default, this will sell the asset using a market order. The order-type for the emergency-sell can be changed by setting the `emergencyexit` value in the `order_types` dictionary - however, this is not advised.
### Understand order_time_in_force ### Understand order_time_in_force

View File

@ -77,7 +77,7 @@ You can use "current" market data by using the [dataprovider](strategy-customiza
### Is there a setting to only SELL the coins being held and not perform anymore BUYS? ### Is there a setting to only SELL the coins being held and not perform anymore BUYS?
You can use the `/stopbuy` command in Telegram to prevent future buys, followed by `/forcesell all` (sell all open trades). You can use the `/stopbuy` command in Telegram to prevent future buys, followed by `/forceexit all` (sell all open trades).
### I want to run multiple bots on the same machine ### I want to run multiple bots on the same machine
@ -117,10 +117,10 @@ As the message says, your exchange does not support market orders and you have o
To fix this, redefine order types in the strategy to use "limit" instead of "market": To fix this, redefine order types in the strategy to use "limit" instead of "market":
``` ``` python
order_types = { order_types = {
... ...
'stoploss': 'limit', "stoploss": "limit",
... ...
} }
``` ```

View File

@ -101,8 +101,8 @@ Assuming both buy and sell are using market orders, a configuration similar to t
``` jsonc ``` jsonc
"order_types": { "order_types": {
"buy": "market", "entry": "market",
"sell": "market" "exit": "market"
// ... // ...
}, },
"bid_strategy": { "bid_strategy": {

View File

@ -145,9 +145,9 @@ python3 scripts/rest_client.py --config rest_config.json <command> [optional par
| `locks` | Displays currently locked pairs. | `locks` | Displays currently locked pairs.
| `delete_lock <lock_id>` | Deletes (disables) the lock by id. | `delete_lock <lock_id>` | Deletes (disables) the lock by id.
| `profit` | Display a summary of your profit/loss from close trades and some stats about your performance. | `profit` | Display a summary of your profit/loss from close trades and some stats about your performance.
| `forcesell <trade_id>` | Instantly sells the given trade (Ignoring `minimum_roi`). | `forceexit <trade_id>` | Instantly exits the given trade (Ignoring `minimum_roi`).
| `forcesell all` | Instantly sells all open trades (Ignoring `minimum_roi`). | `forceexit all` | Instantly exits all open trades (Ignoring `minimum_roi`).
| `forcebuy <pair> [rate]` | Instantly buys the given pair. Rate is optional. (`forcebuy_enable` must be set to True) | `forceenter <pair> [rate]` | Instantly enters the given pair. Rate is optional. (`forcebuy_enable` must be set to True)
| `forceenter <pair> <side> [rate]` | Instantly longs or shorts the given pair. Rate is optional. (`forcebuy_enable` must be set to True) | `forceenter <pair> <side> [rate]` | Instantly longs or shorts the given pair. Rate is optional. (`forcebuy_enable` must be set to True)
| `performance` | Show performance of each finished trade grouped by pair. | `performance` | Show performance of each finished trade grouped by pair.
| `balance` | Show account balance per currency. | `balance` | Show account balance per currency.

View File

@ -104,8 +104,8 @@ To mitigate this, you can try to match the first order on the opposite orderbook
``` jsonc ``` jsonc
"order_types": { "order_types": {
"buy": "limit", "entry": "limit",
"sell": "limit" "exit": "limit"
// ... // ...
}, },
"bid_strategy": { "bid_strategy": {

View File

@ -49,14 +49,14 @@ sqlite3
SELECT * FROM trades; SELECT * FROM trades;
``` ```
## Fix trade still open after a manual sell on the exchange ## Fix trade still open after a manual exit on the exchange
!!! Warning !!! Warning
Manually selling a pair on the exchange will not be detected by the bot and it will try to sell anyway. Whenever possible, forcesell <tradeid> should be used to accomplish the same thing. Manually selling a pair on the exchange will not be detected by the bot and it will try to sell anyway. Whenever possible, forceexit <tradeid> should be used to accomplish the same thing.
It is strongly advised to backup your database file before making any manual changes. It is strongly advised to backup your database file before making any manual changes.
!!! Note !!! Note
This should not be necessary after /forcesell, as forcesell orders are closed automatically by the bot on the next iteration. This should not be necessary after /forceexit, as forceexit orders are closed automatically by the bot on the next iteration.
```sql ```sql
UPDATE trades UPDATE trades

View File

@ -17,7 +17,7 @@ Those stoploss modes can be *on exchange* or *off exchange*.
These modes can be configured with these values: These modes can be configured with these values:
``` python ``` python
'emergencysell': 'market', 'emergencyexit': 'market',
'stoploss_on_exchange': False 'stoploss_on_exchange': False
'stoploss_on_exchange_interval': 60, 'stoploss_on_exchange_interval': 60,
'stoploss_on_exchange_limit_ratio': 0.99 'stoploss_on_exchange_limit_ratio': 0.99
@ -52,30 +52,30 @@ The bot cannot do these every 5 seconds (at each iteration), otherwise it would
So this parameter will tell the bot how often it should update the stoploss order. The default value is 60 (1 minute). So this parameter will tell the bot how often it should update the stoploss order. The default value is 60 (1 minute).
This same logic will reapply a stoploss order on the exchange should you cancel it accidentally. This same logic will reapply a stoploss order on the exchange should you cancel it accidentally.
### forcesell ### forceexit
`forcesell` is an optional value, which defaults to the same value as `sell` and is used when sending a `/forcesell` command from Telegram or from the Rest API. `forceexit` is an optional value, which defaults to the same value as `exit` and is used when sending a `/forceexit` command from Telegram or from the Rest API.
### forcebuy ### forceentry
`forcebuy` is an optional value, which defaults to the same value as `buy` and is used when sending a `/forcebuy` command from Telegram or from the Rest API. `forceentry` is an optional value, which defaults to the same value as `entry` and is used when sending a `/forceentry` command from Telegram or from the Rest API.
### emergencysell ### emergencyexit
`emergencysell` is an optional value, which defaults to `market` and is used when creating stop loss on exchange orders fails. `emergencyexit` is an optional value, which defaults to `market` and is used when creating stop loss on exchange orders fails.
The below is the default which is used if not changed in strategy or configuration file. The below is the default which is used if not changed in strategy or configuration file.
Example from strategy file: Example from strategy file:
``` python ``` python
order_types = { order_types = {
'buy': 'limit', "entry": "limit",
'sell': 'limit', "exit": "limit",
'emergencysell': 'market', "emergencyexit": "market",
'stoploss': 'market', "stoploss": "market",
'stoploss_on_exchange': True, "stoploss_on_exchange": True,
'stoploss_on_exchange_interval': 60, "stoploss_on_exchange_interval": 60,
'stoploss_on_exchange_limit_ratio': 0.99 "stoploss_on_exchange_limit_ratio": 0.99
} }
``` ```

View File

@ -171,8 +171,8 @@ official commands. You can ask at any moment for help with `/help`.
| `/locks` | Show currently locked pairs. | `/locks` | Show currently locked pairs.
| `/unlock <pair or lock_id>` | Remove the lock for this pair (or for this lock id). | `/unlock <pair or lock_id>` | Remove the lock for this pair (or for this lock id).
| `/profit [<n>]` | Display a summary of your profit/loss from close trades and some stats about your performance, over the last n days (all trades by default) | `/profit [<n>]` | Display a summary of your profit/loss from close trades and some stats about your performance, over the last n days (all trades by default)
| `/forcesell <trade_id>` | Instantly sells the given trade (Ignoring `minimum_roi`). | `/forceexit <trade_id>` | Instantly exits the given trade (Ignoring `minimum_roi`).
| `/forcesell all` | Instantly sells all open trades (Ignoring `minimum_roi`). | `/forceexit all` | Instantly exits all open trades (Ignoring `minimum_roi`).
| `/forcelong <pair> [rate]` | Instantly buys the given pair. Rate is optional and only applies to limit orders. (`forcebuy_enable` must be set to True) | `/forcelong <pair> [rate]` | Instantly buys the given pair. Rate is optional and only applies to limit orders. (`forcebuy_enable` must be set to True)
| `/forceshort <pair> [rate]` | Instantly shorts the given pair. Rate is optional and only applies to limit orders. This will only work on non-spot markets. (`forcebuy_enable` must be set to True) | `/forceshort <pair> [rate]` | Instantly shorts the given pair. Rate is optional and only applies to limit orders. This will only work on non-spot markets. (`forcebuy_enable` must be set to True)
| `/performance` | Show performance of each finished trade grouped by pair | `/performance` | Show performance of each finished trade grouped by pair

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:
@ -227,5 +230,31 @@ def _validate_time_in_force(conf: Dict[str, Any]) -> None:
"DEPRECATED: Using 'buy' and 'sell' for time_in_force is deprecated." "DEPRECATED: Using 'buy' and 'sell' for time_in_force is deprecated."
"Please migrate your time_in_force settings to use 'entry' and 'exit'." "Please migrate your time_in_force settings to use 'entry' and 'exit'."
) )
time_in_force['entry'] = time_in_force.pop('buy') process_deprecated_setting(
time_in_force['exit'] = time_in_force.pop('sell') conf, 'order_time_in_force', 'buy', 'order_time_in_force', 'entry')
process_deprecated_setting(
conf, 'order_time_in_force', 'sell', 'order_time_in_force', 'exit')
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 order_types 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)

View File

@ -64,6 +64,7 @@ def process_deprecated_setting(config: Dict[str, Any],
section_new_config = config.get(section_new, {}) if section_new else config section_new_config = config.get(section_new, {}) if section_new else config
section_new_config[name_new] = section_old_config[name_old] section_new_config[name_new] = section_old_config[name_old]
del section_old_config[name_old]
def process_temporary_deprecated_settings(config: Dict[str, Any]) -> None: def process_temporary_deprecated_settings(config: Dict[str, Any]) -> None:

View File

@ -20,7 +20,7 @@ DEFAULT_DB_DRYRUN_URL = 'sqlite:///tradesv3.dryrun.sqlite'
UNLIMITED_STAKE_AMOUNT = 'unlimited' UNLIMITED_STAKE_AMOUNT = 'unlimited'
DEFAULT_AMOUNT_RESERVE_PERCENT = 0.05 DEFAULT_AMOUNT_RESERVE_PERCENT = 0.05
REQUIRED_ORDERTIF = ['entry', 'exit'] REQUIRED_ORDERTIF = ['entry', 'exit']
REQUIRED_ORDERTYPES = ['buy', 'sell', 'stoploss', 'stoploss_on_exchange'] REQUIRED_ORDERTYPES = ['entry', 'exit', 'stoploss', 'stoploss_on_exchange']
ORDERBOOK_SIDES = ['ask', 'bid'] ORDERBOOK_SIDES = ['ask', 'bid']
ORDERTYPE_POSSIBILITIES = ['limit', 'market'] ORDERTYPE_POSSIBILITIES = ['limit', 'market']
ORDERTIF_POSSIBILITIES = ['gtc', 'fok', 'ioc'] ORDERTIF_POSSIBILITIES = ['gtc', 'fok', 'ioc']
@ -214,11 +214,11 @@ CONF_SCHEMA = {
'order_types': { 'order_types': {
'type': 'object', 'type': 'object',
'properties': { 'properties': {
'buy': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES}, 'entry': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES},
'sell': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES}, 'exit': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES},
'forcesell': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES}, 'forceexit': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES},
'forcebuy': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES}, 'forceentry': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES},
'emergencysell': { 'emergencyexit': {
'type': 'string', 'type': 'string',
'enum': ORDERTYPE_POSSIBILITIES, 'enum': ORDERTYPE_POSSIBILITIES,
'default': 'market'}, 'default': 'market'},
@ -228,7 +228,7 @@ CONF_SCHEMA = {
'stoploss_on_exchange_limit_ratio': {'type': 'number', 'minimum': 0.0, 'stoploss_on_exchange_limit_ratio': {'type': 'number', 'minimum': 0.0,
'maximum': 1.0} 'maximum': 1.0}
}, },
'required': ['buy', 'sell', 'stoploss', 'stoploss_on_exchange'] 'required': ['entry', 'exit', 'stoploss', 'stoploss_on_exchange']
}, },
'order_time_in_force': { 'order_time_in_force': {
'type': 'object', 'type': 'object',

View File

@ -629,7 +629,7 @@ class FreqtradeBot(LoggingMixin):
f"{stake_amount} ...") f"{stake_amount} ...")
amount = (stake_amount / enter_limit_requested) * leverage amount = (stake_amount / enter_limit_requested) * leverage
order_type = ordertype or self.strategy.order_types['buy'] order_type = ordertype or self.strategy.order_types['entry']
if not pos_adjust and not strategy_safe_wrapper( if not pos_adjust and not strategy_safe_wrapper(
self.strategy.confirm_trade_entry, default_retval=True)( self.strategy.confirm_trade_entry, default_retval=True)(
@ -1155,7 +1155,7 @@ class FreqtradeBot(LoggingMixin):
max_timeouts = self.config.get( max_timeouts = self.config.get(
'unfilledtimeout', {}).get('exit_timeout_count', 0) 'unfilledtimeout', {}).get('exit_timeout_count', 0)
if canceled and max_timeouts > 0 and canceled_count >= max_timeouts: if canceled and max_timeouts > 0 and canceled_count >= max_timeouts:
logger.warning(f'Emergencyselling trade {trade}, as the sell order ' logger.warning(f'Emergency exiting trade {trade}, as the exit order '
f'timed out {max_timeouts} times.') f'timed out {max_timeouts} times.')
try: try:
self.execute_trade_exit( self.execute_trade_exit(
@ -1248,11 +1248,11 @@ class FreqtradeBot(LoggingMixin):
self.update_trade_state(trade, trade.open_order_id, corder) self.update_trade_state(trade, trade.open_order_id, corder)
trade.open_order_id = None trade.open_order_id = None
logger.info('Partial %s order timeout for %s.', trade.enter_side, trade) logger.info(f'Partial {trade.enter_side} order timeout for {trade}.')
reason += f", {constants.CANCEL_REASON['PARTIALLY_FILLED']}" reason += f", {constants.CANCEL_REASON['PARTIALLY_FILLED']}"
self.wallets.update() self.wallets.update()
self._notify_enter_cancel(trade, order_type=self.strategy.order_types[trade.enter_side], self._notify_enter_cancel(trade, order_type=self.strategy.order_types['entry'],
reason=reason) reason=reason)
return was_trade_fully_canceled return was_trade_fully_canceled
@ -1297,7 +1297,7 @@ class FreqtradeBot(LoggingMixin):
self.wallets.update() self.wallets.update()
self._notify_exit_cancel( self._notify_exit_cancel(
trade, trade,
order_type=self.strategy.order_types[trade.exit_side], order_type=self.strategy.order_types['exit'],
reason=reason reason=reason
) )
return cancelled return cancelled
@ -1353,7 +1353,7 @@ class FreqtradeBot(LoggingMixin):
is_short=trade.is_short, is_short=trade.is_short,
open_date=trade.open_date, open_date=trade.open_date,
) )
exit_type = 'sell' exit_type = 'exit'
if sell_reason.sell_type in (SellType.STOP_LOSS, SellType.TRAILING_STOP_LOSS): if sell_reason.sell_type in (SellType.STOP_LOSS, SellType.TRAILING_STOP_LOSS):
exit_type = 'stoploss' exit_type = 'stoploss'
@ -1380,7 +1380,7 @@ class FreqtradeBot(LoggingMixin):
order_type = ordertype or self.strategy.order_types[exit_type] order_type = ordertype or self.strategy.order_types[exit_type]
if sell_reason.sell_type == SellType.EMERGENCY_SELL: if sell_reason.sell_type == SellType.EMERGENCY_SELL:
# Emergency sells (default to market!) # Emergency sells (default to market!)
order_type = self.strategy.order_types.get("emergencysell", "market") order_type = self.strategy.order_types.get("emergencyexit", "market")
amount = self._safe_exit_amount(trade.pair, trade.amount) amount = self._safe_exit_amount(trade.pair, trade.amount)
time_in_force = self.strategy.order_time_in_force['exit'] time_in_force = self.strategy.order_time_in_force['exit']

View File

@ -490,7 +490,7 @@ class Backtesting:
return None return None
# call the custom exit price,with default value as previous closerate # call the custom exit price,with default value as previous closerate
current_profit = trade.calc_profit_ratio(closerate) current_profit = trade.calc_profit_ratio(closerate)
order_type = self.strategy.order_types['sell'] order_type = self.strategy.order_types['exit']
if sell.sell_type in (SellType.SELL_SIGNAL, SellType.CUSTOM_SELL): if sell.sell_type in (SellType.SELL_SIGNAL, SellType.CUSTOM_SELL):
# Custom exit pricing only for sell-signals # Custom exit pricing only for sell-signals
if order_type == 'limit': if order_type == 'limit':
@ -598,7 +598,7 @@ class Backtesting:
current_time = row[DATE_IDX].to_pydatetime() current_time = row[DATE_IDX].to_pydatetime()
entry_tag = row[ENTER_TAG_IDX] if len(row) >= ENTER_TAG_IDX + 1 else None entry_tag = row[ENTER_TAG_IDX] if len(row) >= ENTER_TAG_IDX + 1 else None
# let's call the custom entry price, using the open price as default price # let's call the custom entry price, using the open price as default price
order_type = self.strategy.order_types['buy'] order_type = self.strategy.order_types['entry']
propose_rate = row[OPEN_IDX] propose_rate = row[OPEN_IDX]
if order_type == 'limit': if order_type == 'limit':
propose_rate = strategy_safe_wrapper(self.strategy.custom_entry_price, propose_rate = strategy_safe_wrapper(self.strategy.custom_entry_price,
@ -638,7 +638,7 @@ class Backtesting:
# In case of pos adjust, still return the original trade # In case of pos adjust, still return the original trade
# If not pos adjust, trade is None # If not pos adjust, trade is None
return trade return trade
order_type = self.strategy.order_types['buy'] order_type = self.strategy.order_types['entry']
time_in_force = self.strategy.order_time_in_force['entry'] time_in_force = self.strategy.order_time_in_force['entry']
if not pos_adjust: if not pos_adjust:

View File

@ -138,11 +138,11 @@ class UnfilledTimeout(BaseModel):
class OrderTypes(BaseModel): class OrderTypes(BaseModel):
buy: OrderTypeValues entry: OrderTypeValues
sell: OrderTypeValues exit: OrderTypeValues
emergencysell: Optional[OrderTypeValues] emergencyexit: Optional[OrderTypeValues]
forcesell: Optional[OrderTypeValues] forceexit: Optional[OrderTypeValues]
forcebuy: Optional[OrderTypeValues] forceentry: Optional[OrderTypeValues]
stoploss: OrderTypeValues stoploss: OrderTypeValues
stoploss_on_exchange: bool stoploss_on_exchange: bool
stoploss_on_exchange_interval: Optional[int] stoploss_on_exchange_interval: Optional[int]

View File

@ -712,7 +712,7 @@ class RPC:
trade.pair, refresh=False, side=trade.exit_side) trade.pair, refresh=False, side=trade.exit_side)
sell_reason = SellCheckTuple(sell_type=SellType.FORCE_SELL) sell_reason = SellCheckTuple(sell_type=SellType.FORCE_SELL)
order_type = ordertype or self._freqtrade.strategy.order_types.get( order_type = ordertype or self._freqtrade.strategy.order_types.get(
"forcesell", self._freqtrade.strategy.order_types["sell"]) "forceexit", self._freqtrade.strategy.order_types["exit"])
self._freqtrade.execute_trade_exit( self._freqtrade.execute_trade_exit(
trade, current_rate, sell_reason, ordertype=order_type) trade, current_rate, sell_reason, ordertype=order_type)
@ -735,7 +735,7 @@ class RPC:
trade_filter=[Trade.id == trade_id, Trade.is_open.is_(True), ] trade_filter=[Trade.id == trade_id, Trade.is_open.is_(True), ]
).first() ).first()
if not trade: if not trade:
logger.warning('forcesell: Invalid argument received') logger.warning('forceexit: Invalid argument received')
raise RPCException('invalid argument') raise RPCException('invalid argument')
_exec_forcesell(trade) _exec_forcesell(trade)
@ -784,7 +784,7 @@ class RPC:
# execute buy # execute buy
if not order_type: if not order_type:
order_type = self._freqtrade.strategy.order_types.get( order_type = self._freqtrade.strategy.order_types.get(
'forcebuy', self._freqtrade.strategy.order_types['buy']) 'forceentry', self._freqtrade.strategy.order_types['entry'])
if self._freqtrade.execute_entry(pair, stake_amount, price, if self._freqtrade.execute_entry(pair, stake_amount, price,
ordertype=order_type, trade=trade, ordertype=order_type, trade=trade,
is_short=is_short, is_short=is_short,

View File

@ -944,7 +944,7 @@ class Telegram(RPCHandler):
return return
try: try:
msg = self._rpc._rpc_forceexit(trade_id) msg = self._rpc._rpc_forceexit(trade_id)
self._send_msg('Forcesell Result: `{result}`'.format(**msg)) self._send_msg('Forceexit Result: `{result}`'.format(**msg))
except RPCException as e: except RPCException as e:
self._send_msg(str(e)) self._send_msg(str(e))

View File

@ -90,8 +90,8 @@ class IStrategy(ABC, HyperStrategyMixin):
# Optional order types # Optional order types
order_types: Dict = { order_types: Dict = {
'buy': 'limit', 'entry': 'limit',
'sell': 'limit', 'exit': 'limit',
'stoploss': 'limit', 'stoploss': 'limit',
'stoploss_on_exchange': False, 'stoploss_on_exchange': False,
'stoploss_on_exchange_interval': 60, 'stoploss_on_exchange_interval': 60,

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
"order_types": { "order_types": {
"buy": "limit", "entry": "limit",
"sell": "limit", "exit": "limit",
"emergencysell": "limit", "emergencyexit": "limit",
"stoploss": "limit", "stoploss": "limit",
"stoploss_on_exchange": false "stoploss_on_exchange": false
}, },

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

@ -17,8 +17,8 @@ def test_validate_order_types_gateio(default_conf, mocker):
assert isinstance(exch, Gateio) assert isinstance(exch, Gateio)
default_conf['order_types'] = { default_conf['order_types'] = {
'buy': 'market', 'entry': 'market',
'sell': 'limit', 'exit': 'limit',
'stoploss': 'market', 'stoploss': 'market',
'stoploss_on_exchange': False 'stoploss_on_exchange': False
} }

View File

@ -32,14 +32,14 @@ from tests.conftest import (CURRENT_TEST_STRATEGY, get_args, log_has, log_has_re
ORDER_TYPES = [ ORDER_TYPES = [
{ {
'buy': 'limit', 'entry': 'limit',
'sell': 'limit', 'exit': 'limit',
'stoploss': 'limit', 'stoploss': 'limit',
'stoploss_on_exchange': False 'stoploss_on_exchange': False
}, },
{ {
'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

@ -239,8 +239,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,
} }
@ -251,16 +251,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'
@ -963,6 +963,41 @@ def test_validate_time_in_force(default_conf, caplog) -> None:
validate_config_consistency(conf) validate_config_consistency(conf)
def test_validate_order_types(default_conf, caplog) -> None:
conf = deepcopy(default_conf)
conf['order_types'] = {
'buy': 'limit',
'sell': 'market',
'forcesell': 'market',
'forcebuy': 'limit',
'stoploss': 'market',
'stoploss_on_exchange': False,
}
validate_config_consistency(conf)
assert log_has_re(r"DEPRECATED: Using 'buy' and 'sell' for order_types is.*", caplog)
assert conf['order_types']['entry'] == 'limit'
assert conf['order_types']['exit'] == 'market'
assert conf['order_types']['forceentry'] == 'limit'
assert 'buy' not in conf['order_types']
assert 'sell' not in conf['order_types']
assert 'forcebuy' not in conf['order_types']
assert 'forcesell' not in conf['order_types']
conf = deepcopy(default_conf)
conf['order_types'] = {
'buy': 'limit',
'sell': 'market',
'forcesell': 'market',
'forcebuy': 'limit',
'stoploss': 'market',
'stoploss_on_exchange': False,
}
conf['trading_mode'] = 'futures'
with pytest.raises(OperationalException,
match=r"Please migrate your order_types settings to use the new wording\."):
validate_config_consistency(conf)
def test_load_config_test_comments() -> None: def test_load_config_test_comments() -> None:
""" """
Load config with comments Load config with comments
@ -1280,11 +1315,14 @@ def test_process_deprecated_setting(mocker, default_conf, caplog):
# The value of the new setting shall have been set to the # The value of the new setting shall have been set to the
# value of the deprecated one # value of the deprecated one
assert default_conf['sectionA']['new_setting'] == 'valB' assert default_conf['sectionA']['new_setting'] == 'valB'
# Old setting is removed
assert 'deprecated_setting' not in default_conf['sectionB']
caplog.clear() caplog.clear()
# Delete new setting (deprecated exists) # Delete new setting (deprecated exists)
del default_conf['sectionA']['new_setting'] del default_conf['sectionA']['new_setting']
default_conf['sectionB']['deprecated_setting'] = 'valB'
process_deprecated_setting(default_conf, process_deprecated_setting(default_conf,
'sectionB', 'deprecated_setting', 'sectionB', 'deprecated_setting',
'sectionA', 'new_setting') 'sectionA', 'new_setting')
@ -1298,7 +1336,7 @@ def test_process_deprecated_setting(mocker, default_conf, caplog):
# Assign new setting # Assign new setting
default_conf['sectionA']['new_setting'] = 'valA' default_conf['sectionA']['new_setting'] = 'valA'
# Delete deprecated setting # Delete deprecated setting
del default_conf['sectionB']['deprecated_setting'] default_conf['sectionB'].pop('deprecated_setting', None)
process_deprecated_setting(default_conf, process_deprecated_setting(default_conf,
'sectionB', 'deprecated_setting', 'sectionB', 'deprecated_setting',
'sectionA', 'new_setting') 'sectionA', 'new_setting')

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,
} }
@ -2578,7 +2578,7 @@ def test_check_handle_timedout_sell_usercustom(
assert et_mock.call_count == 0 assert et_mock.call_count == 0
freqtrade.check_handle_timedout() freqtrade.check_handle_timedout()
assert log_has_re('Emergencyselling trade.*', caplog) assert log_has_re('Emergency exiting trade.*', caplog)
assert et_mock.call_count == 1 assert et_mock.call_count == 1
@ -3517,7 +3517,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