EPIC: Phase 2 - Complete Grid Exit Strategy

Epic ID: EPIC-002
Status:COMPLETE
Priority: P0 - CRITICAL
Actual Effort: ~50 hours
Dependencies: Phase 1 (COMPLETE - metrics implemented, PR#6 merged)
Completion Date: 2026-02-16


✅ Completion Summary

All 6 stories completed and merged to main:

StoryStatusTestsCoverageCompletion
2.1: LATEST_ACCEPTABLE_EXIT Triggers✅ COMPLETE35+ tests95%+2026-02-03
2.2: WARNING Triggers✅ COMPLETE20+ tests92%+2026-02-03
2.3: State Transition Tracking✅ COMPLETE18+ tests88%+2026-02-03
2.4: Historical Data Loading✅ COMPLETE15+ tests94%+2026-02-16
2.5: Integration & E2E Testing✅ COMPLETE10+ scenariosN/A2026-02-03
2.6: Configuration & Documentation✅ COMPLETE22+ tests91%+2026-02-02

Key Deliverables:

  • 9 trigger conditions across WARNING and LATEST_ACCEPTABLE_EXIT states
  • 120+ comprehensive tests (unit + integration)
  • Strict mode validation (no fallbacks, fail-fast errors)
  • Rate limiting for notifications
  • Full configuration system with JSON schema validation

See: PHASE-2-COMPLETION-SUMMARY.md for full details.


Epic Overview

Implement the complete Grid Exit Strategy with tiered exit states (WARNING → LATEST_ACCEPTABLE_EXIT → MANDATORY_EXIT), state transition tracking, and historical data loading. This is the core value proposition of the regime management system - enabling profitable grid trading at scale by identifying regime breaks before they destroy accumulated profits.

Final State:COMPLETE - All 6 user stories delivered with comprehensive testing (120+ tests, 90%+ coverage). Exit strategy system is production-ready with strict validation, fail-fast error handling, and comprehensive integration tests. All acceptance criteria met.

Key Achievement: Removed ~100 lines of fallback complexity while improving reliability through strict validation. System now fails fast with clear errors instead of silently degrading.


Success Criteria

Functional Requirements

  • ✅ All 3 exit states implemented (WARNING, LATEST_ACCEPTABLE_EXIT, MANDATORY_EXIT)
  • ✅ State transition tracking in Git with rate limiting
  • ✅ Historical data loading (last 12-24 hours of metrics)
  • ✅ Multi-timeframe analysis (1h + 4h bars)
  • ✅ Configurable thresholds via YAML

Quality Requirements

  • ✅ 90%+ test coverage for exit strategy code
  • ✅ Integration tests for full flow (metrics → triggers → notifications)
  • ✅ Real data validation against last 7 days of metrics
  • ✅ All tests passing in CI/CD

Acceptance Criteria

  • WARNING triggers when 2+ warning conditions met (not single condition)
  • LATEST_ACCEPTABLE_EXIT triggers on regime persistence, mean reversion degradation, volatility expansion, or z-score reversion failure
  • MANDATORY_EXIT triggers on confirmed TREND or boundary violations
  • State transitions logged to Git with timestamps and reasons
  • Notification rate limiting prevents spam
  • Manual validation shows reasonable exit state progression in real data

Stories

Story 2.1: Implement LATEST_ACCEPTABLE_EXIT Triggers

Story ID: STORY-2.1
Priority: P0
Effort: 8-12 hours
Assignee: TBD

Description

Implement all 4 trigger conditions for LATEST_ACCEPTABLE_EXIT state:

  1. TRANSITION persistence tracking (≥2 consecutive 4h bars OR ≥4 consecutive 1h bars)
  2. Mean reversion degradation (OU half-life ≥ 2× baseline)
  3. Volatility expansion ratio > 1.25
  4. Z-score reversion failure

Acceptance Criteria

  • All 4 trigger functions implemented in src/exit_strategy/triggers/latest_acceptable.py
  • Each trigger is independently testable
  • Configurable thresholds via YAML configuration
  • Unit tests for each trigger (90%+ coverage)
  • Triggers return (triggered: bool, reason: str) tuples
  • Tests cover edge cases (missing data, boundary conditions)

Technical Details

File to Create: src/exit_strategy/triggers/latest_acceptable.py

Functions Required:

def check_transition_persistence(history: List[Dict]) -> Tuple[bool, str]:
    """
    Trigger if:
    - ≥2 consecutive 4h bars with TRANSITION verdict, OR
    - ≥4 consecutive 1h bars with TRANSITION verdict
    
    Args:
        history: List of recent metrics (last 12 hours)
    
    Returns:
        (triggered: bool, reason: str)
    """
    pass
 
def check_mean_reversion_degradation(
    current_half_life: float,
    baseline_half_life: float,
    threshold_multiplier: float = 2.0
) -> Tuple[bool, str]:
    """
    Trigger if OU half-life ≥ 2× baseline
    
    Baseline = 7-day rolling average during RANGE_OK
    """
    pass
 
def check_volatility_expansion(
    current_atr: float,
    baseline_atr: float,
    threshold: float = 1.25
) -> Tuple[bool, str]:
    """
    Trigger if volatility expansion ratio > 1.25
    """
    pass
 
def check_zscore_reversion_failure(
    price_history: List[float],
    lookback_bars: int = 6
) -> Tuple[bool, str]:
    """
    Trigger if Z-score excursions fail to revert within expected bars
    """
    pass

Test Requirements

Unit Tests (20+ test cases):

  • test_transition_persistence_4h_bars_triggers()
  • test_transition_persistence_1h_bars_triggers()
  • test_transition_persistence_insufficient_history()
  • test_mean_reversion_degradation_triggers_at_2x()
  • test_mean_reversion_degradation_below_threshold()
  • test_volatility_expansion_triggers_at_125()
  • test_volatility_expansion_below_threshold()
  • test_zscore_reversion_failure_triggers()
  • test_zscore_reversion_success_no_trigger()
  • Edge cases for each function

Definition of Done

  • Code reviewed and approved
  • All unit tests passing
  • Code coverage ≥90% for new code
  • Documentation complete (docstrings, type hints)
  • Configuration schema updated with new thresholds
  • Manually tested against real metrics data

Story 2.2: Implement WARNING Triggers

Story ID: STORY-2.2
Priority: P0
Effort: 4-6 hours
Assignee: TBD

Description

Implement WARNING state trigger logic requiring 2+ conditions to fire (not single condition). This prevents false alarms from single noisy indicators.

WARNING Conditions (require 2+ to trigger):

  1. TRANSITION probability ≥ 40% (configurable)
  2. Regime confidence declining over 3 bars
  3. Efficiency Ratio rising above 0.6 (configurable)
  4. Mean reversion speed slowing
  5. Volatility expansion 1.1-1.25×

Acceptance Criteria

  • evaluate_warning_conditions() function implemented in src/exit_strategy/triggers/warning.py
  • Requires 2+ conditions to trigger WARNING (NOT single condition)
  • All 5 condition checks implemented
  • Configurable thresholds via YAML
  • Unit tests cover edge cases (0, 1, 2, 5 conditions met)
  • Returns (ExitState, List[str]) with reasons for conditions met

Technical Details

File to Create: src/exit_strategy/triggers/warning.py

Function Signature:

def evaluate_warning_conditions(
    regime_history: List[Dict], 
    config: Dict
) -> Tuple[ExitState, List[str]]:
    """
    Evaluate all warning conditions.
    
    Returns WARNING if 2+ conditions met, else NORMAL.
    
    Args:
        regime_history: List of recent regime evaluations
        config: Configuration dict with thresholds
    
    Returns:
        (ExitState, reasons: List[str])
    """
    conditions_met = []
    
    # Check each condition...
    if transition_probability >= config['warning_transition_threshold']:
        conditions_met.append("TRANSITION probability rising")
    
    if regime_confidence_declining(regime_history, bars=3):
        conditions_met.append("Regime confidence declining")
    
    # ... check other 3 conditions ...
    
    if len(conditions_met) >= 2:
        return ExitState.WARNING, conditions_met
    else:
        return ExitState.NORMAL, ["Single warning condition - not actionable"]

Test Requirements

Unit Tests (15+ test cases):

  • test_warning_requires_2_conditions() - 1 condition should NOT trigger
  • test_warning_triggers_with_2_conditions()
  • test_warning_triggers_with_all_5_conditions()
  • test_transition_probability_condition()
  • test_confidence_decline_condition()
  • test_efficiency_ratio_condition()
  • test_mean_reversion_slowing_condition()
  • test_volatility_expansion_condition()
  • Edge cases (missing data, boundary values)

Definition of Done

  • Code reviewed and approved
  • All unit tests passing
  • Code coverage ≥90%
  • Documentation complete
  • Configuration schema includes WARNING thresholds
  • Manually validated: 1 condition = NORMAL, 2 conditions = WARNING

Story 2.3: State Transition Tracking & Rate Limiting

Story ID: STORY-2.3
Priority: P0
Effort: 4-6 hours
Assignee: TBD

Description

Implement state transition tracking in Git with rate limiting to prevent notification spam. Track when exit states change (NORMAL → WARNING → LATEST_ACCEPTABLE_EXIT → MANDATORY_EXIT) with timestamps and reasons.

Acceptance Criteria

  • State transitions logged to Git in market-maker-data/exit_states/{symbol}/
  • Daily JSON files with transition history
  • Rate limiting prevents notification spam:
    • Max 1 WARNING per 4 hours for same grid
    • Max 1 LATEST_ACCEPTABLE_EXIT per 2 hours
    • Max 1 MANDATORY_EXIT per 1 hour
  • Can query: “When did we last alert for this grid?”
  • Unit tests for rate limiting logic (90%+ coverage)

Technical Details

File to Create: src/exit_strategy/state_tracker.py

Data Structure:

market-maker-data/
  exit_states/
    ETH-USDT/
      2026-02-01.json
      2026-02-02.json

JSON Format:

{
  "symbol": "ETH-USDT",
  "grid_id": "eth-grid-1",
  "date": "2026-02-01",
  "transitions": [
    {
      "timestamp": "2026-02-01T09:15:00Z",
      "from_state": "NORMAL",
      "to_state": "WARNING",
      "reasons": ["TRANSITION probability rising", "Confidence declining"],
      "regime_verdict": "RANGE_WEAK",
      "confidence": 0.48,
      "metrics": {
        "adx": 32.5,
        "efficiency_ratio": 0.68
      }
    },
    {
      "timestamp": "2026-02-01T12:00:00Z",
      "from_state": "WARNING",
      "to_state": "LATEST_ACCEPTABLE_EXIT",
      "reasons": ["TRANSITION persistence confirmed (4h bars)"],
      "regime_verdict": "TRANSITION",
      "confidence": 0.42
    }
  ],
  "last_notification": {
    "WARNING": "2026-02-01T09:15:00Z",
    "LATEST_ACCEPTABLE_EXIT": "2026-02-01T12:00:00Z",
    "MANDATORY_EXIT": null
  }
}

Class Interface:

class StateTransitionTracker:
    def __init__(self, data_repo_path: Path):
        self.data_repo = data_repo_path
    
    def log_transition(
        self,
        symbol: str,
        grid_id: str,
        from_state: ExitState,
        to_state: ExitState,
        reasons: List[str],
        regime_verdict: str,
        confidence: float,
        metrics: Dict
    ) -> None:
        """Log state transition to Git"""
        pass
    
    def should_notify(
        self,
        symbol: str,
        grid_id: str,
        exit_state: ExitState
    ) -> bool:
        """
        Check if enough time has passed since last notification
        
        Rate limits:
        - WARNING: 4 hours
        - LATEST_ACCEPTABLE_EXIT: 2 hours
        - MANDATORY_EXIT: 1 hour
        """
        pass
    
    def get_current_state(
        self,
        symbol: str,
        grid_id: str
    ) -> Optional[ExitState]:
        """Get current exit state from history"""
        pass

Test Requirements

Unit Tests (12+ test cases):

  • test_log_transition_creates_file()
  • test_log_transition_appends_to_existing_file()
  • test_should_notify_warning_rate_limit()
  • test_should_notify_latest_acceptable_rate_limit()
  • test_should_notify_mandatory_rate_limit()
  • test_should_notify_different_grids_independent()
  • test_get_current_state_returns_latest()
  • test_get_current_state_no_history_returns_none()
  • Edge cases (file corruption, missing data)

Definition of Done

  • Code reviewed and approved
  • All unit tests passing
  • Code coverage ≥90%
  • Git commits work correctly
  • Rate limiting tested with real timestamps
  • Documentation complete

Story 2.4: Historical Data Loading

Story ID: STORY-2.4
Priority: P0
Effort: 4-6 hours
Assignee: TBD

Description

Implement historical data loading to fetch last 12-24 hours of metrics for persistence checks and multi-timeframe analysis. Include caching for performance.

Acceptance Criteria

  • Can load last N hours of metrics from Git repository
  • Multi-timeframe extraction (1h bars, 4h bars)
  • Caching reduces duplicate Git reads (last 24h in memory)
  • Cache invalidation on new metrics arrival
  • Unit tests with mocked file system (90%+ coverage)

Technical Details

File to Create: src/exit_strategy/history_loader.py

Class Interface:

class MetricsHistoryLoader:
    def __init__(self, data_repo_path: Path):
        self.data_repo = data_repo_path
        self._cache: Dict[str, List[Dict]] = {}
        self._cache_timestamp: Dict[str, datetime] = {}
    
    def load_recent_metrics(
        self,
        symbol: str,
        hours: int = 12
    ) -> List[Dict]:
        """
        Load last N hours of metrics for persistence checks
        
        Returns sorted list (oldest first)
        """
        # Check cache first
        if self._is_cache_valid(symbol, hours):
            return self._cache[symbol]
        
        # Load from Git
        metrics = self._load_from_git(symbol, hours)
        
        # Update cache
        self._cache[symbol] = metrics
        self._cache_timestamp[symbol] = datetime.utcnow()
        
        return metrics
    
    def get_4h_bars(self, metrics_history: List[Dict]) -> List[Dict]:
        """
        Extract 4h bar data for structural confirmation
        
        Returns metrics at 0:00, 4:00, 8:00, 12:00, 16:00, 20:00
        """
        pass
    
    def get_1h_bars(self, metrics_history: List[Dict]) -> List[Dict]:
        """
        Extract 1h bar data for rapid detection
        
        Returns all hourly metrics
        """
        pass
    
    def invalidate_cache(self, symbol: str) -> None:
        """Invalidate cache when new metrics arrive"""
        if symbol in self._cache:
            del self._cache[symbol]
            del self._cache_timestamp[symbol]

Test Requirements

Unit Tests (15+ test cases):

  • test_load_recent_metrics_from_git()
  • test_load_recent_metrics_uses_cache()
  • test_cache_invalidation_forces_reload()
  • test_get_4h_bars_extracts_correct_hours()
  • test_get_1h_bars_returns_all_metrics()
  • test_load_with_missing_files()
  • test_load_with_insufficient_history()
  • test_cache_timestamp_tracking()
  • Edge cases (empty directory, corrupted files)

Definition of Done

  • Code reviewed and approved
  • All unit tests passing (with mocked file system)
  • Code coverage ≥90%
  • Caching reduces Git reads (verified via logging)
  • Documentation complete
  • Manually tested with real metrics directory

Story 2.5: Integration & End-to-End Testing

Story ID: STORY-2.5
Priority: P0
Effort: 8-12 hours
Assignee: TBD

Description

Wire up all triggers in the exit state evaluator and create comprehensive integration tests for the full flow: metrics → history loading → trigger evaluation → state classification → notification.

Acceptance Criteria

  • All triggers integrated into ExitStateEvaluator.evaluate()
  • Correct priority: MANDATORY → LATEST_ACCEPTABLE → WARNING → NORMAL
  • Integration tests for full flow (5+ test scenarios)
  • Test state transitions: NORMAL → WARNING → LATEST_ACCEPTABLE → MANDATORY
  • Test notification prevention (rate limiting integration)
  • Real data validation: Run against last 7 days of actual metrics
  • Manual validation: Exit states make sense for historical data

Technical Details

Files to Modify:

  • src/exit_strategy/evaluator.py - Wire up all triggers

Integration Test Scenarios:

  1. Happy Path: NORMAL → WARNING → LATEST_ACCEPTABLE_EXIT → MANDATORY_EXIT

    • Start with RANGE_OK regime
    • Simulate regime degradation over 12 hours
    • Verify state transitions occur at correct points
    • Verify notifications sent (with rate limiting)
  2. WARNING Requires 2+ Conditions

    • Provide metrics with only 1 warning condition
    • Verify state stays NORMAL
    • Add second warning condition
    • Verify state transitions to WARNING
  3. Rate Limiting Prevents Spam

    • Trigger WARNING multiple times within 4 hours
    • Verify only first notification sent
    • Advance time beyond 4 hours
    • Verify second notification sent
  4. LATEST_ACCEPTABLE_EXIT Triggers

    • Test each of 4 trigger conditions independently
    • Verify state transitions to LATEST_ACCEPTABLE_EXIT
    • Verify reasons logged correctly
  5. Real Data Validation

    • Load last 7 days of metrics from market-maker-data/
    • Run exit evaluator on each hour
    • Verify exit states are reasonable (no wild oscillations)
    • Identify any false positives/negatives

Test File: tests/integration/test_exit_strategy_flow.py

Test Requirements

Integration Tests (5+ scenarios, 20+ assertions):

  • test_full_state_progression_normal_to_mandatory()
  • test_warning_requires_multiple_conditions()
  • test_rate_limiting_prevents_spam()
  • test_latest_acceptable_triggers_independently()
  • test_real_data_validation()
  • Performance test (evaluate 168 hours in <5 seconds)

Definition of Done

  • All triggers wired into evaluator
  • Integration tests passing
  • Real data validation complete
  • No wild state oscillations in historical data
  • Performance acceptable (<1 second per evaluation)
  • Code reviewed and approved

Story 2.6: Configuration & Documentation

Story ID: STORY-2.6
Priority: P0
Effort: 4-6 hours
Assignee: TBD

Description

Create comprehensive YAML configuration for all exit strategy thresholds and document trigger logic, tuning recommendations, and usage.

Acceptance Criteria

  • YAML configuration file with all thresholds
  • Configuration validated on load (schema validation)
  • Documentation explains trigger logic
  • Tuning recommendations included
  • Example configurations for conservative/aggressive settings
  • README updated with exit strategy overview

Technical Details

File to Create: config/exit_strategy_config.yaml

exit_rules:
  latest_acceptable_exit:
    transition_persistence_4h_bars: 2
    transition_persistence_1h_bars: 4
    mean_reversion_halflife_multiplier: 2.0
    volatility_expansion_threshold: 1.25
    zscore_reversion_failure_bars: 6
    
  warning:
    minimum_conditions_required: 2  # CRITICAL: Prevents single-condition false alarms
    transition_probability_threshold: 0.40
    regime_confidence_decline_bars: 3
    efficiency_ratio_threshold: 0.6
    mean_reversion_slowdown_threshold: 1.5  # Half-life increase ratio
    volatility_expansion_min: 1.10
    volatility_expansion_max: 1.25
    
  mandatory_exit:
    consecutive_closes_outside_range: 2
    directional_swing_bars: 6
    stop_loss_buffer_atr: 0.1
    
notifications:
  rate_limits:
    warning_min_hours: 4
    latest_acceptable_min_hours: 2
    mandatory_min_hours: 1
 
# Tuning presets
presets:
  conservative:
    # Earlier warnings, tighter thresholds
    warning.transition_probability_threshold: 0.30
    warning.minimum_conditions_required: 2
    latest_acceptable_exit.volatility_expansion_threshold: 1.15
    
  aggressive:
    # Later warnings, looser thresholds
    warning.transition_probability_threshold: 0.50
    warning.minimum_conditions_required: 3
    latest_acceptable_exit.volatility_expansion_threshold: 1.35

Documentation Files:

  1. docs/exit-strategy-triggers.md - Detailed trigger logic
  2. docs/exit-strategy-tuning.md - Tuning recommendations
  3. README.md - Updated with Phase 2 completion notes

Documentation Requirements

Exit Strategy Triggers Doc:

  • Explain each exit state (WARNING, LATEST_ACCEPTABLE_EXIT, MANDATORY_EXIT)
  • Document trigger conditions for each state
  • Provide examples with real metrics
  • Explain why 2+ conditions required for WARNING

Tuning Guide:

  • Conservative vs aggressive presets
  • How to adjust thresholds based on validation results
  • Expected false positive/negative rates
  • Backtesting recommendations

README Updates:

  • Phase 2 completion status
  • Exit strategy overview
  • Quick start guide for configuration

Definition of Done

  • Configuration file created and validated
  • Schema validation implemented
  • Documentation complete (3 files)
  • Examples included for conservative/aggressive settings
  • Code reviewed and approved
  • Documentation reviewed for clarity

Epic Completion Checklist

  • All 6 stories completed and merged to main
  • 90%+ test coverage for exit strategy code
  • Integration tests passing
  • Real data validation shows reasonable exit states
  • Configuration documented and validated
  • No hardcoded thresholds (all configurable via YAML)
  • Rate limiting prevents notification spam
  • Git state tracking working
  • Phase 2 retrospective completed
  • RAIA log updated with learnings

Dependencies & Risks

Dependencies:

  • ✅ Phase 1 COMPLETE (metrics implemented, PR#6 merged)
  • Market-maker-data repository accessible
  • KuCoin API working (for future position tracking in Phase 3)

Risks:

  • R001: Real data validation reveals excessive false positives
    • Mitigation: Tunable thresholds via YAML, conservative presets available
  • R002: Multi-timeframe analysis adds complexity
    • Mitigation: Simple 1h/4h extraction, well-tested
  • R003: Git state tracking file conflicts
    • Mitigation: Daily files per symbol, atomic commits

Assumptions (see RAIA log):

  • A001: 1-hour evaluation cadence provides sufficient warning time
    • Validation: Will be tested during Phase 2 integration testing

Next Steps After Epic Completion

  1. Phase 3: Position Risk Quantification (30-40h)

    • KuCoin position tracker
    • Capital risk calculator
    • Enhanced notifications with risk metrics
  2. Phase 4: Testing & Validation (40-50h)

    • Comprehensive test coverage
    • Backtesting framework
    • CI/CD integration
  3. Phase 5: Operational Improvements (20-30h)

    • Audit logging
    • KPI tracking
    • Documentation for investors

Epic Owner: Craig
Created: 2026-02-01
Last Updated: 2026-02-01