Update forcesell to work as forceexit
This commit is contained in:
parent
066fb3ce00
commit
0a52d79208
@ -285,7 +285,7 @@ class ForceEnterPayload(BaseModel):
|
||||
stakeamount: Optional[float]
|
||||
|
||||
|
||||
class ForceSellPayload(BaseModel):
|
||||
class ForceExitPayload(BaseModel):
|
||||
tradeid: str
|
||||
ordertype: Optional[OrderTypeValues]
|
||||
|
||||
|
@ -15,7 +15,7 @@ from freqtrade.rpc import RPC
|
||||
from freqtrade.rpc.api_server.api_schemas import (AvailablePairs, Balances, BlacklistPayload,
|
||||
BlacklistResponse, Count, Daily,
|
||||
DeleteLockRequest, DeleteTrade, ForceEnterPayload,
|
||||
ForceEnterResponse, ForceSellPayload, Locks, Logs,
|
||||
ForceEnterResponse, ForceExitPayload, Locks, Logs,
|
||||
OpenTradeSchema, PairHistory, PerformanceEntry,
|
||||
Ping, PlotConfig, Profit, ResultMsg, ShowConfig,
|
||||
Stats, StatusMsg, StrategyListResponse,
|
||||
@ -152,10 +152,11 @@ def forceentry(payload: ForceEnterPayload, rpc: RPC = Depends(get_rpc)):
|
||||
{"status": f"Error entering {payload.side} trade for pair {payload.pair}."})
|
||||
|
||||
|
||||
@router.post('/forceexit', response_model=ResultMsg, tags=['trading'])
|
||||
@router.post('/forcesell', response_model=ResultMsg, tags=['trading'])
|
||||
def forcesell(payload: ForceSellPayload, rpc: RPC = Depends(get_rpc)):
|
||||
def forcesell(payload: ForceExitPayload, rpc: RPC = Depends(get_rpc)):
|
||||
ordertype = payload.ordertype.value if payload.ordertype else None
|
||||
return rpc._rpc_forcesell(payload.tradeid, ordertype)
|
||||
return rpc._rpc_forceexit(payload.tradeid, ordertype)
|
||||
|
||||
|
||||
@router.get('/blacklist', response_model=BlacklistResponse, tags=['info', 'pairlist'])
|
||||
|
@ -658,7 +658,7 @@ class RPC:
|
||||
|
||||
return {'status': 'No more buy will occur from now. Run /reload_config to reset.'}
|
||||
|
||||
def _rpc_forcesell(self, trade_id: str, ordertype: Optional[str] = None) -> Dict[str, str]:
|
||||
def _rpc_forceexit(self, trade_id: str, ordertype: Optional[str] = None) -> Dict[str, str]:
|
||||
"""
|
||||
Handler for forcesell <id>.
|
||||
Sells the given trade at current price
|
||||
|
@ -25,6 +25,7 @@ from freqtrade.__init__ import __version__
|
||||
from freqtrade.constants import DUST_PER_COIN
|
||||
from freqtrade.enums import RPCMessageType
|
||||
from freqtrade.enums.signaltype import SignalDirection
|
||||
from freqtrade.enums.tradingmode import TradingMode
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.misc import chunks, plural, round_coin_value
|
||||
from freqtrade.persistence import Trade
|
||||
@ -115,7 +116,8 @@ class Telegram(RPCHandler):
|
||||
r'/stopbuy$', r'/reload_config$', r'/show_config$',
|
||||
r'/logs$', r'/whitelist$', r'/blacklist$', r'/bl_delete$',
|
||||
r'/weekly$', r'/weekly \d+$', r'/monthly$', r'/monthly \d+$',
|
||||
r'/forcebuy$', r'/edge$', r'/help$', r'/version$']
|
||||
r'/forcebuy$', r'/forcelong$', r'/forceshort$',
|
||||
r'/edge$', r'/help$', r'/version$']
|
||||
# Create keys for generation
|
||||
valid_keys_print = [k.replace('$', '') for k in valid_keys]
|
||||
|
||||
@ -152,7 +154,7 @@ class Telegram(RPCHandler):
|
||||
CommandHandler('balance', self._balance),
|
||||
CommandHandler('start', self._start),
|
||||
CommandHandler('stop', self._stop),
|
||||
CommandHandler('forcesell', self._forcesell),
|
||||
CommandHandler(['forcesell', 'forceexit'], self._forceexit),
|
||||
CommandHandler(['forcebuy', 'forcelong'], partial(self._forcebuy, order_side=SignalDirection.LONG)),
|
||||
CommandHandler('forceshort', partial(self._forcebuy, order_side=SignalDirection.SHORT)),
|
||||
CommandHandler('trades', self._trades),
|
||||
@ -849,7 +851,7 @@ class Telegram(RPCHandler):
|
||||
self._send_msg('Status: `{status}`'.format(**msg))
|
||||
|
||||
@authorized_only
|
||||
def _forcesell(self, update: Update, context: CallbackContext) -> None:
|
||||
def _forceexit(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /forcesell <id>.
|
||||
Sells the given trade at current price
|
||||
@ -863,7 +865,7 @@ class Telegram(RPCHandler):
|
||||
self._send_msg("You must specify a trade-id or 'all'.")
|
||||
return
|
||||
try:
|
||||
msg = self._rpc._rpc_forcesell(trade_id)
|
||||
msg = self._rpc._rpc_forceexit(trade_id)
|
||||
self._send_msg('Forcesell Result: `{result}`'.format(**msg))
|
||||
|
||||
except RPCException as e:
|
||||
@ -1279,16 +1281,21 @@ class Telegram(RPCHandler):
|
||||
:param update: message update
|
||||
:return: None
|
||||
"""
|
||||
forcebuy_text = ("*/forcebuy <pair> [<rate>]:* `Instantly buys the given pair. "
|
||||
forcebuy_text = ("*/forcelong <pair> [<rate>]:* `Instantly buys the given pair. "
|
||||
"Optionally takes a rate at which to buy "
|
||||
"(only applies to limit orders).` \n")
|
||||
"(only applies to limit orders).` \n"
|
||||
)
|
||||
if self._rpc_._freqtrade.trading_mode != TradingMode.SPOT:
|
||||
forcebuy_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"
|
||||
"*/forcesell <trade_id>|all:* `Instantly sells the given trade or all trades, "
|
||||
"*/forceexit <trade_id>|all:* `Instantly exits the given trade or all trades, "
|
||||
"regardless of profit`\n"
|
||||
f"{forcebuy_text if self._config.get('forcebuy_enable', False) else ''}"
|
||||
"*/delete <trade_id>:* `Instantly delete the given trade in the database`\n"
|
||||
|
@ -687,7 +687,7 @@ def test_rpc_stopbuy(mocker, default_conf) -> None:
|
||||
assert freqtradebot.config['max_open_trades'] == 0
|
||||
|
||||
|
||||
def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None:
|
||||
def test_rpc_forceexit(default_conf, ticker, fee, mocker) -> None:
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
|
||||
cancel_order_mock = MagicMock()
|
||||
@ -714,29 +714,29 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None:
|
||||
|
||||
freqtradebot.state = State.STOPPED
|
||||
with pytest.raises(RPCException, match=r'.*trader is not running*'):
|
||||
rpc._rpc_forcesell(None)
|
||||
rpc._rpc_forceexit(None)
|
||||
|
||||
freqtradebot.state = State.RUNNING
|
||||
with pytest.raises(RPCException, match=r'.*invalid argument*'):
|
||||
rpc._rpc_forcesell(None)
|
||||
rpc._rpc_forceexit(None)
|
||||
|
||||
msg = rpc._rpc_forcesell('all')
|
||||
msg = rpc._rpc_forceexit('all')
|
||||
assert msg == {'result': 'Created sell orders for all open trades.'}
|
||||
|
||||
freqtradebot.enter_positions()
|
||||
msg = rpc._rpc_forcesell('all')
|
||||
msg = rpc._rpc_forceexit('all')
|
||||
assert msg == {'result': 'Created sell orders for all open trades.'}
|
||||
|
||||
freqtradebot.enter_positions()
|
||||
msg = rpc._rpc_forcesell('2')
|
||||
msg = rpc._rpc_forceexit('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_forcesell(None)
|
||||
rpc._rpc_forceexit(None)
|
||||
|
||||
with pytest.raises(RPCException, match=r'.*trader is not running*'):
|
||||
rpc._rpc_forcesell('all')
|
||||
rpc._rpc_forceexit('all')
|
||||
|
||||
freqtradebot.state = State.RUNNING
|
||||
assert cancel_order_mock.call_count == 0
|
||||
@ -765,7 +765,7 @@ def test_rpc_forcesell(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_forcesell('3')
|
||||
rpc._rpc_forceexit('3')
|
||||
assert cancel_order_mock.call_count == 1
|
||||
assert trade.amount == filled_amount
|
||||
|
||||
@ -793,7 +793,7 @@ def test_rpc_forcesell(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_forcesell('4')
|
||||
msg = rpc._rpc_forceexit('4')
|
||||
assert msg == {'result': 'Created sell order for trade 4.'}
|
||||
assert cancel_order_mock.call_count == 2
|
||||
assert trade.amount == amount
|
||||
@ -810,7 +810,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None:
|
||||
'filled': 0.0
|
||||
}
|
||||
)
|
||||
msg = rpc._rpc_forcesell('3')
|
||||
msg = rpc._rpc_forceexit('3')
|
||||
assert msg == {'result': 'Created sell order for trade 3.'}
|
||||
# status quo, no exchange calls
|
||||
assert cancel_order_mock.call_count == 3
|
||||
|
@ -940,7 +940,7 @@ def test_telegram_forcesell_handle(default_conf, update, ticker, fee,
|
||||
# /forcesell 1
|
||||
context = MagicMock()
|
||||
context.args = ["1"]
|
||||
telegram._forcesell(update=update, context=context)
|
||||
telegram._forceexit(update=update, context=context)
|
||||
|
||||
assert msg_mock.call_count == 4
|
||||
last_msg = msg_mock.call_args_list[-2][0][0]
|
||||
@ -1007,7 +1007,7 @@ def test_telegram_forcesell_down_handle(default_conf, update, ticker, fee,
|
||||
# /forcesell 1
|
||||
context = MagicMock()
|
||||
context.args = ["1"]
|
||||
telegram._forcesell(update=update, context=context)
|
||||
telegram._forceexit(update=update, context=context)
|
||||
|
||||
assert msg_mock.call_count == 4
|
||||
|
||||
@ -1065,7 +1065,7 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None
|
||||
# /forcesell all
|
||||
context = MagicMock()
|
||||
context.args = ["all"]
|
||||
telegram._forcesell(update=update, context=context)
|
||||
telegram._forceexit(update=update, context=context)
|
||||
|
||||
# Called for each trade 2 times
|
||||
assert msg_mock.call_count == 8
|
||||
@ -1109,7 +1109,7 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None:
|
||||
# /forcesell 1
|
||||
context = MagicMock()
|
||||
context.args = ["1"]
|
||||
telegram._forcesell(update=update, context=context)
|
||||
telegram._forceexit(update=update, context=context)
|
||||
assert msg_mock.call_count == 1
|
||||
assert 'not running' in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
@ -1118,7 +1118,7 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None:
|
||||
freqtradebot.state = State.RUNNING
|
||||
context = MagicMock()
|
||||
context.args = []
|
||||
telegram._forcesell(update=update, context=context)
|
||||
telegram._forceexit(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]
|
||||
|
||||
@ -1128,7 +1128,7 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None:
|
||||
# /forcesell 123456
|
||||
context = MagicMock()
|
||||
context.args = ["123456"]
|
||||
telegram._forcesell(update=update, context=context)
|
||||
telegram._forceexit(update=update, context=context)
|
||||
assert msg_mock.call_count == 1
|
||||
assert 'invalid argument' in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user