]> Piment Noir Git Repositories - freqai-strategies.git/commitdiff
fix(quickadapter): preserve take-profit JSON history (#145)
authorJérôme Benoit <jerome.benoit@piment-noir.org>
Tue, 28 Jul 2026 15:15:27 +0000 (17:15 +0200)
committerGitHub <noreply@github.com>
Tue, 28 Jul 2026 15:15:27 +0000 (17:15 +0200)
* fix(quickadapter): preserve take-profit JSON history

* refactor(quickadapter): drop over-engineered take-profit history migration

Remove the history_v2 versioned key, its legacy-migration branches, the
scalar cd_type/JSON-string recovery paths and the getter write side effect.
The history custom-data row has only ever been written as a dict, so its
cd_type is always "dict" and those defensive branches are unreachable dead
code; a stored dict round-trips as a dict with list fields, and the shared
callback wrapper logs rather than silently swallows exceptions.

_get_trade_history reverts to the minimal reader keyed by the literal
"history", matching the other custom-data row keys in this file. The
take-profit JSON fix for #134 is preserved: the _TakeProfitHistoryEntry
alias keeps the list arm and safe_append_trade_take_profit_price still
accepts a two-element tuple or list with a non-bool int stage and non-bool
numeric price.

quickadapter/user_data/strategies/QuickAdapterV3.py

index 4ef5055e4b6e3b2758eade20785717bf9386a71b..c81dcec0710e54bf689b61f9e5aa81ad6bc87cd3 100644 (file)
@@ -105,6 +105,7 @@ CandleDeviationCacheKey = tuple[
     str, DfSignature, float, float, int, InterpolationDirection, float
 ]
 CandleThresholdCacheKey = tuple[str, DfSignature, str, int, float, float]
+_TakeProfitHistoryEntry = float | tuple[int, float] | list[int | float]
 
 
 class _TradeHistory(TypedDict):
@@ -112,7 +113,7 @@ class _TradeHistory(TypedDict):
     # _UNREALIZED_PNL_TIMEFRAME_KEY / _LEGACY_UNREALIZED_PNL_TIMEFRAME_MINUTES_KEY
     # constants (a TypedDict field cannot reference a constant).
     unrealized_pnl: list[float]
-    take_profit_price: list[float | tuple[int, float]]
+    take_profit_price: list[_TakeProfitHistoryEntry]
     unrealized_pnl_candle_date: NotRequired[str]
     unrealized_pnl_timeframe: NotRequired[str]
     unrealized_pnl_timeframe_minutes: NotRequired[int]
@@ -1540,7 +1541,7 @@ class QuickAdapterV3(IStrategy):
     @staticmethod
     def get_trade_take_profit_price_history(
         trade: Trade,
-    ) -> list[float | tuple[int, float]]:
+    ) -> list[_TakeProfitHistoryEntry]:
         history = QuickAdapterV3._get_trade_history(trade)
         return history.get("take_profit_price", [])
 
@@ -1619,7 +1620,7 @@ class QuickAdapterV3(IStrategy):
 
     def append_trade_take_profit_price(
         self, trade: Trade, take_profit_price: float, exit_stage: int
-    ) -> list[float | tuple[int, float]]:
+    ) -> list[_TakeProfitHistoryEntry]:
         history = QuickAdapterV3._get_trade_history(trade)
         price_history = history.setdefault("take_profit_price", [])
         price_history.append((exit_stage, take_profit_price))
@@ -1631,7 +1632,7 @@ class QuickAdapterV3(IStrategy):
 
     def safe_append_trade_take_profit_price(
         self, trade: Trade, take_profit_price: float, exit_stage: int
-    ) -> list[float | tuple[int, float]]:
+    ) -> list[_TakeProfitHistoryEntry]:
         trade_take_profit_price_history = (
             QuickAdapterV3.get_trade_take_profit_price_history(trade)
         )
@@ -1642,13 +1643,21 @@ class QuickAdapterV3(IStrategy):
         )
         previous_exit_stage = None
         previous_take_profit_price = None
-        if isinstance(previous_take_profit_entry, tuple):
-            previous_exit_stage = (
-                previous_take_profit_entry[0] if previous_take_profit_entry else None
-            )
-            previous_take_profit_price = (
-                previous_take_profit_entry[1] if previous_take_profit_entry else None
+        if (
+            isinstance(previous_take_profit_entry, (tuple, list))
+            and len(previous_take_profit_entry) == 2
+        ):
+            candidate_exit_stage, candidate_take_profit_price = (
+                previous_take_profit_entry
             )
+            if (
+                isinstance(candidate_exit_stage, int)
+                and not isinstance(candidate_exit_stage, bool)
+                and isinstance(candidate_take_profit_price, (int, float))
+                and not isinstance(candidate_take_profit_price, bool)
+            ):
+                previous_exit_stage = candidate_exit_stage
+                previous_take_profit_price = candidate_take_profit_price
         elif isinstance(previous_take_profit_entry, float):
             previous_exit_stage = -1
             previous_take_profit_price = previous_take_profit_entry