we ball again
This commit is contained in:
@@ -1,56 +1,189 @@
|
||||
# Consistency Auditor Memory
|
||||
|
||||
## Last Audit: 2026-02-11 (Hourly Trading Update)
|
||||
## Last Audit: 2026-02-12 (Regime-Adaptive Dual Strategy Update)
|
||||
|
||||
### CRITICAL FINDINGS
|
||||
### AUDIT RESULT: ✅ NO CRITICAL BUGS FOUND
|
||||
|
||||
#### 1. Cooldown Timer Missing in Live Bot ❌
|
||||
**Location**: backtester.rs has it (lines 40, 63, 174-179, 275-281), bot.rs missing
|
||||
**Issue**: Backtester prevents whipsaw re-entry for REENTRY_COOLDOWN_BARS (7 bars) after stop-loss. Live bot can immediately re-buy on same cycle.
|
||||
**Impact**: Live bot will churn more, potentially re-entering failed positions immediately. Backtest vs live divergence.
|
||||
**Fix Required**: Add cooldown_timers HashMap to TradingBot, track in execute_sell, check in execute_buy.
|
||||
The refactor to extract shared logic into `strategy.rs` has **eliminated all previous consistency issues**. Bot and backtester now share identical implementations for all critical trading logic.
|
||||
|
||||
#### 2. Gradual Ramp-Up Missing in Live Bot ⚠️
|
||||
**Location**: backtester.rs has it (lines 42, 64, 196-198, 226, 508), bot.rs missing
|
||||
**Issue**: Backtester limits new positions to 1 per bar during first RAMPUP_PERIOD_BARS (30 bars). Live bot could deploy all capital on first cycle.
|
||||
**Impact**: Live initial deployment faster/riskier than backtest simulates.
|
||||
**Fix Required**: Add new_positions_this_cycle counter to TradingBot, reset each cycle, check in execute_buy.
|
||||
---
|
||||
|
||||
### Confirmed Consistent (2026-02-11)
|
||||
## VERIFIED CONSISTENT (2026-02-12)
|
||||
|
||||
#### Core Trading Logic ✅
|
||||
- **Drawdown halt**: Time-based (35 bars), bot uses trading_cycle_count vs backtester current_bar (equivalent)
|
||||
- **bars_held increment**: Both at START of trading cycle/bar (bot:660-663, bt:531-534) — previous bug FIXED
|
||||
- **Position sizing**: Identical ATR volatility adjustment, confidence scaling (0.7+0.3*conf), caps
|
||||
- **Stop-loss**: Identical 2.5x ATR + 4% hard cap + fixed fallback
|
||||
- **Trailing stop**: Identical 1.5x ATR activation/distance + fixed fallback
|
||||
- **Time exit**: Identical 30-bar threshold
|
||||
- **Sector limits**: Both max 2 per sector (was 3 in daily)
|
||||
- **Max positions**: Both 5 concurrent (was 8 in daily)
|
||||
- **Config constants**: All parameters identical (verified config.rs)
|
||||
### Core Trading Logic ✅
|
||||
- **Signal generation**: Both use shared `indicators::generate_signal()` (indicators.rs:442-650)
|
||||
- **Position sizing**: Both use shared `Strategy::calculate_position_size()` (strategy.rs:29-55)
|
||||
- Volatility-adjusted via ATR
|
||||
- Confidence scaling: 0.7 + 0.3 * confidence
|
||||
- Max position size cap: 25%
|
||||
- Cash reserve: 5%
|
||||
- **Stop-loss/trailing/time exit**: Both use shared `Strategy::check_stop_loss_take_profit()` (strategy.rs:57-128)
|
||||
- Hard max loss cap: 5%
|
||||
- ATR-based stop: 3.0x ATR below entry
|
||||
- Fixed fallback stop: 2.5%
|
||||
- Trailing stop: 2.0x ATR after 2.0x ATR gain
|
||||
- Time exit: 40 bars if below trailing activation threshold
|
||||
|
||||
#### Warmup Requirements ✅
|
||||
**Hourly min_bars()**: max(35 MACD, 15 RSI, 100 EMA, 28 ADX, 20 BB, 63 momentum) + 5 = 105 bars
|
||||
Both fetch ~158 calendar days for hourly. MACD needs slow+signal (26+9=35), ADX needs 2x (14*2=28), all accounted for.
|
||||
### Portfolio Controls ✅
|
||||
- **Cooldown timers**: Both implement 5-bar cooldown after stop-loss (bot:395-406,521-533; bt:133-138,242-247)
|
||||
- **Ramp-up period**: Both limit to 1 new position per bar for first 15 bars (bot:433-441; bt:158-161)
|
||||
- **Drawdown circuit breaker**: Both halt for 20 bars at 12% drawdown (bot:244-268; bt:83-118)
|
||||
- **Sector limits**: Both enforce max 2 per sector (bot:423-430; bt:149-156)
|
||||
- **Max concurrent positions**: Both enforce max 7 (bot:414-421; bt:145-147)
|
||||
- **Momentum ranking**: Both filter to top 10 momentum stocks (bot:669-690; bt:438-449)
|
||||
- **bars_held increment**: Both increment at START of trading cycle/bar (bot:614-617; bt:433-436)
|
||||
|
||||
#### Expected Differences ✅
|
||||
- **Slippage**: Backtester 10 bps, live actual fills (correct)
|
||||
- **Already-holding**: Different APIs, same logic
|
||||
### Warmup Requirements ✅
|
||||
**Daily mode**: `max(35 MACD, 15 RSI, 50 EMA, 28 ADX, 20 BB, 63 momentum) + 5 = 68 bars`
|
||||
**Hourly mode**: `max(35 MACD, 15 RSI, 200 EMA, 28 ADX, 20 BB, 63 momentum) + 5 = 205 bars`
|
||||
|
||||
### Config (2026-02-11 Hourly)
|
||||
- RISK_PER_TRADE: 0.75% (was 1% daily)
|
||||
- ATR_STOP_MULTIPLIER: 2.5x (was 2.0x daily)
|
||||
- ATR_TRAIL_MULTIPLIER: 1.5x
|
||||
- ATR_TRAIL_ACTIVATION_MULTIPLIER: 1.5x
|
||||
- MAX_CONCURRENT_POSITIONS: 5 (was 8 daily)
|
||||
- MAX_SECTOR_POSITIONS: 2 (was 3 daily)
|
||||
- TIME_EXIT_BARS: 30 (~4.3 days)
|
||||
- REENTRY_COOLDOWN_BARS: 7 (~1 day)
|
||||
- RAMPUP_PERIOD_BARS: 30 (~4.3 days)
|
||||
- DRAWDOWN_HALT_BARS: 35 (~5 days)
|
||||
- Partial exits REMOVED (was destroying avg win/loss ratio)
|
||||
- Take profit REMOVED (was capping winners)
|
||||
Calculation in `config.rs:169-183` (`IndicatorParams::min_bars()`)
|
||||
- RSI-2/3 warmup covered by RSI-14 requirement (15 > 3)
|
||||
- MACD needs slow + signal periods (26 + 9 = 35)
|
||||
- ADX needs 2x period for smoothing (14 * 2 = 28)
|
||||
- Hourly EMA-200 dominates warmup requirement
|
||||
|
||||
### Hourly Indicator Periods
|
||||
RSI/MACD/ADX/BB/ATR: Standard periods (14, 12/26/9, etc) — NOT 7x scaled
|
||||
Momentum: 63 (~9 days), EMA: 20/50/100. Uses textbook periods appropriate for hourly bars.
|
||||
Both bot.rs and backtester.rs fetch sufficient historical data and validate bar count before trading.
|
||||
|
||||
---
|
||||
|
||||
## INTENTIONAL DIFFERENCES (Not Bugs) ✅
|
||||
|
||||
### 1. Slippage Modeling
|
||||
- **Backtester**: Applies 10 bps on both entry and exit (backtester.rs:63-71)
|
||||
- **Live bot**: Uses actual fill prices from Alpaca API (bot.rs:456-460)
|
||||
- **Verdict**: Expected difference. Backtester simulates realistic costs; live bot gets market fills.
|
||||
|
||||
### 2. RSI Short Period Scaling
|
||||
- **Daily mode**: `rsi_short_period: 2` (Connors RSI-2 for mean reversion)
|
||||
- **Hourly mode**: `rsi_short_period: 3` (adjusted for intraday noise)
|
||||
- **Verdict**: Intentional design choice per comment "Slightly longer for hourly noise"
|
||||
|
||||
### 3. EMA Trend Period Scaling
|
||||
- **Daily mode**: `ema_trend: 50` (50-day trend filter)
|
||||
- **Hourly mode**: `ema_trend: 200` (200-hour ≈ 28.5-day trend filter)
|
||||
- **Verdict**: Hourly uses 4x scaling (not 7x like other indicators) for longer-term trend context. Appears intentional.
|
||||
|
||||
---
|
||||
|
||||
## STRATEGY ARCHITECTURE (2026-02-12)
|
||||
|
||||
### Regime-Adaptive Dual Signal
|
||||
The new strategy uses **ADX for regime detection** and switches between two modes:
|
||||
|
||||
#### RANGE-BOUND (ADX < 20): Mean Reversion
|
||||
- **Entry**: Connors RSI-2 extreme oversold (RSI-2 < 10) + price above 200 EMA
|
||||
- **Exit**: RSI-2 extreme overbought (RSI-2 > 90) or standard exits
|
||||
- **Conviction boosters**: Bollinger Band extremes, volume confirmation
|
||||
- **Logic**: indicators.rs:490-526
|
||||
|
||||
#### TRENDING (ADX > 25): Momentum Pullback
|
||||
- **Entry**: Pullbacks in strong trends (RSI-14 dips 25-40, price near EMA support, MACD confirming)
|
||||
- **Exit**: Trend break (EMA crossover down) or standard exits
|
||||
- **Conviction boosters**: Strong trend (ADX > 40), DI+/DI- alignment
|
||||
- **Logic**: indicators.rs:531-557
|
||||
|
||||
#### UNIVERSAL SIGNALS (Both Regimes)
|
||||
- RSI-14 extremes in trending context (indicators.rs:564-570)
|
||||
- MACD crossovers (indicators.rs:573-583)
|
||||
- EMA crossovers (indicators.rs:599-608)
|
||||
- Volume gate (reduces scores 50% if volume < 80% of 20-period MA) (indicators.rs:611-614)
|
||||
|
||||
### Signal Thresholds
|
||||
- **StrongBuy**: total_score >= 7.0
|
||||
- **Buy**: total_score >= 4.5
|
||||
- **StrongSell**: total_score <= -7.0
|
||||
- **Sell**: total_score <= -4.0
|
||||
- **Hold**: everything else
|
||||
|
||||
Confidence: `(total_score.abs() / 12.0).min(1.0)`
|
||||
|
||||
---
|
||||
|
||||
## CONFIG PARAMETERS (2026-02-12)
|
||||
|
||||
### Indicator Periods
|
||||
- RSI: 14 (standard), RSI-2 (daily) / RSI-3 (hourly) for mean reversion
|
||||
- MACD: 12/26/9 (standard)
|
||||
- Momentum: 63 bars
|
||||
- EMA: 9/21/50 (daily), 9/21/200 (hourly)
|
||||
- ADX: 14, thresholds: 20 (range), 25 (trend), 40 (strong)
|
||||
- Bollinger Bands: 20-period, 2 std dev
|
||||
- ATR: 14-period
|
||||
- Volume MA: 20-period, threshold: 0.8x
|
||||
|
||||
### Risk Management
|
||||
- **Position sizing**: 1.2% risk per trade (RISK_PER_TRADE)
|
||||
- **ATR stop**: 3.0x ATR below entry (was 2.5x)
|
||||
- **ATR trailing stop**: 2.0x ATR distance, activates after 2.0x ATR gain (was 1.5x/1.5x)
|
||||
- **Max position size**: 25% (was 22%)
|
||||
- **Max loss cap**: 5% (was 4%)
|
||||
- **Stop loss fallback**: 2.5% (when ATR unavailable)
|
||||
- **Time exit**: 40 bars (was 30)
|
||||
- **Cash reserve**: 5%
|
||||
|
||||
### Portfolio Limits
|
||||
- **Max concurrent positions**: 7 (was 5)
|
||||
- **Max per sector**: 2 (unchanged)
|
||||
- **Momentum ranking**: Top 10 stocks (was 4)
|
||||
- **Drawdown halt**: 12% triggers 20-bar cooldown (was 35 bars)
|
||||
- **Reentry cooldown**: 5 bars after stop-loss (was 7)
|
||||
- **Ramp-up period**: 15 bars, 1 new position per bar (was 30 bars)
|
||||
|
||||
### Backtester
|
||||
- **Slippage**: 10 bps per trade
|
||||
- **Risk-free rate**: 5% annually for Sharpe/Sortino
|
||||
|
||||
---
|
||||
|
||||
## KEY LESSONS
|
||||
|
||||
### 1. Shared Logic Eliminates Drift
|
||||
Extracting common logic into `strategy.rs` ensures bot and backtester CANNOT diverge. Previously, duplicate implementations led to subtle differences (partial exits, bars_held increment timing, cooldown logic).
|
||||
|
||||
### 2. Warmup Must Account for Longest Chain
|
||||
For hourly mode, EMA-200 dominates warmup (205 bars). ADX also needs 2x period (28 bars) for proper smoothing. The `+ 5` safety margin is critical.
|
||||
|
||||
### 3. NaN Handling is Critical
|
||||
Indicators can produce NaN during warmup or with insufficient data. The signal generator uses safe defaults (e.g., `if adx.is_nan() { 22.0 }`) to prevent scoring errors.
|
||||
|
||||
### 4. ATR Fallbacks Prevent Edge Cases
|
||||
When ATR is zero/unavailable (e.g., low volatility or warmup), code falls back to fixed percentage stops. Without this, position sizing could explode or stops could fail.
|
||||
|
||||
### 5. Slippage Modeling is Non-Negotiable
|
||||
The backtester applies 10 bps slippage on both sides (20 bps round-trip) to simulate realistic fills. This prevents overfitting to unrealistic backtest performance.
|
||||
|
||||
---
|
||||
|
||||
## AUDIT CHECKLIST (For Future Audits)
|
||||
|
||||
When new changes are made, verify:
|
||||
|
||||
1. **Signal generation**: Still using shared `indicators::generate_signal()`?
|
||||
2. **Position sizing**: Still using shared `Strategy::calculate_position_size()`?
|
||||
3. **Risk management**: Still using shared `Strategy::check_stop_loss_take_profit()`?
|
||||
4. **Cooldown timers**: Identical logic in both files?
|
||||
5. **Ramp-up period**: Identical logic in both files?
|
||||
6. **Drawdown halt**: Identical trigger and resume logic?
|
||||
7. **Sector limits**: Same `MAX_SECTOR_POSITIONS` constant?
|
||||
8. **Max positions**: Same `MAX_CONCURRENT_POSITIONS` constant?
|
||||
9. **Momentum ranking**: Same `TOP_MOMENTUM_COUNT` constant?
|
||||
10. **bars_held increment**: Both increment at START of cycle/bar?
|
||||
11. **Warmup calculation**: Does `min_bars()` cover all indicators?
|
||||
12. **Config propagation**: Are new constants used consistently?
|
||||
13. **NaN handling**: Safe defaults for all indicator checks?
|
||||
14. **ATR guards**: Checks for `> 0.0` before division?
|
||||
|
||||
---
|
||||
|
||||
## FILES AUDITED (2026-02-12)
|
||||
- `/home/work/Documents/rust/invest-bot/src/bot.rs` (785 lines)
|
||||
- `/home/work/Documents/rust/invest-bot/src/backtester.rs` (880 lines)
|
||||
- `/home/work/Documents/rust/invest-bot/src/config.rs` (199 lines)
|
||||
- `/home/work/Documents/rust/invest-bot/src/indicators.rs` (651 lines)
|
||||
- `/home/work/Documents/rust/invest-bot/src/strategy.rs` (141 lines)
|
||||
- `/home/work/Documents/rust/invest-bot/src/types.rs` (234 lines)
|
||||
|
||||
**Total**: 2,890 lines audited
|
||||
**Issues found**: 0 critical, 0 medium, 0 low
|
||||
**Status**: ✅ PRODUCTION READY
|
||||
|
||||
Reference in New Issue
Block a user