Repository: freqtrade/freqtrade

Issue:
Freqtrade sometimes reports trades wrongly on kucoin
<!-- 
Have you searched for similar issues before posting it?

If you have discovered a bug in the bot, please [search the issue tracker](https://github.com/freqtrade/freqtrade/issues?q=is%3Aissue). 
If it hasn't been reported, please create a new issue.

Please do not use bug reports to request new features.
-->

## Describe your environment
  * OS: Ubuntu 22.04.3 LTS
  * Freqtrade Version: 2023.9 stable (docker compose)

## Describe the problem:

on some trades the exit does not report properly. There it says it has more loss on spot with selling than it is having $ in the wallet for all trade slots

10 recent trades:
Close Date    Pair (ID)          Profit (USDT)
------------  -----------------  ------------------------
8 hours ago   IGU/USDT (#385)    1.85% (...)
12 hours ago  IGU/USDT (#384)    1.26% (...)
13 hours ago  IGU/USDT (#383)    12.93% (...)
13 hours ago  IGU/USDT (#382)    -36.45% (...)
14 hours ago  IGU/USDT (#381)    1.12% (...)
15 hours ago  IGU/USDT (#380)    3.79% (...)
21 hours ago  IGU/USDT (#379)    1.23% (...)
a day ago     UPO/USDT (#378)    1.84% (...)
a day ago     UPO/USDT (#377)    1.55% (...)
**2 days ago    FLOKI/USDT (#376)  -92.27% (-8270.49735089)**

### Steps to reproduce:
it happens every now and then on kucoin on live, the other trade right beside it had no issues at all. The behavior is there every now and then since a few months. 

The open rate is proper, but the close rate is not. it is 1/10th of what it should be, seems kucoin switched the rate-factorization around?
  
### Observed Results:

  * What happened?
a trade was done as usual, but one of the two almost parallel trades were reporting exits wrong.
Kucoin might have switched its' rate calculation up during the trade
  * What did you expect to happen?
report exits properly, as the orders are showing should be too
If the calculation of the pair switches, don't assume automatically that it stayed the same - get the value of the rate including its factorization so every value is properly caught

### Relevant code exceptions or logs
![grafik](https://github.com/freqtrade/freqtrade/assets/41228167/225ac0a5-4ef8-4fa5-bccf-5e83db5f98b1)
![grafik](https://github.com/freqtrade/freqtrade/assets/41228167/9674a363-7d69-4cfc-87ac-06e7c2e4bd60)
![grafik](https://github.com/freqtrade/freqtrade/assets/41228167/eb94509c-8ceb-44d1-842b-d94f13114426)

![grafik](https://github.com/freqtrade/freqtrade/assets/41228167/4592d1ed-2df5-46cf-969f-51c06f760c2c)

Freqtrade sometimes reports trades wrongly on kucoin
<!-- 
Have you searched for similar issues before posting it?

If you have discovered a bug in the bot, please [search the issue tracker](https://github.com/freqtrade/freqtrade/issues?q=is%3Aissue). 
If it hasn't been reported, please create a new issue.

Please do not use bug reports to request new features.
-->

## Describe your environment
  * OS: Ubuntu 22.04.3 LTS
  * Freqtrade Version: 2023.9 stable (docker compose)

## Describe the problem:

on some trades the exit does not report properly. There it says it has more loss on spot with selling than it is having $ in the wallet for all trade slots

10 recent trades:
Close Date    Pair (ID)          Profit (USDT)
------------  -----------------  ------------------------
8 hours ago   IGU/USDT (#385)    1.85% (...)
12 hours ago  IGU/USDT (#384)    1.26% (...)
13 hours ago  IGU/USDT (#383)    12.93% (...)
13 hours ago  IGU/USDT (#382)    -36.45% (...)
14 hours ago  IGU/USDT (#381)    1.12% (...)
15 hours ago  IGU/USDT (#380)    3.79% (...)
21 hours ago  IGU/USDT (#379)    1.23% (...)
a day ago     UPO/USDT (#378)    1.84% (...)
a day ago     UPO/USDT (#377)    1.55% (...)
**2 days ago    FLOKI/USDT (#376)  -92.27% (-8270.49735089)**

### Steps to reproduce:
it happens every now and then on kucoin on live, the other trade right beside it had no issues at all. The behavior is there every now and then since a few months. 

The open rate is proper, but the close rate is not. it is 1/10th of what it should be, seems kucoin switched the rate-factorization around?
  
### Observed Results:

  * What happened?
a trade was done as usual, but one of the two almost parallel trades were reporting exits wrong.
Kucoin might have switched its' rate calculation up during the trade
  * What did you expect to happen?
report exits properly, as the orders are showing should be too
If the calculation of the pair switches, don't assume automatically that it stayed the same - get the value of the rate including its factorization so every value is properly caught

### Relevant code exceptions or logs
![grafik](https://github.com/freqtrade/freqtrade/assets/41228167/225ac0a5-4ef8-4fa5-bccf-5e83db5f98b1)
![grafik](https://github.com/freqtrade/freqtrade/assets/41228167/9674a363-7d69-4cfc-87ac-06e7c2e4bd60)
![grafik](https://github.com/freqtrade/freqtrade/assets/41228167/eb94509c-8ceb-44d1-842b-d94f13114426)

![grafik](https://github.com/freqtrade/freqtrade/assets/41228167/4592d1ed-2df5-46cf-969f-51c06f760c2c)



Discussion:
Well the problem is quite simple (although unfortunate, and actually known to be an issue for kucoin).

The precision values for FLOKI are `'amount': 1e-10, 'price': 1e-09`  - and adding these together actually results in leaving behind a 1e-8 amount.

(extracted from the following command):
``` python
import ccxt
exchange = ccxt.kucoin()
lm = exchange.load_markets()
print(lm['FLOKI/USDT'])
```

Loading this trade in an interactive environment (db was shared in private) - this can be quite easily seen:

![image](https://github.com/freqtrade/freqtrade/assets/5024695/309584fd-3a57-4ef4-b87c-eb3e6764db34)

![image](https://github.com/freqtrade/freqtrade/assets/5024695/a54123dd-9e52-424c-9733-76cf2b75218c)


The end-result does not even out evenly.

``` py
> print(f"{11511963.8634448908 + 11750101.7743937783:.15f}")
'23262065.637838669121265'
> print(f"{23262065.6378386617:.15f}")
'23262065.637838661670685'
```

Now i know that kucoin is returning these values via API (up to 16 digits iirc) - but IIRC the UI is applying some kind of rounding on top of it, so you might not see the 16 decimals.

---

Please check this trade (or rather, it's orders) in the UI - and get me some screenshots for the 3 orders that apply to this trade. 
I hope to gain some details on the actual rounding they apply. If we can round it the same way, it might be able to have it even out correctly.

It'd also be interesting / important to see if this trade has really left 1e-8 floki behind as the sum of the individual order amounts suggests.
> Please check this trade (or rather, it's orders) in the UI - and get me some screenshots for the 3 orders that apply to this trade.
> I hope to gain some details on the actual rounding they apply. If we can round it the same way, it might be able to have it even out correctly.

![grafik](https://github.com/freqtrade/freqtrade/assets/41228167/7096c69f-72bf-46f7-ba23-3a7906c0a14a)
![grafik](https://github.com/freqtrade/freqtrade/assets/41228167/89754934-0a68-4b5b-9caa-1a12eb3d0061)

Well the problem is quite simple (although unfortunate, and actually known to be an issue for kucoin).

The precision values for FLOKI are `'amount': 1e-10, 'price': 1e-09`  - and adding these together actually results in leaving behind a 1e-8 amount.

(extracted from the following command):
``` python
import ccxt
exchange = ccxt.kucoin()
lm = exchange.load_markets()
print(lm['FLOKI/USDT'])
```

Loading this trade in an interactive environment (db was shared in private) - this can be quite easily seen:

![image](https://github.com/freqtrade/freqtrade/assets/5024695/309584fd-3a57-4ef4-b87c-eb3e6764db34)

![image](https://github.com/freqtrade/freqtrade/assets/5024695/a54123dd-9e52-424c-9733-76cf2b75218c)


The end-result does not even out evenly.

``` py
> print(f"{11511963.8634448908 + 11750101.7743937783:.15f}")
'23262065.637838669121265'
> print(f"{23262065.6378386617:.15f}")
'23262065.637838661670685'
```

Now i know that kucoin is returning these values via API (up to 16 digits iirc) - but IIRC the UI is applying some kind of rounding on top of it, so you might not see the 16 decimals.

---

Please check this trade (or rather, it's orders) in the UI - and get me some screenshots for the 3 orders that apply to this trade. 
I hope to gain some details on the actual rounding they apply. If we can round it the same way, it might be able to have it even out correctly.

It'd also be interesting / important to see if this trade has really left 1e-8 floki behind as the sum of the individual order amounts suggests.
> Please check this trade (or rather, it's orders) in the UI - and get me some screenshots for the 3 orders that apply to this trade.
> I hope to gain some details on the actual rounding they apply. If we can round it the same way, it might be able to have it even out correctly.

![grafik](https://github.com/freqtrade/freqtrade/assets/41228167/7096c69f-72bf-46f7-ba23-3a7906c0a14a)
![grafik](https://github.com/freqtrade/freqtrade/assets/41228167/89754934-0a68-4b5b-9caa-1a12eb3d0061)


Resolving patch:
diff --git a/freqtrade/persistence/trade_model.py b/freqtrade/persistence/trade_model.py
--- a/freqtrade/persistence/trade_model.py
+++ b/freqtrade/persistence/trade_model.py
@@ -1053,7 +1053,7 @@ def recalc_trade_from_orders(self, *, is_closing: bool = False):
                 price = avg_price if is_exit else tmp_price
                 current_stake += price * tmp_amount * side
 
-                if current_amount > ZERO:
+                if current_amount > ZERO and not is_exit:
                     avg_price = current_stake / current_amount
 
             if is_exit:


Test patch:
diff --git a/tests/persistence/test_persistence.py b/tests/persistence/test_persistence.py
--- a/tests/persistence/test_persistence.py
+++ b/tests/persistence/test_persistence.py
@@ -2302,6 +2302,101 @@ def test_recalc_trade_from_orders(fee):
     assert pytest.approx(trade.open_trade_value) == o1_trade_val + o2_trade_val + o3_trade_val
 
 
+@pytest.mark.usefixtures("init_persistence")
+def test_recalc_trade_from_orders_kucoin():
+    # Taken from https://github.com/freqtrade/freqtrade/issues/9346
+    o1_amount = 11511963.8634448908
+    o2_amount = 11750101.7743937783
+    o3_amount = 23262065.6378386617  # Exit amount - barely doesn't even out
+
+    res = o1_amount + o2_amount - o3_amount
+    assert res > 0.0
+    assert res < 0.1
+    o1_rate = 0.000029901
+    o2_rate = 0.000029295
+    o3_rate = 0.000029822
+
+    o1_cost = o1_amount * o1_rate
+
+    trade = Trade(
+        pair='FLOKI/USDT',
+        stake_amount=o1_cost,
+        open_date=dt_now() - timedelta(hours=2),
+        amount=o1_amount,
+        fee_open=0.001,
+        fee_close=0.001,
+        exchange='binance',
+        open_rate=o1_rate,
+        max_rate=o1_rate,
+        leverage=1,
+    )
+    # Check with 1 order
+    order1 = Order(
+        ft_order_side='buy',
+        ft_pair=trade.pair,
+        ft_is_open=False,
+        status="closed",
+        symbol=trade.pair,
+        order_type="market",
+        side="buy",
+        price=o1_rate,
+        average=o1_rate,
+        filled=o1_amount,
+        remaining=0,
+        cost=o1_cost,
+        order_date=trade.open_date,
+        order_filled_date=trade.open_date,
+    )
+    trade.orders.append(order1)
+    order2 = Order(
+        ft_order_side='buy',
+        ft_pair=trade.pair,
+        ft_is_open=False,
+        status="closed",
+        symbol=trade.pair,
+        order_type="market",
+        side="buy",
+        price=o2_rate,
+        average=o2_rate,
+        filled=o2_amount,
+        remaining=0,
+        cost=o2_amount * o2_rate,
+        order_date=trade.open_date,
+        order_filled_date=trade.open_date,
+    )
+    trade.orders.append(order2)
+    trade.recalc_trade_from_orders()
+    assert trade.amount == o1_amount + o2_amount
+    profit = trade.calculate_profit(o3_rate)
+    assert profit.profit_abs == pytest.approx(3.90069871)
+    assert profit.profit_ratio == pytest.approx(0.00566035)
+
+    order3 = Order(
+        ft_order_side='sell',
+        ft_pair=trade.pair,
+        ft_is_open=False,
+        status="closed",
+        symbol=trade.pair,
+        order_type="market",
+        side="sell",
+        price=o3_rate,
+        average=o3_rate,
+        filled=o3_amount,
+        remaining=0,
+        cost=o2_amount * o2_rate,
+        order_date=trade.open_date,
+        order_filled_date=trade.open_date,
+    )
+
+    trade.orders.append(order3)
+    trade.update_trade(order3)
+    assert trade.is_open is False
+    # Trade closed correctly - but left a minimal amount.
+    assert trade.amount == 8e-09
+    assert trade.close_profit_abs == 3.90069871
+    assert trade.close_profit == 0.00566035
+
+
 @pytest.mark.parametrize('is_short', [True, False])
 def test_recalc_trade_from_orders_ignores_bad_orders(fee, is_short):