From 58ceda4b903172d153d34337c9bb4f72a2dda4dc Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 22 Jan 2020 19:54:55 +0100 Subject: [PATCH 1/2] update wallets after forcesell --- freqtrade/rpc/rpc.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index d58b99f39..c38f36c1d 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -425,6 +425,7 @@ class RPC: for trade in Trade.get_open_trades(): _exec_forcesell(trade) Trade.session.flush() + self._freqtrade.wallets.update() return {'result': 'Created sell orders for all open trades.'} # Query for trade @@ -437,6 +438,7 @@ class RPC: _exec_forcesell(trade) Trade.session.flush() + self._freqtrade.wallets.update() return {'result': f'Created sell order for trade {trade_id}.'} def _rpc_forcebuy(self, pair: str, price: Optional[float]) -> Optional[Trade]: From aad10ceee35303370c4a01a1b88e92691b01b615 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 22 Jan 2020 20:50:09 +0100 Subject: [PATCH 2/2] Add threading lock object for /forcesell Protects against stoploss_on_exchange order recreation in case of /forcesell (it's a timing issue, so may or may not happen). --- freqtrade/freqtradebot.py | 16 +++++++++++----- freqtrade/rpc/rpc.py | 37 +++++++++++++++++++------------------ 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 387ddb063..e3856e200 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -7,6 +7,7 @@ import traceback from datetime import datetime from math import isclose from os import getpid +from threading import Lock from typing import Any, Dict, List, Optional, Tuple import arrow @@ -27,7 +28,6 @@ from freqtrade.state import State from freqtrade.strategy.interface import IStrategy, SellType from freqtrade.wallets import Wallets - logger = logging.getLogger(__name__) @@ -92,6 +92,8 @@ class FreqtradeBot: # the initial state of the bot. # Keep this at the end of this initialization method. self.rpc: RPCManager = RPCManager(self) + # Protect sell-logic from forcesell and viceversa + self._sell_lock = Lock() def cleanup(self) -> None: """ @@ -132,8 +134,12 @@ class FreqtradeBot: self.dataprovider.refresh(self._create_pair_whitelist(self.active_pair_whitelist), self.strategy.informative_pairs()) - # First process current opened trades (positions) - self.exit_positions(trades) + # Protect from collisions with forcesell. + # Without this, freqtrade my try to recreate stoploss_on_exchange orders + # while selling is in process, since telegram messages arrive in an different thread. + with self._sell_lock: + # First process current opened trades (positions) + self.exit_positions(trades) # Then looking for buy opportunities if self.get_free_open_trades(): @@ -748,8 +754,8 @@ class FreqtradeBot: Check and execute sell """ should_sell = self.strategy.should_sell( - trade, sell_rate, datetime.utcnow(), buy, sell, - force_stoploss=self.edge.stoploss(trade.pair) if self.edge else 0 + trade, sell_rate, datetime.utcnow(), buy, sell, + force_stoploss=self.edge.stoploss(trade.pair) if self.edge else 0 ) if should_sell.sell_flag: diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index c38f36c1d..41097c211 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -420,26 +420,27 @@ class RPC: if self._freqtrade.state != State.RUNNING: raise RPCException('trader is not running') - if trade_id == 'all': - # Execute sell for all open orders - for trade in Trade.get_open_trades(): - _exec_forcesell(trade) + with self._freqtrade._sell_lock: + if trade_id == 'all': + # Execute sell for all open orders + for trade in Trade.get_open_trades(): + _exec_forcesell(trade) + Trade.session.flush() + self._freqtrade.wallets.update() + return {'result': 'Created sell orders for all open trades.'} + + # Query for trade + trade = Trade.get_trades( + trade_filter=[Trade.id == trade_id, Trade.is_open.is_(True), ] + ).first() + if not trade: + logger.warning('forcesell: Invalid argument received') + raise RPCException('invalid argument') + + _exec_forcesell(trade) Trade.session.flush() self._freqtrade.wallets.update() - return {'result': 'Created sell orders for all open trades.'} - - # Query for trade - trade = Trade.get_trades( - trade_filter=[Trade.id == trade_id, Trade.is_open.is_(True), ] - ).first() - if not trade: - logger.warning('forcesell: Invalid argument received') - raise RPCException('invalid argument') - - _exec_forcesell(trade) - Trade.session.flush() - self._freqtrade.wallets.update() - return {'result': f'Created sell order for trade {trade_id}.'} + return {'result': f'Created sell order for trade {trade_id}.'} def _rpc_forcebuy(self, pair: str, price: Optional[float]) -> Optional[Trade]: """