Repository: freqtrade/freqtrade

Issue:
Hyperopt loss going wrong way ?
<!-- 
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

  * Operating system: ____ Manjaro
  * Python Version: _____ Python 3.11.8
  * CCXT version: _____ ccxt==4.2.76
  * Freqtrade Version: ____ freqtrade 2024.4
  
Note: All issues other than enhancement requests will be closed without further comment if the above template is deleted or not filled out.

## Describe the problem:

Hi. It seems loss function score is broken somehow (maybe smthg to do with its sign).
Example below (as on 2024-05-07) says #5 is best while obviously it should be #10.
Regards.

### Steps to reproduce:

  1. command used:
  `docker-compose run --rm  freqtrade hyperopt --disable-param-export --config ./user_data/config_binanceWEB.json --strategy TAZ_15m_binance000 --timerange=20230901-20240501 --spaces buy --hyperopt-loss ProfitDrawDownHyperOptLoss --epoch 1000 --timeframe 15m --timeframe-detail 1m --j 1`
  
### Observed Results:
```
+--------+---------+----------+--------------------------+--------------+---------------------------+----------------+--------------+---------------------------+
|   Best |   Epoch |   Trades |    Win  Draw  Loss  Win% |   Avg profit |                    Profit |   Avg duration |    Objective |       Max Drawdown (Acct) |
|--------+---------+----------+--------------------------+--------------+---------------------------+----------------+--------------+---------------------------|
| * Best |   1/135 |        4 |      3     0     1  75.0 |        1.59% |        30.785     (3.08%) |        1:35:00 |    -29.70234 |         3.559     (0.35%) |
| *      |   2/135 |       11 |      8     0     3  72.7 |       -0.94% |      -221.333   (-22.13%) |        0:24:00 |    247.98383 |       145.339    (15.98%) |
| *      |   3/135 |       51 |     42     0     9  82.4 |        0.43% |      -315.876   (-31.59%) |        0:54:00 |     87.71371 |       417.773    (40.04%) |
| *      |   4/135 |        1 |      1     0     0   100 |        2.29% |         3.795     (0.38%) |        0:00:00 |     -3.79499 |                        -- |
| * Best |   5/135 |       51 |     42     0     9  82.4 |        0.11% |      -459.923   (-45.99%) |        0:28:00 | -4,180.04668 |       510.315    (50.84%) |
| *      |   6/135 |      170 |    145     0    25  85.3 |        0.69% |      -581.544   (-58.15%) |        0:52:00 |   -100.19843 |       721.638    (68.98%) |
| *      |   7/135 |        2 |      2     0     0   100 |        2.29% |         7.604     (0.76%) |        0:00:00 |     -7.60435 |                        -- |
| *      |   8/135 |       15 |     14     0     1  93.3 |        1.78% |        64.167     (6.42%) |        1:46:00 |    -59.80514 |        56.433     (5.31%) |
| *      |   9/135 |      184 |    158     0    26  85.9 |        0.74% |      -628.566   (-62.86%) |        0:46:00 |     11.19232 |       790.266    (74.53%) |
| *      |  10/135 |      190 |    174     0    16  91.6 |        1.48% |       602.105    (60.21%) |        1:01:00 |   -586.82945 |       304.910    (16.04%) |
| *      |  11/135 |        5 |      5     0     0   100 |        2.29% |        22.971     (2.30%) |        0:03:00 |    -22.97094 |                        -- |
| *      |  12/135 |      345 |    298     0    47  86.4 |        0.94% |      -494.568   (-49.46%) |        1:21:00 |     17.80432 |       746.868    (70.59%) |
| *      |  13/135 |       44 |     37     0     7  84.1 |        0.64% |      -251.262   (-25.13%) |        0:55:00 |   -624.95887 |       352.895    (35.02%) |
| *      |  14/135 |        2 |      2     0     0   100 |        2.29% |         7.604     (0.76%) |        0:00:00 |     -7.60435 |                        -- |
| *      |  15/135 |        4 |      4     0     0   100 |        2.29% |        15.263     (1.53%) |        0:01:00 |    -15.26296 |                        -- |
| *      |  16/135 |       49 |     40     0     9  81.6 |        0.20% |      -429.174   (-42.92%) |        0:35:00 |   -901.90677 |       471.865    (46.65%) |
| *      |  17/135 |      362 |    310     0    52  85.6 |        0.79% |      -759.062   (-75.91%) |        1:10:00 | -1,031.79724 |       842.359    (82.04%) |
```


Discussion:
Well the calculation is not really ideal - that's also been reported in #9563 and #9819.

The sign is however not "really" the problem (at least not the only one) - it's the overall combination in the formula - as well as the (at least in part, ignorance of the range of values a value can reach.

While the variable says "max_drawdown_abs" - it's really a relative value that's retrieved.
it's however NOT bound to be between 0 and one - but rather, it's unbound in it's upper bound, as the account level drawdown can be > 100% (it's drawdown from the high point, not from starting balance).

So if your strategy does great, your "high" value may be 100% profit (from 1000$ to 2000$) - but then it's in a bad run and loses 1200$ (your net-loss is 200$). 
What you'd get then is ~1.2 (120%).

---

for your particular result - if you simply invert the sign - you'd get `#5` as "worst" result ... but `#2` as best ... which is still a rather losing strategy.
`#12` would be pretty high up, too - which is losing AND has a pretty high drawdown - both not very ideal situations.


---

In reality / summary - i think the way this loss function combines the values is not ideal - and I'm more than happy to get a PR that sees it improved.

The (simple, from a user perspective) alternative is to pick a different loss function - which there's plenty of - and all try to search for different "improvements".
Well, thanks for the comprehensive answer. I guess I'll stick with OnlyProfitHyperOptLoss and manually discard frightening drawdowns for now.

Resolving patch:
diff --git a/freqtrade/optimize/hyperopt_loss/hyperopt_loss_profit_drawdown.py b/freqtrade/optimize/hyperopt_loss/hyperopt_loss_profit_drawdown.py
--- a/freqtrade/optimize/hyperopt_loss/hyperopt_loss_profit_drawdown.py
+++ b/freqtrade/optimize/hyperopt_loss/hyperopt_loss_profit_drawdown.py
@@ -10,23 +10,26 @@
 
 from pandas import DataFrame
 
+from freqtrade.constants import Config
 from freqtrade.data.metrics import calculate_max_drawdown
 from freqtrade.optimize.hyperopt import IHyperOptLoss
 
 
-# higher numbers penalize drawdowns more severely
+# smaller numbers penalize drawdowns more severely
 DRAWDOWN_MULT = 0.075
 
 
 class ProfitDrawDownHyperOptLoss(IHyperOptLoss):
     @staticmethod
-    def hyperopt_loss_function(results: DataFrame, trade_count: int, *args, **kwargs) -> float:
+    def hyperopt_loss_function(results: DataFrame, config: Config, *args, **kwargs) -> float:
         total_profit = results["profit_abs"].sum()
 
         try:
-            drawdown = calculate_max_drawdown(results, value_col="profit_abs")
+            drawdown = calculate_max_drawdown(
+                results, starting_balance=config["dry_run_wallet"], value_col="profit_abs"
+            )
             relative_account_drawdown = drawdown.relative_account_drawdown
         except ValueError:
             relative_account_drawdown = 0
 
-        return -1 * (total_profit * (1 - relative_account_drawdown * DRAWDOWN_MULT))
+        return -1 * (total_profit - (relative_account_drawdown * total_profit) / DRAWDOWN_MULT)


Test patch: