From 8f9adc398a0b73eb81ae1145b3309785f0863a00 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Fri, 18 Sep 2026 17:23:00 +0200 Subject: [PATCH] fix(reforcexy): port applicable QuickAdapter PR #261 fixes (#263) * fix(reforcexy): port applicable QuickAdapter PR #261 fixes - Align prediction history to requested candle dates and exact strategy indexes (recorded/produced provenance, dedupe, exact-index attach). - Freeze deployment feature coordinates for continual learning with independent cache/disk state restoration and explicit rejection of legacy or incompatible artifacts. - Keep raw OHLC environment prices out of feature preprocessing; cold HPO trials use fresh candidate pipelines while the final continuation reuses the frozen deployment pipeline on the raw split. - Return the current finite evaluation best checkpoint (best_model.zip) with final-policy fallback. - Optional per-pair produced-observation action statistics with session warmup, numeric coercion and population standard deviation. - Refresh BasedPyright snapshot; 2 argument-type errors resolved. * fix(reforcexy): align resumed gamma and invalidate incompatible HPO state * docs(reforcexy): clarify continuation and objective compatibility * docs(reforcexy): condense continuation guidance * fix(reforcexy): preserve replay buffer on best checkpoint selection * chore(reforcexy): refresh basedpyright snapshot positions --- README.md | 9 + ReforceXY/.basedpyright/diagnostics.json | 408 +++++++-------- ReforceXY/user_data/freqaimodels/ReforceXY.py | 487 +++++++++++++++--- 3 files changed, 628 insertions(+), 276 deletions(-) diff --git a/README.md b/README.md index 79801d6..ab77e54 100644 --- a/README.md +++ b/README.md @@ -344,6 +344,15 @@ PPO, MaskablePPO, RecurrentPPO, DQN, QRDQN The documented list of model tunables is at the top of the [ReforceXY.py](./ReforceXY/user_data/freqaimodels/ReforceXY.py) file. +Continual learning reuses the deployed policy and fitted feature pipeline. +Reset trained models or use a new `freqai.identifier` to change feature +coordinates or migrate incompatible artifacts. HPO studies and saved best +parameters are reused only when their objective identity matches. + +Optional `fit_live_predictions_candles` statistics count produced observations +per pair after session startup; restarts reset the warmup. See the model +docstrings for continuation, HPO and statistics details. + The rewarding logic and tunables are documented in the [reward space analysis](./ReforceXY/reward_space_analysis/README.md). diff --git a/ReforceXY/.basedpyright/diagnostics.json b/ReforceXY/.basedpyright/diagnostics.json index bd7f0af..0d84044 100644 --- a/ReforceXY/.basedpyright/diagnostics.json +++ b/ReforceXY/.basedpyright/diagnostics.json @@ -541,985 +541,965 @@ "startCharacter": 4, "startLine": 4496 }, - { - "endCharacter": 51, - "endLine": 683, - "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": 26, - "startLine": 683 - }, - { - "endCharacter": 51, - "endLine": 683, - "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": 26, - "startLine": 683 - }, { "endCharacter": 60, - "endLine": 687, + "endLine": 755, "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": 30, - "startLine": 687 + "startLine": 755 }, { "endCharacter": 60, - "endLine": 687, + "endLine": 755, "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": 30, - "startLine": 687 + "startLine": 755 }, { "endCharacter": 22, - "endLine": 758, + "endLine": 827, "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]\"", "rule": "reportAttributeAccessIssue", "severity": "error", "startCharacter": 13, - "startLine": 758 + "startLine": 827 }, { "endCharacter": 37, - "endLine": 758, + "endLine": 827, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "Cannot assign to attribute \"eval_env\" for class \"ReforceXY*\"\n  Expression of type \"VecEnv\" cannot be assigned to attribute \"eval_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]\"", "rule": "reportAttributeAccessIssue", "severity": "error", "startCharacter": 29, - "startLine": 758 + "startLine": 827 }, { "endCharacter": 38, - "endLine": 1033, + "endLine": 1260, "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": 1033 + "startLine": 1260 }, { "endCharacter": 36, - "endLine": 1037, + "endLine": 1264, "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": 1037 + "startLine": 1264 }, { "endCharacter": 80, - "endLine": 1040, + "endLine": 1267, "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": 1040 + "startLine": 1267 }, { "endCharacter": 78, - "endLine": 1041, + "endLine": 1268, "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": 1041 + "startLine": 1268 }, { "endCharacter": 80, - "endLine": 1042, + "endLine": 1269, "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": 1042 + "startLine": 1269 }, { "endCharacter": 83, - "endLine": 1110, + "endLine": 1341, "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": 55, - "startLine": 1110 + "startLine": 1341 }, { "endCharacter": 52, - "endLine": 1141, + "endLine": 1382, "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 \"BaseEnvironment\" in function \"get_callbacks\"\n  Type \"VecMonitor | SubprocVecEnv | Env[Unknown, Unknown]\" is not assignable to type \"BaseEnvironment\"\n    \"SubprocVecEnv\" is not assignable to \"BaseEnvironment\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 39, - "startLine": 1141 + "startLine": 1382 }, { "endCharacter": 50, - "endLine": 1302, + "endLine": 1611, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "Argument of type \"NDArray[float32]\" cannot be assigned to parameter \"x\" of type \"float32\" in function \"append\"\n  \"ndarray[_AnyShape, dtype[float32]]\" is not assignable to \"floating[_32Bit]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 36, - "startLine": 1302 + "startLine": 1611 }, { - "endCharacter": 32, - "endLine": 2027, + "endCharacter": 44, + "endLine": 2372, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "\"best_trial_params\" is possibly unbound", "rule": "reportPossiblyUnboundVariable", "severity": "error", - "startCharacter": 15, - "startLine": 2027 + "startCharacter": 27, + "startLine": 2372 }, { "endCharacter": 12, - "endLine": 2036, + "endLine": 2392, "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": 2036 + "startLine": 2392 }, { "endCharacter": 16, - "endLine": 2037, + "endLine": 2393, "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": 2037 + "startLine": 2393 }, { "endCharacter": 47, - "endLine": 2082, + "endLine": 2432, "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", "rule": "reportArgumentType", "severity": "error", "startCharacter": 38, - "startLine": 2082 + "startLine": 2432 }, { "endCharacter": 45, - "endLine": 2084, + "endLine": 2434, "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", "rule": "reportArgumentType", "severity": "error", "startCharacter": 36, - "startLine": 2084 + "startLine": 2434 }, { "endCharacter": 45, - "endLine": 2086, + "endLine": 2436, "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", "rule": "reportArgumentType", "severity": "error", "startCharacter": 37, - "startLine": 2086 + "startLine": 2436 }, { "endCharacter": 43, - "endLine": 2088, + "endLine": 2438, "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", "rule": "reportArgumentType", "severity": "error", "startCharacter": 35, - "startLine": 2088 + "startLine": 2438 }, { "endCharacter": 22, - "endLine": 2129, + "endLine": 2486, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "Operator \"*\" not supported for \"None\"", "rule": "reportOptionalOperand", "severity": "error", "startCharacter": 15, - "startLine": 2129 + "startLine": 2486 }, { "endCharacter": 96, - "endLine": 2131, + "endLine": 2488, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "Operator \"*\" not supported for \"None\"", "rule": "reportOptionalOperand", "severity": "error", "startCharacter": 89, - "startLine": 2131 + "startLine": 2488 }, { "endCharacter": 23, - "endLine": 2134, + "endLine": 2491, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "Operator \"*\" not supported for \"None\"", "rule": "reportOptionalOperand", "severity": "error", "startCharacter": 16, - "startLine": 2134 + "startLine": 2491 }, { "endCharacter": 98, - "endLine": 2136, + "endLine": 2493, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "Operator \"*\" not supported for \"None\"", "rule": "reportOptionalOperand", "severity": "error", "startCharacter": 91, - "startLine": 2136 + "startLine": 2493 }, { "endCharacter": 44, - "endLine": 2148, + "endLine": 2505, "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": 2148 + "startLine": 2505 }, { "endCharacter": 133, - "endLine": 2150, + "endLine": 2507, "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": 2150 + "startLine": 2507 }, { "endCharacter": 30, - "endLine": 2153, + "endLine": 2510, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "Operator \">\" not supported for \"None\"", "rule": "reportOptionalOperand", "severity": "error", "startCharacter": 15, - "startLine": 2153 + "startLine": 2510 }, { "endCharacter": 47, - "endLine": 2197, + "endLine": 2560, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "Argument of type \"VecEnv\" cannot be assigned to parameter \"eval_env\" of type \"BaseEnvironment\" in function \"get_callbacks\"\n  \"VecEnv\" is not assignable to \"BaseEnvironment\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 39, - "startLine": 2197 + "startLine": 2560 }, { "endCharacter": 46, - "endLine": 2254, + "endLine": 2617, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "\"is_pruned\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 37, - "startLine": 2254 + "startLine": 2617 }, { "endCharacter": 57, - "endLine": 2257, + "endLine": 2620, "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": 2257 + "startLine": 2620 }, { "endCharacter": 37, - "endLine": 2267, + "endLine": 2630, "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]\"", "rule": "reportAttributeAccessIssue", "severity": "error", "startCharacter": 33, - "startLine": 2267 + "startLine": 2630 }, { "endCharacter": 36, - "endLine": 2272, + "endLine": 2635, "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]\"", "rule": "reportAttributeAccessIssue", "severity": "error", "startCharacter": 32, - "startLine": 2272 + "startLine": 2635 }, { "endCharacter": 7, - "endLine": 2304, + "endLine": 2667, "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": 2304 + "startLine": 2667 }, { "endCharacter": 34, - "endLine": 2316, + "endLine": 2679, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "Declaration \"_last_closed_position\" is obscured by a declaration of the same name", "rule": "reportRedeclaration", "severity": "error", "startCharacter": 13, - "startLine": 2316 + "startLine": 2679 }, { "endCharacter": 36, - "endLine": 2317, + "endLine": 2680, "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": 2317 + "startLine": 2680 }, { "endCharacter": 13, - "endLine": 3153, + "endLine": 3516, "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": 3153 + "startLine": 3516 }, { "endCharacter": 26, - "endLine": 3175, + "endLine": 3538, "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": 3175 + "startLine": 3538 }, { "endCharacter": 55, - "endLine": 3504, + "endLine": 3867, "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": 3504 + "startLine": 3867 }, { "endCharacter": 55, - "endLine": 3504, + "endLine": 3867, "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": 3504 + "startLine": 3867 }, { "endCharacter": 59, - "endLine": 3528, + "endLine": 3891, "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": 3528 + "startLine": 3891 }, { "endCharacter": 59, - "endLine": 3528, + "endLine": 3891, "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": 3528 + "startLine": 3891 }, { "endCharacter": 24, - "endLine": 3573, + "endLine": 3936, "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": 3573 + "startLine": 3936 }, { "endCharacter": 53, - "endLine": 3641, + "endLine": 4004, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "\"name\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 49, - "startLine": 3641 + "startLine": 4004 }, { "endCharacter": 12, - "endLine": 3675, + "endLine": 4038, "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": 3675 + "startLine": 4038 }, { "endCharacter": 20, - "endLine": 3788, + "endLine": 4151, "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": 3788 + "startLine": 4151 }, { "endCharacter": 65, - "endLine": 3981, + "endLine": 4344, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "Type \"Any | None\" is not assignable to return type \"float\"\n  Type \"Any | None\" is not assignable to type \"float\"\n    \"None\" is not assignable to \"float\"", "rule": "reportReturnType", "severity": "error", "startCharacter": 15, - "startLine": 3981 + "startLine": 4344 }, { "endCharacter": 40, - "endLine": 4020, + "endLine": 4383, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "\"Figure\" is not exported from module \"matplotlib.pyplot\"", "rule": "reportPrivateImportUsage", "severity": "error", "startCharacter": 34, - "startLine": 4020 + "startLine": 4383 }, { "endCharacter": 54, - "endLine": 4518, + "endLine": 4881, "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": 4518 + "startLine": 4881 }, { "endCharacter": 54, - "endLine": 4518, + "endLine": 4881, "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": 4518 + "startLine": 4881 }, { "endCharacter": 50, - "endLine": 4530, + "endLine": 4893, "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": 4530 + "startLine": 4893 }, { "endCharacter": 72, - "endLine": 4541, + "endLine": 4904, "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": 4541 + "startLine": 4904 }, { "endCharacter": 72, - "endLine": 4541, + "endLine": 4904, "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": 4541 + "startLine": 4904 }, { "endCharacter": 73, - "endLine": 4550, + "endLine": 4913, "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": 4550 + "startLine": 4913 }, { "endCharacter": 73, - "endLine": 4550, + "endLine": 4913, "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": 4550 + "startLine": 4913 }, { "endCharacter": 58, - "endLine": 4559, + "endLine": 4922, "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": 4559 + "startLine": 4922 }, { "endCharacter": 58, - "endLine": 4559, + "endLine": 4922, "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": 4559 + "startLine": 4922 }, { "endCharacter": 50, - "endLine": 4783, + "endLine": 5146, "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": 4783 + "startLine": 5146 }, { "endCharacter": 20, - "endLine": 4960, + "endLine": 5323, "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": 4960 + "startLine": 5323 }, { "endCharacter": 20, - "endLine": 4960, + "endLine": 5323, "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": 4960 + "startLine": 5323 }, { "endCharacter": 59, - "endLine": 4965, + "endLine": 5328, "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": 4965 + "startLine": 5328 }, { "endCharacter": 59, - "endLine": 4965, + "endLine": 5328, "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": 4965 + "startLine": 5328 }, { "endCharacter": 65, - "endLine": 4966, + "endLine": 5329, "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": 4966 + "startLine": 5329 }, { "endCharacter": 65, - "endLine": 4966, + "endLine": 5329, "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": 4966 + "startLine": 5329 }, { "endCharacter": 57, - "endLine": 4967, + "endLine": 5330, "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": 31, - "startLine": 4967 + "startLine": 5330 }, { "endCharacter": 57, - "endLine": 4967, + "endLine": 5330, "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": 31, - "startLine": 4967 + "startLine": 5330 }, { "endCharacter": 63, - "endLine": 4969, + "endLine": 5332, "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": 4969 + "startLine": 5332 }, { "endCharacter": 63, - "endLine": 4969, + "endLine": 5332, "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": 4969 + "startLine": 5332 }, { "endCharacter": 61, - "endLine": 4971, + "endLine": 5334, "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": 4971 + "startLine": 5334 }, { "endCharacter": 61, - "endLine": 4971, + "endLine": 5334, "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": 4971 + "startLine": 5334 }, { "endCharacter": 67, - "endLine": 4972, + "endLine": 5335, "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": 4972 + "startLine": 5335 }, { "endCharacter": 67, - "endLine": 4972, + "endLine": 5335, "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": 4972 + "startLine": 5335 }, { "endCharacter": 73, - "endLine": 4973, + "endLine": 5336, "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": 4973 + "startLine": 5336 }, { "endCharacter": 73, - "endLine": 4973, + "endLine": 5336, "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": 4973 + "startLine": 5336 }, { "endCharacter": 61, - "endLine": 4974, + "endLine": 5337, "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": 4974 + "startLine": 5337 }, { "endCharacter": 61, - "endLine": 4974, + "endLine": 5337, "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": 4974 + "startLine": 5337 }, { "endCharacter": 76, - "endLine": 4978, + "endLine": 5341, "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": 4978 + "startLine": 5341 }, { "endCharacter": 76, - "endLine": 4978, + "endLine": 5341, "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": 4978 + "startLine": 5341 }, { "endCharacter": 89, - "endLine": 4980, + "endLine": 5343, "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": 4980 + "startLine": 5343 }, { "endCharacter": 89, - "endLine": 4980, + "endLine": 5343, "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": 4980 + "startLine": 5343 }, { "endCharacter": 83, - "endLine": 4981, + "endLine": 5344, "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": 4981 + "startLine": 5344 }, { "endCharacter": 83, - "endLine": 4981, + "endLine": 5344, "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": 4981 + "startLine": 5344 }, { "endCharacter": 57, - "endLine": 5006, + "endLine": 5369, "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": 31, - "startLine": 5006 + "startLine": 5369 }, { "endCharacter": 57, - "endLine": 5006, + "endLine": 5369, "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": 31, - "startLine": 5006 + "startLine": 5369 }, { "endCharacter": 65, - "endLine": 5007, + "endLine": 5370, "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": 5007 + "startLine": 5370 }, { "endCharacter": 65, - "endLine": 5007, + "endLine": 5370, "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": 5007 + "startLine": 5370 }, { "endCharacter": 67, - "endLine": 5009, + "endLine": 5372, "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": 5009 + "startLine": 5372 }, { "endCharacter": 67, - "endLine": 5009, + "endLine": 5372, "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": 5009 + "startLine": 5372 }, { "endCharacter": 87, - "endLine": 5012, + "endLine": 5375, "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": 5012 + "startLine": 5375 }, { "endCharacter": 87, - "endLine": 5012, + "endLine": 5375, "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": 5012 + "startLine": 5375 }, { "endCharacter": 93, - "endLine": 5013, + "endLine": 5376, "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": 5013 + "startLine": 5376 }, { "endCharacter": 93, - "endLine": 5013, + "endLine": 5376, "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": 5013 + "startLine": 5376 }, { "endCharacter": 89, - "endLine": 5014, + "endLine": 5377, "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": 5014 + "startLine": 5377 }, { "endCharacter": 89, - "endLine": 5014, + "endLine": 5377, "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": 5014 + "startLine": 5377 }, { "endCharacter": 89, - "endLine": 5015, + "endLine": 5378, "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": 5015 + "startLine": 5378 }, { "endCharacter": 89, - "endLine": 5015, + "endLine": 5378, "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": 5015 + "startLine": 5378 }, { "endCharacter": 75, - "endLine": 5016, + "endLine": 5379, "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": 5016 + "startLine": 5379 }, { "endCharacter": 75, - "endLine": 5016, + "endLine": 5379, "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": 5016 + "startLine": 5379 }, { "endCharacter": 33, diff --git a/ReforceXY/user_data/freqaimodels/ReforceXY.py b/ReforceXY/user_data/freqaimodels/ReforceXY.py index 7c0344a..8fc752f 100644 --- a/ReforceXY/user_data/freqaimodels/ReforceXY.py +++ b/ReforceXY/user_data/freqaimodels/ReforceXY.py @@ -32,8 +32,16 @@ import matplotlib.transforms as mtransforms import numpy as np import optunahub import pandas as pd +import rapidjson import torch as th -from freqtrade.freqai.data_drawer import FreqaiDataDrawer +from datasieve.pipeline import Pipeline +from freqtrade.exceptions import DependencyException +from freqtrade.freqai.data_drawer import ( + FEATURE_PIPELINE, + METADATA, + METADATA_NUMBER_MODE, + FreqaiDataDrawer, +) from freqtrade.freqai.data_kitchen import FreqaiDataKitchen from freqtrade.freqai.RL.Base5ActionRLEnv import Actions, Base5ActionRLEnv, Positions from freqtrade.freqai.RL.BaseEnvironment import BaseEnvironment @@ -43,6 +51,7 @@ from freqtrade.freqai.RL.BaseReinforcementLearningModel import ( from freqtrade.freqai.tensorboard.TensorboardCallback import TensorboardCallback from freqtrade.strategy import timeframe_to_minutes from gymnasium.spaces import Box +from joblib.externals import cloudpickle from matplotlib.lines import Line2D from numpy.typing import NDArray from optuna import Trial, TrialPruned, create_study, delete_study @@ -81,6 +90,31 @@ from stable_baselines3.common.vec_env import ( _DATE_PRED_DEDUP_SENTINEL = "_freqai_strategies_date_pred_repair_patched" +def _recorded_prediction_mask(frame: pd.DataFrame) -> NDArray[np.bool_]: + """Identify recorded rows from metadata, never from prediction magnitudes.""" + recorded = np.zeros(len(frame), dtype=bool) + if "close_price" in frame: + close = pd.to_numeric(frame["close_price"], errors="coerce") + recorded |= (close.gt(0) & close.lt(np.inf)).fillna(False).to_numpy(dtype=bool) + if "do_predict" in frame: + status = pd.to_numeric(frame["do_predict"], errors="coerce") + recorded |= ( + (status.gt(-np.inf) & status.lt(np.inf) & status.ne(0)) + .fillna(False) + .to_numpy(dtype=bool) + ) + return recorded + + +def _produced_prediction_mask(frame: pd.DataFrame) -> NDArray[np.bool_]: + """Exclude expired-model placeholders from recorded model observations.""" + produced = _recorded_prediction_mask(frame) + if "do_predict" in frame: + status = pd.to_numeric(frame["do_predict"], errors="coerce") + produced &= status.ne(2).fillna(True).to_numpy(dtype=bool) + return produced + + def _dedupe_historic_predictions_on_date_pred(frame: pd.DataFrame) -> pd.DataFrame: """Normalize dates and retain the latest recorded row per candle, in date order. @@ -99,13 +133,7 @@ def _dedupe_historic_predictions_on_date_pred(frame: pd.DataFrame) -> pd.DataFra result["date_pred"] = date_pred return result - recorded = np.zeros(len(frame), dtype=bool) - if "close_price" in frame: - close = pd.to_numeric(frame["close_price"], errors="coerce") - recorded |= (close.gt(0) & close.lt(np.inf)).fillna(False).to_numpy(dtype=bool) - if "do_predict" in frame: - status = pd.to_numeric(frame["do_predict"], errors="coerce") - recorded |= (status.notna() & status.ne(0)).to_numpy(dtype=bool) + recorded = _recorded_prediction_mask(frame) # Rank only metadata, without copying or coercing all prediction columns. order = pd.DataFrame( {"date_pred": date_pred.array, "recorded": recorded, "position": np.arange(len(frame))} @@ -120,6 +148,21 @@ def _dedupe_historic_predictions_on_date_pred(frame: pd.DataFrame) -> pd.DataFra return result.reset_index(drop=True) +def _align_historic_predictions(history: pd.DataFrame, dataframe: pd.DataFrame) -> pd.DataFrame: + """Align unique, normalized history to requested candle dates and exact index.""" + dates = pd.to_datetime(dataframe["date"], utc=True, errors="coerce", format="mixed") + indexed = history.set_index("date_pred", drop=False) + aligned = indexed.reindex(pd.DatetimeIndex(dates)) + missing = ~pd.DatetimeIndex(dates).isin(indexed.index) + aligned.index = dataframe.index + aligned["date_pred"] = dates.array + if "do_predict" in aligned: + aligned["do_predict"] = aligned["do_predict"].where(~missing, 0) + else: + aligned["do_predict"] = 0 + return aligned + + def _install_date_pred_dedup_patch() -> None: """Repair persisted history and duplicates produced by older Freqtrade writers. @@ -157,14 +200,12 @@ def _install_date_pred_dedup_patch() -> None: self.historic_predictions[pair] = _dedupe_historic_predictions_on_date_pred( self.historic_predictions[pair] ) - original_set_initial(self, pair, pred_df, dataframe) - history = self.historic_predictions[pair] - repaired = _dedupe_historic_predictions_on_date_pred(history) - if repaired is not history: - self.historic_predictions[pair] = repaired - self.model_return_values[pair] = repaired.tail(len(dataframe.index)).reset_index( - drop=True - ) + original_set_initial( + self, pair, pred_df.reset_index(drop=True), dataframe.reset_index(drop=True) + ) + repaired = _dedupe_historic_predictions_on_date_pred(self.historic_predictions[pair]) + self.historic_predictions[pair] = repaired + self.model_return_values[pair] = _align_historic_predictions(repaired, dataframe) @wraps(original_append) def append_model_predictions( @@ -180,23 +221,24 @@ def _install_date_pred_dedup_patch() -> None: ) if self.historic_predictions[pair].empty and not strat_df.empty: # Legacy append requires an initialized row; let upstream construct it. - original_set_initial(self, pair, pd.DataFrame(index=pd.RangeIndex(1)), strat_df.tail(1)) - original_append(self, pair, predictions, do_preds, dk, strat_df) - history = self.historic_predictions[pair] - repaired = _dedupe_historic_predictions_on_date_pred(history) - if repaired is not history: - self.historic_predictions[pair] = repaired - self.model_return_values[pair] = repaired.tail(len(strat_df.index)).reset_index( - drop=True + original_set_initial( + self, + pair, + pd.DataFrame(index=pd.RangeIndex(1)), + strat_df.tail(1).reset_index(drop=True), ) + original_append(self, pair, predictions, do_preds, dk, strat_df) + repaired = _dedupe_historic_predictions_on_date_pred(self.historic_predictions[pair]) + self.historic_predictions[pair] = repaired + self.model_return_values[pair] = _align_historic_predictions(repaired, strat_df) @wraps(original_attach) def attach_return_values_to_return_dataframe( self, pair: str, dataframe: pd.DataFrame ) -> pd.DataFrame: - self.model_return_values[pair] = _dedupe_historic_predictions_on_date_pred( - self.model_return_values[pair] - ) + repaired = _dedupe_historic_predictions_on_date_pred(self.historic_predictions[pair]) + self.historic_predictions[pair] = repaired + self.model_return_values[pair] = _align_historic_predictions(repaired, dataframe) return original_attach(self, pair, dataframe) replacements = ( @@ -264,7 +306,14 @@ class ReforceXY(BaseReinforcementLearningModel): ... "freqai": { ... - "fit_live_predictions_candles": 0, // Optional non-negative integer; omitted or 0 disables prediction statistics + "fit_live_predictions_candles": 0, // Optional non-negative integer; omitted or 0 disables action statistics + // Live/dry-run: latest N produced observations per pair after session startup. + // Restart resets warmup; numeric mean/std are zero until N observations exist. + // Downtime and expired status 2 do not count; neutral/exit actions and recorded + // rejected predictions do. Backtests use the previous N rows, not the current row. + // Numeric objects are coerced; non-finite samples are ignored. Empty finite + // samples yield zeros; constants have zero spread; nonnumeric objects are skipped. + // Statistics do not gate RL actions. Population standard deviation is used. "model_training_parameters": { "device": "auto", // PyTorch device (auto|cpu|cuda|cuda:0) "gpu_memory_fraction": null, // GPU VRAM fraction limit per process (0.0, 1.0], null disables @@ -302,6 +351,20 @@ class ReforceXY(BaseReinforcementLearningModel): } } } + + Continual learning keeps the deployed policy's fitted feature coordinates frozen, + including feature selection and scaling. Reset trained models or use a new + freqai.identifier to change those coordinates or migrate legacy artifacts. + First training and continual_learning=false use fresh pipelines and policies. + Hyperopt always uses fresh current-window pipelines and cold policies; final + continuation uses the original raw split in the deployed coordinates. Selected + constructor/network parameters do not rebuild an existing policy's architecture; + a resumed policy keeps its own discount gamma for environment reward shaping. + Raw OHLC prices are preserved separately for every environment. Noise augments + training features only, never evaluation or prediction features. + Prediction uses the best checkpoint selected by the current fit's evaluation, + falling back to the final policy if no usable current checkpoint was selected. + Requirements: - pip install optuna optunahub -r https://hub.optuna.org/samplers/auto_sampler/requirements.txt @@ -311,6 +374,9 @@ class ReforceXY(BaseReinforcementLearningModel): _LOG_2: Final[float] = math.log(2.0) + _DEPLOYMENT_COORDINATE_MARKER_KEY: Final[str] = "reforcexy_deployment_coordinates" + _DEPLOYMENT_COORDINATE_GENERATION: Final[str] = "frozen-pipelines-v1" + DEFAULT_BASE_FACTOR: Final[float] = 100.0 DEFAULT_MAX_TRADE_DURATION_CANDLES: Final[int] = 128 @@ -423,6 +489,8 @@ class ReforceXY(BaseReinforcementLearningModel): _QUARANTINE_TAG: Final[str] = "corrupt" _QUARANTINE_TIE_BREAK_LIMIT: Final[int] = 99 _BEST_PARAMS_LOCK_FILENAME: Final[str] = ".hyperopt-best-params.lock" + # Bump on objective changes that make persisted trials or best params incompatible. + _OPTUNA_OBJECTIVE_IDENTITY: Final[str] = "raw-market-prices-v1" _PPO_N_STEPS: Final[tuple[int, ...]] = (512, 1024, 2048, 4096) _PPO_N_STEPS_MIN: Final[int] = min(_PPO_N_STEPS) _PPO_N_STEPS_MAX: Final[int] = max(_PPO_N_STEPS) @@ -681,7 +749,7 @@ class ReforceXY(BaseReinforcementLearningModel): gamma: float | None = None if model_params and isinstance(model_params.get("gamma"), (int, float)): - gamma = float(model_params.get("gamma")) + gamma = float(model_params["gamma"]) elif self.hyperopt: best_trial_params = self.load_best_trial_params(pair) if best_trial_params and isinstance(best_trial_params.get("gamma"), (int, float)): @@ -712,6 +780,7 @@ class ReforceXY(BaseReinforcementLearningModel): prices_train: DataFrame, prices_test: DataFrame, dk: FreqaiDataKitchen, + model_params: dict[str, Any] | None = None, ) -> None: """ Set training and evaluation environments @@ -720,9 +789,9 @@ class ReforceXY(BaseReinforcementLearningModel): logger.info("Env [%s]: closing environments", dk.pair) self.close_envs() - train_df = data_dictionary.get("train_features") - test_df = data_dictionary.get("test_features") - env_dict = self.pack_env_dict(dk.pair) + train_df = data_dictionary["train_features"] + test_df = data_dictionary["test_features"] + env_dict = self.pack_env_dict(dk.pair, model_params=model_params) seed = self.get_model_params().get("seed", 42) if self.check_envs: @@ -1022,7 +1091,165 @@ class ReforceXY(BaseReinforcementLearningModel): callbacks.append(self.optuna_eval_callback) return callbacks - def fit(self, data_dictionary: dict[str, Any], dk: FreqaiDataKitchen, **kwargs) -> Any: + def _resolve_deployment_state( + self, dk: FreqaiDataKitchen, pair: str + ) -> tuple[Any, Pipeline] | None: + """Restore the deployed policy and an independent fitted feature pipeline.""" + if not self.continual_learning: + return None + model = self.dd.model_dictionary.get(pair) + previous = self.dd.pair_dict.get(pair, {}) + if model is None and not previous.get("model_filename"): + return None + try: + cached = self.dd.meta_data_dictionary.get(pair, {}) + metadata = cached.get(METADATA) + feature_pipeline = cached.get(FEATURE_PIPELINE) + if model is None or metadata is None or feature_pipeline is None: + # The current kitchen points to the new window, not deployed files. + previous_dk = copy.copy(dk) + previous_dk.data_path = Path(previous["data_path"]) + previous_dk.model_filename = previous["model_filename"] + prefix = previous_dk.data_path / previous_dk.model_filename + if metadata is None: + with Path(f"{prefix}_{METADATA}.json").open("r") as fp: + metadata = rapidjson.load(fp, number_mode=METADATA_NUMBER_MODE) + if feature_pipeline is None: + with Path(f"{prefix}_{FEATURE_PIPELINE}.pkl").open("rb") as fp: + feature_pipeline = cloudpickle.load(fp) + if model is None: + model = self.MODELCLASS.load(Path(f"{prefix}_model")) + if ( + metadata.get(self._DEPLOYMENT_COORDINATE_MARKER_KEY) + != self._DEPLOYMENT_COORDINATE_GENERATION + ): + raise ValueError( + "persisted deployment coordinate generation is missing or incompatible" + ) + if not isinstance(model, self.MODELCLASS): + raise ValueError("persisted policy type differs from the configured model") + for name, expected, actual in ( + ("features", dk.training_features_list, metadata["training_features_list"]), + ("labels", dk.label_list, metadata["label_list"]), + ( + "feature pipeline inputs", + dk.training_features_list, + feature_pipeline.features_in, + ), + ): + if list(expected) != list(actual): + raise ValueError(f"persisted {name} differ in names or order") + state = model, copy.deepcopy(feature_pipeline) + except Exception as exc: + raise DependencyException( + f"[{pair}] Cannot continue training with matching persisted pipelines: {exc}. " + "Reset trained models or use a new freqai.identifier." + ) from exc + logger.info( + f"[{pair}] Continuing deployment in the persisted feature coordinate system; " + "reset trained models to change pipeline configuration" + ) + return state + + def _apply_training_pipeline( + self, + data_dictionary: dict[str, Any], + dk: FreqaiDataKitchen, + deployment_state: tuple[Any, Pipeline] | None = None, + ) -> dict[str, Any]: + """Preserve chronological raw splits; never transform RL action labels.""" + dd = data_dictionary.copy() + dk.feature_pipeline = ( + self.define_data_pipeline(threads=dk.thread_count) + if deployment_state is None + else deployment_state[1] + ) + for split in ("train", "test"): + features = data_dictionary[f"{split}_features"] + labels = data_dictionary[f"{split}_labels"] + weights = data_dictionary[f"{split}_weights"] + if split == "test" and features.empty: + continue + transform = ( + dk.feature_pipeline.fit_transform + if split == "train" and deployment_state is None + else dk.feature_pipeline.transform + ) + transformed, transformed_labels, transformed_weights = transform( + features.copy(), labels.copy(), weights.copy() + ) + transformed = cast("pd.DataFrame", transformed) + transformed_labels = cast("pd.DataFrame", transformed_labels) + transformed_weights = cast("NDArray[np.float64]", np.asarray(transformed_weights)) + if len(transformed) != len(features) or len(transformed_weights) != len(features): + raise DependencyException( + f"[{dk.pair}] RL preprocessing must preserve chronological rows " + "and their alignment with raw prices." + ) + # DataSieve reconstructs frames with RangeIndex while retaining row order. + transformed.index = features.index + transformed_labels.index = labels.index + if split == "train" and deployment_state is not None: + # Noise.transform is a no-op; only augmentation may be fitted. + for name, transformer in dk.feature_pipeline.steps: + if name == "noise": + noisy, _, _, _ = transformer.fit_transform(transformed.to_numpy(copy=True)) + transformed = pd.DataFrame( + noisy, columns=transformed.columns, index=transformed.index + ) + dd[f"{split}_features"] = transformed + dd[f"{split}_labels"] = transformed_labels + dd[f"{split}_weights"] = transformed_weights + dk.data_dictionary = dd + return dd + + def train(self, unfiltered_df: DataFrame, pair: str, dk: FreqaiDataKitchen, **kwargs) -> Any: + """Train cold candidates and continue only in persisted deployment coordinates.""" + # Drawer metadata can alias this kitchen; never invalidate the deployed marker. + dk.data = dk.data.copy() + dk.data.pop(self._DEPLOYMENT_COORDINATE_MARKER_KEY, None) + logger.info(f"--------------------Starting training {pair} --------------------") + features_filtered, labels_filtered = dk.filter_features( + unfiltered_df, dk.training_features_list, dk.label_list, training_filter=True + ) + raw_data = dk.make_train_test_datasets(features_filtered, labels_filtered) + self.df_raw = copy.deepcopy(raw_data["train_features"]) + dk.fit_labels() + # Capture prices once, before normalization and optional raw-OHLC removal. + prices_train, prices_test = self.build_ohlc_price_dataframes(raw_data, pair, dk) + deployment_state = self._resolve_deployment_state(dk, pair) + dd = self._apply_training_pipeline( + raw_data, dk, deployment_state=None if self.hyperopt else deployment_state + ) + logger.info( + f"Training model on {len(dd['train_features'].columns)}" + f" features and {len(dd['train_features'])} data points" + ) + model = self.fit( + dd, + dk, + prices_train=prices_train, + prices_test=prices_test, + deployment_state=deployment_state, + raw_data_dictionary=raw_data if self.hyperopt and deployment_state else None, + **kwargs, + ) + if model is not None: + dk.data[self._DEPLOYMENT_COORDINATE_MARKER_KEY] = self._DEPLOYMENT_COORDINATE_GENERATION + logger.info(f"--------------------done training {pair}--------------------") + return model + + def fit( + self, + data_dictionary: dict[str, Any], + dk: FreqaiDataKitchen, + *, + prices_train: DataFrame, + prices_test: DataFrame, + deployment_state: tuple[Any, Pipeline] | None = None, + raw_data_dictionary: dict[str, Any] | None = None, + **kwargs, + ) -> Any: """ Model fitting method :param data_dictionary: dict = common data dictionary containing all train/test features/labels/weights. @@ -1075,7 +1302,7 @@ class ReforceXY(BaseReinforcementLearningModel): start_time = time.time() if self.hyperopt: - best_params = self.optimize(dk, total_timesteps) + best_params = self.optimize(dk, total_timesteps, prices_train, prices_test) if best_params is None: logger.error( "Hyperopt [%s]: optimization failed, using default model params", @@ -1085,6 +1312,10 @@ class ReforceXY(BaseReinforcementLearningModel): model_params = best_params else: model_params = self.get_model_params() + if raw_data_dictionary is not None: + data_dictionary = self._apply_training_pipeline( + raw_data_dictionary, dk, deployment_state=deployment_state + ) logger.info("Model [%s]: %s params: %s", dk.pair, self.model_type, model_params) # "PPO" @@ -1117,18 +1348,28 @@ class ReforceXY(BaseReinforcementLearningModel): else: tensorboard_log_path = None - # Rebuild train and eval environments before training to sync model parameters - prices_train, prices_test = self.build_ohlc_price_dataframes( - dk.data_dictionary, dk.pair, dk + # Reward shaping must use the resumed policy's discount gamma, + # not the configured or HPO-selected value. + resumed_gamma: dict[str, Any] | None = None + if deployment_state is not None: + learner_gamma = getattr(deployment_state[0], "gamma", None) + if isinstance(learner_gamma, (int, float)) and np.isfinite(learner_gamma): + resumed_gamma = {"gamma": float(learner_gamma)} + self.set_train_and_eval_environments( + data_dictionary, + prices_train, + prices_test, + dk, + model_params=resumed_gamma, ) - self.set_train_and_eval_environments(dk.data_dictionary, prices_train, prices_test, dk) - model = self.get_init_model(dk.pair) + model = deployment_state[0] if deployment_state is not None else None if model is not None: logger.info( "Training [%s]: continual training activated, starting from previously trained model state", dk.pair, ) + model.tb_logger = getattr(self, "tb_logger", None) model.set_env(self.train_env) else: model = self.MODELCLASS( @@ -1160,12 +1401,19 @@ class ReforceXY(BaseReinforcementLearningModel): time_spent = time.time() - start_time self.dd.update_metric_tracker("fit_time", time_spent, dk.pair) - model_filename = dk.model_filename if dk.model_filename else "best" - model_filepath = Path(dk.data_path / f"{model_filename}_model.zip") - if model_filepath.is_file(): + model_filepath = Path(dk.data_path) / "best_model.zip" + current_best = np.isfinite(getattr(self.eval_callback, "best_mean_reward", np.nan)) + if current_best and model_filepath.is_file(): logger.info("Model [%s]: found best model at %s", dk.pair, model_filepath) try: - best_model = self.MODELCLASS.load(dk.data_path / f"{model_filename}_model") + best_model = self.MODELCLASS.load(model_filepath) + # SB3 archives exclude replay buffers; restore the final buffer so + # the next cached retrain keeps the experience accumulated this fit. + if ( + hasattr(model, "replay_buffer") + and getattr(best_model, "replay_buffer", None) is not None + ): + best_model.replay_buffer = model.replay_buffer return best_model except Exception as e: logger.error( @@ -1176,13 +1424,74 @@ class ReforceXY(BaseReinforcementLearningModel): ) logger.warning( - "Model [%s]: best model not found at %s, using final model", + "Model [%s]: no usable current best checkpoint at %s, using final model", dk.pair, model_filepath, ) return model + def fit_live_predictions(self, dk: FreqaiDataKitchen, pair: str) -> None: + """Compute optional action statistics from prior prediction observations.""" + fit_live_predictions_candles = self.freqai_info.get("fit_live_predictions_candles", 0) + if not fit_live_predictions_candles: + return + + warmed_up = True + history = self.dd.historic_predictions[pair] + if self.live: + history = _dedupe_historic_predictions_on_date_pred(history) + if not hasattr(self, "_prediction_session_cutoffs"): + self._prediction_session_cutoffs: dict[str, pd.Timestamp] = {} + if pair not in self._prediction_session_cutoffs: + initial_dates = pd.to_datetime( + self.dd.model_return_values[pair]["date_pred"], + utc=True, + errors="coerce", + format="mixed", + ) + self._prediction_session_cutoffs[pair] = initial_dates.max() + cutoff = self._prediction_session_cutoffs[pair] + eligible = ( + _produced_prediction_mask(history) & history["date_pred"].gt(cutoff).to_numpy() + ) + history = history.loc[eligible] + remaining = fit_live_predictions_candles - len(history) + warmed_up = remaining <= 0 + if not warmed_up: + logger.warning( + f"[{pair}] Fit live predictions not warmed up: {remaining} produced observations until warmup completion" + ) + pred_df = history.tail(fit_live_predictions_candles).reset_index(drop=True) + + dk.data["labels_mean"], dk.data["labels_std"] = {}, {} + for label_col in dk.label_list + dk.unique_class_list: + raw_label = pred_df.get(label_col) + if raw_label is None: + continue + # Downtime filling can leave numeric predictions in object-typed columns. + pred_label = pd.to_numeric(raw_label, errors="coerce") + if raw_label.dtype == object and pred_label.isna().all(): + continue + if not warmed_up: + f = [0.0, 0.0] + else: + values = pred_label.to_numpy(dtype=float, na_value=np.nan) + values = values[np.isfinite(values)] + if values.size == 0: + f = (0.0, 0.0) + else: + sample_mean = float(np.mean(values)) + sample_std = float(np.std(values, ddof=0)) + f = ( + sample_mean if np.isfinite(sample_mean) else 0.0, + sample_std if np.isfinite(sample_std) else 0.0, + ) + dk.data["labels_mean"][label_col], dk.data["labels_std"][label_col] = ( + f[0], + f[1], + ) + def rl_model_predict( self, dataframe: DataFrame, dk: FreqaiDataKitchen, model: Any ) -> DataFrame: @@ -1648,9 +1957,16 @@ class ReforceXY(BaseReinforcementLearningModel): max(min_resource, ReforceXY._ceil_to_multiple(total_timesteps, rollout)), ) - def optimize(self, dk: FreqaiDataKitchen, total_timesteps: int) -> dict[str, Any] | None: + def optimize( + self, + dk: FreqaiDataKitchen, + total_timesteps: int, + prices_train: DataFrame, + prices_test: DataFrame, + ) -> dict[str, Any] | None: """ Runs hyperparameter optimization using Optuna and returns the best hyperparameters found merged with the user defined parameters + Only studies and best params with the current objective identity are reused. """ identifier = self.freqai_info.get("identifier", "no_id_provided") study_name = f"{identifier}-{dk.pair}" @@ -1693,6 +2009,25 @@ class ReforceXY(BaseReinforcementLearningModel): direction = StudyDirection.MAXIMIZE load_if_exists = not continuous and not pair_purge_triggered + if load_if_exists: + try: + existing_study_id = storage.get_study_id_from_name(study_name) + except KeyError: + pass + else: + existing_identity = storage.get_study_user_attrs(existing_study_id).get( + "objective_identity" + ) + if existing_identity != ReforceXY._OPTUNA_OBJECTIVE_IDENTITY: + logger.warning( + "Hyperopt [%s]: objective identity %r incompatible with %r; resetting study", + study_name, + existing_identity, + ReforceXY._OPTUNA_OBJECTIVE_IDENTITY, + ) + ReforceXY.delete_study(study_name, storage) + # Fail closed if deletion failed rather than reload incompatible trials. + load_if_exists = False study: Study = create_study( study_name=study_name, sampler=self.create_sampler(), @@ -1701,6 +2036,8 @@ class ReforceXY(BaseReinforcementLearningModel): storage=storage, load_if_exists=load_if_exists, ) + if study.user_attrs.get("objective_identity") != ReforceXY._OPTUNA_OBJECTIVE_IDENTITY: + study.set_user_attr("objective_identity", ReforceXY._OPTUNA_OBJECTIVE_IDENTITY) logger.info( "Hyperopt [%s]: study created (direction=%s, n_trials=%s, timeout=%s, continuous=%s, load_if_exists=%s)", study_name, @@ -1729,7 +2066,7 @@ class ReforceXY(BaseReinforcementLearningModel): start_time = time.time() try: study.optimize( - lambda trial: self.objective(trial, dk, total_timesteps), + lambda trial: self.objective(trial, dk, total_timesteps, prices_train, prices_test), n_trials=self.optuna_n_trials, timeout=( hours_to_seconds(self.optuna_timeout_hours) @@ -1905,7 +2242,7 @@ class ReforceXY(BaseReinforcementLearningModel): def save_best_trial_params(self, best_trial_params: dict[str, Any], pair: str) -> None: """ - Save the best trial hyperparameters found during hyperparameter optimization + Save the best trial hyperparameters with the current objective identity. """ best_trial_params_path = self._best_trial_params_path(pair) logger.info("Hyperopt [%s]: saving best params to %s", pair, best_trial_params_path) @@ -1958,7 +2295,14 @@ class ReforceXY(BaseReinforcementLearningModel): write_file.fileno(), stat.S_IMODE(existing_metadata.st_mode), ) - json.dump(best_trial_params, write_file, indent=4) + json.dump( + { + "objective_identity": ReforceXY._OPTUNA_OBJECTIVE_IDENTITY, + "params": best_trial_params, + }, + write_file, + indent=4, + ) write_file.flush() os.fsync(write_file.fileno()) temporary_path.replace(best_trial_params_path) @@ -1987,7 +2331,7 @@ class ReforceXY(BaseReinforcementLearningModel): def load_best_trial_params(self, pair: str) -> dict[str, Any] | None: """ - Load the best trial hyperparameters found and saved during hyperparameter optimization + Load saved best trial hyperparameters only for the current objective identity. """ best_trial_params_path = self._best_trial_params_path(pair) if not best_trial_params_path.parent.is_dir(): @@ -2025,26 +2369,32 @@ class ReforceXY(BaseReinforcementLearningModel): if quarantined is None: raise return None - return best_trial_params + if ( + not isinstance(best_trial_params, dict) + or best_trial_params.get("objective_identity") != ReforceXY._OPTUNA_OBJECTIVE_IDENTITY + or not isinstance(best_trial_params.get("params"), dict) + ): + logger.warning( + "Hyperopt [%s]: ignoring best params with missing or incompatible objective identity " + "(expected %r) or invalid params payload", + pair, + ReforceXY._OPTUNA_OBJECTIVE_IDENTITY, + ) + return None + return best_trial_params["params"] def _get_train_and_eval_environments( self, dk: FreqaiDataKitchen, - train_df: DataFrame | None = None, - test_df: DataFrame | None = None, - prices_train: DataFrame | None = None, - prices_test: DataFrame | None = None, + train_df: DataFrame, + test_df: DataFrame, + prices_train: DataFrame, + prices_test: DataFrame, seed: int | None = None, env_info: dict[str, Any] | None = None, trial: Trial | None = None, model_params: dict[str, Any] | None = None, ) -> tuple[VecEnv, VecEnv]: - if train_df is None or test_df is None or prices_train is None or prices_test is None: - train_df = dk.data_dictionary["train_features"] - test_df = dk.data_dictionary["test_features"] - prices_train, prices_test = self.build_ohlc_price_dataframes( - dk.data_dictionary, dk.pair, dk - ) seed: int = self.get_model_params().get("seed", 42) if seed is None else seed if trial is not None: seed += trial.number @@ -2115,7 +2465,14 @@ class ReforceXY(BaseReinforcementLearningModel): f"Hyperopt [{trial.study.study_name}]: model type '{self.model_type}' not supported" ) - def objective(self, trial: Trial, dk: FreqaiDataKitchen, total_timesteps: int) -> float: + def objective( + self, + trial: Trial, + dk: FreqaiDataKitchen, + total_timesteps: int, + prices_train: DataFrame, + prices_test: DataFrame, + ) -> float: """ Objective function for Optuna trials hyperparameter optimization """ @@ -2184,7 +2541,13 @@ class ReforceXY(BaseReinforcementLearningModel): tensorboard_log_path = None train_env, eval_env = self._get_train_and_eval_environments( - dk, trial=trial, model_params=params + dk, + train_df=dk.data_dictionary["train_features"], + test_df=dk.data_dictionary["test_features"], + prices_train=prices_train, + prices_test=prices_test, + trial=trial, + model_params=params, ) model = self.MODELCLASS( -- 2.53.0