From 863430621ae3f341ac8639881d1f06db64a23ed7 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Sat, 31 Oct 2026 02:29:27 +0100 Subject: [PATCH] fix(freqai): bind backtest state to saved artifacts (#300) 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 | 354 +++++++++--------- ReforceXY/README.md | 11 +- .../tests/test_model_training_observations.py | 265 ++++++++++++- ReforceXY/user_data/freqaimodels/ReforceXY.py | 9 +- quickadapter/.basedpyright/diagnostics.json | 200 +++++----- quickadapter/README.md | 9 +- quickadapter/tests/test_prediction_history.py | 97 +++++ .../freqaimodels/QuickAdapterRegressorV3.py | 31 +- 8 files changed, 665 insertions(+), 311 deletions(-) diff --git a/ReforceXY/.basedpyright/diagnostics.json b/ReforceXY/.basedpyright/diagnostics.json index fa570c2..6e88324 100644 --- a/ReforceXY/.basedpyright/diagnostics.json +++ b/ReforceXY/.basedpyright/diagnostics.json @@ -205,7 +205,7 @@ "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, @@ -215,7 +215,7 @@ "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, @@ -233,853 +233,853 @@ }, { "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, diff --git a/ReforceXY/README.md b/ReforceXY/README.md index 017c55a..0bd4e16 100644 --- a/ReforceXY/README.md +++ b/ReforceXY/README.md @@ -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. | diff --git a/ReforceXY/tests/test_model_training_observations.py b/ReforceXY/tests/test_model_training_observations.py index 751e872..44512fd 100644 --- a/ReforceXY/tests/test_model_training_observations.py +++ b/ReforceXY/tests/test_model_training_observations.py @@ -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]) diff --git a/ReforceXY/user_data/freqaimodels/ReforceXY.py b/ReforceXY/user_data/freqaimodels/ReforceXY.py index 5598ba6..1c847cc 100644 --- a/ReforceXY/user_data/freqaimodels/ReforceXY.py +++ b/ReforceXY/user_data/freqaimodels/ReforceXY.py @@ -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)}) diff --git a/quickadapter/.basedpyright/diagnostics.json b/quickadapter/.basedpyright/diagnostics.json index 910fb00..ec5274d 100644 --- a/quickadapter/.basedpyright/diagnostics.json +++ b/quickadapter/.basedpyright/diagnostics.json @@ -243,503 +243,503 @@ }, { "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, diff --git a/quickadapter/README.md b/quickadapter/README.md index ac02fb2..fe7bd58 100644 --- a/quickadapter/README.md +++ b/quickadapter/README.md @@ -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 diff --git a/quickadapter/tests/test_prediction_history.py b/quickadapter/tests/test_prediction_history.py index c96e81c..aea9e2c 100644 --- a/quickadapter/tests/test_prediction_history.py +++ b/quickadapter/tests/test_prediction_history.py @@ -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() diff --git a/quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py b/quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py index f3aefe4..f65c859 100644 --- a/quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py +++ b/quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py @@ -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 -- 2.53.0