]> Piment Noir Git Repositories - freqai-strategies.git/commitdiff
fix(freqai): bind backtest state to saved artifacts (#300)
authorJérôme Benoit <jerome.benoit@piment-noir.org>
Sat, 31 Oct 2026 01:29:27 +0000 (02:29 +0100)
committerGitHub <noreply@github.com>
Sat, 31 Oct 2026 01:29:27 +0000 (02:29 +0100)
Restore non-live continuation components from the validated saved artifact. Preserve live caches and honor the configured RL continuation device.

Closes #276

ReforceXY/.basedpyright/diagnostics.json
ReforceXY/README.md
ReforceXY/tests/test_model_training_observations.py
ReforceXY/user_data/freqaimodels/ReforceXY.py
quickadapter/.basedpyright/diagnostics.json
quickadapter/README.md
quickadapter/tests/test_prediction_history.py
quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py

index fa570c2c31dee9d5cc50f55b1ac6947ef38381da..6e883244f8f1ecde5cdfef548f13000a24768492 100644 (file)
       "endCharacter": 22,
       "endLine": 1122,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
-      "message": "Cannot assign to attribute \"train_env\" for class \"ReforceXY*\"\n  Expression of type \"VecEnv\" cannot be assigned to attribute \"train_env\" of class \"ReforceXY\"\n    Type \"VecEnv\" is not assignable to type \"VecMonitor | SubprocVecEnv | Env[Unknown, Unknown]\"\n      \"VecEnv\" is not assignable to \"VecMonitor\"\n      \"VecEnv\" is not assignable to \"SubprocVecEnv\"\n      \"VecEnv\" is not assignable to \"Env[Unknown, Unknown]\"",
+      "message": "Cannot assign to attribute \"train_env\" for class \"ReforceXY*\"\n  Expression of type \"VecEnv\" cannot be assigned to attribute \"train_env\" of class \"ReforceXY\"\n    Type \"VecEnv\" is not assignable to type \"VecMonitor | SubprocVecEnv | Env[Any, Any]\"\n      \"VecEnv\" is not assignable to \"VecMonitor\"\n      \"VecEnv\" is not assignable to \"SubprocVecEnv\"\n      \"VecEnv\" is not assignable to \"Env[Any, Any]\"",
       "rule": "reportAttributeAccessIssue",
       "severity": "error",
       "startCharacter": 13,
       "endCharacter": 37,
       "endLine": 1122,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
-      "message": "Cannot assign to attribute \"eval_env\" for class \"ReforceXY*\"\n  Expression of type \"VecEnv | None\" cannot be assigned to attribute \"eval_env\" of class \"ReforceXY\"\n    Type \"VecEnv | None\" is not assignable to type \"VecMonitor | SubprocVecEnv | Env[Unknown, Unknown]\"\n      Type \"VecEnv\" is not assignable to type \"VecMonitor | SubprocVecEnv | Env[Unknown, Unknown]\"\n        \"VecEnv\" is not assignable to \"VecMonitor\"\n        \"VecEnv\" is not assignable to \"SubprocVecEnv\"\n        \"VecEnv\" is not assignable to \"Env[Unknown, Unknown]\"",
+      "message": "Cannot assign to attribute \"eval_env\" for class \"ReforceXY*\"\n  Expression of type \"VecEnv | None\" cannot be assigned to attribute \"eval_env\" of class \"ReforceXY\"\n    Type \"VecEnv | None\" is not assignable to type \"VecMonitor | SubprocVecEnv | Env[Any, Any]\"\n      Type \"VecEnv\" is not assignable to type \"VecMonitor | SubprocVecEnv | Env[Any, Any]\"\n        \"VecEnv\" is not assignable to \"VecMonitor\"\n        \"VecEnv\" is not assignable to \"SubprocVecEnv\"\n        \"VecEnv\" is not assignable to \"Env[Any, Any]\"",
       "rule": "reportAttributeAccessIssue",
       "severity": "error",
       "startCharacter": 29,
     },
     {
       "endCharacter": 38,
-      "endLine": 1617,
+      "endLine": 1620,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"obj\" of type \"Sized\" in function \"len\"\n  Type \"Any | None\" is not assignable to type \"Sized\"\n    \"None\" is incompatible with protocol \"Sized\"\n      \"__len__\" is not present",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 30,
-      "startLine": 1617
+      "startLine": 1620
     },
     {
       "endCharacter": 36,
-      "endLine": 1621,
+      "endLine": 1624,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"obj\" of type \"Sized\" in function \"len\"\n  Type \"Any | None\" is not assignable to type \"Sized\"\n    \"None\" is incompatible with protocol \"Sized\"\n      \"__len__\" is not present",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 29,
-      "startLine": 1621
+      "startLine": 1624
     },
     {
       "endCharacter": 80,
-      "endLine": 1624,
+      "endLine": 1627,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"timeframe\" of type \"str\" in function \"steps_to_days\"\n  Type \"Any | None\" is not assignable to type \"str\"\n    \"None\" is not assignable to \"str\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 52,
-      "startLine": 1624
+      "startLine": 1627
     },
     {
       "endCharacter": 78,
-      "endLine": 1625,
+      "endLine": 1628,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"timeframe\" of type \"str\" in function \"steps_to_days\"\n  Type \"Any | None\" is not assignable to type \"str\"\n    \"None\" is not assignable to \"str\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 50,
-      "startLine": 1625
+      "startLine": 1628
     },
     {
       "endCharacter": 80,
-      "endLine": 1626,
+      "endLine": 1629,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"timeframe\" of type \"str\" in function \"steps_to_days\"\n  Type \"Any | None\" is not assignable to type \"str\"\n    \"None\" is not assignable to \"str\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 52,
-      "startLine": 1626
+      "startLine": 1629
     },
     {
       "endCharacter": 56,
-      "endLine": 1710,
+      "endLine": 1713,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
-      "message": "Argument of type \"VecMonitor | SubprocVecEnv | Env[Unknown, Unknown]\" cannot be assigned to parameter \"eval_env\" of type \"VecEnv | None\" in function \"get_callbacks\"\n  Type \"VecMonitor | SubprocVecEnv | Env[Unknown, Unknown]\" is not assignable to type \"VecEnv | None\"\n    Type \"Env[Unknown, Unknown]\" is not assignable to type \"VecEnv | None\"\n      \"Env[Unknown, Unknown]\" is not assignable to \"VecEnv\"\n      \"Env[Unknown, Unknown]\" is not assignable to \"None\"",
+      "message": "Argument of type \"VecMonitor | SubprocVecEnv | Env[Any, Any]\" cannot be assigned to parameter \"eval_env\" of type \"VecEnv | None\" in function \"get_callbacks\"\n  Type \"VecMonitor | SubprocVecEnv | Env[Any, Any]\" is not assignable to type \"VecEnv | None\"\n    Type \"Env[Any, Any]\" is not assignable to type \"VecEnv | None\"\n      \"Env[Any, Any]\" is not assignable to \"VecEnv\"\n      \"Env[Any, Any]\" is not assignable to \"None\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 43,
-      "startLine": 1710
+      "startLine": 1713
     },
     {
       "endCharacter": 84,
-      "endLine": 1730,
+      "endLine": 1733,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"float | list[float]\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"float | list[float]\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"list[float]\" is not assignable to type \"ConvertibleToFloat\"\n      \"list[float]\" is not assignable to \"str\"\n      \"list[float]\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"list[float]\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"list[float]\" is incompatible with protocol \"SupportsIndex\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 67,
-      "startLine": 1730
+      "startLine": 1733
     },
     {
       "endCharacter": 84,
-      "endLine": 1730,
+      "endLine": 1733,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"float | list[float]\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"float | list[float]\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"list[float]\" is not assignable to type \"ConvertibleToFloat\"\n      \"list[float]\" is not assignable to \"str\"\n      \"list[float]\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"list[float]\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"list[float]\" is incompatible with protocol \"SupportsIndex\"\n  ...",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 67,
-      "startLine": 1730
+      "startLine": 1733
     },
     {
       "endCharacter": 44,
-      "endLine": 2808,
+      "endLine": 2811,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "\"best_trial_params\" is possibly unbound",
       "rule": "reportPossiblyUnboundVariable",
       "severity": "error",
       "startCharacter": 27,
-      "startLine": 2808
+      "startLine": 2811
     },
     {
       "endCharacter": 12,
-      "endLine": 2828,
+      "endLine": 2831,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Parameter declaration \"seed\" is obscured by a declaration of the same name",
       "rule": "reportRedeclaration",
       "severity": "error",
       "startCharacter": 8,
-      "startLine": 2828
+      "startLine": 2831
     },
     {
       "endCharacter": 16,
-      "endLine": 2829,
+      "endLine": 2832,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Parameter declaration \"env_info\" is obscured by a declaration of the same name",
       "rule": "reportRedeclaration",
       "severity": "error",
       "startCharacter": 8,
-      "startLine": 2829
+      "startLine": 2832
     },
     {
       "endCharacter": 51,
-      "endLine": 2874,
+      "endLine": 2877,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
-      "message": "Argument of type \"list[() -> BaseEnvironment]\" cannot be assigned to parameter \"env_fns\" of type \"list[() -> Env[Unknown, Unknown]]\" in function \"__init__\"\n  \"list[() -> BaseEnvironment]\" is not assignable to \"list[() -> Env[Unknown, Unknown]]\"\n    Type parameter \"_T@list\" is invariant, but \"() -> BaseEnvironment\" is not the same as \"() -> Env[Unknown, Unknown]\"\n    Consider switching from \"list\" to \"Sequence\" which is covariant",
+      "message": "Argument of type \"list[() -> BaseEnvironment]\" cannot be assigned to parameter \"env_fns\" of type \"list[() -> Env[Any, Any]]\" in function \"__init__\"\n  \"list[() -> BaseEnvironment]\" is not assignable to \"list[() -> Env[Any, Any]]\"\n    Type parameter \"_T@list\" is invariant, but \"() -> BaseEnvironment\" is not the same as \"() -> Env[Any, Any]\"\n    Consider switching from \"list\" to \"Sequence\" which is covariant",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 42,
-      "startLine": 2874
+      "startLine": 2877
     },
     {
       "endCharacter": 53,
-      "endLine": 2880,
+      "endLine": 2883,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
-      "message": "Argument of type \"list[() -> BaseEnvironment]\" cannot be assigned to parameter \"env_fns\" of type \"list[() -> Env[Unknown, Unknown]]\" in function \"__init__\"\n  \"list[() -> BaseEnvironment]\" is not assignable to \"list[() -> Env[Unknown, Unknown]]\"\n    Type parameter \"_T@list\" is invariant, but \"() -> BaseEnvironment\" is not the same as \"() -> Env[Unknown, Unknown]\"\n    Consider switching from \"list\" to \"Sequence\" which is covariant",
+      "message": "Argument of type \"list[() -> BaseEnvironment]\" cannot be assigned to parameter \"env_fns\" of type \"list[() -> Env[Any, Any]]\" in function \"__init__\"\n  \"list[() -> BaseEnvironment]\" is not assignable to \"list[() -> Env[Any, Any]]\"\n    Type parameter \"_T@list\" is invariant, but \"() -> BaseEnvironment\" is not the same as \"() -> Env[Any, Any]\"\n    Consider switching from \"list\" to \"Sequence\" which is covariant",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 45,
-      "startLine": 2880
+      "startLine": 2883
     },
     {
       "endCharacter": 22,
-      "endLine": 2943,
+      "endLine": 2946,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Operator \"*\" not supported for \"None\"",
       "rule": "reportOptionalOperand",
       "severity": "error",
       "startCharacter": 15,
-      "startLine": 2943
+      "startLine": 2946
     },
     {
       "endCharacter": 96,
-      "endLine": 2945,
+      "endLine": 2948,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Operator \"*\" not supported for \"None\"",
       "rule": "reportOptionalOperand",
       "severity": "error",
       "startCharacter": 89,
-      "startLine": 2945
+      "startLine": 2948
     },
     {
       "endCharacter": 23,
-      "endLine": 2948,
+      "endLine": 2951,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Operator \"*\" not supported for \"None\"",
       "rule": "reportOptionalOperand",
       "severity": "error",
       "startCharacter": 16,
-      "startLine": 2948
+      "startLine": 2951
     },
     {
       "endCharacter": 98,
-      "endLine": 2950,
+      "endLine": 2953,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Operator \"*\" not supported for \"None\"",
       "rule": "reportOptionalOperand",
       "severity": "error",
       "startCharacter": 91,
-      "startLine": 2950
+      "startLine": 2953
     },
     {
       "endCharacter": 44,
-      "endLine": 2962,
+      "endLine": 2965,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Operator \"*\" not supported for types \"Any | None\" and \"Any | int | None\"\n  Operator \"*\" not supported for types \"None\" and \"None\"\n  Operator \"*\" not supported for types \"None\" and \"int\"",
       "rule": "reportOperatorIssue",
       "severity": "error",
       "startCharacter": 15,
-      "startLine": 2962
+      "startLine": 2965
     },
     {
       "endCharacter": 133,
-      "endLine": 2964,
+      "endLine": 2967,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Operator \"*\" not supported for types \"Any | None\" and \"Any | int | None\"\n  Operator \"*\" not supported for types \"None\" and \"None\"\n  Operator \"*\" not supported for types \"None\" and \"int\"",
       "rule": "reportOperatorIssue",
       "severity": "error",
       "startCharacter": 106,
-      "startLine": 2964
+      "startLine": 2967
     },
     {
       "endCharacter": 30,
-      "endLine": 2967,
+      "endLine": 2970,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Operator \">\" not supported for \"None\"",
       "rule": "reportOptionalOperand",
       "severity": "error",
       "startCharacter": 15,
-      "startLine": 2967
+      "startLine": 2970
     },
     {
       "endCharacter": 54,
-      "endLine": 3033,
+      "endLine": 3036,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "\"is_pruned\" is not a known attribute of \"None\"",
       "rule": "reportOptionalMemberAccess",
       "severity": "error",
       "startCharacter": 45,
-      "startLine": 3033
+      "startLine": 3036
     },
     {
       "endCharacter": 67,
-      "endLine": 3036,
+      "endLine": 3039,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "\"use_masking\" is not a known attribute of \"None\"",
       "rule": "reportOptionalMemberAccess",
       "severity": "error",
       "startCharacter": 56,
-      "startLine": 3036
+      "startLine": 3039
     },
     {
       "endCharacter": 60,
-      "endLine": 3044,
+      "endLine": 3047,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "\"update_best_reward\" is not a known attribute of \"None\"",
       "rule": "reportOptionalMemberAccess",
       "severity": "error",
       "startCharacter": 42,
-      "startLine": 3044
+      "startLine": 3047
     },
     {
       "endCharacter": 84,
-      "endLine": 3044,
+      "endLine": 3047,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"float | list[float]\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"float | list[float]\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"list[float]\" is not assignable to type \"ConvertibleToFloat\"\n      \"list[float]\" is not assignable to \"str\"\n      \"list[float]\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"list[float]\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"list[float]\" is incompatible with protocol \"SupportsIndex\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 67,
-      "startLine": 3044
+      "startLine": 3047
     },
     {
       "endCharacter": 84,
-      "endLine": 3044,
+      "endLine": 3047,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"float | list[float]\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"float | list[float]\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"list[float]\" is not assignable to type \"ConvertibleToFloat\"\n      \"list[float]\" is not assignable to \"str\"\n      \"list[float]\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"list[float]\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"list[float]\" is incompatible with protocol \"SupportsIndex\"\n  ...",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 67,
-      "startLine": 3044
+      "startLine": 3047
     },
     {
       "endCharacter": 46,
-      "endLine": 3098,
+      "endLine": 3101,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "\"is_pruned\" is not a known attribute of \"None\"",
       "rule": "reportOptionalMemberAccess",
       "severity": "error",
       "startCharacter": 37,
-      "startLine": 3098
+      "startLine": 3101
     },
     {
       "endCharacter": 57,
-      "endLine": 3101,
+      "endLine": 3104,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "\"best_mean_reward\" is not a known attribute of \"None\"",
       "rule": "reportOptionalMemberAccess",
       "severity": "error",
       "startCharacter": 41,
-      "startLine": 3101
+      "startLine": 3104
     },
     {
       "endCharacter": 37,
-      "endLine": 3111,
+      "endLine": 3114,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
-      "message": "Cannot assign to attribute \"train_env\" for class \"ReforceXY*\"\n  Type \"None\" is not assignable to type \"VecMonitor | SubprocVecEnv | Env[Unknown, Unknown]\"\n    \"None\" is not assignable to \"VecMonitor\"\n    \"None\" is not assignable to \"SubprocVecEnv\"\n    \"None\" is not assignable to \"Env[Unknown, Unknown]\"",
+      "message": "Cannot assign to attribute \"train_env\" for class \"ReforceXY*\"\n  Type \"None\" is not assignable to type \"VecMonitor | SubprocVecEnv | Env[Any, Any]\"\n    \"None\" is not assignable to \"VecMonitor\"\n    \"None\" is not assignable to \"SubprocVecEnv\"\n    \"None\" is not assignable to \"Env[Any, Any]\"",
       "rule": "reportAttributeAccessIssue",
       "severity": "error",
       "startCharacter": 33,
-      "startLine": 3111
+      "startLine": 3114
     },
     {
       "endCharacter": 36,
-      "endLine": 3116,
+      "endLine": 3119,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
-      "message": "Cannot assign to attribute \"eval_env\" for class \"ReforceXY*\"\n  Type \"None\" is not assignable to type \"VecMonitor | SubprocVecEnv | Env[Unknown, Unknown]\"\n    \"None\" is not assignable to \"VecMonitor\"\n    \"None\" is not assignable to \"SubprocVecEnv\"\n    \"None\" is not assignable to \"Env[Unknown, Unknown]\"",
+      "message": "Cannot assign to attribute \"eval_env\" for class \"ReforceXY*\"\n  Type \"None\" is not assignable to type \"VecMonitor | SubprocVecEnv | Env[Any, Any]\"\n    \"None\" is not assignable to \"VecMonitor\"\n    \"None\" is not assignable to \"SubprocVecEnv\"\n    \"None\" is not assignable to \"Env[Any, Any]\"",
       "rule": "reportAttributeAccessIssue",
       "severity": "error",
       "startCharacter": 32,
-      "startLine": 3116
+      "startLine": 3119
     },
     {
       "endCharacter": 7,
-      "endLine": 3161,
+      "endLine": 3164,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Declaration \"MyRLEnv\" is obscured by a declaration of the same name",
       "rule": "reportRedeclaration",
       "severity": "error",
       "startCharacter": 0,
-      "startLine": 3161
+      "startLine": 3164
     },
     {
       "endCharacter": 36,
-      "endLine": 3173,
+      "endLine": 3176,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Declaration \"_last_closed_trade_tick\" is obscured by a declaration of the same name",
       "rule": "reportRedeclaration",
       "severity": "error",
       "startCharacter": 13,
-      "startLine": 3173
+      "startLine": 3176
     },
     {
       "endCharacter": 13,
-      "endLine": 3765,
+      "endLine": 3768,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Method \"reset\" overrides class \"BaseEnvironment\" in an incompatible manner\n  Return type mismatch: base method returns type \"tuple[DataFrame, dict[Unknown, Unknown]]\", override returns type \"tuple[NDArray[float32], dict[str, Any]]\"\n    \"tuple[NDArray[float32], dict[str, Any]]\" is not assignable to \"tuple[DataFrame, dict[Unknown, Unknown]]\"\n      Tuple entry 1 is incorrect type\n        \"ndarray[_AnyShape, dtype[float32]]\" is not assignable to \"DataFrame\"",
       "rule": "reportIncompatibleMethodOverride",
       "severity": "error",
       "startCharacter": 8,
-      "startLine": 3765
+      "startLine": 3768
     },
     {
       "endCharacter": 26,
-      "endLine": 3786,
+      "endLine": 3789,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Type \"tuple[DataFrame, dict[Unknown, Unknown]]\" is not assignable to return type \"tuple[NDArray[float32], dict[str, Any]]\"\n  \"DataFrame\" is not assignable to \"ndarray[_AnyShape, dtype[float32]]\"",
       "rule": "reportReturnType",
       "severity": "error",
       "startCharacter": 15,
-      "startLine": 3786
+      "startLine": 3789
     },
     {
       "endCharacter": 55,
-      "endLine": 4091,
+      "endLine": 4094,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"float | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"float | None\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 44,
-      "startLine": 4091
+      "startLine": 4094
     },
     {
       "endCharacter": 55,
-      "endLine": 4091,
+      "endLine": 4094,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"float | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"float | None\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"\n  ...",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 44,
-      "startLine": 4091
+      "startLine": 4094
     },
     {
       "endCharacter": 59,
-      "endLine": 4115,
+      "endLine": 4118,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"float | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"float | None\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 48,
-      "startLine": 4115
+      "startLine": 4118
     },
     {
       "endCharacter": 59,
-      "endLine": 4115,
+      "endLine": 4118,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"float | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"float | None\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"\n  ...",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 48,
-      "startLine": 4115
+      "startLine": 4118
     },
     {
       "endCharacter": 24,
-      "endLine": 4144,
+      "endLine": 4147,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Method \"_get_observation\" overrides class \"BaseEnvironment\" in an incompatible manner\n  Return type mismatch: base method returns type \"DataFrame\", override returns type \"NDArray[float32]\"\n    \"ndarray[_AnyShape, dtype[float32]]\" is not assignable to \"DataFrame\"",
       "rule": "reportIncompatibleMethodOverride",
       "severity": "error",
       "startCharacter": 8,
-      "startLine": 4144
+      "startLine": 4147
     },
     {
       "endCharacter": 12,
-      "endLine": 4237,
+      "endLine": 4240,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Method \"step\" overrides class \"Base5ActionRLEnv\" in an incompatible manner\n  Return type mismatch: base method returns type \"tuple[DataFrame, float, bool, Literal[False], dict[str, Unknown]]\", override returns type \"tuple[NDArray[float32], float, bool, bool, dict[str, Any]]\"\n    \"tuple[NDArray[float32], float, bool, bool, dict[str, Any]]\" is not assignable to \"tuple[DataFrame, float, bool, Literal[False], dict[str, Unknown]]\"\n      Tuple entry 1 is incorrect type\n        \"ndarray[_AnyShape, dtype[float32]]\" is not assignable to \"DataFrame\"",
       "rule": "reportIncompatibleMethodOverride",
       "severity": "error",
       "startCharacter": 8,
-      "startLine": 4237
+      "startLine": 4240
     },
     {
       "endCharacter": 20,
-      "endLine": 4432,
+      "endLine": 4435,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Method \"action_masks\" overrides class \"BaseEnvironment\" in an incompatible manner\n  Return type mismatch: base method returns type \"list[bool]\", override returns type \"NDArray[bool_]\"\n    \"ndarray[_AnyShape, dtype[bool_]]\" is not assignable to \"list[bool]\"",
       "rule": "reportIncompatibleMethodOverride",
       "severity": "error",
       "startCharacter": 8,
-      "startLine": 4432
+      "startLine": 4435
     },
     {
       "endCharacter": 40,
-      "endLine": 4581,
+      "endLine": 4584,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "\"Figure\" is not exported from module \"matplotlib.pyplot\"",
       "rule": "reportPrivateImportUsage",
       "severity": "error",
       "startCharacter": 34,
-      "startLine": 4581
+      "startLine": 4584
     },
     {
       "endCharacter": 54,
-      "endLine": 5066,
+      "endLine": 5069,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 45,
-      "startLine": 5066
+      "startLine": 5069
     },
     {
       "endCharacter": 54,
-      "endLine": 5066,
+      "endLine": 5069,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"\n  ...",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 45,
-      "startLine": 5066
+      "startLine": 5069
     },
     {
       "endCharacter": 50,
-      "endLine": 5078,
+      "endLine": 5081,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"object\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"object\" is not assignable to type \"ConvertibleToFloat\"\n    \"object\" is not assignable to \"str\"\n    \"object\" is incompatible with protocol \"Buffer\"\n      \"__buffer__\" is not present\n    \"object\" is incompatible with protocol \"SupportsFloat\"\n      \"__float__\" is not present\n    \"object\" is incompatible with protocol \"SupportsIndex\"\n      \"__index__\" is not present",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 37,
-      "startLine": 5078
+      "startLine": 5081
     },
     {
       "endCharacter": 72,
-      "endLine": 5089,
+      "endLine": 5092,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"float | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"float | None\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 70,
-      "startLine": 5089
+      "startLine": 5092
     },
     {
       "endCharacter": 72,
-      "endLine": 5089,
+      "endLine": 5092,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"float | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"float | None\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"\n  ...",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 70,
-      "startLine": 5089
+      "startLine": 5092
     },
     {
       "endCharacter": 73,
-      "endLine": 5098,
+      "endLine": 5101,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"float | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"float | None\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 71,
-      "startLine": 5098
+      "startLine": 5101
     },
     {
       "endCharacter": 73,
-      "endLine": 5098,
+      "endLine": 5101,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"float | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"float | None\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"\n  ...",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 71,
-      "startLine": 5098
+      "startLine": 5101
     },
     {
       "endCharacter": 58,
-      "endLine": 5107,
+      "endLine": 5110,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 56,
-      "startLine": 5107
+      "startLine": 5110
     },
     {
       "endCharacter": 58,
-      "endLine": 5107,
+      "endLine": 5110,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"\n  ...",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 56,
-      "startLine": 5107
+      "startLine": 5110
     },
     {
       "endCharacter": 50,
-      "endLine": 5338,
+      "endLine": 5341,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Mapping[Unknown, Unknown]\" cannot be assigned to parameter \"src\" of type \"dict[str, Any]\" in function \"deepmerge\"\n  \"Mapping[Unknown, Unknown]\" is not assignable to \"dict[str, Any]\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 49,
-      "startLine": 5338
+      "startLine": 5341
     },
     {
       "endCharacter": 20,
-      "endLine": 5497,
+      "endLine": 5500,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 18,
-      "startLine": 5497
+      "startLine": 5500
     },
     {
       "endCharacter": 20,
-      "endLine": 5497,
+      "endLine": 5500,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"\n  ...",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 18,
-      "startLine": 5497
+      "startLine": 5500
     },
     {
       "endCharacter": 59,
-      "endLine": 5502,
+      "endLine": 5505,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n    Type \"None\" is not assignable to type \"ConvertibleToInt\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsInt\"\n        \"__int__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 31,
-      "startLine": 5502
+      "startLine": 5505
     },
     {
       "endCharacter": 59,
-      "endLine": 5502,
+      "endLine": 5505,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n    Type \"None\" is not assignable to type \"ConvertibleToInt\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsInt\"\n        \"__int__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"\n  ...",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 31,
-      "startLine": 5502
+      "startLine": 5505
     },
     {
       "endCharacter": 65,
-      "endLine": 5503,
+      "endLine": 5506,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n    Type \"None\" is not assignable to type \"ConvertibleToInt\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsInt\"\n        \"__int__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 34,
-      "startLine": 5503
+      "startLine": 5506
     },
     {
       "endCharacter": 65,
-      "endLine": 5503,
+      "endLine": 5506,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n    Type \"None\" is not assignable to type \"ConvertibleToInt\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsInt\"\n        \"__int__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"\n  ...",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 34,
-      "startLine": 5503
+      "startLine": 5506
     },
     {
       "endCharacter": 63,
-      "endLine": 5506,
+      "endLine": 5509,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 34,
-      "startLine": 5506
+      "startLine": 5509
     },
     {
       "endCharacter": 63,
-      "endLine": 5506,
+      "endLine": 5509,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"\n  ...",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 34,
-      "startLine": 5506
+      "startLine": 5509
     },
     {
       "endCharacter": 61,
-      "endLine": 5508,
+      "endLine": 5511,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n    Type \"None\" is not assignable to type \"ConvertibleToInt\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsInt\"\n        \"__int__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 32,
-      "startLine": 5508
+      "startLine": 5511
     },
     {
       "endCharacter": 61,
-      "endLine": 5508,
+      "endLine": 5511,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n    Type \"None\" is not assignable to type \"ConvertibleToInt\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsInt\"\n        \"__int__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"\n  ...",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 32,
-      "startLine": 5508
+      "startLine": 5511
     },
     {
       "endCharacter": 67,
-      "endLine": 5509,
+      "endLine": 5512,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 36,
-      "startLine": 5509
+      "startLine": 5512
     },
     {
       "endCharacter": 67,
-      "endLine": 5509,
+      "endLine": 5512,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"\n  ...",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 36,
-      "startLine": 5509
+      "startLine": 5512
     },
     {
       "endCharacter": 73,
-      "endLine": 5510,
+      "endLine": 5513,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 39,
-      "startLine": 5510
+      "startLine": 5513
     },
     {
       "endCharacter": 73,
-      "endLine": 5510,
+      "endLine": 5513,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"\n  ...",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 39,
-      "startLine": 5510
+      "startLine": 5513
     },
     {
       "endCharacter": 61,
-      "endLine": 5511,
+      "endLine": 5514,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 33,
-      "startLine": 5511
+      "startLine": 5514
     },
     {
       "endCharacter": 61,
-      "endLine": 5511,
+      "endLine": 5514,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"\n  ...",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 33,
-      "startLine": 5511
+      "startLine": 5514
     },
     {
       "endCharacter": 89,
-      "endLine": 5518,
+      "endLine": 5521,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n    Type \"None\" is not assignable to type \"ConvertibleToInt\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsInt\"\n        \"__int__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 52,
-      "startLine": 5518
+      "startLine": 5521
     },
     {
       "endCharacter": 89,
-      "endLine": 5518,
+      "endLine": 5521,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n    Type \"None\" is not assignable to type \"ConvertibleToInt\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsInt\"\n        \"__int__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"\n  ...",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 52,
-      "startLine": 5518
+      "startLine": 5521
     },
     {
       "endCharacter": 83,
-      "endLine": 5519,
+      "endLine": 5522,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n    Type \"None\" is not assignable to type \"ConvertibleToInt\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsInt\"\n        \"__int__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 49,
-      "startLine": 5519
+      "startLine": 5522
     },
     {
       "endCharacter": 83,
-      "endLine": 5519,
+      "endLine": 5522,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n    Type \"None\" is not assignable to type \"ConvertibleToInt\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsInt\"\n        \"__int__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"\n  ...",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 49,
-      "startLine": 5519
+      "startLine": 5522
     },
     {
       "endCharacter": 65,
-      "endLine": 5545,
+      "endLine": 5548,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n    Type \"None\" is not assignable to type \"ConvertibleToInt\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsInt\"\n        \"__int__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 34,
-      "startLine": 5545
+      "startLine": 5548
     },
     {
       "endCharacter": 65,
-      "endLine": 5545,
+      "endLine": 5548,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n    Type \"None\" is not assignable to type \"ConvertibleToInt\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsInt\"\n        \"__int__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"\n  ...",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 34,
-      "startLine": 5545
+      "startLine": 5548
     },
     {
       "endCharacter": 67,
-      "endLine": 5547,
+      "endLine": 5550,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n    Type \"None\" is not assignable to type \"ConvertibleToInt\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsInt\"\n        \"__int__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 35,
-      "startLine": 5547
+      "startLine": 5550
     },
     {
       "endCharacter": 67,
-      "endLine": 5547,
+      "endLine": 5550,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n    Type \"None\" is not assignable to type \"ConvertibleToInt\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsInt\"\n        \"__int__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"\n  ...",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 35,
-      "startLine": 5547
+      "startLine": 5550
     },
     {
       "endCharacter": 87,
-      "endLine": 5550,
+      "endLine": 5553,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 46,
-      "startLine": 5550
+      "startLine": 5553
     },
     {
       "endCharacter": 87,
-      "endLine": 5550,
+      "endLine": 5553,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"\n  ...",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 46,
-      "startLine": 5550
+      "startLine": 5553
     },
     {
       "endCharacter": 93,
-      "endLine": 5551,
+      "endLine": 5554,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 49,
-      "startLine": 5551
+      "startLine": 5554
     },
     {
       "endCharacter": 93,
-      "endLine": 5551,
+      "endLine": 5554,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"\n  ...",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 49,
-      "startLine": 5551
+      "startLine": 5554
     },
     {
       "endCharacter": 89,
-      "endLine": 5552,
+      "endLine": 5555,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 47,
-      "startLine": 5552
+      "startLine": 5555
     },
     {
       "endCharacter": 89,
-      "endLine": 5552,
+      "endLine": 5555,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"\n  ...",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 47,
-      "startLine": 5552
+      "startLine": 5555
     },
     {
       "endCharacter": 89,
-      "endLine": 5553,
+      "endLine": 5556,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n    Type \"None\" is not assignable to type \"ConvertibleToInt\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsInt\"\n        \"__int__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 46,
-      "startLine": 5553
+      "startLine": 5556
     },
     {
       "endCharacter": 89,
-      "endLine": 5553,
+      "endLine": 5556,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n    Type \"None\" is not assignable to type \"ConvertibleToInt\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsInt\"\n        \"__int__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"\n  ...",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 46,
-      "startLine": 5553
+      "startLine": 5556
     },
     {
       "endCharacter": 75,
-      "endLine": 5554,
+      "endLine": 5557,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n    Type \"None\" is not assignable to type \"ConvertibleToInt\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsInt\"\n        \"__int__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 39,
-      "startLine": 5554
+      "startLine": 5557
     },
     {
       "endCharacter": 75,
-      "endLine": 5554,
+      "endLine": 5557,
       "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n  Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n    Type \"None\" is not assignable to type \"ConvertibleToInt\"\n      \"None\" is not assignable to \"str\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"SupportsInt\"\n        \"__int__\" is not present\n      \"None\" is incompatible with protocol \"SupportsIndex\"\n  ...",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 39,
-      "startLine": 5554
+      "startLine": 5557
     },
     {
       "endCharacter": 33,
index 017c55ad03e0a9d492407322314ede8d7b73dc7e..0bd4e168afb9a5bd73523ea0080e2004a62a2439 100644 (file)
@@ -244,10 +244,11 @@ Training disables `shuffle_after_split`. HPO studies and saved best parameters
 are reused only when their objective identity matches.
 
 Backtests continue only from a saved policy whose training cutoff precedes
-the current window's end and its last available candle boundary. A later
-deployment under the same identifier is not reused for an earlier window,
-even when FreqAI has saved only metadata for intervening windows. Live and
-dry-run restarts still restore compatible deployed policies.
+the current window's end and its last available candle boundary. The policy,
+fitted feature pipeline and any required replay come from that saved artifact.
+A later deployment under the same identifier is not reused for an earlier
+window, even when FreqAI has saved only metadata for intervening windows.
+Live and dry-run restarts still restore compatible deployed policies.
 
 ## Live inference and backtesting
 
@@ -374,7 +375,7 @@ Moving base images can change them; record the runtime version.
 | freqai.model_training_parameters.learning_rate                 | SDK default unless lr_schedule=true; then initial 0.0003 if omitted                          | SDK numeric/schedule; app wraps numeric only                                                             | Omitted                                            | Do not call 0.0003 an unconditional app default.                                                                                                                                                    |
 | freqai.model_training_parameters.clip_range                    | SDK default unless PPO-family cr_schedule=true; then initial 0.2 if omitted                  | SDK numeric/schedule; app wraps numeric only                                                             | Omitted                                            | Only PPO family schedule conversion.                                                                                                                                                                |
 | freqai.model_training_parameters.gpu_memory_fraction           | null/omitted disables                                                                        | Numeric in (0,1] intended; ignored invalid bounds/no CUDA, not robust type validation                    | Omitted                                            | App-only; applies torch CUDA per-process limit to device 0, removed before SDK constructor.                                                                                                         |
-| freqai.model_training_parameters.device                        | No app fallback; SDK auto                                                                    | PyTorch/SB3 device specification                                                                         | auto                                               | Forwarded; doc examples auto/cpu/cuda/cuda:0 are not an app-enforced exhaustive choice list.                                                                                                        |
+| freqai.model_training_parameters.device                        | SDK auto when omitted                                                                        | PyTorch/SB3 device specification                                                                         | auto                                               | Forwarded to new learners and continuation archive loads; live cached copies retain their deployed device. Native device syntax applies.                                                            |
 | freqai.model_training_parameters.verbose                       | SDK constructor default; callback lookup fallback 0                                          | SDK verbosity integer                                                                                    | 1                                                  | Forwarded to constructor, also read for callback verbosity.                                                                                                                                         |
 | freqai.model_training_parameters.policy_kwargs.net_arch        | [128,128] per branch for PPO family; [128,128] for others                                    | small\|medium\|large\|extra_large, list of layer widths; PPO family additionally {pi:[...],vf:[...]}     | Omitted                                            | Presets use two layers of 128/256/512/1024; PPO list expanded to both heads, invalid/missing head resets to [128,128]. Other families accept list or presets. Layer element validation remains SDK. |
 | freqai.model_training_parameters.policy_kwargs.activation_fn   | relu                                                                                         | relu\|tanh\|elu\|leaky_relu                                                                              | Omitted                                            | Converted to torch class; unknown name silently falls back to ReLU.                                                                                                                                 |
index 751e872284552a31110a7e0a2a834bab781cc0ba..44512fd02cc20b52acadbdae251c77c2c4e7e395 100644 (file)
@@ -1,5 +1,6 @@
 """Regression contracts for learning eligibility, replay and temporal observations."""
 
+import copy
 import json
 import tempfile
 from pathlib import Path
@@ -11,10 +12,10 @@ import pandas as pd
 from freqtrade.enums import RunMode
 from freqtrade.exceptions import DependencyException
 from freqtrade.exchange import timeframe_to_seconds
-from freqtrade.freqai.data_drawer import METADATA, FreqaiDataDrawer
+from freqtrade.freqai.data_drawer import FEATURE_PIPELINE, METADATA, FreqaiDataDrawer
 from freqtrade.freqai.data_kitchen import FreqaiDataKitchen
 from optuna import TrialPruned, create_study
-from qa_support import QaTestCase, RecordingPolicy, model_config
+from qa_support import PAIR, PROJECT_ROOT, QaTestCase, RecordingPolicy, model_config
 
 from ReforceXY.user_data.freqaimodels.ReforceXY import ReforceXY
 
@@ -50,7 +51,7 @@ class TrainingObservationsTest(QaTestCase):
                 )
                 for column in ("open", "high", "low", "close"):
                     frame[f"%-raw_{column}"] = 100 + np.arange(64) * 0.1
-                dk = FreqaiDataKitchen(config, live=True, pair="BTC/USDT")
+                dk = FreqaiDataKitchen(config, live=True, pair=PAIR)
                 dk.data_path = Path(temp) / "initial"
                 dk.model_filename = "cb_btc_initial"
                 dk.label_list = ["&-action"]
@@ -177,7 +178,7 @@ class TrainingObservationsTest(QaTestCase):
                 "device": "cpu",
                 "policy_kwargs": {"net_arch": [8]},
             }
-            pair = "BTC/USDT"
+            pair = PAIR
 
             def frame(day, feature_offset):
                 values = np.arange(64)
@@ -222,7 +223,7 @@ class TrainingObservationsTest(QaTestCase):
             backtest_config["runmode"] = RunMode.BACKTEST
             backtest_config["timerange"] = "20260101-20260105"
             backtest_config["config_files"] = [
-                "/workspace/ReforceXY/user_data/config-template.json"
+                str(PROJECT_ROOT / "user_data" / "config-template.json")
             ]
             backtest = ReforceXY(config=backtest_config)
             backtest.live = False
@@ -272,13 +273,249 @@ class TrainingObservationsTest(QaTestCase):
             transformed = boundary_dk.data_dictionary["train_features"]["%-feature"]
             self.assertGreater(transformed.min(), 2.0)
 
+    def test_backtest_archive_continuation_preserves_explicit_cpu_placement(self):
+        for algorithm in ("DQN", "QRDQN"):
+            with self.subTest(algorithm=algorithm), tempfile.TemporaryDirectory() as temp:
+                config = model_config(temp)
+                info = config["freqai"]
+                info["continual_learning"] = True
+                info["data_split_parameters"]["test_size"] = 0
+                info["rl_config"]["model_type"] = algorithm
+                info["model_training_parameters"] = {
+                    "learning_starts": 0,
+                    "buffer_size": 256,
+                    "batch_size": 8,
+                    "train_freq": 4,
+                    "gradient_steps": 1,
+                    "device": "cpu",
+                    "policy_kwargs": {"net_arch": [8]},
+                }
+                owner = ReforceXY(config=config)
+                owner.live = True
+                owner.can_short = False
+                self.addCleanup(owner.close_envs)
+                values = np.arange(64)
+                frame = pd.DataFrame(
+                    {
+                        "date": pd.date_range("2026-01-01", periods=64, freq="5min", tz="UTC"),
+                        "%-feature": np.sin(values),
+                        "&-action": np.zeros(64),
+                    }
+                )
+                for column in ("open", "high", "low", "close"):
+                    frame[f"%-raw_{column}"] = 100.0 + values * 0.1
+                dk = FreqaiDataKitchen(config, live=True, pair=PAIR)
+                timestamp = int((frame["date"].iloc[-1] + pd.Timedelta(minutes=5)).timestamp())
+                dk.set_paths(dk.pair, timestamp)
+                dk.set_new_model_names(dk.pair, timestamp)
+                dk.training_features_list = [column for column in frame if column.startswith("%")]
+                dk.label_list = ["&-action"]
+                trained = owner.train(frame, dk.pair, dk)
+                owner.dd.get_pair_dict_info(dk.pair)
+                owner.dd.pair_dict[dk.pair]["trained_timestamp"] = timestamp
+                owner.dd.save_data(trained, dk.pair, dk)
+
+                owner.live = False
+                current = copy.copy(dk)
+                current.live = False
+                current.data_path = Path(temp) / "continued"
+                current.data_path.mkdir()
+                restored, pipeline = owner._resolve_deployment_state(
+                    current, dk.pair, as_of_ts=timestamp + 300
+                )
+                self.assertEqual(restored.device.type, "cpu")
+                self.assertTrue(
+                    all(value.device.type == "cpu" for value in restored.policy.parameters())
+                )
+                self.assertEqual(restored.replay_buffer.device, restored.device)
+                sample = restored.replay_buffer.sample(1)
+                replay_tensors = (
+                    sample.observations,
+                    sample.actions,
+                    sample.next_observations,
+                    sample.dones,
+                    sample.rewards,
+                )
+                self.assertTrue(all(value.device.type == "cpu" for value in replay_tensors))
+                action, _ = restored.predict(sample.observations.cpu().numpy(), deterministic=True)
+                self.assertTrue(restored.action_space.contains(action[0]))
+                previous_updates = restored._n_updates
+                previous_parameters = [
+                    value.detach().cpu().numpy().copy() for value in restored.policy.parameters()
+                ]
+                owner.fit(
+                    dk.data_dictionary,
+                    current,
+                    prices_train=dk.data_dictionary["train_prices"],
+                    prices_test=dk.data_dictionary["test_prices"],
+                    deployment_state=(restored, pipeline),
+                )
+                self.assertGreater(restored._n_updates, previous_updates)
+                self.assertEqual(restored.device.type, "cpu")
+                self.assertTrue(
+                    any(
+                        not np.array_equal(before, after.detach().cpu().numpy())
+                        for before, after in zip(
+                            previous_parameters, restored.policy.parameters(), strict=True
+                        )
+                    )
+                )
+
+    def test_backtest_continuation_binds_policy_pipeline_and_replay_to_the_artifact(self):
+        for algorithm in ("DQN", "QRDQN"):
+            with self.subTest(algorithm=algorithm), tempfile.TemporaryDirectory() as temp:
+
+                def deployment(algorithm, day, offset, seed, gamma, reward):
+                    config = model_config(temp)
+                    info = config["freqai"]
+                    info["identifier"] = f"artifact-{algorithm}-{seed}"
+                    info["continual_learning"] = True
+                    info["rl_config"]["model_type"] = algorithm
+                    info["model_training_parameters"] = {
+                        "learning_starts": 0,
+                        "buffer_size": 128,
+                        "batch_size": 8,
+                        "train_freq": 4,
+                        "gradient_steps": 1,
+                        "device": "cpu",
+                        "seed": seed,
+                        "gamma": gamma,
+                        "policy_kwargs": {"net_arch": [8]},
+                    }
+                    owner = ReforceXY(config=config)
+                    owner.live = True
+                    owner.can_short = False
+                    self.addCleanup(owner.close_envs)
+                    values = np.arange(64)
+                    frame = pd.DataFrame(
+                        {
+                            "date": pd.date_range(day, periods=64, freq="5min", tz="UTC"),
+                            "%-feature": np.sin(values) + offset,
+                            "&-action": np.zeros(64),
+                        }
+                    )
+                    for column in ("open", "high", "low", "close"):
+                        frame[f"%-raw_{column}"] = 100.0 + values * 0.1
+                    dk = FreqaiDataKitchen(config, live=True, pair=PAIR)
+                    timestamp = int((frame["date"].iloc[-1] + pd.Timedelta(minutes=5)).timestamp())
+                    dk.set_paths(dk.pair, timestamp)
+                    dk.set_new_model_names(dk.pair, timestamp)
+                    dk.label_list = ["&-action"]
+                    dk.training_features_list = [c for c in frame if c.startswith("%")]
+                    model = owner.train(frame, dk.pair, dk)
+                    observation = np.full(
+                        (model.n_envs, *model.observation_space.shape),
+                        0.125,
+                        dtype=np.float32,
+                    )
+                    model.replay_buffer.add(
+                        observation,
+                        observation + 0.125,
+                        np.zeros((model.n_envs, 1), dtype=np.int64),
+                        np.full(model.n_envs, reward),
+                        np.zeros(model.n_envs, dtype=bool),
+                        [{} for _ in range(model.n_envs)],
+                    )
+                    owner.dd.get_pair_dict_info(dk.pair)
+                    owner.dd.pair_dict[dk.pair]["trained_timestamp"] = timestamp
+                    owner.dd.save_data(model, dk.pair, dk)
+                    return owner, dk, model, frame
+
+                owner, saved, model, frame = deployment(
+                    algorithm, "2026-01-01", 0.0, 42, 0.95, 17.0
+                )
+                future, future_dk, future_model, _ = deployment(
+                    algorithm, "2026-02-01", 1000.0, 43, 0.8, 29.0
+                )
+                pair = saved.pair
+                saved_cache = owner.dd.meta_data_dictionary[pair]
+                future_cache = future.dd.meta_data_dictionary[pair]
+                probe = frame[saved.training_features_list]
+                expected_features = saved.feature_pipeline.transform(probe)[0]
+                self.assertFalse(
+                    np.allclose(
+                        expected_features, future_cache[FEATURE_PIPELINE].transform(probe)[0]
+                    )
+                )
+                expected_policy = {
+                    name: value.detach().cpu().numpy().copy()
+                    for name, value in model.policy.state_dict().items()
+                }
+                size = model.replay_buffer.size()
+                expected_replay = {
+                    name: getattr(model.replay_buffer, name)[:size].copy()
+                    for name in ("observations", "next_observations", "actions", "rewards", "dones")
+                }
+                cached_empty = owner.MODELCLASS.load(
+                    future_dk.data_path / f"{future_dk.model_filename}_model"
+                )
+                self.assertEqual(cached_empty.replay_buffer.size(), 0)
+                cached_old = owner.MODELCLASS.load(
+                    saved.data_path / f"{saved.model_filename}_model"
+                )
+                cached_old.replay_buffer = copy.deepcopy(future_model.replay_buffer)
+                owner.live = False
+                current_config = {
+                    **owner.config,
+                    "runmode": RunMode.BACKTEST,
+                    "timerange": "20260101-20260105",
+                    "config_files": [str(PROJECT_ROOT / "user_data" / "config-template.json")],
+                }
+                current = FreqaiDataKitchen(current_config, live=False, pair=pair)
+                current.training_features_list = saved.training_features_list.copy()
+                current.label_list = saved.label_list.copy()
+                cutoff = int(pd.Timestamp("2026-01-03", tz="UTC").timestamp())
+                owner.dd.pair_dict[pair]["trained_timestamp"] = future.dd.pair_dict[pair][
+                    "trained_timestamp"
+                ]
+                previous = copy.deepcopy(owner.dd.pair_dict[pair])
+                for name, cached_model, cached in (
+                    ("future", future_model, future_cache),
+                    ("metadata_only", None, future_cache),
+                    ("empty_replay", cached_empty, saved_cache),
+                    ("future_replay", cached_old, saved_cache),
+                ):
+                    with self.subTest(cache=name):
+                        owner.dd.model_dictionary = (
+                            {pair: cached_model} if cached_model is not None else {}
+                        )
+                        owner.dd.meta_data_dictionary = {pair: cached}
+                        restored, pipeline = owner._resolve_deployment_state(
+                            current, pair, as_of_ts=cutoff
+                        )
+                        self.assertEqual(restored.gamma, model.gamma)
+                        for parameter, expected in expected_policy.items():
+                            np.testing.assert_array_equal(
+                                restored.policy.state_dict()[parameter].detach().cpu().numpy(),
+                                expected,
+                            )
+                        np.testing.assert_allclose(pipeline.transform(probe)[0], expected_features)
+                        self.assertEqual(restored.replay_buffer.size(), size)
+                        self.assertEqual(restored.replay_buffer.pos, model.replay_buffer.pos)
+                        self.assertEqual(restored.replay_buffer.device, restored.device)
+                        for field, expected in expected_replay.items():
+                            np.testing.assert_array_equal(
+                                getattr(restored.replay_buffer, field)[:size], expected
+                            )
+                        self.assertEqual(owner.dd.pair_dict[pair], previous)
+                        self.assertIs(owner.dd.meta_data_dictionary[pair], cached)
+                        if cached_model is not None:
+                            self.assertIs(owner.dd.model_dictionary[pair], cached_model)
+
+                owner.dd.model_dictionary = {pair: future_model}
+                owner.dd.meta_data_dictionary = {pair: future_cache}
+                (saved.data_path / saved.data["reforcexy_replay"]).unlink()
+                with self.assertRaises(DependencyException):
+                    owner._resolve_deployment_state(current, pair, as_of_ts=cutoff)
+                self.assertIs(owner.dd.model_dictionary[pair], future_model)
+
     def test_provenance_does_not_change_other_drawers(self):
         with tempfile.TemporaryDirectory() as temp:
             config = model_config(temp)
             model = ReforceXY(config=config)
             self.addCleanup(model.close_envs)
             drawer = FreqaiDataDrawer(Path(temp), config)
-            pair = "BTC/USDT"
+            pair = PAIR
             date = pd.Timestamp("2026-01-01", tz="UTC")
             drawer.historic_predictions[pair] = pd.DataFrame(
                 {
@@ -297,7 +534,7 @@ class TrainingObservationsTest(QaTestCase):
             )
 
     def test_native_bootstrap_append_and_restart_provenance(self):
-        pair = "BTC/USDT"
+        pair = PAIR
         marker = "_freqai_strategies_produced"
         with tempfile.TemporaryDirectory() as temp:
             config = model_config(temp)
@@ -403,7 +640,7 @@ class TrainingObservationsTest(QaTestCase):
             )
 
     def test_native_schema_extension_and_restart_preserve_unknown_metadata(self):
-        pair = "BTC/USDT"
+        pair = PAIR
         marker = "_freqai_strategies_produced"
         with tempfile.TemporaryDirectory() as temp:
             config = model_config(temp)
@@ -505,7 +742,7 @@ class TrainingObservationsTest(QaTestCase):
             self.assertEqual(dk.data["labels_mean"]["&-action"], 0.0)
 
     def test_native_expiration_ignores_stale_production_and_resume_records_real_zero(self):
-        pair = "BTC/USDT"
+        pair = PAIR
         marker = "_freqai_strategies_produced"
         with tempfile.TemporaryDirectory() as temp:
             config = model_config(temp)
@@ -570,7 +807,7 @@ class TrainingObservationsTest(QaTestCase):
             self.assertEqual(dk.data["labels_mean"]["&-action"], 2.0)
 
     def test_partially_marked_history_restores_provable_observations(self):
-        pair = "BTC/USDT"
+        pair = PAIR
         marker = "_freqai_strategies_produced"
         with tempfile.TemporaryDirectory() as temp:
             config = model_config(temp)
@@ -634,7 +871,7 @@ class TrainingObservationsTest(QaTestCase):
             self.assertNotIn(marker, dk.return_dataframe)
 
     def test_duplicate_date_keeps_provable_observation_after_restart(self):
-        pair = "BTC/USDT"
+        pair = PAIR
         marker = "_freqai_strategies_produced"
         date = pd.Timestamp("2026-01-01", tz="UTC")
         for statuses, markers in (([1, 0], None), ([0, 0], [True, False])):
@@ -667,7 +904,7 @@ class TrainingObservationsTest(QaTestCase):
                 self.assertEqual(dk.data["labels_mean"]["&-action"], 7.0)
 
     def test_legacy_zero_status_is_ambiguous_but_expired_status_is_excluded(self):
-        pair = "BTC/USDT"
+        pair = PAIR
         history = pd.DataFrame(
             {
                 "date_pred": pd.date_range("2026-01-01", periods=4, freq="5min", tz="UTC"),
@@ -685,7 +922,7 @@ class TrainingObservationsTest(QaTestCase):
         self.assertEqual(dk.data["labels_mean"]["&-action"], 7.0)
 
     def test_live_action_statistics_resume_persisted_observations(self):
-        pair = "BTC/USDT"
+        pair = PAIR
         dates = pd.date_range("2026-01-01", periods=4, freq="5min", tz="UTC")
         history = pd.DataFrame(
             {
@@ -717,7 +954,7 @@ class TrainingObservationsTest(QaTestCase):
             model.frame_stacking = 2
             model.get_state_info = lambda pair: (0.5, 0.0, 0)
             policy = RecordingPolicy()
-            dk = SimpleNamespace(pair="BTC/USDT", label_list=["&-action"], data_dictionary={})
+            dk = SimpleNamespace(pair=PAIR, label_list=["&-action"], data_dictionary={})
 
             def predict(value, minute, valid=1):
                 dk.do_predict = np.array([valid])
index 5598ba6380b924247386a265fadacf95a6d71412..1c847cca062c0e0d310b7636a42df55ac4e080cc 100644 (file)
@@ -1401,7 +1401,7 @@ class ReforceXY(BaseReinforcementLearningModel):
         """Restore independent training copies of the deployed policy and feature pipeline."""
         if not self.continual_learning:
             return None
-        model = self.dd.model_dictionary.get(pair)
+        model = self.dd.model_dictionary.get(pair) if self.live else None
         cached_model = model
         previous = self.dd.pair_dict.get(pair, {})
         if model is None and not previous.get("model_filename"):
@@ -1420,7 +1420,7 @@ class ReforceXY(BaseReinforcementLearningModel):
             ):
                 return None
         try:
-            cached = self.dd.meta_data_dictionary.get(pair, {})
+            cached = self.dd.meta_data_dictionary.get(pair, {}) if self.live else {}
             metadata = cached.get(METADATA)
             feature_pipeline = cached.get(FEATURE_PIPELINE)
             prefix: Path | None = None
@@ -1446,7 +1446,10 @@ class ReforceXY(BaseReinforcementLearningModel):
                     feature_pipeline = cloudpickle.load(fp)
             if model is None:
                 assert prefix is not None
-                model = self.MODELCLASS.load(Path(f"{prefix}_model"))
+                model = self.MODELCLASS.load(
+                    Path(f"{prefix}_model"),
+                    device=self.model_training_parameters.get("device", "auto"),
+                )
             if not isinstance(model, self.MODELCLASS):
                 raise ValueError("persisted policy type differs from the configured model")
             ReforceXY._resolve_discount_gamma({"gamma": getattr(model, "gamma", None)})
index 910fb00999c8ef368f78d28583dc8ec92a7fae49..ec5274ddf7c5061256ac799ff1ff9444ec9f6e01 100644 (file)
     },
     {
       "endCharacter": 87,
-      "endLine": 3242,
+      "endLine": 3257,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Cannot access attribute \"index\" for class \"NDArray[Unknown]\"\n  Attribute \"index\" is unknown",
       "rule": "reportAttributeAccessIssue",
       "severity": "error",
       "startCharacter": 82,
-      "startLine": 3242
+      "startLine": 3257
     },
     {
       "endCharacter": 68,
-      "endLine": 3243,
+      "endLine": 3258,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Cannot access attribute \"index\" for class \"NDArray[Unknown]\"\n  Attribute \"index\" is unknown",
       "rule": "reportAttributeAccessIssue",
       "severity": "error",
       "startCharacter": 63,
-      "startLine": 3243
+      "startLine": 3258
     },
     {
       "endCharacter": 71,
-      "endLine": 3252,
+      "endLine": 3267,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Cannot access attribute \"index\" for class \"NDArray[Unknown]\"\n  Attribute \"index\" is unknown",
       "rule": "reportAttributeAccessIssue",
       "severity": "error",
       "startCharacter": 66,
-      "startLine": 3252
+      "startLine": 3267
     },
     {
       "endCharacter": 47,
-      "endLine": 3255,
+      "endLine": 3270,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Cannot access attribute \"loc\" for class \"NDArray[Unknown]\"\n  Attribute \"loc\" is unknown",
       "rule": "reportAttributeAccessIssue",
       "severity": "error",
       "startCharacter": 44,
-      "startLine": 3255
+      "startLine": 3270
     },
     {
       "endCharacter": 47,
-      "endLine": 3255,
+      "endLine": 3270,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Cannot access attribute \"loc\" for class \"list[Unknown]\"\n  Attribute \"loc\" is unknown",
       "rule": "reportAttributeAccessIssue",
       "severity": "error",
       "startCharacter": 44,
-      "startLine": 3255
+      "startLine": 3270
     },
     {
       "endCharacter": 43,
-      "endLine": 3256,
+      "endLine": 3271,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Cannot access attribute \"loc\" for class \"NDArray[Unknown]\"\n  Attribute \"loc\" is unknown",
       "rule": "reportAttributeAccessIssue",
       "severity": "error",
       "startCharacter": 40,
-      "startLine": 3256
+      "startLine": 3271
     },
     {
       "endCharacter": 43,
-      "endLine": 3256,
+      "endLine": 3271,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Cannot access attribute \"loc\" for class \"list[Unknown]\"\n  Attribute \"loc\" is unknown",
       "rule": "reportAttributeAccessIssue",
       "severity": "error",
       "startCharacter": 40,
-      "startLine": 3256
+      "startLine": 3271
     },
     {
       "endCharacter": 31,
-      "endLine": 3277,
+      "endLine": 3292,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Cannot access attribute \"empty\" for class \"NDArray[Unknown]\"\n  Attribute \"empty\" is unknown",
       "rule": "reportAttributeAccessIssue",
       "severity": "error",
       "startCharacter": 26,
-      "startLine": 3277
+      "startLine": 3292
     },
     {
       "endCharacter": 31,
-      "endLine": 3277,
+      "endLine": 3292,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Cannot access attribute \"empty\" for class \"list[Unknown]\"\n  Attribute \"empty\" is unknown",
       "rule": "reportAttributeAccessIssue",
       "severity": "error",
       "startCharacter": 26,
-      "startLine": 3277
+      "startLine": 3292
     },
     {
       "endCharacter": 60,
-      "endLine": 3277,
+      "endLine": 3292,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Cannot access attribute \"empty\" for class \"NDArray[Unknown]\"\n  Attribute \"empty\" is unknown",
       "rule": "reportAttributeAccessIssue",
       "severity": "error",
       "startCharacter": 55,
-      "startLine": 3277
+      "startLine": 3292
     },
     {
       "endCharacter": 60,
-      "endLine": 3277,
+      "endLine": 3292,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Cannot access attribute \"empty\" for class \"list[Unknown]\"\n  Attribute \"empty\" is unknown",
       "rule": "reportAttributeAccessIssue",
       "severity": "error",
       "startCharacter": 55,
-      "startLine": 3277
+      "startLine": 3292
     },
     {
       "endCharacter": 73,
-      "endLine": 3289,
+      "endLine": 3304,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Argument of type \"Unknown | Any | ((value: Unknown, start: SupportsIndex = 0, stop: SupportsIndex = sys.maxsize, /) -> int)\" cannot be assigned to parameter \"target\" of type \"Index[Any]\" in function \"get_indexer\"\n  Type \"Unknown | Any | ((value: Unknown, start: SupportsIndex = 0, stop: SupportsIndex = sys.maxsize, /) -> int)\" is not assignable to type \"Index[Any]\"\n    \"MethodType\" is not assignable to \"Index[Any]\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 53,
-      "startLine": 3289
+      "startLine": 3304
     },
     {
       "endCharacter": 73,
-      "endLine": 3289,
+      "endLine": 3304,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Cannot access attribute \"index\" for class \"NDArray[Unknown]\"\n  Attribute \"index\" is unknown",
       "rule": "reportAttributeAccessIssue",
       "severity": "error",
       "startCharacter": 68,
-      "startLine": 3289
+      "startLine": 3304
     },
     {
       "endCharacter": 83,
-      "endLine": 3290,
+      "endLine": 3305,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Argument of type \"Unknown | Any | ((value: Unknown, start: SupportsIndex = 0, stop: SupportsIndex = sys.maxsize, /) -> int)\" cannot be assigned to parameter \"target\" of type \"Index[Any]\" in function \"get_indexer\"\n  Type \"Unknown | Any | ((value: Unknown, start: SupportsIndex = 0, stop: SupportsIndex = sys.maxsize, /) -> int)\" is not assignable to type \"Index[Any]\"\n    \"MethodType\" is not assignable to \"Index[Any]\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 58,
-      "startLine": 3290
+      "startLine": 3305
     },
     {
       "endCharacter": 83,
-      "endLine": 3290,
+      "endLine": 3305,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Cannot access attribute \"index\" for class \"NDArray[Unknown]\"\n  Attribute \"index\" is unknown",
       "rule": "reportAttributeAccessIssue",
       "severity": "error",
       "startCharacter": 78,
-      "startLine": 3290
+      "startLine": 3305
     },
     {
       "endCharacter": 46,
-      "endLine": 3525,
+      "endLine": 3540,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Cannot access attribute \"empty\" for class \"Buffer\"\n  Attribute \"empty\" is unknown",
       "rule": "reportAttributeAccessIssue",
       "severity": "error",
       "startCharacter": 41,
-      "startLine": 3525
+      "startLine": 3540
     },
     {
       "endCharacter": 46,
-      "endLine": 3525,
+      "endLine": 3540,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Cannot access attribute \"empty\" for class \"_NestedSequence[_SupportsArray[dtype[Any]]]\"\n  Attribute \"empty\" is unknown",
       "rule": "reportAttributeAccessIssue",
       "severity": "error",
       "startCharacter": 41,
-      "startLine": 3525
+      "startLine": 3540
     },
     {
       "endCharacter": 46,
-      "endLine": 3525,
+      "endLine": 3540,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Cannot access attribute \"empty\" for class \"_NestedSequence[complex | bytes | str]\"\n  Attribute \"empty\" is unknown",
       "rule": "reportAttributeAccessIssue",
       "severity": "error",
       "startCharacter": 41,
-      "startLine": 3525
+      "startLine": 3540
     },
     {
       "endCharacter": 46,
-      "endLine": 3525,
+      "endLine": 3540,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Cannot access attribute \"empty\" for class \"_SupportsArray[dtype[Any]]\"\n  Attribute \"empty\" is unknown",
       "rule": "reportAttributeAccessIssue",
       "severity": "error",
       "startCharacter": 41,
-      "startLine": 3525
+      "startLine": 3540
     },
     {
       "endCharacter": 46,
-      "endLine": 3525,
+      "endLine": 3540,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Cannot access attribute \"empty\" for class \"bytes\"\n  Attribute \"empty\" is unknown",
       "rule": "reportAttributeAccessIssue",
       "severity": "error",
       "startCharacter": 41,
-      "startLine": 3525
+      "startLine": 3540
     },
     {
       "endCharacter": 46,
-      "endLine": 3525,
+      "endLine": 3540,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Cannot access attribute \"empty\" for class \"complex\"\n  Attribute \"empty\" is unknown",
       "rule": "reportAttributeAccessIssue",
       "severity": "error",
       "startCharacter": 41,
-      "startLine": 3525
+      "startLine": 3540
     },
     {
       "endCharacter": 46,
-      "endLine": 3525,
+      "endLine": 3540,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Cannot access attribute \"empty\" for class \"str\"\n  Attribute \"empty\" is unknown",
       "rule": "reportAttributeAccessIssue",
       "severity": "error",
       "startCharacter": 41,
-      "startLine": 3525
+      "startLine": 3540
     },
     {
       "endCharacter": 41,
-      "endLine": 3532,
+      "endLine": 3547,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Argument of type \"ArrayLike\" cannot be assigned to parameter \"features\" of type \"DataFrame\" in function \"_sanitize_pipeline_weights\"\n  Type \"ArrayLike\" is not assignable to type \"DataFrame\"\n    \"Buffer\" is not assignable to \"DataFrame\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 16,
-      "startLine": 3532
+      "startLine": 3547
     },
     {
       "endCharacter": 39,
-      "endLine": 3581,
+      "endLine": 3596,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Argument of type \"ArrayLike\" cannot be assigned to parameter \"features\" of type \"DataFrame\" in function \"_sanitize_pipeline_weights\"\n  Type \"ArrayLike\" is not assignable to type \"DataFrame\"\n    \"Buffer\" is not assignable to \"DataFrame\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 20,
-      "startLine": 3581
+      "startLine": 3596
     },
     {
       "endCharacter": 19,
-      "endLine": 3691,
+      "endLine": 3706,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Argument of type \"int | Any | None\" cannot be assigned to parameter \"gap\" of type \"int\" in function \"__init__\"\n  Type \"int | Any | None\" is not assignable to type \"int\"\n    \"None\" is not assignable to \"int\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 16,
-      "startLine": 3691
+      "startLine": 3706
     },
     {
       "endCharacter": 21,
-      "endLine": 3802,
+      "endLine": 3817,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"X\" of type \"DataFrame\" in function \"hp_objective\"\n  Type \"Any | None\" is not assignable to type \"DataFrame\"\n    \"None\" is not assignable to \"DataFrame\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 20,
-      "startLine": 3802
+      "startLine": 3817
     },
     {
       "endCharacter": 21,
-      "endLine": 3803,
+      "endLine": 3818,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"y\" of type \"DataFrame\" in function \"hp_objective\"\n  Type \"Any | None\" is not assignable to type \"DataFrame\"\n    \"None\" is not assignable to \"DataFrame\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 20,
-      "startLine": 3803
+      "startLine": 3818
     },
     {
       "endCharacter": 33,
-      "endLine": 3804,
+      "endLine": 3819,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"train_weights\" of type \"NDArray[floating[Any]]\" in function \"hp_objective\"\n  Type \"Any | None\" is not assignable to type \"NDArray[floating[Any]]\"\n    \"None\" is not assignable to \"ndarray[_AnyShape, dtype[floating[Any]]]\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 20,
-      "startLine": 3804
+      "startLine": 3819
     },
     {
       "endCharacter": 32,
-      "endLine": 3805,
+      "endLine": 3820,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"X_validation\" of type \"DataFrame\" in function \"hp_objective\"\n  Type \"Any | None\" is not assignable to type \"DataFrame\"\n    \"None\" is not assignable to \"DataFrame\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 20,
-      "startLine": 3805
+      "startLine": 3820
     },
     {
       "endCharacter": 32,
-      "endLine": 3806,
+      "endLine": 3821,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"y_validation\" of type \"DataFrame\" in function \"hp_objective\"\n  Type \"Any | None\" is not assignable to type \"DataFrame\"\n    \"None\" is not assignable to \"DataFrame\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 20,
-      "startLine": 3806
+      "startLine": 3821
     },
     {
       "endCharacter": 38,
-      "endLine": 3807,
+      "endLine": 3822,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"validation_weights\" of type \"NDArray[floating[Any]]\" in function \"hp_objective\"\n  Type \"Any | None\" is not assignable to type \"NDArray[floating[Any]]\"\n    \"None\" is not assignable to \"ndarray[_AnyShape, dtype[floating[Any]]]\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 20,
-      "startLine": 3807
+      "startLine": 3822
     },
     {
       "endCharacter": 24,
-      "endLine": 3828,
+      "endLine": 3843,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"X_test\" of type \"DataFrame\" in function \"make_test_set_and_weights\"\n  Type \"Any | None\" is not assignable to type \"DataFrame\"\n    \"None\" is not assignable to \"DataFrame\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 12,
-      "startLine": 3828
+      "startLine": 3843
     },
     {
       "endCharacter": 24,
-      "endLine": 3829,
+      "endLine": 3844,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"y_test\" of type \"DataFrame\" in function \"make_test_set_and_weights\"\n  Type \"Any | None\" is not assignable to type \"DataFrame\"\n    \"None\" is not assignable to \"DataFrame\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 12,
-      "startLine": 3829
+      "startLine": 3844
     },
     {
       "endCharacter": 30,
-      "endLine": 3830,
+      "endLine": 3845,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"test_weights\" of type \"NDArray[floating[Any]]\" in function \"make_test_set_and_weights\"\n  Type \"Any | None\" is not assignable to type \"NDArray[floating[Any]]\"\n    \"None\" is not assignable to \"ndarray[_AnyShape, dtype[floating[Any]]]\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 12,
-      "startLine": 3830
+      "startLine": 3845
     },
     {
       "endCharacter": 15,
-      "endLine": 3836,
+      "endLine": 3851,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"X\" of type \"DataFrame\" in function \"fit_regressor\"\n  Type \"Any | None\" is not assignable to type \"DataFrame\"\n    \"None\" is not assignable to \"DataFrame\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 14,
-      "startLine": 3836
+      "startLine": 3851
     },
     {
       "endCharacter": 15,
-      "endLine": 3837,
+      "endLine": 3852,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"y\" of type \"DataFrame\" in function \"fit_regressor\"\n  Type \"Any | None\" is not assignable to type \"DataFrame\"\n    \"None\" is not assignable to \"DataFrame\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 14,
-      "startLine": 3837
+      "startLine": 3852
     },
     {
       "endCharacter": 39,
-      "endLine": 3838,
+      "endLine": 3853,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"train_weights\" of type \"NDArray[floating[Any]]\" in function \"fit_regressor\"\n  Type \"Any | None\" is not assignable to type \"NDArray[floating[Any]]\"\n    \"None\" is not assignable to \"ndarray[_AnyShape, dtype[floating[Any]]]\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 26,
-      "startLine": 3838
+      "startLine": 3853
     },
     {
       "endCharacter": 38,
-      "endLine": 3848,
+      "endLine": 3863,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "\"columns\" is not a known attribute of \"None\"",
       "rule": "reportOptionalMemberAccess",
       "severity": "error",
       "startCharacter": 31,
-      "startLine": 3848
+      "startLine": 3863
     },
     {
       "endCharacter": 34,
-      "endLine": 3849,
+      "endLine": 3864,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "\"index\" is not a known attribute of \"None\"",
       "rule": "reportOptionalMemberAccess",
       "severity": "error",
       "startCharacter": 29,
-      "startLine": 3849
+      "startLine": 3864
     },
     {
       "endCharacter": 82,
-      "endLine": 3851,
+      "endLine": 3866,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "\"copy\" is not a known attribute of \"None\"",
       "rule": "reportOptionalMemberAccess",
       "severity": "error",
       "startCharacter": 78,
-      "startLine": 3851
+      "startLine": 3866
     },
     {
       "endCharacter": 62,
-      "endLine": 4271,
+      "endLine": 4286,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "\"get_pair_dataframe\" is not a known attribute of \"None\"",
       "rule": "reportOptionalMemberAccess",
       "severity": "error",
       "startCharacter": 44,
-      "startLine": 4271
+      "startLine": 4286
     },
     {
       "endCharacter": 68,
-      "endLine": 4652,
+      "endLine": 4667,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Type \"floating[Any] | NDArray[float64]\" is not assignable to return type \"NDArray[floating[Any]]\"\n  Type \"floating[Any] | NDArray[float64]\" is not assignable to type \"NDArray[floating[Any]]\"\n    \"floating[Any]\" is not assignable to \"ndarray[_AnyShape, dtype[floating[Any]]]\"",
       "rule": "reportReturnType",
       "severity": "error",
       "startCharacter": 15,
-      "startLine": 4648
+      "startLine": 4663
     },
     {
       "endCharacter": 13,
-      "endLine": 4681,
+      "endLine": 4696,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "No overloads for \"cdist\" match the provided arguments",
       "rule": "reportCallIssue",
       "severity": "error",
       "startCharacter": 19,
-      "startLine": 4676
+      "startLine": 4691
     },
     {
       "endCharacter": 38,
-      "endLine": 4679,
+      "endLine": 4694,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Argument of type \"str\" cannot be assigned to parameter \"metric\" of type \"_MetricFunc\" in function \"cdist\"\n  Type \"str\" is not assignable to type \"_MetricFunc\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 23,
-      "startLine": 4679
+      "startLine": 4694
     },
     {
       "endCharacter": 9,
-      "endLine": 4784,
+      "endLine": 4799,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "No overloads for \"pdist\" match the provided arguments",
       "rule": "reportCallIssue",
       "severity": "error",
       "startCharacter": 36,
-      "startLine": 4782
+      "startLine": 4797
     },
     {
       "endCharacter": 42,
-      "endLine": 4783,
+      "endLine": 4798,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Argument of type \"str\" cannot be assigned to parameter \"metric\" of type \"_MetricFunc\" in function \"pdist\"\n  Type \"str\" is not assignable to type \"_MetricFunc\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 27,
-      "startLine": 4783
+      "startLine": 4798
     },
     {
       "endCharacter": 9,
-      "endLine": 4861,
+      "endLine": 4876,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "No overloads for \"cdist\" match the provided arguments",
       "rule": "reportCallIssue",
       "severity": "error",
       "startCharacter": 15,
-      "startLine": 4856
+      "startLine": 4871
     },
     {
       "endCharacter": 34,
-      "endLine": 4859,
+      "endLine": 4874,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Argument of type \"str\" cannot be assigned to parameter \"metric\" of type \"_MetricFunc\" in function \"cdist\"\n  Type \"str\" is not assignable to type \"_MetricFunc\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 19,
-      "startLine": 4859
+      "startLine": 4874
     },
     {
       "endCharacter": 99,
-      "endLine": 5050,
+      "endLine": 5065,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "No overloads for \"pdist\" match the provided arguments",
       "rule": "reportCallIssue",
       "severity": "error",
       "startCharacter": 12,
-      "startLine": 5050
+      "startLine": 5065
     },
     {
       "endCharacter": 79,
-      "endLine": 5050,
+      "endLine": 5065,
       "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py",
       "message": "Argument of type \"str\" cannot be assigned to parameter \"metric\" of type \"_MetricFunc\" in function \"pdist\"\n  Type \"str\" is not assignable to type \"_MetricFunc\"",
       "rule": "reportArgumentType",
       "severity": "error",
       "startCharacter": 64,
-      "startLine": 5050
+      "startLine": 5065
     },
     {
       "endCharacter": 55,
index ac02fb2f596e98b0ff4698329498d477e48f8e5f..fe7bd58be7b94ccd210501b3555dab4d1e5afa89 100644 (file)
@@ -144,10 +144,11 @@ wins.
 ## Continual learning
 
 In backtests, continual training uses only a saved model whose training cutoff
-precedes the current window's end and its last available candle boundary. A
-later model left under the same identifier is not reused when a backtest is
-extended into the past. Live and dry-run restarts still restore compatible
-deployed models.
+precedes the current window's end and its last available candle boundary. The
+model and both fitted pipelines are restored from that saved artifact; missing
+or incompatible required files prevent continuation. A later model left under
+the same identifier is not reused when a backtest is extended into the past.
+Live and dry-run restarts still restore compatible deployed models.
 
 ## Backtest cache reuse
 
index c96e81c6fc9000a3ff3a45b88ff16147b82bbe7b..aea9e2c9fb1706805b7bba06a6898231c389afe4 100644 (file)
@@ -1,5 +1,6 @@
 """Runtime contracts for FreqAI's native QuickAdapter prediction history.; requires the Freqtrade QA image."""
 
+import copy
 import unittest
 from types import SimpleNamespace
 from unittest import mock
@@ -7,7 +8,9 @@ from unittest import mock
 import numpy as np
 import pandas as pd
 from freqtrade.enums import RunMode
+from freqtrade.exceptions import DependencyException
 from freqtrade.exchange import timeframe_to_seconds
+from freqtrade.freqai.data_drawer import FEATURE_PIPELINE, LABEL_PIPELINE, METADATA
 from freqtrade.freqai.data_kitchen import FreqaiDataKitchen
 from qa_support import CONFIG_TEMPLATE, PAIR, QaTestCase, model_config, temporary_directory
 
@@ -178,6 +181,100 @@ class PredictionHistoryTest(QaTestCase):
                 else:
                     backtest.dd.save_metadata(dk)
 
+    def test_backtest_continuation_loads_the_validated_artifact_despite_future_caches(self):
+        with temporary_directory() as temp:
+
+            def deployment(day, offset, amplitude, estimators):
+                config = model_config(
+                    temp,
+                    freqai={
+                        "identifier": f"artifact-{estimators}",
+                        "optuna_hyperopt": {"enabled": False},
+                        "model_training_parameters": {"n_estimators": estimators},
+                    },
+                )
+                values = np.arange(48)
+                frame = pd.DataFrame(
+                    {
+                        "date": pd.date_range(day, periods=48, freq="5min", tz="UTC"),
+                        "%-feature": np.sin(values / 4) + offset,
+                        "&s-extrema": amplitude * np.cos(values / 5),
+                    }
+                )
+                owner = QuickAdapterRegressorV3(config=config)
+                owner.live = True
+                dk = FreqaiDataKitchen(config, live=True, pair=PAIR)
+                timestamp = int((frame["date"].iloc[-1] + pd.Timedelta(minutes=5)).timestamp())
+                dk.set_paths(PAIR, timestamp)
+                dk.set_new_model_names(PAIR, timestamp)
+                dk.training_features_list = ["%-feature"]
+                dk.label_list = ["&s-extrema"]
+                model = owner.train(frame, PAIR, dk)
+                owner.dd.get_pair_dict_info(PAIR)
+                owner.dd.pair_dict[PAIR]["trained_timestamp"] = timestamp
+                owner.dd.save_data(model, PAIR, dk)
+                return owner, dk, model, frame
+
+            owner, saved, model, frame = deployment("2026-01-01", 0.0, 1.0, 2)
+            future, _, future_model, _ = deployment("2026-02-01", 1000.0, 10.0, 5)
+            cached = future.dd.meta_data_dictionary[PAIR]
+            probe = frame[["%-feature"]]
+            labels = frame[["&s-extrema"]]
+            expected_features = saved.feature_pipeline.transform(probe)[0]
+            expected_labels = saved.label_pipeline.transform(labels)[0]
+            expected_predictions = model.predict(expected_features)
+            self.assertFalse(
+                np.allclose(expected_predictions, future_model.predict(expected_features))
+            )
+            self.assertFalse(
+                np.allclose(expected_features, cached[FEATURE_PIPELINE].transform(probe)[0])
+            )
+            self.assertFalse(
+                np.allclose(expected_labels, cached[LABEL_PIPELINE].transform(labels)[0])
+            )
+
+            owner.live = False
+            current_config = {
+                **owner.config,
+                "runmode": RunMode.BACKTEST,
+                "timerange": "20260101-20260105",
+                "config_files": [str(CONFIG_TEMPLATE)],
+            }
+            current = FreqaiDataKitchen(current_config, live=False, pair=PAIR)
+            current.training_features_list = ["%-feature"]
+            current.label_list = ["&s-extrema"]
+            cutoff = int(pd.Timestamp("2026-01-03", tz="UTC").timestamp())
+            # A metadata-only window may advance this timestamp, not the saved filename.
+            owner.dd.pair_dict[PAIR]["trained_timestamp"] = future.dd.pair_dict[PAIR][
+                "trained_timestamp"
+            ]
+            previous = copy.deepcopy(owner.dd.pair_dict[PAIR])
+            for model_cached, metadata_cached in ((True, True), (False, True), (True, False)):
+                with self.subTest(model_cached=model_cached, metadata_cached=metadata_cached):
+                    owner.dd.model_dictionary = {PAIR: future_model} if model_cached else {}
+                    owner.dd.meta_data_dictionary = {PAIR: cached} if metadata_cached else {}
+                    restored, features, label_pipeline = owner._resolve_deployment_state(
+                        current, PAIR, as_of_ts=cutoff
+                    )
+                    np.testing.assert_array_equal(
+                        restored.predict(expected_features), expected_predictions
+                    )
+                    np.testing.assert_allclose(features.transform(probe)[0], expected_features)
+                    np.testing.assert_allclose(label_pipeline.transform(labels)[0], expected_labels)
+                    self.assertEqual(owner.dd.pair_dict[PAIR], previous)
+                    if model_cached:
+                        self.assertIs(owner.dd.model_dictionary[PAIR], future_model)
+                    if metadata_cached:
+                        self.assertIs(owner.dd.meta_data_dictionary[PAIR], cached)
+
+            owner.dd.model_dictionary = {PAIR: future_model}
+            owner.dd.meta_data_dictionary = {PAIR: cached}
+            (saved.data_path / f"{saved.model_filename}_model.joblib").unlink()
+            with self.assertRaises(DependencyException):
+                owner._resolve_deployment_state(current, PAIR, as_of_ts=cutoff)
+            self.assertIs(owner.dd.model_dictionary[PAIR], future_model)
+            self.assertIs(owner.dd.meta_data_dictionary[PAIR][METADATA], cached[METADATA])
+
 
 if __name__ == "__main__":
     unittest.main()
index f3aefe46c61bf4ec9d45fd2efd6103765ba68785..f65c859e181be42928793e5e255e00102179d31a 100644 (file)
@@ -2516,11 +2516,11 @@ class QuickAdapterRegressorV3(BaseRegressionModel):
 
         @wraps(load)
         def loaded(drawer: FreqaiDataDrawer, coin: str, kitchen: FreqaiDataKitchen) -> Any:
-            # A backtest window must not inherit another window's cached metadata.
-            if not kitchen.live:
-                drawer.meta_data_dictionary.pop(coin, None)
-                drawer.model_dictionary.pop(coin, None)
-            model = load(coin, kitchen)
+            model = (
+                load(coin, kitchen)
+                if kitchen.live
+                else self._load_deployment_artifact(drawer, coin, kitchen)
+            )
             if model is not None and not kitchen.live:
                 drawer.model_dictionary[coin] = model
                 drawer.meta_data_dictionary[coin] = {
@@ -2988,6 +2988,17 @@ class QuickAdapterRegressorV3(BaseRegressionModel):
             label_weighting_config=label_weighting_config,
         )
 
+    @staticmethod
+    def _load_deployment_artifact(
+        drawer: FreqaiDataDrawer, pair: str, dk: FreqaiDataKitchen
+    ) -> Any:
+        """Load native artifact files without consulting or evicting shared caches."""
+        artifact_drawer = copy.copy(drawer)
+        artifact_drawer.model_dictionary = {}
+        artifact_drawer.meta_data_dictionary = {}
+        # Instance hooks can close over the original drawer and its mutable caches.
+        return FreqaiDataDrawer.load_data(artifact_drawer, pair, dk)
+
     def _resolve_deployment_state(
         self, dk: FreqaiDataKitchen, pair: str, *, as_of_ts: int | None = None
     ) -> tuple[Any, Pipeline, Pipeline] | None:
@@ -3014,7 +3025,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel):
         else:
             return None
 
-        model = self.dd.model_dictionary.get(pair)
+        model = self.dd.model_dictionary.get(pair) if self.live else None
         previous = self.dd.pair_dict.get(pair, {})
         if (
             not self.live
@@ -3038,7 +3049,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel):
             ):
                 return None
         try:
-            cached = self.dd.meta_data_dictionary.get(pair)
+            cached = self.dd.meta_data_dictionary.get(pair) if self.live else None
             if model is not None and cached is not None:
                 metadata = cached[METADATA]
                 feature_pipeline = cached[FEATURE_PIPELINE]
@@ -3048,7 +3059,11 @@ class QuickAdapterRegressorV3(BaseRegressionModel):
                 previous_dk = copy.copy(dk)
                 previous_dk.data_path = Path(previous["data_path"])
                 previous_dk.model_filename = previous["model_filename"]
-                model = self.dd.load_data(pair, previous_dk)
+                if self.live:
+                    model = self.dd.load_data(pair, previous_dk)
+                else:
+                    previous_dk.live = False
+                    model = self._load_deployment_artifact(self.dd, pair, previous_dk)
                 metadata = previous_dk.data
                 feature_pipeline = previous_dk.feature_pipeline
                 label_pipeline = previous_dk.label_pipeline