From c07d8b5a96df01d9c946067d5996a9aeec744243 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Wed, 23 Sep 2026 15:10:53 +0200 Subject: [PATCH] fix(freqai): preserve restart state correctly Squash merge PR #274 after all quality and CodeQL checks passed. --- README.md | 47 ++- ReforceXY/.basedpyright/diagnostics.json | 368 +++++++++--------- ReforceXY/tests/test_training_observations.py | 286 +++++++++++++- ReforceXY/user_data/freqaimodels/ReforceXY.py | 277 ++++++++----- quickadapter/.basedpyright/diagnostics.json | 296 +++++++------- .../freqaimodels/QuickAdapterRegressorV3.py | 289 +++++++++++--- .../user_data/strategies/LabelTransformer.py | 6 - .../user_data/strategies/QuickAdapterV3.py | 2 +- 8 files changed, 1056 insertions(+), 515 deletions(-) diff --git a/README.md b/README.md index ab08b9f..35d248f 100644 --- a/README.md +++ b/README.md @@ -170,11 +170,25 @@ specific; equally specific patterns follow declaration order, so the later one wins. In live and dry-run modes, each pair requires -`freqai.fit_live_predictions_candles` model predictions after session startup -before adaptive thresholds become available. Restarting requires a new warmup. -Downtime and expired-model rows are excluded; genuine zero and outlier-rejected -predictions count. Predictions align to candle dates; candles without -predictions have `do_predict=0` and downtime zeros display but never calibrate. +`freqai.fit_live_predictions_candles` real model predictions before adaptive +thresholds become available. The Nth observation first affects the next +prediction update, not the candle that produced it. FreqAI bootstrap +predictions made from the initial training frame do not count. Warmup progress +from persisted real predictions is restored after a restart. FreqAI saves +prediction history after training attempts and on clean shutdown. After an +abrupt stop or hard reboot, predictions since the last history save may be +lost, so those observations must accumulate again. Legacy rows missing +provenance, including rows in partly marked histories, count only when their +nonzero, nonexpired prediction status distinguishes them from bootstrap; +ambiguous rejected rows and explicit false markers do not count. + +A pair starts a new warmup when the time since its last observation, or a gap +within its observations, is greater than +`fit_live_predictions_candles × timeframe`. An observation exactly one horizon +old remains eligible. Downtime and expired-model rows are excluded; genuine +zero and outlier-rejected predictions count. Predictions align to candle dates; +candles without predictions have `do_predict=0` and downtime zeros display but +never calibrate. ### Backtest evaluation protocol @@ -348,7 +362,8 @@ The documented list of model tunables is at the top of the Continual learning trains an independent copy of the deployed policy with its fitted feature pipeline. DQN/QRDQN deployments each persist their replay buffer; -missing or incompatible replay data prevents continuation. Reset trained models +it is loaded only when continual training starts. Missing or incompatible replay +data prevents continuation but does not prevent inference. Reset trained models or use a new `freqai.identifier` to migrate incompatible artifacts, including deployments without the chronological training marker. Training disables `shuffle_after_split`. HPO studies and saved best parameters are reused only @@ -356,9 +371,20 @@ when their objective identity matches. ### Live inference -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. +Optional `fit_live_predictions_candles` statistics use the latest persisted real +predictions per pair, excluding FreqAI bootstrap rows. Available observations +are used before a full window accumulates and survive restarts. FreqAI returns +the initial strategy frame before calculating live statistics; restored +statistics appear on the next prediction update. Legacy rows missing provenance, +including rows in partly marked histories, can count when their nonzero, +nonexpired prediction status distinguishes them from bootstrap; zero-status rows +remain excluded because bootstrap and rejected predictions cannot be +distinguished. Explicit false markers remain excluded. +On duplicate candle dates, a provable prediction takes precedence over an +ambiguous close-bearing legacy row during history restoration. +Rows with an invalid `date_pred` are discarded with a per-pair warning and the +discarded-row count; valid duplicates retain the same precedence. +See the model docstrings for continuation, HPO and statistics details. With `hold_potential_enabled=true`, ReforceXY enables `add_state_info` before constructing environments so training and inference use the same observations. @@ -382,7 +408,8 @@ numeric value. Environment prices remain raw regardless of current evaluation run when available. DQN/QRDQN HPO rejects warmup budgets that leave no gradient update and trials that finish without learning. A zero-sized holdout remains supported when HPO is disabled, including with raw OHLC feature -removal. +removal. An interrupted ReforceXY fit is logged and still selects the best +usable checkpoint when available, falling back to the final model otherwise. ### Reward and portfolio accounting diff --git a/ReforceXY/.basedpyright/diagnostics.json b/ReforceXY/.basedpyright/diagnostics.json index 85cab2b..a305e4b 100644 --- a/ReforceXY/.basedpyright/diagnostics.json +++ b/ReforceXY/.basedpyright/diagnostics.json @@ -373,923 +373,923 @@ }, { "endCharacter": 22, - "endLine": 914, + "endLine": 1010, "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": 914 + "startLine": 1010 }, { "endCharacter": 37, - "endLine": 914, + "endLine": 1010, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "Cannot assign to attribute \"eval_env\" for class \"ReforceXY*\"\n  Expression of type \"VecEnv | None\" cannot be assigned to attribute \"eval_env\" of class \"ReforceXY\"\n    Type \"VecEnv | None\" is not assignable to type \"VecMonitor | SubprocVecEnv | Env[Unknown, Unknown]\"\n      Type \"VecEnv\" is not assignable to type \"VecMonitor | SubprocVecEnv | Env[Unknown, Unknown]\"\n        \"VecEnv\" is not assignable to \"VecMonitor\"\n        \"VecEnv\" is not assignable to \"SubprocVecEnv\"\n        \"VecEnv\" is not assignable to \"Env[Unknown, Unknown]\"", "rule": "reportAttributeAccessIssue", "severity": "error", "startCharacter": 29, - "startLine": 914 + "startLine": 1010 }, { "endCharacter": 24, - "endLine": 1173, + "endLine": 1269, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "Argument of type \"VecEnv\" cannot be assigned to parameter \"eval_env\" of type \"BaseEnvironment\" in function \"__init__\"\n  \"VecEnv\" is not assignable to \"BaseEnvironment\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 16, - "startLine": 1173 + "startLine": 1269 }, { "endCharacter": 38, - "endLine": 1378, + "endLine": 1477, "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": 1378 + "startLine": 1477 }, { "endCharacter": 36, - "endLine": 1382, + "endLine": 1481, "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": 1382 + "startLine": 1481 }, { "endCharacter": 80, - "endLine": 1385, + "endLine": 1484, "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": 1385 + "startLine": 1484 }, { "endCharacter": 78, - "endLine": 1386, + "endLine": 1485, "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": 1386 + "startLine": 1485 }, { "endCharacter": 80, - "endLine": 1387, + "endLine": 1486, "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": 1387 + "startLine": 1486 }, { "endCharacter": 56, - "endLine": 1472, + "endLine": 1571, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "Argument of type \"VecMonitor | SubprocVecEnv | Env[Unknown, Unknown]\" cannot be assigned to parameter \"eval_env\" of type \"VecEnv | None\" in function \"get_callbacks\"\n  Type \"VecMonitor | SubprocVecEnv | Env[Unknown, Unknown]\" is not assignable to type \"VecEnv | None\"\n    Type \"Env[Unknown, Unknown]\" is not assignable to type \"VecEnv | None\"\n      \"Env[Unknown, Unknown]\" is not assignable to \"VecEnv\"\n      \"Env[Unknown, Unknown]\" is not assignable to \"None\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 43, - "startLine": 1472 + "startLine": 1571 }, { "endCharacter": 84, - "endLine": 1492, + "endLine": 1591, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "Argument of type \"float | list[float]\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"float | list[float]\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"list[float]\" is not assignable to type \"ConvertibleToFloat\"\n      \"list[float]\" is not assignable to \"str\"\n      \"list[float]\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"list[float]\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"list[float]\" is incompatible with protocol \"SupportsIndex\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 67, - "startLine": 1492 + "startLine": 1591 }, { "endCharacter": 84, - "endLine": 1492, + "endLine": 1591, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "Argument of type \"float | list[float]\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"float | list[float]\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"list[float]\" is not assignable to type \"ConvertibleToFloat\"\n      \"list[float]\" is not assignable to \"str\"\n      \"list[float]\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"list[float]\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"list[float]\" is incompatible with protocol \"SupportsIndex\"\n ...", "rule": "reportArgumentType", "severity": "error", "startCharacter": 67, - "startLine": 1492 + "startLine": 1591 }, { "endCharacter": 44, - "endLine": 2586, + "endLine": 2665, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "\"best_trial_params\" is possibly unbound", "rule": "reportPossiblyUnboundVariable", "severity": "error", "startCharacter": 27, - "startLine": 2586 + "startLine": 2665 }, { "endCharacter": 12, - "endLine": 2606, + "endLine": 2685, "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": 2606 + "startLine": 2685 }, { "endCharacter": 16, - "endLine": 2607, + "endLine": 2686, "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": 2607 + "startLine": 2686 }, { "endCharacter": 51, - "endLine": 2650, + "endLine": 2729, "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": 42, - "startLine": 2650 + "startLine": 2729 }, { "endCharacter": 53, - "endLine": 2656, + "endLine": 2735, "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": 45, - "startLine": 2656 + "startLine": 2735 }, { "endCharacter": 22, - "endLine": 2719, + "endLine": 2798, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "Operator \"*\" not supported for \"None\"", "rule": "reportOptionalOperand", "severity": "error", "startCharacter": 15, - "startLine": 2719 + "startLine": 2798 }, { "endCharacter": 96, - "endLine": 2721, + "endLine": 2800, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "Operator \"*\" not supported for \"None\"", "rule": "reportOptionalOperand", "severity": "error", "startCharacter": 89, - "startLine": 2721 + "startLine": 2800 }, { "endCharacter": 23, - "endLine": 2724, + "endLine": 2803, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "Operator \"*\" not supported for \"None\"", "rule": "reportOptionalOperand", "severity": "error", "startCharacter": 16, - "startLine": 2724 + "startLine": 2803 }, { "endCharacter": 98, - "endLine": 2726, + "endLine": 2805, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "Operator \"*\" not supported for \"None\"", "rule": "reportOptionalOperand", "severity": "error", "startCharacter": 91, - "startLine": 2726 + "startLine": 2805 }, { "endCharacter": 44, - "endLine": 2738, + "endLine": 2817, "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": 2738 + "startLine": 2817 }, { "endCharacter": 133, - "endLine": 2740, + "endLine": 2819, "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": 2740 + "startLine": 2819 }, { "endCharacter": 30, - "endLine": 2743, + "endLine": 2822, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "Operator \">\" not supported for \"None\"", "rule": "reportOptionalOperand", "severity": "error", "startCharacter": 15, - "startLine": 2743 + "startLine": 2822 }, { "endCharacter": 54, - "endLine": 2808, + "endLine": 2887, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "\"is_pruned\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 45, - "startLine": 2808 + "startLine": 2887 }, { "endCharacter": 67, - "endLine": 2811, + "endLine": 2890, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "\"use_masking\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 56, - "startLine": 2811 + "startLine": 2890 }, { "endCharacter": 60, - "endLine": 2819, + "endLine": 2898, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "\"update_best_reward\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 42, - "startLine": 2819 + "startLine": 2898 }, { "endCharacter": 84, - "endLine": 2819, + "endLine": 2898, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "Argument of type \"float | list[float]\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"float | list[float]\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"list[float]\" is not assignable to type \"ConvertibleToFloat\"\n      \"list[float]\" is not assignable to \"str\"\n      \"list[float]\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"list[float]\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"list[float]\" is incompatible with protocol \"SupportsIndex\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 67, - "startLine": 2819 + "startLine": 2898 }, { "endCharacter": 84, - "endLine": 2819, + "endLine": 2898, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "Argument of type \"float | list[float]\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n  Type \"float | list[float]\" is not assignable to type \"ConvertibleToFloat\"\n    Type \"list[float]\" is not assignable to type \"ConvertibleToFloat\"\n      \"list[float]\" is not assignable to \"str\"\n      \"list[float]\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"list[float]\" is incompatible with protocol \"SupportsFloat\"\n        \"__float__\" is not present\n      \"list[float]\" is incompatible with protocol \"SupportsIndex\"\n ...", "rule": "reportArgumentType", "severity": "error", "startCharacter": 67, - "startLine": 2819 + "startLine": 2898 }, { "endCharacter": 46, - "endLine": 2873, + "endLine": 2952, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "\"is_pruned\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 37, - "startLine": 2873 + "startLine": 2952 }, { "endCharacter": 57, - "endLine": 2876, + "endLine": 2955, "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": 2876 + "startLine": 2955 }, { "endCharacter": 37, - "endLine": 2886, + "endLine": 2965, "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": 2886 + "startLine": 2965 }, { "endCharacter": 36, - "endLine": 2891, + "endLine": 2970, "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": 2891 + "startLine": 2970 }, { "endCharacter": 7, - "endLine": 2936, + "endLine": 3015, "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": 2936 + "startLine": 3015 }, { "endCharacter": 36, - "endLine": 2948, + "endLine": 3027, "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": 2948 + "startLine": 3027 }, { "endCharacter": 13, - "endLine": 3540, + "endLine": 3619, "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": 3540 + "startLine": 3619 }, { "endCharacter": 26, - "endLine": 3561, + "endLine": 3640, "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": 3561 + "startLine": 3640 }, { "endCharacter": 55, - "endLine": 3869, + "endLine": 3948, "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": 3869 + "startLine": 3948 }, { "endCharacter": 55, - "endLine": 3869, + "endLine": 3948, "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": 3869 + "startLine": 3948 }, { "endCharacter": 59, - "endLine": 3893, + "endLine": 3972, "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": 3893 + "startLine": 3972 }, { "endCharacter": 59, - "endLine": 3893, + "endLine": 3972, "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": 3893 + "startLine": 3972 }, { "endCharacter": 24, - "endLine": 3922, + "endLine": 4001, "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": 3922 + "startLine": 4001 }, { "endCharacter": 12, - "endLine": 4024, + "endLine": 4103, "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": 4024 + "startLine": 4103 }, { "endCharacter": 20, - "endLine": 4207, + "endLine": 4290, "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": 4207 + "startLine": 4290 }, { "endCharacter": 40, - "endLine": 4356, + "endLine": 4439, "file": "ReforceXY/user_data/freqaimodels/ReforceXY.py", "message": "\"Figure\" is not exported from module \"matplotlib.pyplot\"", "rule": "reportPrivateImportUsage", "severity": "error", "startCharacter": 34, - "startLine": 4356 + "startLine": 4439 }, { "endCharacter": 54, - "endLine": 4859, + "endLine": 4942, "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": 4859 + "startLine": 4942 }, { "endCharacter": 54, - "endLine": 4859, + "endLine": 4942, "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": 4859 + "startLine": 4942 }, { "endCharacter": 50, - "endLine": 4871, + "endLine": 4954, "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": 4871 + "startLine": 4954 }, { "endCharacter": 72, - "endLine": 4882, + "endLine": 4965, "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": 4882 + "startLine": 4965 }, { "endCharacter": 72, - "endLine": 4882, + "endLine": 4965, "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": 4882 + "startLine": 4965 }, { "endCharacter": 73, - "endLine": 4891, + "endLine": 4974, "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": 4891 + "startLine": 4974 }, { "endCharacter": 73, - "endLine": 4891, + "endLine": 4974, "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": 4891 + "startLine": 4974 }, { "endCharacter": 58, - "endLine": 4900, + "endLine": 4983, "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": 4900 + "startLine": 4983 }, { "endCharacter": 58, - "endLine": 4900, + "endLine": 4983, "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": 4900 + "startLine": 4983 }, { "endCharacter": 50, - "endLine": 5131, + "endLine": 5214, "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": 5131 + "startLine": 5214 }, { "endCharacter": 20, - "endLine": 5308, + "endLine": 5391, "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": 5308 + "startLine": 5391 }, { "endCharacter": 20, - "endLine": 5308, + "endLine": 5391, "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": 5308 + "startLine": 5391 }, { "endCharacter": 59, - "endLine": 5313, + "endLine": 5396, "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": 5313 + "startLine": 5396 }, { "endCharacter": 59, - "endLine": 5313, + "endLine": 5396, "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": 5313 + "startLine": 5396 }, { "endCharacter": 65, - "endLine": 5314, + "endLine": 5397, "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": 5314 + "startLine": 5397 }, { "endCharacter": 65, - "endLine": 5314, + "endLine": 5397, "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": 5314 + "startLine": 5397 }, { "endCharacter": 57, - "endLine": 5315, + "endLine": 5398, "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": 5315 + "startLine": 5398 }, { "endCharacter": 57, - "endLine": 5315, + "endLine": 5398, "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": 5315 + "startLine": 5398 }, { "endCharacter": 63, - "endLine": 5317, + "endLine": 5400, "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": 5317 + "startLine": 5400 }, { "endCharacter": 63, - "endLine": 5317, + "endLine": 5400, "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": 5317 + "startLine": 5400 }, { "endCharacter": 61, - "endLine": 5319, + "endLine": 5402, "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": 5319 + "startLine": 5402 }, { "endCharacter": 61, - "endLine": 5319, + "endLine": 5402, "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": 5319 + "startLine": 5402 }, { "endCharacter": 67, - "endLine": 5320, + "endLine": 5403, "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": 5320 + "startLine": 5403 }, { "endCharacter": 67, - "endLine": 5320, + "endLine": 5403, "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": 5320 + "startLine": 5403 }, { "endCharacter": 73, - "endLine": 5321, + "endLine": 5404, "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": 5321 + "startLine": 5404 }, { "endCharacter": 73, - "endLine": 5321, + "endLine": 5404, "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": 5321 + "startLine": 5404 }, { "endCharacter": 61, - "endLine": 5322, + "endLine": 5405, "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": 5322 + "startLine": 5405 }, { "endCharacter": 61, - "endLine": 5322, + "endLine": 5405, "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": 5322 + "startLine": 5405 }, { "endCharacter": 89, - "endLine": 5329, + "endLine": 5412, "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": 5329 + "startLine": 5412 }, { "endCharacter": 89, - "endLine": 5329, + "endLine": 5412, "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": 5329 + "startLine": 5412 }, { "endCharacter": 83, - "endLine": 5330, + "endLine": 5413, "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": 5330 + "startLine": 5413 }, { "endCharacter": 83, - "endLine": 5330, + "endLine": 5413, "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": 5330 + "startLine": 5413 }, { "endCharacter": 57, - "endLine": 5355, + "endLine": 5438, "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": 5355 + "startLine": 5438 }, { "endCharacter": 57, - "endLine": 5355, + "endLine": 5438, "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": 5355 + "startLine": 5438 }, { "endCharacter": 65, - "endLine": 5356, + "endLine": 5439, "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": 5356 + "startLine": 5439 }, { "endCharacter": 65, - "endLine": 5356, + "endLine": 5439, "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": 5356 + "startLine": 5439 }, { "endCharacter": 67, - "endLine": 5358, + "endLine": 5441, "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": 5358 + "startLine": 5441 }, { "endCharacter": 67, - "endLine": 5358, + "endLine": 5441, "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": 5358 + "startLine": 5441 }, { "endCharacter": 87, - "endLine": 5361, + "endLine": 5444, "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": 5361 + "startLine": 5444 }, { "endCharacter": 87, - "endLine": 5361, + "endLine": 5444, "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": 5361 + "startLine": 5444 }, { "endCharacter": 93, - "endLine": 5362, + "endLine": 5445, "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": 5362 + "startLine": 5445 }, { "endCharacter": 93, - "endLine": 5362, + "endLine": 5445, "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": 5362 + "startLine": 5445 }, { "endCharacter": 89, - "endLine": 5363, + "endLine": 5446, "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": 5363 + "startLine": 5446 }, { "endCharacter": 89, - "endLine": 5363, + "endLine": 5446, "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": 5363 + "startLine": 5446 }, { "endCharacter": 89, - "endLine": 5364, + "endLine": 5447, "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": 5364 + "startLine": 5447 }, { "endCharacter": 89, - "endLine": 5364, + "endLine": 5447, "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": 5364 + "startLine": 5447 }, { "endCharacter": 75, - "endLine": 5365, + "endLine": 5448, "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": 5365 + "startLine": 5448 }, { "endCharacter": 75, - "endLine": 5365, + "endLine": 5448, "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": 5365 + "startLine": 5448 }, { "endCharacter": 33, diff --git a/ReforceXY/tests/test_training_observations.py b/ReforceXY/tests/test_training_observations.py index 9bbd436..6fa931a 100644 --- a/ReforceXY/tests/test_training_observations.py +++ b/ReforceXY/tests/test_training_observations.py @@ -8,6 +8,8 @@ from unittest import mock import numpy as np import pandas as pd +from freqtrade.exceptions import DependencyException +from freqtrade.freqai.data_drawer import FreqaiDataDrawer from freqtrade.freqai.data_kitchen import FreqaiDataKitchen from optuna import TrialPruned, create_study @@ -60,27 +62,32 @@ class TrainingObservationsTest(unittest.TestCase): replay = trained.replay_buffer.observations.copy() size = trained.replay_buffer.size() self.assertGreater(size, 0) - model.dd.model_dictionary.clear() + model.dd.model_dictionary[dk.pair] = trained model.dd.meta_data_dictionary.clear() + direct_clone, _ = model._resolve_deployment_state(dk, dk.pair) + self.assertEqual(direct_clone.replay_buffer.size(), size) + np.testing.assert_array_equal(direct_clone.replay_buffer.observations, replay) + direct_clone.replay_buffer.observations.flat[0] += 42 + np.testing.assert_array_equal(trained.replay_buffer.observations, replay) + model.dd.model_dictionary.clear() restored = model.dd.load_data(dk.pair, dk) - self.assertEqual(restored.replay_buffer.size(), size) - np.testing.assert_array_equal(restored.replay_buffer.observations, replay) + self.assertEqual(restored.replay_buffer.size(), 0) clone, _ = model._resolve_deployment_state(dk, dk.pair) self.assertEqual(clone.replay_buffer.size(), size) - self.assertIsNot(clone.replay_buffer, restored.replay_buffer) - clone.replay_buffer.observations.flat[0] += 123.0 - self.assertNotEqual( - clone.replay_buffer.observations.flat[0], - restored.replay_buffer.observations.flat[0], - ) - np.testing.assert_array_equal(restored.replay_buffer.observations, replay) + np.testing.assert_array_equal(clone.replay_buffer.observations, replay) + clone.replay_buffer.observations.flat[0] += 42 + np.testing.assert_array_equal(trained.replay_buffer.observations, replay) + self.assertEqual(restored.replay_buffer.size(), 0) self.assertIs(model.dd.load_data(dk.pair, dk), restored) model.dd.model_dictionary.clear() (dk.data_path / dk.data["reforcexy_replay"]).unlink() - for _ in range(2): - with self.assertRaises(FileNotFoundError): - model.dd.load_data(dk.pair, dk) - self.assertNotIn(dk.pair, model.dd.model_dictionary) + inference_model = model.dd.load_data(dk.pair, dk) + self.assertIsNotNone(inference_model) + model.continual_learning = False + self.assertIsNone(model._resolve_deployment_state(dk, dk.pair)) + model.continual_learning = True + with self.assertRaises(DependencyException): + model._resolve_deployment_state(dk, dk.pair) params = model.get_model_params() trial = create_study(direction="maximize").ask() for starts in (64, 50000): @@ -113,6 +120,257 @@ class TrainingObservationsTest(unittest.TestCase): ) self.assertTrue(np.isfinite(score)) + def test_provenance_does_not_change_other_drawers(self): + with tempfile.TemporaryDirectory() as temp: + config = model_config(temp) + model = ReforceXY(config=config) + self.addCleanup(model.close_envs) + drawer = FreqaiDataDrawer(Path(temp), config) + pair = "BTC/USDT" + date = pd.Timestamp("2026-01-01", tz="UTC") + drawer.historic_predictions[pair] = pd.DataFrame( + { + "date_pred": [date], + "&-action": [1.0], + "do_predict": [1], + "close_price": [100.0], + } + ) + candles = pd.DataFrame({"date": [date]}) + drawer.set_initial_return_values(pair, pd.DataFrame({"&-action": [99.0]}), candles) + self.assertNotIn("_freqai_strategies_produced", drawer.historic_predictions[pair]) + self.assertNotIn( + "_freqai_strategies_produced", + drawer.attach_return_values_to_return_dataframe(pair, candles), + ) + + def test_native_bootstrap_append_and_restart_provenance(self): + pair = "BTC/USDT" + marker = "_freqai_strategies_produced" + with tempfile.TemporaryDirectory() as temp: + config = model_config(temp) + config["freqai"]["fit_live_predictions_candles"] = 3 + model = ReforceXY(config=config) + model.live = True + self.addCleanup(model.close_envs) + dates = pd.date_range("2026-01-01", periods=4, freq="5min", tz="UTC") + strat_df = pd.DataFrame( + {"date": dates, "high": [101.0] * 4, "low": [99.0] * 4, "close": [100.0] * 4} + ) + dk = SimpleNamespace( + data={"extra_returns_per_train": {}}, label_list=["&-action"], unique_class_list=[] + ) + bootstrap = pd.DataFrame({"&-action": [21.0, 22.0, 23.0, 24.0]}) + model.set_initial_historic_predictions(bootstrap, dk, pair, strat_df) + model.dd.set_initial_return_values(pair, bootstrap, strat_df) + self.assertEqual(model.dd.historic_predictions[pair][marker].tolist(), [False] * 4) + model.fit_live_predictions(dk, pair) + self.assertEqual(dk.data["labels_mean"]["&-action"], 0.0) + returned = model.dd.attach_return_values_to_return_dataframe(pair, strat_df) + self.assertNotIn(marker, returned) + + # Native same-candle append overwrites a bootstrap candle. A rejected + # prediction is nevertheless produced and must enter the statistics. + model.dd.append_model_predictions( + pair, pd.DataFrame({"&-action": [3.0]}), np.array([0]), dk, strat_df + ) + model.fit_live_predictions(dk, pair) + self.assertEqual(dk.data["labels_mean"]["&-action"], 3.0) + self.assertNotIn( + marker, model.dd.attach_return_values_to_return_dataframe(pair, strat_df) + ) + + future = pd.date_range(dates[-1] + pd.Timedelta(minutes=5), periods=3, freq="5min") + resumed = pd.concat( + [ + strat_df, + pd.DataFrame( + { + "date": future, + "high": [101.0] * 3, + "low": [99.0] * 3, + "close": [100.0] * 3, + } + ), + ], + ignore_index=True, + ) + model.dd.append_model_predictions( + pair, pd.DataFrame({"&-action": [9.0]}), np.array([1]), dk, resumed + ) + history = model.dd.historic_predictions[pair] + self.assertEqual(history[marker].tolist(), [False] * 3 + [True, False, False, True]) + model.fit_live_predictions(dk, pair) + self.assertEqual(dk.data["labels_mean"]["&-action"], 6.0) + self.assertNotIn( + marker, model.dd.attach_return_values_to_return_dataframe(pair, resumed) + ) + model.dd.append_model_predictions( + pair, pd.DataFrame({"&-action": [99.0]}), np.array([2]), dk, resumed + ) + model.fit_live_predictions(dk, pair) + self.assertEqual(dk.data["labels_mean"]["&-action"], 3.0) + model.dd.append_model_predictions( + pair, pd.DataFrame({"&-action": [9.0]}), np.array([1]), dk, resumed + ) + self.assertEqual(len(model.dd.historic_predictions[pair]), len(resumed)) + model.fit_live_predictions(dk, pair) + self.assertEqual(dk.data["labels_mean"]["&-action"], 6.0) + + model.dd.save_historic_predictions_to_disk() + restored = ReforceXY(config=config) + restored.live = True + self.addCleanup(restored.close_envs) + self.assertTrue(restored.dd.load_historic_predictions_from_disk()) + self.assertEqual( + restored.dd.historic_predictions[pair][marker].tolist(), history[marker].tolist() + ) + restored.fit_live_predictions(dk, pair) + self.assertEqual(dk.data["labels_mean"]["&-action"], 6.0) + self.assertNotIn( + marker, restored.dd.attach_return_values_to_return_dataframe(pair, resumed) + ) + + def test_partially_marked_history_restores_provable_observations(self): + pair = "BTC/USDT" + marker = "_freqai_strategies_produced" + with tempfile.TemporaryDirectory() as temp: + config = model_config(temp) + config["freqai"]["fit_live_predictions_candles"] = 4 + source = ReforceXY(config=config) + self.addCleanup(source.close_envs) + dates = pd.date_range("2026-01-01", periods=4, freq="5min", tz="UTC") + source.dd.historic_predictions[pair] = pd.DataFrame( + { + "date_pred": dates, + "&-action": [7.0, 9.0, 99.0, 1000.0], + "&-action_mean": [0.0] * 4, + "&-action_std": [0.0] * 4, + "close_price": [100.0] * 4, + "high_price": [101.0] * 4, + "low_price": [99.0] * 4, + "do_predict": [1, 0, 2, 1], + marker: [np.nan, 1.0, np.nan, 0.0], + } + ) + source.dd.save_historic_predictions_to_disk() + + restarted = ReforceXY(config=config) + restarted.live = True + self.addCleanup(restarted.close_envs) + dk = SimpleNamespace( + data={"extra_returns_per_train": {}}, + label_list=["&-action"], + unique_class_list=[], + return_dataframe=pd.DataFrame(), + ) + restarted.predict = lambda frame, kitchen, **kwargs: ( + pd.DataFrame({"&-action": np.ones(len(frame))}), + np.ones(len(frame), dtype=int), + ) + restarted.fit_live_predictions(dk, pair) + self.assertEqual(dk.data["labels_mean"]["&-action"], 8.0) + + candles = pd.DataFrame( + {"date": dates, "high": [101.0] * 4, "low": [99.0] * 4, "close": [100.0] * 4} + ) + restarted.build_strategy_return_arrays(candles, dk, pair, 0) + self.assertEqual( + restarted.dd.historic_predictions[pair][marker].tolist(), [True, True, False, False] + ) + + next_candle = pd.DataFrame( + { + "date": [dates[-1] + pd.Timedelta(minutes=5)], + "high": [101.0], + "low": [99.0], + "close": [100.0], + } + ) + restarted.dk = SimpleNamespace(check_if_model_expired=lambda timestamp: False) + restarted.build_strategy_return_arrays( + pd.concat([candles, next_candle], ignore_index=True), dk, pair, 0 + ) + self.assertEqual(dk.return_dataframe["&-action_mean"].iloc[-1], 8.0) + self.assertEqual(dk.return_dataframe["&-action_std"].iloc[-1], 1.0) + self.assertNotIn(marker, dk.return_dataframe) + + def test_duplicate_date_keeps_provable_observation_after_restart(self): + pair = "BTC/USDT" + marker = "_freqai_strategies_produced" + date = pd.Timestamp("2026-01-01", tz="UTC") + for statuses, markers in (([1, 0], None), ([0, 0], [True, False])): + with self.subTest(markers=markers), tempfile.TemporaryDirectory() as temp: + config = model_config(temp) + config["freqai"]["fit_live_predictions_candles"] = 3 + source = ReforceXY(config=config) + self.addCleanup(source.close_envs) + history = pd.DataFrame( + { + "date_pred": [date, date], + "&-action": [7.0, 99.0], + "close_price": [100.0, 100.0], + "do_predict": statuses, + } + ) + if markers is not None: + history[marker] = markers + source.dd.historic_predictions[pair] = history + source.dd.save_historic_predictions_to_disk() + + restarted = ReforceXY(config=config) + restarted.live = True + self.addCleanup(restarted.close_envs) + restored = restarted.dd.historic_predictions[pair] + self.assertEqual(restored["&-action"].tolist(), [7.0]) + self.assertEqual(restored[marker].tolist(), [True]) + dk = SimpleNamespace(data={}, label_list=["&-action"], unique_class_list=[]) + restarted.fit_live_predictions(dk, pair) + self.assertEqual(dk.data["labels_mean"]["&-action"], 7.0) + + def test_legacy_zero_status_is_ambiguous_but_expired_status_is_excluded(self): + pair = "BTC/USDT" + history = pd.DataFrame( + { + "date_pred": pd.date_range("2026-01-01", periods=4, freq="5min", tz="UTC"), + "&-action": [99.0, 7.0, 88.0, 77.0], + "close_price": [100.0] * 4, + "do_predict": [0, 1, 2, np.nan], + } + ) + model = ReforceXY.__new__(ReforceXY) + model.live = True + model.freqai_info = {"fit_live_predictions_candles": 4} + model.dd = SimpleNamespace(historic_predictions={pair: history}) + dk = SimpleNamespace(data={}, label_list=["&-action"], unique_class_list=[]) + model.fit_live_predictions(dk, pair) + self.assertEqual(dk.data["labels_mean"]["&-action"], 7.0) + + def test_live_action_statistics_resume_persisted_observations(self): + pair = "BTC/USDT" + dates = pd.date_range("2026-01-01", periods=4, freq="5min", tz="UTC") + history = pd.DataFrame( + { + "date_pred": dates, + "&-action": [1.0, 2.0, 3.0, 99.0], + "do_predict": [1, 1, 1, 2], + "close_price": [100.0, 101.0, 102.0, 103.0], + } + ) + model = ReforceXY.__new__(ReforceXY) + model.live = True + model.freqai_info = {"fit_live_predictions_candles": 4} + model.dd = SimpleNamespace( + historic_predictions={pair: history}, + model_return_values={pair: history.tail(1)}, + ) + dk = SimpleNamespace(data={}, label_list=["&-action"], unique_class_list=[]) + + model.fit_live_predictions(dk, pair) + + self.assertEqual(dk.data["labels_mean"]["&-action"], 2.0) + self.assertAlmostEqual(dk.data["labels_std"]["&-action"], np.std([1.0, 2.0, 3.0])) + def test_frame_validity_and_gap_reset(self): with tempfile.TemporaryDirectory() as temp: model = ReforceXY(config=model_config(temp)) diff --git a/ReforceXY/user_data/freqaimodels/ReforceXY.py b/ReforceXY/user_data/freqaimodels/ReforceXY.py index c2e573a..f12d205 100644 --- a/ReforceXY/user_data/freqaimodels/ReforceXY.py +++ b/ReforceXY/user_data/freqaimodels/ReforceXY.py @@ -7,7 +7,6 @@ import math import os import stat import time -import warnings from collections import defaultdict, deque from collections.abc import Callable, Iterator, Mapping from contextlib import contextmanager, suppress @@ -57,7 +56,6 @@ from joblib.externals import cloudpickle from matplotlib.lines import Line2D from numpy.typing import NDArray from optuna import Trial, TrialPruned, create_study, delete_study -from optuna.exceptions import ExperimentalWarning from optuna.pruners import BasePruner, HyperbandPruner from optuna.samplers import BaseSampler, TPESampler from optuna.storages import ( @@ -103,11 +101,21 @@ def _update_eval_best_reward(callback: Any, mean_reward: float, model: Any) -> N _DATE_PRED_DEDUP_SENTINEL = "_freqai_strategies_date_pred_repair_patched" +_PRODUCED_COLUMN = "_freqai_strategies_produced" -def _recorded_prediction_mask(frame: pd.DataFrame) -> NDArray[np.bool_]: - """Identify recorded rows from metadata, never from prediction magnitudes.""" +def _legacy_produced_prediction_mask(frame: pd.DataFrame) -> NDArray[np.bool_]: + """Bootstrap has status 0; only nonzero, nonexpired statuses prove legacy production.""" + if "do_predict" not in frame: + return np.zeros(len(frame), dtype=bool) + status = pd.to_numeric(frame["do_predict"], errors="coerce") + return (status.notna() & np.isfinite(status) & status.ne(0) & status.ne(2)).to_numpy(dtype=bool) + + +def _recorded_prediction_rank(frame: pd.DataFrame) -> NDArray[np.int8]: + """Prefer provable predictions to ambiguous legacy rows and placeholders.""" recorded = np.zeros(len(frame), dtype=bool) + status = None 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) @@ -118,44 +126,79 @@ def _recorded_prediction_mask(frame: pd.DataFrame) -> NDArray[np.bool_]: .fillna(False) .to_numpy(dtype=bool) ) - return recorded + recorded &= status.ne(2).fillna(True).to_numpy(dtype=bool) + rank = recorded.astype(np.int8) + rank[_legacy_produced_prediction_mask(frame)] = 2 + if _PRODUCED_COLUMN in frame: + marker = frame[_PRODUCED_COLUMN] + proven = marker.eq(True).fillna(False).to_numpy(dtype=bool) + if status is not None: + proven = proven & status.ne(2).fillna(True).to_numpy(dtype=bool) + rank[marker.notna().to_numpy(dtype=bool)] = 0 + rank[proven] = 2 + return rank def _produced_prediction_mask(frame: pd.DataFrame) -> NDArray[np.bool_]: - """Exclude expired-model placeholders from recorded model observations.""" - produced = _recorded_prediction_mask(frame) + """Select real model outputs, excluding bootstrap and expired-model placeholders.""" + produced = _legacy_produced_prediction_mask(frame) + if _PRODUCED_COLUMN in frame: + marker = frame[_PRODUCED_COLUMN] + produced = np.where( + marker.notna(), marker.eq(True).fillna(False).to_numpy(dtype=bool), produced + ) if "do_predict" in frame: status = pd.to_numeric(frame["do_predict"], errors="coerce") - produced &= status.ne(2).fillna(True).to_numpy(dtype=bool) + produced = 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. +def _ensure_prediction_provenance(frame: pd.DataFrame) -> pd.DataFrame: + """Preserve explicit markers and infer only distinguishable legacy predictions.""" + if _PRODUCED_COLUMN not in frame: + result = frame.copy() + result[_PRODUCED_COLUMN] = _legacy_produced_prediction_mask(frame) + return result + missing = frame[_PRODUCED_COLUMN].isna() + if not missing.any(): + return frame + result = frame.copy() + result[_PRODUCED_COLUMN] = frame[_PRODUCED_COLUMN].where( + ~missing, _legacy_produced_prediction_mask(frame) + ) + return result + - Freqtrade fills downtime rows with zeros/NaNs, without a candle close or a - prediction status. Those rows must not replace recorded predictions, including - zero predictions and rejected predictions (do_predict == 0 with a candle close). - Indistinguishable rows use last-write-wins; label magnitudes never rank rows. - Invalid dates cannot match a candle and are discarded. +def _dedupe_historic_predictions_on_date_pred(frame: pd.DataFrame, pair: str) -> pd.DataFrame: + """Retain the most provable prediction per candle, in date order. + + Proven outputs (legacy nonzero statuses or explicit markers) outrank + ambiguous close-bearing rows, which outrank downtime and expired placeholders. + Equally ranked rows use last-write-wins; invalid dates are discarded. """ date_pred = pd.to_datetime(frame["date_pred"], utc=True, errors="coerce", format="mixed") valid = date_pred.notna() - if valid.all() and date_pred.is_monotonic_increasing and date_pred.is_unique: + if not valid.all(): + logger.warning( + "FreqAI prediction history [%s]: discarded invalid date_pred entries (count=%d)", + pair, + len(frame) - int(valid.sum()), + ) + elif date_pred.is_monotonic_increasing and date_pred.is_unique: if date_pred.dtype == frame["date_pred"].dtype: return frame result = frame.copy() result["date_pred"] = date_pred return result - recorded = _recorded_prediction_mask(frame) + rank = _recorded_prediction_rank(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))} + {"date_pred": date_pred.array, "rank": rank, "position": np.arange(len(frame))} ) kept = ( order.loc[valid.to_numpy()] - .sort_values(["date_pred", "recorded", "position"]) + .sort_values(["date_pred", "rank", "position"]) .drop_duplicates("date_pred", keep="last") ) result = frame.iloc[kept.index].copy() @@ -179,11 +222,11 @@ def _align_historic_predictions(history: pd.DataFrame, dataframe: pd.DataFrame) def _install_date_pred_dedup_patch() -> None: - """Normalize persisted history and duplicate predictions before upstream writes. + """Repair persisted prediction dates before Freqtrade's positional writes. - Normalize before upstream positional writes and before disk repair can discard - a recorded duplicate. Already-clean upstream results are preserved. Recheck - these synchronous method contracts on Freqtrade upgrades. + Normalize before upstream disk repair discards a provable duplicate, and + align the returned candles after writes. Both model copies of this global + patch must have identical behavior regardless of import order. """ names = ( "set_initial_return_values", @@ -202,7 +245,7 @@ def _install_date_pred_dedup_patch() -> None: ) pending.append(not getattr(current, _DATE_PRED_DEDUP_SENTINEL, False)) if iscoroutinefunction(original) or iscoroutinefunction(current): - raise RuntimeError("Repair [global]: requires synchronous drawer methods") + raise RuntimeError("FreqAI prediction repair requires synchronous drawer methods") if not any(pending): return original_set_initial, original_append, original_attach = originals[:3] @@ -212,12 +255,12 @@ def _install_date_pred_dedup_patch() -> None: self, pair: str, pred_df: pd.DataFrame, dataframe: pd.DataFrame ) -> None: self.historic_predictions[pair] = _dedupe_historic_predictions_on_date_pred( - self.historic_predictions[pair] + self.historic_predictions[pair], pair ) 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]) + repaired = _dedupe_historic_predictions_on_date_pred(self.historic_predictions[pair], pair) self.historic_predictions[pair] = repaired self.model_return_values[pair] = _align_historic_predictions(repaired, dataframe) @@ -231,7 +274,7 @@ def _install_date_pred_dedup_patch() -> None: strat_df: pd.DataFrame, ) -> None: self.historic_predictions[pair] = _dedupe_historic_predictions_on_date_pred( - self.historic_predictions[pair] + self.historic_predictions[pair], pair ) if self.historic_predictions[pair].empty and not strat_df.empty: # Append requires an initialized row; let upstream construct it. @@ -242,7 +285,7 @@ def _install_date_pred_dedup_patch() -> None: 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]) + repaired = _dedupe_historic_predictions_on_date_pred(self.historic_predictions[pair], pair) self.historic_predictions[pair] = repaired self.model_return_values[pair] = _align_historic_predictions(repaired, strat_df) @@ -250,7 +293,7 @@ def _install_date_pred_dedup_patch() -> None: def attach_return_values_to_return_dataframe( self, pair: str, dataframe: pd.DataFrame ) -> pd.DataFrame: - repaired = _dedupe_historic_predictions_on_date_pred(self.historic_predictions[pair]) + repaired = _dedupe_historic_predictions_on_date_pred(self.historic_predictions[pair], pair) self.historic_predictions[pair] = repaired self.model_return_values[pair] = _align_historic_predictions(repaired, dataframe) return original_attach(self, pair, dataframe) @@ -265,7 +308,7 @@ def _install_date_pred_dedup_patch() -> None: @wraps(original_repair) def repair_historic_predictions(self, pair: str, pair_df: pd.DataFrame) -> pd.DataFrame: if "date_pred" in pair_df: - pair_df = _dedupe_historic_predictions_on_date_pred(pair_df) + pair_df = _dedupe_historic_predictions_on_date_pred(pair_df, pair) return original_repair(self, pair, pair_df) replacements += (repair_historic_predictions,) @@ -304,9 +347,6 @@ class _Samplers(NamedTuple): matplotlib.use("Agg") -warnings.filterwarnings("ignore", category=UserWarning) -warnings.filterwarnings("ignore", category=FutureWarning) -warnings.filterwarnings("ignore", category=ExperimentalWarning) logger = logging.getLogger(__name__) @@ -321,10 +361,13 @@ class ReforceXY(BaseReinforcementLearningModel): "freqai": { ... "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. + // Live/dry-run: latest N persisted produced observations per pair. + // Initial full-frame predictions are bootstrap samples, not observations. + // Available observations are used after the next actual prediction append + // and survive restarts. Downtime and expired status 2 do not count; + // neutral/exit actions and recorded rejected predictions do. Legacy history + // without provenance cannot distinguish bootstrap from rejected status 0: + // those ambiguous rows are excluded. Backtests use the previous N rows. // 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. @@ -527,7 +570,7 @@ class ReforceXY(BaseReinforcementLearningModel): raise ValueError( f"Config [global]: fit_live_predictions_candles=" f"{fit_live_predictions_candles!r} invalid; " - "must be a non-negative integer (0 disables label statistics)" + "must be a non-negative integer (0 disables action statistics)" ) self.freqai_info["fit_live_predictions_candles"] = fit_live_predictions_candles @@ -581,10 +624,77 @@ class ReforceXY(BaseReinforcementLearningModel): self.unset_unsupported() self._configure_gpu_memory() self._install_replay_persistence() + self._install_prediction_provenance() + + def _install_prediction_provenance(self) -> None: + """Keep live observation metadata local to this model's drawer instance.""" + drawer = self.dd + set_initial = drawer.set_initial_return_values + append = drawer.append_model_predictions + attach = drawer.attach_return_values_to_return_dataframe + load = drawer.load_historic_predictions_from_disk + + def migrate() -> None: + for pair, history in drawer.historic_predictions.items(): + if "date_pred" in history: + drawer.historic_predictions[pair] = _ensure_prediction_provenance(history) + + @wraps(load) + def load_with_provenance() -> bool: + loaded = load() + migrate() + return loaded + + @wraps(set_initial) + def set_initial_with_provenance( + pair: str, pred_df: pd.DataFrame, dataframe: pd.DataFrame + ) -> None: + drawer.historic_predictions[pair] = _ensure_prediction_provenance( + drawer.historic_predictions[pair] + ) + set_initial(pair, pred_df, dataframe) + drawer.historic_predictions[pair] = _ensure_prediction_provenance( + drawer.historic_predictions[pair] + ) + drawer.model_return_values[pair] = _align_historic_predictions( + drawer.historic_predictions[pair], dataframe + ) + + @wraps(append) + def append_with_provenance( + pair: str, + predictions: pd.DataFrame, + do_preds: NDArray[np.int_], + dk: FreqaiDataKitchen, + strat_df: pd.DataFrame, + ) -> None: + drawer.historic_predictions[pair] = _ensure_prediction_provenance( + drawer.historic_predictions[pair] + ) + append(pair, predictions, do_preds, dk, strat_df) + history = drawer.historic_predictions[pair] + # The native writer may return without writing an older candle. + if ( + not history.empty + and not strat_df.empty + and pd.to_datetime(history["date_pred"].iloc[-1], utc=True) + == pd.to_datetime(strat_df["date"].iloc[-1], utc=True) + ): + history.loc[history.index[-1], _PRODUCED_COLUMN] = True + drawer.model_return_values[pair] = _align_historic_predictions(history, strat_df) + + @wraps(attach) + def attach_without_provenance(pair: str, dataframe: pd.DataFrame) -> pd.DataFrame: + return attach(pair, dataframe).drop(columns=_PRODUCED_COLUMN, errors="ignore") + + migrate() + drawer.load_historic_predictions_from_disk = load_with_provenance + drawer.set_initial_return_values = set_initial_with_provenance + drawer.append_model_predictions = append_with_provenance + drawer.attach_return_values_to_return_dataframe = attach_without_provenance def _install_replay_persistence(self) -> None: save_data = self.dd.save_data - load_data = self.dd.load_data @wraps(save_data) def save_with_replay(model, coin, dk): @@ -603,21 +713,7 @@ class ReforceXY(BaseReinforcementLearningModel): dk.data["reforcexy_replay"] = filename return save_data(model, coin, dk) - @wraps(load_data) - def load_with_replay(coin, dk): - cached = self.dd.model_dictionary.get(coin) if dk.live else None - model = load_data(coin, dk) - if model is not None and model is not cached: - try: - self._restore_replay(model, dk.data, dk.data_path, coin) - except Exception: - if self.dd.model_dictionary.get(coin) is model: - self.dd.model_dictionary.pop(coin, None) - raise - return model - self.dd.save_data = save_with_replay - self.dd.load_data = load_with_replay @staticmethod def _restore_replay(model: Any, metadata: dict[str, Any], directory: Path, pair: str) -> None: @@ -1241,8 +1337,11 @@ class ReforceXY(BaseReinforcementLearningModel): archive.seek(0) model = self.MODELCLASS.load(archive, device=cached_model.device) # Off-policy experience is excluded from SB3 model archives. - if getattr(cached_model, "replay_buffer", None) is not None: - model.replay_buffer = copy.deepcopy(cached_model.replay_buffer) + replay_buffer = getattr(cached_model, "replay_buffer", None) + if replay_buffer is not None and replay_buffer.size() > 0: + model.replay_buffer = copy.deepcopy(replay_buffer) + elif hasattr(model, "load_replay_buffer"): + self._restore_replay(model, metadata, Path(previous["data_path"]), pair) state = model, copy.deepcopy(feature_pipeline) except Exception as exc: raise DependencyException( @@ -1354,7 +1453,7 @@ class ReforceXY(BaseReinforcementLearningModel): ) if model is not None: dk.data[self._DEPLOYMENT_COORDINATE_MARKER_KEY] = self._DEPLOYMENT_COORDINATE_GENERATION - logger.info("Training [%s]: completed", pair) + logger.info("Training [%s]: model selection finished", pair) return model def fit( @@ -1492,7 +1591,7 @@ class ReforceXY(BaseReinforcementLearningModel): ) _update_eval_best_reward(self.eval_callback, float(final_mean_reward), model) except KeyboardInterrupt: - pass + logger.warning("Training [%s]: model fitting interrupted by user", dk.pair) finally: if self.progressbar_callback: self.progressbar_callback.on_training_end() @@ -1532,40 +1631,23 @@ class ReforceXY(BaseReinforcementLearningModel): return model + def set_initial_historic_predictions( + self, pred_df: pd.DataFrame, dk: FreqaiDataKitchen, pair: str, strat_df: pd.DataFrame + ) -> None: + """Keep Freqtrade's full-frame bootstrap out of live prediction statistics.""" + super().set_initial_historic_predictions(pred_df, dk, pair, strat_df) + self.dd.historic_predictions[pair][_PRODUCED_COLUMN] = False + 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( - "Predict [%s]: fit live predictions not warmed up; " - "%d more produced observations required for warmup completion", - pair, - remaining, - ) + history = _dedupe_historic_predictions_on_date_pred(history, pair) + history = history.loc[_produced_prediction_mask(history)] pred_df = history.tail(fit_live_predictions_candles).reset_index(drop=True) dk.data["labels_mean"], dk.data["labels_std"] = {}, {} @@ -1577,20 +1659,17 @@ class ReforceXY(BaseReinforcementLearningModel): 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] + 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: - 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, - ) + 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], @@ -4042,10 +4121,14 @@ class MyRLEnv(Base5ActionRLEnv): if self._position == Positions.Neutral: exit_pnl = pre_pnl elif action != Actions.Neutral.value: + try: + action_name = Actions(action).name + except ValueError: + action_name = "unknown" logger.warning( "Env [%s]: invalid action=%s (%d) in position=%s at tick=%d", self.id, - Actions(action).name, + action_name, action, self._position.name, self._current_tick, diff --git a/quickadapter/.basedpyright/diagnostics.json b/quickadapter/.basedpyright/diagnostics.json index 5a5b444..0684d8a 100644 --- a/quickadapter/.basedpyright/diagnostics.json +++ b/quickadapter/.basedpyright/diagnostics.json @@ -3,743 +3,743 @@ "diagnostics": [ { "endCharacter": 46, - "endLine": 1357, + "endLine": 1400, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"str | None\" cannot be assigned to parameter \"default\" of type \"str\" in function \"_validate_label_selection_metric\"\n  Type \"str | None\" is not assignable to type \"str\"\n    \"None\" is not assignable to \"str\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 24, - "startLine": 1357 + "startLine": 1400 }, { "endCharacter": 79, - "endLine": 2139, + "endLine": 2183, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Cannot access attribute \"index\" for class \"NDArray[Unknown]\"\n  Attribute \"index\" is unknown", "rule": "reportAttributeAccessIssue", "severity": "error", "startCharacter": 74, - "startLine": 2139 + "startLine": 2183 }, { "endCharacter": 72, - "endLine": 2141, + "endLine": 2185, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Cannot access attribute \"index\" for class \"NDArray[Unknown]\"\n  Attribute \"index\" is unknown", "rule": "reportAttributeAccessIssue", "severity": "error", "startCharacter": 67, - "startLine": 2141 + "startLine": 2185 }, { "endCharacter": 90, - "endLine": 2150, + "endLine": 2194, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Cannot access attribute \"index\" for class \"NDArray[Unknown]\"\n  Attribute \"index\" is unknown", "rule": "reportAttributeAccessIssue", "severity": "error", "startCharacter": 85, - "startLine": 2150 + "startLine": 2194 }, { "endCharacter": 34, - "endLine": 2163, + "endLine": 2207, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"Unknown | NDArray[Unknown] | Any | list[Unknown]\" cannot be assigned to parameter \"train_features\" of type \"DataFrame\" in function \"_filter_train_by_mask\"\n  Type \"Unknown | NDArray[Unknown] | Any | list[Unknown]\" is not assignable to type \"DataFrame\"\n    \"list[Unknown]\" is not assignable to \"DataFrame\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 20, - "startLine": 2163 + "startLine": 2207 }, { "endCharacter": 32, - "endLine": 2164, + "endLine": 2208, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"Unknown | NDArray[Unknown] | Any | list[Unknown]\" cannot be assigned to parameter \"train_labels\" of type \"DataFrame\" in function \"_filter_train_by_mask\"\n  Type \"Unknown | NDArray[Unknown] | Any | list[Unknown]\" is not assignable to type \"DataFrame\"\n    \"list[Unknown]\" is not assignable to \"DataFrame\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 20, - "startLine": 2164 + "startLine": 2208 }, { "endCharacter": 38, - "endLine": 2165, + "endLine": 2209, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"Unknown | NDArray[Unknown] | Any | list[Unknown]\" cannot be assigned to parameter \"train_weights\" of type \"NDArray[floating[Any]]\" in function \"_filter_train_by_mask\"\n  Type \"Unknown | NDArray[Unknown] | Any | list[Unknown]\" is not assignable to type \"NDArray[floating[Any]]\"\n    \"list[Unknown]\" is not assignable to \"ndarray[_AnyShape, dtype[floating[Any]]]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 20, - "startLine": 2165 + "startLine": 2209 }, { "endCharacter": 59, - "endLine": 2168, + "endLine": 2212, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"Unknown | NDArray[Unknown] | Any | list[Unknown] | None\" cannot be assigned to parameter \"train_label_weights\" of type \"NDArray[floating[Any]] | None\" in function \"_filter_train_by_mask\"\n  Type \"Unknown | NDArray[Unknown] | Any | list[Unknown] | None\" is not assignable to type \"NDArray[floating[Any]] | None\"\n    Type \"list[Unknown]\" is not assignable to type \"NDArray[floating[Any]] | None\"\n      \"list[Unknown]\" is not assignable to \"ndarray[_AnyShape, dtype[floating[Any]]]\"\n      \"list[Unknown]\" is not assignable to \"None\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 40, - "startLine": 2168 + "startLine": 2212 }, { "endCharacter": 34, - "endLine": 2202, + "endLine": 2246, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"DataFrame | Unknown | NDArray[Unknown] | Any | list[Unknown]\" cannot be assigned to parameter \"features\" of type \"DataFrame\" in function \"_shuffle_split_rows\"\n  Type \"DataFrame | Unknown | NDArray[Unknown] | Any | list[Unknown]\" is not assignable to type \"DataFrame\"\n    \"list[Unknown]\" is not assignable to \"DataFrame\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 20, - "startLine": 2202 + "startLine": 2246 }, { "endCharacter": 32, - "endLine": 2203, + "endLine": 2247, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"DataFrame | Unknown | NDArray[Unknown] | Any | list[Unknown]\" cannot be assigned to parameter \"labels\" of type \"DataFrame\" in function \"_shuffle_split_rows\"\n  Type \"DataFrame | Unknown | NDArray[Unknown] | Any | list[Unknown]\" is not assignable to type \"DataFrame\"\n    \"list[Unknown]\" is not assignable to \"DataFrame\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 20, - "startLine": 2203 + "startLine": 2247 }, { "endCharacter": 38, - "endLine": 2204, + "endLine": 2248, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"NDArray[floating[Any]] | Unknown | NDArray[Unknown] | Any | list[Unknown]\" cannot be assigned to parameter \"base_weights\" of type \"NDArray[floating[Any]]\" in function \"_shuffle_split_rows\"\n  Type \"NDArray[floating[Any]] | Unknown | NDArray[Unknown] | Any | list[Unknown]\" is not assignable to type \"NDArray[floating[Any]]\"\n    \"list[Unknown]\" is not assignable to \"ndarray[_AnyShape, dtype[floating[Any]]]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 20, - "startLine": 2204 + "startLine": 2248 }, { "endCharacter": 39, - "endLine": 2205, + "endLine": 2249, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"NDArray[floating[Any]] | Unknown | NDArray[Unknown] | Any | list[Unknown] | None\" cannot be assigned to parameter \"label_weights\" of type \"NDArray[floating[Any]] | None\" in function \"_shuffle_split_rows\"\n  Type \"NDArray[floating[Any]] | Unknown | NDArray[Unknown] | Any | list[Unknown] | None\" is not assignable to type \"NDArray[floating[Any]] | None\"\n    Type \"list[Unknown]\" is not assignable to type \"NDArray[floating[Any]] | None\"\n      \"list[Unknown]\" is not assignable to \"ndarray[_AnyShape, dtype[floating[Any]]]\"\n      \"list[Unknown]\" is not assignable to \"None\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 20, - "startLine": 2205 + "startLine": 2249 }, { "endCharacter": 37, - "endLine": 2212, + "endLine": 2256, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"Unknown | NDArray[Unknown] | Any | list[Unknown] | DataFrame\" cannot be assigned to parameter \"features\" of type \"DataFrame\" in function \"_shuffle_split_rows\"\n  Type \"Unknown | NDArray[Unknown] | Any | list[Unknown] | DataFrame\" is not assignable to type \"DataFrame\"\n    \"list[Unknown]\" is not assignable to \"DataFrame\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 24, - "startLine": 2212 + "startLine": 2256 }, { "endCharacter": 35, - "endLine": 2213, + "endLine": 2257, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"Unknown | NDArray[Unknown] | Any | list[Unknown] | DataFrame\" cannot be assigned to parameter \"labels\" of type \"DataFrame\" in function \"_shuffle_split_rows\"\n  Type \"Unknown | NDArray[Unknown] | Any | list[Unknown] | DataFrame\" is not assignable to type \"DataFrame\"\n    \"list[Unknown]\" is not assignable to \"DataFrame\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 24, - "startLine": 2213 + "startLine": 2257 }, { "endCharacter": 41, - "endLine": 2214, + "endLine": 2258, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"Unknown | NDArray[Unknown] | Any | list[Unknown] | ndarray[_AnyShape, dtype[floating[Any]]]\" cannot be assigned to parameter \"base_weights\" of type \"NDArray[floating[Any]]\" in function \"_shuffle_split_rows\"\n  Type \"Unknown | NDArray[Unknown] | Any | list[Unknown] | ndarray[_AnyShape, dtype[floating[Any]]]\" is not assignable to type \"NDArray[floating[Any]]\"\n    \"list[Unknown]\" is not assignable to \"ndarray[_AnyShape, dtype[floating[Any]]]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 24, - "startLine": 2214 + "startLine": 2258 }, { "endCharacter": 42, - "endLine": 2215, + "endLine": 2259, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"Unknown | NDArray[Unknown] | Any | list[Unknown] | ndarray[_AnyShape, dtype[floating[Any]]] | None\" cannot be assigned to parameter \"label_weights\" of type \"NDArray[floating[Any]] | None\" in function \"_shuffle_split_rows\"\n  Type \"Unknown | NDArray[Unknown] | Any | list[Unknown] | ndarray[_AnyShape, dtype[floating[Any]]] | None\" is not assignable to type \"NDArray[floating[Any]] | None\"\n    Type \"list[Unknown]\" is not assignable to type \"NDArray[floating[Any]] | None\"\n      \"list[Unknown]\" is not assignable to \"ndarray[_AnyShape, dtype[floating[Any]]]\"\n      \"list[Unknown]\" is not assignable to \"None\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 24, - "startLine": 2215 + "startLine": 2259 }, { "endCharacter": 30, - "endLine": 2233, + "endLine": 2277, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"NDArray[floating[Any]] | Unknown | NDArray[Unknown] | Any | list[Unknown]\" cannot be assigned to parameter \"base_weights\" of type \"NDArray[floating[Any]]\" in function \"_compose_train_weights_with_support\"\n  Type \"NDArray[floating[Any]] | Unknown | NDArray[Unknown] | Any | list[Unknown]\" is not assignable to type \"NDArray[floating[Any]]\"\n    \"list[Unknown]\" is not assignable to \"ndarray[_AnyShape, dtype[floating[Any]]]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 12, - "startLine": 2233 + "startLine": 2277 }, { "endCharacter": 31, - "endLine": 2234, + "endLine": 2278, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"NDArray[floating[Any]] | Unknown | NDArray[Unknown] | Any | list[Unknown] | None\" cannot be assigned to parameter \"label_weights\" of type \"NDArray[floating[Any]] | None\" in function \"_compose_train_weights_with_support\"\n  Type \"NDArray[floating[Any]] | Unknown | NDArray[Unknown] | Any | list[Unknown] | None\" is not assignable to type \"NDArray[floating[Any]] | None\"\n    Type \"list[Unknown]\" is not assignable to type \"NDArray[floating[Any]] | None\"\n      \"list[Unknown]\" is not assignable to \"ndarray[_AnyShape, dtype[floating[Any]]]\"\n      \"list[Unknown]\" is not assignable to \"None\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 12, - "startLine": 2234 + "startLine": 2278 }, { "endCharacter": 33, - "endLine": 2240, + "endLine": 2284, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"NDArray[floating[Any]] | Unknown | NDArray[Unknown] | Any | list[Unknown]\" cannot be assigned to parameter \"base_weights\" of type \"NDArray[floating[Any]]\" in function \"_compose_eval_weights\"\n  Type \"NDArray[floating[Any]] | Unknown | NDArray[Unknown] | Any | list[Unknown]\" is not assignable to type \"NDArray[floating[Any]]\"\n    \"list[Unknown]\" is not assignable to \"ndarray[_AnyShape, dtype[floating[Any]]]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 16, - "startLine": 2240 + "startLine": 2284 }, { "endCharacter": 34, - "endLine": 2241, + "endLine": 2285, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"NDArray[floating[Any]] | Unknown | NDArray[Unknown] | Any | list[Unknown] | None\" cannot be assigned to parameter \"label_weights\" of type \"NDArray[floating[Any]] | None\" in function \"_compose_eval_weights\"\n  Type \"NDArray[floating[Any]] | Unknown | NDArray[Unknown] | Any | list[Unknown] | None\" is not assignable to type \"NDArray[floating[Any]] | None\"\n    Type \"list[Unknown]\" is not assignable to type \"NDArray[floating[Any]] | None\"\n      \"list[Unknown]\" is not assignable to \"ndarray[_AnyShape, dtype[floating[Any]]]\"\n      \"list[Unknown]\" is not assignable to \"None\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 16, - "startLine": 2241 + "startLine": 2285 }, { "endCharacter": 26, - "endLine": 2248, + "endLine": 2292, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"DataFrame | Unknown | NDArray[Unknown] | Any | list[Unknown]\" cannot be assigned to parameter \"train_df\" of type \"DataFrame\" in function \"build_data_dictionary\"\n  Type \"DataFrame | Unknown | NDArray[Unknown] | Any | list[Unknown]\" is not assignable to type \"DataFrame\"\n    \"list[Unknown]\" is not assignable to \"DataFrame\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 12, - "startLine": 2248 + "startLine": 2292 }, { "endCharacter": 25, - "endLine": 2249, + "endLine": 2293, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"DataFrame | Unknown | NDArray[Unknown] | Any | list[Unknown]\" cannot be assigned to parameter \"test_df\" of type \"DataFrame\" in function \"build_data_dictionary\"\n  Type \"DataFrame | Unknown | NDArray[Unknown] | Any | list[Unknown]\" is not assignable to type \"DataFrame\"\n    \"list[Unknown]\" is not assignable to \"DataFrame\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 12, - "startLine": 2249 + "startLine": 2293 }, { "endCharacter": 24, - "endLine": 2250, + "endLine": 2294, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"DataFrame | Unknown | NDArray[Unknown] | Any | list[Unknown]\" cannot be assigned to parameter \"train_labels\" of type \"DataFrame\" in function \"build_data_dictionary\"\n  Type \"DataFrame | Unknown | NDArray[Unknown] | Any | list[Unknown]\" is not assignable to type \"DataFrame\"\n    \"list[Unknown]\" is not assignable to \"DataFrame\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 12, - "startLine": 2250 + "startLine": 2294 }, { "endCharacter": 23, - "endLine": 2251, + "endLine": 2295, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"DataFrame | Unknown | NDArray[Unknown] | Any | list[Unknown]\" cannot be assigned to parameter \"test_labels\" of type \"DataFrame\" in function \"build_data_dictionary\"\n  Type \"DataFrame | Unknown | NDArray[Unknown] | Any | list[Unknown]\" is not assignable to type \"DataFrame\"\n    \"list[Unknown]\" is not assignable to \"DataFrame\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 12, - "startLine": 2251 + "startLine": 2295 }, { "endCharacter": 87, - "endLine": 2558, + "endLine": 2602, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Cannot access attribute \"index\" for class \"NDArray[Unknown]\"\n  Attribute \"index\" is unknown", "rule": "reportAttributeAccessIssue", "severity": "error", "startCharacter": 82, - "startLine": 2558 + "startLine": 2602 }, { "endCharacter": 68, - "endLine": 2559, + "endLine": 2603, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Cannot access attribute \"index\" for class \"NDArray[Unknown]\"\n  Attribute \"index\" is unknown", "rule": "reportAttributeAccessIssue", "severity": "error", "startCharacter": 63, - "startLine": 2559 + "startLine": 2603 }, { "endCharacter": 63, - "endLine": 2570, + "endLine": 2614, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Cannot access attribute \"index\" for class \"NDArray[Unknown]\"\n  Attribute \"index\" is unknown", "rule": "reportAttributeAccessIssue", "severity": "error", "startCharacter": 58, - "startLine": 2570 + "startLine": 2614 }, { "endCharacter": 47, - "endLine": 2572, + "endLine": 2616, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Cannot access attribute \"loc\" for class \"NDArray[Unknown]\"\n  Attribute \"loc\" is unknown", "rule": "reportAttributeAccessIssue", "severity": "error", "startCharacter": 44, - "startLine": 2572 + "startLine": 2616 }, { "endCharacter": 47, - "endLine": 2572, + "endLine": 2616, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Cannot access attribute \"loc\" for class \"list[Unknown]\"\n  Attribute \"loc\" is unknown", "rule": "reportAttributeAccessIssue", "severity": "error", "startCharacter": 44, - "startLine": 2572 + "startLine": 2616 }, { "endCharacter": 43, - "endLine": 2573, + "endLine": 2617, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Cannot access attribute \"loc\" for class \"NDArray[Unknown]\"\n  Attribute \"loc\" is unknown", "rule": "reportAttributeAccessIssue", "severity": "error", "startCharacter": 40, - "startLine": 2573 + "startLine": 2617 }, { "endCharacter": 43, - "endLine": 2573, + "endLine": 2617, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Cannot access attribute \"loc\" for class \"list[Unknown]\"\n  Attribute \"loc\" is unknown", "rule": "reportAttributeAccessIssue", "severity": "error", "startCharacter": 40, - "startLine": 2573 + "startLine": 2617 }, { "endCharacter": 31, - "endLine": 2594, + "endLine": 2638, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Cannot access attribute \"empty\" for class \"NDArray[Unknown]\"\n  Attribute \"empty\" is unknown", "rule": "reportAttributeAccessIssue", "severity": "error", "startCharacter": 26, - "startLine": 2594 + "startLine": 2638 }, { "endCharacter": 31, - "endLine": 2594, + "endLine": 2638, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Cannot access attribute \"empty\" for class \"list[Unknown]\"\n  Attribute \"empty\" is unknown", "rule": "reportAttributeAccessIssue", "severity": "error", "startCharacter": 26, - "startLine": 2594 + "startLine": 2638 }, { "endCharacter": 60, - "endLine": 2594, + "endLine": 2638, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Cannot access attribute \"empty\" for class \"NDArray[Unknown]\"\n  Attribute \"empty\" is unknown", "rule": "reportAttributeAccessIssue", "severity": "error", "startCharacter": 55, - "startLine": 2594 + "startLine": 2638 }, { "endCharacter": 60, - "endLine": 2594, + "endLine": 2638, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Cannot access attribute \"empty\" for class \"list[Unknown]\"\n  Attribute \"empty\" is unknown", "rule": "reportAttributeAccessIssue", "severity": "error", "startCharacter": 55, - "startLine": 2594 + "startLine": 2638 }, { "endCharacter": 73, - "endLine": 2606, + "endLine": 2650, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"Unknown | Any | ((value: Unknown, start: SupportsIndex = 0, stop: SupportsIndex = sys.maxsize, /) -> int)\" cannot be assigned to parameter \"target\" of type \"Index[Any]\" in function \"get_indexer\"\n  Type \"Unknown | Any | ((value: Unknown, start: SupportsIndex = 0, stop: SupportsIndex = sys.maxsize, /) -> int)\" is not assignable to type \"Index[Any]\"\n    \"MethodType\" is not assignable to \"Index[Any]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 53, - "startLine": 2606 + "startLine": 2650 }, { "endCharacter": 73, - "endLine": 2606, + "endLine": 2650, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Cannot access attribute \"index\" for class \"NDArray[Unknown]\"\n  Attribute \"index\" is unknown", "rule": "reportAttributeAccessIssue", "severity": "error", "startCharacter": 68, - "startLine": 2606 + "startLine": 2650 }, { "endCharacter": 83, - "endLine": 2607, + "endLine": 2651, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"Unknown | Any | ((value: Unknown, start: SupportsIndex = 0, stop: SupportsIndex = sys.maxsize, /) -> int)\" cannot be assigned to parameter \"target\" of type \"Index[Any]\" in function \"get_indexer\"\n  Type \"Unknown | Any | ((value: Unknown, start: SupportsIndex = 0, stop: SupportsIndex = sys.maxsize, /) -> int)\" is not assignable to type \"Index[Any]\"\n    \"MethodType\" is not assignable to \"Index[Any]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 58, - "startLine": 2607 + "startLine": 2651 }, { "endCharacter": 83, - "endLine": 2607, + "endLine": 2651, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Cannot access attribute \"index\" for class \"NDArray[Unknown]\"\n  Attribute \"index\" is unknown", "rule": "reportAttributeAccessIssue", "severity": "error", "startCharacter": 78, - "startLine": 2607 + "startLine": 2651 }, { "endCharacter": 46, - "endLine": 2842, + "endLine": 2886, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Cannot access attribute \"empty\" for class \"Buffer\"\n  Attribute \"empty\" is unknown", "rule": "reportAttributeAccessIssue", "severity": "error", "startCharacter": 41, - "startLine": 2842 + "startLine": 2886 }, { "endCharacter": 46, - "endLine": 2842, + "endLine": 2886, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Cannot access attribute \"empty\" for class \"_NestedSequence[_SupportsArray[dtype[Any]]]\"\n  Attribute \"empty\" is unknown", "rule": "reportAttributeAccessIssue", "severity": "error", "startCharacter": 41, - "startLine": 2842 + "startLine": 2886 }, { "endCharacter": 46, - "endLine": 2842, + "endLine": 2886, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Cannot access attribute \"empty\" for class \"_NestedSequence[complex | bytes | str]\"\n  Attribute \"empty\" is unknown", "rule": "reportAttributeAccessIssue", "severity": "error", "startCharacter": 41, - "startLine": 2842 + "startLine": 2886 }, { "endCharacter": 46, - "endLine": 2842, + "endLine": 2886, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Cannot access attribute \"empty\" for class \"_SupportsArray[dtype[Any]]\"\n  Attribute \"empty\" is unknown", "rule": "reportAttributeAccessIssue", "severity": "error", "startCharacter": 41, - "startLine": 2842 + "startLine": 2886 }, { "endCharacter": 46, - "endLine": 2842, + "endLine": 2886, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Cannot access attribute \"empty\" for class \"bytes\"\n  Attribute \"empty\" is unknown", "rule": "reportAttributeAccessIssue", "severity": "error", "startCharacter": 41, - "startLine": 2842 + "startLine": 2886 }, { "endCharacter": 46, - "endLine": 2842, + "endLine": 2886, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Cannot access attribute \"empty\" for class \"complex\"\n  Attribute \"empty\" is unknown", "rule": "reportAttributeAccessIssue", "severity": "error", "startCharacter": 41, - "startLine": 2842 + "startLine": 2886 }, { "endCharacter": 46, - "endLine": 2842, + "endLine": 2886, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Cannot access attribute \"empty\" for class \"str\"\n  Attribute \"empty\" is unknown", "rule": "reportAttributeAccessIssue", "severity": "error", "startCharacter": 41, - "startLine": 2842 + "startLine": 2886 }, { "endCharacter": 41, - "endLine": 2849, + "endLine": 2893, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"ArrayLike\" cannot be assigned to parameter \"features\" of type \"DataFrame\" in function \"_sanitize_pipeline_weights\"\n  Type \"ArrayLike\" is not assignable to type \"DataFrame\"\n    \"Buffer\" is not assignable to \"DataFrame\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 16, - "startLine": 2849 + "startLine": 2893 }, { "endCharacter": 39, - "endLine": 2898, + "endLine": 2942, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"ArrayLike\" cannot be assigned to parameter \"features\" of type \"DataFrame\" in function \"_sanitize_pipeline_weights\"\n  Type \"ArrayLike\" is not assignable to type \"DataFrame\"\n    \"Buffer\" is not assignable to \"DataFrame\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 20, - "startLine": 2898 + "startLine": 2942 }, { "endCharacter": 19, - "endLine": 3008, + "endLine": 3052, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"int | Any | None\" cannot be assigned to parameter \"gap\" of type \"int\" in function \"__init__\"\n  Type \"int | Any | None\" is not assignable to type \"int\"\n    \"None\" is not assignable to \"int\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 16, - "startLine": 3008 + "startLine": 3052 }, { "endCharacter": 21, - "endLine": 3120, + "endLine": 3164, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"X\" of type \"DataFrame\" in function \"hp_objective\"\n  Type \"Any | None\" is not assignable to type \"DataFrame\"\n    \"None\" is not assignable to \"DataFrame\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 20, - "startLine": 3120 + "startLine": 3164 }, { "endCharacter": 21, - "endLine": 3121, + "endLine": 3165, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"y\" of type \"DataFrame\" in function \"hp_objective\"\n  Type \"Any | None\" is not assignable to type \"DataFrame\"\n    \"None\" is not assignable to \"DataFrame\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 20, - "startLine": 3121 + "startLine": 3165 }, { "endCharacter": 33, - "endLine": 3122, + "endLine": 3166, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"train_weights\" of type \"NDArray[floating[Any]]\" in function \"hp_objective\"\n  Type \"Any | None\" is not assignable to type \"NDArray[floating[Any]]\"\n    \"None\" is not assignable to \"ndarray[_AnyShape, dtype[floating[Any]]]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 20, - "startLine": 3122 + "startLine": 3166 }, { "endCharacter": 32, - "endLine": 3123, + "endLine": 3167, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"X_validation\" of type \"DataFrame\" in function \"hp_objective\"\n  Type \"Any | None\" is not assignable to type \"DataFrame\"\n    \"None\" is not assignable to \"DataFrame\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 20, - "startLine": 3123 + "startLine": 3167 }, { "endCharacter": 32, - "endLine": 3124, + "endLine": 3168, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"y_validation\" of type \"DataFrame\" in function \"hp_objective\"\n  Type \"Any | None\" is not assignable to type \"DataFrame\"\n    \"None\" is not assignable to \"DataFrame\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 20, - "startLine": 3124 + "startLine": 3168 }, { "endCharacter": 38, - "endLine": 3125, + "endLine": 3169, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"validation_weights\" of type \"NDArray[floating[Any]]\" in function \"hp_objective\"\n  Type \"Any | None\" is not assignable to type \"NDArray[floating[Any]]\"\n    \"None\" is not assignable to \"ndarray[_AnyShape, dtype[floating[Any]]]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 20, - "startLine": 3125 + "startLine": 3169 }, { "endCharacter": 24, - "endLine": 3146, + "endLine": 3190, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"X_test\" of type \"DataFrame\" in function \"make_test_set_and_weights\"\n  Type \"Any | None\" is not assignable to type \"DataFrame\"\n    \"None\" is not assignable to \"DataFrame\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 12, - "startLine": 3146 + "startLine": 3190 }, { "endCharacter": 24, - "endLine": 3147, + "endLine": 3191, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"y_test\" of type \"DataFrame\" in function \"make_test_set_and_weights\"\n  Type \"Any | None\" is not assignable to type \"DataFrame\"\n    \"None\" is not assignable to \"DataFrame\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 12, - "startLine": 3147 + "startLine": 3191 }, { "endCharacter": 30, - "endLine": 3148, + "endLine": 3192, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"test_weights\" of type \"NDArray[floating[Any]]\" in function \"make_test_set_and_weights\"\n  Type \"Any | None\" is not assignable to type \"NDArray[floating[Any]]\"\n    \"None\" is not assignable to \"ndarray[_AnyShape, dtype[floating[Any]]]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 12, - "startLine": 3148 + "startLine": 3192 }, { "endCharacter": 15, - "endLine": 3154, + "endLine": 3198, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"X\" of type \"DataFrame\" in function \"fit_regressor\"\n  Type \"Any | None\" is not assignable to type \"DataFrame\"\n    \"None\" is not assignable to \"DataFrame\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 14, - "startLine": 3154 + "startLine": 3198 }, { "endCharacter": 15, - "endLine": 3155, + "endLine": 3199, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"y\" of type \"DataFrame\" in function \"fit_regressor\"\n  Type \"Any | None\" is not assignable to type \"DataFrame\"\n    \"None\" is not assignable to \"DataFrame\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 14, - "startLine": 3155 + "startLine": 3199 }, { "endCharacter": 39, - "endLine": 3156, + "endLine": 3200, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"train_weights\" of type \"NDArray[floating[Any]]\" in function \"fit_regressor\"\n  Type \"Any | None\" is not assignable to type \"NDArray[floating[Any]]\"\n    \"None\" is not assignable to \"ndarray[_AnyShape, dtype[floating[Any]]]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 26, - "startLine": 3156 + "startLine": 3200 }, { "endCharacter": 38, - "endLine": 3166, + "endLine": 3210, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "\"columns\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 31, - "startLine": 3166 + "startLine": 3210 }, { "endCharacter": 34, - "endLine": 3167, + "endLine": 3211, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "\"index\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 29, - "startLine": 3167 + "startLine": 3211 }, { "endCharacter": 82, - "endLine": 3169, + "endLine": 3213, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "\"copy\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 78, - "startLine": 3169 + "startLine": 3213 }, { "endCharacter": 62, - "endLine": 3468, + "endLine": 3647, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "\"get_pair_dataframe\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 44, - "startLine": 3468 + "startLine": 3647 }, { "endCharacter": 68, - "endLine": 3848, + "endLine": 4027, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Type \"floating[Any] | NDArray[float64]\" is not assignable to return type \"NDArray[floating[Any]]\"\n  Type \"floating[Any] | NDArray[float64]\" is not assignable to type \"NDArray[floating[Any]]\"\n    \"floating[Any]\" is not assignable to \"ndarray[_AnyShape, dtype[floating[Any]]]\"", "rule": "reportReturnType", "severity": "error", "startCharacter": 15, - "startLine": 3844 + "startLine": 4023 }, { "endCharacter": 13, - "endLine": 3877, + "endLine": 4056, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "No overloads for \"cdist\" match the provided arguments", "rule": "reportCallIssue", "severity": "error", "startCharacter": 19, - "startLine": 3872 + "startLine": 4051 }, { "endCharacter": 38, - "endLine": 3875, + "endLine": 4054, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"str\" cannot be assigned to parameter \"metric\" of type \"_MetricFunc\" in function \"cdist\"\n  Type \"str\" is not assignable to type \"_MetricFunc\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 23, - "startLine": 3875 + "startLine": 4054 }, { "endCharacter": 9, - "endLine": 3980, + "endLine": 4159, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "No overloads for \"pdist\" match the provided arguments", "rule": "reportCallIssue", "severity": "error", "startCharacter": 36, - "startLine": 3978 + "startLine": 4157 }, { "endCharacter": 42, - "endLine": 3979, + "endLine": 4158, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"str\" cannot be assigned to parameter \"metric\" of type \"_MetricFunc\" in function \"pdist\"\n  Type \"str\" is not assignable to type \"_MetricFunc\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 27, - "startLine": 3979 + "startLine": 4158 }, { "endCharacter": 9, - "endLine": 4057, + "endLine": 4236, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "No overloads for \"cdist\" match the provided arguments", "rule": "reportCallIssue", "severity": "error", "startCharacter": 15, - "startLine": 4052 + "startLine": 4231 }, { "endCharacter": 34, - "endLine": 4055, + "endLine": 4234, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"str\" cannot be assigned to parameter \"metric\" of type \"_MetricFunc\" in function \"cdist\"\n  Type \"str\" is not assignable to type \"_MetricFunc\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 19, - "startLine": 4055 + "startLine": 4234 }, { "endCharacter": 99, - "endLine": 4246, + "endLine": 4425, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "No overloads for \"pdist\" match the provided arguments", "rule": "reportCallIssue", "severity": "error", "startCharacter": 12, - "startLine": 4246 + "startLine": 4425 }, { "endCharacter": 79, - "endLine": 4246, + "endLine": 4425, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "Argument of type \"str\" cannot be assigned to parameter \"metric\" of type \"_MetricFunc\" in function \"pdist\"\n  Type \"str\" is not assignable to type \"_MetricFunc\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 64, - "startLine": 4246 + "startLine": 4425 }, { "endCharacter": 55, diff --git a/quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py b/quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py index d83bd33..623a22b 100644 --- a/quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py +++ b/quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py @@ -3,7 +3,6 @@ import json import logging import random import time -import warnings from collections.abc import Callable from collections.abc import Set as AbstractSet from dataclasses import dataclass @@ -33,6 +32,7 @@ from datasieve.pipeline import Pipeline from datasieve.transforms import SKLearnWrapper from freqtrade.enums import TRADE_MODES from freqtrade.exceptions import DependencyException +from freqtrade.exchange import timeframe_to_seconds from freqtrade.freqai.base_models.BaseRegressionModel import BaseRegressionModel from freqtrade.freqai.data_drawer import ( FEATURE_PIPELINE, @@ -123,11 +123,21 @@ from Utils import ( ) _DATE_PRED_DEDUP_SENTINEL = "_freqai_strategies_date_pred_repair_patched" +_PRODUCED_PREDICTION_COLUMN = "_freqai_strategies_produced" -def _recorded_prediction_mask(frame: pd.DataFrame) -> NDArray[np.bool_]: - """Identify recorded rows from metadata, never from prediction magnitudes.""" +def _legacy_produced_prediction_mask(frame: pd.DataFrame) -> NDArray[np.bool_]: + """Bootstrap has status 0; only nonzero, nonexpired statuses prove legacy production.""" + if "do_predict" not in frame: + return np.zeros(len(frame), dtype=bool) + status = pd.to_numeric(frame["do_predict"], errors="coerce") + return (status.notna() & np.isfinite(status) & status.ne(0) & status.ne(2)).to_numpy(dtype=bool) + + +def _recorded_prediction_rank(frame: pd.DataFrame) -> NDArray[np.int8]: + """Prefer provable predictions to ambiguous legacy rows and placeholders.""" recorded = np.zeros(len(frame), dtype=bool) + status = None 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) @@ -138,44 +148,78 @@ def _recorded_prediction_mask(frame: pd.DataFrame) -> NDArray[np.bool_]: .fillna(False) .to_numpy(dtype=bool) ) - return recorded + recorded &= status.ne(2).fillna(True).to_numpy(dtype=bool) + rank = recorded.astype(np.int8) + rank[_legacy_produced_prediction_mask(frame)] = 2 + if _PRODUCED_PREDICTION_COLUMN in frame: + marker = frame[_PRODUCED_PREDICTION_COLUMN] + proven = marker.eq(True).fillna(False).to_numpy(dtype=bool) + if status is not None: + proven = proven & status.ne(2).fillna(True).to_numpy(dtype=bool) + rank[marker.notna().to_numpy(dtype=bool)] = 0 + rank[proven] = 2 + return rank def _produced_prediction_mask(frame: pd.DataFrame) -> NDArray[np.bool_]: - """Exclude expired-model placeholders from recorded model observations.""" - produced = _recorded_prediction_mask(frame) + """Select real model outputs, excluding bootstrap and expired-model placeholders.""" + produced = _legacy_produced_prediction_mask(frame) + if _PRODUCED_PREDICTION_COLUMN in frame: + marker = frame[_PRODUCED_PREDICTION_COLUMN] + produced = np.where( + marker.notna(), marker.eq(True).fillna(False).to_numpy(dtype=bool), produced + ) if "do_predict" in frame: status = pd.to_numeric(frame["do_predict"], errors="coerce") - produced &= status.ne(2).fillna(True).to_numpy(dtype=bool) + produced = 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. +def _ensure_produced_prediction_column(frame: pd.DataFrame) -> pd.DataFrame: + if _PRODUCED_PREDICTION_COLUMN not in frame: + result = frame.copy() + result[_PRODUCED_PREDICTION_COLUMN] = _legacy_produced_prediction_mask(frame) + return result + missing = frame[_PRODUCED_PREDICTION_COLUMN].isna() + if not missing.any(): + return frame + result = frame.copy() + result[_PRODUCED_PREDICTION_COLUMN] = frame[_PRODUCED_PREDICTION_COLUMN].where( + ~missing, _legacy_produced_prediction_mask(frame) + ) + return result + + +def _dedupe_historic_predictions_on_date_pred(frame: pd.DataFrame, pair: str) -> pd.DataFrame: + """Retain the most provable prediction per candle, in date order. - Freqtrade fills downtime rows with zeros/NaNs, without a candle close or a - prediction status. Those rows must not replace recorded predictions, including - zero predictions and rejected predictions (do_predict == 0 with a candle close). - Indistinguishable legacy rows use last-write-wins; label magnitudes never rank rows. - Invalid dates cannot match a candle and are discarded. + Proven outputs (legacy nonzero statuses or explicit markers) outrank + ambiguous close-bearing rows, which outrank downtime and expired placeholders. + Equally ranked rows use last-write-wins; invalid dates are discarded. """ date_pred = pd.to_datetime(frame["date_pred"], utc=True, errors="coerce", format="mixed") valid = date_pred.notna() - if valid.all() and date_pred.is_monotonic_increasing and date_pred.is_unique: + if not valid.all(): + logger.warning( + "FreqAI prediction history [%s]: discarded invalid date_pred entries (count=%d)", + pair, + len(frame) - int(valid.sum()), + ) + elif date_pred.is_monotonic_increasing and date_pred.is_unique: if date_pred.dtype == frame["date_pred"].dtype: return frame result = frame.copy() result["date_pred"] = date_pred return result - recorded = _recorded_prediction_mask(frame) + rank = _recorded_prediction_rank(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))} + {"date_pred": date_pred.array, "rank": rank, "position": np.arange(len(frame))} ) kept = ( order.loc[valid.to_numpy()] - .sort_values(["date_pred", "recorded", "position"]) + .sort_values(["date_pred", "rank", "position"]) .drop_duplicates("date_pred", keep="last") ) result = frame.iloc[kept.index].copy() @@ -199,12 +243,11 @@ def _align_historic_predictions(history: pd.DataFrame, dataframe: pd.DataFrame) def _install_date_pred_dedup_patch() -> None: - """Repair persisted history and duplicates produced by older Freqtrade writers. + """Repair persisted prediction dates before Freqtrade's positional writes. - Normalize before upstream positional writes and after legacy duplicate writes. - Normalize before upstream disk repair can discard a recorded duplicate. - Already-clean upstream results are preserved. Recheck these synchronous - method contracts on Freqtrade upgrades. + Normalize before upstream disk repair discards a provable duplicate, and + align the returned candles after writes. Both model copies of this global + patch must have identical behavior regardless of import order. """ names = ( "set_initial_return_values", @@ -233,12 +276,12 @@ def _install_date_pred_dedup_patch() -> None: self, pair: str, pred_df: pd.DataFrame, dataframe: pd.DataFrame ) -> None: self.historic_predictions[pair] = _dedupe_historic_predictions_on_date_pred( - self.historic_predictions[pair] + self.historic_predictions[pair], pair ) 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]) + repaired = _dedupe_historic_predictions_on_date_pred(self.historic_predictions[pair], pair) self.historic_predictions[pair] = repaired self.model_return_values[pair] = _align_historic_predictions(repaired, dataframe) @@ -252,10 +295,10 @@ def _install_date_pred_dedup_patch() -> None: strat_df: pd.DataFrame, ) -> None: self.historic_predictions[pair] = _dedupe_historic_predictions_on_date_pred( - self.historic_predictions[pair] + self.historic_predictions[pair], pair ) if self.historic_predictions[pair].empty and not strat_df.empty: - # Legacy append requires an initialized row; let upstream construct it. + # Append requires an initialized row; let upstream construct it. original_set_initial( self, pair, @@ -263,7 +306,7 @@ def _install_date_pred_dedup_patch() -> None: 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]) + repaired = _dedupe_historic_predictions_on_date_pred(self.historic_predictions[pair], pair) self.historic_predictions[pair] = repaired self.model_return_values[pair] = _align_historic_predictions(repaired, strat_df) @@ -271,7 +314,7 @@ def _install_date_pred_dedup_patch() -> None: def attach_return_values_to_return_dataframe( self, pair: str, dataframe: pd.DataFrame ) -> pd.DataFrame: - repaired = _dedupe_historic_predictions_on_date_pred(self.historic_predictions[pair]) + repaired = _dedupe_historic_predictions_on_date_pred(self.historic_predictions[pair], pair) self.historic_predictions[pair] = repaired self.model_return_values[pair] = _align_historic_predictions(repaired, dataframe) return original_attach(self, pair, dataframe) @@ -286,7 +329,7 @@ def _install_date_pred_dedup_patch() -> None: @wraps(original_repair) def repair_historic_predictions(self, pair: str, pair_df: pd.DataFrame) -> pd.DataFrame: if "date_pred" in pair_df: - pair_df = _dedupe_historic_predictions_on_date_pred(pair_df) + pair_df = _dedupe_historic_predictions_on_date_pred(pair_df, pair) return original_repair(self, pair, pair_df) replacements += (repair_historic_predictions,) @@ -309,7 +352,7 @@ SelectionMethod = DistanceMethod | ClusterMethod | DensityMethod ValidationMode = Literal["warn", "raise", "none"] _VALIDATION_MODES: Final[tuple[ValidationMode, ...]] = get_args(ValidationMode) SplitFn = Callable[[pd.DataFrame, pd.DataFrame, "SampleWeightInputs", pd.DataFrame], dict[str, Any]] -warnings.simplefilter(action="ignore", category=FutureWarning) + logger = logging.getLogger(__name__) @@ -322,7 +365,7 @@ def _log_known_at_none_once(pair: str, context: str) -> None: return _KNOWN_AT_NONE_LOGGED.add(key) logger.info( - f"[{pair}] {context}: No