Repository: freqtrade/freqtrade

Issue:
Forcesell didn't update Balance
## Step 1: Have you search for this issue before posting it?

Not found a similar question

## Step 2: Describe your environment

  * Operating system: Ubuntu 18.04.3 LTS
  * Python Version: 2.7.17 / 3.6.9____ (`python -V`)
  * CCXT version: _____ (`pip freeze | grep ccxt`)
  * Branch: Master
  * Last Commit ID: 7c53dcb0afd2ad663faad4bc03406f0575c87f6b(`git log --format="%H" -n 1`)
  
## Step 3: Describe the problem:
After /Forcesell XX Balance isn't updated.
Before Forcesell : 
BTC:
 Available:  0.00274315
After Forcesell : 
BTC:
 Available:  0.00274315

Balance on Exchange (Binance) 
0.00326254

Looks like /forcesell sells the given Amount but didn't update the Balance (BTC).

### Steps to reproduce:

  1. /balance
  2. /forcesell XX (Telegram-Bot)
  3. /balance
  


Discussion:
A few questions first:

does /balance also contain the balance of XX (the sold currency) - or is this gone in the 2nd call?

I think this should be fixed in #2768, in combination with #2734 most likely.

One thing to consier about this: 
`/balance` is a convenient feature - however calling it too often will lead to blocks on the exchange - therefore manually calling it is limited to 1 per hour (using the latest develop) - this is not implemented in master (what you are currently using) at all.

Also, it's not intended to replace looking at balance on the exchange - /balance does not claim to be 100% uptodate all the time and may be lagging behind.
It looks like : 
/forcesell has sold the coins against BTC
/balance didn't notice, it's the same after several hours.
A /balance after a buy shows the right amount of BTC.

The steps of the balance are only recognisable if you calculate them in your poket calculator.

Maybe you can alter the message of /balance, that it will only be checked if a buy/sell occurs.

To your question above, I was only looking at the BTC-balance, which was the same.
After 3 hours it was still the same amount.

After a buy the /balance is right, and it's also right for all other coins. (some had wrong value)

#2768 is the same, thank you for finding.
So we can close this problem.

i'll have a closer look at the code before closing ... but it should (in theory) be fixed in the latest develop version - assuming a maximum of 1h delay is acceptable.

we maybe should put this as a note in the documentation ...
You're right - in case of forcesell wallets are not updated if the trade fills immediately (either via market order - or by luck with a limit order).

should be a quick fix

Resolving patch:
diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py
--- a/freqtrade/freqtradebot.py
+++ b/freqtrade/freqtradebot.py
@@ -7,6 +7,7 @@
 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.strategy.interface import IStrategy, SellType
 from freqtrade.wallets import Wallets
 
-
 logger = logging.getLogger(__name__)
 
 
@@ -92,6 +92,8 @@ def __init__(self, config: Dict[str, Any]) -> None:
         # 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 @@ def process(self) -> None:
         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 @@ def _check_and_execute_sell(self, trade: Trade, sell_rate: float,
         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
--- a/freqtrade/rpc/rpc.py
+++ b/freqtrade/rpc/rpc.py
@@ -420,24 +420,27 @@ def _exec_forcesell(trade: Trade) -> None:
         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()
-            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()
-        return {'result': f'Created sell order for trade {trade_id}.'}
+            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]:
         """


Test patch: