/forcesell: handle trades with open orders
This commit is contained in:
parent
6e68315d2c
commit
ae37f49b51
@ -96,21 +96,27 @@ class Trade(_DECL_BASE):
|
|||||||
self.open_rate = order['rate']
|
self.open_rate = order['rate']
|
||||||
self.amount = order['amount']
|
self.amount = order['amount']
|
||||||
logger.info('LIMIT_BUY has been fulfilled for %s.', self)
|
logger.info('LIMIT_BUY has been fulfilled for %s.', self)
|
||||||
|
self.open_order_id = None
|
||||||
elif order['type'] == 'LIMIT_SELL':
|
elif order['type'] == 'LIMIT_SELL':
|
||||||
# Set close rate and set actual profit
|
self.close(order['rate'])
|
||||||
self.close_rate = order['rate']
|
else:
|
||||||
|
raise ValueError('Unknown order type: {}'.format(order['type']))
|
||||||
|
Trade.session.flush()
|
||||||
|
|
||||||
|
def close(self, rate: float) -> None:
|
||||||
|
"""
|
||||||
|
Sets close_rate to the given rate, calculates total profit
|
||||||
|
and marks trade as closed
|
||||||
|
"""
|
||||||
|
self.close_rate = rate
|
||||||
self.close_profit = self.calc_profit()
|
self.close_profit = self.calc_profit()
|
||||||
self.close_date = datetime.utcnow()
|
self.close_date = datetime.utcnow()
|
||||||
self.is_open = False
|
self.is_open = False
|
||||||
|
self.open_order_id = None
|
||||||
logger.info(
|
logger.info(
|
||||||
'Marking %s as closed as the trade is fulfilled and found no open orders for it.',
|
'Marking %s as closed as the trade is fulfilled and found no open orders for it.',
|
||||||
self
|
self
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
raise ValueError('Unknown order type: {}'.format(order['type']))
|
|
||||||
|
|
||||||
self.open_order_id = None
|
|
||||||
Trade.session.flush()
|
|
||||||
|
|
||||||
def calc_profit(self, rate: Optional[float] = None) -> float:
|
def calc_profit(self, rate: Optional[float] = None) -> float:
|
||||||
"""
|
"""
|
||||||
|
@ -371,6 +371,28 @@ def _stop(bot: Bot, update: Update) -> None:
|
|||||||
send_msg('*Status:* `already stopped`', bot=bot)
|
send_msg('*Status:* `already stopped`', bot=bot)
|
||||||
|
|
||||||
|
|
||||||
|
def _exec_forcesell(trade: Trade) -> None:
|
||||||
|
# Check if there is there is an open order
|
||||||
|
if trade.open_order_id:
|
||||||
|
order = exchange.get_order(trade.open_order_id)
|
||||||
|
|
||||||
|
# Cancel open LIMIT_BUY orders and close trade
|
||||||
|
if order and not order['closed'] and order['type'] == 'LIMIT_BUY':
|
||||||
|
exchange.cancel_order(trade.open_order_id)
|
||||||
|
trade.close(order.get('rate', trade.open_rate))
|
||||||
|
# TODO: sell amount which has been bought already
|
||||||
|
return
|
||||||
|
|
||||||
|
# Ignore trades with an attached LIMIT_SELL order
|
||||||
|
if order and not order['closed'] and order['type'] == 'LIMIT_SELL':
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get current rate and execute sell
|
||||||
|
current_rate = exchange.get_ticker(trade.pair)['bid']
|
||||||
|
from freqtrade.main import execute_sell
|
||||||
|
execute_sell(trade, current_rate)
|
||||||
|
|
||||||
|
|
||||||
@authorized_only
|
@authorized_only
|
||||||
def _forcesell(bot: Bot, update: Update) -> None:
|
def _forcesell(bot: Bot, update: Update) -> None:
|
||||||
"""
|
"""
|
||||||
@ -388,10 +410,7 @@ def _forcesell(bot: Bot, update: Update) -> None:
|
|||||||
if trade_id == 'all':
|
if trade_id == 'all':
|
||||||
# Execute sell for all open orders
|
# Execute sell for all open orders
|
||||||
for trade in Trade.query.filter(Trade.is_open.is_(True)).all():
|
for trade in Trade.query.filter(Trade.is_open.is_(True)).all():
|
||||||
# Get current rate
|
_exec_forcesell(trade)
|
||||||
current_rate = exchange.get_ticker(trade.pair)['bid']
|
|
||||||
from freqtrade.main import execute_sell
|
|
||||||
execute_sell(trade, current_rate)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# Query for trade
|
# Query for trade
|
||||||
@ -403,10 +422,8 @@ def _forcesell(bot: Bot, update: Update) -> None:
|
|||||||
send_msg('Invalid argument. See `/help` to view usage')
|
send_msg('Invalid argument. See `/help` to view usage')
|
||||||
logger.warning('/forcesell: Invalid argument received')
|
logger.warning('/forcesell: Invalid argument received')
|
||||||
return
|
return
|
||||||
# Get current rate
|
|
||||||
current_rate = exchange.get_ticker(trade.pair)['bid']
|
_exec_forcesell(trade)
|
||||||
from freqtrade.main import execute_sell
|
|
||||||
execute_sell(trade, current_rate)
|
|
||||||
|
|
||||||
|
|
||||||
@authorized_only
|
@authorized_only
|
||||||
|
@ -14,7 +14,8 @@ from freqtrade.misc import update_state, State, get_state
|
|||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
from freqtrade.rpc import telegram
|
from freqtrade.rpc import telegram
|
||||||
from freqtrade.rpc.telegram import authorized_only, is_enabled, send_msg, _status, _status_table, \
|
from freqtrade.rpc.telegram import authorized_only, is_enabled, send_msg, _status, _status_table, \
|
||||||
_profit, _forcesell, _performance, _daily, _count, _start, _stop, _balance, _version, _help
|
_profit, _forcesell, _performance, _daily, _count, _start, _stop, _balance, _version, _help, \
|
||||||
|
_exec_forcesell
|
||||||
|
|
||||||
|
|
||||||
def test_is_enabled(default_conf, mocker):
|
def test_is_enabled(default_conf, mocker):
|
||||||
@ -220,6 +221,30 @@ def test_forcesell_handle(default_conf, update, ticker, mocker):
|
|||||||
assert '0.07256061 (profit: ~-0.64%)' in rpc_mock.call_args_list[-1][0][0]
|
assert '0.07256061 (profit: ~-0.64%)' in rpc_mock.call_args_list[-1][0][0]
|
||||||
|
|
||||||
|
|
||||||
|
def test_exec_forcesell_open_orders(default_conf, ticker, mocker):
|
||||||
|
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||||
|
cancel_order_mock = MagicMock()
|
||||||
|
mocker.patch.multiple('freqtrade.main.exchange',
|
||||||
|
get_ticker=ticker,
|
||||||
|
get_order=MagicMock(return_value={
|
||||||
|
'closed': None,
|
||||||
|
'type': 'LIMIT_BUY',
|
||||||
|
}),
|
||||||
|
cancel_order=cancel_order_mock)
|
||||||
|
trade = Trade(
|
||||||
|
pair='BTC_ETH',
|
||||||
|
open_rate=1,
|
||||||
|
exchange='BITTREX',
|
||||||
|
open_order_id='123456789',
|
||||||
|
amount=1,
|
||||||
|
fee=0.0,
|
||||||
|
)
|
||||||
|
_exec_forcesell(trade)
|
||||||
|
|
||||||
|
assert cancel_order_mock.call_count == 1
|
||||||
|
assert trade.is_open is False
|
||||||
|
|
||||||
|
|
||||||
def test_forcesell_all_handle(default_conf, update, ticker, mocker):
|
def test_forcesell_all_handle(default_conf, update, ticker, mocker):
|
||||||
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
||||||
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True)
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True)
|
||||||
|
Loading…
Reference in New Issue
Block a user