Retain temporal costs on invalid actions and select policies on non-PBRS evaluation scores.
Advance reward compatibility, correct terminal analytical transitions and synthetic duration bounds, and reuse canonical architecture and analytical definitions.
Update the mathematical reference, behavioral regressions and BasedPyright snapshot.
},
{
"endCharacter": 5,
- "endLine": 2210,
+ "endLine": 2235,
"file": "ReforceXY/reward_space_analysis/reward_space_analysis.py",
"message": "No overloads for \"cut\" match the provided arguments",
"rule": "reportCallIssue",
"severity": "error",
"startCharacter": 17,
- "startLine": 2205
+ "startLine": 2230
},
{
"endCharacter": 21,
- "endLine": 2207,
+ "endLine": 2232,
"file": "ReforceXY/reward_space_analysis/reward_space_analysis.py",
"message": "Argument of type \"_Array1D[Any]\" cannot be assigned to parameter \"bins\" of type \"int | Sequence[float] | Index[int] | Index[float] | IntervalIndex[Interval[Any]] | Series[Any]\" in function \"cut\"\n Type \"_Array1D[Any]\" is not assignable to type \"int | Sequence[float] | Index[int] | Index[float] | IntervalIndex[Interval[Any]] | Series[Any]\"\n \"ndarray[tuple[int], dtype[Any]]\" is not assignable to \"int\"\n \"ndarray[tuple[int], dtype[Any]]\" is not assignable to \"Sequence[float]\"\n \"ndarray[tuple[int], dtype[Any]]\" is not assignable to \"Index[int]\"\n \"ndarray[tuple[int], dtype[Any]]\" is not assignable to \"Index[float]\"\n \"ndarray[tuple[int], dtype[Any]]\" is not assignable to \"IntervalIndex[Interval[Any]]\"\n \"ndarray[tuple[int], dtype[Any]]\" is not assignable to \"Series[Any]\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 13,
- "startLine": 2207
+ "startLine": 2232
},
{
"endCharacter": 5,
- "endLine": 2273,
+ "endLine": 2298,
"file": "ReforceXY/reward_space_analysis/reward_space_analysis.py",
"message": "Type \"dict[bytes, bytes] | dict[str, RewardParamValue]\" is not assignable to declared type \"RewardParams\"\n Type \"dict[bytes, bytes] | dict[str, RewardParamValue]\" is not assignable to type \"RewardParams\"\n \"dict[bytes, bytes]\" is not assignable to \"dict[str, RewardParamValue]\"\n Type parameter \"_KT@dict\" is invariant, but \"bytes\" is not the same as \"str\"\n Type parameter \"_VT@dict\" is invariant, but \"bytes\" is not the same as \"RewardParamValue\"",
"rule": "reportAssignmentType",
"severity": "error",
"startCharacter": 34,
- "startLine": 2269
+ "startLine": 2294
},
{
"endCharacter": 43,
- "endLine": 2270,
+ "endLine": 2295,
"file": "ReforceXY/reward_space_analysis/reward_space_analysis.py",
"message": "No overloads for \"__init__\" match the provided arguments",
"rule": "reportCallIssue",
"severity": "error",
"startCharacter": 8,
- "startLine": 2270
+ "startLine": 2295
},
{
"endCharacter": 42,
- "endLine": 2270,
+ "endLine": 2295,
"file": "ReforceXY/reward_space_analysis/reward_space_analysis.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"iterable\" of type \"Iterable[list[bytes]]\" in function \"__init__\"\n Type \"Any | None\" is not assignable to type \"Iterable[list[bytes]]\"\n \"None\" is incompatible with protocol \"Iterable[list[bytes]]\"\n \"__iter__\" is not present",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 13,
- "startLine": 2270
+ "startLine": 2295
},
{
"endCharacter": 60,
- "endLine": 2532,
+ "endLine": 2557,
"file": "ReforceXY/reward_space_analysis/reward_space_analysis.py",
"message": "Cannot access attribute \"importances_mean\" for class \"dict[Unknown, Bunch]\"\n Attribute \"importances_mean\" is unknown",
"rule": "reportAttributeAccessIssue",
"severity": "error",
"startCharacter": 44,
- "startLine": 2532
+ "startLine": 2557
},
{
"endCharacter": 58,
- "endLine": 2533,
+ "endLine": 2558,
"file": "ReforceXY/reward_space_analysis/reward_space_analysis.py",
"message": "Cannot access attribute \"importances_std\" for class \"dict[Unknown, Bunch]\"\n Attribute \"importances_std\" is unknown",
"rule": "reportAttributeAccessIssue",
"severity": "error",
"startCharacter": 43,
- "startLine": 2533
+ "startLine": 2558
},
{
"endCharacter": 88,
- "endLine": 2552,
+ "endLine": 2577,
"file": "ReforceXY/reward_space_analysis/reward_space_analysis.py",
"message": "Cannot access attribute \"columns\" for class \"NDArray[Unknown]\"\n Attribute \"columns\" is unknown",
"rule": "reportAttributeAccessIssue",
"severity": "error",
"startCharacter": 81,
- "startLine": 2552
+ "startLine": 2577
},
{
"endCharacter": 88,
- "endLine": 2552,
+ "endLine": 2577,
"file": "ReforceXY/reward_space_analysis/reward_space_analysis.py",
"message": "Cannot access attribute \"columns\" for class \"list[Unknown]\"\n Attribute \"columns\" is unknown",
"rule": "reportAttributeAccessIssue",
"severity": "error",
"startCharacter": 81,
- "startLine": 2552
+ "startLine": 2577
},
{
"endCharacter": 17,
- "endLine": 2561,
+ "endLine": 2586,
"file": "ReforceXY/reward_space_analysis/reward_space_analysis.py",
"message": "Object of type \"None\" cannot be called",
"rule": "reportOptionalCall",
"severity": "error",
"startCharacter": 28,
- "startLine": 2555
+ "startLine": 2580
},
{
"endCharacter": 29,
- "endLine": 3162,
+ "endLine": 3187,
"file": "ReforceXY/reward_space_analysis/reward_space_analysis.py",
"message": "No overloads for \"ptp\" match the provided arguments",
"rule": "reportCallIssue",
"severity": "error",
"startCharacter": 11,
- "startLine": 3162
+ "startLine": 3187
},
{
"endCharacter": 28,
- "endLine": 3162,
+ "endLine": 3187,
"file": "ReforceXY/reward_space_analysis/reward_space_analysis.py",
"message": "Argument of type \"np_1darray[Any] | ExtensionArray | Categorical[object]\" cannot be assigned to parameter \"a\" of type \"_ArrayLikeNumeric_co\" in function \"ptp\"\n Type \"np_1darray[Any] | ExtensionArray | Categorical[object]\" is not assignable to type \"_ArrayLikeNumeric_co\"\n Type \"ExtensionArray\" is not assignable to type \"_ArrayLikeNumeric_co\"\n \"ExtensionArray\" is incompatible with protocol \"_SupportsArray[dtype[number[Any, Any] | numpy.bool[builtins.bool] | object_ | timedelta64[Any]]]\"\n \"__array__\" is not present\n \"ExtensionArray\" is incompatible with protocol \"_NestedSequence[_SupportsArray[dtype[number[Any, Any] | numpy.bool[builtins.bool] | object_ | timedelta64[Any]]]]\"\n \"__reversed__\" is not present\n \"count\" is not present\n \"index\" is not present\n ...",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 18,
- "startLine": 3162
+ "startLine": 3187
},
{
"endCharacter": 65,
- "endLine": 3171,
+ "endLine": 3196,
"file": "ReforceXY/reward_space_analysis/reward_space_analysis.py",
"message": "Cannot access attribute \"mean\" for class \"Categorical[object]\"\n Attribute \"mean\" is unknown",
"rule": "reportAttributeAccessIssue",
"severity": "error",
"startCharacter": 61,
- "startLine": 3171
+ "startLine": 3196
},
{
"endCharacter": 65,
- "endLine": 3171,
+ "endLine": 3196,
"file": "ReforceXY/reward_space_analysis/reward_space_analysis.py",
"message": "Cannot access attribute \"mean\" for class \"ExtensionArray\"\n Attribute \"mean\" is unknown",
"rule": "reportAttributeAccessIssue",
"severity": "error",
"startCharacter": 61,
- "startLine": 3171
+ "startLine": 3196
},
{
"endCharacter": 5,
- "endLine": 4083,
+ "endLine": 4108,
"file": "ReforceXY/reward_space_analysis/reward_space_analysis.py",
"message": "Type \"dict[bytes, bytes] | dict[str, RewardParamValue]\" is not assignable to declared type \"RewardParams\"\n Type \"dict[bytes, bytes] | dict[str, RewardParamValue]\" is not assignable to type \"RewardParams\"\n \"dict[bytes, bytes]\" is not assignable to \"dict[str, RewardParamValue]\"\n Type parameter \"_KT@dict\" is invariant, but \"bytes\" is not the same as \"str\"\n Type parameter \"_VT@dict\" is invariant, but \"bytes\" is not the same as \"RewardParamValue\"",
"rule": "reportAssignmentType",
"severity": "error",
"startCharacter": 34,
- "startLine": 4079
+ "startLine": 4104
},
{
"endCharacter": 43,
- "endLine": 4080,
+ "endLine": 4105,
"file": "ReforceXY/reward_space_analysis/reward_space_analysis.py",
"message": "No overloads for \"__init__\" match the provided arguments",
"rule": "reportCallIssue",
"severity": "error",
"startCharacter": 8,
- "startLine": 4080
+ "startLine": 4105
},
{
"endCharacter": 42,
- "endLine": 4080,
+ "endLine": 4105,
"file": "ReforceXY/reward_space_analysis/reward_space_analysis.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"iterable\" of type \"Iterable[list[bytes]]\" in function \"__init__\"\n Type \"Any | None\" is not assignable to type \"Iterable[list[bytes]]\"\n \"None\" is incompatible with protocol \"Iterable[list[bytes]]\"\n \"__iter__\" is not present",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 13,
- "startLine": 4080
+ "startLine": 4105
},
{
"endCharacter": 22,
- "endLine": 1086,
+ "endLine": 1122,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Cannot assign to attribute \"train_env\" for class \"ReforceXY*\"\n Expression of type \"VecEnv\" cannot be assigned to attribute \"train_env\" of class \"ReforceXY\"\n Type \"VecEnv\" is not assignable to type \"VecMonitor | SubprocVecEnv | Env[Unknown, Unknown]\"\n \"VecEnv\" is not assignable to \"VecMonitor\"\n \"VecEnv\" is not assignable to \"SubprocVecEnv\"\n \"VecEnv\" is not assignable to \"Env[Unknown, Unknown]\"",
"rule": "reportAttributeAccessIssue",
"severity": "error",
"startCharacter": 13,
- "startLine": 1086
+ "startLine": 1122
},
{
"endCharacter": 37,
- "endLine": 1086,
+ "endLine": 1122,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Cannot assign to attribute \"eval_env\" for class \"ReforceXY*\"\n Expression of type \"VecEnv | None\" cannot be assigned to attribute \"eval_env\" of class \"ReforceXY\"\n Type \"VecEnv | None\" is not assignable to type \"VecMonitor | SubprocVecEnv | Env[Unknown, Unknown]\"\n Type \"VecEnv\" is not assignable to type \"VecMonitor | SubprocVecEnv | Env[Unknown, Unknown]\"\n \"VecEnv\" is not assignable to \"VecMonitor\"\n \"VecEnv\" is not assignable to \"SubprocVecEnv\"\n \"VecEnv\" is not assignable to \"Env[Unknown, Unknown]\"",
"rule": "reportAttributeAccessIssue",
"severity": "error",
"startCharacter": 29,
- "startLine": 1086
+ "startLine": 1122
},
{
"endCharacter": 24,
- "endLine": 1347,
+ "endLine": 1384,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"VecEnv\" cannot be assigned to parameter \"eval_env\" of type \"BaseEnvironment\" in function \"__init__\"\n \"VecEnv\" is not assignable to \"BaseEnvironment\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 16,
- "startLine": 1347
+ "startLine": 1384
},
{
"endCharacter": 38,
- "endLine": 1580,
+ "endLine": 1617,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"obj\" of type \"Sized\" in function \"len\"\n Type \"Any | None\" is not assignable to type \"Sized\"\n \"None\" is incompatible with protocol \"Sized\"\n \"__len__\" is not present",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 30,
- "startLine": 1580
+ "startLine": 1617
},
{
"endCharacter": 36,
- "endLine": 1584,
+ "endLine": 1621,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"obj\" of type \"Sized\" in function \"len\"\n Type \"Any | None\" is not assignable to type \"Sized\"\n \"None\" is incompatible with protocol \"Sized\"\n \"__len__\" is not present",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 29,
- "startLine": 1584
+ "startLine": 1621
},
{
"endCharacter": 80,
- "endLine": 1587,
+ "endLine": 1624,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"timeframe\" of type \"str\" in function \"steps_to_days\"\n Type \"Any | None\" is not assignable to type \"str\"\n \"None\" is not assignable to \"str\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 52,
- "startLine": 1587
+ "startLine": 1624
},
{
"endCharacter": 78,
- "endLine": 1588,
+ "endLine": 1625,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"timeframe\" of type \"str\" in function \"steps_to_days\"\n Type \"Any | None\" is not assignable to type \"str\"\n \"None\" is not assignable to \"str\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 50,
- "startLine": 1588
+ "startLine": 1625
},
{
"endCharacter": 80,
- "endLine": 1589,
+ "endLine": 1626,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"timeframe\" of type \"str\" in function \"steps_to_days\"\n Type \"Any | None\" is not assignable to type \"str\"\n \"None\" is not assignable to \"str\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 52,
- "startLine": 1589
+ "startLine": 1626
},
{
"endCharacter": 56,
- "endLine": 1673,
+ "endLine": 1710,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"VecMonitor | SubprocVecEnv | Env[Unknown, Unknown]\" cannot be assigned to parameter \"eval_env\" of type \"VecEnv | None\" in function \"get_callbacks\"\n Type \"VecMonitor | SubprocVecEnv | Env[Unknown, Unknown]\" is not assignable to type \"VecEnv | None\"\n Type \"Env[Unknown, Unknown]\" is not assignable to type \"VecEnv | None\"\n \"Env[Unknown, Unknown]\" is not assignable to \"VecEnv\"\n \"Env[Unknown, Unknown]\" is not assignable to \"None\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 43,
- "startLine": 1673
+ "startLine": 1710
},
{
"endCharacter": 84,
- "endLine": 1693,
+ "endLine": 1730,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"float | list[float]\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"float | list[float]\" is not assignable to type \"ConvertibleToFloat\"\n Type \"list[float]\" is not assignable to type \"ConvertibleToFloat\"\n \"list[float]\" is not assignable to \"str\"\n \"list[float]\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"list[float]\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"list[float]\" is incompatible with protocol \"SupportsIndex\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 67,
- "startLine": 1693
+ "startLine": 1730
},
{
"endCharacter": 84,
- "endLine": 1693,
+ "endLine": 1730,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"float | list[float]\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"float | list[float]\" is not assignable to type \"ConvertibleToFloat\"\n Type \"list[float]\" is not assignable to type \"ConvertibleToFloat\"\n \"list[float]\" is not assignable to \"str\"\n \"list[float]\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"list[float]\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"list[float]\" is incompatible with protocol \"SupportsIndex\"\n ...",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 67,
- "startLine": 1693
+ "startLine": 1730
},
{
"endCharacter": 44,
- "endLine": 2771,
+ "endLine": 2808,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "\"best_trial_params\" is possibly unbound",
"rule": "reportPossiblyUnboundVariable",
"severity": "error",
"startCharacter": 27,
- "startLine": 2771
+ "startLine": 2808
},
{
"endCharacter": 12,
- "endLine": 2791,
+ "endLine": 2828,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Parameter declaration \"seed\" is obscured by a declaration of the same name",
"rule": "reportRedeclaration",
"severity": "error",
"startCharacter": 8,
- "startLine": 2791
+ "startLine": 2828
},
{
"endCharacter": 16,
- "endLine": 2792,
+ "endLine": 2829,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Parameter declaration \"env_info\" is obscured by a declaration of the same name",
"rule": "reportRedeclaration",
"severity": "error",
"startCharacter": 8,
- "startLine": 2792
+ "startLine": 2829
},
{
"endCharacter": 51,
- "endLine": 2837,
+ "endLine": 2874,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"list[() -> BaseEnvironment]\" cannot be assigned to parameter \"env_fns\" of type \"list[() -> Env[Unknown, Unknown]]\" in function \"__init__\"\n \"list[() -> BaseEnvironment]\" is not assignable to \"list[() -> Env[Unknown, Unknown]]\"\n Type parameter \"_T@list\" is invariant, but \"() -> BaseEnvironment\" is not the same as \"() -> Env[Unknown, Unknown]\"\n Consider switching from \"list\" to \"Sequence\" which is covariant",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 42,
- "startLine": 2837
+ "startLine": 2874
},
{
"endCharacter": 53,
- "endLine": 2843,
+ "endLine": 2880,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"list[() -> BaseEnvironment]\" cannot be assigned to parameter \"env_fns\" of type \"list[() -> Env[Unknown, Unknown]]\" in function \"__init__\"\n \"list[() -> BaseEnvironment]\" is not assignable to \"list[() -> Env[Unknown, Unknown]]\"\n Type parameter \"_T@list\" is invariant, but \"() -> BaseEnvironment\" is not the same as \"() -> Env[Unknown, Unknown]\"\n Consider switching from \"list\" to \"Sequence\" which is covariant",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 45,
- "startLine": 2843
+ "startLine": 2880
},
{
"endCharacter": 22,
- "endLine": 2906,
+ "endLine": 2943,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Operator \"*\" not supported for \"None\"",
"rule": "reportOptionalOperand",
"severity": "error",
"startCharacter": 15,
- "startLine": 2906
+ "startLine": 2943
},
{
"endCharacter": 96,
- "endLine": 2908,
+ "endLine": 2945,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Operator \"*\" not supported for \"None\"",
"rule": "reportOptionalOperand",
"severity": "error",
"startCharacter": 89,
- "startLine": 2908
+ "startLine": 2945
},
{
"endCharacter": 23,
- "endLine": 2911,
+ "endLine": 2948,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Operator \"*\" not supported for \"None\"",
"rule": "reportOptionalOperand",
"severity": "error",
"startCharacter": 16,
- "startLine": 2911
+ "startLine": 2948
},
{
"endCharacter": 98,
- "endLine": 2913,
+ "endLine": 2950,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Operator \"*\" not supported for \"None\"",
"rule": "reportOptionalOperand",
"severity": "error",
"startCharacter": 91,
- "startLine": 2913
+ "startLine": 2950
},
{
"endCharacter": 44,
- "endLine": 2925,
+ "endLine": 2962,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Operator \"*\" not supported for types \"Any | None\" and \"Any | int | None\"\n Operator \"*\" not supported for types \"None\" and \"None\"\n Operator \"*\" not supported for types \"None\" and \"int\"",
"rule": "reportOperatorIssue",
"severity": "error",
"startCharacter": 15,
- "startLine": 2925
+ "startLine": 2962
},
{
"endCharacter": 133,
- "endLine": 2927,
+ "endLine": 2964,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Operator \"*\" not supported for types \"Any | None\" and \"Any | int | None\"\n Operator \"*\" not supported for types \"None\" and \"None\"\n Operator \"*\" not supported for types \"None\" and \"int\"",
"rule": "reportOperatorIssue",
"severity": "error",
"startCharacter": 106,
- "startLine": 2927
+ "startLine": 2964
},
{
"endCharacter": 30,
- "endLine": 2930,
+ "endLine": 2967,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Operator \">\" not supported for \"None\"",
"rule": "reportOptionalOperand",
"severity": "error",
"startCharacter": 15,
- "startLine": 2930
+ "startLine": 2967
},
{
"endCharacter": 54,
- "endLine": 2996,
+ "endLine": 3033,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "\"is_pruned\" is not a known attribute of \"None\"",
"rule": "reportOptionalMemberAccess",
"severity": "error",
"startCharacter": 45,
- "startLine": 2996
+ "startLine": 3033
},
{
"endCharacter": 67,
- "endLine": 2999,
+ "endLine": 3036,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "\"use_masking\" is not a known attribute of \"None\"",
"rule": "reportOptionalMemberAccess",
"severity": "error",
"startCharacter": 56,
- "startLine": 2999
+ "startLine": 3036
},
{
"endCharacter": 60,
- "endLine": 3007,
+ "endLine": 3044,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "\"update_best_reward\" is not a known attribute of \"None\"",
"rule": "reportOptionalMemberAccess",
"severity": "error",
"startCharacter": 42,
- "startLine": 3007
+ "startLine": 3044
},
{
"endCharacter": 84,
- "endLine": 3007,
+ "endLine": 3044,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"float | list[float]\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"float | list[float]\" is not assignable to type \"ConvertibleToFloat\"\n Type \"list[float]\" is not assignable to type \"ConvertibleToFloat\"\n \"list[float]\" is not assignable to \"str\"\n \"list[float]\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"list[float]\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"list[float]\" is incompatible with protocol \"SupportsIndex\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 67,
- "startLine": 3007
+ "startLine": 3044
},
{
"endCharacter": 84,
- "endLine": 3007,
+ "endLine": 3044,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"float | list[float]\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"float | list[float]\" is not assignable to type \"ConvertibleToFloat\"\n Type \"list[float]\" is not assignable to type \"ConvertibleToFloat\"\n \"list[float]\" is not assignable to \"str\"\n \"list[float]\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"list[float]\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"list[float]\" is incompatible with protocol \"SupportsIndex\"\n ...",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 67,
- "startLine": 3007
+ "startLine": 3044
},
{
"endCharacter": 46,
- "endLine": 3061,
+ "endLine": 3098,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "\"is_pruned\" is not a known attribute of \"None\"",
"rule": "reportOptionalMemberAccess",
"severity": "error",
"startCharacter": 37,
- "startLine": 3061
+ "startLine": 3098
},
{
"endCharacter": 57,
- "endLine": 3064,
+ "endLine": 3101,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "\"best_mean_reward\" is not a known attribute of \"None\"",
"rule": "reportOptionalMemberAccess",
"severity": "error",
"startCharacter": 41,
- "startLine": 3064
+ "startLine": 3101
},
{
"endCharacter": 37,
- "endLine": 3074,
+ "endLine": 3111,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Cannot assign to attribute \"train_env\" for class \"ReforceXY*\"\n Type \"None\" is not assignable to type \"VecMonitor | SubprocVecEnv | Env[Unknown, Unknown]\"\n \"None\" is not assignable to \"VecMonitor\"\n \"None\" is not assignable to \"SubprocVecEnv\"\n \"None\" is not assignable to \"Env[Unknown, Unknown]\"",
"rule": "reportAttributeAccessIssue",
"severity": "error",
"startCharacter": 33,
- "startLine": 3074
+ "startLine": 3111
},
{
"endCharacter": 36,
- "endLine": 3079,
+ "endLine": 3116,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Cannot assign to attribute \"eval_env\" for class \"ReforceXY*\"\n Type \"None\" is not assignable to type \"VecMonitor | SubprocVecEnv | Env[Unknown, Unknown]\"\n \"None\" is not assignable to \"VecMonitor\"\n \"None\" is not assignable to \"SubprocVecEnv\"\n \"None\" is not assignable to \"Env[Unknown, Unknown]\"",
"rule": "reportAttributeAccessIssue",
"severity": "error",
"startCharacter": 32,
- "startLine": 3079
+ "startLine": 3116
},
{
"endCharacter": 7,
- "endLine": 3124,
+ "endLine": 3161,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Declaration \"MyRLEnv\" is obscured by a declaration of the same name",
"rule": "reportRedeclaration",
"severity": "error",
"startCharacter": 0,
- "startLine": 3124
+ "startLine": 3161
},
{
"endCharacter": 36,
- "endLine": 3136,
+ "endLine": 3173,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Declaration \"_last_closed_trade_tick\" is obscured by a declaration of the same name",
"rule": "reportRedeclaration",
"severity": "error",
"startCharacter": 13,
- "startLine": 3136
+ "startLine": 3173
},
{
"endCharacter": 13,
- "endLine": 3728,
+ "endLine": 3765,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Method \"reset\" overrides class \"BaseEnvironment\" in an incompatible manner\n Return type mismatch: base method returns type \"tuple[DataFrame, dict[Unknown, Unknown]]\", override returns type \"tuple[NDArray[float32], dict[str, Any]]\"\n \"tuple[NDArray[float32], dict[str, Any]]\" is not assignable to \"tuple[DataFrame, dict[Unknown, Unknown]]\"\n Tuple entry 1 is incorrect type\n \"ndarray[_AnyShape, dtype[float32]]\" is not assignable to \"DataFrame\"",
"rule": "reportIncompatibleMethodOverride",
"severity": "error",
"startCharacter": 8,
- "startLine": 3728
+ "startLine": 3765
},
{
"endCharacter": 26,
- "endLine": 3749,
+ "endLine": 3786,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Type \"tuple[DataFrame, dict[Unknown, Unknown]]\" is not assignable to return type \"tuple[NDArray[float32], dict[str, Any]]\"\n \"DataFrame\" is not assignable to \"ndarray[_AnyShape, dtype[float32]]\"",
"rule": "reportReturnType",
"severity": "error",
"startCharacter": 15,
- "startLine": 3749
+ "startLine": 3786
},
{
"endCharacter": 55,
- "endLine": 4057,
+ "endLine": 4091,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"float | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"float | None\" is not assignable to type \"ConvertibleToFloat\"\n Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 44,
- "startLine": 4057
+ "startLine": 4091
},
{
"endCharacter": 55,
- "endLine": 4057,
+ "endLine": 4091,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"float | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"float | None\" is not assignable to type \"ConvertibleToFloat\"\n Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"\n ...",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 44,
- "startLine": 4057
+ "startLine": 4091
},
{
"endCharacter": 59,
- "endLine": 4081,
+ "endLine": 4115,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"float | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"float | None\" is not assignable to type \"ConvertibleToFloat\"\n Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 48,
- "startLine": 4081
+ "startLine": 4115
},
{
"endCharacter": 59,
- "endLine": 4081,
+ "endLine": 4115,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"float | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"float | None\" is not assignable to type \"ConvertibleToFloat\"\n Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"\n ...",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 48,
- "startLine": 4081
+ "startLine": 4115
},
{
"endCharacter": 24,
- "endLine": 4110,
+ "endLine": 4144,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Method \"_get_observation\" overrides class \"BaseEnvironment\" in an incompatible manner\n Return type mismatch: base method returns type \"DataFrame\", override returns type \"NDArray[float32]\"\n \"ndarray[_AnyShape, dtype[float32]]\" is not assignable to \"DataFrame\"",
"rule": "reportIncompatibleMethodOverride",
"severity": "error",
"startCharacter": 8,
- "startLine": 4110
+ "startLine": 4144
},
{
"endCharacter": 12,
- "endLine": 4203,
+ "endLine": 4237,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Method \"step\" overrides class \"Base5ActionRLEnv\" in an incompatible manner\n Return type mismatch: base method returns type \"tuple[DataFrame, float, bool, Literal[False], dict[str, Unknown]]\", override returns type \"tuple[NDArray[float32], float, bool, bool, dict[str, Any]]\"\n \"tuple[NDArray[float32], float, bool, bool, dict[str, Any]]\" is not assignable to \"tuple[DataFrame, float, bool, Literal[False], dict[str, Unknown]]\"\n Tuple entry 1 is incorrect type\n \"ndarray[_AnyShape, dtype[float32]]\" is not assignable to \"DataFrame\"",
"rule": "reportIncompatibleMethodOverride",
"severity": "error",
"startCharacter": 8,
- "startLine": 4203
+ "startLine": 4237
},
{
"endCharacter": 20,
- "endLine": 4393,
+ "endLine": 4432,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Method \"action_masks\" overrides class \"BaseEnvironment\" in an incompatible manner\n Return type mismatch: base method returns type \"list[bool]\", override returns type \"NDArray[bool_]\"\n \"ndarray[_AnyShape, dtype[bool_]]\" is not assignable to \"list[bool]\"",
"rule": "reportIncompatibleMethodOverride",
"severity": "error",
"startCharacter": 8,
- "startLine": 4393
+ "startLine": 4432
},
{
"endCharacter": 40,
- "endLine": 4542,
+ "endLine": 4581,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "\"Figure\" is not exported from module \"matplotlib.pyplot\"",
"rule": "reportPrivateImportUsage",
"severity": "error",
"startCharacter": 34,
- "startLine": 4542
+ "startLine": 4581
},
{
"endCharacter": 54,
- "endLine": 5027,
+ "endLine": 5066,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 45,
- "startLine": 5027
+ "startLine": 5066
},
{
"endCharacter": 54,
- "endLine": 5027,
+ "endLine": 5066,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"\n ...",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 45,
- "startLine": 5027
+ "startLine": 5066
},
{
"endCharacter": 50,
- "endLine": 5039,
+ "endLine": 5078,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"object\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"object\" is not assignable to type \"ConvertibleToFloat\"\n \"object\" is not assignable to \"str\"\n \"object\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"object\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"object\" is incompatible with protocol \"SupportsIndex\"\n \"__index__\" is not present",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 37,
- "startLine": 5039
+ "startLine": 5078
},
{
"endCharacter": 72,
- "endLine": 5050,
+ "endLine": 5089,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"float | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"float | None\" is not assignable to type \"ConvertibleToFloat\"\n Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 70,
- "startLine": 5050
+ "startLine": 5089
},
{
"endCharacter": 72,
- "endLine": 5050,
+ "endLine": 5089,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"float | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"float | None\" is not assignable to type \"ConvertibleToFloat\"\n Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"\n ...",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 70,
- "startLine": 5050
+ "startLine": 5089
},
{
"endCharacter": 73,
- "endLine": 5059,
+ "endLine": 5098,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"float | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"float | None\" is not assignable to type \"ConvertibleToFloat\"\n Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 71,
- "startLine": 5059
+ "startLine": 5098
},
{
"endCharacter": 73,
- "endLine": 5059,
+ "endLine": 5098,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"float | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"float | None\" is not assignable to type \"ConvertibleToFloat\"\n Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"\n ...",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 71,
- "startLine": 5059
+ "startLine": 5098
},
{
"endCharacter": 58,
- "endLine": 5068,
+ "endLine": 5107,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 56,
- "startLine": 5068
+ "startLine": 5107
},
{
"endCharacter": 58,
- "endLine": 5068,
+ "endLine": 5107,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"\n ...",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 56,
- "startLine": 5068
+ "startLine": 5107
},
{
"endCharacter": 50,
- "endLine": 5299,
+ "endLine": 5338,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Mapping[Unknown, Unknown]\" cannot be assigned to parameter \"src\" of type \"dict[str, Any]\" in function \"deepmerge\"\n \"Mapping[Unknown, Unknown]\" is not assignable to \"dict[str, Any]\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 49,
- "startLine": 5299
+ "startLine": 5338
},
{
"endCharacter": 20,
- "endLine": 5479,
+ "endLine": 5497,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 18,
- "startLine": 5479
+ "startLine": 5497
},
{
"endCharacter": 20,
- "endLine": 5479,
+ "endLine": 5497,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"\n ...",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 18,
- "startLine": 5479
+ "startLine": 5497
},
{
"endCharacter": 59,
- "endLine": 5484,
+ "endLine": 5502,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n Type \"None\" is not assignable to type \"ConvertibleToInt\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsInt\"\n \"__int__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 31,
- "startLine": 5484
+ "startLine": 5502
},
{
"endCharacter": 59,
- "endLine": 5484,
+ "endLine": 5502,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n Type \"None\" is not assignable to type \"ConvertibleToInt\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsInt\"\n \"__int__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"\n ...",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 31,
- "startLine": 5484
+ "startLine": 5502
},
{
"endCharacter": 65,
- "endLine": 5485,
+ "endLine": 5503,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n Type \"None\" is not assignable to type \"ConvertibleToInt\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsInt\"\n \"__int__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 34,
- "startLine": 5485
+ "startLine": 5503
},
{
"endCharacter": 65,
- "endLine": 5485,
+ "endLine": 5503,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n Type \"None\" is not assignable to type \"ConvertibleToInt\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsInt\"\n \"__int__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"\n ...",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 34,
- "startLine": 5485
+ "startLine": 5503
},
{
"endCharacter": 63,
- "endLine": 5488,
+ "endLine": 5506,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 34,
- "startLine": 5488
+ "startLine": 5506
},
{
"endCharacter": 63,
- "endLine": 5488,
+ "endLine": 5506,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"\n ...",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 34,
- "startLine": 5488
+ "startLine": 5506
},
{
"endCharacter": 61,
- "endLine": 5490,
+ "endLine": 5508,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n Type \"None\" is not assignable to type \"ConvertibleToInt\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsInt\"\n \"__int__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 32,
- "startLine": 5490
+ "startLine": 5508
},
{
"endCharacter": 61,
- "endLine": 5490,
+ "endLine": 5508,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n Type \"None\" is not assignable to type \"ConvertibleToInt\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsInt\"\n \"__int__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"\n ...",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 32,
- "startLine": 5490
+ "startLine": 5508
},
{
"endCharacter": 67,
- "endLine": 5491,
+ "endLine": 5509,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 36,
- "startLine": 5491
+ "startLine": 5509
},
{
"endCharacter": 67,
- "endLine": 5491,
+ "endLine": 5509,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"\n ...",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 36,
- "startLine": 5491
+ "startLine": 5509
},
{
"endCharacter": 73,
- "endLine": 5492,
+ "endLine": 5510,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 39,
- "startLine": 5492
+ "startLine": 5510
},
{
"endCharacter": 73,
- "endLine": 5492,
+ "endLine": 5510,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"\n ...",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 39,
- "startLine": 5492
+ "startLine": 5510
},
{
"endCharacter": 61,
- "endLine": 5493,
+ "endLine": 5511,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 33,
- "startLine": 5493
+ "startLine": 5511
},
{
"endCharacter": 61,
- "endLine": 5493,
+ "endLine": 5511,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"\n ...",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 33,
- "startLine": 5493
+ "startLine": 5511
},
{
"endCharacter": 89,
- "endLine": 5500,
+ "endLine": 5518,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n Type \"None\" is not assignable to type \"ConvertibleToInt\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsInt\"\n \"__int__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 52,
- "startLine": 5500
+ "startLine": 5518
},
{
"endCharacter": 89,
- "endLine": 5500,
+ "endLine": 5518,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n Type \"None\" is not assignable to type \"ConvertibleToInt\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsInt\"\n \"__int__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"\n ...",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 52,
- "startLine": 5500
+ "startLine": 5518
},
{
"endCharacter": 83,
- "endLine": 5501,
+ "endLine": 5519,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n Type \"None\" is not assignable to type \"ConvertibleToInt\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsInt\"\n \"__int__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 49,
- "startLine": 5501
+ "startLine": 5519
},
{
"endCharacter": 83,
- "endLine": 5501,
+ "endLine": 5519,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n Type \"None\" is not assignable to type \"ConvertibleToInt\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsInt\"\n \"__int__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"\n ...",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 49,
- "startLine": 5501
+ "startLine": 5519
},
{
"endCharacter": 65,
- "endLine": 5527,
+ "endLine": 5545,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n Type \"None\" is not assignable to type \"ConvertibleToInt\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsInt\"\n \"__int__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 34,
- "startLine": 5527
+ "startLine": 5545
},
{
"endCharacter": 65,
- "endLine": 5527,
+ "endLine": 5545,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n Type \"None\" is not assignable to type \"ConvertibleToInt\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsInt\"\n \"__int__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"\n ...",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 34,
- "startLine": 5527
+ "startLine": 5545
},
{
"endCharacter": 67,
- "endLine": 5529,
+ "endLine": 5547,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n Type \"None\" is not assignable to type \"ConvertibleToInt\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsInt\"\n \"__int__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 35,
- "startLine": 5529
+ "startLine": 5547
},
{
"endCharacter": 67,
- "endLine": 5529,
+ "endLine": 5547,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n Type \"None\" is not assignable to type \"ConvertibleToInt\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsInt\"\n \"__int__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"\n ...",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 35,
- "startLine": 5529
+ "startLine": 5547
},
{
"endCharacter": 87,
- "endLine": 5532,
+ "endLine": 5550,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 46,
- "startLine": 5532
+ "startLine": 5550
},
{
"endCharacter": 87,
- "endLine": 5532,
+ "endLine": 5550,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"\n ...",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 46,
- "startLine": 5532
+ "startLine": 5550
},
{
"endCharacter": 93,
- "endLine": 5533,
+ "endLine": 5551,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 49,
- "startLine": 5533
+ "startLine": 5551
},
{
"endCharacter": 93,
- "endLine": 5533,
+ "endLine": 5551,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"\n ...",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 49,
- "startLine": 5533
+ "startLine": 5551
},
{
"endCharacter": 89,
- "endLine": 5534,
+ "endLine": 5552,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 47,
- "startLine": 5534
+ "startLine": 5552
},
{
"endCharacter": 89,
- "endLine": 5534,
+ "endLine": 5552,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToFloat\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToFloat\"\n Type \"None\" is not assignable to type \"ConvertibleToFloat\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsFloat\"\n \"__float__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"\n ...",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 47,
- "startLine": 5534
+ "startLine": 5552
},
{
"endCharacter": 89,
- "endLine": 5535,
+ "endLine": 5553,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n Type \"None\" is not assignable to type \"ConvertibleToInt\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsInt\"\n \"__int__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 46,
- "startLine": 5535
+ "startLine": 5553
},
{
"endCharacter": 89,
- "endLine": 5535,
+ "endLine": 5553,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n Type \"None\" is not assignable to type \"ConvertibleToInt\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsInt\"\n \"__int__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"\n ...",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 46,
- "startLine": 5535
+ "startLine": 5553
},
{
"endCharacter": 75,
- "endLine": 5536,
+ "endLine": 5554,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n Type \"None\" is not assignable to type \"ConvertibleToInt\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsInt\"\n \"__int__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 39,
- "startLine": 5536
+ "startLine": 5554
},
{
"endCharacter": 75,
- "endLine": 5536,
+ "endLine": 5554,
"file": "ReforceXY/user_data/freqaimodels/ReforceXY.py",
"message": "Argument of type \"Any | None\" cannot be assigned to parameter \"x\" of type \"ConvertibleToInt\" in function \"__new__\"\n Type \"Any | None\" is not assignable to type \"ConvertibleToInt\"\n Type \"None\" is not assignable to type \"ConvertibleToInt\"\n \"None\" is not assignable to \"str\"\n \"None\" is incompatible with protocol \"Buffer\"\n \"__buffer__\" is not present\n \"None\" is incompatible with protocol \"SupportsInt\"\n \"__int__\" is not present\n \"None\" is incompatible with protocol \"SupportsIndex\"\n ...",
"rule": "reportArgumentType",
"severity": "error",
"startCharacter": 39,
- "startLine": 5536
+ "startLine": 5554
},
{
"endCharacter": 33,
timeout. Evaluation environments/episodes also cost resources; a larger budget
is not evidence of better economics.
-The objective maximizes the best mean deterministic evaluation **episode reward**
-across periodic and final evaluations. Progress may display the latest mean
-reward, which is not the selected best mean reward. The objective is not net
-portfolio profit, Sharpe or Sortino. The holdout participates in model/HPO
-selection; reserve a separate unseen market period for economic confirmation
-under the [evaluation protocol](../docs/evaluation.md).
+The objective maximizes the best mean deterministic evaluation **episode score**
+across periodic and final evaluations. This score excludes PBRS shaping but
+retains enabled non-PBRS additives. The same score selects checkpoints and
+controls no-improvement stopping. Progress may display the latest mean, not the
+selected best mean. The objective is not net portfolio profit, Sharpe or
+Sortino. The holdout participates in model/HPO selection; reserve a separate
+unseen market period under the [evaluation protocol](../docs/evaluation.md).
Optuna trains fresh candidates in current-window feature coordinates. Sampled
parameters override supplied model parameters during trials. Hyperband prunes
fitted feature pipeline. DQN/QRDQN deployments each persist their replay buffer;
it is loaded only when continual training starts. Missing or incompatible replay
data prevents continuation but does not prevent inference. The deployment
-generation covers frozen feature coordinates and PBRS reward semantics. Otherwise
-compatible older generations, including `chronological-frozen-pipelines-v2`, support
-inference but cannot continue training. Reset trained models or use a new
-`freqai.identifier` to migrate. Training disables `shuffle_after_split`. HPO
-studies and saved best parameters are reused only when their objective identity
-matches.
+generation covers frozen feature coordinates and reward semantics, including
+idle/hold costs on invalid actions. Otherwise compatible older generations,
+including `chronological-frozen-pipelines-v2` and
+`chronological-frozen-pipelines-pbrs-v3`, support inference but cannot continue
+training. Reset trained models or use a new `freqai.identifier` to migrate.
+Training disables `shuffle_after_split`. HPO studies and saved best parameters
+are reused only when their objective identity matches.
Backtests continue only from a saved policy whose training cutoff precedes
the current window's end and its last available candle boundary. A later
| freqai.rl_config.model_reward_parameters.rr | Schema: 1; direct constructor requires the member | Numeric reward-to-risk factor | 2 | The schema injects an omitted member into an existing reward map; direct environment construction without it raises. Native reward target uses rr × profit_aim. |
| freqai.rl_config.model_reward_parameters.profit_aim | Schema: 0.025; direct constructor requires the member | Numeric profit target | 0.025 | The schema injects an omitted member into an existing reward map; direct environment construction without it raises. This reward target does not place a live take-profit order. |
| freqai.rl_config.model_reward_parameters.max_trade_duration_candles | 128 | Integer-convertible value; no local range validation | 96 | Duration scale for reward penalties, not a live timeout. The schema accepts the top-level homonym `freqai.rl_config.max_trade_duration_candles`, but ReforceXY ignores it; only this reward-map member takes effect. |
-| freqai.rl_config.model_reward_parameters.max_idle_duration_candles | 4 × effective max_trade_duration_candles | Integer-convertible value; no local range validation | Omitted (384 from the supplied trade scale) | Idle duration scale for reward penalties, not a live timer. |
+| freqai.rl_config.model_reward_parameters.max_idle_duration_candles | 4 × effective max_trade_duration_candles | None or integer-convertible value; no local range validation | Omitted (384 from the supplied trade scale) | Missing/None derives the scale; explicit zero uses denominator 1. Reward scale, not a live timer. |
### HPO settings
delta over the returned next observation. Termination liquidates any remaining
position once and sets terminal shaping to `-prev_potential`, even for tiny
nonzero potentials. `get_env_history()` returns one metrics/price row per transition.
-Its `execution_tick` is the transition/action/fill
+History `reward` remains the shaped training reward; `reward_selection` records
+the separate evaluation contribution without PBRS.
+The transition's `execution_tick` is the action/fill
key before the tick increment; its `tick` is the returned post-increment price and
observation row (normally `execution_tick + 1`). Exit-efficiency extrema include
the fee-adjusted PnL at entry and subsequent retained market marks. Ordered
(Simulation-only).
- **`--trading_mode`** (spot|margin|futures, default: spot) – spot: no shorts;
margin/futures: shorts enabled. (Simulation-only).
-- **`--max_duration_ratio`** (float, default: 2.5) – Upper multiple for sampled
- trade durations (idle derived). (Simulation-only; not in reward params; cannot
- be set via `--params`).
+- **`--max_duration_ratio`** (float, default: 2.5) – Synthetic trade-duration
+ bound: `max(1, trunc(max_trade_duration_candles * max_duration_ratio))`
+ candles. At the bound, sampling chooses the valid exit with probability 1.
+ This is not a runtime exit timeout. (Simulation-only; not in `--params`).
- **`--pnl_base_std`** (float, default: 0.02) – Base standard deviation for
synthetic PnL generation (pre-scaling). (Simulation-only).
- **`--pnl_duration_vol_scale`** (float, default: 0.5) – Additional PnL
0.03=3%).
- **`--risk_reward_ratio`** (float, default: 2.0) – Risk-reward multiplier.
- **`--action_masking`** (bare flag, default: true) – Disable via
- `--params action_masking=false`. With masking enabled, sample
- only valid actions. When disabled, sample an invalid action with 10% probability
- and apply the configured invalid-action penalty. Invalid actions leave the held
- position unchanged, except for an independent terminal liquidation.
+ `--params action_masking=false`. Masking samples only valid actions. Without
+ masking, invalid actions have 10% probability except at the forced duration
+ exit. They retain the current position's time cost and add the configured
+ invalid-action penalty. An independent terminal liquidation can still close
+ the position.
### Reward & Shaping
integer. Strict validation checks a numeric gamma in its native precision before
normalizing it.
Simulation consumes extended integer durations without a binary64 cap. Ordinary
-fractional duration caps retain float-product truncation; extended caps use the
-supplied ratio's exact rational value. Duration relationship tables keep candle
+fractional duration bounds use float-product truncation; extended bounds use the
+supplied ratio's exact rational value. Both have a one-candle physical minimum.
+Duration relationship tables keep candle
units when their float bounds and labels are safe. At larger scales, the same
12 right-closed uniform bins use exact assignment and explicitly declared
`duration / max_trade_duration_candles` coordinates; the first bin includes zero.
| `base_factor` | 100.0 | Base reward scale |
| `invalid_action` | -2.0 | Penalty for invalid actions |
+**Reward composition:**
+
+```text
+R_base = invalid_penalty + idle_penalty + hold_penalty + exit_reward
+R_train = R_base + reward_shaping + entry_additive + exit_additive
+R_selection = R_base + entry_additive + exit_additive
+```
+
+The invalid penalty is `invalid_action` for an invalid unmasked action, and zero otherwise.
+
+Time costs use the pre-action duration when the action leaves the position
+unchanged, including invalid actions. A valid entry has no idle cost; a valid
+exit has no hold cost. `exit_reward = pnl * exit_factor` on a valid exit.
+Episode termination also adds a liquidation exit reward and any enabled exit
+additive at the next marked price if a position remains open.
+
+Training uses `R_train`. Runtime evaluation sums `R_selection` without discounting
+for checkpoint selection, HPO and no-improvement stopping. The synthetic `reward`
+column remains `R_train`; it is not the runtime selection score or portfolio
+profit. Canonical mode suppresses both optional additives.
+
+For transition-faithful Python API calculations, supply the actual `next_context`
+after time advances and before terminal liquidation. It is required, with the
+resulting position's side, whenever `terminated=True` leaves a position open.
+Missing or wrong-side terminal states raise `ValueError`. Without a nonterminal
+`next_context`, the helper evaluates its local state approximation, not an
+observed market transition.
+
#### Exit Factor
The exit factor is computed as:
`exit_factor` = `base_factor` · `pnl_target_coefficient` ·
`efficiency_coefficient` · `time_attenuation_coefficient`
+Time attenuation reduces the magnitude of both profitable and losing exit
+rewards. Holding costs are separate.
+
##### PnL Target
| Parameter | Default | Description |
#### Duration Penalties
-| Parameter | Default | Description |
-| ---------------------------- | ------- | ------------------------------------------------------------------------------------------- |
-| `max_trade_duration_candles` | 128 | Trade duration cap |
-| `max_idle_duration_candles` | None | Idle hazard threshold; missing/None derives 4× trade cap; idle clock keeps counting past it |
-| `idle_penalty_ratio` | 1.0 | Idle penalty ratio |
-| `idle_penalty_power` | 1.025 | Idle penalty exponent |
-| `hold_penalty_ratio` | 1.0 | Hold penalty ratio |
-| `hold_penalty_power` | 1.025 | Hold penalty exponent |
+| Parameter | Default | Description |
+| ---------------------------- | ------- | ----------------------------------------------------------------------------- |
+| `max_trade_duration_candles` | 128 | Trade-duration scale and overtime threshold; not an exit timeout |
+| `max_idle_duration_candles` | None | Idle scale; missing/None derives 4× the trade scale; the clock keeps counting |
+| `idle_penalty_ratio` | 1.0 | Idle penalty ratio |
+| `idle_penalty_power` | 1.025 | Idle penalty exponent |
+| `hold_penalty_ratio` | 1.0 | Hold penalty ratio |
+| `hold_penalty_power` | 1.025 | Hold penalty exponent |
-An explicit zero threshold uses denominator 1 and does not disable the idle
-penalty.
+Let `B = base_factor`, `a = profit_aim`, `q = risk_reward_ratio`,
+`D = max(1, max_trade_duration_candles)` and
+`I = max(1, effective max_idle_duration_candles)`. The time-cost scale is
+`C = B * a / q`, distinct from the PnL target `a * q`.
+
+For unchanged positions:
+
+```text
+idle_penalty = -C * idle_penalty_ratio * (idle_duration / I)^idle_penalty_power
+
+hold_penalty = 0 if trade_duration / D < 1
+ = -C * hold_penalty_ratio
+ * (trade_duration / D - 1)^hold_penalty_power otherwise
+```
+
+Idle applies while neutral; hold applies while long or short. Invalid no-op
+actions keep these costs, even when `invalid_action=0`. Durations are candles,
+not elapsed seconds. An explicit idle scale of zero uses denominator 1; it
+does not disable the cost. With exponent zero, `0^0 = 1`, so the hold cost
+already applies at its threshold.
#### Validation
- `m_dur = 1.0` if `r_pnl ≥ 0` (profit side)
- `m_dur = risk_reward_ratio` if `r_pnl < 0` (loss side)
-The loss-side duration multiplier (`m_dur = risk_reward_ratio`) scales the
-duration penalty when holding losing positions, encouraging faster exits from
-losses compared to symmetric treatment.
+The loss-side multiplier changes the duration term of the potential, not the
+base holding cost or optional additives. It does not establish faster exits
+or better financial outcomes for a fitted policy.
#### Entry Additive (Optional)
| `entry_additive_transform_pnl` | tanh | PnL transform |
| `entry_additive_transform_duration` | tanh | Duration transform |
+Both optional additives use the same signal, with `k = entry` or `exit`:
+
+```text
+A_k(pnl, r_dur) = base_factor * k_additive_ratio * 0.5
+ * [T_pnl(k_additive_gain * r_pnl)
+ + sign_eps(r_pnl) * T_dur(k_additive_gain * max(r_dur, 0))]
+```
+
+Use `r_pnl` and `sign_eps` from the hold-potential definition, and the selected
+additive's transforms. There is no loss-side `risk_reward_ratio` multiplier.
+Entry uses fee-aware PnL at the fill with `r_dur = 0`.
+
#### Exit Additive (Optional)
| Parameter | Default | Description |
| `exit_additive_transform_pnl` | tanh | PnL transform |
| `exit_additive_transform_duration` | tanh | Duration transform |
+Exit uses PnL and trade duration at the exit fill, with
+`r_dur = trade_duration / max(1, max_trade_duration_candles)`. A terminal
+liquidation uses the actual next marked PnL and advanced duration instead.
+Additives are zero when disabled and are always suppressed in canonical mode.
+
### Exit Attenuation Kernels
`r` = duration ratio and `grace` = `exit_plateau_grace`.
The `sample_entry_prob`, `sample_exit_prob`, and `sample_neutral_prob` columns in
`reward_samples.csv` report marginal probabilities of valid actions when applicable.
-With masking disabled, these probabilities include the 90% valid-action mass;
-they are not conditional on drawing a valid action.
+With masking disabled, ordinary draws include the 90% valid-action mass; they
+are not conditional on drawing a valid action. At the synthetic duration bound,
+the corresponding valid exit has probability 1 and invalid actions are not drawn.
### Manifest (`manifest.json`)
"""
Calculate time-based attenuation coefficient using configurable strategy.
- Returns a coefficient (typically in range [0.5, 2.0]) to multiply with base_factor.
+ Returns a coefficient in [0, 1] for validated parameters.
"""
if duration_ratio < 0.0:
duration_ratio = 0.0
stacklevel=2,
)
alpha = 1.0
- return 1.0 / math.pow(1.0 + dr, alpha)
+ return math.pow(1.0 + dr, -alpha)
def _half_life_kernel(dr: float) -> float:
hl = _get_float_param(params, "exit_half_life")
"""
Compute PnL target coefficient based on PnL/target ratio using tanh.
- Returns a coefficient (typically 0.5-2.0) to be multiplied with base_factor.
+ For nonnegative tunables, amplification ranges from 1 to ``1 + win_reward_factor``
+ for gains, or ``1 + win_reward_factor * rr`` for losses. Here ``rr`` is the positive
+ ``risk_reward_ratio``, or ``RISK_REWARD_RATIO_DEFAULT`` otherwise.
The coefficient rewards trades that exceed pnl_target and penalizes losses
beyond the risk/reward threshold.
risk_reward_ratio: Risk/reward ratio for loss penalty calculation
Returns:
- float: Coefficient ≥ 0.0 (typically 0.5-2.0 range)
+ float: Nonnegative amplification coefficient for nonnegative tunables.
"""
pnl_target_coefficient = 1.0
Whether to apply action masking (affects invalid action penalty)
prev_potential : float, optional
Previous state potential Φ(s), by default np.nan
+ next_context : RewardContext, optional
+ Actual marked state after advancing time, before terminal liquidation.
+ Required when a terminal transition leaves a position open.
+ terminated : bool
+ Close any resulting open position and clear the terminal potential.
Returns
-------
short_allowed=short_allowed,
)
- base_reward: float | None = None
+ base_reward = 0.0
if not is_valid and not action_masking:
breakdown.invalid_penalty = _get_float_param(params, "invalid_action")
- base_reward = breakdown.invalid_penalty
base_factor = _get_float_param(params, "base_factor", base_factor)
context.trade_duration, max_trade_duration_candles
)
- # Base reward calculation
- if base_reward is None:
- if context.action == Actions.Neutral:
- if context.position == Positions.Neutral:
- base_reward = _idle_penalty(context, idle_factor, params)
- breakdown.idle_penalty = base_reward
- elif context.position in (Positions.Long, Positions.Short):
- base_reward = _hold_penalty(context, hold_factor, params)
- breakdown.hold_penalty = base_reward
- else:
- base_reward = 0.0
- else:
- is_exit_action = (
- context.action == Actions.Long_exit and context.position == Positions.Long
- ) or (context.action == Actions.Short_exit and context.position == Positions.Short)
- if is_exit_action:
- base_reward = _compute_exit_reward(
- base_factor,
- pnl_target,
- current_duration_ratio,
- context,
- params,
- risk_reward_ratio,
- )
- breakdown.exit_component = base_reward
- breakdown.exit_pnl = context.current_pnl
- else:
- base_reward = 0.0
+ # Time advances even when an invalid action leaves the physical state unchanged.
+ if context.action == Actions.Neutral or not is_valid:
+ if context.position == Positions.Neutral:
+ base_reward = _idle_penalty(context, idle_factor, params)
+ breakdown.idle_penalty = base_reward
+ elif context.position in (Positions.Long, Positions.Short):
+ base_reward = _hold_penalty(context, hold_factor, params)
+ breakdown.hold_penalty = base_reward
+ else:
+ is_exit_action = (
+ context.action == Actions.Long_exit and context.position == Positions.Long
+ ) or (context.action == Actions.Short_exit and context.position == Positions.Short)
+ if is_exit_action:
+ base_reward = _compute_exit_reward(
+ base_factor,
+ pnl_target,
+ current_duration_ratio,
+ context,
+ params,
+ risk_reward_ratio,
+ )
+ breakdown.exit_component = base_reward
+ breakdown.exit_pnl = context.current_pnl
+ base_reward += breakdown.invalid_penalty
breakdown.base_reward = float(base_reward)
# === PBRS INTEGRATION ===
next_duration_ratio = _compute_duration_ratio(
next_context.trade_duration, max_trade_duration_candles
)
- terminal_context = next_context if next_context is not None else context
breakdown.terminal_liquidation = bool(
terminated and next_position in (Positions.Long, Positions.Short)
)
if breakdown.terminal_liquidation:
+ if next_context is None or next_context.position != next_position:
+ raise ValueError(
+ "next_context with the resulting open position is required for terminal liquidation"
+ )
liquidation_reward = _compute_exit_reward(
base_factor,
pnl_target,
next_duration_ratio,
- terminal_context,
+ next_context,
params,
risk_reward_ratio,
)
base_reward += liquidation_reward
breakdown.base_reward = float(base_reward)
breakdown.exit_component += liquidation_reward
- breakdown.exit_pnl = terminal_context.current_pnl
+ breakdown.exit_pnl = next_context.current_pnl
# Resolve effective PBRS and additive enablement for every transition.
exit_mode = _resolve_exit_potential_mode(
)
hold_potential_enabled = _get_bool_param(params, "hold_potential_enabled")
- entry_additive_enabled = (
- False if exit_mode == "canonical" else _get_bool_param(params, "entry_additive_enabled")
- )
- exit_additive_enabled = (
- False if exit_mode == "canonical" else _get_bool_param(params, "exit_additive_enabled")
+ entry_additive_enabled, exit_additive_enabled, _ = _resolve_additive_enablement(
+ exit_mode,
+ _get_bool_param(params, "entry_additive_enabled"),
+ _get_bool_param(params, "exit_additive_enabled"),
)
pbrs_enabled = bool(hold_potential_enabled or entry_additive_enabled or exit_additive_enabled)
)
if breakdown.terminal_liquidation and exit_additive_enabled:
exit_additive += _compute_exit_additive(
- terminal_context.current_pnl, pnl_target, next_duration_ratio, params, base_factor
+ next_pnl, pnl_target, next_duration_ratio, params, base_factor
)
if terminated:
next_potential = 0.0
idle_duration: int,
max_idle_duration_candles: int,
action_masking: bool = True,
+ max_trade_duration_cap: int | None = None,
) -> tuple[Actions, float, float, float]:
+ if (
+ max_trade_duration_cap is not None
+ and position in (Positions.Long, Positions.Short)
+ and trade_duration >= max_trade_duration_cap
+ ):
+ action = Actions.Long_exit if position == Positions.Long else Actions.Short_exit
+ return action, float("nan"), 1.0, float("nan")
+
entry_prob, exit_prob, neutral_prob = _sampling_probabilities(
position,
short_allowed=short_allowed,
max_idle_duration_candles = get_max_idle_duration_candles(
params, max_trade_duration_candles=max_trade_duration_candles
)
- max_trade_duration_cap = _scale_duration(max_trade_duration_candles, max_duration_ratio)
+ max_trade_duration_cap = max(1, _scale_duration(max_trade_duration_candles, max_duration_ratio))
samples: list[dict[str, float | None]] = []
prev_potential: float = 0.0
idle_duration=idle_duration,
max_idle_duration_candles=max_idle_duration_candles,
action_masking=action_masking,
+ max_trade_duration_cap=max_trade_duration_cap,
)
context = RewardContext(
idle_duration = 0
entry_open = current_open
if position in (Positions.Long, Positions.Short):
- trade_duration = min(trade_duration + 1, max_trade_duration_cap)
+ trade_duration += 1
else:
idle_duration += 1
# Simulate synthetic open-price movement.
# INVARIANT 1: masked sampling must only emit legal actions; unmasked
# actions may be invalid but cannot change position before terminal liquidation.
if _get_bool_param(params, "action_masking", True):
- long_exits = df[(df["action"] == 2.0) & (df["position"] != 1.0)]
- short_exits = df[(df["action"] == 4.0) & (df["position"] != 0.0)]
+ long_exits = df[
+ (df["action"] == Actions.Long_exit.value) & (df["position"] != Positions.Long.value)
+ ]
+ short_exits = df[
+ (df["action"] == Actions.Short_exit.value) & (df["position"] != Positions.Short.value)
+ ]
if len(long_exits) > 0:
raise AssertionError(f"Sim: {len(long_exits)} Long_exit actions without Long position")
if len(short_exits) > 0:
f"Sim: {len(short_exits)} Short_exit actions without Short position"
)
- long_entries = df[(df["action"] == 1.0) & (df["position"] != 0.5)]
- short_entries = df[(df["action"] == 3.0) & (df["position"] != 0.5)]
+ long_entries = df[
+ (df["action"] == Actions.Long_enter.value) & (df["position"] != Positions.Neutral.value)
+ ]
+ short_entries = df[
+ (df["action"] == Actions.Short_enter.value)
+ & (df["position"] != Positions.Neutral.value)
+ ]
if len(long_entries) > 0:
raise AssertionError(
f"Sim: {len(long_entries)} Long_enter actions without Neutral position"
raise AssertionError(f"Sim: {len(changed_on_invalid)} invalid actions changed position")
# INVARIANT 2: Duration logic
- neutral_with_trade = df[(df["position"] == 0.5) & (df["trade_duration"] > 0)]
+ neutral_with_trade = df[
+ (df["position"] == Positions.Neutral.value) & (df["trade_duration"] > 0)
+ ]
if len(neutral_with_trade) > 0:
raise AssertionError(
f"Sim: {len(neutral_with_trade)} Neutral positions with non-zero trade_duration"
)
- inpos_with_idle = df[(df["position"] != 0.5) & (df["idle_duration"] > 0)]
+ inpos_with_idle = df[(df["position"] != Positions.Neutral.value) & (df["idle_duration"] > 0)]
if len(inpos_with_idle) > 0:
raise AssertionError(
f"Sim: {len(inpos_with_idle)} in-position samples with idle_duration > 0"
)
# INVARIANT 3: Neutral states have zero PnL (simulation design)
- neutral_with_pnl = df[(df["position"] == 0.5) & (df["pnl"].abs() > eps_pnl)]
+ neutral_with_pnl = df[(df["position"] == Positions.Neutral.value) & (df["pnl"].abs() > eps_pnl)]
if len(neutral_with_pnl) > 0:
raise AssertionError(f"Sim: {len(neutral_with_pnl)} Neutral positions with non-zero pnl")
pnl_target = _resolve_pnl_target(profit_aim, risk_reward_ratio)
total = len(df)
# Map numeric position codes to readable labels to avoid casting Neutral (0.5) to 0
- pos_label_map = {0.0: "Short", 0.5: "Neutral", 1.0: "Long"}
+ pos_label_map = {position.value: position.name for position in Positions}
pos_labeled = df["position"].map(pos_label_map)
pos_counts = (
pos_labeled.value_counts().reindex(["Short", "Neutral", "Long"]).fillna(0).astype(int)
"--max_duration_ratio",
type=float,
default=2.5,
- help="Multiple of max duration used when sampling trade/idle durations.",
+ help="Synthetic trade-duration bound multiplier; positions exit at the bound (minimum one candle).",
)
parser.add_argument(
"--pnl_base_std",
f.write("| Feature | KL Div | JS Dist | Wasserstein | KS Stat | KS p-value |\n")
f.write("|---------|--------|---------|-------------|---------|------------|\n")
- features = ["pnl", "trade_duration", "idle_duration"]
- for feature in features:
+ for feature in _DISTRIBUTION_SHIFT_FEATURES:
kl = distribution_shift.get(f"{feature}_kl_divergence", np.nan)
js = distribution_shift.get(f"{feature}_js_distance", np.nan)
ws = distribution_shift.get(f"{feature}_wasserstein", np.nan)
| ID | Category | Description | Owning test | Notes |
| --------------------------------------------- | ---------- | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| report-additives-deterministic-092 | components | Additives deterministic report section | components/test_additives.py::test_additive_activation_deterministic_contribution | Integration/PBRS may reference outcome non-owning |
-| robustness-decomposition-integrity-101 | robustness | Single active core component equals total reward under mutually exclusive scenarios | robustness/test_robustness.py::test_decomposition_integrity | Scenarios: idle, hold, exit, invalid; non-owning refs integration/test_reward_calculation.py |
+| robustness-decomposition-integrity-101 | robustness | Selected scenarios isolate idle, hold, exit and pre-overtime invalid costs | robustness/test_robustness.py::test_decomposition_integrity | Invalid overtime costs are covered by components-invalid-time-costs-160 |
| robustness-exit-mode-fallback-102 | robustness | Unknown exit_attenuation_mode falls back to linear w/ warning | robustness/test_robustness.py::test_robustness_102_unknown_exit_mode_fallback_linear | |
| robustness-negative-grace-clamp-103 | robustness | Negative exit_plateau_grace clamps to 0.0 w/ warning | robustness/test_robustness.py::test_robustness_103_negative_plateau_grace_clamped | |
| robustness-invalid-power-tau-104 | robustness | Invalid power tau falls back alpha=1.0 w/ warning | robustness/test_robustness.py::test_robustness_104_invalid_power_tau_fallback_alpha_one | |
| api-unmasked-sample-probabilities-144 | api | Unmasked sample probabilities match marginal valid-action frequencies | api/test_api_helpers.py::test_unmasked_sampling_probabilities_match_action_frequencies | Spot/futures entries, long/short exits, and neutral probability |
| statistics-report-inference-projection-146 | statistics | Constant confidence intervals require declared independence, even with an empty hypothesis map | statistics/test_statistics.py::test_report_projects_constant_intervals_only_with_declared_independence | Numeric projection; True → False → default rewrites the same report; non-owning bounds reference: statistics-constant-dist-exact-ci-113a |
| statistics-report-feature-availability-147 | statistics | Complete importance exports require a fitted model and finite means and standard deviations | statistics/test_statistics.py::test_report_certifies_feature_importance_only_from_finite_estimates | Real finite-zero, single-feature, empty-feature and one-test-row states; numerical CSV and availability. |
-
-| cli-effective-reward-boundaries-145 | cli | Effective profit-target fallback and explicit zero idle denominator reach artifacts | cli/test_cli_params_and_csv.py::test_zero_target_and_idle_cap_reach_rewards_and_manifest | Metadata and near-target market coverage agree with runtime boundaries |
-| pbrs-native-integer-durations-150 | pbrs | Native integer durations and adjustment metadata retain exact values | pbrs/test_pbrs.py::test_native_integer_durations_preserve_exact_values | Python, signed NumPy and unsigned NumPy integers; strict and relaxed |
-| pbrs-native-duration-bounds-151 | pbrs | Floating duration bounds are checked before truncation without narrowing | pbrs/test_pbrs.py::test_native_duration_bounds_precede_truncation | Extended-precision cases run when supported by the platform |
-| pbrs-native-gamma-reward-148 | pbrs | Finite out-of-range native gamma clamps reach the public reward | pbrs/test_pbrs.py::test_native_finite_gamma_clamps_the_public_reward | Extended-range cases run when supported by the platform |
-| pbrs-relaxed-native-gamma-149 | pbrs | Relaxed native gamma clamps reach rewards while strict validation rejects | pbrs/test_pbrs.py::test_relaxed_native_gamma_clamps_before_reward_calculation | Preserves direct, strict and relaxed boundary contracts |
-| api-native-duration-consumers-152 | api | Validated extended native durations reach real trajectories and finite rewards | api/test_api_helpers.py::test_simulation_consumes_extended_native_duration_without_narrowing | Includes the actual exit hazard and terminal state |
-| api-fractional-duration-cap-153 | api | Ordinary fractional caps retain truncation and rounded boundaries | api/test_api_helpers.py::test_simulation_preserves_small_fractional_duration_caps | Includes zero cap and one-third / one-seventh ratios |
-| api-nonfinite-duration-fallback-154 | api | Explicit non-finite native durations preserve configured/default idle-cap precedence | api/test_api_helpers.py::test_nonfinite_explicit_duration_uses_configured_then_default_idle_cap | Python and NumPy NaN and both infinities |
-| api-unbounded-duration-report-155 | api | Public reports retain exact duration configuration beyond integer-string digit limits | api/test_api_helpers.py::test_public_report_preserves_unbounded_duration_configuration | Leaves interpreter globals and boolean override semantics unchanged |
-| statistics-extended-duration-mass-156 | statistics | Large-duration bins retain all observations, reward means and declared ratio units | statistics/test_statistics.py::test_extended_duration_relationship_bins_preserve_mass_and_reward_means | Actual simulator output |
-| statistics-exact-duration-boundary-157 | statistics | Adjacent integer observations at extended right-closed boundaries stay distinguishable | statistics/test_statistics.py::test_extended_duration_bins_distinguish_exact_right_closed_boundaries | Includes outside-range clipping |
-| statistics-candle-duration-units-158 | statistics | Ordinary duration relationship tables retain candle-valued boundaries | statistics/test_statistics.py::test_ordinary_duration_relationship_bins_retain_candle_units | Unchanged ordinary output scale |
-| statistics-nullable-duration-bins-159 | statistics | Nullable numeric duration observations remain excluded from aggregates at extended scales | statistics/test_statistics.py::test_extended_duration_bins_preserve_nullable_observation_semantics | Counts and reward means use only nonmissing observations |
+| cli-effective-reward-boundaries-145 | cli | Effective profit-target fallback and explicit zero idle denominator reach artifacts | cli/test_cli_params_and_csv.py::test_zero_target_and_idle_cap_reach_rewards_and_manifest | Metadata and near-target market coverage agree with runtime boundaries |
+| pbrs-native-integer-durations-150 | pbrs | Native integer durations and adjustment metadata retain exact values | pbrs/test_pbrs.py::test_native_integer_durations_preserve_exact_values | Python, signed NumPy and unsigned NumPy integers; strict and relaxed |
+| pbrs-native-duration-bounds-151 | pbrs | Floating duration bounds are checked before truncation without narrowing | pbrs/test_pbrs.py::test_native_duration_bounds_precede_truncation | Extended-precision cases run when supported by the platform |
+| pbrs-native-gamma-reward-148 | pbrs | Finite out-of-range native gamma clamps reach the public reward | pbrs/test_pbrs.py::test_native_finite_gamma_clamps_the_public_reward | Extended-range cases run when supported by the platform |
+| pbrs-relaxed-native-gamma-149 | pbrs | Relaxed native gamma clamps reach rewards while strict validation rejects | pbrs/test_pbrs.py::test_relaxed_native_gamma_clamps_before_reward_calculation | Preserves direct, strict and relaxed boundary contracts |
+| api-native-duration-consumers-152 | api | Validated extended native durations reach real trajectories and finite rewards | api/test_api_helpers.py::test_simulation_consumes_extended_native_duration_without_narrowing | Includes the actual exit hazard and terminal state |
+| api-nonfinite-duration-fallback-154 | api | Explicit non-finite native durations preserve configured/default idle-cap precedence | api/test_api_helpers.py::test_nonfinite_explicit_duration_uses_configured_then_default_idle_cap | Python and NumPy NaN and both infinities |
+| api-unbounded-duration-report-155 | api | Public reports retain exact duration configuration beyond integer-string digit limits | api/test_api_helpers.py::test_public_report_preserves_unbounded_duration_configuration | Leaves interpreter globals and boolean override semantics unchanged |
+| statistics-extended-duration-mass-156 | statistics | Large-duration bins retain all observations, reward means and declared ratio units | statistics/test_statistics.py::test_extended_duration_relationship_bins_preserve_mass_and_reward_means | Actual simulator output |
+| statistics-exact-duration-boundary-157 | statistics | Adjacent integer observations at extended right-closed boundaries stay distinguishable | statistics/test_statistics.py::test_extended_duration_bins_distinguish_exact_right_closed_boundaries | Includes outside-range clipping |
+| statistics-candle-duration-units-158 | statistics | Ordinary duration relationship tables retain candle-valued boundaries | statistics/test_statistics.py::test_ordinary_duration_relationship_bins_retain_candle_units | Unchanged ordinary output scale |
+| statistics-nullable-duration-bins-159 | statistics | Nullable numeric duration observations remain excluded from aggregates at extended scales | statistics/test_statistics.py::test_extended_duration_bins_preserve_nullable_observation_semantics | Counts and reward means use only nonmissing observations |
+| components-invalid-time-costs-160 | components | Invalid no-ops retain the current state's idle or hold cost | components/test_reward_components.py::test_invalid_actions_add_to_the_unchanged_states_time_cost | Neutral, long and short states |
+| components-power-subnormal-161 | components | Power attenuation retains representable subnormals and underflows without changing kernels | components/test_reward_components.py::test_power_kernel_retains_subnormal_values_and_true_underflow | No absolute tolerance masking |
+| pbrs-physical-duration-bound-162 | pbrs | Synthetic duration bounds produce valid exits without freezing the physical clock | pbrs/test_pbrs.py::test_synthetic_duration_cap_closes_trades_without_freezing_the_clock | Includes fractional one-candle minimum, unmasked sampling and terminal PBRS |
+| pbrs-terminal-next-state-163 | pbrs | Terminal liquidation requires the actual marked next state | pbrs/test_pbrs.py::test_terminal_liquidation_requires_the_actual_marked_next_state | Both sides, fees, missing and wrong-side states |
### Non-Owning Smoke / Reference Checks
self.assertTrue(df.iloc[-1]["terminated"])
self.assertEqual(df.iloc[-1]["next_position"], Positions.Neutral.value)
- def test_simulation_preserves_small_fractional_duration_caps(self):
- """Keep ordinary float-product truncation, including zero and rounded boundaries."""
- for duration, ratio, expected in ((1, 0.5, 0), (3, 1 / 3, 1), (7, 1 / 7, 1), (10, 0.3, 3)):
- with self.subTest(duration=duration, ratio=ratio):
- df = simulate_samples_with_defaults(
- self.base_params(max_trade_duration_candles=duration),
- num_samples=8,
- seed=42,
- trading_mode="spot",
- max_duration_ratio=ratio,
- )
- self.assertEqual(df["next_trade_duration"].max(), expected)
-
def test_public_report_preserves_unbounded_duration_configuration(self):
"""The real report retains exact integer configuration beyond Python digit limits."""
duration = 10**4900
values = (
df[["sample_entry_prob", "sample_exit_prob", "sample_neutral_prob"]].stack().dropna()
)
- prob_upper_bound = SCENARIOS.API_PROBABILITY_UPPER_BOUND
- self.assertTrue(((values >= 0.0) & (values <= prob_upper_bound)).all())
+ self.assertTrue(((values >= 0.0) & (values <= 1.0)).all())
def test_unmasked_simulation_samples_invalid_actions_without_changing_position(self):
"""Unmasked actions expose penalties without inventing trades or wrong-side exits."""
self.assertGreater(len(invalid), 0)
self.assertLess(len(invalid), len(df))
self.assertTrue((invalid["reward_invalid"] == params["invalid_action"]).all())
- self.assertTrue((invalid["reward_base"] == params["invalid_action"]).all())
+ np.testing.assert_allclose(
+ invalid["reward_base"],
+ invalid["reward_invalid"]
+ + invalid["reward_idle"]
+ + invalid["reward_hold"]
+ + invalid["reward_exit"],
+ atol=TOLERANCE.IDENTITY_STRICT,
+ rtol=0.0,
+ )
wrong_exit = invalid.loc[
(
(
self.assertAlmostEqualFloat(
breakdown.total,
breakdown.invalid_penalty
+ + breakdown.hold_penalty
+ breakdown.reward_shaping
+ breakdown.entry_additive
+ breakdown.exit_additive,
tolerance=TOLERANCE.IDENTITY_RELAXED,
- msg="Total should equal invalid penalty plus shaping/additives",
+ msg="Invalid actions retain the current position's time cost",
)
def test_new_invariant_and_warn_parameters(self):
_compute_efficiency_coefficient,
_compute_hold_potential,
_compute_pnl_target_coefficient,
+ _compute_time_attenuation_coefficient,
_get_exit_factor,
_get_float_param,
get_max_idle_duration_candles,
class TestRewardComponents(RewardSpaceTestBase):
+ def test_invalid_actions_add_to_the_unchanged_states_time_cost(self):
+ """Retain the physical time cost when an invalid action cannot trade."""
+ params = self.base_params(hold_potential_enabled=False)
+ trade_duration = 3 * params["max_trade_duration_candles"]
+ idle_duration = 3 * get_max_idle_duration_candles(params)
+ for position, invalid_action in (
+ (Positions.Neutral, Actions.Long_exit),
+ (Positions.Long, Actions.Long_enter),
+ (Positions.Short, Actions.Short_enter),
+ ):
+ with self.subTest(position=position):
+ common = {
+ "pnl": 0.0,
+ "trade_duration": trade_duration,
+ "idle_duration": idle_duration,
+ "max_unrealized_profit": 0.0,
+ "min_unrealized_profit": 0.0,
+ "position": position,
+ }
+ valid = calculate_reward_with_defaults(
+ self.make_ctx(**common, action=Actions.Neutral), params, action_masking=False
+ )
+ invalid = calculate_reward_with_defaults(
+ self.make_ctx(**common, action=invalid_action), params, action_masking=False
+ )
+ self.assertLess(valid.base_reward, params["invalid_action"])
+ self.assertAlmostEqualFloat(
+ invalid.base_reward,
+ valid.base_reward + params["invalid_action"],
+ tolerance=TOLERANCE.IDENTITY_STRICT,
+ )
+ self.assertEqual(invalid.idle_penalty, valid.idle_penalty)
+ self.assertEqual(invalid.hold_penalty, valid.hold_penalty)
+
+ def test_power_kernel_retains_subnormal_values_and_true_underflow(self):
+ """Avoid intermediate overflow without substituting a different kernel."""
+ params = self.base_params(
+ exit_attenuation_mode="power",
+ exit_plateau=False,
+ exit_power_tau=SCENARIOS.POWER_SUBNORMAL_TAU,
+ )
+ ratio = SCENARIOS.POWER_SUBNORMAL_DURATION
+ expected = math.exp(
+ math.log(SCENARIOS.POWER_SUBNORMAL_TAU) / math.log(2) * math.log1p(ratio)
+ )
+ actual = _compute_time_attenuation_coefficient(ratio, params)
+ self.assertGreater(actual, 0.0)
+ # Absolute tolerances would accept the erroneous linear fallback.
+ self.assertTrue(math.isclose(actual, expected, rel_tol=TOLERANCE.RELATIVE, abs_tol=0.0))
+ self.assertEqual(
+ _compute_time_attenuation_coefficient(SCENARIOS.POWER_UNDERFLOW_DURATION, params), 0.0
+ )
+
def test_hold_potential_computation_finite(self):
"""Test hold potential computation returns finite values."""
params = {
API_ENTRY_RATE_DRAWS: Draw count for entry-rate estimation (2000)
API_MAX_TRADE_DURATION_CANDLES: Max trade duration used in API simulation tests (40)
API_MAX_DURATION_RATIO: Max duration ratio used in API simulation tests (1.5)
- API_PROBABILITY_UPPER_BOUND: Upper bound for exposed sampling probabilities (0.9)
API_EXTREME_BASE_FACTOR: Extreme base_factor used to trigger warning paths (10000000.0)
+ CLOCK_TRADE_SCALE: One-candle reward scale exposing the synthetic clock boundary
+ CLOCK_MIN_DURATION_RATIO: Fractional cap requiring a one-candle physical minimum (0.5)
+ CLOCK_DURATION_RATIO: Two-candle synthetic cap after ordinary truncation (2.5)
+ TERMINAL_ENTRY_FEE: Symmetric fee used by terminal liquidation oracles (0.001)
+ POWER_SUBNORMAL_TAU: Strictly accepted power-kernel tau (1e-6)
+ POWER_SUBNORMAL_DURATION: Ratio with a representable subnormal result (1e16)
+ POWER_UNDERFLOW_DURATION: Ratio whose power-kernel result underflows to zero (1e18)
# CLI smoke parameters
CLI_NUM_SAMPLES_STANDARD: Default CLI sample size for smoke runs (200)
API_ENTRY_RATE_DRAWS: int = 2000
API_MAX_TRADE_DURATION_CANDLES: int = 40
API_MAX_DURATION_RATIO: float = 1.5
- API_PROBABILITY_UPPER_BOUND: float = 0.9
API_EXTREME_BASE_FACTOR: float = 10_000_000.0
+ CLOCK_TRADE_SCALE: int = 1
+ CLOCK_MIN_DURATION_RATIO: float = 0.5
+ CLOCK_DURATION_RATIO: float = 2.5
+ TERMINAL_ENTRY_FEE: float = 0.001
+ POWER_SUBNORMAL_TAU: float = 1e-6
+ POWER_SUBNORMAL_DURATION: float = 1e16
+ POWER_UNDERFLOW_DURATION: float = 1e18
# CLI smoke parameters
CLI_NUM_SAMPLES_STANDARD: int = 200
class TestSimulationParity(RewardSpaceTestBase):
"""Synthetic durations and reports follow runtime transition semantics."""
+ def test_synthetic_duration_cap_closes_trades_without_freezing_the_clock(self):
+ """Bound physical trade duration with valid exits and truthful probabilities."""
+ scale = SCENARIOS.CLOCK_TRADE_SCALE
+ params = self.base_params(
+ max_trade_duration_candles=scale,
+ action_masking=False,
+ hold_potential_enabled=True,
+ exit_potential_mode="canonical",
+ )
+ for ratio in (SCENARIOS.CLOCK_MIN_DURATION_RATIO, SCENARIOS.CLOCK_DURATION_RATIO):
+ with self.subTest(ratio=ratio):
+ cap = max(1, int(scale * ratio))
+ df = simulate_samples(
+ params=params,
+ num_samples=SCENARIOS.SAMPLE_SIZE_LARGE,
+ seed=SEEDS.BASE,
+ base_factor=PARAMS.BASE_FACTOR,
+ profit_aim=PARAMS.PROFIT_AIM,
+ risk_reward_ratio=PARAMS.RISK_REWARD_RATIO,
+ max_duration_ratio=ratio,
+ trading_mode="futures",
+ pnl_base_std=PARAMS.PNL_STD,
+ pnl_duration_vol_scale=PARAMS.PNL_DUR_VOL_SCALE,
+ )
+ age = forced_exits = 0
+ for row in df.itertuples():
+ self.assertEqual(row.trade_duration, age)
+ if row.position != Positions.Neutral.value and age == cap:
+ expected = (
+ Actions.Long_exit
+ if row.position == Positions.Long.value
+ else Actions.Short_exit
+ )
+ self.assertEqual(row.action, expected.value)
+ self.assertEqual(row.sample_exit_prob, 1.0)
+ self.assertEqual(row.is_invalid, 0.0)
+ self.assertEqual(row.next_position, Positions.Neutral.value)
+ forced_exits += 1
+ if row.next_position == Positions.Neutral.value:
+ age = 0
+ elif row.position == Positions.Neutral.value:
+ age = 1
+ else:
+ age += 1
+ self.assertEqual(row.next_trade_duration, age)
+ self.assertLessEqual(age, cap)
+ self.assertGreater(forced_exits, 0)
+ verification = reward_space_analysis.verify_pbrs_trajectory(
+ df, float(params["potential_gamma"])
+ )
+ self.assertTrue(verification["verified"], verification)
+
+ def test_terminal_liquidation_requires_the_actual_marked_next_state(self):
+ """Reject absent/wrong-side states and retain fee-aware terminal entry losses."""
+ fee = SCENARIOS.TERMINAL_ENTRY_FEE
+ params = self.base_params(
+ base_factor=PARAMS.BASE_FACTOR,
+ entry_fee_rate=fee,
+ exit_fee_rate=fee,
+ hold_potential_enabled=False,
+ efficiency_weight=0.0,
+ )
+ for side, action, pnl in (
+ (Positions.Long, Actions.Long_enter, 1 / (1 + fee) ** 2 - 1),
+ (Positions.Short, Actions.Short_enter, 1 - (1 + fee) ** 2),
+ ):
+ with self.subTest(side=side):
+ context = self.make_ctx(
+ pnl=0.0,
+ trade_duration=0,
+ idle_duration=0,
+ max_unrealized_profit=0.0,
+ min_unrealized_profit=0.0,
+ position=Positions.Neutral,
+ action=action,
+ )
+ with self.assertRaisesRegex(ValueError, "next_context"):
+ calculate_reward_with_defaults(context, params, terminated=True)
+ wrong = self.make_ctx(
+ pnl=0.0,
+ trade_duration=0,
+ idle_duration=0,
+ max_unrealized_profit=0.0,
+ min_unrealized_profit=0.0,
+ position=Positions.Neutral,
+ action=Actions.Neutral,
+ )
+ with self.assertRaisesRegex(ValueError, "next_context"):
+ calculate_reward_with_defaults(
+ context, params, terminated=True, next_context=wrong
+ )
+ next_context = self.make_ctx(
+ pnl=pnl,
+ trade_duration=1,
+ idle_duration=0,
+ max_unrealized_profit=pnl,
+ min_unrealized_profit=pnl,
+ position=side,
+ action=Actions.Neutral,
+ )
+ result = calculate_reward_with_defaults(
+ context, params, terminated=True, next_context=next_context
+ )
+ self.assertTrue(result.terminal_liquidation)
+ self.assertAlmostEqualFloat(
+ result.total, PARAMS.BASE_FACTOR * pnl, tolerance=TOLERANCE.IDENTITY_STRICT
+ )
+ self.assertEqual(result.exit_pnl, pnl)
+
def test_neutral_terminal_closes_stored_potential(self):
context = reward_space_analysis.RewardContext(
current_pnl=0.0,
# Owns invariant: robustness-decomposition-integrity-101 (robustness category)
def test_decomposition_integrity(self):
- """reward must equal the single active core component under mutually exclusive scenarios (idle/hold/exit/invalid)."""
+ """Isolate idle, hold, exit and pre-overtime invalid costs in selected scenarios."""
scenarios = [
{
"ctx": self.make_ctx(
self.assertNotEqual(result["gamma"], 0.91)
def test_incompatible_or_missing_study_identity_discards_old_trials(self):
- for identity in (None, "terminal-liquidation-risk-normalized-trained-policy-v3"):
+ for identity in (
+ None,
+ "terminal-liquidation-risk-normalized-trained-policy-v3",
+ "terminal-liquidation-risk-normalized-trained-policy-v5",
+ ):
with self.subTest(identity=identity):
model = self.model()
study = optuna.create_study(
{},
{"params": {"gamma": 0.1}},
{"objective_identity": "legacy", "params": {"gamma": 0.1}},
+ {
+ "objective_identity": "terminal-liquidation-risk-normalized-trained-policy-v5",
+ "params": {"gamma": 0.1},
+ },
{"objective_identity": ReforceXY._OPTUNA_OBJECTIVE_IDENTITY, "params": []},
):
with self.subTest(payload=payload):
--- /dev/null
+"""Reward costs, numerical boundaries and the native model-selection objective."""
+
+import math
+import tempfile
+from types import SimpleNamespace
+
+import numpy as np
+import pandas as pd
+from qa_support import PAIR, QaTestCase, model_config
+from sb3_contrib.common.maskable.evaluation import evaluate_policy
+
+from ReforceXY.user_data.freqaimodels.ReforceXY import (
+ Actions,
+ MyRLEnv,
+ Positions,
+ ReforceXY,
+)
+
+
+class _TradeOrWaitPolicy:
+ """Choose an observable long round trip, or remain in cash."""
+
+ def __init__(self, trade):
+ self.trade = trade
+
+ def predict(self, observation, state=None, **kwargs):
+ positions = observation[:, -1, -2]
+ actions = (
+ np.where(
+ positions == Positions.Neutral.value,
+ Actions.Long_enter.value,
+ Actions.Long_exit.value,
+ )
+ if self.trade
+ else np.full(len(observation), Actions.Neutral.value)
+ )
+ return actions, state
+
+
+class RewardContractsTest(QaTestCase):
+ def env(self, *, opens=None, **parameters):
+ prices = pd.DataFrame({"open": [100.0] * 16 if opens is None else opens})
+ env = MyRLEnv(
+ df=prices.copy(),
+ prices=prices,
+ df_raw=prices.copy(),
+ window_size=1,
+ reward_kwargs={"rr": 2.0, "profit_aim": 0.03},
+ fee=0.0,
+ can_short=True,
+ config={
+ "stake_amount": "unlimited",
+ "freqai": {
+ "rl_config": {
+ "add_state_info": True,
+ "max_training_drawdown_pct": 0.99,
+ "model_reward_parameters": {
+ "max_trade_duration_candles": 1,
+ "max_idle_duration_candles": 1,
+ "hold_potential_enabled": False,
+ **parameters,
+ },
+ }
+ },
+ },
+ live=True,
+ )
+ self.addCleanup(env.close)
+ env.reset(seed=42)
+ return env
+
+ def test_invalid_noop_keeps_time_cost_for_every_position(self):
+ for entry, invalid in (
+ (None, Actions.Long_exit),
+ (Actions.Long_enter, Actions.Long_enter),
+ (Actions.Short_enter, Actions.Short_enter),
+ ):
+ for penalty in (0.0, -2.0):
+ with self.subTest(entry=entry, penalty=penalty):
+ valid = self.env(invalid_action=penalty)
+ invalid_env = self.env(invalid_action=penalty)
+ for env in (valid, invalid_env):
+ if entry is not None:
+ env.step(entry.value)
+ for _ in range(3):
+ env.step(Actions.Neutral.value)
+ obs, reward, _, _, info = valid.step(Actions.Neutral.value)
+ bad_obs, bad_reward, _, _, bad_info = invalid_env.step(invalid.value)
+ np.testing.assert_array_equal(bad_obs, obs)
+ self.assertLess(reward, 0.0)
+ self.assertAlmostEqual(bad_reward, reward + penalty)
+ self.assertEqual(bad_info["reward_idle"], info["reward_idle"])
+ self.assertEqual(bad_info["reward_hold"], info["reward_hold"])
+ self.assertEqual(invalid_env._total_profit, valid._total_profit)
+ self.assertEqual(len(invalid_env.trade_history), len(valid.trade_history))
+
+ def test_valid_entry_and_exit_do_not_pay_noop_costs(self):
+ env = self.env()
+ for _ in range(3):
+ env.step(Actions.Neutral.value)
+ _, reward, _, _, info = env.step(Actions.Long_enter.value)
+ self.assertEqual(reward, 0.0)
+ self.assertEqual(info["reward_idle"], 0.0)
+ for _ in range(3):
+ env.step(Actions.Neutral.value)
+ _, reward, _, _, info = env.step(Actions.Long_exit.value)
+ self.assertEqual(reward, 0.0)
+ self.assertEqual(info["reward_hold"], 0.0)
+
+ def test_null_idle_scale_derives_trade_scale_but_zero_remains_explicit(self):
+ for idle_scale, denominator in ((None, 8), (0, 1)):
+ with self.subTest(idle_scale=idle_scale):
+ env = self.env(max_trade_duration_candles=2, max_idle_duration_candles=idle_scale)
+ for _ in range(3):
+ env.step(Actions.Neutral.value)
+ _, reward, _, _, _ = env.step(Actions.Neutral.value)
+ self.assertAlmostEqual(reward, -1.5 * (3 / denominator) ** 1.025)
+
+ def test_power_exit_preserves_python_and_numpy_tau(self):
+ for tau in (0.25, np.float32(0.25), np.longdouble(0.25), np.int64(1)):
+ with self.subTest(tau_type=type(tau)):
+ env = self.env(
+ opens=[100.0, 100.0, 105.0, 105.0],
+ exit_attenuation_mode="power",
+ exit_plateau=False,
+ exit_power_tau=tau,
+ )
+ env.step(Actions.Long_enter.value)
+ _, reward, terminated, _, _ = env.step(Actions.Long_exit.value)
+ self.assertTrue(terminated)
+ # At duration ratio one, K(1)=tau; the fee-free exit has base reward 7.5.
+ self.assertAlmostEqual(reward, 7.5 * float(tau))
+
+ def test_power_underflow_does_not_switch_to_a_linear_kernel(self):
+ env = self.env()
+ params = {"exit_attenuation_mode": "power", "exit_plateau": False, "exit_power_tau": 1e-6}
+ ratio = 1e16
+ expected = math.exp(math.log(1e-6) / math.log(2) * math.log1p(ratio))
+ actual = env._compute_time_attenuation_coefficient(ratio, params)
+ self.assertGreater(actual, 0.0)
+ # Subnormal rounding needs relative tolerance; an absolute epsilon would hide the defect.
+ self.assertTrue(math.isclose(actual, expected, rel_tol=1e-4, abs_tol=0.0))
+ self.assertEqual(env._compute_time_attenuation_coefficient(1e18, params), 0.0)
+
+ def test_native_evaluation_preserves_base_ranking_without_losing_training_shaping(self):
+ for ratio in (0.4, 1e16):
+ for stacking in (0, 2):
+ with (
+ self.subTest(ratio=ratio, stacking=stacking),
+ tempfile.TemporaryDirectory() as temp,
+ ):
+ config = model_config(
+ temp,
+ freqai={
+ "model_training_parameters": {"gamma": 0.8},
+ "rl_config": {
+ "add_state_info": True,
+ "frame_stacking": stacking,
+ "model_reward_parameters": {
+ "profit_aim": 0.025,
+ "hold_potential_enabled": True,
+ "hold_potential_ratio": ratio,
+ "max_trade_duration_candles": 1,
+ "hold_potential_transform_pnl": "clip",
+ "hold_potential_transform_duration": "clip",
+ "idle_penalty_ratio": 0.0,
+ },
+ },
+ },
+ )
+ model = ReforceXY(config=config)
+ model.live = True
+ self.addCleanup(model.close_envs)
+ prices = pd.DataFrame({"open": [100.0, 100.0, 105.0, 105.0]})
+ # Use the real factory and VecMonitor, not a replacement evaluation implementation.
+ model.set_train_and_eval_environments(
+ {"train_features": prices.copy(), "test_features": prices.copy()},
+ prices,
+ prices,
+ SimpleNamespace(pair=PAIR),
+ )
+ model.train_env.set_attr("fee", 0.0)
+ model.eval_env.set_attr("fee", 0.0)
+ trade_score, _ = evaluate_policy(
+ _TradeOrWaitPolicy(True), model.eval_env, n_eval_episodes=1, warn=False
+ )
+ cash_score, _ = evaluate_policy(
+ _TradeOrWaitPolicy(False), model.eval_env, n_eval_episodes=1, warn=False
+ )
+ self.assertAlmostEqual(trade_score, 7.5)
+ self.assertEqual(cash_score, 0.0)
+ model.train_env.reset()
+ _, rewards, _, _ = model.train_env.step([Actions.Long_enter.value])
+ np.testing.assert_allclose(rewards, [0.8 * 100 * ratio], rtol=1e-6)
+ model.close_envs()
+
+ def test_evaluation_retains_entry_and_terminal_exit_additives(self):
+ with tempfile.TemporaryDirectory() as temp:
+ fee = 0.001
+ config = model_config(
+ temp,
+ freqai={
+ "rl_config": {
+ "add_state_info": True,
+ "model_reward_parameters": {
+ "hold_potential_enabled": True,
+ "exit_potential_mode": "non_canonical",
+ "entry_additive_enabled": True,
+ "exit_additive_enabled": True,
+ "max_trade_duration_candles": 1,
+ },
+ }
+ },
+ )
+ model = ReforceXY(config=config)
+ model.live = True
+ self.addCleanup(model.close_envs)
+ prices = pd.DataFrame({"open": [100.0] * 3})
+ model.set_train_and_eval_environments(
+ {"train_features": prices.copy(), "test_features": prices.copy()},
+ prices,
+ prices,
+ SimpleNamespace(pair=PAIR),
+ )
+ model.eval_env.set_attr("fee", fee)
+ score, _ = evaluate_policy(
+ _TradeOrWaitPolicy(True), model.eval_env, n_eval_episodes=1, warn=False
+ )
+ pnl = 1 / (1 + fee) ** 2 - 1
+ entry_additive = 6.25 * 0.5 * math.tanh(pnl / 0.06)
+ terminal_additive = 6.25 * 0.5 * (math.tanh(pnl / 0.06) - math.tanh(1.0))
+ self.assertAlmostEqual(score, 100 * pnl + entry_additive + terminal_additive, places=6)
+
+ def test_monitored_scores_retain_small_returns_between_large_exits_after_reset(self):
+ for multiprocessing in (False, True):
+ with (
+ self.subTest(multiprocessing=multiprocessing),
+ tempfile.TemporaryDirectory() as temp,
+ ):
+ base = 2**25
+ small_mark = 150.0 * (1.0 + 1.0 / base)
+ config = model_config(
+ temp,
+ freqai={
+ "rl_config": {
+ "n_eval_envs": 2 if multiprocessing else 1,
+ "eval_multiprocessing": multiprocessing,
+ "model_reward_parameters": {
+ "base_factor": base,
+ "win_reward_factor": 0.0,
+ "efficiency_weight": 0.0,
+ "idle_penalty_ratio": 0.0,
+ },
+ }
+ },
+ )
+ model = ReforceXY(config=config)
+ model.live = True
+ self.addCleanup(model.close_envs)
+ prices = pd.DataFrame(
+ {
+ "open": [
+ 100.0,
+ 100.0,
+ 150.0,
+ 150.0,
+ small_mark,
+ small_mark,
+ 1.5 * small_mark,
+ ]
+ }
+ )
+ model.set_train_and_eval_environments(
+ {"train_features": prices.copy(), "test_features": prices.copy()},
+ prices,
+ prices,
+ SimpleNamespace(pair=PAIR),
+ )
+ model.eval_env.set_attr("fee", 0.0)
+ for _ in range(2):
+ model.eval_env.reset()
+ for action in (
+ Actions.Long_enter,
+ Actions.Long_exit,
+ Actions.Long_enter,
+ Actions.Long_exit,
+ Actions.Short_enter,
+ ):
+ _, _, done, infos = model.eval_env.step(
+ [action.value] * model.eval_env.num_envs
+ )
+ self.assertTrue(done.all())
+ # Three exit rewards are 2**24, 1 and -2**24; equity remains positive.
+ for info in infos:
+ self.assertEqual(info["episode"]["r"], 1.0)
+ self.assertGreater(info["total_profit"], 0.0)
+ model.close_envs()
metadata_path = dk.data_path / f"{dk.model_filename}_{METADATA}.json"
metadata = json.loads(metadata_path.read_text())
current_generation = metadata[model._DEPLOYMENT_COORDINATE_MARKER_KEY]
- metadata[model._DEPLOYMENT_COORDINATE_MARKER_KEY] = (
- "chronological-frozen-pipelines-v2"
- )
- metadata_path.write_text(json.dumps(metadata))
- for cached in (False, True):
- with self.subTest(legacy_cached=cached):
- model.dd.model_dictionary.clear()
- model.dd.meta_data_dictionary.clear()
- if cached:
- inference = model.dd.load_data(dk.pair, dk)
- action, _ = inference.predict(
- np.zeros(inference.observation_space.shape, dtype=np.float32),
- deterministic=True,
- )
- self.assertTrue(inference.action_space.contains(action))
- with self.assertRaisesRegex(DependencyException, "Reset trained models"):
- model._resolve_deployment_state(dk, dk.pair)
+ for legacy_generation in (
+ "chronological-frozen-pipelines-v2",
+ "chronological-frozen-pipelines-pbrs-v3",
+ ):
+ metadata[model._DEPLOYMENT_COORDINATE_MARKER_KEY] = legacy_generation
+ metadata_path.write_text(json.dumps(metadata))
+ for cached in (False, True):
+ with self.subTest(
+ legacy_generation=legacy_generation, legacy_cached=cached
+ ):
+ model.dd.model_dictionary.clear()
+ model.dd.meta_data_dictionary.clear()
+ if cached:
+ inference = model.dd.load_data(dk.pair, dk)
+ action, _ = inference.predict(
+ np.zeros(inference.observation_space.shape, dtype=np.float32),
+ deterministic=True,
+ )
+ self.assertTrue(inference.action_space.contains(action))
+ with self.assertRaises(DependencyException):
+ model._resolve_deployment_state(dk, dk.pair)
metadata[model._DEPLOYMENT_COORDINATE_MARKER_KEY] = current_generation
metadata_path.write_text(json.dumps(metadata))
model.dd.meta_data_dictionary.clear()
DummyVecEnv,
SubprocVecEnv,
VecEnv,
+ VecEnvWrapper,
VecFrameStack,
VecMonitor,
)
+from stable_baselines3.common.vec_env.base_vec_env import VecEnvObs, VecEnvStepReturn
+
+
+class _SelectionRewardVecEnv(VecEnvWrapper):
+ """Expose non-PBRS evaluation rewards before VecMonitor aggregates episodes.
+
+ The repository does not use VecNormalize. Adding it requires aligning train
+ and evaluation wrapper chains for SB3's normalization synchronization.
+ """
+
+ def reset(self) -> VecEnvObs:
+ return self.venv.reset()
+
+ def step_wait(self) -> VecEnvStepReturn:
+ observations, _, dones, infos = self.venv.step_wait()
+ rewards = np.fromiter(
+ (float(info["reward_selection"]) for info in infos),
+ dtype=np.float64,
+ count=self.num_envs,
+ )
+ return observations, rewards, dones, infos
+
+
+class _RewardVecMonitor(VecMonitor):
+ """Accumulate native episode scores without narrowing to float32."""
+
+ def __init__(self, venv: VecEnv):
+ super().__init__(venv)
+ self.episode_returns = np.zeros(self.num_envs, dtype=np.float64)
+
+ def reset(self) -> VecEnvObs:
+ observations = super().reset()
+ # VecMonitor.reset recreates float32 storage; keep native reward precision.
+ self.episode_returns = np.zeros(self.num_envs, dtype=np.float64)
+ return observations
def _update_eval_best_reward(callback: Any, mean_reward: float, model: Any) -> None:
_LOG_2: Final[float] = math.log(2.0)
_DEPLOYMENT_COORDINATE_MARKER_KEY: Final[str] = "reforcexy_deployment_coordinates"
- _DEPLOYMENT_COORDINATE_GENERATION: Final[str] = "chronological-frozen-pipelines-pbrs-v3"
+ _DEPLOYMENT_COORDINATE_GENERATION: Final[str] = "chronological-frozen-pipelines-pbrs-v4"
DEFAULT_BASE_FACTOR: Final[float] = 100.0
DEFAULT_DISCOUNT_GAMMA: Final[float] = 0.95
*_OPTIMIZER_CLASSES_OPTUNA,
"adam",
)
- _NET_ARCH_SIZES: Final[tuple[NetArchSize, ...]] = (
- "small",
- "medium",
- "large",
- "extra_large",
- )
+ # Shared by named architectures and plain-configuration fallbacks.
+ _NET_ARCH_WIDTHS: Final[dict[NetArchSize, int]] = {
+ "small": 128,
+ "medium": 256,
+ "large": 512,
+ "extra_large": 1024,
+ }
+ _NET_ARCH_SIZES: Final[tuple[NetArchSize, ...]] = tuple(_NET_ARCH_WIDTHS)
_STORAGE_BACKENDS: Final[tuple[StorageBackend, ...]] = ("sqlite", "file")
_SAMPLERS: Final[_Samplers] = _Samplers()
_JOURNAL_TAIL_PROBE_BYTES: Final[int] = 64 * 1024
_QUARANTINE_TIE_BREAK_LIMIT: Final[int] = 99
_BEST_PARAMS_LOCK_FILENAME: Final[str] = ".hyperopt-best-params.lock"
# Bump on objective changes that make persisted trials or best params incompatible.
- _OPTUNA_OBJECTIVE_IDENTITY: Final[str] = (
- "terminal-liquidation-risk-normalized-trained-policy-v5"
- )
+ _OPTUNA_OBJECTIVE_IDENTITY: Final[str] = "unshaped-evaluation-temporal-costs-trained-policy-v6"
_PPO_N_STEPS: Final[tuple[int, ...]] = (512, 1024, 2048, 4096)
_PPO_N_STEPS_MIN: Final[int] = min(_PPO_N_STEPS)
_PPO_N_STEPS_MAX: Final[int] = max(_PPO_N_STEPS)
if not model_params.get("policy_kwargs"):
model_params["policy_kwargs"] = {}
- default_net_arch: list[int] = [128, 128]
+ default_width = ReforceXY._NET_ARCH_WIDTHS[ReforceXY._NET_ARCH_SIZES[0]]
+ default_net_arch: list[int] = [default_width, default_width]
net_arch: list[int] | dict[str, list[int]] | NetArchSize = model_params.get(
"policy_kwargs", {}
).get("net_arch", default_net_arch)
if eval_env is not None:
eval_env = VecFrameStack(eval_env, n_stack=self.frame_stacking)
- train_env = VecMonitor(train_env)
+ train_env = _RewardVecMonitor(train_env)
if eval_env is not None:
- eval_env = VecMonitor(eval_env)
+ eval_env = _RewardVecMonitor(_SelectionRewardVecEnv(eval_env))
return train_env, eval_env
except BaseException:
ReforceXY.DEFAULT_MAX_TRADE_DURATION_CANDLES,
)
)
+ max_idle_duration = model_reward_parameters.get("max_idle_duration_candles")
self.max_idle_duration_candles: int = int(
- model_reward_parameters.get(
- "max_idle_duration_candles",
- ReforceXY.DEFAULT_IDLE_DURATION_MULTIPLIER * self.max_trade_duration_candles,
- )
+ ReforceXY.DEFAULT_IDLE_DURATION_MULTIPLIER * self.max_trade_duration_candles
+ if max_idle_duration is None
+ else max_idle_duration
)
# === PBRS COMMON PARAMETERS ===
self._potential_gamma = ReforceXY._resolve_discount_gamma(
def _power(dr: float, p: Mapping[str, Any]) -> float:
tau = p.get("exit_power_tau")
- if isinstance(tau, (int, float)):
+ if isinstance(tau, (int, float, np.integer, np.floating)):
tau = float(tau)
alpha = -math.log(tau) / ReforceXY._LOG_2 if 0.0 < tau <= 1.0 else 1.0
else:
alpha = 1.0
- return 1.0 / math.pow(1.0 + dr, alpha)
+ return math.pow(1.0 + dr, -alpha)
def _half_life(dr: float, p: Mapping[str, Any]) -> float:
hl = float(p.get("exit_half_life", ReforceXY.DEFAULT_EXIT_HALF_LIFE))
self, pnl: float, pnl_target: float, model_reward_parameters: Mapping[str, Any]
) -> float:
"""
- Compute PnL target coefficient (typically 0.5-2.0) using tanh on PnL/target ratio.
+ Compute PnL amplification: [1, 1 + win_reward_factor] for gains,
+ and [1, 1 + win_reward_factor * rr] for losses with nonnegative tunables.
"""
pnl_target_coefficient = 1.0
# 1. Invalid action
if not self.action_masking and not self._is_valid(action):
self.tensorboard_log("invalid", category="actions")
- base_reward = float(
+ self._last_invalid_penalty = float(
model_reward_parameters.get("invalid_action", ReforceXY.DEFAULT_INVALID_ACTION)
)
- self._last_invalid_penalty = float(base_reward)
max_trade_duration = max(1, self.max_trade_duration_candles)
trade_duration = self.get_trade_duration()
)
idle_factor = base_factor * (self.profit_aim / self.rr)
hold_factor = idle_factor
+ unchanged_position = not self.is_tradesignal(action)
# 2. Idle penalty
- if (
- base_reward is None
- and action == Actions.Neutral.value
- and self._position == Positions.Neutral
- ):
+ if base_reward is None and unchanged_position and self._position == Positions.Neutral:
max_idle_duration = max(1, self.max_idle_duration_candles)
idle_penalty_ratio = float(
model_reward_parameters.get(
if (
base_reward is None
and self._position in (Positions.Short, Positions.Long)
- and action == Actions.Neutral.value
+ and unchanged_position
):
hold_penalty_ratio = float(
model_reward_parameters.get(
if base_reward is None:
base_reward = 0.0
- return base_reward
+ return base_reward + self._last_invalid_penalty
def _get_observation(self) -> NDArray[np.float32]:
start_idx = max(0, self._current_tick - self.window_size)
execution_tick = self._current_tick
exit_pnl = None
reward = self.calculate_reward(action)
+ selection_reward = reward
trade_type = self.execute_trade(action)
entry_pnl = self.get_unrealized_profit() if previous_position == Positions.Neutral else 0.0
if trade_type is not None and self._position in (Positions.Long, Positions.Short):
exit_additive_scale=self._exit_additive_ratio * base_factor,
)
reward += shaping + entry_additive + exit_additive
+ selection_reward += entry_additive + exit_additive
terminated = self.is_terminated()
terminal_liquidation = terminated and self._position in (Positions.Long, Positions.Short)
if terminal_liquidation:
self.rl_config.get("model_reward_parameters", {}),
)
reward += liquidation_reward
+ selection_reward += liquidation_reward
self._last_exit_reward += liquidation_reward
if self._exit_additive_enabled and not self.is_pbrs_invariant_mode():
liquidation_additive = self._compute_exit_additive(
self._exit_additive_ratio * base_factor,
)
reward += liquidation_additive
+ selection_reward += liquidation_additive
self._last_exit_additive += liquidation_additive
self._total_exit_additive += liquidation_additive
exit_pnl = terminal_pnl
"reward_hold": round(self._last_hold_penalty, 5),
"reward_exit": round(self._last_exit_reward, 5),
"reward": round(reward, 5),
+ "reward_selection": selection_reward,
"total_reward": round(self.total_reward, 5),
"pbrs_invariant": self.is_pbrs_invariant_mode(),
"idle_duration": idle_duration,
def get_net_arch(model_type: str, net_arch_type: NetArchSize) -> list[int] | dict[str, list[int]]:
- """
- Get network architecture
- """
+ """Build only the selected architecture, falling back to the smallest size."""
+ width = ReforceXY._NET_ARCH_WIDTHS.get(
+ net_arch_type, ReforceXY._NET_ARCH_WIDTHS[ReforceXY._NET_ARCH_SIZES[0]]
+ )
if ReforceXY._MODEL_TYPES[0] in model_type: # "PPO"
- return {
- ReforceXY._NET_ARCH_SIZES[0]: {
- "pi": [128, 128],
- "vf": [128, 128],
- }, # "small"
- ReforceXY._NET_ARCH_SIZES[1]: {
- "pi": [256, 256],
- "vf": [256, 256],
- }, # "medium"
- ReforceXY._NET_ARCH_SIZES[2]: {
- "pi": [512, 512],
- "vf": [512, 512],
- }, # "large"
- ReforceXY._NET_ARCH_SIZES[3]: {
- "pi": [1024, 1024],
- "vf": [1024, 1024],
- }, # "extra_large"
- }.get(net_arch_type, {"pi": [128, 128], "vf": [128, 128]})
- return {
- ReforceXY._NET_ARCH_SIZES[0]: [128, 128], # "small"
- ReforceXY._NET_ARCH_SIZES[1]: [256, 256], # "medium"
- ReforceXY._NET_ARCH_SIZES[2]: [512, 512], # "large"
- ReforceXY._NET_ARCH_SIZES[3]: [1024, 1024], # "extra_large"
- }.get(net_arch_type, [128, 128])
+ return {"pi": [width, width], "vf": [width, width]}
+ return [width, width]
def get_activation_fn(