From d0fd7639596812f63de84cfb8d2ef92c921fd73c Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Mon, 5 Oct 2026 16:00:48 +0200 Subject: [PATCH] feat(quickadapter): configure the exit-stage fraction series (#286) * feat(quickadapter): configure the exit-stage fraction series * fix(quickadapter): use integer-power retracement ladder * fix(quickadapter): correct the exit-stage ladder documentation and guards * fix(quickadapter): bound the ladder base and index rungs by stage * refactor(quickadapter): name the ladder after natr_multiplier_fraction * test(quickadapter): restore partial exit regression guarantees * test(quickadapter): avoid pinning diagnostic wording * refactor(quickadapter): align take-profit extension terminology * test(quickadapter): avoid pinning private diagnostic presentation * feat(quickadapter): default final retracement fraction to 0.5 * refactor(quickadapter): consistently access static exit stage settings * docs(quickadapter): keep exit fraction documentation in the configuration table * chore(quickadapter): bump version to 3.13.0-rc.14 * fix(quickadapter): harmonize exit-pricing diagnostics * docs(quickadapter): remove incidental startup log detail * fix(quickadapter): report effective startup configuration * fix(quickadapter): deduplicate startup horizon warnings * docs(quickadapter): focus option descriptions on user behavior --- CONTRIBUTING.md | 4 + quickadapter/.basedpyright/diagnostics.json | 856 +++++++++--------- quickadapter/README.md | 15 +- .../tests/test_regressor_label_config.py | 194 +++- quickadapter/tests/test_strategy_features.py | 131 ++- quickadapter/tests/test_strategy_sizing.py | 20 +- .../tests/test_utils_config_resolution.py | 42 +- .../freqaimodels/QuickAdapterRegressorV3.py | 224 +++-- .../user_data/strategies/QuickAdapterV3.py | 190 ++-- quickadapter/user_data/strategies/Utils.py | 82 +- 10 files changed, 1106 insertions(+), 652 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 018d0a6..bbc0584 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -50,6 +50,10 @@ teardown errors; each test still owns isolation of any other mutable state. The reward-space analysis suite runs separately with `uv`, without a Freqtrade image. +Regression tests should cover behavior, numerical boundaries and error conditions. +For private diagnostics, check the exception type and relevant input identity; +do not pin incidental wording, number formatting or arbitrary message lengths. + ## Coverage gate Both strategy suites enforce their own runtime coverage gates. ReforceXY diff --git a/quickadapter/.basedpyright/diagnostics.json b/quickadapter/.basedpyright/diagnostics.json index f0977ea..2421c5d 100644 --- a/quickadapter/.basedpyright/diagnostics.json +++ b/quickadapter/.basedpyright/diagnostics.json @@ -3,1623 +3,1623 @@ "diagnostics": [ { "endCharacter": 46, - "endLine": 1400, + "endLine": 1402, "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": 1400 + "startLine": 1402 }, { "endCharacter": 79, - "endLine": 2183, + "endLine": 2265, "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": 2183 + "startLine": 2265 }, { "endCharacter": 72, - "endLine": 2185, + "endLine": 2267, "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": 2185 + "startLine": 2267 }, { "endCharacter": 90, - "endLine": 2194, + "endLine": 2276, "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": 2194 + "startLine": 2276 }, { "endCharacter": 34, - "endLine": 2207, + "endLine": 2289, "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": 2207 + "startLine": 2289 }, { "endCharacter": 32, - "endLine": 2208, + "endLine": 2290, "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": 2208 + "startLine": 2290 }, { "endCharacter": 38, - "endLine": 2209, + "endLine": 2291, "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": 2209 + "startLine": 2291 }, { "endCharacter": 59, - "endLine": 2212, + "endLine": 2294, "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": 2212 + "startLine": 2294 }, { "endCharacter": 34, - "endLine": 2246, + "endLine": 2328, "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": 2246 + "startLine": 2328 }, { "endCharacter": 32, - "endLine": 2247, + "endLine": 2329, "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": 2247 + "startLine": 2329 }, { "endCharacter": 38, - "endLine": 2248, + "endLine": 2330, "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": 2248 + "startLine": 2330 }, { "endCharacter": 39, - "endLine": 2249, + "endLine": 2331, "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": 2249 + "startLine": 2331 }, { "endCharacter": 37, - "endLine": 2256, + "endLine": 2338, "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": 2256 + "startLine": 2338 }, { "endCharacter": 35, - "endLine": 2257, + "endLine": 2339, "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": 2257 + "startLine": 2339 }, { "endCharacter": 41, - "endLine": 2258, + "endLine": 2340, "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": 2258 + "startLine": 2340 }, { "endCharacter": 42, - "endLine": 2259, + "endLine": 2341, "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": 2259 + "startLine": 2341 }, { "endCharacter": 30, - "endLine": 2277, + "endLine": 2359, "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": 2277 + "startLine": 2359 }, { "endCharacter": 31, - "endLine": 2278, + "endLine": 2360, "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": 2278 + "startLine": 2360 }, { "endCharacter": 33, - "endLine": 2284, + "endLine": 2366, "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": 2284 + "startLine": 2366 }, { "endCharacter": 34, - "endLine": 2285, + "endLine": 2367, "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": 2285 + "startLine": 2367 }, { "endCharacter": 26, - "endLine": 2292, + "endLine": 2374, "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": 2292 + "startLine": 2374 }, { "endCharacter": 25, - "endLine": 2293, + "endLine": 2375, "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": 2293 + "startLine": 2375 }, { "endCharacter": 24, - "endLine": 2294, + "endLine": 2376, "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": 2294 + "startLine": 2376 }, { "endCharacter": 23, - "endLine": 2295, + "endLine": 2377, "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": 2295 + "startLine": 2377 }, { "endCharacter": 87, - "endLine": 2621, + "endLine": 2703, "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": 2621 + "startLine": 2703 }, { "endCharacter": 68, - "endLine": 2622, + "endLine": 2704, "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": 2622 + "startLine": 2704 }, { "endCharacter": 63, - "endLine": 2633, + "endLine": 2715, "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": 2633 + "startLine": 2715 }, { "endCharacter": 47, - "endLine": 2635, + "endLine": 2717, "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": 2635 + "startLine": 2717 }, { "endCharacter": 47, - "endLine": 2635, + "endLine": 2717, "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": 2635 + "startLine": 2717 }, { "endCharacter": 43, - "endLine": 2636, + "endLine": 2718, "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": 2636 + "startLine": 2718 }, { "endCharacter": 43, - "endLine": 2636, + "endLine": 2718, "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": 2636 + "startLine": 2718 }, { "endCharacter": 31, - "endLine": 2657, + "endLine": 2739, "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": 2657 + "startLine": 2739 }, { "endCharacter": 31, - "endLine": 2657, + "endLine": 2739, "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": 2657 + "startLine": 2739 }, { "endCharacter": 60, - "endLine": 2657, + "endLine": 2739, "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": 2657 + "startLine": 2739 }, { "endCharacter": 60, - "endLine": 2657, + "endLine": 2739, "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": 2657 + "startLine": 2739 }, { "endCharacter": 73, - "endLine": 2669, + "endLine": 2751, "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": 2669 + "startLine": 2751 }, { "endCharacter": 73, - "endLine": 2669, + "endLine": 2751, "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": 2669 + "startLine": 2751 }, { "endCharacter": 83, - "endLine": 2670, + "endLine": 2752, "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": 2670 + "startLine": 2752 }, { "endCharacter": 83, - "endLine": 2670, + "endLine": 2752, "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": 2670 + "startLine": 2752 }, { "endCharacter": 46, - "endLine": 2905, + "endLine": 2987, "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": 2905 + "startLine": 2987 }, { "endCharacter": 46, - "endLine": 2905, + "endLine": 2987, "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": 2905 + "startLine": 2987 }, { "endCharacter": 46, - "endLine": 2905, + "endLine": 2987, "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": 2905 + "startLine": 2987 }, { "endCharacter": 46, - "endLine": 2905, + "endLine": 2987, "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": 2905 + "startLine": 2987 }, { "endCharacter": 46, - "endLine": 2905, + "endLine": 2987, "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": 2905 + "startLine": 2987 }, { "endCharacter": 46, - "endLine": 2905, + "endLine": 2987, "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": 2905 + "startLine": 2987 }, { "endCharacter": 46, - "endLine": 2905, + "endLine": 2987, "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": 2905 + "startLine": 2987 }, { "endCharacter": 41, - "endLine": 2912, + "endLine": 2994, "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": 2912 + "startLine": 2994 }, { "endCharacter": 39, - "endLine": 2961, + "endLine": 3043, "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": 2961 + "startLine": 3043 }, { "endCharacter": 19, - "endLine": 3071, + "endLine": 3153, "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": 3071 + "startLine": 3153 }, { "endCharacter": 21, - "endLine": 3183, + "endLine": 3265, "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": 3183 + "startLine": 3265 }, { "endCharacter": 21, - "endLine": 3184, + "endLine": 3266, "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": 3184 + "startLine": 3266 }, { "endCharacter": 33, - "endLine": 3185, + "endLine": 3267, "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": 3185 + "startLine": 3267 }, { "endCharacter": 32, - "endLine": 3186, + "endLine": 3268, "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": 3186 + "startLine": 3268 }, { "endCharacter": 32, - "endLine": 3187, + "endLine": 3269, "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": 3187 + "startLine": 3269 }, { "endCharacter": 38, - "endLine": 3188, + "endLine": 3270, "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": 3188 + "startLine": 3270 }, { "endCharacter": 24, - "endLine": 3209, + "endLine": 3291, "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": 3209 + "startLine": 3291 }, { "endCharacter": 24, - "endLine": 3210, + "endLine": 3292, "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": 3210 + "startLine": 3292 }, { "endCharacter": 30, - "endLine": 3211, + "endLine": 3293, "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": 3211 + "startLine": 3293 }, { "endCharacter": 15, - "endLine": 3217, + "endLine": 3299, "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": 3217 + "startLine": 3299 }, { "endCharacter": 15, - "endLine": 3218, + "endLine": 3300, "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": 3218 + "startLine": 3300 }, { "endCharacter": 39, - "endLine": 3219, + "endLine": 3301, "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": 3219 + "startLine": 3301 }, { "endCharacter": 38, - "endLine": 3229, + "endLine": 3311, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "\"columns\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 31, - "startLine": 3229 + "startLine": 3311 }, { "endCharacter": 34, - "endLine": 3230, + "endLine": 3312, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "\"index\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 29, - "startLine": 3230 + "startLine": 3312 }, { "endCharacter": 82, - "endLine": 3232, + "endLine": 3314, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "\"copy\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 78, - "startLine": 3232 + "startLine": 3314 }, { "endCharacter": 62, - "endLine": 3675, + "endLine": 3757, "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": 3675 + "startLine": 3757 }, { "endCharacter": 68, - "endLine": 4055, + "endLine": 4137, "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": 4051 + "startLine": 4133 }, { "endCharacter": 13, - "endLine": 4084, + "endLine": 4166, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "No overloads for \"cdist\" match the provided arguments", "rule": "reportCallIssue", "severity": "error", "startCharacter": 19, - "startLine": 4079 + "startLine": 4161 }, { "endCharacter": 38, - "endLine": 4082, + "endLine": 4164, "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": 4082 + "startLine": 4164 }, { "endCharacter": 9, - "endLine": 4187, + "endLine": 4269, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "No overloads for \"pdist\" match the provided arguments", "rule": "reportCallIssue", "severity": "error", "startCharacter": 36, - "startLine": 4185 + "startLine": 4267 }, { "endCharacter": 42, - "endLine": 4186, + "endLine": 4268, "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": 4186 + "startLine": 4268 }, { "endCharacter": 9, - "endLine": 4264, + "endLine": 4346, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "No overloads for \"cdist\" match the provided arguments", "rule": "reportCallIssue", "severity": "error", "startCharacter": 15, - "startLine": 4259 + "startLine": 4341 }, { "endCharacter": 34, - "endLine": 4262, + "endLine": 4344, "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": 4262 + "startLine": 4344 }, { "endCharacter": 99, - "endLine": 4453, + "endLine": 4535, "file": "quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py", "message": "No overloads for \"pdist\" match the provided arguments", "rule": "reportCallIssue", "severity": "error", "startCharacter": 12, - "startLine": 4453 + "startLine": 4535 }, { "endCharacter": 79, - "endLine": 4453, + "endLine": 4535, "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": 4453 + "startLine": 4535 }, { "endCharacter": 55, - "endLine": 223, + "endLine": 225, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Type \"int\" is not assignable to declared type \"cached_property[int]\"\n  \"int\" is not assignable to \"cached_property[int]\"", "rule": "reportAssignmentType", "severity": "error", "startCharacter": 24, - "startLine": 223 + "startLine": 225 }, { "endCharacter": 15, - "endLine": 259, + "endLine": 269, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Class variable \"minimal_roi\" overrides instance variable of same name in class \"IStrategy\"", "rule": "reportIncompatibleVariableOverride", "severity": "error", "startCharacter": 4, - "startLine": 259 + "startLine": 269 }, { "endCharacter": 72, - "endLine": 259, + "endLine": 269, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Operator \"*\" not supported for types \"cached_property[int]\" and \"Literal[864]\"", "rule": "reportOperatorIssue", "severity": "error", "startCharacter": 49, - "startLine": 259 + "startLine": 269 }, { "endCharacter": 64, - "endLine": 272, + "endLine": 282, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"timeframe\" of type \"str\" in function \"timeframe_to_minutes\"\n  Type \"Any | None\" is not assignable to type \"str\"\n    \"None\" is not assignable to \"str\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 36, - "startLine": 272 + "startLine": 282 }, { "endCharacter": 17, - "endLine": 281, + "endLine": 291, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "\"can_short\" overrides symbol of same name in class \"IStrategy\"\n  \"property\" is not assignable to \"bool\"", "rule": "reportIncompatibleVariableOverride", "severity": "error", "startCharacter": 8, - "startLine": 281 + "startLine": 291 }, { "endCharacter": 19, - "endLine": 285, + "endLine": 295, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "\"plot_config\" overrides symbol of same name in class \"IStrategy\"\n  \"cached_property[dict[str, Any]]\" is not assignable to \"dict[Unknown, Unknown]\"", "rule": "reportIncompatibleVariableOverride", "severity": "error", "startCharacter": 8, - "startLine": 285 + "startLine": 295 }, { "endCharacter": 19, - "endLine": 329, + "endLine": 339, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "\"protections\" overrides symbol of same name in class \"IStrategy\"\n  \"cached_property[list[dict[str, Any]]]\" is not assignable to \"list[Unknown]\"", "rule": "reportIncompatibleVariableOverride", "severity": "error", "startCharacter": 8, - "startLine": 329 + "startLine": 339 }, { "endCharacter": 28, - "endLine": 402, + "endLine": 412, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "\"startup_candle_count\" overrides symbol of same name in class \"IStrategy\"\n  \"property\" is not assignable to \"int\"", "rule": "reportIncompatibleVariableOverride", "severity": "error", "startCharacter": 8, - "startLine": 402 + "startLine": 412 }, { "endCharacter": 44, - "endLine": 461, + "endLine": 491, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Operator \"/\" not supported for \"None\"", "rule": "reportOptionalOperand", "severity": "error", "startCharacter": 12, - "startLine": 461 + "startLine": 491 }, { "endCharacter": 38, - "endLine": 716, + "endLine": 766, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "\"to_numpy\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 30, - "startLine": 716 + "startLine": 766 }, { "endCharacter": 44, - "endLine": 749, + "endLine": 799, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Operator \"-\" not supported for \"None\"", "rule": "reportOptionalOperand", "severity": "error", "startCharacter": 38, - "startLine": 749 + "startLine": 799 }, { "endCharacter": 38, - "endLine": 757, + "endLine": 807, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Object of type \"None\" is not subscriptable", "rule": "reportOptionalSubscript", "severity": "error", "startCharacter": 36, - "startLine": 757 + "startLine": 807 }, { "endCharacter": 39, - "endLine": 758, + "endLine": 808, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Object of type \"None\" is not subscriptable", "rule": "reportOptionalSubscript", "severity": "error", "startCharacter": 37, - "startLine": 758 + "startLine": 808 }, { "endCharacter": 38, - "endLine": 759, + "endLine": 809, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Object of type \"None\" is not subscriptable", "rule": "reportOptionalSubscript", "severity": "error", "startCharacter": 36, - "startLine": 759 + "startLine": 809 }, { "endCharacter": 44, - "endLine": 782, + "endLine": 832, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Operator \"-\" not supported for types \"Series[Any] | None\" and \"Series[Any] | None\"\n  Operator \"-\" not supported for types \"Series[Any]\" and \"None\"\n  Operator \"-\" not supported for types \"None\" and \"Series[Any]\"\n  Operator \"-\" not supported for types \"None\" and \"None\"", "rule": "reportOperatorIssue", "severity": "error", "startCharacter": 29, - "startLine": 782 + "startLine": 832 }, { "endCharacter": 66, - "endLine": 782, + "endLine": 832, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"Series[Any] | None\" cannot be assigned to parameter \"s1\" of type \"Series[Any]\" in function \"non_zero_diff\"\n  Type \"Series[Any] | None\" is not assignable to type \"Series[Any]\"\n    \"None\" is not assignable to \"Series[Any]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 61, - "startLine": 782 + "startLine": 832 }, { "endCharacter": 72, - "endLine": 782, + "endLine": 832, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"Series[Any] | None\" cannot be assigned to parameter \"s2\" of type \"Series[Any]\" in function \"non_zero_diff\"\n  Type \"Series[Any] | None\" is not assignable to type \"Series[Any]\"\n    \"None\" is not assignable to \"Series[Any]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 68, - "startLine": 782 + "startLine": 832 }, { "endCharacter": 56, - "endLine": 788, + "endLine": 838, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"Series[Any] | None\" cannot be assigned to parameter \"p1\" of type \"T@get_distance\" in function \"get_distance\"\n  Type \"Series[Any] | None\" is not assignable to type \"Series[Any]\"\n    \"None\" is not assignable to \"Series[Any]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 50, - "startLine": 788 + "startLine": 838 }, { "endCharacter": 58, - "endLine": 789, + "endLine": 839, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"Series[Any] | None\" cannot be assigned to parameter \"p1\" of type \"T@get_distance\" in function \"get_distance\"\n  Type \"Series[Any] | None\" is not assignable to type \"Series[Any]\"\n    \"None\" is not assignable to \"Series[Any]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 52, - "startLine": 789 + "startLine": 839 }, { "endCharacter": 57, - "endLine": 790, + "endLine": 840, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"Series[Any] | None\" cannot be assigned to parameter \"p1\" of type \"T@get_distance\" in function \"get_distance\"\n  Type \"Series[Any] | None\" is not assignable to type \"Series[Any]\"\n    \"None\" is not assignable to \"Series[Any]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 51, - "startLine": 790 + "startLine": 840 }, { "endCharacter": 44, - "endLine": 793, + "endLine": 843, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"Series[Any] | None\" cannot be assigned to parameter \"series\" of type \"Series[Any]\" in function \"zlema\"\n  Type \"Series[Any] | None\" is not assignable to type \"Series[Any]\"\n    \"None\" is not assignable to \"Series[Any]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 38, - "startLine": 793 + "startLine": 843 }, { "endCharacter": 44, - "endLine": 794, + "endLine": 844, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"Series[Any] | None\" cannot be assigned to parameter \"series\" of type \"Series[Any]\" in function \"zlema\"\n  Type \"Series[Any] | None\" is not assignable to type \"Series[Any]\"\n    \"None\" is not assignable to \"Series[Any]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 38, - "startLine": 794 + "startLine": 844 }, { "endCharacter": 44, - "endLine": 795, + "endLine": 845, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"Series[Any] | None\" cannot be assigned to parameter \"series\" of type \"Series[Any]\" in function \"zlema\"\n  Type \"Series[Any] | None\" is not assignable to type \"Series[Any]\"\n    \"None\" is not assignable to \"Series[Any]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 38, - "startLine": 795 + "startLine": 845 }, { "endCharacter": 61, - "endLine": 796, + "endLine": 846, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"Series[Any] | None\" cannot be assigned to parameter \"p1\" of type \"T@get_distance\" in function \"get_distance\"\n  Type \"Series[Any] | None\" is not assignable to type \"Series[Any]\"\n    \"None\" is not assignable to \"Series[Any]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 55, - "startLine": 796 + "startLine": 846 }, { "endCharacter": 61, - "endLine": 797, + "endLine": 847, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"Series[Any] | None\" cannot be assigned to parameter \"p1\" of type \"T@get_distance\" in function \"get_distance\"\n  Type \"Series[Any] | None\" is not assignable to type \"Series[Any]\"\n    \"None\" is not assignable to \"Series[Any]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 55, - "startLine": 797 + "startLine": 847 }, { "endCharacter": 61, - "endLine": 798, + "endLine": 848, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"Series[Any] | None\" cannot be assigned to parameter \"p1\" of type \"T@get_distance\" in function \"get_distance\"\n  Type \"Series[Any] | None\" is not assignable to type \"Series[Any]\"\n    \"None\" is not assignable to \"Series[Any]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 55, - "startLine": 798 + "startLine": 848 }, { "endCharacter": 81, - "endLine": 806, + "endLine": 856, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"Series[Any]\" cannot be assigned to parameter \"p2\" of type \"T@get_distance\" in function \"get_distance\"\n  \"Series[Any]\" is not assignable to \"float\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 58, - "startLine": 806 + "startLine": 856 }, { "endCharacter": 67, - "endLine": 819, + "endLine": 869, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"Series[Any] | None\" cannot be assigned to parameter \"p1\" of type \"T@get_distance\" in function \"get_distance\"\n  Type \"Series[Any] | None\" is not assignable to type \"Series[Any]\"\n    \"None\" is not assignable to \"Series[Any]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 61, - "startLine": 819 + "startLine": 869 }, { "endCharacter": 68, - "endLine": 820, + "endLine": 870, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"Series[Any] | None\" cannot be assigned to parameter \"p1\" of type \"T@get_distance\" in function \"get_distance\"\n  Type \"Series[Any] | None\" is not assignable to type \"Series[Any]\"\n    \"None\" is not assignable to \"Series[Any]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 62, - "startLine": 820 + "startLine": 870 }, { "endCharacter": 67, - "endLine": 821, + "endLine": 871, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"Series[Any] | None\" cannot be assigned to parameter \"p1\" of type \"T@get_distance\" in function \"get_distance\"\n  Type \"Series[Any] | None\" is not assignable to type \"Series[Any]\"\n    \"None\" is not assignable to \"Series[Any]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 61, - "startLine": 821 + "startLine": 871 }, { "endCharacter": 44, - "endLine": 822, + "endLine": 872, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Operator \"-\" not supported for types \"Series[Any] | None\" and \"Series[Any] | None\"\n  Operator \"-\" not supported for types \"Series[Any]\" and \"None\" when expected type is \"_SetItemValueNotDataFrame\"\n  Operator \"-\" not supported for types \"None\" and \"Series[Any]\" when expected type is \"_SetItemValueNotDataFrame\"\n  Operator \"-\" not supported for types \"None\" and \"None\" when expected type is \"_SetItemValueNotDataFrame\"", "rule": "reportOperatorIssue", "severity": "error", "startCharacter": 30, - "startLine": 822 + "startLine": 872 }, { "endCharacter": 56, - "endLine": 823, + "endLine": 873, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "No overloads for \"__call__\" match the provided arguments", "rule": "reportCallIssue", "severity": "error", "startCharacter": 31, - "startLine": 823 + "startLine": 873 }, { "endCharacter": 47, - "endLine": 823, + "endLine": 873, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"Series[Any] | None\" cannot be assigned to parameter \"x1\" of type \"ArrayLike\" in function \"__call__\"\n  Type \"Series[Any] | None\" is not assignable to type \"ArrayLike\"\n    Type \"None\" is not assignable to type \"ArrayLike\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"_SupportsArray[dtype[Any]]\"\n        \"__array__\" is not present\n      \"None\" is incompatible with protocol \"_NestedSequence[_SupportsArray[dtype[Any]]]\"\n        \"__len__\" is not present\n ...", "rule": "reportArgumentType", "severity": "error", "startCharacter": 42, - "startLine": 823 + "startLine": 873 }, { "endCharacter": 55, - "endLine": 823, + "endLine": 873, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"Series[Any] | None\" cannot be assigned to parameter \"x2\" of type \"ArrayLike\" in function \"__call__\"\n  Type \"Series[Any] | None\" is not assignable to type \"ArrayLike\"\n    Type \"None\" is not assignable to type \"ArrayLike\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"_SupportsArray[dtype[Any]]\"\n        \"__array__\" is not present\n      \"None\" is incompatible with protocol \"_NestedSequence[_SupportsArray[dtype[Any]]]\"\n        \"__len__\" is not present\n ...", "rule": "reportArgumentType", "severity": "error", "startCharacter": 49, - "startLine": 823 + "startLine": 873 }, { "endCharacter": 64, - "endLine": 824, + "endLine": 874, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "No overloads for \"__call__\" match the provided arguments", "rule": "reportCallIssue", "severity": "error", "startCharacter": 39, - "startLine": 824 + "startLine": 874 }, { "endCharacter": 55, - "endLine": 824, + "endLine": 874, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"Series[Any] | None\" cannot be assigned to parameter \"x1\" of type \"ArrayLike\" in function \"__call__\"\n  Type \"Series[Any] | None\" is not assignable to type \"ArrayLike\"\n    Type \"None\" is not assignable to type \"ArrayLike\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"_SupportsArray[dtype[Any]]\"\n        \"__array__\" is not present\n      \"None\" is incompatible with protocol \"_NestedSequence[_SupportsArray[dtype[Any]]]\"\n        \"__len__\" is not present\n ...", "rule": "reportArgumentType", "severity": "error", "startCharacter": 50, - "startLine": 824 + "startLine": 874 }, { "endCharacter": 63, - "endLine": 824, + "endLine": 874, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"Series[Any] | None\" cannot be assigned to parameter \"x2\" of type \"ArrayLike\" in function \"__call__\"\n  Type \"Series[Any] | None\" is not assignable to type \"ArrayLike\"\n    Type \"None\" is not assignable to type \"ArrayLike\"\n      \"None\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"None\" is incompatible with protocol \"_SupportsArray[dtype[Any]]\"\n        \"__array__\" is not present\n      \"None\" is incompatible with protocol \"_NestedSequence[_SupportsArray[dtype[Any]]]\"\n        \"__len__\" is not present\n ...", "rule": "reportArgumentType", "severity": "error", "startCharacter": 57, - "startLine": 824 + "startLine": 874 }, { "endCharacter": 55, - "endLine": 832, + "endLine": 882, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"Series[Any] | None\" cannot be assigned to parameter \"p1\" of type \"T@get_distance\" in function \"get_distance\"\n  Type \"Series[Any] | None\" is not assignable to type \"Series[Any]\"\n    \"None\" is not assignable to \"Series[Any]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 49, - "startLine": 832 + "startLine": 882 }, { "endCharacter": 55, - "endLine": 833, + "endLine": 883, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"Series[Any] | None\" cannot be assigned to parameter \"p1\" of type \"T@get_distance\" in function \"get_distance\"\n  Type \"Series[Any] | None\" is not assignable to type \"Series[Any]\"\n    \"None\" is not assignable to \"Series[Any]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 49, - "startLine": 833 + "startLine": 883 }, { "endCharacter": 55, - "endLine": 834, + "endLine": 884, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"Series[Any] | None\" cannot be assigned to parameter \"p1\" of type \"T@get_distance\" in function \"get_distance\"\n  Type \"Series[Any] | None\" is not assignable to type \"Series[Any]\"\n    \"None\" is not assignable to \"Series[Any]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 49, - "startLine": 834 + "startLine": 884 }, { "endCharacter": 55, - "endLine": 835, + "endLine": 885, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"Series[Any] | None\" cannot be assigned to parameter \"p1\" of type \"T@get_distance\" in function \"get_distance\"\n  Type \"Series[Any] | None\" is not assignable to type \"Series[Any]\"\n    \"None\" is not assignable to \"Series[Any]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 49, - "startLine": 835 + "startLine": 885 }, { "endCharacter": 55, - "endLine": 836, + "endLine": 886, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"Series[Any] | None\" cannot be assigned to parameter \"p1\" of type \"T@get_distance\" in function \"get_distance\"\n  Type \"Series[Any] | None\" is not assignable to type \"Series[Any]\"\n    \"None\" is not assignable to \"Series[Any]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 49, - "startLine": 836 + "startLine": 886 }, { "endCharacter": 55, - "endLine": 837, + "endLine": 887, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"Series[Any] | None\" cannot be assigned to parameter \"p1\" of type \"T@get_distance\" in function \"get_distance\"\n  Type \"Series[Any] | None\" is not assignable to type \"Series[Any]\"\n    \"None\" is not assignable to \"Series[Any]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 49, - "startLine": 837 + "startLine": 887 }, { "endCharacter": 50, - "endLine": 866, + "endLine": 916, "file": "quickadapter/user_data/strategies/QuickAdapterV3.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": 44, - "startLine": 866 + "startLine": 916 }, { "endCharacter": 50, - "endLine": 866, + "endLine": 916, "file": "quickadapter/user_data/strategies/QuickAdapterV3.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": 44, - "startLine": 866 + "startLine": 916 }, { "endCharacter": 29, - "endLine": 867, + "endLine": 917, "file": "quickadapter/user_data/strategies/QuickAdapterV3.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": 23, - "startLine": 867 + "startLine": 917 }, { "endCharacter": 29, - "endLine": 867, + "endLine": 917, "file": "quickadapter/user_data/strategies/QuickAdapterV3.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": 23, - "startLine": 867 + "startLine": 917 }, { "endCharacter": 60, - "endLine": 904, + "endLine": 954, "file": "quickadapter/user_data/strategies/QuickAdapterV3.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": 50, - "startLine": 904 + "startLine": 954 }, { "endCharacter": 60, - "endLine": 904, + "endLine": 954, "file": "quickadapter/user_data/strategies/QuickAdapterV3.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": 50, - "startLine": 904 + "startLine": 954 }, { "endCharacter": 35, - "endLine": 905, + "endLine": 955, "file": "quickadapter/user_data/strategies/QuickAdapterV3.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": 25, - "startLine": 905 + "startLine": 955 }, { "endCharacter": 35, - "endLine": 905, + "endLine": 955, "file": "quickadapter/user_data/strategies/QuickAdapterV3.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": 25, - "startLine": 905 + "startLine": 955 }, { "endCharacter": 41, - "endLine": 1130, + "endLine": 1180, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Operator \"<\" not supported for \"None\"", "rule": "reportOptionalOperand", "severity": "error", "startCharacter": 12, - "startLine": 1130 + "startLine": 1180 }, { "endCharacter": 41, - "endLine": 1140, + "endLine": 1190, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Operator \">\" not supported for \"None\"", "rule": "reportOptionalOperand", "severity": "error", "startCharacter": 12, - "startLine": 1140 + "startLine": 1190 }, { "endCharacter": 66, - "endLine": 1153, + "endLine": 1203, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"timeframe\" of type \"str\" in function \"timeframe_to_prev_date\"\n  Type \"Any | None\" is not assignable to type \"str\"\n    \"None\" is not assignable to \"str\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 38, - "startLine": 1153 + "startLine": 1203 }, { "endCharacter": 70, - "endLine": 1284, + "endLine": 1334, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Expected 1 more positional argument", "rule": "reportCallIssue", "severity": "error", "startCharacter": 48, - "startLine": 1284 + "startLine": 1334 }, { "endCharacter": 74, - "endLine": 1348, + "endLine": 1398, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"int | None\" cannot be assigned to parameter \"trade_duration_candles\" of type \"int\" in function \"get_trade_natr\"\n  Type \"int | None\" is not assignable to type \"int\"\n    \"None\" is not assignable to \"int\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 52, - "startLine": 1348 + "startLine": 1398 }, { "endCharacter": 74, - "endLine": 1375, + "endLine": 1425, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"int | None\" cannot be assigned to parameter \"trade_duration_candles\" of type \"int\" in function \"get_trade_natr\"\n  Type \"int | None\" is not assignable to type \"int\"\n    \"None\" is not assignable to \"int\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 52, - "startLine": 1375 + "startLine": 1425 }, { "endCharacter": 96, - "endLine": 1427, + "endLine": 1477, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"timeframe\" of type \"str\" in function \"get_analyzed_dataframe\"\n  Type \"Any | None\" is not assignable to type \"str\"\n    \"None\" is not assignable to \"str\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 68, - "startLine": 1427 + "startLine": 1477 }, { "endCharacter": 77, - "endLine": 1461, + "endLine": 1507, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Operator \"<=\" not supported for \"None\"", "rule": "reportOptionalOperand", "severity": "error", "startCharacter": 57, - "startLine": 1461 + "startLine": 1507 }, { "endCharacter": 9, - "endLine": 1466, + "endLine": 1512, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Operator \"+\" not supported for types \"float\" and \"float | None\"\n  Operator \"+\" not supported for types \"float\" and \"None\"", "rule": "reportOperatorIssue", "severity": "error", "startCharacter": 28, - "startLine": 1464 + "startLine": 1510 }, { "endCharacter": 33, - "endLine": 1465, + "endLine": 1511, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Operator \"-\" not supported for \"None\"", "rule": "reportOptionalOperand", "severity": "error", "startCharacter": 13, - "startLine": 1465 + "startLine": 1511 }, { "endCharacter": 67, - "endLine": 1471, + "endLine": 1517, "file": "quickadapter/user_data/strategies/QuickAdapterV3.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": 47, - "startLine": 1471 + "startLine": 1517 }, { "endCharacter": 67, - "endLine": 1471, + "endLine": 1517, "file": "quickadapter/user_data/strategies/QuickAdapterV3.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": 47, - "startLine": 1471 + "startLine": 1517 }, { "endCharacter": 64, - "endLine": 1625, + "endLine": 1671, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "\"isoformat\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 55, - "startLine": 1625 + "startLine": 1671 }, { "endCharacter": 37, - "endLine": 1676, + "endLine": 1722, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Operator \"<=\" not supported for \"None\"", "rule": "reportOptionalOperand", "severity": "error", "startCharacter": 15, - "startLine": 1676 + "startLine": 1722 }, { "endCharacter": 48, - "endLine": 1678, + "endLine": 1724, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Operator \"<=\" not supported for \"None\"", "rule": "reportOptionalOperand", "severity": "error", "startCharacter": 15, - "startLine": 1678 + "startLine": 1724 }, { "endCharacter": 96, - "endLine": 1858, + "endLine": 1904, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"timeframe\" of type \"str\" in function \"get_analyzed_dataframe\"\n  Type \"Any | None\" is not assignable to type \"str\"\n    \"None\" is not assignable to \"str\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 68, - "startLine": 1858 + "startLine": 1904 }, { "endCharacter": 39, - "endLine": 1935, + "endLine": 1981, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Operator \"+\" not supported for \"None\"", "rule": "reportOptionalOperand", "severity": "error", "startCharacter": 21, - "startLine": 1935 + "startLine": 1981 }, { "endCharacter": 90, - "endLine": 1935, + "endLine": 1981, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Operator \"*\" not supported for types \"float\" and \"Any | None\"\n  Operator \"*\" not supported for types \"float\" and \"None\"", "rule": "reportOperatorIssue", "severity": "error", "startCharacter": 62, - "startLine": 1935 + "startLine": 1981 }, { "endCharacter": 96, - "endLine": 2239, + "endLine": 2285, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"timeframe\" of type \"str\" in function \"get_analyzed_dataframe\"\n  Type \"Any | None\" is not assignable to type \"str\"\n    \"None\" is not assignable to \"str\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 68, - "startLine": 2239 + "startLine": 2285 }, { "endCharacter": 85, - "endLine": 2264, + "endLine": 2310, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "\"isoformat\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 76, - "startLine": 2264 + "startLine": 2310 }, { "endCharacter": 47, - "endLine": 2270, + "endLine": 2316, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Operator \"<\" not supported for \"None\"", "rule": "reportOptionalOperand", "severity": "error", "startCharacter": 16, - "startLine": 2270 + "startLine": 2316 }, { "endCharacter": 42, - "endLine": 2274, + "endLine": 2320, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"str\" cannot be assigned to parameter \"side\" of type \"TradeDirection\" in function \"reversal_confirmed\"\n  Type \"str\" is not assignable to type \"TradeDirection\"\n    \"str\" is not assignable to type \"Literal['long']\"\n    \"str\" is not assignable to type \"Literal['short']\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 16, - "startLine": 2274 + "startLine": 2320 }, { "endCharacter": 42, - "endLine": 2275, + "endLine": 2321, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"str\" cannot be assigned to parameter \"order\" of type \"OrderType\" in function \"reversal_confirmed\"\n  Type \"str\" is not assignable to type \"OrderType\"\n    \"str\" is not assignable to type \"Literal['entry']\"\n    \"str\" is not assignable to type \"Literal['exit']\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 16, - "startLine": 2275 + "startLine": 2321 }, { "endCharacter": 69, - "endLine": 2277, + "endLine": 2323, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"int | float\" cannot be assigned to parameter \"lookback_period_candles\" of type \"int\" in function \"reversal_confirmed\"\n  Type \"int | float\" is not assignable to type \"int\"\n    \"float\" is not assignable to \"int\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 16, - "startLine": 2277 + "startLine": 2323 }, { "endCharacter": 47, - "endLine": 2288, + "endLine": 2334, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Operator \">\" not supported for \"None\"", "rule": "reportOptionalOperand", "severity": "error", "startCharacter": 16, - "startLine": 2288 + "startLine": 2334 }, { "endCharacter": 43, - "endLine": 2292, + "endLine": 2338, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"str\" cannot be assigned to parameter \"side\" of type \"TradeDirection\" in function \"reversal_confirmed\"\n  Type \"str\" is not assignable to type \"TradeDirection\"\n    \"str\" is not assignable to type \"Literal['long']\"\n    \"str\" is not assignable to type \"Literal['short']\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 16, - "startLine": 2292 + "startLine": 2338 }, { "endCharacter": 42, - "endLine": 2293, + "endLine": 2339, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"str\" cannot be assigned to parameter \"order\" of type \"OrderType\" in function \"reversal_confirmed\"\n  Type \"str\" is not assignable to type \"OrderType\"\n    \"str\" is not assignable to type \"Literal['entry']\"\n    \"str\" is not assignable to type \"Literal['exit']\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 16, - "startLine": 2293 + "startLine": 2339 }, { "endCharacter": 69, - "endLine": 2295, + "endLine": 2341, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"int | float\" cannot be assigned to parameter \"lookback_period_candles\" of type \"int\" in function \"reversal_confirmed\"\n  Type \"int | float\" is not assignable to type \"int\"\n    \"float\" is not assignable to \"int\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 16, - "startLine": 2295 + "startLine": 2341 }, { "endCharacter": 52, - "endLine": 2324, + "endLine": 2370, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"datetime | None\" cannot be assigned to parameter \"current_candle_date\" of type \"datetime\" in function \"_normalize_final_take_profit_state\"\n  Type \"datetime | None\" is not assignable to type \"datetime\"\n    \"None\" is not assignable to \"datetime\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 36, - "startLine": 2324 + "startLine": 2370 }, { "endCharacter": 96, - "endLine": 2444, + "endLine": 2490, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"Any | None\" cannot be assigned to parameter \"timeframe\" of type \"str\" in function \"get_analyzed_dataframe\"\n  Type \"Any | None\" is not assignable to type \"str\"\n    \"None\" is not assignable to \"str\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 68, - "startLine": 2444 + "startLine": 2490 }, { "endCharacter": 43, - "endLine": 2453, + "endLine": 2499, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"str\" cannot be assigned to parameter \"order\" of type \"OrderType\" in function \"reversal_confirmed\"\n  Type \"str\" is not assignable to type \"OrderType\"\n    \"str\" is not assignable to type \"Literal['entry']\"\n    \"str\" is not assignable to type \"Literal['exit']\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 16, - "startLine": 2453 + "startLine": 2499 }, { "endCharacter": 69, - "endLine": 2455, + "endLine": 2501, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"int | float\" cannot be assigned to parameter \"lookback_period_candles\" of type \"int\" in function \"reversal_confirmed\"\n  Type \"int | float\" is not assignable to type \"int\"\n    \"float\" is not assignable to \"int\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 16, - "startLine": 2455 + "startLine": 2501 }, { "endCharacter": 32, - "endLine": 2531, + "endLine": 2577, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"LocalTrade\" cannot be assigned to parameter \"trade\" of type \"Trade\" in function \"get_trade_annotation_line_start_date\"\n  \"LocalTrade\" is not assignable to \"Trade\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 27, - "startLine": 2531 + "startLine": 2577 }, { "endCharacter": 72, - "endLine": 2534, + "endLine": 2580, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"LocalTrade\" cannot be assigned to parameter \"trade\" of type \"Trade\" in function \"get_trade_exit_stage\"\n  \"LocalTrade\" is not assignable to \"Trade\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 67, - "startLine": 2534 + "startLine": 2580 }, { "endCharacter": 36, - "endLine": 2541, + "endLine": 2587, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"LocalTrade\" cannot be assigned to parameter \"trade\" of type \"Trade\" in function \"get_take_profit_target\"\n  \"LocalTrade\" is not assignable to \"Trade\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 31, - "startLine": 2541 + "startLine": 2587 }, { "endCharacter": 71, - "endLine": 2572, + "endLine": 2618, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"LocalTrade\" cannot be assigned to parameter \"trade\" of type \"Trade\" in function \"get_trade_entry_date\"\n  \"LocalTrade\" is not assignable to \"Trade\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 66, - "startLine": 2572 + "startLine": 2618 }, { "endCharacter": 32, - "endLine": 2603, + "endLine": 2649, "file": "quickadapter/user_data/strategies/QuickAdapterV3.py", "message": "Argument of type \"LocalTrade\" cannot be assigned to parameter \"trade\" of type \"Trade\" in function \"get_take_profit_target\"\n  \"LocalTrade\" is not assignable to \"Trade\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 27, - "startLine": 2603 + "startLine": 2649 }, { "endCharacter": 35, @@ -1653,523 +1653,523 @@ }, { "endCharacter": 98, - "endLine": 1638, + "endLine": 1710, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Type \"bool | bool_\" is not assignable to return type \"bool\"\n  Type \"bool | bool_\" is not assignable to type \"bool\"\n    \"numpy.bool[builtins.bool]\" is not assignable to \"bool\"", "rule": "reportReturnType", "severity": "error", "startCharacter": 15, - "startLine": 1638 + "startLine": 1710 }, { "endCharacter": 33, - "endLine": 1668, + "endLine": 1744, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Argument of type \"Scalar\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n  Type \"Scalar\" is not assignable to type \"ConvertibleToInt\"\n    Type \"complex\" is not assignable to type \"ConvertibleToInt\"\n      \"complex\" is not assignable to \"str\"\n      \"complex\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"complex\" is incompatible with protocol \"SupportsInt\"\n        \"__int__\" is not present\n      \"complex\" is incompatible with protocol \"SupportsIndex\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 20, - "startLine": 1668 + "startLine": 1744 }, { "endCharacter": 33, - "endLine": 1668, + "endLine": 1744, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Argument of type \"Scalar\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n  Type \"Scalar\" is not assignable to type \"ConvertibleToInt\"\n    Type \"complex\" is not assignable to type \"ConvertibleToInt\"\n      \"complex\" is not assignable to \"str\"\n      \"complex\" is incompatible with protocol \"Buffer\"\n        \"__buffer__\" is not present\n      \"complex\" is incompatible with protocol \"SupportsInt\"\n        \"__int__\" is not present\n      \"complex\" is incompatible with protocol \"SupportsIndex\"\n ...", "rule": "reportArgumentType", "severity": "error", "startCharacter": 20, - "startLine": 1668 + "startLine": 1744 }, { "endCharacter": 23, - "endLine": 1680, + "endLine": 1756, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Type \"float\" is not assignable to return type \"T@get_distance\"\n  Type \"float\" is not assignable to type \"T@get_distance\"", "rule": "reportReturnType", "severity": "error", "startCharacter": 11, - "startLine": 1680 + "startLine": 1756 }, { "endCharacter": 83, - "endLine": 2533, + "endLine": 2609, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Argument of type \"str\" cannot be assigned to parameter \"key\" of type \"CombinedMetric\" in function \"__getitem__\"\n  Type \"str\" is not assignable to type \"CombinedMetric\"\n    \"str\" is not assignable to type \"Literal['amplitude']\"\n    \"str\" is not assignable to type \"Literal['amplitude_threshold_ratio']\"\n    \"str\" is not assignable to type \"Literal['volume_rate']\"\n    \"str\" is not assignable to type \"Literal['speed']\"\n    \"str\" is not assignable to type \"Literal['efficiency_ratio']\"\n    \"str\" is not assignable to type \"Literal['volume_weighted_efficiency_ratio']\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 58, - "startLine": 2533 + "startLine": 2609 }, { "endCharacter": 38, - "endLine": 3423, + "endLine": 3499, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Cannot access attribute \"__func__\" for class \"FunctionType\"\n  Attribute \"__func__\" is unknown", "rule": "reportFunctionMemberAccess", "severity": "error", "startCharacter": 30, - "startLine": 3423 + "startLine": 3499 }, { "endCharacter": 34, - "endLine": 3425, + "endLine": 3501, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Cannot access attribute \"__func__\" for class \"FunctionType\"\n  Attribute \"__func__\" is unknown", "rule": "reportFunctionMemberAccess", "severity": "error", "startCharacter": 26, - "startLine": 3425 + "startLine": 3501 }, { "endCharacter": 55, - "endLine": 3655, + "endLine": 3731, "file": "quickadapter/user_data/strategies/Utils.py", "message": "\"rolling\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 48, - "startLine": 3655 + "startLine": 3731 }, { "endCharacter": 38, - "endLine": 3680, + "endLine": 3756, "file": "quickadapter/user_data/strategies/Utils.py", "message": "\"rolling\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 31, - "startLine": 3680 + "startLine": 3756 }, { "endCharacter": 55, - "endLine": 3706, + "endLine": 3782, "file": "quickadapter/user_data/strategies/Utils.py", "message": "\"rolling\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 48, - "startLine": 3706 + "startLine": 3782 }, { "endCharacter": 56, - "endLine": 3707, + "endLine": 3783, "file": "quickadapter/user_data/strategies/Utils.py", "message": "\"rolling\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 49, - "startLine": 3707 + "startLine": 3783 }, { "endCharacter": 33, - "endLine": 3779, + "endLine": 3855, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Argument of type \"Series[Any] | NDArray[floating[Any]]\" cannot be assigned to parameter \"series\" of type \"Series[Any]\" in function \"calculate_zero_lag\"\n  Type \"Series[Any] | NDArray[floating[Any]]\" is not assignable to type \"Series[Any]\"\n    \"ndarray[_AnyShape, dtype[floating[Any]]]\" is not assignable to \"Series[Any]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 27, - "startLine": 3779 + "startLine": 3855 }, { "endCharacter": 69, - "endLine": 3779, + "endLine": 3855, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Expected 1 more positional argument", "rule": "reportCallIssue", "severity": "error", "startCharacter": 59, - "startLine": 3779 + "startLine": 3855 }, { "endCharacter": 40, - "endLine": 3846, + "endLine": 3922, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Argument of type \"Series[Any] | None\" cannot be assigned to parameter \"series\" of type \"Series[Any]\" in function \"calculate_zero_lag\"\n  Type \"Series[Any] | None\" is not assignable to type \"Series[Any]\"\n    \"None\" is not assignable to \"Series[Any]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 35, - "startLine": 3846 + "startLine": 3922 }, { "endCharacter": 38, - "endLine": 3847, + "endLine": 3923, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Argument of type \"Series[Any] | None\" cannot be assigned to parameter \"series\" of type \"Series[Any]\" in function \"calculate_zero_lag\"\n  Type \"Series[Any] | None\" is not assignable to type \"Series[Any]\"\n    \"None\" is not assignable to \"Series[Any]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 34, - "startLine": 3847 + "startLine": 3923 }, { "endCharacter": 42, - "endLine": 3848, + "endLine": 3924, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Argument of type \"Series[Any] | None\" cannot be assigned to parameter \"series\" of type \"Series[Any]\" in function \"calculate_zero_lag\"\n  Type \"Series[Any] | None\" is not assignable to type \"Series[Any]\"\n    \"None\" is not assignable to \"Series[Any]\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 36, - "startLine": 3848 + "startLine": 3924 }, { "endCharacter": 45, - "endLine": 3850, + "endLine": 3926, "file": "quickadapter/user_data/strategies/Utils.py", "message": "\"index\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 40, - "startLine": 3850 + "startLine": 3926 }, { "endCharacter": 33, - "endLine": 3852, + "endLine": 3928, "file": "quickadapter/user_data/strategies/Utils.py", "message": "\"iloc\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 29, - "startLine": 3852 + "startLine": 3928 }, { "endCharacter": 31, - "endLine": 3853, + "endLine": 3929, "file": "quickadapter/user_data/strategies/Utils.py", "message": "\"iloc\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 27, - "startLine": 3853 + "startLine": 3929 }, { "endCharacter": 48, - "endLine": 3858, + "endLine": 3934, "file": "quickadapter/user_data/strategies/Utils.py", "message": "\"index\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 43, - "startLine": 3858 + "startLine": 3934 }, { "endCharacter": 40, - "endLine": 3859, + "endLine": 3935, "file": "quickadapter/user_data/strategies/Utils.py", "message": "\"iloc\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 36, - "startLine": 3859 + "startLine": 3935 }, { "endCharacter": 59, - "endLine": 3861, + "endLine": 3937, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Cannot access attribute \"iloc\" for class \"NDArray[Any]\"\n  Attribute \"iloc\" is unknown", "rule": "reportAttributeAccessIssue", "severity": "error", "startCharacter": 55, - "startLine": 3861 + "startLine": 3937 }, { "endCharacter": 34, - "endLine": 3863, + "endLine": 3939, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Cannot access attribute \"iloc\" for class \"NDArray[Any]\"\n  Attribute \"iloc\" is unknown", "rule": "reportAttributeAccessIssue", "severity": "error", "startCharacter": 30, - "startLine": 3863 + "startLine": 3939 }, { "endCharacter": 51, - "endLine": 3863, + "endLine": 3939, "file": "quickadapter/user_data/strategies/Utils.py", "message": "\"iloc\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 47, - "startLine": 3863 + "startLine": 3939 }, { "endCharacter": 72, - "endLine": 3863, + "endLine": 3939, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Cannot access attribute \"iloc\" for class \"NDArray[Any]\"\n  Attribute \"iloc\" is unknown", "rule": "reportAttributeAccessIssue", "severity": "error", "startCharacter": 68, - "startLine": 3863 + "startLine": 3939 }, { "endCharacter": 40, - "endLine": 3930, + "endLine": 4006, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Type \"(Series[Any] | NDArray[floating[Any]], int) -> (Series[Any] | NDArray[floating[Any]])\" is not assignable to declared type \"(series: Unknown, timeperiod: Unknown) -> Series[Any]\"\n  Type \"(Series[Any] | NDArray[floating[Any]], int) -> (Series[Any] | NDArray[floating[Any]])\" is not assignable to type \"(series: Unknown, timeperiod: Unknown) -> Series[Any]\"\n    Missing keyword parameter \"series\"\n    Missing keyword parameter \"timeperiod\"\n      Position-only parameter mismatch; parameter \"series\" is not position-only\n      Position-only parameter mismatch; parameter \"timeperiod\" is not position-only\n      Position-only parameter mismatch; expected 2 but received 0\n      Function return type \"Series[Any] | NDArray[floating[Any]]\" is incompatible with type \"Series[Any]\"\n        Type \"Series[Any] | NDArray[floating[Any]]\" is not assignable to type \"Series[Any]\"\n ...", "rule": "reportAssignmentType", "severity": "error", "startCharacter": 20, - "startLine": 3930 + "startLine": 4006 }, { "endCharacter": 33, - "endLine": 3932, + "endLine": 4008, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Type \"(Series[Any] | NDArray[floating[Any]], int) -> (Series[Any] | NDArray[floating[Any]])\" is not assignable to declared type \"(series: Unknown, timeperiod: Unknown) -> Series[Any]\"\n  Type \"(Series[Any] | NDArray[floating[Any]], int) -> (Series[Any] | NDArray[floating[Any]])\" is not assignable to type \"(series: Unknown, timeperiod: Unknown) -> Series[Any]\"\n    Missing keyword parameter \"series\"\n    Missing keyword parameter \"timeperiod\"\n      Position-only parameter mismatch; parameter \"series\" is not position-only\n      Position-only parameter mismatch; parameter \"timeperiod\" is not position-only\n      Position-only parameter mismatch; expected 2 but received 0\n      Function return type \"Series[Any] | NDArray[floating[Any]]\" is incompatible with type \"Series[Any]\"\n        Type \"Series[Any] | NDArray[floating[Any]]\" is not assignable to type \"Series[Any]\"\n ...", "rule": "reportAssignmentType", "severity": "error", "startCharacter": 16, - "startLine": 3932 + "startLine": 4008 }, { "endCharacter": 35, - "endLine": 3978, + "endLine": 4054, "file": "quickadapter/user_data/strategies/Utils.py", "message": "\"to_numpy\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 27, - "startLine": 3978 + "startLine": 4054 }, { "endCharacter": 33, - "endLine": 3979, + "endLine": 4055, "file": "quickadapter/user_data/strategies/Utils.py", "message": "\"to_numpy\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 25, - "startLine": 3979 + "startLine": 4055 }, { "endCharacter": 37, - "endLine": 4096, + "endLine": 4172, "file": "quickadapter/user_data/strategies/Utils.py", "message": "\"to_numpy\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 29, - "startLine": 4096 + "startLine": 4172 }, { "endCharacter": 35, - "endLine": 4097, + "endLine": 4173, "file": "quickadapter/user_data/strategies/Utils.py", "message": "\"to_numpy\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 27, - "startLine": 4097 + "startLine": 4173 }, { "endCharacter": 33, - "endLine": 4098, + "endLine": 4174, "file": "quickadapter/user_data/strategies/Utils.py", "message": "\"to_numpy\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 25, - "startLine": 4098 + "startLine": 4174 }, { "endCharacter": 39, - "endLine": 4118, + "endLine": 4194, "file": "quickadapter/user_data/strategies/Utils.py", "message": "\"to_numpy\" is not a known attribute of \"None\"", "rule": "reportOptionalMemberAccess", "severity": "error", "startCharacter": 31, - "startLine": 4118 + "startLine": 4194 }, { "endCharacter": 92, - "endLine": 4477, + "endLine": 4553, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Type \"bool_ | bool\" is not assignable to return type \"bool\"\n  Type \"bool_ | bool\" is not assignable to type \"bool\"\n    \"numpy.bool[builtins.bool]\" is not assignable to \"bool\"", "rule": "reportReturnType", "severity": "error", "startCharacter": 15, - "startLine": 4477 + "startLine": 4553 }, { "endCharacter": 85, - "endLine": 4878, + "endLine": 4954, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Argument of type \"XGBoostPruningCallback\" cannot be assigned to parameter \"object\" of type \"RegressorCallback\" in function \"append\"\n  Type \"XGBoostPruningCallback\" is not assignable to type \"RegressorCallback\"\n    Type \"XGBoostPruningCallback\" is not assignable to type \"(...) -> Any\"\n    \"XGBoostPruningCallback\" is not assignable to \"TrainingCallback\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 16, - "startLine": 4878 + "startLine": 4954 }, { "endCharacter": 29, - "endLine": 4921, + "endLine": 4997, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Argument of type \"list[tuple[DataFrame, DataFrame]] | None\" cannot be assigned to parameter \"eval_set\" of type \"List[_LGBM_ScikitValidSet] | None\" in function \"fit\"\n  Type \"list[tuple[DataFrame, DataFrame]] | None\" is not assignable to type \"List[_LGBM_ScikitValidSet] | None\"\n    Type \"list[tuple[DataFrame, DataFrame]]\" is not assignable to type \"List[_LGBM_ScikitValidSet] | None\"\n      \"list[tuple[DataFrame, DataFrame]]\" is not assignable to \"List[_LGBM_ScikitValidSet]\"\n        Type parameter \"_T@list\" is invariant, but \"tuple[DataFrame, DataFrame]\" is not the same as \"_LGBM_ScikitValidSet\"\n        Consider switching from \"list\" to \"Sequence\" which is covariant\n      \"list[tuple[DataFrame, DataFrame]]\" is not assignable to \"None\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 21, - "startLine": 4921 + "startLine": 4997 }, { "endCharacter": 43, - "endLine": 4922, + "endLine": 4998, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Argument of type \"list[NDArray[floating[Any]]] | None\" cannot be assigned to parameter \"eval_sample_weight\" of type \"List[_LGBM_WeightType] | None\" in function \"fit\"\n  Type \"list[NDArray[floating[Any]]] | None\" is not assignable to type \"List[_LGBM_WeightType] | None\"\n    Type \"list[NDArray[floating[Any]]]\" is not assignable to type \"List[_LGBM_WeightType] | None\"\n      \"list[NDArray[floating[Any]]]\" is not assignable to \"List[_LGBM_WeightType]\"\n        Type parameter \"_T@list\" is invariant, but \"NDArray[floating[Any]]\" is not the same as \"_LGBM_WeightType\"\n        Consider switching from \"list\" to \"Sequence\" which is covariant\n      \"list[NDArray[floating[Any]]]\" is not assignable to \"None\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 31, - "startLine": 4922 + "startLine": 4998 }, { "endCharacter": 62, - "endLine": 4925, + "endLine": 5001, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Argument of type \"list[RegressorCallback] | None\" cannot be assigned to parameter \"callbacks\" of type \"List[(...) -> Unknown] | None\" in function \"fit\"\n  Type \"list[RegressorCallback] | None\" is not assignable to type \"List[(...) -> Unknown] | None\"\n    Type \"list[RegressorCallback]\" is not assignable to type \"List[(...) -> Unknown] | None\"\n      \"list[RegressorCallback]\" is not assignable to \"List[(...) -> Unknown]\"\n        Type parameter \"_T@list\" is invariant, but \"RegressorCallback\" is not the same as \"(...) -> Unknown\"\n        Consider switching from \"list\" to \"Sequence\" which is covariant\n      \"list[RegressorCallback]\" is not assignable to \"None\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 22, - "startLine": 4925 + "startLine": 5001 }, { "endCharacter": 35, - "endLine": 4947, + "endLine": 5023, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Object of type \"None\" is not subscriptable", "rule": "reportOptionalSubscript", "severity": "error", "startCharacter": 27, - "startLine": 4947 + "startLine": 5023 }, { "endCharacter": 35, - "endLine": 4993, + "endLine": 5069, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Object of type \"None\" is not subscriptable", "rule": "reportOptionalSubscript", "severity": "error", "startCharacter": 27, - "startLine": 4993 + "startLine": 5069 }, { "endCharacter": 49, - "endLine": 5063, + "endLine": 5139, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Argument of type \"CatBoostPruningCallback\" cannot be assigned to parameter \"object\" of type \"RegressorCallback\" in function \"append\"\n  Type \"CatBoostPruningCallback\" is not assignable to type \"RegressorCallback\"\n    Type \"CatBoostPruningCallback\" is not assignable to type \"(...) -> Any\"\n    \"CatBoostPruningCallback\" is not assignable to \"TrainingCallback\"", "rule": "reportArgumentType", "severity": "error", "startCharacter": 33, - "startLine": 5063 + "startLine": 5139 }, { "endCharacter": 75, - "endLine": 5306, + "endLine": 5382, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Operator \"<=\" not supported for \"None\"", "rule": "reportOptionalOperand", "severity": "error", "startCharacter": 54, - "startLine": 5306 + "startLine": 5382 }, { "endCharacter": 23, - "endLine": 5469, + "endLine": 5545, "file": "quickadapter/user_data/strategies/Utils.py", "message": "\"best_params\" is possibly unbound", "rule": "reportPossiblyUnboundVariable", "severity": "error", "startCharacter": 12, - "startLine": 5469 + "startLine": 5545 }, { "endCharacter": 38, - "endLine": 5476, + "endLine": 5552, "file": "quickadapter/user_data/strategies/Utils.py", "message": "\"best_params\" is possibly unbound", "rule": "reportPossiblyUnboundVariable", "severity": "error", "startCharacter": 27, - "startLine": 5476 + "startLine": 5552 }, { "endCharacter": 22, - "endLine": 5488, + "endLine": 5564, "file": "quickadapter/user_data/strategies/Utils.py", "message": "\"best_params\" is possibly unbound", "rule": "reportPossiblyUnboundVariable", "severity": "error", "startCharacter": 11, - "startLine": 5488 + "startLine": 5564 }, { "endCharacter": 22, - "endLine": 5646, + "endLine": 5722, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Declaration \"default_ranges\" is obscured by a declaration of the same name", "rule": "reportRedeclaration", "severity": "error", "startCharacter": 8, - "startLine": 5646 + "startLine": 5722 }, { "endCharacter": 14, - "endLine": 5682, + "endLine": 5758, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Declaration \"params\" is obscured by a declaration of the same name", "rule": "reportRedeclaration", "severity": "error", "startCharacter": 8, - "startLine": 5682 + "startLine": 5758 }, { "endCharacter": 22, - "endLine": 5766, + "endLine": 5842, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Declaration \"default_ranges\" is obscured by a declaration of the same name", "rule": "reportRedeclaration", "severity": "error", "startCharacter": 8, - "startLine": 5766 + "startLine": 5842 }, { "endCharacter": 14, - "endLine": 5800, + "endLine": 5876, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Declaration \"params\" is obscured by a declaration of the same name", "rule": "reportRedeclaration", "severity": "error", "startCharacter": 8, - "startLine": 5800 + "startLine": 5876 }, { "endCharacter": 22, - "endLine": 5871, + "endLine": 5947, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Declaration \"default_ranges\" is obscured by a declaration of the same name", "rule": "reportRedeclaration", "severity": "error", "startCharacter": 8, - "startLine": 5871 + "startLine": 5947 }, { "endCharacter": 22, - "endLine": 5972, + "endLine": 6048, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Declaration \"default_ranges\" is obscured by a declaration of the same name", "rule": "reportRedeclaration", "severity": "error", "startCharacter": 8, - "startLine": 5972 + "startLine": 6048 }, { "endCharacter": 26, - "endLine": 6055, + "endLine": 6131, "file": "quickadapter/user_data/strategies/Utils.py", "message": "Declaration \"default_ranges\" is obscured by a declaration of the same name", "rule": "reportRedeclaration", "severity": "error", "startCharacter": 12, - "startLine": 6055 + "startLine": 6131 } ], "filesAnalyzed": 5, diff --git a/quickadapter/README.md b/quickadapter/README.md index 8d13f9b..cf76ca1 100644 --- a/quickadapter/README.md +++ b/quickadapter/README.md @@ -201,10 +201,11 @@ and the [FreqAI parameter reference][freqai-parameters]. ### Exit pricing -| Path | Runtime fallback | Type / Range | Description | -| --------------------------------------------------- | ---------------- | ------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| exit_pricing.trade_natr_method | `moving_average` | enum {`moving_average`,`quantile_interpolation`,`weighted_average`} | Trade NATR (Normalized Average True Range) aggregation for stoploss and take-profit distances. `moving_average` uses KAMA to preserve nonnegative volatility. The stoploss never loosens, including after partial exits, and remains unchanged on order fills. | -| exit_pricing.final_take_profit_retracement_fraction | 0.25 | float (0,1] | Fraction of the final take-profit target distance used as the frozen trailing retracement distance after the final target arms the exit. The final exit tracks the best subsequent per-candle rate and exits only after this material adverse move; elapsed stagnation alone does not exit. Plot annotations show only the current trail boundary from the candle that established it; earlier boundaries are not retained. | +| Path | Runtime fallback | Type / Range | Description | +| -------------------------------------------------------------- | ---------------------- | ------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| exit_pricing.trade_natr_method | `moving_average` | enum {`moving_average`,`quantile_interpolation`,`weighted_average`} | Trade NATR (Normalized Average True Range) aggregation for stoploss and take-profit distances. `moving_average` uses KAMA to preserve nonnegative volatility. The stoploss never loosens, including after partial exits, and remains unchanged on order fills. | +| exit_pricing.final_take_profit_retracement_fraction | 0.5 | float (0,1] | Fraction of the final take-profit target distance used as the frozen trailing retracement distance after the final target arms the exit. The final exit tracks the best subsequent per-candle rate and exits only after this material adverse move; elapsed stagnation alone does not exit. Plot annotations show only the current trail boundary from the candle that established it; earlier boundaries are not retained. | +| exit_pricing.take_profit_stage_natr_multiplier_fraction_series | `fibonacci_extensions` | enum {`fibonacci_extensions`,`pi_extensions`} | Partial target distances from entry, as percentages of the full target distance: approximately 23.6%, 38.2% and 61.8% for `fibonacci_extensions`, or 3.2%, 10.1% and 31.8% for `pi_extensions`. Pi places partial targets closer to entry. Stake reduction percentages and the final target are unchanged. Invalid values use the default. | ### Reversal confirmation @@ -220,7 +221,7 @@ and the [FreqAI parameter reference][freqai-parameters]. | Path | Runtime fallback | Type / Range | Description | | ----------------------------------- | ---------------- | -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | freqai.fit_live_predictions_candles | 100 | int >= 1 | Required real-prediction calibration window per pair; also strategy startup candles and protection lookback basis. The template explicitly supplies 864. See [prediction warmup](#prediction-warmup-and-restarts). | -| freqai.regressor | `xgboost` | enum {`xgboost`,`lightgbm`,`histgradientboostingregressor`,`ngboost`,`catboost`} | Machine learning regressor. With validation and early stopping enabled, NGBoost uses the iteration with the best validation score for prediction and to determine the final refit size. | +| freqai.regressor | `xgboost` | enum {`xgboost`,`lightgbm`,`histgradientboostingregressor`,`ngboost`,`catboost`} | Machine learning regressor. Unknown names warn and fall back to `xgboost`. With validation and early stopping enabled, NGBoost uses the iteration with the best validation score for prediction and to determine the final refit size. | | freqai.continual_learning | false | bool | Continue training the deployed XGBoost, LightGBM or CPU CatBoost model. GPU CatBoost and other regressors train from scratch. With two-stage selection (see `test_size`), HPO and selection train from scratch; only the final refit continues the deployed model, adding the selected number of boosting rounds. Continued training reuses the saved feature and label transformations, including after restart. Missing or incompatible transformations or deployment metadata prevent continuation. Reset trained models or use a new `freqai.identifier` to change pipeline settings or recover from incompatible saved state. | ### Model training parameters @@ -288,7 +289,7 @@ and the [FreqAI parameter reference][freqai-parameters]. | Path | Runtime fallback | Type / Range | Description | | -------------------------------------------------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | freqai.feature_parameters.label_period_candles | min/max midpoint | int >= 1 | Zigzag labeling NATR period. | -| freqai.feature_parameters.label_horizon_candles | `label_period_candles` | int >= 1 | Conservative fixed purge horizon in candles: the magnitude of the causal guards' purge and of the default `timeseries_split` gap (see `causal_mode` for how the guards consume it). When unset, falls back to `label_period_candles`. | +| freqai.feature_parameters.label_horizon_candles | `label_period_candles` | int >= 1 | Conservative fixed purge horizon in candles: the magnitude of the causal guards' purge and of the default `timeseries_split` gap (see `causal_mode` for how the guards consume it). When unset or invalid, falls back to each pair's `label_period_candles`. … | | freqai.feature_parameters.causal_mode | true | bool | Causal split-guard master toggle. When `true` (default): (1) rejects `data_split_parameters.shuffle=true`, `feature_parameters.shuffle_after_split=true`, and `feature_parameters.reverse_train_test_order=true` (two of these rejections are independent of this toggle: `timeseries_split` rejects `shuffle_after_split` structurally, and an active holdout `test_size != 0` rejects all three at evaluation); (2) for `timeseries_split`, auto-sets `gap=label_horizon_candles` when `gap` is unset or `0` and rejects an explicit `gap= 1 | Minimum labeling NATR period used for reversals labeling HPO. | | freqai.feature_parameters.max_label_period_candles | 24 | int >= 1 | Maximum labeling NATR period used for reversals labeling HPO. | @@ -325,7 +326,7 @@ and the [FreqAI parameter reference][freqai-parameters]. | Path | Runtime fallback | Type / Range | Description | | ----------------------------------------------------------- | ---------------- | -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| freqai.optuna_hyperopt.enabled | false | bool | Enables regressor and dynamic label HPO. | +| freqai.optuna_hyperopt.enabled | false | bool | Enables regressor and dynamic label HPO. Active only when FreqAI is enabled and `test_size` is not 0. | | freqai.optuna_hyperopt.sampler | `tpe` | enum {`tpe`,`auto`} | HPO sampler algorithm for `hp` namespace. `tpe` uses [TPESampler](https://optuna.readthedocs.io/en/stable/reference/samplers/generated/optuna.samplers.TPESampler.html) with multivariate, group, and constant_liar (when multiple workers), `auto` uses [AutoSampler](https://hub.optuna.org/samplers/auto_sampler). | | freqai.optuna_hyperopt.label_sampler | `auto` | enum {`auto`,`tpe`,`nsgaii`,`nsgaiii`} | HPO sampler algorithm for multi-objective `label` namespace. `nsgaii` uses [NSGAIISampler](https://optuna.readthedocs.io/en/stable/reference/samplers/generated/optuna.samplers.NSGAIISampler.html), `nsgaiii` uses [NSGAIIISampler](https://optuna.readthedocs.io/en/stable/reference/samplers/generated/optuna.samplers.NSGAIIISampler.html). | | freqai.optuna_hyperopt.storage | `file` | enum {`file`,`sqlite`} | HPO storage backend. | diff --git a/quickadapter/tests/test_regressor_label_config.py b/quickadapter/tests/test_regressor_label_config.py index 4d0bff0..2a0d894 100644 --- a/quickadapter/tests/test_regressor_label_config.py +++ b/quickadapter/tests/test_regressor_label_config.py @@ -13,6 +13,7 @@ from unittest import mock import numpy as np import pandas as pd import QuickAdapterV3 as strategy_module +from freqtrade.enums import RunMode from LabelTransformer import ( CUSTOM_THRESHOLD_METHODS, PREDICTION_METHODS, @@ -20,7 +21,12 @@ from LabelTransformer import ( ) from qa_support import PAIR, QaTestCase, model_config, temporary_directory from QuickAdapterV3 import QuickAdapterV3 -from Utils import _OPTUNA_LABEL_SELECTION_SCHEMA_VERSION, _OPTUNA_NAMESPACES, enum_error_message +from Utils import ( + _OPTUNA_LABEL_SELECTION_SCHEMA_VERSION, + _OPTUNA_NAMESPACES, + DEFAULT_REGRESSOR, + REGRESSORS, +) import quickadapter.user_data.freqaimodels.QuickAdapterRegressorV3 as regressor_module from quickadapter.user_data.freqaimodels.QuickAdapterRegressorV3 import ( @@ -115,13 +121,133 @@ def json_roundtrip(metadata: dict) -> object: class RegressorLabelConfigTest(QaTestCase): - def test_unknown_label_method_raises_with_the_canonical_enum_message(self): - with self.assertRaises(ValueError) as caught: + def test_unknown_regressor_warns_before_preserving_the_canonical_fallback(self): + rejected = "unknown-regressor" + with temporary_directory() as temp: + config = model_config( + temp, + freqai={"regressor": rejected, "label_prediction": {"method": "thresholding"}}, + ) + with self.assertLogs(REGRESSOR_LOGGER, level="WARNING") as logs: + model = QuickAdapterRegressorV3(config=config) + self.assertEqual(model.regressor, DEFAULT_REGRESSOR) + self.assertEqual(config["freqai"]["regressor"], DEFAULT_REGRESSOR) + warning = "\n".join(record.getMessage() for record in logs.records) + for value in (rejected, DEFAULT_REGRESSOR, *REGRESSORS): + self.assertIn(value, warning) + + def test_startup_deduplicates_horizon_warnings_without_merging_pair_fallbacks(self): + pairs = [PAIR, "ETH/USDT", "SOL/USDT"] + for rejected in (0, True, None, [], {}): + with self.subTest(rejected=rejected), temporary_directory() as temp: + config = model_config( + temp, + exchange={"pair_whitelist": pairs}, + freqai={ + "optuna_hyperopt": {"enabled": False}, + "feature_parameters": { + "label_period_candles": 7, + "label_horizon_candles": rejected, + }, + "label_prediction": {"method": "thresholding"}, + }, + ) + config["runmode"] = RunMode.BACKTEST + with self.assertLogs(REGRESSOR_LOGGER, level="INFO") as initial: + model = QuickAdapterRegressorV3(config=config) + horizon_warnings = [ + record + for record in initial.records + if record.levelname == "WARNING" + and "label_horizon_candles" in record.getMessage() + ] + self.assertEqual(1, len(horizon_warnings)) + params = dict(model.get_optuna_params(pairs[1], _OPTUNA_NAMESPACES.label)) + params["label_period_candles"] = 13 + model.set_optuna_params(pairs[1], _OPTUNA_NAMESPACES.label, params) + for _ in range(2): + with self.assertLogs(REGRESSOR_LOGGER, level="INFO") as startup: + model._log_model_configuration() + horizon_warnings = [ + record + for record in startup.records + if record.levelname == "WARNING" + and "label_horizon_candles" in record.getMessage() + ] + self.assertEqual(2, len(horizon_warnings)) + messages = [record.getMessage() for record in startup.records] + for pair, horizon in zip(pairs, (7, 13, 7), strict=True): + pair_message = next( + message for message in messages if f"{pair}:" in message + ) + self.assertIn(f"label_horizon_candles={horizon}", pair_message) + with self.assertLogs(REGRESSOR_LOGGER, level="WARNING") as runtime: + horizons = [model._label_horizon_candles(pair) for pair in pairs] + self.assertEqual([7, 13, 7], horizons) + self.assertEqual(3, len(runtime.records)) + + def test_startup_reports_requested_hpo_separately_from_effective_activation(self): + for requested, freqai_enabled, test_size, active in ( + (False, True, 0.2, False), + (True, False, 0.2, False), + (True, True, 0, False), + (True, True, 0.2, True), + ): + with ( + self.subTest( + requested=requested, freqai_enabled=freqai_enabled, test_size=test_size + ), + temporary_directory() as temp, + ): + config = model_config( + temp, + freqai={ + "enabled": freqai_enabled, + "optuna_hyperopt": {"enabled": requested}, + "data_split_parameters": {"test_size": test_size}, + "label_prediction": {"method": "thresholding"}, + }, + ) + with self.assertLogs(REGRESSOR_LOGGER, level="INFO") as logs: + model = QuickAdapterRegressorV3(config=config) + self.assertEqual(model._optuna_hyperopt, active) + self.assertEqual(bool(model._optuna_label_candle), active) + fields = { + key.strip(): value.strip() + for record in logs.records + for key, separator, value in [record.getMessage().partition(":")] + if separator + } + self.assertEqual(fields["enabled"], str(requested)) + self.assertEqual(fields["active"], str(active)) + self.assertEqual("inactive_reason" in fields, not active) + self.assertEqual("n_trials" in fields, active) + + def test_inactive_requested_hpo_preserves_configured_label_validation(self): + for feature_parameters, error in ( + ({"label_method": "unknown-method"}, ValueError), + ({"label_p_order": "invalid-order"}, ValueError), + ({"label_weights": 7}, TypeError), + ): + with self.subTest(feature_parameters=feature_parameters), temporary_directory() as temp: + config = model_config( + temp, + freqai={ + "feature_parameters": feature_parameters, + "data_split_parameters": {"test_size": 0}, + "label_prediction": {"method": "thresholding"}, + }, + ) + config["runmode"] = RunMode.BACKTEST + with self.assertRaises(error): + QuickAdapterRegressorV3(config=config) + config["freqai"]["optuna_hyperopt"]["enabled"] = False + model = QuickAdapterRegressorV3(config=config) + self.assertFalse(model._optuna_hyperopt) + + def test_unknown_label_method_is_rejected(self): + with self.assertRaises(ValueError): label_regressor()._resolve_label_method_config("kde") - self.assertEqual( - str(caught.exception), - enum_error_message("label_method", "kde", QuickAdapterRegressorV3._SELECTION_METHODS), - ) def test_category_defaults_are_resolved_per_method(self): expected = { @@ -176,43 +302,25 @@ class RegressorLabelConfigTest(QaTestCase): with self.assertLogs(REGRESSOR_LOGGER, level="WARNING") as logs: resolved = model._resolve_label_method_config(method) self.assertEqual(resolved["distance_metric"], expected_default) - self.assertIn(f"Invalid {key} value {bad_metric!r}", "\n".join(logs.output)) + self.assertIn(bad_metric, "\n".join(record.getMessage() for record in logs.records)) def test_cluster_and_cluster_trial_selection_methods_are_fatal(self): for key in ("label_cluster_selection_method", "label_cluster_trial_selection_method"): with self.subTest(key=key): model = label_regressor(ft_params={key: "kalman"}) - with self.assertRaises(ValueError) as caught: + with self.assertRaises(ValueError): model._resolve_label_method_config("kmeans") - self.assertEqual( - str(caught.exception), - enum_error_message(key, "kalman", QuickAdapterRegressorV3._DISTANCE_METHODS), - ) def test_aggregate_metric_is_rejected_for_cluster_and_density_categories(self): aggregates = set(QuickAdapterRegressorV3._POWER_MEAN_MAP) | {"weighted_sum"} for aggregate in aggregates: - with self.subTest(aggregate=aggregate): - with self.assertRaises(ValueError) as caught: - QuickAdapterRegressorV3._validate_label_selection_metric( - aggregate, - ctx="label_cluster_metric", - default="euclidean", - aggregate_allowed=False, - mode="raise", - ) - self.assertEqual( - str(caught.exception), - enum_error_message( - "label_cluster_metric", - aggregate, - tuple( - candidate - for candidate in QuickAdapterRegressorV3._DISTANCE_METRICS - if candidate - in QuickAdapterRegressorV3._CLUSTER_DENSITY_DISTANCE_METRICS_SET - ), - ), + with self.subTest(aggregate=aggregate), self.assertRaises(ValueError): + QuickAdapterRegressorV3._validate_label_selection_metric( + aggregate, + ctx="label_cluster_metric", + default="euclidean", + aggregate_allowed=False, + mode="raise", ) def test_probability_metrics_are_rejected_for_cluster_and_density_categories(self): @@ -603,18 +711,10 @@ class RegressorLabelConfigTest(QaTestCase): model.optuna_create_sampler("auto") load_module.assert_called_once_with("samplers/auto_sampler") - def test_optuna_create_sampler_reports_a_missing_sampler_through_the_enum_message(self): + def test_optuna_create_sampler_rejects_a_missing_sampler(self): model = label_regressor(_optuna_config=optuna_config(sampler=None)) - with self.assertRaises(ValueError) as caught: + with self.assertRaises(ValueError): model.optuna_create_sampler(None) - self.assertEqual( - str(caught.exception), - enum_error_message( - "optuna sampler", - None, - QuickAdapterRegressorV3._OPTUNA_SAMPLERS, - ), - ) def test_optuna_create_sampler_refuses_an_unmapped_sampler_by_exhaustiveness(self): model = label_regressor(_optuna_config=optuna_config()) @@ -646,12 +746,8 @@ class RegressorLabelConfigTest(QaTestCase): def test_optuna_samplers_by_namespace_refuses_an_unknown_namespace(self): model = label_regressor(_optuna_config=optuna_config()) - with self.assertRaises(ValueError) as caught: + with self.assertRaises(ValueError): model.optuna_samplers_by_namespace("hp_label") - self.assertEqual( - str(caught.exception), - enum_error_message("namespace", "hp_label", _OPTUNA_NAMESPACES), - ) def test_candle_pool_full_is_centred_on_the_label_frequency(self): for frequency, expected in ( diff --git a/quickadapter/tests/test_strategy_features.py b/quickadapter/tests/test_strategy_features.py index 0a199c2..cfea3d2 100644 --- a/quickadapter/tests/test_strategy_features.py +++ b/quickadapter/tests/test_strategy_features.py @@ -3,6 +3,7 @@ import datetime import math import unittest +from itertools import pairwise from types import SimpleNamespace from unittest import mock @@ -13,6 +14,10 @@ from EnumErrors import enum_error_message from numpy.testing import assert_allclose from qa_support import PAIR, QaTestCase from QuickAdapterV3 import QuickAdapterV3 +from Utils import ( + TAKE_PROFIT_STAGE_NATR_MULTIPLIER_FRACTION_SERIES, + get_take_profit_stage_natr_multiplier_fractions, +) LONG = QuickAdapterV3._TRADE_LONG SHORT = QuickAdapterV3._TRADE_SHORT @@ -73,7 +78,7 @@ def candles( return frame -def strategy(label_period_candles=4, label_natr_multiplier=MULTIPLIER): +def strategy(label_period_candles=4, label_natr_multiplier=MULTIPLIER, fraction_series=None): """A candle-cache strategy whose label parameters come from the pair, not the frame.""" model = object.__new__(QuickAdapterV3) model._candle_deviation_cache = {} @@ -91,6 +96,10 @@ def strategy(label_period_candles=4, label_natr_multiplier=MULTIPLIER): "timeframe": TIMEFRAME, "exit_pricing": {"trade_natr_method": "quantile_interpolation"}, } + if fraction_series is not None: + model.config["exit_pricing"]["take_profit_stage_natr_multiplier_fraction_series"] = ( + fraction_series + ) return model @@ -529,10 +538,12 @@ class StrategyFeaturesTest(QaTestCase): long_trade = trade() # One candle of duration, a two-value trade NATR window and multiplier 3.0 make the # distance open_rate * 0.07 * 3.0 * fraction * log10(10), and log10(10) is exactly 1. - for stage, params in sorted(QuickAdapterV3.partial_exit_stages.items()): + for stage in sorted(QuickAdapterV3.partial_exit_stages): with self.subTest(stage=stage): - fraction = params[0] + fraction = model.partial_exit_stage_natr_multiplier_fractions[stage] self.assertIsInstance(fraction, float) + self.assertGreater(fraction, 0.0) + self.assertLess(fraction, 1.0) distance = 100.0 * 0.07 * MULTIPLIER * fraction target = model.get_take_profit_target(frame, long_trade, stage) self.assertIsNotNone(target) @@ -553,6 +564,120 @@ class StrategyFeaturesTest(QaTestCase): ] self.assertEqual(stages, sorted(stages)) + def test_the_fibonacci_extensions_define_the_default_partial_ladder(self): + # Frozen reference rungs, not a restatement of the formula: a change of + # base or of the stage count must fail here. They are phi**-3, phi**-2 + # and phi**-1 rounded to six decimals. + assert_allclose( + tuple(strategy().partial_exit_stage_natr_multiplier_fractions.values()), + (0.236068, 0.381966, 0.618034), + rtol=1e-6, + ) + self.assertEqual(strategy().get_exit_stage_natr_multiplier_fraction(99), 1.0) + + def test_every_series_raises_its_fraction_with_the_stage_index(self): + for series in TAKE_PROFIT_STAGE_NATR_MULTIPLIER_FRACTION_SERIES: + with self.subTest(series=series): + # Stage fractions follow stage indices, not insertion order. + reordered_stages = dict( + sorted(QuickAdapterV3.partial_exit_stages.items(), reverse=True) + ) + with mock.patch.object(QuickAdapterV3, "partial_exit_stages", reordered_stages): + model = strategy(fraction_series=series) + by_stage = model.partial_exit_stage_natr_multiplier_fractions + stages = sorted(QuickAdapterV3.partial_exit_stages) + self.assertEqual(sorted(by_stage), stages) + for earlier, later in pairwise(stages): + self.assertLess(by_stage[earlier], by_stage[later]) + + def test_no_series_ladder_reaches_the_final_stage_fraction(self): + for series in TAKE_PROFIT_STAGE_NATR_MULTIPLIER_FRACTION_SERIES: + with self.subTest(series=series): + fractions = tuple( + strategy( + fraction_series=series + ).partial_exit_stage_natr_multiplier_fractions.values() + ) + self.assertNotIn(1.0, fractions) + self.assertEqual(max(fractions), fractions[-1]) + + def test_the_ladder_is_indexed_by_the_stage_count(self): + self.assertEqual( + len(get_take_profit_stage_natr_multiplier_fractions("fibonacci_extensions", 0)), 0 + ) + for count in (1, 2, 3, 4, 5): + with self.subTest(count=count): + fractions = get_take_profit_stage_natr_multiplier_fractions( + "fibonacci_extensions", count + ) + self.assertEqual(len(fractions), count) + self.assertEqual(max(fractions), fractions[-1]) + + def test_the_pi_extensions_define_the_partial_ladder(self): + assert_allclose( + tuple( + strategy( + fraction_series="pi_extensions" + ).partial_exit_stage_natr_multiplier_fractions.values() + ), + (0.03225153, 0.10132118, 0.31830989), + rtol=1e-6, + ) + + def test_selecting_a_series_changes_the_stage_distances(self): + frame = candles([100.0, 100.0, 100.0], natr=[1.0, 4.0, 8.0], multiplier=MULTIPLIER) + long_trade = trade() + fibonacci = strategy(fraction_series="fibonacci_extensions") + pi = strategy(fraction_series="pi_extensions") + self.assertEqual(pi.take_profit_stage_natr_multiplier_fraction_series, "pi_extensions") + for stage in sorted(QuickAdapterV3.partial_exit_stages): + with self.subTest(stage=stage): + fibonacci_distance = fibonacci.get_take_profit_target(frame, long_trade, stage)[1] + pi_distance = pi.get_take_profit_target(frame, long_trade, stage)[1] + # The larger pi extension base yields smaller reciprocal fractions. + self.assertLess(pi_distance, fibonacci_distance) + # The final distance is independent of the partial extension series. + self.assertEqual( + fibonacci.get_take_profit_target(frame, long_trade, QuickAdapterV3._FINAL_EXIT_STAGE), + pi.get_take_profit_target(frame, long_trade, QuickAdapterV3._FINAL_EXIT_STAGE), + ) + + def test_fraction_series_validates_names_and_stage_counts(self): + with self.assertRaises(ValueError): + get_take_profit_stage_natr_multiplier_fractions("not_a_series", 3) + self.assertEqual( + get_take_profit_stage_natr_multiplier_fractions("fibonacci_extensions", 0), () + ) + assert_allclose( + get_take_profit_stage_natr_multiplier_fractions("fibonacci_extensions", 1), + (0.618033988749895,), + rtol=1e-14, + ) + + def test_a_degenerate_ladder_base_is_refused_by_name(self): + from Utils import _TAKE_PROFIT_STAGE_NATR_MULTIPLIER_FRACTION_BASES + + for name, base in (("zero", 0.0), ("unit", 1.0), ("near", 1.0 + 2**-52)): + with self.subTest(base=base): + _TAKE_PROFIT_STAGE_NATR_MULTIPLIER_FRACTION_BASES[name] = base + self.addCleanup(_TAKE_PROFIT_STAGE_NATR_MULTIPLIER_FRACTION_BASES.pop, name, None) + self.addCleanup(get_take_profit_stage_natr_multiplier_fractions.cache_clear) + with self.assertRaises(ValueError) as raised: + get_take_profit_stage_natr_multiplier_fractions(name, 3) + self.assertIn(name, str(raised.exception)) + + def test_an_underflowing_ladder_is_refused(self): + # pi**-650 is the smallest positive subnormal; pi**-651 underflows to 0.0. + fractions = get_take_profit_stage_natr_multiplier_fractions("pi_extensions", 650) + self.assertEqual(len(fractions), 650) + self.assertEqual(fractions[0], math.ulp(0.0)) + with self.assertRaises(ValueError): + get_take_profit_stage_natr_multiplier_fractions("pi_extensions", 651) + + def test_a_negative_partial_stage_count_is_rejected(self): + with self.assertRaises(ValueError): + get_take_profit_stage_natr_multiplier_fractions("fibonacci_extensions", -1) + def test_a_short_take_profit_target_flips_the_sign(self): model = strategy() frame = candles([100.0, 100.0, 100.0], natr=[1.0, 4.0, 8.0], multiplier=MULTIPLIER) diff --git a/quickadapter/tests/test_strategy_sizing.py b/quickadapter/tests/test_strategy_sizing.py index 0df66a1..d1ba224 100644 --- a/quickadapter/tests/test_strategy_sizing.py +++ b/quickadapter/tests/test_strategy_sizing.py @@ -295,9 +295,13 @@ class RuntimeSizingTest(QaTestCase): ) def test_partial_exit_reduces_stake_after_the_real_target_is_crossed(self): - for short, favorable, unfavorable in ((False, 120.0, 105.0), (True, 80.0, 95.0)): + for short in (False, True): with self.subTest(short=short): position = self.position(short) + target = self.model.get_take_profit_target(self.frame, position, 0)[0] + # Priced off the resolved target so the ladder remains a free parameter. + crossed = target + (-1.0 if short else 1.0) + below = position.open_rate store = {} # Isolate only database persistence, not target or sizing calculations. with ( @@ -315,12 +319,12 @@ class RuntimeSizingTest(QaTestCase): before = self.model.adjust_trade_position( position, self.now, - favorable, + crossed, 0.2, 1.0, 1000.0, - favorable, - unfavorable, + crossed, + below, 0.2, 0.2, ) @@ -328,16 +332,16 @@ class RuntimeSizingTest(QaTestCase): after = self.model.adjust_trade_position( position, self.now, - unfavorable, + below, 0.2, 1.0, 1000.0, - unfavorable, - favorable, + below, + crossed, 0.2, 0.2, ) - stake_reduction = -100.0 * QuickAdapterV3.partial_exit_stages[0][1] + stake_reduction = -100.0 * QuickAdapterV3.partial_exit_stages[0][0] direction = "short" if short else "long" self.assertEqual(after, (stake_reduction, f"take_profit_{direction}_0")) diff --git a/quickadapter/tests/test_utils_config_resolution.py b/quickadapter/tests/test_utils_config_resolution.py index 99a3d07..3addb11 100644 --- a/quickadapter/tests/test_utils_config_resolution.py +++ b/quickadapter/tests/test_utils_config_resolution.py @@ -23,10 +23,7 @@ from Utils import ( normalize_fit_live_predictions_config, ) -CANONICAL_EXIT_PRICING: dict[str, Any] = { - "trade_natr_method": "moving_average", - "final_take_profit_retracement_fraction": 0.25, -} +CANONICAL_EXIT_PRICING: dict[str, Any] = copy.deepcopy(DEFAULTS_EXIT_PRICING) CANONICAL_PROTECTIONS: dict[str, Any] = { "trade_duration_candles": 72, "lookback_period_fraction": 0.5, @@ -111,6 +108,29 @@ class UtilsConfigResolutionTest(QaTestCase): self.assertEqual(method, resolved["trade_natr_method"]) self.assertEqual([], sink) + def test_exit_pricing_unknown_fraction_series_falls_back_with_a_warning(self) -> None: + with ( + recorded_warnings() as (logger, _), + self.assertLogs(logger, level=logging.WARNING) as captured, + ): + resolved = get_exit_pricing_config( + {"take_profit_stage_natr_multiplier_fraction_series": "nope"}, logger + ) + self.assertEqual(CANONICAL_EXIT_PRICING, resolved) + self.assertEqual([logging.WARNING], [record.levelno for record in captured.records]) + + def test_exit_pricing_every_supported_fraction_series_is_preserved(self) -> None: + for series in ("fibonacci_extensions", "pi_extensions"): + with self.subTest(series=series): + with recorded_warnings() as (logger, sink): + resolved = get_exit_pricing_config( + {"take_profit_stage_natr_multiplier_fraction_series": series}, logger + ) + self.assertEqual( + series, resolved["take_profit_stage_natr_multiplier_fraction_series"] + ) + self.assertEqual([], sink) + def test_exit_pricing_retracement_fraction_range_is_half_open_at_zero(self) -> None: with recorded_warnings() as (logger, sink): self.assertEqual( @@ -123,18 +143,18 @@ class UtilsConfigResolutionTest(QaTestCase): for rejected in (0, 0.0, -0.5, None, True, "0.25"): with self.subTest(rejected=rejected): - with recorded_warnings() as (logger, sink): + with ( + recorded_warnings() as (logger, _), + self.assertLogs(logger, level=logging.WARNING) as captured, + ): resolved = get_exit_pricing_config( {"final_take_profit_retracement_fraction": rejected}, logger ) - self.assertEqual(0.25, resolved["final_take_profit_retracement_fraction"]) self.assertEqual( - [ - f"Invalid exit_pricing final_take_profit_retracement_fraction value " - f"{rejected!r}: must be a finite number > 0 <= 1, using default 0.25" - ], - sink, + DEFAULTS_EXIT_PRICING["final_take_profit_retracement_fraction"], + resolved["final_take_profit_retracement_fraction"], ) + self.assertEqual([logging.WARNING], [record.levelno for record in captured.records]) def test_exit_pricing_returns_a_fresh_dict_each_call(self) -> None: with recorded_warnings() as (logger, sink): diff --git a/quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py b/quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py index 0dc2276..5fd5220 100644 --- a/quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py +++ b/quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py @@ -48,8 +48,10 @@ from LabelTransformer import ( CUSTOM_THRESHOLD_METHODS, EXTREMA_SELECTION_METHODS, LABEL_WEIGHT_SUPPORT_POLICIES, + NORMALIZATION_TYPES, PREDICTION_METHODS, SKIMAGE_THRESHOLD_METHODS, + STANDARDIZATION_TYPES, ExtremaSelectionMethod, LabelTransformer, LabelWeightSupportPolicy, @@ -450,7 +452,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel): https://github.com/sponsors/robcaulk """ - version = "3.13.0-rc.13" + version = "3.13.0-rc.14" _CALIBRATION_START_KEY: Final[str] = "quickadapter_calibration_start" _DEPLOYMENT_COORDINATE_MARKER_KEY: Final[str] = "quickadapter_deployment_coordinates" @@ -1630,11 +1632,14 @@ class QuickAdapterRegressorV3(BaseRegressionModel): def _causal_mode(self) -> bool: return get_causal_mode(self.ft_params, logger) - def _label_horizon_candles(self, pair: str | None = None) -> int: - if pair is None: - return get_label_horizon_candles(self.ft_params, logger) - label_params = self.get_optuna_params(pair, _OPTUNA_NAMESPACES.label) - return get_label_horizon_candles({**self.ft_params, **label_params}, logger) + def _label_horizon_candles( + self, pair: str | None = None, *, warning_messages: set[str] | None = None + ) -> int: + config = self.ft_params + if pair is not None: + label_params = self.get_optuna_params(pair, _OPTUNA_NAMESPACES.label) + config = {**config, **label_params} + return get_label_horizon_candles(config, logger, warning_messages=warning_messages) @property def _optuna_label_candle_pool_full(self) -> list[int]: @@ -1721,6 +1726,11 @@ class QuickAdapterRegressorV3(BaseRegressionModel): self.regressor: Regressor = self.freqai_info.get("regressor", DEFAULT_REGRESSOR) if self.regressor not in set(REGRESSORS): + logger.warning( + "%s, using default %r", + enum_error_message("freqai.regressor", self.regressor, REGRESSORS), + DEFAULT_REGRESSOR, + ) self.regressor = DEFAULT_REGRESSOR self.freqai_info["regressor"] = self.regressor self._log_model_configuration() @@ -1733,10 +1743,89 @@ class QuickAdapterRegressorV3(BaseRegressionModel): logger.info(f"Model Version: {self.version}") logger.info(f"Regressor: {self.regressor}") + split_parameters = self.data_split_parameters + split_method = split_parameters.get( + "method", QuickAdapterRegressorV3.DATA_SPLIT_METHOD_DEFAULT + ) + logger.info("Data Split:") + logger.info(f" method: {split_method}") + if split_method == QuickAdapterRegressorV3._DATA_SPLIT_TIMESERIES: + test_size = split_parameters.get("test_size") + logger.info( + f" test_size: {format_number(test_size) if test_size is not None else 'dynamic (dataset size and n_splits)'}" + ) + logger.info( + f" n_splits: {split_parameters.get('n_splits', QuickAdapterRegressorV3.TIMESERIES_N_SPLITS_DEFAULT)}" + ) + gap = split_parameters.get("gap") + if gap is None: + gap = QuickAdapterRegressorV3.TIMESERIES_GAP_DEFAULT + if gap == 0: + gap_source = ( + "label_horizon_candles" if self._causal_mode else "label_period_candles" + ) + logger.info( + f" gap: automatic from per-pair {gap_source} (deferred until training)" + ) + else: + logger.info(f" gap: {gap}") + logger.info( + f" max_train_size: {split_parameters.get('max_train_size', QuickAdapterRegressorV3.TIMESERIES_MAX_TRAIN_SIZE_DEFAULT)}" + ) + else: + logger.info( + f" test_size: {format_number(split_parameters.get('test_size', QuickAdapterRegressorV3._TEST_SIZE))}" + ) + for key in ("train_size", "random_state", "stratify"): + if key in split_parameters: + logger.info(f" {key}: {split_parameters[key]}") + logger.info(f" validation_size: {format_number(self._get_validation_size())}") + + logger.info("Causality and Ordering:") + logger.info(f" causal_mode: {self._causal_mode}") + logger.info(f" shuffle: {split_parameters.get('shuffle', False)}") + logger.info(f" shuffle_after_split: {self.ft_params.get('shuffle_after_split', False)}") + logger.info( + f" reverse_train_test_order: {self.ft_params.get('reverse_train_test_order', False)}" + ) + + logger.info("Training:") + logger.info(f" continual_learning: {self.continual_learning}") + logger.info("Configured Model Training Overrides:") + logger.info(" These overrides are not final fitted, HPO-selected or refit parameters.") + logger.info( + f" model_training_parameters: {format_dict(self.model_training_parameters, style=_FORMAT_STYLE_DICT)}" + ) + logger.info("Optuna Hyperopt:") optuna_config = self._optuna_config logger.info(f" enabled: {optuna_config.get('enabled')}") + logger.info(f" active: {bool(self._optuna_hyperopt)}") + label_method = self.ft_params.get( + "label_method", QuickAdapterRegressorV3.LABEL_METHOD_DEFAULT + ) + label_config = {} + formatted_label_weights = None + label_p_order = None + label_p_order_config = self.ft_params.get("label_p_order") + # Preserve the configured-enabled validation boundary even when HPO is inactive. if optuna_config.get("enabled"): + label_config = self._resolve_label_method_config(label_method) + label_weights = self.ft_params.get("label_weights") + if label_weights is not None: + formatted_label_weights = [format_number(w) for w in label_weights] + if label_p_order_config is not None: + label_p_order = float(label_p_order_config) + + if not self._optuna_hyperopt: + if not optuna_config.get("enabled"): + inactive_reason = "configuration disabled" + elif not self.freqai_info.get("enabled", False): + inactive_reason = "FreqAI disabled" + else: + inactive_reason = "zero validation/holdout size" + logger.info(f" inactive_reason: {inactive_reason}") + else: logger.info(f" n_jobs: {optuna_config.get('n_jobs')}") logger.info(f" sampler: {optuna_config.get('sampler')}") logger.info(f" storage: {optuna_config.get('storage')}") @@ -1759,24 +1848,18 @@ class QuickAdapterRegressorV3(BaseRegressionModel): logger.info(f" label_sampler: {optuna_config.get('label_sampler')}") logger.info(f" label_candles_step: {optuna_config.get('label_candles_step')}") - label_method = self.ft_params.get( - "label_method", QuickAdapterRegressorV3.LABEL_METHOD_DEFAULT - ) + logger.info(f" label_method: {label_method}") - label_config = self._resolve_label_method_config(label_method) QuickAdapterRegressorV3._log_label_method_config(label_config) - label_weights = self.ft_params.get("label_weights") - if label_weights is not None: - formatted_label_weights = [format_number(w) for w in label_weights] + if formatted_label_weights is not None: logger.info(f" label_weights: [{', '.join(formatted_label_weights)}]") else: logger.info(" label_weights: [1.0, ...] * n_objectives, l1 normalized (default)") - label_p_order_config = self.ft_params.get("label_p_order") if label_p_order_config is not None: - logger.info(f" label_p_order: {format_number(float(label_p_order_config))}") + logger.info(f" label_p_order: {format_number(label_p_order)}") else: distance_metric = label_config["distance_metric"] if distance_metric in { @@ -1800,17 +1883,21 @@ class QuickAdapterRegressorV3(BaseRegressionModel): ) logger.info(" Pipeline:") logger.info(f" standardization: {col_pipeline['standardization']}") - logger.info( - f" robust_quantiles: ({format_number(col_pipeline['robust_quantiles'][0])}, {format_number(col_pipeline['robust_quantiles'][1])})" - ) - logger.info( - f" mmad_scaling_factor: {format_number(col_pipeline['mmad_scaling_factor'])}" - ) + if col_pipeline["standardization"] == STANDARDIZATION_TYPES[2]: # "robust" + logger.info( + f" robust_quantiles: ({format_number(col_pipeline['robust_quantiles'][0])}, {format_number(col_pipeline['robust_quantiles'][1])})" + ) + elif col_pipeline["standardization"] == STANDARDIZATION_TYPES[3]: # "mmad" + logger.info( + f" mmad_scaling_factor: {format_number(col_pipeline['mmad_scaling_factor'])}" + ) logger.info(f" normalization: {col_pipeline['normalization']}") - logger.info( - f" minmax_range: ({format_number(col_pipeline['minmax_range'][0])}, {format_number(col_pipeline['minmax_range'][1])})" - ) - logger.info(f" sigmoid_scale: {format_number(col_pipeline['sigmoid_scale'])}") + if col_pipeline["normalization"] == NORMALIZATION_TYPES[1]: # "minmax" + logger.info( + f" minmax_range: ({format_number(col_pipeline['minmax_range'][0])}, {format_number(col_pipeline['minmax_range'][1])})" + ) + elif col_pipeline["normalization"] == NORMALIZATION_TYPES[2]: # "sigmoid" + logger.info(f" sigmoid_scale: {format_number(col_pipeline['sigmoid_scale'])}") logger.info(f" gamma: {format_number(col_pipeline['gamma'])}") col_prediction = get_label_column_config( @@ -1818,15 +1905,22 @@ class QuickAdapterRegressorV3(BaseRegressionModel): ) logger.info(" Prediction:") logger.info(f" method: {col_prediction['method']}") - logger.info(f" selection_method: {col_prediction['selection_method']}") - logger.info(f" threshold_method: {col_prediction['threshold_method']}") - logger.info( - f" outlier_quantile: {format_number(col_prediction['outlier_quantile'])}" - ) - logger.info( - f" soft_extremum_alpha: {format_number(col_prediction['soft_extremum_alpha'])}" - ) - logger.info(f" keep_fraction: {format_number(col_prediction['keep_fraction'])}") + if col_prediction["method"] == PREDICTION_METHODS[1]: # "thresholding" + logger.info(f" selection_method: {col_prediction['selection_method']}") + logger.info(f" threshold_method: {col_prediction['threshold_method']}") + logger.info( + f" outlier_quantile: {format_number(col_prediction['outlier_quantile'])}" + ) + if ( + col_prediction["threshold_method"] == CUSTOM_THRESHOLD_METHODS[1] + ): # "soft_extremum" + logger.info( + f" soft_extremum_alpha: {format_number(col_prediction['soft_extremum_alpha'])}" + ) + if col_prediction["selection_method"] in EXTREMA_SELECTION_METHODS[:2]: + logger.info( + f" keep_fraction: {format_number(col_prediction['keep_fraction'])}" + ) if col_prediction["method"] == PREDICTION_METHODS[0]: # "none" logger.warning( f" Prediction method is 'none' for label [{label_col}]: " @@ -1834,53 +1928,41 @@ class QuickAdapterRegressorV3(BaseRegressionModel): f"entry signals based on them will never trigger." ) - default_label_period_candles, default_label_natr_multiplier = self._label_defaults - label_period_candles = self.ft_params.get( - "label_period_candles", default_label_period_candles - ) - label_natr_multiplier = float( - self.ft_params.get("label_natr_multiplier", default_label_natr_multiplier) - ) - logger.info("Label Hyperparameters:") + logger.info("Label Parameters:") logger.info(f" fit_live_predictions_candles: {self._fit_live_predictions_candles}") - if self._optuna_hyperopt: - logger.info(f" label_period_candles: {label_period_candles} (initial value)") + horizon_warning_messages: set[str] = set() + for pair in self.pairs: + params = self.get_optuna_params(pair, _OPTUNA_NAMESPACES.label) + horizon = self._label_horizon_candles(pair, warning_messages=horizon_warning_messages) logger.info( - f" label_natr_multiplier: {format_number(label_natr_multiplier)} (initial value)" + f" {pair}: label_period_candles={params.get('label_period_candles')}, " + f"label_natr_multiplier={format_number(params.get('label_natr_multiplier'))}, " + f"label_horizon_candles={horizon} (resolved at startup)" ) - logger.info(f" label_frequency_candles: {self._label_frequency_candles}") - logger.info(f" min_label_period_candles: {self._min_label_period_candles}") - logger.info(f" max_label_period_candles: {self._max_label_period_candles}") - logger.info( - f" min_label_natr_multiplier: {format_number(self._min_label_natr_multiplier)}" - ) - logger.info( - f" max_label_natr_multiplier: {format_number(self._max_label_natr_multiplier)}" - ) + label_frequency_candles = self._label_frequency_candles if self._optuna_hyperopt: - logger.info("Label Parameters:") - for pair in self.pairs: - params = self._optuna_label_params.get(pair, {}) - if params: - logger.info( - f" {pair}: label_period_candles={params.get('label_period_candles')}, " - f"label_natr_multiplier={format_number(params.get('label_natr_multiplier'))}, " - f"label_horizon_candles={self._label_horizon_candles(pair)}" - ) - else: - logger.info("Label Parameters:") - logger.info(f" label_period_candles: {label_period_candles}") - logger.info(f" label_natr_multiplier: {format_number(label_natr_multiplier)}") - logger.info(f" label_horizon_candles: {self._label_horizon_candles()}") + logger.info("Label Hyperparameters:") + logger.info(f" label_frequency_candles: {label_frequency_candles}") + logger.info(f" min_label_period_candles: {self._min_label_period_candles}") + logger.info(f" max_label_period_candles: {self._max_label_period_candles}") + logger.info( + f" min_label_natr_multiplier: {format_number(self._min_label_natr_multiplier)}" + ) + logger.info( + f" max_label_natr_multiplier: {format_number(self._max_label_natr_multiplier)}" + ) scaler = self.ft_params.get("scaler", QuickAdapterRegressorV3.SCALER_DEFAULT) feature_range = self.ft_params.get("range", QuickAdapterRegressorV3.RANGE_DEFAULT) - logger.info("Feature Parameters:") - logger.info(f" scaler: {scaler}") - logger.info( + # Preserve range resolution independently of whether the selected scaler uses it. + formatted_feature_range = ( f" range: ({format_number(feature_range[0])}, {format_number(feature_range[1])})" ) + logger.info("Feature Parameters:") + logger.info(f" scaler: {scaler}") + if scaler == QuickAdapterRegressorV3.SCALER_DEFAULT: # "minmax" + logger.info(formatted_feature_range) logger.info("=" * 60) diff --git a/quickadapter/user_data/strategies/QuickAdapterV3.py b/quickadapter/user_data/strategies/QuickAdapterV3.py index 5388032..77e3eba 100644 --- a/quickadapter/user_data/strategies/QuickAdapterV3.py +++ b/quickadapter/user_data/strategies/QuickAdapterV3.py @@ -27,6 +27,7 @@ from freqtrade.strategy.interface import IStrategy from freqtrade.util import FtPrecise from LabelTransformer import ( COMBINED_AGGREGATIONS, + FILL_BANDWIDTHS, FILL_METHODS, SMOOTHING_METHOD_MODES, SMOOTHING_METHODS, @@ -77,6 +78,7 @@ from Utils import ( get_ma_fn, get_reversal_confirmation_config, get_smoothing_kernel_half_width, + get_take_profit_stage_natr_multiplier_fractions, is_finite_number, label_known_at_lookahead_column_name, label_weight_column_name, @@ -218,7 +220,7 @@ class QuickAdapterV3(IStrategy): _ANNOTATION_LINE_OFFSET_CANDLES: Final[int] = 10 def version(self) -> str: - return "3.13.0-rc.13" + return "3.13.0-rc.14" timeframe = "5m" timeframe_minutes = timeframe_to_minutes(timeframe) @@ -228,11 +230,14 @@ class QuickAdapterV3(IStrategy): position_adjustment_enable = True - # {stage: (natr_multiplier_fraction, stake_percent, color)} - partial_exit_stages: ClassVar[dict[int, tuple[float, float, str]]] = { - 0: (0.4858, 0.4, "lime"), - 1: (0.6180, 0.3, "yellow"), - 2: (0.7640, 0.2, "coral"), + # {stage: (stake_percent, color)}. The reciprocal-extension fractions are + # not literals here: they resolve from the configured + # ``exit_pricing.take_profit_stage_natr_multiplier_fraction_series`` ladder, + # which is their single source of truth. The final rung stays the literal. + partial_exit_stages: ClassVar[dict[int, tuple[float, str]]] = { + 0: (0.4, "lime"), + 1: (0.3, "yellow"), + 2: (0.2, "coral"), } # (natr_multiplier_fraction, stake_percent, color) @@ -245,6 +250,11 @@ class QuickAdapterV3(IStrategy): # Final full-exit stage, derived from the configured partial exits. _FINAL_EXIT_STAGE: Final[int] = max(partial_exit_stages.keys(), default=-1) + 1 + # ``get_trade_exit_stage`` derives a stage from the number of filled take-profit + # exits, so every stage it can reach must exist here. A gap would price a + # missing stage at the final rung instead of arming it. + assert sorted(partial_exit_stages) == list(range(len(partial_exit_stages))) + _TAKE_PROFIT_ORDER_TAG_PREFIX: Final[str] = "take_profit_" _FINAL_TAKE_PROFIT_STATE_KEY: Final[str] = "final_take_profit_state" _FINAL_TAKE_PROFIT_STATE_VERSION: Final[int] = 3 @@ -436,6 +446,26 @@ class QuickAdapterV3(IStrategy): def final_take_profit_retracement_fraction(self) -> float: return float(self.exit_pricing["final_take_profit_retracement_fraction"]) + @property + def take_profit_stage_natr_multiplier_fraction_series(self) -> str: + return str(self.exit_pricing["take_profit_stage_natr_multiplier_fraction_series"]) + + @cached_property + def partial_exit_stage_natr_multiplier_fractions(self) -> dict[int, float]: + """Reciprocal-extension fractions of the full take-profit distance, by stage.""" + fractions = get_take_profit_stage_natr_multiplier_fractions( + self.take_profit_stage_natr_multiplier_fraction_series, + len(QuickAdapterV3.partial_exit_stages), + ) + return dict(zip(sorted(QuickAdapterV3.partial_exit_stages), fractions, strict=True)) + + def get_exit_stage_natr_multiplier_fraction(self, exit_stage: int) -> float: + """Return a partial reciprocal-extension fraction or the final fraction ``1.0``.""" + fractions = self.partial_exit_stage_natr_multiplier_fractions + if exit_stage in fractions: + return fractions[exit_stage] + return QuickAdapterV3._FINAL_EXIT_STAGE_PARAMS[0] + @cached_property def reversal_confirmation(self) -> dict[str, int | float]: return get_reversal_confirmation_config(self.config.get("reversal_confirmation"), logger) @@ -511,12 +541,19 @@ class QuickAdapterV3(IStrategy): self._log_strategy_configuration() def _log_strategy_configuration(self) -> None: + label_weighting = self.label_weighting + label_smoothing = self.label_smoothing + reversal_confirmation = self.reversal_confirmation + trade_natr_method = self.trade_natr_method + final_take_profit_retracement_fraction = self.final_take_profit_retracement_fraction + fraction_series = self.take_profit_stage_natr_multiplier_fraction_series + partial_exit_stage_fractions = self.partial_exit_stage_natr_multiplier_fractions + protections = self.protections + logger.info("=" * 60) logger.info("QuickAdapter Strategy Configuration") logger.info("=" * 60) - label_weighting = self.label_weighting - label_smoothing = self.label_smoothing for label_col in LABEL_COLUMNS: logger.info(f"Label [{label_col}]:") @@ -525,62 +562,72 @@ class QuickAdapterV3(IStrategy): ) logger.info(" Weighting:") logger.info(f" strategy: {col_weighting['strategy']}") - logger.info( - f" metric_coefficients: {format_dict(col_weighting['metric_coefficients'], style=_FORMAT_STYLE_DICT)}" - ) - logger.info(f" aggregation: {col_weighting['aggregation']}") - if col_weighting["aggregation"] == COMBINED_AGGREGATIONS[5]: # "softmax" - logger.info( - f" softmax_temperature: {format_number(col_weighting['softmax_temperature'])}" - ) - fill_method = col_weighting["fill_method"] - logger.info(f" fill_method: {fill_method}") - if fill_method in ( - QuickAdapterV3._FILL_EPSILON, - QuickAdapterV3._FILL_EPSILON_GAUSSIAN, - ): - logger.info(f" fill_epsilon: {format_number(col_weighting['fill_epsilon'])}") - logger.info(f" fill_epsilon_baseline: {col_weighting['fill_epsilon_baseline']}") - if fill_method in ( - QuickAdapterV3._FILL_GAUSSIAN, - QuickAdapterV3._FILL_EPSILON_GAUSSIAN, - ): - logger.info( - f" fill_sigma_candles: {format_number(col_weighting['fill_sigma_candles'])}" - ) + if col_weighting["strategy"] != QuickAdapterV3._WEIGHT_NONE: + if col_weighting["strategy"] == WEIGHT_STRATEGIES[8]: # "combined" + logger.info( + f" metric_coefficients: {format_dict(col_weighting['metric_coefficients'], style=_FORMAT_STYLE_DICT)}" + ) + logger.info(f" aggregation: {col_weighting['aggregation']}") + if col_weighting["aggregation"] == COMBINED_AGGREGATIONS[5]: # "softmax" + logger.info( + f" softmax_temperature: {format_number(col_weighting['softmax_temperature'])}" + ) + fill_method = col_weighting["fill_method"] + logger.info(f" fill_method: {fill_method}") + if fill_method in ( + QuickAdapterV3._FILL_EPSILON, + QuickAdapterV3._FILL_EPSILON_GAUSSIAN, + ): + logger.info(f" fill_epsilon: {format_number(col_weighting['fill_epsilon'])}") + logger.info( + f" fill_epsilon_baseline: {col_weighting['fill_epsilon_baseline']}" + ) + if fill_method in ( + QuickAdapterV3._FILL_GAUSSIAN, + QuickAdapterV3._FILL_EPSILON_GAUSSIAN, + ): + logger.info( + f" fill_sigma_candles: {format_number(col_weighting['fill_sigma_candles'])}" + ) + logger.info(f" fill_bandwidth: {col_weighting['fill_bandwidth']}") + if col_weighting["fill_bandwidth"] == FILL_BANDWIDTHS[1]: # "knn" + logger.info( + f" fill_sigma_min_candles: {format_number(col_weighting['fill_sigma_min_candles'])}" + ) + logger.info( + f" fill_bandwidth_neighbors: {col_weighting['fill_bandwidth_neighbors']}" + ) + logger.info( + f" fill_bandwidth_alpha: {format_number(col_weighting['fill_bandwidth_alpha'])}" + ) + logger.info(f" support_policy: {col_weighting['support_policy']}") logger.info( - f" fill_sigma_min_candles: {format_number(col_weighting['fill_sigma_min_candles'])}" + f" min_pivot_equivalent_count: {col_weighting['min_pivot_equivalent_count']}" ) - logger.info(f" fill_bandwidth: {col_weighting['fill_bandwidth']}") logger.info( - f" fill_bandwidth_neighbors: {col_weighting['fill_bandwidth_neighbors']}" + f" min_positive_label_weight_fraction: {format_number(col_weighting['min_positive_label_weight_fraction'])}" ) logger.info( - f" fill_bandwidth_alpha: {format_number(col_weighting['fill_bandwidth_alpha'])}" + f" min_effective_sample_size: {format_number(col_weighting['min_effective_sample_size'])}" ) - logger.info(f" support_policy: {col_weighting['support_policy']}") - logger.info( - f" min_pivot_equivalent_count: {col_weighting['min_pivot_equivalent_count']}" - ) - logger.info( - f" min_positive_label_weight_fraction: {format_number(col_weighting['min_positive_label_weight_fraction'])}" - ) - logger.info( - f" min_effective_sample_size: {format_number(col_weighting['min_effective_sample_size'])}" - ) col_smoothing = get_label_column_config( label_col, label_smoothing["default"], label_smoothing["columns"] ) logger.info(" Smoothing:") - logger.info(f" method: {col_smoothing['method']}") - logger.info(f" window_candles: {col_smoothing['window_candles']}") - logger.info(f" beta: {format_number(col_smoothing['beta'])}") - logger.info(f" polyorder: {col_smoothing['polyorder']}") - logger.info(f" mode: {col_smoothing['mode']}") - logger.info(f" sigma: {format_number(col_smoothing['sigma'])}") - method = col_smoothing["method"] + logger.info(f" method: {method}") + if method != SMOOTHING_METHODS[0]: # "none" + logger.info(f" window_candles: {col_smoothing['window_candles']}") + if method in SMOOTHING_METHODS[2:4]: # Kaiser families + logger.info(f" beta: {format_number(col_smoothing['beta'])}") + if method == QuickAdapterV3._SMOOTHING_SAVGOL: + logger.info(f" polyorder: {col_smoothing['polyorder']}") + if method in SMOOTHING_METHOD_MODES: + logger.info(f" mode: {col_smoothing['mode']}") + if method == SMOOTHING_METHODS[8]: # "gaussian_filter1d" + logger.info(f" sigma: {format_number(col_smoothing['sigma'])}") + if col_weighting["strategy"] != QuickAdapterV3._WEIGHT_NONE and ( method == QuickAdapterV3._SMOOTHING_SMM or (method == QuickAdapterV3._SMOOTHING_SAVGOL and col_smoothing["polyorder"] >= 2) @@ -599,24 +646,23 @@ class QuickAdapterV3(IStrategy): logger.info("Reversal Confirmation:") logger.info( - f" lookback_period_candles: {self.reversal_confirmation['lookback_period_candles']}" + f" lookback_period_candles: {reversal_confirmation['lookback_period_candles']}" ) + logger.info(f" decay_fraction: {format_number(reversal_confirmation['decay_fraction'])}") logger.info( - f" decay_fraction: {format_number(self.reversal_confirmation['decay_fraction'])}" + f" min_natr_multiplier_fraction: {format_number(reversal_confirmation['min_natr_multiplier_fraction'])}" ) logger.info( - f" min_natr_multiplier_fraction: {format_number(self.reversal_confirmation['min_natr_multiplier_fraction'])}" - ) - logger.info( - f" max_natr_multiplier_fraction: {format_number(self.reversal_confirmation['max_natr_multiplier_fraction'])}" + f" max_natr_multiplier_fraction: {format_number(reversal_confirmation['max_natr_multiplier_fraction'])}" ) logger.info("Exit Pricing:") - logger.info(f" trade_natr_method: {self.trade_natr_method}") + logger.info(f" trade_natr_method: {trade_natr_method}") logger.info( " final_take_profit_retracement_fraction: " - f"{format_number(self.final_take_profit_retracement_fraction)}" + f"{format_number(final_take_profit_retracement_fraction)}" ) + logger.info(f" take_profit_stage_natr_multiplier_fraction_series: {fraction_series}") logger.info("Custom Stoploss:") logger.info( @@ -625,21 +671,25 @@ class QuickAdapterV3(IStrategy): logger.info("Partial Take-Profit Stages:") for stage, ( - natr_multiplier_fraction, stake_percent, color, ) in QuickAdapterV3.partial_exit_stages.items(): logger.info( - f" stage {stage}: natr_multiplier_fraction={format_number(natr_multiplier_fraction)}, stake_percent={format_number(stake_percent)}, color={color}" + f" stage {stage}: natr_multiplier_fraction=" + f"{format_number(partial_exit_stage_fractions[stage])}, " + f"stake_percent={format_number(stake_percent)}, color={color}" ) + logger.info("Final Exit:") logger.info( - f"Final Exit: natr_multiplier_fraction={format_number(QuickAdapterV3._FINAL_EXIT_STAGE_PARAMS[0])}, stake_percent={format_number(QuickAdapterV3._FINAL_EXIT_STAGE_PARAMS[1])}, color={QuickAdapterV3._FINAL_EXIT_STAGE_PARAMS[2]}" + f" natr_multiplier_fraction: {format_number(QuickAdapterV3._FINAL_EXIT_STAGE_PARAMS[0])}" ) + logger.info(f" stake_percent: {format_number(QuickAdapterV3._FINAL_EXIT_STAGE_PARAMS[1])}") + logger.info(f" color: {QuickAdapterV3._FINAL_EXIT_STAGE_PARAMS[2]}") logger.info("Protections:") - if self.protections: - for protection in self.protections: + if protections: + for protection in protections: method = protection.get("method", "Unknown") protection_params = {k: v for k, v in protection.items() if k != "method"} logger.info( @@ -1453,11 +1503,7 @@ class QuickAdapterV3(IStrategy): def get_take_profit_target( self, df: DataFrame, trade: Trade, exit_stage: int ) -> tuple[float, float] | None: - natr_multiplier_fraction = ( - QuickAdapterV3.partial_exit_stages[exit_stage][0] - if exit_stage in QuickAdapterV3.partial_exit_stages - else QuickAdapterV3._FINAL_EXIT_STAGE_PARAMS[0] - ) + natr_multiplier_fraction = self.get_exit_stage_natr_multiplier_fraction(exit_stage) take_profit_distance = self.get_take_profit_distance(df, trade, natr_multiplier_fraction) if not is_finite_number(take_profit_distance) or take_profit_distance <= 0: return None @@ -1880,7 +1926,7 @@ class QuickAdapterV3(IStrategy): ), ) if trade_partial_exit: - trade_stake_percent = QuickAdapterV3.partial_exit_stages[trade_exit_stage][1] + trade_stake_percent = QuickAdapterV3.partial_exit_stages[trade_exit_stage][0] trade_partial_stake_amount = trade_stake_percent * trade.stake_amount tag = ( f"{QuickAdapterV3._TAKE_PROFIT_ORDER_TAG_PREFIX}" @@ -2550,7 +2596,7 @@ class QuickAdapterV3(IStrategy): "end": end_date, "y_start": partial_take_profit_price, "y_end": partial_take_profit_price, - "color": QuickAdapterV3.partial_exit_stages[take_profit_stage][2], + "color": QuickAdapterV3.partial_exit_stages[take_profit_stage][1], "line_style": "solid", "width": 1, "label": f"Partial Take-Profit Stage {take_profit_stage}", diff --git a/quickadapter/user_data/strategies/Utils.py b/quickadapter/user_data/strategies/Utils.py index 82965d7..a808e03 100644 --- a/quickadapter/user_data/strategies/Utils.py +++ b/quickadapter/user_data/strategies/Utils.py @@ -1041,6 +1041,70 @@ TRADE_NATR_METHODS: Final[tuple[TradeNatrMethod, ...]] = ( ) +TakeProfitStageNatrMultiplierFractionSeries = Literal["fibonacci_extensions", "pi_extensions"] +TAKE_PROFIT_STAGE_NATR_MULTIPLIER_FRACTION_SERIES: Final[ + tuple[TakeProfitStageNatrMultiplierFractionSeries, ...] +] = ( + "fibonacci_extensions", + "pi_extensions", +) + +# Extension bases: stage ``k`` of ``n`` uses the reciprocal of ``base ** (n - k)``. +# These fractions place partial targets inside the full take-profit distance; +# they do not extend it. Fibonacci uses integer powers of phi; pi uses integer +# powers of pi as a custom geometric construction, not a trading convention. +# The final fraction ``1.0`` is the separate ``_FINAL_EXIT_STAGE_PARAMS`` +# literal. +_TAKE_PROFIT_STAGE_NATR_MULTIPLIER_FRACTION_BASES: Final[dict[str, float]] = { + "fibonacci_extensions": (1.0 + math.sqrt(5.0)) / 2.0, + "pi_extensions": math.pi, +} + +# The closest partial fraction is 1 / base; a 1.5 floor keeps it at most 2/3. +# This relative gap does not guarantee distinct prices after rounding. +_TAKE_PROFIT_STAGE_NATR_MULTIPLIER_FRACTION_MIN_BASE: Final[float] = 1.5 + + +@lru_cache(maxsize=_CACHE_MAXSIZE_SMALL) +def get_take_profit_stage_natr_multiplier_fractions( + series: str, partial_stage_count: int +) -> tuple[float, ...]: + """Return ascending reciprocal-extension fractions for partial take-profit targets. + + Stage ``k`` of ``n`` uses ``1 / base ** (n - k)``. Each fraction scales + the full take-profit distance and remains strictly below the independent + final fraction ``1.0``. + """ + base = _TAKE_PROFIT_STAGE_NATR_MULTIPLIER_FRACTION_BASES.get(series) + if base is None: + raise ValueError( + enum_error_message( + "take_profit_stage_natr_multiplier_fraction_series", + series, + TAKE_PROFIT_STAGE_NATR_MULTIPLIER_FRACTION_SERIES, + ) + ) + if partial_stage_count < 0: + raise ValueError(f"Invalid partial_stage_count value {partial_stage_count}: must be >= 0") + if base < _TAKE_PROFIT_STAGE_NATR_MULTIPLIER_FRACTION_MIN_BASE: + raise ValueError( + f"Invalid base value {base!r} for series {series!r}: " + f"must be >= {_TAKE_PROFIT_STAGE_NATR_MULTIPLIER_FRACTION_MIN_BASE}" + ) + + fractions = tuple( + base ** -(partial_stage_count - stage) for stage in range(partial_stage_count) + ) + if any(fraction <= 0.0 for fraction in fractions): + # Only reachable by denormal underflow: the base floor above guarantees + # ``base > 1``, so no rung can reach or exceed ``1.0``. + raise ValueError( + f"Invalid partial_stage_count value {partial_stage_count!r} for series {series!r}: " + "NATR multiplier fractions must be > 0 (underflow to zero)" + ) + return fractions + + SPARSE_TRAINING_MASS_THRESHOLD: Final[float] = 0.05 DEFAULT_FIT_LIVE_PREDICTIONS_CANDLES: Final[int] = 100 @@ -1440,7 +1504,10 @@ def get_label_prediction_config( DEFAULTS_EXIT_PRICING: Final[dict[str, Any]] = { "trade_natr_method": TRADE_NATR_METHODS[0], # "moving_average" - "final_take_profit_retracement_fraction": 0.25, + "final_take_profit_retracement_fraction": 0.5, + "take_profit_stage_natr_multiplier_fraction_series": ( + TAKE_PROFIT_STAGE_NATR_MULTIPLIER_FRACTION_SERIES[0] + ), } _EXIT_PRICING_SPECS: Final[dict[str, _ParamSpec]] = { @@ -1449,6 +1516,9 @@ _EXIT_PRICING_SPECS: Final[dict[str, _ParamSpec]] = { _NumericValidator(min_value=0, max_value=1, min_exclusive=True), output_type=float, ), + "take_profit_stage_natr_multiplier_fraction_series": _ParamSpec( + _EnumValidator(TAKE_PROFIT_STAGE_NATR_MULTIPLIER_FRACTION_SERIES), output_type=str + ), } @@ -1634,7 +1704,9 @@ def get_causal_mode(config: dict[str, Any], logger: Logger) -> bool: return causal_mode -def get_label_horizon_candles(config: dict[str, Any], logger: Logger) -> int: +def get_label_horizon_candles( + config: dict[str, Any], logger: Logger, *, warning_messages: set[str] | None = None +) -> int: def _is_positive_int(value: Any) -> bool: return not isinstance(value, bool) and isinstance(value, (int, np.integer)) and value >= 1 @@ -1643,10 +1715,14 @@ def get_label_horizon_candles(config: dict[str, Any], logger: Logger) -> int: fallback = 1 label_horizon_candles = config.get("label_horizon_candles", fallback) if not _is_positive_int(label_horizon_candles): - logger.warning( + message = ( f"Invalid label_horizon_candles value {label_horizon_candles!r}: " f"must be int >= 1, using {fallback!r}" ) + if warning_messages is None or message not in warning_messages: + logger.warning(message) + if warning_messages is not None: + warning_messages.add(message) return fallback return int(label_horizon_candles) -- 2.53.0