From f48d8705dee4f3dba15e9252929c431e6b73ef12 Mon Sep 17 00:00:00 2001 From: Dennis Thiessen Date: Wed, 24 Jun 2026 09:24:35 +0200 Subject: [PATCH] remove min_target_probability gate + add chart time-range presets min_target_probability is gone: it filtered on the probability model the calibration has repeatedly shown to be weak and overconfident, it was redundant with the momentum gate, and as an off-by-default knob it just invited bad tuning. Removed from the backend gate, activation config/schema, the frontend mirror (qualifiesSetup / activationSummary), and ActivationSettings. The probability model stays where it does real work (primary-target selection + display). Charts: with multi-year history the all-bars default was unreadable. Added time-range presets (1M / 3M / 6M / YTD / 1Y / 3Y / 5Y / All), defaulting to 1Y; clicking a preset always re-applies (snaps back after a manual zoom). Y-axis autoscale and wheel-zoom / drag-pan were already there. 339 backend tests pass; frontend build clean. Co-Authored-By: Claude Opus 4.8 --- app/schemas/admin.py | 1 - app/services/admin_service.py | 4 -- app/services/qualification.py | 16 ++--- .../components/admin/ActivationSettings.tsx | 16 +---- .../components/charts/CandlestickChart.tsx | 72 +++++++++++++++---- frontend/src/lib/qualification.ts | 4 -- frontend/src/lib/types.ts | 1 - tests/unit/test_activation_settings.py | 12 +--- tests/unit/test_qualification.py | 5 -- 9 files changed, 68 insertions(+), 63 deletions(-) diff --git a/app/schemas/admin.py b/app/schemas/admin.py index 18760fa..1831fdb 100644 --- a/app/schemas/admin.py +++ b/app/schemas/admin.py @@ -62,7 +62,6 @@ class ActivationConfigUpdate(BaseModel): min_momentum_percentile: float | None = Field(default=None, ge=0, le=100) min_rr: float | None = Field(default=None, ge=0) min_confidence: float | None = Field(default=None, ge=0, le=100) - min_target_probability: float | None = Field(default=None, ge=0, le=100) require_high_conviction: bool | None = None exclude_conflicts: bool | None = None diff --git a/app/services/admin_service.py b/app/services/admin_service.py index bea22dc..8af6b5c 100644 --- a/app/services/admin_service.py +++ b/app/services/admin_service.py @@ -46,7 +46,6 @@ _ACTIVATION_FLOAT_KEYS: dict[str, str] = { "min_momentum_percentile": "activation_min_momentum_percentile", "min_rr": "activation_min_rr", "min_confidence": "activation_min_confidence", - "min_target_probability": "activation_min_target_probability", } _ACTIVATION_BOOL_KEYS: dict[str, str] = { "require_high_conviction": "activation_require_high_conviction", @@ -56,7 +55,6 @@ ACTIVATION_DEFAULTS: dict[str, float | bool] = { "min_momentum_percentile": 80.0, "min_rr": 1.2, "min_confidence": 55.0, - "min_target_probability": 0.0, "require_high_conviction": False, "exclude_conflicts": False, } @@ -207,8 +205,6 @@ async def update_activation_config( raise ValidationError("min_rr must be >= 0") if "min_confidence" in updates and not 0 <= updates["min_confidence"] <= 100: raise ValidationError("min_confidence must be between 0 and 100") - if "min_target_probability" in updates and not 0 <= updates["min_target_probability"] <= 100: - raise ValidationError("min_target_probability must be between 0 and 100") for public_key, storage_key in _ACTIVATION_FLOAT_KEYS.items(): if public_key in updates and updates[public_key] is not None: diff --git a/app/services/qualification.py b/app/services/qualification.py index 9949fed..d563a02 100644 --- a/app/services/qualification.py +++ b/app/services/qualification.py @@ -5,10 +5,9 @@ performance stats (server) and mirrored on the frontend. The core selection is cross-sectional momentum: a setup's ticker must rank in the top ``min_momentum_percentile`` of the universe by 12-1 month momentum — the one signal the backtest showed actually sorts forward returns. R:R and confidence -remain as floors, and conviction/conflict/target-probability survive as optional -tighteners (off by default). The momentum percentile is computed across the -universe and attached to each setup upstream; when it's absent the gate falls -back to the floors. +remain as floors, and conviction/conflict survive as optional tighteners (off by +default). The momentum percentile is computed across the universe and attached to +each setup upstream; when it's absent the gate falls back to the floors. """ from __future__ import annotations @@ -50,9 +49,9 @@ def setup_qualifies(setup: Any, config: dict) -> bool: recommended_action, risk_level and a ``targets`` list of dicts. Gate order: R:R floor → freshness (live R:R) → confidence floor → momentum - percentile (the core selection) → optional conviction / conflict / - target-probability tighteners. ``min_momentum_percentile`` defaults to 0 (off) - for callers that pass a legacy config without the key. + percentile (the core selection) → optional conviction / conflict tighteners. + ``min_momentum_percentile`` defaults to 0 (off) for callers that pass a legacy + config without the key. """ if setup.rr_ratio < config["min_rr"]: return False @@ -85,7 +84,4 @@ def setup_qualifies(setup: Any, config: dict) -> bool: if config.get("exclude_conflicts"): if (setup.risk_level or "") != "Low": return False - min_tp = float(config.get("min_target_probability", 0.0)) - if min_tp > 0 and best_target_probability(setup) < min_tp: - return False return True diff --git a/frontend/src/components/admin/ActivationSettings.tsx b/frontend/src/components/admin/ActivationSettings.tsx index d1113c5..ea60db1 100644 --- a/frontend/src/components/admin/ActivationSettings.tsx +++ b/frontend/src/components/admin/ActivationSettings.tsx @@ -7,7 +7,6 @@ const DEFAULTS: ActivationConfig = { min_momentum_percentile: 80, min_rr: 1.2, min_confidence: 55, - min_target_probability: 0, require_high_conviction: false, exclude_conflicts: false, }; @@ -91,20 +90,7 @@ export function ActivationSettings() {

Optional tighteners

Off by default — turn on to be more selective on top of the momentum gate.

-
- +