diff --git a/README.md b/README.md index efa334a27..02eb47e00 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,7 @@ Telegram is not mandatory. However, this is a great way to control your bot. Mor - `/stopbuy`: Stop entering new trades. - `/status |[table]`: Lists all or specific open trades. - `/profit []`: Lists cumulative profit from all finished trades, over the last n days. -- `/forcesell |all`: Instantly sells the given trade (Ignoring `minimum_roi`). +- `/forceexit |all`: Instantly exits the given trade (Ignoring `minimum_roi`). - `/performance`: Show performance of each finished trade grouped by pair - `/balance`: Show account balance per currency. - `/daily `: Shows profit or loss per day, over the last n days. diff --git a/config_examples/config_full.example.json b/config_examples/config_full.example.json index f48086975..ce16e021f 100644 --- a/config_examples/config_full.example.json +++ b/config_examples/config_full.example.json @@ -51,11 +51,11 @@ "order_book_top": 1 }, "order_types": { - "buy": "limit", - "sell": "limit", - "emergencysell": "market", - "forcesell": "market", - "forcebuy": "market", + "entry": "limit", + "exit": "limit", + "emergencyexit": "market", + "forceexit": "market", + "forceentry": "market", "stoploss": "market", "stoploss_on_exchange": false, "stoploss_on_exchange_interval": 60, diff --git a/docs/configuration.md b/docs/configuration.md index 99c13ca5a..9610b5866 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -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).
*Defaults to `0.0`.*
**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).
*Defaults to `false`.*
**Datatype:** Boolean | `ignore_buying_expired_candle_after` | Specifies the number of seconds until a buy signal is no longer used.
**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).
**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).
**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).
**Datatype:** Dict | `custom_price_max_distance_ratio` | Configure maximum distance ratio between current and custom entry or exit price.
*Defaults to `0.02` 2%).*
**Datatype:** Positive float | `exchange.name` | **Required.** Name of the exchange class to use. [List below](#user-content-what-values-for-exchangename).
**Datatype:** String @@ -374,29 +374,28 @@ For example, if your strategy is using a 1h timeframe, and you only want to buy ### 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 limit-orders, and create stoplosses using market orders. It also allows to set the stoploss "on exchange" which means stoploss order would be placed immediately once 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. -If this is configured, the following 4 values (`buy`, `sell`, `stoploss` and -`stoploss_on_exchange`) need to be present, otherwise, the bot will fail to start. +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. -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: ```python order_types = { - "buy": "limit", - "sell": "limit", - "emergencysell": "market", - "forcebuy": "market", - "forcesell": "market", + "entry": "limit", + "exit": "limit", + "emergencyexit": "market", + "forceentry": "market", + "forceexit": "market", "stoploss": "market", "stoploss_on_exchange": False, "stoploss_on_exchange_interval": 60, @@ -408,11 +407,11 @@ Configuration: ```json "order_types": { - "buy": "limit", - "sell": "limit", - "emergencysell": "market", - "forcebuy": "market", - "forcesell": "market", + "entry": "limit", + "exit": "limit", + "emergencyexit": "market", + "forceentry": "market", + "forceexit": "market", "stoploss": "market", "stoploss_on_exchange": false, "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. !!! 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 diff --git a/docs/faq.md b/docs/faq.md index 27bc077ec..147e850ac 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -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? -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 @@ -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": -``` +``` python order_types = { ... - 'stoploss': 'limit', + "stoploss": "limit", ... } ``` diff --git a/docs/includes/pricing.md b/docs/includes/pricing.md index ed8a45e68..f495be68f 100644 --- a/docs/includes/pricing.md +++ b/docs/includes/pricing.md @@ -101,8 +101,8 @@ Assuming both buy and sell are using market orders, a configuration similar to t ``` jsonc "order_types": { - "buy": "market", - "sell": "market" + "entry": "market", + "exit": "market" // ... }, "bid_strategy": { diff --git a/docs/rest-api.md b/docs/rest-api.md index 5a6b1b7a0..25e7ee205 100644 --- a/docs/rest-api.md +++ b/docs/rest-api.md @@ -145,9 +145,9 @@ python3 scripts/rest_client.py --config rest_config.json [optional par | `locks` | Displays currently locked pairs. | `delete_lock ` | Deletes (disables) the lock by id. | `profit` | Display a summary of your profit/loss from close trades and some stats about your performance. -| `forcesell ` | Instantly sells the given trade (Ignoring `minimum_roi`). -| `forcesell all` | Instantly sells all open trades (Ignoring `minimum_roi`). -| `forcebuy [rate]` | Instantly buys the given pair. Rate is optional. (`forcebuy_enable` must be set to True) +| `forceexit ` | Instantly exits the given trade (Ignoring `minimum_roi`). +| `forceexit all` | Instantly exits all open trades (Ignoring `minimum_roi`). +| `forceenter [rate]` | Instantly enters the given pair. Rate is optional. (`forcebuy_enable` must be set to True) | `forceenter [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. | `balance` | Show account balance per currency. diff --git a/docs/sandbox-testing.md b/docs/sandbox-testing.md index 5f572eba8..94a25b35f 100644 --- a/docs/sandbox-testing.md +++ b/docs/sandbox-testing.md @@ -104,8 +104,8 @@ To mitigate this, you can try to match the first order on the opposite orderbook ``` jsonc "order_types": { - "buy": "limit", - "sell": "limit" + "entry": "limit", + "exit": "limit" // ... }, "bid_strategy": { diff --git a/docs/sql_cheatsheet.md b/docs/sql_cheatsheet.md index caa3f53a6..0e2abc239 100644 --- a/docs/sql_cheatsheet.md +++ b/docs/sql_cheatsheet.md @@ -49,14 +49,14 @@ sqlite3 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 - Manually selling a pair on the exchange will not be detected by the bot and it will try to sell anyway. Whenever possible, forcesell 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 should be used to accomplish the same thing. It is strongly advised to backup your database file before making any manual changes. !!! 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 UPDATE trades diff --git a/docs/stoploss.md b/docs/stoploss.md index d0e106d8f..631357e52 100644 --- a/docs/stoploss.md +++ b/docs/stoploss.md @@ -17,7 +17,7 @@ Those stoploss modes can be *on exchange* or *off exchange*. These modes can be configured with these values: ``` python - 'emergencysell': 'market', + 'emergencyexit': 'market', 'stoploss_on_exchange': False 'stoploss_on_exchange_interval': 60, '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). 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. Example from strategy file: ``` python order_types = { - 'buy': 'limit', - 'sell': 'limit', - 'emergencysell': 'market', - 'stoploss': 'market', - 'stoploss_on_exchange': True, - 'stoploss_on_exchange_interval': 60, - 'stoploss_on_exchange_limit_ratio': 0.99 + "entry": "limit", + "exit": "limit", + "emergencyexit": "market", + "stoploss": "market", + "stoploss_on_exchange": True, + "stoploss_on_exchange_interval": 60, + "stoploss_on_exchange_limit_ratio": 0.99 } ``` diff --git a/docs/telegram-usage.md b/docs/telegram-usage.md index ebdd062ee..a09579889 100644 --- a/docs/telegram-usage.md +++ b/docs/telegram-usage.md @@ -171,8 +171,8 @@ official commands. You can ask at any moment for help with `/help`. | `/locks` | Show currently locked pairs. | `/unlock ` | Remove the lock for this pair (or for this lock id). | `/profit []` | 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 ` | Instantly sells the given trade (Ignoring `minimum_roi`). -| `/forcesell all` | Instantly sells all open trades (Ignoring `minimum_roi`). +| `/forceexit ` | Instantly exits the given trade (Ignoring `minimum_roi`). +| `/forceexit all` | Instantly exits all open trades (Ignoring `minimum_roi`). | `/forcelong [rate]` | Instantly buys the given pair. Rate is optional and only applies to limit orders. (`forcebuy_enable` must be set to True) | `/forceshort [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 diff --git a/freqtrade/configuration/config_validation.py b/freqtrade/configuration/config_validation.py index 87a309f12..267509b43 100644 --- a/freqtrade/configuration/config_validation.py +++ b/freqtrade/configuration/config_validation.py @@ -6,6 +6,7 @@ from jsonschema import Draft4Validator, validators from jsonschema.exceptions import ValidationError, best_match from freqtrade import constants +from freqtrade.configuration.deprecated_settings import process_deprecated_setting from freqtrade.enums import RunMode, TradingMode 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 """ - 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'): 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'): 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: _validate_time_in_force(conf) + _validate_order_types(conf) 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." "Please migrate your time_in_force settings to use 'entry' and 'exit'." ) - time_in_force['entry'] = time_in_force.pop('buy') - time_in_force['exit'] = time_in_force.pop('sell') + process_deprecated_setting( + 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) diff --git a/freqtrade/configuration/deprecated_settings.py b/freqtrade/configuration/deprecated_settings.py index 5efe26bd2..1257baa37 100644 --- a/freqtrade/configuration/deprecated_settings.py +++ b/freqtrade/configuration/deprecated_settings.py @@ -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[name_new] = section_old_config[name_old] + del section_old_config[name_old] def process_temporary_deprecated_settings(config: Dict[str, Any]) -> None: diff --git a/freqtrade/constants.py b/freqtrade/constants.py index bafda93db..fabac5830 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -20,7 +20,7 @@ DEFAULT_DB_DRYRUN_URL = 'sqlite:///tradesv3.dryrun.sqlite' UNLIMITED_STAKE_AMOUNT = 'unlimited' DEFAULT_AMOUNT_RESERVE_PERCENT = 0.05 REQUIRED_ORDERTIF = ['entry', 'exit'] -REQUIRED_ORDERTYPES = ['buy', 'sell', 'stoploss', 'stoploss_on_exchange'] +REQUIRED_ORDERTYPES = ['entry', 'exit', 'stoploss', 'stoploss_on_exchange'] ORDERBOOK_SIDES = ['ask', 'bid'] ORDERTYPE_POSSIBILITIES = ['limit', 'market'] ORDERTIF_POSSIBILITIES = ['gtc', 'fok', 'ioc'] @@ -214,11 +214,11 @@ CONF_SCHEMA = { 'order_types': { 'type': 'object', 'properties': { - 'buy': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES}, - 'sell': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES}, - 'forcesell': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES}, - 'forcebuy': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES}, - 'emergencysell': { + 'entry': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES}, + 'exit': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES}, + 'forceexit': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES}, + 'forceentry': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES}, + 'emergencyexit': { 'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES, 'default': 'market'}, @@ -228,7 +228,7 @@ CONF_SCHEMA = { 'stoploss_on_exchange_limit_ratio': {'type': 'number', 'minimum': 0.0, 'maximum': 1.0} }, - 'required': ['buy', 'sell', 'stoploss', 'stoploss_on_exchange'] + 'required': ['entry', 'exit', 'stoploss', 'stoploss_on_exchange'] }, 'order_time_in_force': { 'type': 'object', diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index cbc6be11a..23804bfec 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -629,7 +629,7 @@ class FreqtradeBot(LoggingMixin): f"{stake_amount} ...") 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( self.strategy.confirm_trade_entry, default_retval=True)( @@ -1155,7 +1155,7 @@ class FreqtradeBot(LoggingMixin): max_timeouts = self.config.get( 'unfilledtimeout', {}).get('exit_timeout_count', 0) 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.') try: self.execute_trade_exit( @@ -1248,11 +1248,11 @@ class FreqtradeBot(LoggingMixin): self.update_trade_state(trade, trade.open_order_id, corder) 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']}" 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) return was_trade_fully_canceled @@ -1297,7 +1297,7 @@ class FreqtradeBot(LoggingMixin): self.wallets.update() self._notify_exit_cancel( trade, - order_type=self.strategy.order_types[trade.exit_side], + order_type=self.strategy.order_types['exit'], reason=reason ) return cancelled @@ -1353,7 +1353,7 @@ class FreqtradeBot(LoggingMixin): is_short=trade.is_short, open_date=trade.open_date, ) - exit_type = 'sell' + exit_type = 'exit' if sell_reason.sell_type in (SellType.STOP_LOSS, SellType.TRAILING_STOP_LOSS): exit_type = 'stoploss' @@ -1380,7 +1380,7 @@ class FreqtradeBot(LoggingMixin): order_type = ordertype or self.strategy.order_types[exit_type] if sell_reason.sell_type == SellType.EMERGENCY_SELL: # 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) time_in_force = self.strategy.order_time_in_force['exit'] diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 8e354b2fc..33d7578cf 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -490,7 +490,7 @@ class Backtesting: return None # call the custom exit price,with default value as previous 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): # Custom exit pricing only for sell-signals if order_type == 'limit': @@ -598,7 +598,7 @@ class Backtesting: current_time = row[DATE_IDX].to_pydatetime() 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 - order_type = self.strategy.order_types['buy'] + order_type = self.strategy.order_types['entry'] propose_rate = row[OPEN_IDX] if order_type == 'limit': 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 # If not pos adjust, trade is None 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'] if not pos_adjust: diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index 548362bf5..b6d175c0f 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -138,11 +138,11 @@ class UnfilledTimeout(BaseModel): class OrderTypes(BaseModel): - buy: OrderTypeValues - sell: OrderTypeValues - emergencysell: Optional[OrderTypeValues] - forcesell: Optional[OrderTypeValues] - forcebuy: Optional[OrderTypeValues] + entry: OrderTypeValues + exit: OrderTypeValues + emergencyexit: Optional[OrderTypeValues] + forceexit: Optional[OrderTypeValues] + forceentry: Optional[OrderTypeValues] stoploss: OrderTypeValues stoploss_on_exchange: bool stoploss_on_exchange_interval: Optional[int] diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index f47bc2668..392d77320 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -712,7 +712,7 @@ class RPC: trade.pair, refresh=False, side=trade.exit_side) sell_reason = SellCheckTuple(sell_type=SellType.FORCE_SELL) 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( 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), ] ).first() if not trade: - logger.warning('forcesell: Invalid argument received') + logger.warning('forceexit: Invalid argument received') raise RPCException('invalid argument') _exec_forcesell(trade) @@ -784,7 +784,7 @@ class RPC: # execute buy if not order_type: 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, ordertype=order_type, trade=trade, is_short=is_short, diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 90a602ef8..29f63215d 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -944,7 +944,7 @@ class Telegram(RPCHandler): return try: 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: self._send_msg(str(e)) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index bec89131b..17233a027 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -90,8 +90,8 @@ class IStrategy(ABC, HyperStrategyMixin): # Optional order types order_types: Dict = { - 'buy': 'limit', - 'sell': 'limit', + 'entry': 'limit', + 'exit': 'limit', 'stoploss': 'limit', 'stoploss_on_exchange': False, 'stoploss_on_exchange_interval': 60, diff --git a/freqtrade/templates/base_strategy.py.j2 b/freqtrade/templates/base_strategy.py.j2 index 0a20eaf2a..883444a50 100644 --- a/freqtrade/templates/base_strategy.py.j2 +++ b/freqtrade/templates/base_strategy.py.j2 @@ -78,8 +78,8 @@ class {{ strategy }}(IStrategy): # Optional order type mapping. order_types = { - 'buy': 'limit', - 'sell': 'limit', + 'entry': 'limit', + 'exit': 'limit', 'stoploss': 'market', 'stoploss_on_exchange': False } diff --git a/freqtrade/templates/sample_short_strategy.py b/freqtrade/templates/sample_short_strategy.py index 535c2222d..2b099ee6a 100644 --- a/freqtrade/templates/sample_short_strategy.py +++ b/freqtrade/templates/sample_short_strategy.py @@ -79,8 +79,8 @@ class SampleShortStrategy(IStrategy): # Optional order type mapping. order_types = { - 'buy': 'limit', - 'sell': 'limit', + 'entry': 'limit', + 'exit': 'limit', 'stoploss': 'market', 'stoploss_on_exchange': False } diff --git a/freqtrade/templates/sample_strategy.py b/freqtrade/templates/sample_strategy.py index 3da92fa0b..69da8b414 100644 --- a/freqtrade/templates/sample_strategy.py +++ b/freqtrade/templates/sample_strategy.py @@ -80,8 +80,8 @@ class SampleStrategy(IStrategy): # Optional order type mapping. order_types = { - 'buy': 'limit', - 'sell': 'limit', + 'entry': 'limit', + 'exit': 'limit', 'stoploss': 'market', 'stoploss_on_exchange': False } diff --git a/freqtrade/templates/subtemplates/exchange_bittrex.j2 b/freqtrade/templates/subtemplates/exchange_bittrex.j2 index 0394790ce..2d9afd578 100644 --- a/freqtrade/templates/subtemplates/exchange_bittrex.j2 +++ b/freqtrade/templates/subtemplates/exchange_bittrex.j2 @@ -1,7 +1,7 @@ "order_types": { - "buy": "limit", - "sell": "limit", - "emergencysell": "limit", + "entry": "limit", + "exit": "limit", + "emergencyexit": "limit", "stoploss": "limit", "stoploss_on_exchange": false }, diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 1f6be2860..bb2408b5c 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -951,8 +951,8 @@ def test_validate_order_types(default_conf, mocker): mocker.patch('freqtrade.exchange.Exchange.name', 'Bittrex') default_conf['order_types'] = { - 'buy': 'limit', - 'sell': 'limit', + 'entry': 'limit', + 'exit': 'limit', 'stoploss': 'market', '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)) default_conf['order_types'] = { - 'buy': 'limit', - 'sell': 'limit', + 'entry': 'limit', + 'exit': 'limit', 'stoploss': 'market', 'stoploss_on_exchange': False } @@ -972,8 +972,8 @@ def test_validate_order_types(default_conf, mocker): Exchange(default_conf) default_conf['order_types'] = { - 'buy': 'limit', - 'sell': 'limit', + 'entry': 'limit', + 'exit': 'limit', 'stoploss': 'limit', 'stoploss_on_exchange': True } diff --git a/tests/exchange/test_gateio.py b/tests/exchange/test_gateio.py index 6f7862909..3ecce96aa 100644 --- a/tests/exchange/test_gateio.py +++ b/tests/exchange/test_gateio.py @@ -17,8 +17,8 @@ def test_validate_order_types_gateio(default_conf, mocker): assert isinstance(exch, Gateio) default_conf['order_types'] = { - 'buy': 'market', - 'sell': 'limit', + 'entry': 'market', + 'exit': 'limit', 'stoploss': 'market', 'stoploss_on_exchange': False } diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 1b11bf0da..0d15c23e8 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -32,14 +32,14 @@ from tests.conftest import (CURRENT_TEST_STRATEGY, get_args, log_has, log_has_re ORDER_TYPES = [ { - 'buy': 'limit', - 'sell': 'limit', + 'entry': 'limit', + 'exit': 'limit', 'stoploss': 'limit', 'stoploss_on_exchange': False }, { - 'buy': 'limit', - 'sell': 'limit', + 'entry': 'limit', + 'exit': 'limit', 'stoploss': 'limit', 'stoploss_on_exchange': True }] diff --git a/tests/strategy/strats/hyperoptable_strategy.py b/tests/strategy/strats/hyperoptable_strategy.py index e843f6b58..33734f241 100644 --- a/tests/strategy/strats/hyperoptable_strategy.py +++ b/tests/strategy/strats/hyperoptable_strategy.py @@ -34,8 +34,8 @@ class HyperoptableStrategy(IStrategy): # Optional order type mapping order_types = { - 'buy': 'limit', - 'sell': 'limit', + 'entry': 'limit', + 'exit': 'limit', 'stoploss': 'limit', 'stoploss_on_exchange': False } diff --git a/tests/strategy/strats/strategy_test_v2.py b/tests/strategy/strats/strategy_test_v2.py index fd70cf346..a9ca7d9e2 100644 --- a/tests/strategy/strats/strategy_test_v2.py +++ b/tests/strategy/strats/strategy_test_v2.py @@ -36,8 +36,8 @@ class StrategyTestV2(IStrategy): # Optional order type mapping order_types = { - 'buy': 'limit', - 'sell': 'limit', + 'entry': 'limit', + 'exit': 'limit', 'stoploss': 'limit', 'stoploss_on_exchange': False } diff --git a/tests/strategy/strats/strategy_test_v3.py b/tests/strategy/strats/strategy_test_v3.py index ee3ce5773..7b2c7a99f 100644 --- a/tests/strategy/strats/strategy_test_v3.py +++ b/tests/strategy/strats/strategy_test_v3.py @@ -37,8 +37,8 @@ class StrategyTestV3(IStrategy): # Optional order type mapping order_types = { - 'buy': 'limit', - 'sell': 'limit', + 'entry': 'limit', + 'exit': 'limit', 'stoploss': 'limit', 'stoploss_on_exchange': False } diff --git a/tests/strategy/test_strategy_loading.py b/tests/strategy/test_strategy_loading.py index b8fe90e23..1de821146 100644 --- a/tests/strategy/test_strategy_loading.py +++ b/tests/strategy/test_strategy_loading.py @@ -239,8 +239,8 @@ def test_strategy_override_order_types(caplog, default_conf): caplog.set_level(logging.INFO) order_types = { - 'buy': 'market', - 'sell': 'limit', + 'entry': 'market', + 'exit': 'limit', 'stoploss': 'limit', 'stoploss_on_exchange': True, } @@ -251,16 +251,16 @@ def test_strategy_override_order_types(caplog, default_conf): strategy = StrategyResolver.load_strategy(default_conf) 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 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) default_conf.update({ 'strategy': CURRENT_TEST_STRATEGY, - 'order_types': {'buy': 'market'} + 'order_types': {'exit': 'market'} }) # Raise error for invalid configuration with pytest.raises(ImportError, diff --git a/tests/test_configuration.py b/tests/test_configuration.py index e2ab3c9b5..2c1141940 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -798,8 +798,8 @@ def test_validate_max_open_trades(default_conf): def test_validate_price_side(default_conf): default_conf['order_types'] = { - "buy": "limit", - "sell": "limit", + "entry": "limit", + "exit": "limit", "stoploss": "limit", "stoploss_on_exchange": False, } @@ -807,21 +807,21 @@ def test_validate_price_side(default_conf): validate_config_consistency(default_conf) conf = deepcopy(default_conf) - conf['order_types']['buy'] = 'market' + conf['order_types']['entry'] = 'market' with pytest.raises(OperationalException, match='Market buy orders require bid_strategy.price_side = "ask".'): validate_config_consistency(conf) conf = deepcopy(default_conf) - conf['order_types']['sell'] = 'market' + conf['order_types']['exit'] = 'market' with pytest.raises(OperationalException, match='Market sell orders require ask_strategy.price_side = "bid".'): validate_config_consistency(conf) # Validate inversed case conf = deepcopy(default_conf) - conf['order_types']['sell'] = 'market' - conf['order_types']['buy'] = 'market' + conf['order_types']['exit'] = 'market' + conf['order_types']['entry'] = 'market' conf['ask_strategy']['price_side'] = 'bid' conf['bid_strategy']['price_side'] = 'ask' @@ -963,6 +963,41 @@ def test_validate_time_in_force(default_conf, caplog) -> None: 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: """ 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 # value of the deprecated one assert default_conf['sectionA']['new_setting'] == 'valB' + # Old setting is removed + assert 'deprecated_setting' not in default_conf['sectionB'] caplog.clear() # Delete new setting (deprecated exists) del default_conf['sectionA']['new_setting'] + default_conf['sectionB']['deprecated_setting'] = 'valB' process_deprecated_setting(default_conf, 'sectionB', 'deprecated_setting', 'sectionA', 'new_setting') @@ -1298,7 +1336,7 @@ def test_process_deprecated_setting(mocker, default_conf, caplog): # Assign new setting default_conf['sectionA']['new_setting'] = 'valA' # Delete deprecated setting - del default_conf['sectionB']['deprecated_setting'] + default_conf['sectionB'].pop('deprecated_setting', None) process_deprecated_setting(default_conf, 'sectionB', 'deprecated_setting', 'sectionA', 'new_setting') diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index d115eddc9..85c105672 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -91,8 +91,8 @@ def test_order_dict(default_conf_usdt, mocker, runmode, caplog) -> None: conf = default_conf_usdt.copy() conf['runmode'] = runmode conf['order_types'] = { - 'buy': 'market', - 'sell': 'limit', + 'entry': 'market', + 'exit': 'limit', 'stoploss': 'limit', '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['runmode'] = runmode conf['order_types'] = { - 'buy': 'market', - 'sell': 'limit', + 'entry': 'market', + 'exit': 'limit', 'stoploss': 'limit', 'stoploss_on_exchange': False, } @@ -2578,7 +2578,7 @@ def test_check_handle_timedout_sell_usercustom( assert et_mock.call_count == 0 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 @@ -3517,7 +3517,7 @@ def test_execute_trade_exit_market_order( 'freqtrade.exchange.Exchange', fetch_ticker=ticker_usdt_sell_up ) - freqtrade.config['order_types']['sell'] = 'market' + freqtrade.config['order_types']['exit'] = 'market' freqtrade.execute_trade_exit( trade=trade, diff --git a/tests/test_integration.py b/tests/test_integration.py index 70ee1c52c..9115b431b 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -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.strategy.order_types['stoploss_on_exchange'] = True # 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_exit = MagicMock(return_value=True) patch_get_signal(freqtrade) @@ -173,7 +173,7 @@ def test_forcebuy_last_unlimited(default_conf, ticker, fee, mocker, balance_rati rpc = RPC(freqtrade) freqtrade.strategy.order_types['stoploss_on_exchange'] = True # Switch ordertype to market to close trade immediately - freqtrade.strategy.order_types['sell'] = 'market' + freqtrade.strategy.order_types['exit'] = 'market' patch_get_signal(freqtrade) # Create 4 trades