diff --git a/README.md b/README.md index 02eb47e00..679dbcab0 100644 --- a/README.md +++ b/README.md @@ -129,6 +129,7 @@ Telegram is not mandatory. However, this is a great way to control your bot. Mor - `/status |[table]`: Lists all or specific open trades. - `/profit []`: Lists cumulative profit from all finished trades, over the last n days. - `/forceexit |all`: Instantly exits the given trade (Ignoring `minimum_roi`). +- `/fx |all`: Alias to `/forceexit` - `/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 56dea574d..04837089f 100644 --- a/config_examples/config_full.example.json +++ b/config_examples/config_full.example.json @@ -54,9 +54,9 @@ "order_types": { "entry": "limit", "exit": "limit", - "emergencyexit": "market", - "forceexit": "market", - "forceentry": "market", + "emergency_exit": "market", + "force_exit": "market", + "force_entry": "market", "stoploss": "market", "stoploss_on_exchange": false, "stoploss_on_exchange_interval": 60, diff --git a/docs/backtesting.md b/docs/backtesting.md index 648d084eb..5d836d01b 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -366,7 +366,7 @@ This table can tell you which area needs some additional work (e.g. all or many ### Left open trades table -The 3rd table contains all trades the bot had to `forceexit` at the end of the backtesting period to present you the full picture. +The 3rd table contains all trades the bot had to `force_exit` at the end of the backtesting period to present you the full picture. This is necessary to simulate realistic behavior, since the backtest period has to end at some point, while realistically, you could leave the bot running forever. These trades are also included in the first table, but are also shown separately in this table for clarity. diff --git a/docs/configuration.md b/docs/configuration.md index 52fef8d7e..ad5b073be 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -376,7 +376,7 @@ 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 (`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. +The `order_types` configuration parameter maps actions (`entry`, `exit`, `stoploss`, `emergency_exit`, `force_exit`, `force_entry`) 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 enter using limit orders, exit using limit-orders, and create stoplosses using market orders. It also allows to set the @@ -386,7 +386,7 @@ stoploss "on exchange" which means stoploss order would be placed immediately on 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 (`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) +For information on (`emergency_exit`,`force_exit`, `force_entry`, `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: @@ -394,9 +394,9 @@ Syntax for Strategy: order_types = { "entry": "limit", "exit": "limit", - "emergencyexit": "market", - "forceentry": "market", - "forceexit": "market", + "emergency_exit": "market", + "force_entry": "market", + "force_exit": "market", "stoploss": "market", "stoploss_on_exchange": False, "stoploss_on_exchange_interval": 60, @@ -410,9 +410,9 @@ Configuration: "order_types": { "entry": "limit", "exit": "limit", - "emergencyexit": "market", - "forceentry": "market", - "forceexit": "market", + "emergency_exit": "market", + "force_entry": "market", + "force_exit": "market", "stoploss": "market", "stoploss_on_exchange": false, "stoploss_on_exchange_interval": 60 @@ -435,7 +435,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 exit" is initiated. By default, this will exit the trade using a market order. The order-type for the emergency-exit can be changed by setting the `emergencyexit` 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 exit the trade using a market order. The order-type for the emergency-exit can be changed by setting the `emergency_exit` value in the `order_types` dictionary - however, this is not advised. ### Understand order_time_in_force diff --git a/docs/rest-api.md b/docs/rest-api.md index 25e7ee205..e3f9ff53d 100644 --- a/docs/rest-api.md +++ b/docs/rest-api.md @@ -223,8 +223,8 @@ forceenter :param side: 'long' or 'short' :param price: Optional - price to buy -forcesell - Force-sell a trade. +forceexit + Force-exit a trade. :param tradeid: Id of the trade (can be received via status command) diff --git a/docs/sql_cheatsheet.md b/docs/sql_cheatsheet.md index 7b9b99881..49372b002 100644 --- a/docs/sql_cheatsheet.md +++ b/docs/sql_cheatsheet.md @@ -52,11 +52,11 @@ SELECT * FROM trades; ## 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, forceexit 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 /forceexit, as forceexit orders are closed automatically by the bot on the next iteration. + This should not be necessary after /forceexit, as force_exit 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 2d95813ac..573fdbd6c 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 - 'emergencyexit': 'market', + 'emergency_exit': 'market', 'stoploss_on_exchange': False 'stoploss_on_exchange_interval': 60, 'stoploss_on_exchange_limit_ratio': 0.99 @@ -52,17 +52,17 @@ 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. -### forceexit +### force_exit -`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. +`force_exit` 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. -### forceentry +### force_entry -`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. +`force_entry` 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. -### emergencyexit +### emergency_exit -`emergencyexit` is an optional value, which defaults to `market` and is used when creating stop loss on exchange orders fails. +`emergency_exit` 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: @@ -71,7 +71,7 @@ Example from strategy file: order_types = { "entry": "limit", "exit": "limit", - "emergencyexit": "market", + "emergency_exit": "market", "stoploss": "market", "stoploss_on_exchange": True, "stoploss_on_exchange_interval": 60, diff --git a/docs/strategy_migration.md b/docs/strategy_migration.md index 328f9f97f..57e453298 100644 --- a/docs/strategy_migration.md +++ b/docs/strategy_migration.md @@ -9,6 +9,8 @@ You can use the quick summary as checklist. Please refer to the detailed section ## Quick summary / migration checklist +Note : `force_exit`, `force_enter`, `emergency_exit` are changed to `force_exit`, `force_enter`, `emergency_exit` respectively. + * Strategy methods: * [`populate_buy_trend()` -> `populate_entry_trend()`](#populate_buy_trend) * [`populate_sell_trend()` -> `populate_exit_trend()`](#populate_sell_trend) @@ -334,6 +336,7 @@ After: #### `order_types` `order_types` have changed all wordings from `buy` to `entry` - and `sell` to `exit`. +And two words are joined with `_`. ``` python hl_lines="2-6" order_types = { @@ -354,9 +357,9 @@ After: order_types = { "entry": "limit", "exit": "limit", - "emergencyexit": "market", - "forceexit": "market", - "forceentry": "market", + "emergency_exit": "market", + "force_exit": "market", + "force_entry": "market", "stoploss": "market", "stoploss_on_exchange": false, "stoploss_on_exchange_interval": 60 diff --git a/docs/telegram-usage.md b/docs/telegram-usage.md index 29187cf95..4c0296f65 100644 --- a/docs/telegram-usage.md +++ b/docs/telegram-usage.md @@ -173,6 +173,7 @@ official commands. You can ask at any moment for help with `/help`. | `/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) | `/forceexit ` | Instantly exits the given trade (Ignoring `minimum_roi`). | `/forceexit all` | Instantly exits all open trades (Ignoring `minimum_roi`). +| `/fx` | alias for `/forceexit` | `/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 @@ -285,7 +286,7 @@ Starting capital is either taken from the `available_capital` setting, or calcul > **BINANCE:** Long ETH/BTC with limit `0.03400000` (`1.000000 ETH`, `225.290 USD`) Omitting the pair will open a query asking for the pair to trade (based on the current whitelist). -Trades crated through `/forceentry` will have the buy-tag of `forceentry`. +Trades created through `/forcelong` will have the buy-tag of `force_entry`. ![Telegram force-buy screenshot](assets/telegram_forcebuy.png) diff --git a/freqtrade/configuration/config_validation.py b/freqtrade/configuration/config_validation.py index 416b9760b..6e4a4b0ef 100644 --- a/freqtrade/configuration/config_validation.py +++ b/freqtrade/configuration/config_validation.py @@ -94,8 +94,8 @@ def _validate_unlimited_amount(conf: Dict[str, Any]) -> None: :raise: OperationalException if config validation failed """ if (not conf.get('edge', {}).get('enabled') - and conf.get('max_open_trades') == float('inf') - and conf.get('stake_amount') == constants.UNLIMITED_STAKE_AMOUNT): + and conf.get('max_open_trades') == float('inf') + and conf.get('stake_amount') == constants.UNLIMITED_STAKE_AMOUNT): raise OperationalException("`max_open_trades` and `stake_amount` cannot both be unlimited.") @@ -244,7 +244,9 @@ def _validate_time_in_force(conf: Dict[str, Any]) -> None: 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']): + old_order_types = ['buy', 'sell', 'emergencysell', 'forcebuy', + 'forcesell', 'emergencyexit', 'forceexit', 'forceentry'] + if any(x in order_types for x in old_order_types): if conf.get('trading_mode', TradingMode.SPOT) != TradingMode.SPOT: raise OperationalException( "Please migrate your order_types settings to use the new wording.") @@ -256,9 +258,12 @@ def _validate_order_types(conf: Dict[str, Any]) -> None: for o, n in [ ('buy', 'entry'), ('sell', 'exit'), - ('emergencysell', 'emergencyexit'), - ('forcesell', 'forceexit'), - ('forcebuy', 'forceentry'), + ('emergencysell', 'emergency_exit'), + ('forcesell', 'force_exit'), + ('forcebuy', 'force_entry'), + ('emergencyexit', 'emergency_exit'), + ('forceexit', 'force_exit'), + ('forceentry', 'force_entry'), ]: process_deprecated_setting(conf, 'order_types', o, 'order_types', n) diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 4425c8ee7..b508fd807 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -216,9 +216,9 @@ CONF_SCHEMA = { 'properties': { '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': { + 'force_exit': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES}, + 'force_entry': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES}, + 'emergency_exit': { 'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES, 'default': 'market'}, diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index a69f405df..6e1a0b208 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -191,7 +191,7 @@ class FreqtradeBot(LoggingMixin): # Check and handle any timed out open orders self.check_handle_timedout() - # Protect from collisions with forceexit. + # Protect from collisions with force_exit. # Without this, freqtrade my try to recreate stoploss_on_exchange orders # while exiting is in process, since telegram messages arrive in an different thread. with self._exit_lock: @@ -1379,7 +1379,7 @@ class FreqtradeBot(LoggingMixin): order_type = ordertype or self.strategy.order_types[exit_type] if exit_check.exit_type == ExitType.EMERGENCY_EXIT: # Emergency sells (default to market!) - order_type = self.strategy.order_types.get("emergencyexit", "market") + order_type = self.strategy.order_types.get("emergency_exit", "market") amount = self._safe_exit_amount(trade.pair, trade.amount) time_in_force = self.strategy.order_time_in_force['exit'] diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index 629617ddc..dc8e0cd23 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -140,9 +140,9 @@ class UnfilledTimeout(BaseModel): class OrderTypes(BaseModel): entry: OrderTypeValues exit: OrderTypeValues - emergencyexit: Optional[OrderTypeValues] - forceexit: Optional[OrderTypeValues] - forceentry: Optional[OrderTypeValues] + emergency_exit: Optional[OrderTypeValues] + force_exit: Optional[OrderTypeValues] + force_entry: Optional[OrderTypeValues] stoploss: OrderTypeValues stoploss_on_exchange: bool stoploss_on_exchange_interval: Optional[int] diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index 61c5243aa..69338d665 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -135,13 +135,13 @@ def show_config(rpc: Optional[RPC] = Depends(get_rpc_optional), config=Depends(g return resp -# /forcebuy is deprecated with short addition. use ForceEntry instead +# /forcebuy is deprecated with short addition. use /forceentry instead @router.post('/forceenter', response_model=ForceEnterResponse, tags=['trading']) @router.post('/forcebuy', response_model=ForceEnterResponse, tags=['trading']) -def forceentry(payload: ForceEnterPayload, rpc: RPC = Depends(get_rpc)): +def force_entry(payload: ForceEnterPayload, rpc: RPC = Depends(get_rpc)): ordertype = payload.ordertype.value if payload.ordertype else None stake_amount = payload.stakeamount if payload.stakeamount else None - entry_tag = payload.entry_tag if payload.entry_tag else 'forceentry' + entry_tag = payload.entry_tag if payload.entry_tag else 'force_entry' trade = rpc._rpc_force_entry(payload.pair, payload.price, order_side=payload.side, order_type=ordertype, stake_amount=stake_amount, @@ -154,11 +154,12 @@ def forceentry(payload: ForceEnterPayload, rpc: RPC = Depends(get_rpc)): {"status": f"Error entering {payload.side} trade for pair {payload.pair}."}) +# /forcesell is deprecated with short addition. use /forceexit instead @router.post('/forceexit', response_model=ResultMsg, tags=['trading']) @router.post('/forcesell', response_model=ResultMsg, tags=['trading']) def forcesell(payload: ForceExitPayload, rpc: RPC = Depends(get_rpc)): ordertype = payload.ordertype.value if payload.ordertype else None - return rpc._rpc_forceexit(payload.tradeid, ordertype) + return rpc._rpc_force_exit(payload.tradeid, ordertype) @router.get('/blacklist', response_model=BlacklistResponse, tags=['info', 'pairlist']) diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 4e75f15ad..d3e20eaf1 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -684,7 +684,7 @@ class RPC: return {'status': 'No more buy will occur from now. Run /reload_config to reset.'} - def _rpc_forceexit(self, trade_id: str, ordertype: Optional[str] = None) -> Dict[str, str]: + def _rpc_force_exit(self, trade_id: str, ordertype: Optional[str] = None) -> Dict[str, str]: """ Handler for forcesell . Sells the given trade at current price @@ -709,7 +709,7 @@ class RPC: trade.pair, side='exit', is_short=trade.is_short, refresh=True) exit_check = ExitCheckTuple(exit_type=ExitType.FORCE_EXIT) order_type = ordertype or self._freqtrade.strategy.order_types.get( - "forceexit", self._freqtrade.strategy.order_types["exit"]) + "force_exit", self._freqtrade.strategy.order_types["exit"]) self._freqtrade.execute_trade_exit( trade, current_rate, exit_check, ordertype=order_type) @@ -732,7 +732,7 @@ class RPC: trade_filter=[Trade.id == trade_id, Trade.is_open.is_(True), ] ).first() if not trade: - logger.warning('forceexit: Invalid argument received') + logger.warning('force_exit: Invalid argument received') raise RPCException('invalid argument') _exec_forcesell(trade) @@ -744,14 +744,14 @@ class RPC: order_type: Optional[str] = None, order_side: SignalDirection = SignalDirection.LONG, stake_amount: Optional[float] = None, - enter_tag: Optional[str] = 'forceentry') -> Optional[Trade]: + enter_tag: Optional[str] = 'force_entry') -> Optional[Trade]: """ Handler for forcebuy Buys a pair trade at the given or current price """ if not self._freqtrade.config.get('forcebuy_enable', False): - raise RPCException('Forceentry not enabled.') + raise RPCException('Force_entry not enabled.') if self._freqtrade.state != State.RUNNING: raise RPCException('trader is not running') @@ -781,7 +781,7 @@ class RPC: # execute buy if not order_type: order_type = self._freqtrade.strategy.order_types.get( - 'forceentry', self._freqtrade.strategy.order_types['entry']) + 'force_entry', 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 fc949cdde..ca34515f3 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -153,11 +153,11 @@ class Telegram(RPCHandler): CommandHandler('balance', self._balance), CommandHandler('start', self._start), CommandHandler('stop', self._stop), - CommandHandler(['forcesell', 'forceexit'], self._forceexit), + CommandHandler(['forcesell', 'forceexit', 'fx'], self._force_exit), CommandHandler(['forcebuy', 'forcelong'], partial( - self._forceenter, order_side=SignalDirection.LONG)), + self._force_enter, order_side=SignalDirection.LONG)), CommandHandler('forceshort', partial( - self._forceenter, order_side=SignalDirection.SHORT)), + self._force_enter, order_side=SignalDirection.SHORT)), CommandHandler('trades', self._trades), CommandHandler('delete', self._delete_trade), CommandHandler('performance', self._performance), @@ -197,7 +197,7 @@ class Telegram(RPCHandler): pattern='update_exit_reason_performance'), CallbackQueryHandler(self._mix_tag_performance, pattern='update_mix_tag_performance'), CallbackQueryHandler(self._count, pattern='update_count'), - CallbackQueryHandler(self._forceenter_inline), + CallbackQueryHandler(self._force_enter_inline), ] for handle in handles: self._updater.dispatcher.add_handler(handle) @@ -926,7 +926,7 @@ class Telegram(RPCHandler): self._send_msg('Status: `{status}`'.format(**msg)) @authorized_only - def _forceexit(self, update: Update, context: CallbackContext) -> None: + def _force_exit(self, update: Update, context: CallbackContext) -> None: """ Handler for /forcesell . Sells the given trade at current price @@ -940,20 +940,20 @@ class Telegram(RPCHandler): self._send_msg("You must specify a trade-id or 'all'.") return try: - msg = self._rpc._rpc_forceexit(trade_id) - self._send_msg('Forceexit Result: `{result}`'.format(**msg)) + msg = self._rpc._rpc_force_exit(trade_id) + self._send_msg('Force_exit Result: `{result}`'.format(**msg)) except RPCException as e: self._send_msg(str(e)) - def _forceenter_action(self, pair, price: Optional[float], order_side: SignalDirection): + def _force_enter_action(self, pair, price: Optional[float], order_side: SignalDirection): if pair != 'cancel': try: self._rpc._rpc_force_entry(pair, price, order_side=order_side) except RPCException as e: self._send_msg(str(e)) - def _forceenter_inline(self, update: Update, _: CallbackContext) -> None: + def _force_enter_inline(self, update: Update, _: CallbackContext) -> None: if update.callback_query: query = update.callback_query if query.data and '_||_' in query.data: @@ -961,7 +961,7 @@ class Telegram(RPCHandler): order_side = SignalDirection(side) query.answer() query.edit_message_text(text=f"Manually entering {order_side} for {pair}") - self._forceenter_action(pair, None, order_side) + self._force_enter_action(pair, None, order_side) @staticmethod def _layout_inline_keyboard(buttons: List[InlineKeyboardButton], @@ -969,7 +969,7 @@ class Telegram(RPCHandler): return [buttons[i:i + cols] for i in range(0, len(buttons), cols)] @authorized_only - def _forceenter( + def _force_enter( self, update: Update, context: CallbackContext, order_side: SignalDirection) -> None: """ Handler for /forcelong and `/forceshort @@ -981,7 +981,7 @@ class Telegram(RPCHandler): if context.args: pair = context.args[0] price = float(context.args[1]) if len(context.args) > 1 else None - self._forceenter_action(pair, price, order_side) + self._force_enter_action(pair, price, order_side) else: whitelist = self._rpc._rpc_whitelist()['whitelist'] pair_buttons = [ @@ -1359,23 +1359,25 @@ class Telegram(RPCHandler): :param update: message update :return: None """ - forceenter_text = ("*/forcelong []:* `Instantly buys the given pair. " - "Optionally takes a rate at which to buy " - "(only applies to limit orders).` \n" - ) + force_enter_text = ("*/forcelong []:* `Instantly buys the given pair. " + "Optionally takes a rate at which to buy " + "(only applies to limit orders).` \n" + ) if self._rpc._freqtrade.trading_mode != TradingMode.SPOT: - forceenter_text += ("*/forceshort []:* `Instantly shorts the given pair. " - "Optionally takes a rate at which to sell " - "(only applies to limit orders).` \n") + force_enter_text += ("*/forceshort []:* `Instantly shorts the given pair. " + "Optionally takes a rate at which to sell " + "(only applies to limit orders).` \n") message = ( "_BotControl_\n" "------------\n" "*/start:* `Starts the trader`\n" "*/stop:* Stops the trader\n" "*/stopbuy:* `Stops buying, but handles open trades gracefully` \n" + # TODO: forceenter forceshort forcelong missing "*/forceexit |all:* `Instantly exits the given trade or all trades, " "regardless of profit`\n" - f"{forceenter_text if self._config.get('forcebuy_enable', False) else ''}" + "*/fe |all:* `Alias to /forceexit`" + f"{force_enter_text if self._config.get('forcebuy_enable', False) else ''}" "*/delete :* `Instantly delete the given trade in the database`\n" "*/whitelist:* `Show current whitelist` \n" "*/blacklist [pair]:* `Show current blacklist, or adds one or more pairs " diff --git a/freqtrade/templates/subtemplates/exchange_bittrex.j2 b/freqtrade/templates/subtemplates/exchange_bittrex.j2 index 2d9afd578..023862314 100644 --- a/freqtrade/templates/subtemplates/exchange_bittrex.j2 +++ b/freqtrade/templates/subtemplates/exchange_bittrex.j2 @@ -1,7 +1,7 @@ "order_types": { "entry": "limit", "exit": "limit", - "emergencyexit": "limit", + "emergency_exit": "limit", "stoploss": "limit", "stoploss_on_exchange": false }, diff --git a/scripts/rest_client.py b/scripts/rest_client.py index e23954dd4..9c5f820b9 100755 --- a/scripts/rest_client.py +++ b/scripts/rest_client.py @@ -261,7 +261,7 @@ class FtRestClient(): } return self._post("forcebuy", data=data) - def forceenter(self, pair, side, price=None): + def force_enter(self, pair, side, price=None): """Force entering a trade :param pair: Pair to buy (ETH/BTC) @@ -273,7 +273,7 @@ class FtRestClient(): "side": side, "price": price, } - return self._post("forceenter", data=data) + return self._post("force_enter", data=data) def forcesell(self, tradeid): """Force-sell a trade. diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index 4bb221003..a9e887be9 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -771,7 +771,7 @@ def test_rpc_stopbuy(mocker, default_conf) -> None: assert freqtradebot.config['max_open_trades'] == 0 -def test_rpc_forceexit(default_conf, ticker, fee, mocker) -> None: +def test_rpc_force_exit(default_conf, ticker, fee, mocker) -> None: mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) cancel_order_mock = MagicMock() @@ -798,29 +798,29 @@ def test_rpc_forceexit(default_conf, ticker, fee, mocker) -> None: freqtradebot.state = State.STOPPED with pytest.raises(RPCException, match=r'.*trader is not running*'): - rpc._rpc_forceexit(None) + rpc._rpc_force_exit(None) freqtradebot.state = State.RUNNING with pytest.raises(RPCException, match=r'.*invalid argument*'): - rpc._rpc_forceexit(None) + rpc._rpc_force_exit(None) - msg = rpc._rpc_forceexit('all') + msg = rpc._rpc_force_exit('all') assert msg == {'result': 'Created sell orders for all open trades.'} freqtradebot.enter_positions() - msg = rpc._rpc_forceexit('all') + msg = rpc._rpc_force_exit('all') assert msg == {'result': 'Created sell orders for all open trades.'} freqtradebot.enter_positions() - msg = rpc._rpc_forceexit('2') + msg = rpc._rpc_force_exit('2') assert msg == {'result': 'Created sell order for trade 2.'} freqtradebot.state = State.STOPPED with pytest.raises(RPCException, match=r'.*trader is not running*'): - rpc._rpc_forceexit(None) + rpc._rpc_force_exit(None) with pytest.raises(RPCException, match=r'.*trader is not running*'): - rpc._rpc_forceexit('all') + rpc._rpc_force_exit('all') freqtradebot.state = State.RUNNING assert cancel_order_mock.call_count == 0 @@ -849,7 +849,7 @@ def test_rpc_forceexit(default_conf, ticker, fee, mocker) -> None: ) # check that the trade is called, which is done by ensuring exchange.cancel_order is called # and trade amount is updated - rpc._rpc_forceexit('3') + rpc._rpc_force_exit('3') assert cancel_order_mock.call_count == 1 assert trade.amount == filled_amount @@ -877,7 +877,7 @@ def test_rpc_forceexit(default_conf, ticker, fee, mocker) -> None: } ) # check that the trade is called, which is done by ensuring exchange.cancel_order is called - msg = rpc._rpc_forceexit('4') + msg = rpc._rpc_force_exit('4') assert msg == {'result': 'Created sell order for trade 4.'} assert cancel_order_mock.call_count == 2 assert trade.amount == amount @@ -894,7 +894,7 @@ def test_rpc_forceexit(default_conf, ticker, fee, mocker) -> None: 'filled': 0.0 } ) - msg = rpc._rpc_forceexit('3') + msg = rpc._rpc_force_exit('3') assert msg == {'result': 'Created sell order for trade 3.'} # status quo, no exchange calls assert cancel_order_mock.call_count == 3 @@ -1182,7 +1182,7 @@ def test_rpc_count(mocker, default_conf, ticker, fee) -> None: assert counts["current"] == 1 -def test_rpc_forceentry(mocker, default_conf, ticker, fee, limit_buy_order_open) -> None: +def test_rpc_force_entry(mocker, default_conf, ticker, fee, limit_buy_order_open) -> None: default_conf['forcebuy_enable'] = True mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) buy_mm = MagicMock(return_value=limit_buy_order_open) @@ -1221,7 +1221,7 @@ def test_rpc_forceentry(mocker, default_conf, ticker, fee, limit_buy_order_open) pair = 'LTC/BTC' trade = rpc._rpc_force_entry(pair, 0.0001, order_type='limit', stake_amount=0.05) assert trade.stake_amount == 0.05 - assert trade.buy_tag == 'forceentry' + assert trade.buy_tag == 'force_entry' # Test not buying pair = 'XRP/BTC' @@ -1234,7 +1234,7 @@ def test_rpc_forceentry(mocker, default_conf, ticker, fee, limit_buy_order_open) assert trade is None -def test_rpc_forceentry_stopped(mocker, default_conf) -> None: +def test_rpc_force_entry_stopped(mocker, default_conf) -> None: default_conf['forcebuy_enable'] = True default_conf['initial_state'] = 'stopped' mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) @@ -1247,18 +1247,18 @@ def test_rpc_forceentry_stopped(mocker, default_conf) -> None: rpc._rpc_force_entry(pair, None) -def test_rpc_forceentry_disabled(mocker, default_conf) -> None: +def test_rpc_force_entry_disabled(mocker, default_conf) -> None: mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot) rpc = RPC(freqtradebot) pair = 'ETH/BTC' - with pytest.raises(RPCException, match=r'Forceentry not enabled.'): + with pytest.raises(RPCException, match=r'Force_entry not enabled.'): rpc._rpc_force_entry(pair, None) -def test_rpc_forceentry_wrong_mode(mocker, default_conf) -> None: +def test_rpc_force_entry_wrong_mode(mocker, default_conf) -> None: default_conf['forcebuy_enable'] = True mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index fdd3a610e..992e4edf7 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -1077,13 +1077,13 @@ def test_api_whitelist(botclient): 'forcebuy', 'forceenter', ]) -def test_api_forceentry(botclient, mocker, fee, endpoint): +def test_api_force_entry(botclient, mocker, fee, endpoint): ftbot, client = botclient rc = client_post(client, f"{BASE_URI}/{endpoint}", data='{"pair": "ETH/BTC"}') assert_response(rc, 502) - assert rc.json() == {"error": f"Error querying /api/v1/{endpoint}: Forceentry not enabled."} + assert rc.json() == {"error": f"Error querying /api/v1/{endpoint}: Force_entry not enabled."} # enable forcebuy ftbot.config['forcebuy_enable'] = True diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 7ee8d8a84..f104e7153 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -95,7 +95,7 @@ def test_telegram_init(default_conf, mocker, caplog) -> None: message_str = ("rpc.telegram is listening for following commands: [['status'], ['profit'], " "['balance'], ['start'], ['stop'], " - "['forcesell', 'forceexit'], ['forcebuy', 'forcelong'], ['forceshort'], " + "['forcesell', 'forceexit', 'fx'], ['forcebuy', 'forcelong'], ['forceshort'], " "['trades'], ['delete'], ['performance'], " "['buys', 'entries'], ['sells', 'exits'], ['mix_tags'], " "['stats'], ['daily'], ['weekly'], ['monthly'], " @@ -1035,7 +1035,7 @@ def test_telegram_forcesell_handle(default_conf, update, ticker, fee, # /forcesell 1 context = MagicMock() context.args = ["1"] - telegram._forceexit(update=update, context=context) + telegram._force_exit(update=update, context=context) assert msg_mock.call_count == 4 last_msg = msg_mock.call_args_list[-2][0][0] @@ -1103,7 +1103,7 @@ def test_telegram_forcesell_down_handle(default_conf, update, ticker, fee, # /forcesell 1 context = MagicMock() context.args = ["1"] - telegram._forceexit(update=update, context=context) + telegram._force_exit(update=update, context=context) assert msg_mock.call_count == 4 @@ -1162,7 +1162,7 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None # /forcesell all context = MagicMock() context.args = ["all"] - telegram._forceexit(update=update, context=context) + telegram._force_exit(update=update, context=context) # Called for each trade 2 times assert msg_mock.call_count == 8 @@ -1207,7 +1207,7 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None: # /forcesell 1 context = MagicMock() context.args = ["1"] - telegram._forceexit(update=update, context=context) + telegram._force_exit(update=update, context=context) assert msg_mock.call_count == 1 assert 'not running' in msg_mock.call_args_list[0][0][0] @@ -1216,7 +1216,7 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None: freqtradebot.state = State.RUNNING context = MagicMock() context.args = [] - telegram._forceexit(update=update, context=context) + telegram._force_exit(update=update, context=context) assert msg_mock.call_count == 1 assert "You must specify a trade-id or 'all'." in msg_mock.call_args_list[0][0][0] @@ -1226,12 +1226,12 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None: # /forcesell 123456 context = MagicMock() context.args = ["123456"] - telegram._forceexit(update=update, context=context) + telegram._force_exit(update=update, context=context) assert msg_mock.call_count == 1 assert 'invalid argument' in msg_mock.call_args_list[0][0][0] -def test_forceenter_handle(default_conf, update, mocker) -> None: +def test_force_enter_handle(default_conf, update, mocker) -> None: mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0) fbuy_mock = MagicMock(return_value=None) @@ -1243,7 +1243,7 @@ def test_forceenter_handle(default_conf, update, mocker) -> None: # /forcelong ETH/BTC context = MagicMock() context.args = ["ETH/BTC"] - telegram._forceenter(update=update, context=context, order_side=SignalDirection.LONG) + telegram._force_enter(update=update, context=context, order_side=SignalDirection.LONG) assert fbuy_mock.call_count == 1 assert fbuy_mock.call_args_list[0][0][0] == 'ETH/BTC' @@ -1256,7 +1256,7 @@ def test_forceenter_handle(default_conf, update, mocker) -> None: # /forcelong ETH/BTC 0.055 context = MagicMock() context.args = ["ETH/BTC", "0.055"] - telegram._forceenter(update=update, context=context, order_side=SignalDirection.LONG) + telegram._force_enter(update=update, context=context, order_side=SignalDirection.LONG) assert fbuy_mock.call_count == 1 assert fbuy_mock.call_args_list[0][0][0] == 'ETH/BTC' @@ -1264,20 +1264,20 @@ def test_forceenter_handle(default_conf, update, mocker) -> None: assert fbuy_mock.call_args_list[0][0][1] == 0.055 -def test_forceenter_handle_exception(default_conf, update, mocker) -> None: +def test_force_enter_handle_exception(default_conf, update, mocker) -> None: mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) patch_get_signal(freqtradebot) update.message.text = '/forcebuy ETH/Nonepair' - telegram._forceenter(update=update, context=MagicMock(), order_side=SignalDirection.LONG) + telegram._force_enter(update=update, context=MagicMock(), order_side=SignalDirection.LONG) assert msg_mock.call_count == 1 - assert msg_mock.call_args_list[0][0][0] == 'Forceentry not enabled.' + assert msg_mock.call_args_list[0][0][0] == 'Force_entry not enabled.' -def test_forceenter_no_pair(default_conf, update, mocker) -> None: +def test_force_enter_no_pair(default_conf, update, mocker) -> None: mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0) fbuy_mock = MagicMock(return_value=None) @@ -1289,7 +1289,7 @@ def test_forceenter_no_pair(default_conf, update, mocker) -> None: context = MagicMock() context.args = [] - telegram._forceenter(update=update, context=context, order_side=SignalDirection.LONG) + telegram._force_enter(update=update, context=context, order_side=SignalDirection.LONG) assert fbuy_mock.call_count == 0 assert msg_mock.call_count == 1 @@ -1301,7 +1301,7 @@ def test_forceenter_no_pair(default_conf, update, mocker) -> None: update = MagicMock() update.callback_query = MagicMock() update.callback_query.data = 'XRP/USDT_||_long' - telegram._forceenter_inline(update, None) + telegram._force_enter_inline(update, None) assert fbuy_mock.call_count == 1 diff --git a/tests/test_configuration.py b/tests/test_configuration.py index f8ec4656b..64a0446fa 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -977,7 +977,7 @@ def test__validate_order_types(default_conf, caplog) -> None: 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 conf['order_types']['force_entry'] == 'limit' assert 'buy' not in conf['order_types'] assert 'sell' not in conf['order_types'] assert 'forcebuy' not in conf['order_types']