Merge pull request #6653 from mkavinkumar1/renaming-forceentry-forceexit
renamed forceentry forceexit
This commit is contained in:
commit
ef2f8be526
@ -129,6 +129,7 @@ Telegram is not mandatory. However, this is a great way to control your bot. Mor
|
||||
- `/status <trade_id>|[table]`: Lists all or specific open trades.
|
||||
- `/profit [<n>]`: Lists cumulative profit from all finished trades, over the last n days.
|
||||
- `/forceexit <trade_id>|all`: Instantly exits the given trade (Ignoring `minimum_roi`).
|
||||
- `/fx <trade_id>|all`: Alias to `/forceexit`
|
||||
- `/performance`: Show performance of each finished trade grouped by pair
|
||||
- `/balance`: Show account balance per currency.
|
||||
- `/daily <n>`: Shows profit or loss per day, over the last n days.
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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 <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.
|
||||
|
||||
!!! 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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -173,6 +173,7 @@ official commands. You can ask at any moment for help with `/help`.
|
||||
| `/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)
|
||||
| `/forceexit <trade_id>` | Instantly exits the given trade (Ignoring `minimum_roi`).
|
||||
| `/forceexit all` | Instantly exits all open trades (Ignoring `minimum_roi`).
|
||||
| `/fx` | alias for `/forceexit`
|
||||
| `/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)
|
||||
| `/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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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'},
|
||||
|
@ -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']
|
||||
|
@ -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]
|
||||
|
@ -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'])
|
||||
|
@ -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 <id>.
|
||||
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 <asset> <price>
|
||||
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,
|
||||
|
@ -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 <id>.
|
||||
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 <asset> <price> and `/forceshort <asset> <price>
|
||||
@ -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 <pair> [<rate>]:* `Instantly buys the given pair. "
|
||||
"Optionally takes a rate at which to buy "
|
||||
"(only applies to limit orders).` \n"
|
||||
)
|
||||
force_enter_text = ("*/forcelong <pair> [<rate>]:* `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 <pair> [<rate>]:* `Instantly shorts the given pair. "
|
||||
"Optionally takes a rate at which to sell "
|
||||
"(only applies to limit orders).` \n")
|
||||
force_enter_text += ("*/forceshort <pair> [<rate>]:* `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 <trade_id>|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 <trade_id>|all:* `Alias to /forceexit`"
|
||||
f"{force_enter_text if self._config.get('forcebuy_enable', False) else ''}"
|
||||
"*/delete <trade_id>:* `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 "
|
||||
|
@ -1,7 +1,7 @@
|
||||
"order_types": {
|
||||
"entry": "limit",
|
||||
"exit": "limit",
|
||||
"emergencyexit": "limit",
|
||||
"emergency_exit": "limit",
|
||||
"stoploss": "limit",
|
||||
"stoploss_on_exchange": false
|
||||
},
|
||||
|
@ -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.
|
||||
|
@ -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())
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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']
|
||||
|
Loading…
Reference in New Issue
Block a user