From 03cff429f73323e65960c20f2e23195271fad04d Mon Sep 17 00:00:00 2001 From: GifariKemal Date: Wed, 11 Feb 2026 22:14:20 +0700 Subject: [PATCH] fix(v0.2.6): critical grace period & threshold unit bugs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fuzzy/Kelly grace threshold: 200 ($200) → 2.0 ($2) — was suppressing ALL loss exits - Fuzzy/Kelly grace period: unified with dynamic grace_minutes (respects ever_profitable, Golden) - NO_RECOVERY: 1500 ($1500) → 15.0 ($15) — safety net now actually triggers - EMERGENCY_MAX_LOSS: 2000 ($2000) → 20.0 ($20) — safety net now actually triggers - Golden emergency exit: never-profitable + loss >$5 + 45s → immediate cut - Golden grace floor: 1.0 min (never-prof) / 1.5 min (ever-prof), was 2.0 min Co-Authored-By: Claude Opus 4.6 --- CHANGELOG.md | 33 ++++++++++++++++++++++ VERSION | 2 +- src/smart_risk_manager.py | 58 +++++++++++++++++++++------------------ 3 files changed, 66 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7d8288..10a1eca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,39 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 --- +## [0.2.6] - 2026-02-11 + +### Fixed (Critical: Grace Period & Threshold Unit Bugs) +**Problem:** Trade #162554401 lost -$8.01 in Golden Session. Fuzzy exit confidence was 94.58% at t=86s but was SUPPRESSED by grace period. Three critical bugs discovered: + +#### BUG FIX 1: Fuzzy/Kelly Grace Threshold Wrong Unit +- `abs(current_profit) < 200` was meant to be $2.00 but current_profit is in DOLLARS +- So `200` = $200 threshold — effectively suppressed ALL loss exits during grace +- **Fix:** Changed to `abs(current_profit) < 2.0` — only suppress micro-losses (<$2) + +#### BUG FIX 2: Fuzzy/Kelly Grace Period Not Unified +- Fuzzy/Kelly section had its OWN hardcoded grace (90s for high_volatility) +- This IGNORED all v0.2.5 fixes (ever_profitable cap, Golden Session reduction) +- **Fix:** Replaced hardcoded dict with `grace_minutes * 60` (unified dynamic grace) + +#### BUG FIX 3: NO_RECOVERY & EMERGENCY Thresholds Wrong Unit +- `NO_RECOVERY_THRESHOLD = 1500` ($1500) and `EMERGENCY_MAX_LOSS = 2000` ($2000) +- These safety nets NEVER trigger for 0.01 lot trades (max ~$25 loss) +- **Fix:** Changed to 15.0 ($15) and 20.0 ($20) respectively + +#### NEW: Golden Session Emergency Exit +- Never-profitable trades in Golden Session with loss > $5 after 45s → immediate exit +- No grace period, no fuzzy threshold — just cut the loss fast +- Golden Session floor reduced: 1.0 min (never-profitable) / 1.5 min (ever-profitable) + +#### Impact Analysis +- Trade #162554401 scenario: fuzzy 94.58% at -$7.93 would now EXIT (not suppressed) +- Grace period in Golden + never-profitable: 72s (was 90s hardcoded) +- Losses > $2 no longer suppressed during grace period at all +- Safety nets (NO_RECOVERY $15, EMERGENCY $20) now actually functional + +--- + ## [0.2.5] - 2026-02-11 ### Fixed (Professor AI Analysis: Golden Session + Loss Protection) diff --git a/VERSION b/VERSION index 3a4036f..53a75d6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.2.5 +0.2.6 diff --git a/src/smart_risk_manager.py b/src/smart_risk_manager.py index 2b253fe..7ae354f 100644 --- a/src/smart_risk_manager.py +++ b/src/smart_risk_manager.py @@ -1218,9 +1218,11 @@ class SmartRiskManager: if not guard.ever_profitable: grace_minutes = min(grace_minutes, 2.0) - # v0.2.5: Golden Session — reduce grace by 40% (extreme vol = fast moves) + # v0.2.5f: Golden Session — reduce grace by 40% (extreme vol = fast moves) + # Lower floor for never-profitable trades (1 min vs 1.5 min) if is_golden: - grace_minutes = max(2.0, grace_minutes * 0.60) + golden_floor = 1.0 if not guard.ever_profitable else 1.5 + grace_minutes = max(golden_floor, grace_minutes * 0.60) # Log dynamic multipliers periodically (every 60s) if len(guard.profit_timestamps) > 0: @@ -1496,27 +1498,19 @@ class SmartRiskManager: else: # === LOSS TRADES: Exit faster to minimize damage === - # FIX v0.1.2: Grace period untuk loss trades - cegah early exit pada micro swings - grace_period_sec = { - "ranging": 120, - "mean_reverting": 120, - "volatile": 90, - "high_volatility": 90, - "crisis": 60, - "trending": 60, - "normal": 90, - }.get(regime, 90) - - time_since_entry = time.time() - guard.entry_time.timestamp() - in_grace_period = time_since_entry < grace_period_sec + # v0.2.5f: Unified grace — use dynamic grace_minutes (respects + # ever_profitable cap, Golden Session reduction, velocity-based) + grace_period_sec = grace_minutes * 60 + in_grace_period = trade_age_seconds < grace_period_sec # Lower threshold for losses (75%) if exit_confidence > 0.75: - # Suppress exit during grace period for small losses (<$2) - if in_grace_period and abs(current_profit) < 200: # $2.00 + # v0.2.5f: Only suppress tiny losses (<$2) during grace + # BUG FIX: was 200 (=$200, never triggers) → 2.0 (=$2) + if in_grace_period and abs(current_profit) < 2.0: logger.info( f"[GRACE PERIOD] Loss fuzzy={exit_confidence:.2%} suppressed " - f"(t={time_since_entry:.0f}s < {grace_period_sec}s, loss=${current_profit:.2f})" + f"(t={trade_age_seconds:.0f}s < {grace_period_sec:.0f}s, loss=${current_profit:.2f})" ) else: return True, ExitReason.POSITION_LIMIT, ( @@ -1531,11 +1525,11 @@ class SmartRiskManager: exit_confidence, current_profit, tp_hard ) if should_exit and close_fraction > 0.3: - # Suppress kelly exit during grace period for small losses - if in_grace_period and abs(current_profit) < 200: # $2.00 + # v0.2.5f: Only suppress tiny losses (<$2) during grace + if in_grace_period and abs(current_profit) < 2.0: logger.info( f"[GRACE PERIOD] Kelly loss exit suppressed " - f"(t={time_since_entry:.0f}s < {grace_period_sec}s)" + f"(t={trade_age_seconds:.0f}s < {grace_period_sec:.0f}s)" ) else: return True, ExitReason.POSITION_LIMIT, ( @@ -1546,20 +1540,32 @@ class SmartRiskManager: # CHECK -1: NO RECOVERY ZONE ($15 threshold) # If loss >= $15, exit immediately - no point waiting for recovery - NO_RECOVERY_THRESHOLD = 1500 # $15.00 per 0.01 lot + # v0.2.5f: Fixed unit — current_profit is in DOLLARS (was 1500=$1500, never triggered) + NO_RECOVERY_THRESHOLD = 15.0 # $15.00 if current_profit <= -NO_RECOVERY_THRESHOLD: return True, ExitReason.POSITION_LIMIT, ( f"[NO RECOVERY] Loss ${abs(current_profit):.2f} too deep " - f"(threshold ${NO_RECOVERY_THRESHOLD/100:.2f}) - cut immediately" + f"(threshold ${NO_RECOVERY_THRESHOLD:.2f}) - cut immediately" ) - # CHECK 0: EMERGENCY CAP ($20 per 0.01 lot) + # CHECK 0: EMERGENCY CAP ($20) # Absolute maximum loss cap - last resort protection - EMERGENCY_MAX_LOSS = 2000 # $20.00 per 0.01 lot + # v0.2.5f: Fixed unit — current_profit is in DOLLARS (was 2000=$2000, never triggered) + EMERGENCY_MAX_LOSS = 20.0 # $20.00 if current_profit <= -EMERGENCY_MAX_LOSS: return True, ExitReason.POSITION_LIMIT, ( f"[EMERGENCY CAP] Max loss ${abs(current_profit):.2f} exceeded " - f"${EMERGENCY_MAX_LOSS/100:.2f} limit - emergency exit!" + f"${EMERGENCY_MAX_LOSS:.2f} limit - emergency exit!" + ) + + # === v0.2.5f: GOLDEN EMERGENCY EXIT === + # Never-profitable trades in Golden Session with steep loss → cut fast + # Golden = extreme volatility, if -$5+ in 45s and never profitable, it's going wrong + if (is_golden and not guard.ever_profitable + and current_profit < -5.0 and trade_age_seconds >= 45): + return True, ExitReason.POSITION_LIMIT, ( + f"[GOLDEN EMERGENCY] Loss ${abs(current_profit):.2f} never-profitable " + f"after {trade_age_seconds:.0f}s in Golden Session — cutting fast" ) # === CHECK 0A: BREAKEVEN SHIELD (percentage-based, dynamic) ===