Alpha Research
Alpha Research: MCX Crude Oil MA21 Strategy
Single Source of Truth for the PlusEV Trading System
Quick Reference: Backtest Proof
| Metric | Value | Notes |
|---|---|---|
| Total P&L | Rs +25,48,101 | 19-month validation period |
| Win Rate | 57.6% | Above 55% threshold |
| Profit Factor | 1.20 | Risk-adjusted returns |
| Total Trades | 7,534 | Statistical significance |
| Initial Capital | Rs 1,00,000 | Starting portfolio |
| Final Capital | Rs 26,48,101 | 2548% return |
Branch: rikk_mtf_backtest001 | Commit: 68dae212
In trading systems, empirical results > theoretical understanding. If the backtest shows it works, we ship it.
Part 1: Core Trading Philosophy
1.1 Foundational Principles
Simple systems outperform complex ones. Market behavior is probabilistic, not predictive. The goal is positioning with asymmetric probabilities, not prediction.
The Asymmetric Probability Edge
When odds are low on one outcome, the opposite side usually carries high odds:
- If new high probability drops to 15%, then 85% odds favor reversal
- We position ourselves on the high-probability side
1.2 Probability Zones (Halves & Thirds)
Every large move is divided into halves and thirds, creating probability zones.
Odds of Continuation After Pullback
┌─────────────────────────────────────────────────────┐ │ SWING HIGH │ ├─────────────────────────────────────────────────────┤ │ TOP THIRD → ~80% chance of new high │ ← IDEAL SHORT ZONE ├─────────────────────────────────────────────────────┤ │ TOP HALF → ~65% chance of new high │ ├─────────────────────────────────────────────────────┤ │ BOTTOM HALF → ~35% chance of new high │ ├─────────────────────────────────────────────────────┤ │ BOTTOM THIRD → ~15% chance of new high │ ← IDEAL LONG ZONE ├─────────────────────────────────────────────────────┤ │ SWING LOW │ └─────────────────────────────────────────────────────┘
Code Implementation
<source lang="python">
- File: signal_generation_trade_management.py:99-109
enable_probability_zone_filter: bool = True probability_zone_swing_lookback: int = 20 # Bars to find swing H/L probability_zone_min_range: float = 20.0 # Min range for valid zone </source>
1.3 The Crash Play (Core Strategy)
What is a Crash Bar?
- Much larger than recent bars
- Fast, violent, elongated movement
- Indicates a structural break
The Crash Play Logic
1. Identify crash bar (bar > 2x average size) 2. Divide it into halves and thirds 3. Watch for bounce INTO these zones 4. Enter when: - Color change confirms (red overtakes green), OR - Price reaches upper zones
Key Statistics
- After a crash, bounces have only ~15% odds of reclaiming highs
- ~85% odds favor continuation lower
- Bounces after crashes are statistically SHORT opportunities
Code Implementation
<source lang="python">
- File: signal_generation_trade_management.py:108
crash_bar_multiplier: float = 2.0 # Bar must be 2x average for crash detection </source>
1.4 Pullback vs Collapse (Critical Distinction)
Healthy Pullback (Tradeable)
| Characteristic | Description |
|---|---|
| Angle | 45-degree drifting decline, NOT vertical |
| Bars | No violent or oversized red bars |
| Depth | Holds above halfway point of prior run |
| Confirmation | Color change (green overtakes red) |
Interpretation: Pullbacks are rests, not reversals. Odds favor continuation.
Collapse (NOT Tradeable)
| Characteristic | Description |
|---|---|
| Angle | Vertical, elongated, violent first drop |
| Depth | Breaks deeply into or below prior run |
| Momentum | Abrupt and forceful |
Interpretation: Indicates institutional selling. Bounces are sucker plays.
1.5 Identifying Market Tops (4 Characteristics)
Major tops usually exhibit at least 2 of these 4 characteristics:
1. Three-to-Five Leg Run-Up
Markets top between leg 3 and leg 5. - Buy: legs 1-2 - Ride: leg 3 - Take profits: legs 4-5 - Buying late (leg 4-5) = large downside vs small upside
2. Vertical Acceleration in Final Leg
If the last leg is STEEPER than prior legs → likely final leg The most vertical leg is usually the top.
3. Exhaustion Bar
Final bar of final leg is: - Larger than recent bars - Often the largest bar of entire run = Bulls deploy all remaining buying power = Market becomes top-heavy and vulnerable
4. Three-Finger Spread (Dual Space)
Large separation between: - Price - 21-period MA - 200-period MA = Many traders holding large unrealized profits = Any red bar can trigger mass profit-taking = Creates fast, violent declines
Code Implementation
<source lang="python">
- File: signal_generation_trade_management.py:107
three_finger_spread_threshold: float = 0.02 # 2% min spread for detection </source>
Part 2: Entry Methodology
2.1 The Fab Four Concept
The Fab Four defines the key reference points for entries and stops:
┌─────────────────────────────────────────────────────┐ │ FAB FOUR ELEMENTS │ ├─────────────────────────────────────────────────────┤ │ 1. Previous Day High/Low (PDHL) │ │ 2. 21-Period Moving Average │ │ 3. 200-Period Moving Average │ │ 4. Yesterday's Close │ └─────────────────────────────────────────────────────┘
MA is a ZONE, Not a Line
The MA is a zone, not a thin line. Don't be exact with this.
<source lang="python">
- File: signal_generation_trade_management.py:68
ma_buffer_points: float = 25.0 # 21 MA zone width (not a thin line) </source>
Fab Four Tightness = Explosive Potential
When the Fab Four elements are tight together:
- Indicates compressed energy
- Expect powerful move (up or down)
- Best trades come from narrowness
2.2 Entry Position Hierarchy
Best Entry Positions
FOR SHORTS: ┌─────────────────────────────────────────────────────┐ │ CEILING (Best short position) │ │ ═══════════════════════════════════════════ │ ← Price right under Fab Four │ Price opens HERE │ └─────────────────────────────────────────────────────┘ FOR LONGS: ┌─────────────────────────────────────────────────────┐ │ Price opens HERE │ │ ═══════════════════════════════════════════ │ ← Price right above Fab Four │ FLOOR (Best long position) │ └─────────────────────────────────────────────────────┘
Multi-Timeframe Alignment
Check all timeframes simultaneously:
┌────────┬────────┬────────┬────────┐
│ 2min │ 5min │ 15min │ Daily │
│ ──── │ ──── │ ──── │ ──── │
│ Under │ Under │ Under │ Under │
│ 200 │ 200 │ 200 │ 200 │
│ Tight │ Tight │ Tight │ Tight │
└────────┴────────┴────────┴────────┘
= ALIGNED = HIGH QUALITY SETUP
Code Implementation
<source lang="python">
- File: setup_quality_detection.py:43-48
timeframe_alignment_weight: float = 0.30 # 30% of setup quality score </source>
2.3 Bar-by-Bar vs Big Bar Stops
The Rule
| When to Use | Stop Method | Why |
|---|---|---|
| Near MA (hugging) | Big Bar stops | Eliminates noise |
| Far from MA (extended) | Bar-by-bar stops | Protect extended gains |
The Warning
Bar-by-bar will knock you out of riches. If there is one stopout that is going to knock you out of the biggest gains of your life, it's going to be bar-by-bar.
Use bar-by-bar ONLY when:
- Price has moved significantly away from MA
- "You had better be moving to Pluto"
- Slant on both 2min and 5min is sharp
Part 3: Stop Loss Methodology
3.1 Stop Placement Hierarchy
Two Stop Options
| Type | Description | When to Use |
|---|---|---|
| Event Stop | Based on price action event | Primary choice |
| Maximum Stop | Dollar-based max loss | Fallback when event too far |
The Goal
┌─────────────────────────────────────────────────────┐ │ FAB FOUR ZONE (Helping Zone) │ ├─────────────────────────────────────────────────────┤ │ ↑ STOP SHOULD BE HERE (above/below Fab Four) │ ├─────────────────────────────────────────────────────┤ │ Entry price │ └─────────────────────────────────────────────────────┘
The real goal is to get your stop beyond the Fab Four helping zone.
Code Implementation
<source lang="python">
- File: signal_generation_trade_management.py:59-60
min_stop_distance: float = 40.0 # TICKET-13: Increased from 20 default_stop_distance: float = 40.0 # Minimum buffer for price noise </source>
3.2 Stop and Reverse Rules
Three criteria for a valid Stop and Reverse:
| Criteria | Description |
|---|---|
| 1. Immediate | Stop out happens INSTANTLY, not gradually |
| 2. Violent | Move that stops you is hard, powerful, shakes earth |
| 3. Decent Position | New position is NOT into resistance/support |
Invalid Stop and Reverse
❌ Going LONG right INTO the 200 MA ❌ Going LONG right INTO tight Fab Four zone ❌ Going SHORT right INTO strong support Even if immediate + violent, bad position = no reversal
Code Implementation
<source lang="python">
- File: signal_generation_trade_management.py:80-81
bos_structure_lookback: int = 10 # TICKET-11: Extended for BOS stops bos_stop_buffer_points: float = 5.0 # Buffer for structure-based stops </source>
Part 4: Color Change Confirmation
4.1 The Color Change Entry
Entry Trigger
FOR SHORTS: Red bar closes BELOW green bar's low → ENTER SHORT Stop: Above the high FOR LONGS: Green bar closes ABOVE red bar's high → ENTER LONG Stop: Below the low
Key Rule: Don't Wait for Bar to Complete
As soon as the red bar crosses below the green, boom boom boom boom boom. You're in.
4.2 Profit Taking Rules
The Two-Bar Rule
Take profits TWO bars after the opposite color, NOT one bar.
SHORT TRADE: ┌─────────────────────────────────────────────────────┐ │ GREEN bar (opposite color) │ ├─────────────────────────────────────────────────────┤ │ RED bar 1 ← IGNITING bar (DO NOT take profit) │ ├─────────────────────────────────────────────────────┤ │ RED bar 2 ← FOLLOW-THROUGH bar (TAKE PROFIT) │ └─────────────────────────────────────────────────────┘
Position Management Flow
Step 1: Enter with 2 lots Step 2: Set stop above high Step 3: Take profit on 1 lot (first follow-through bar) Step 4: Move stop to break-even Step 5: Take profit on remaining lot (second follow-through) Step 6: Done
The first lot is your REAL trade. The second lot is the cherry on top.
Code Implementation
<source lang="python">
- File: signal_generation_trade_management.py:111-118
enable_color_change_filter: bool = False # DISABLED - too restrictive color_change_ma_proximity: float = 20.0 # Points to consider "near" MA require_ma_bounce: bool = False # Don't require strict MA bounce </source>
4.3 The Traffic Jam Concept
When price gaps below everything from prior day:
┌─────────────────────────────────────────────────────┐ │ Yesterday's 200 MA │ │ Yesterday's 20 MA │ │ Yesterday's Close │ │ ═══════════════════════════════════════════ │ │ TRAFFIC JAM ZONE (resistance on any rally) │ │ ═══════════════════════════════════════════ │ │ │ │ TODAY'S GAP DOWN OPEN │ └─────────────────────────────────────────────────────┘
Rule: Any move UP into the traffic jam meets resistance. Short rallies into this zone.
Part 5: Trading Glossary for Engineers
| Trading Term | System Translation | Code Location | Why It Matters |
|---|---|---|---|
| Creeper Move | Small-bodied bars, consistent direction, low volume | market_state_analysis.py:is_creeper_move |
Institutional accumulation before explosion. PENALTY: -50 points |
| Railroad Trend | Alternating green/red bars touching MA | market_state_analysis.py:is_railroad_trend |
Healthy trend, not overextended. BONUS: +15 points |
| Two-Day Trend | Price consistently above/below MA for 2+ days | market_state_analysis.py:has_two_day_trend |
Confirms trend maturity. Required for A+ grade |
| Institutional Fight | High volume with narrow range | market_state_analysis.py:institutional_fight_in_progress |
Buyers vs sellers battle. MULTIPLIER: 0.7x score |
| Discount Zone | Price below 21 MA (for longs) | probability_zone_analysis.py |
80% continuation probability |
| Premium Zone | Price above 21 MA (for shorts) | probability_zone_analysis.py |
Entry zone for short trades |
| Fab Four | PDHL, 21 MA, 200 MA, Yesterday's Close | signal_generation.py:_calculate_stop_loss() |
Stop loss placement hierarchy |
| Color Change | Bar closes opposite color after move | probability_zone_analysis.py:ColorChangeDetector |
Confirms reversal/continuation |
| Break of Structure (BOS) | Price closes beyond recent swing H/L | market_state_analysis.py:bos_detected |
Confirms trend continuation/reversal |
| MA Zone | MA is a band, not thin line (21 MA +/- 25pts) | signal_generation.py:ma_buffer_points=25 |
"Fab Four" concept from philosophy |
| Trend Phase | EARLY / MIDDLE / LATE classification | market_state_analysis.py:trend_phase |
MIDDLE phase = optimal entry timing |
| Crash Bar | Bar > 2x average size, violent | probability_zone_analysis.py:crash_bar_multiplier |
Structural break indicator |
| Traffic Jam | Yesterday's price activity zone | Conceptual | Resistance zone for gaps |
| Three-Finger Spread | Large gap between Price, 21 MA, 200 MA | signal_generation.py:three_finger_spread_threshold |
Market top indicator |
Part 6: Component Architecture
6.1 Pipeline Flow
┌─────────────────┐ ┌───────────────────┐ ┌────────────────────┐
│ Data Manager │ → │ Trend Analysis │ → │ Market State │
│ (1M/5M/1H/1D) │ │ (MA21, MA200) │ │ (7 Detections) │
└─────────────────┘ └───────────────────┘ └────────────────────┘
↓
┌─────────────────┐ ┌───────────────────┐ ┌────────────────────┐
│ Trade Exec │ ← │ Signal Gen │ ← │ Setup Quality │
│ (P&L, Costs) │ │ (Entry/Exit) │ │ (5-Factor Score) │
└─────────────────┘ └───────────────────┘ └────────────────────┘
6.2 Setup Quality Detection
File: src/components/setup_quality_detection.py
5-Factor Weighted Scoring System
| Factor | Weight | What It Measures |
|---|---|---|
| Timeframe Alignment | 30% | All timeframes agree on direction |
| Trend Strength | 20% | Healthy trend without warnings |
| Entry Quality | 15% | Entry near MA, clean execution |
| Key Level Proximity | 20% | Entry near support/resistance |
| Risk/Reward | 15% | Favorable profit potential |
Penalty System
| Condition | Penalty | Trading Reason |
|---|---|---|
| Creeper Move | -50 pts | Quiet accumulation = reversal coming |
| MA Struggle | -30 pts | Price can't break MA = weak trend |
| No Two-Day Trend | -30 pts | Trend not confirmed |
| Wrong Trend Phase | -25 pts | Too early or too late in trend |
| Not Near MA | -40 pts | Entry too far from edge |
| Institutional Fight | 0.7x | Wait for resolution |
Grade Thresholds
| Grade | Score | Position Size | Auto-Trade? |
|---|---|---|---|
| A+ | >= 90 | 2 lots | Yes |
| A | >= 80 | 1 lot | Yes |
| B | >= 70 | 1 lot | No |
| C | >= 60 | 1 lot | No |
| D | >= 50 | 1 lot | No |
| F | < 50 | 1 lot | No |
6.3 Market State Analysis
File: src/components/market_state_analysis.py
7 Detection Algorithms
| # | Algorithm | What It Detects | Trading Implication |
|---|---|---|---|
| 1 | Railroad Trend | Healthy alternating bars | +15 bonus, trend continuation likely |
| 2 | Creeper Move | Small bars, consistent direction | -50 penalty, reversal imminent |
| 3 | Volatility Calc | ATR-based regime | Adjust stop distance |
| 4 | Market State | TRENDING / SIDEWAYS / VOLATILE | Different strategy rules |
| 5 | Two-Day Trend | Multi-day momentum | Required for A+ grade |
| 6 | Trend Phase | EARLY / MIDDLE / LATE | MIDDLE = optimal entry |
| 7 | Institutional Behavior | Volume + price patterns | Fight = wait, Accumulation = prepare |
6.4 Signal Generation
File: src/components/signal_generation_trade_management.py
Entry Filters Applied
| Filter | Config Key | Purpose |
|---|---|---|
| Hour Filter | blocked_hours: [9, 22, 23] |
Block loss-making hours |
| MA Direction | enable_ma_direction_filter: True |
LONG only if MA rising |
| Probability Zone | enable_probability_zone_filter: True |
Entry in favorable zones |
| Color Change | enable_color_change_filter: False |
Disabled (too restrictive) |
Part 7: Branch Changes (service/backtest → rikk_mtf_backtest001)
7.1 Complete Git Diff Summary
Total Changes: 65 files changed across core source, scripts, and data
Modified (M) Source Files
| File | Type | Key Changes |
|---|---|---|
signal_generation_trade_management.py |
Component | TICKET-7,8,10,11,12,13,18,19,20,21,24 + PDF fixes + Grading fix |
trade_execution_engine.py |
Component | TICKET-9,13,15,17,20: Trailing stop, breakeven, ATR |
market_state_analysis.py |
Component | TICKET-25: 5MIN → 1HOUR data |
setup_quality_detection.py |
Component | TICKET-5: Position sizing fix |
timeframe_conversion.py |
Core | Date column fix, 1m skip, emoji removal |
data_structures.py |
Core | TICKET-15: current_stop_price field
|
multi_timeframe_analysis.py |
Component | Whitespace cleanup |
main.py |
Entry | TICKET-12 config, emoji removal |
Added (A) Files
| File | Purpose |
|---|---|
probability_zone_analysis.py |
NEW: Implements trade_philosophy.pdf (974 lines) |
50+ scripts/analyze_*.py |
Analysis scripts for hypothesis building |
7.2 JIRA Ticket Fixes (Complete Details)
TICKET-5: Position Sizing Fix
File: setup_quality_detection.py:544-567
Problem: Previous sizing = 100-200 lots = Rs 5.7 Crore exposure on Rs 1 lakh capital (5700x!)
<source lang="python">
- BEFORE (service/backtest):
multipliers = {
SetupQualityGrade.A_PLUS: 200, # 200 lots (impossible!) SetupQualityGrade.A: 150, SetupQualityGrade.B: 100, # ...
}
- AFTER (rikk_mtf_backtest001):
position_sizes = {
SetupQualityGrade.A_PLUS: 2, # 2 lots (Rs 1.14L exposure - realistic) SetupQualityGrade.A: 1, # 1 lot SetupQualityGrade.B: 1, SetupQualityGrade.C: 1, SetupQualityGrade.D: 1, SetupQualityGrade.F: 1
} </source>
TICKET-7: R:R Preservation After Slippage
File: signal_generation_trade_management.py:241-285
Problem: Slippage moved entry but stop/target stayed fixed = corrupted R:R ratio
<source lang="python">
- BEFORE: Stop and target not adjusted after slippage
actual_execution_price = execution_price + market_slippage # Entry moved
- BUT stop_loss_price and take_profit_price stayed at original levels!
- RESULT: R:R became inverted (target closer than stop)
- AFTER: Shift stop and target with entry to maintain R:R
- Example (LONG with +1.5 pts slippage):
- Before: entry=5710, stop=5680, target=5740 (30 pts each, 1:1 R:R)
- After slippage: entry=5711.5, stop=5680, target=5740
- WRONG R:R: stop=31.5 pts, target=28.5 pts (INVERTED!)
- FIX: entry=5711.5, stop=5681.5, target=5741.5 (30 pts each, 1:1 R:R)
if signal.direction == Direction.LONG:
actual_execution_price = execution_price + market_slippage # TICKET-7: Shift stop and target UP by slippage to maintain R:R adjusted_stop_loss = signal.stop_loss_price + market_slippage adjusted_take_profit = signal.take_profit_price + market_slippage
else: # SHORT
actual_execution_price = execution_price - market_slippage # TICKET-7: Shift stop and target DOWN by slippage to maintain R:R adjusted_stop_loss = signal.stop_loss_price - market_slippage adjusted_take_profit = signal.take_profit_price - market_slippage
</source>
TICKET-8: CTT Rate Fix
File: signal_generation_trade_management.py:48-55
Problem: CTT was 0.05% (5x too high!). Dhan broker calculates Rs 52 on Rs 5.2L = 0.01%
<source lang="python">
- BEFORE:
transaction_tax_rate: float = 0.000500 # 0.05% - WRONG!
- AFTER:
- MCX CTT = 0.01% on SELL side only. Since code charges at entry+exit,
- use 0.005% per leg so round-trip = 0.01% total (matches actual CTT)
- Dhan verified: CTT Rs 52 on Rs 5,20,000 sell value = 0.01%
transaction_tax_rate: float = 0.000050 # 0.005% per leg × 2 = 0.01% total </source>
TICKET-9: Market Impact Slippage Cap
File: trade_execution_engine.py:40-42, 585-605
Problem: 100 lots × 0.1 = 10 pts slippage (excessive!)
<source lang="python">
- BEFORE: No cap on market impact
size_impact = signal.position_size * self.config.market_impact_factor
- AFTER: Cap to prevent excessive slippage
max_market_impact_slippage: float = 2.0 # Maximum 2 points
raw_size_impact = signal.position_size * self.config.market_impact_factor size_impact = min(raw_size_impact, self.config.max_market_impact_slippage)
- For 1-2 lots (after TICKET-5 fix):
- - base: 1.0 pts
- - market_impact: min(1×0.1, 2.0) = 0.1 pts (capped at 2.0)
- - volatility: 0.5 pts
- - total: ~1.6 pts (realistic for MCX crude)
</source>
TICKET-10: Entry Technique Alignment
File: signal_generation_trade_management.py:2012-2140
Problem: Entry technique logic didn't match strategy_engine.py priority order
<source lang="python">
- COMPLETE REWRITE of _determine_entry_technique():
- BEFORE: Random priority order, creeper before BOS, missing key level checks
- AFTER (aligned with strategy_engine.py::_classify_entry_technique_exact()):
- PRIORITY 1: Creeper move → NEAR_MA (NOT green_bar/red_bar!)
- PRIORITY 2: BOS detected → BOS_ENTRY (only if NOT creeper)
- PRIORITY 3: Near key level → DISCOUNT_ZONE / BREAKOUT_PULLBACK
- PRIORITY 4: Near MA → MA_BOUNCE
- PRIORITY 5: Two-day trend → TWO_GREEN_DAILY / TWO_RED_DAILY
- PRIORITY 6: Railroad trend → GREEN_BAR_AFTER_PULLBACK / RED_BAR_AFTER_RALLY
- PRIORITY 7: Fallback → NEAR_MA
</source>
TICKET-11: BOS Stop Settings
File: signal_generation_trade_management.py:78-81
Problem: 5-bar lookback + no buffer = stop too close in trending markets = 66.7% stop rate
<source lang="python">
- BEFORE:
structure_lookback: int = 5 # Too short for BOS
- AFTER:
bos_structure_lookback: int = 10 # Extended lookback for BOS stops bos_stop_buffer_points: float = 5.0 # Buffer below/above structure </source>
TICKET-12: Hour Filter
File: signal_generation_trade_management.py:83-93, 126-137, 571-581
Problem: Certain hours consistently lose money
<source lang="python">
- NEW CONFIGURATION:
enable_hour_filter: bool = True blocked_hours: [9, 22, 23] # Block 9-10 AM and after 10 PM
- Data-driven analysis showed (from 1439 trades):
- Worst hours: 15(-Rs244K), 22(-Rs209K), 09(-Rs199K), 21(-Rs101K), etc.
- Profitable hours: 16, 17, 18, 19, 23 (total +Rs341K)
- Applied in generate_signal():
if self.config.enable_hour_filter and self.config.blocked_hours:
current_hour = current_timestamp.hour
if current_hour in self.config.blocked_hours:
return None # No signal during blocked hours
</source>
TICKET-13: Minimum Stop Distance
File: signal_generation_trade_management.py:57-60, 852-870
Problem: 20pt stops hit too frequently due to normal price noise
<source lang="python">
- BEFORE:
default_stop_distance: float = 20.000000
- AFTER:
min_stop_distance: float = 40.000000 # Minimum stop distance default_stop_distance: float = 40.000000 # Default increased
- ENFORCEMENT in generate_signal():
- TICKET-13 FIX: ENFORCE MINIMUM STOP DISTANCE (40 POINTS)
stop_distance = max(calculated_stop_distance, self.config.min_stop_distance) </source>
TICKET-15: Trailing Stop Tracking
File: data_structures.py:2982-2984, trade_execution_engine.py:54-70, 295-392
Problem: No way to track current trailing stop position vs original
<source lang="python">
- NEW FIELD in Trade dataclass:
current_stop_price: Optional[float] = None # Current trailing stop (moves with price)
- NEW METHOD in TradeExecutionEngine:
def _update_trailing_stop(self, trade: Trade, current_high: float, current_low: float):
""" TICKET-15/17/20: Update stop based on price movement.
Two-Phase Stop Management:
PHASE 1 - Breakeven (TICKET-17): Move stop to entry when profitable
PHASE 2 - Trailing (TICKET-15/20): Trail stop behind price
"""
if trade.current_stop_price is None:
trade.current_stop_price = trade.original_stop_price
# Calculate trailing distance (ATR-based or fixed)
if self.config.trailing_stop_method == "ATR_MULTIPLE":
trailing_distance = self.current_atr * self.config.atr_multiplier
trailing_distance = max(trailing_distance, 15.0) # Min 15 pts for Crude
else:
trailing_distance = self.config.trailing_stop_distance
if trade.direction == Direction.LONG:
current_profit = current_high - trade.entry_price
if current_profit >= self.config.trailing_stop_activation:
new_stop = current_high - trailing_distance
if new_stop > trade.current_stop_price:
trade.current_stop_price = new_stop # Only move UP
</source>
TICKET-17: Breakeven Stop
File: trade_execution_engine.py:64-68
Problem: Needed automatic breakeven move when in profit
<source lang="python">
- NEW CONFIGURATION:
enable_breakeven_stop: bool = True breakeven_activation: float = 25.0 # Points profit before moving to breakeven breakeven_buffer: float = 2.0 # Points above/below entry for buffer
- Tested 10pt activation but cut winners too short (26% WR vs 49%)
- 25pt activation is the sweet spot
</source>
TICKET-18: Creeper Move Handling
File: signal_generation_trade_management.py:582-595
Problem: Previous implementation blocked creeper signals entirely (too aggressive)
<source lang="python">
- BEFORE: Creeper signals blocked with return None
- AFTER (comment explains fix):
- CORE BEHAVIOR: Creeper moves get -50 penalty + 0.7x target + uses NEAR_MA
- Previous implementation: Blocked entirely with return None (too aggressive)
- FIX: Let creeper signals through - penalty applied in setup_quality_detection,
- NEAR_MA technique used in _determine_entry_technique()
- Let the scoring system decide rather than blocking outright
</source>
TICKET-19: MA Direction Filter + Score Adjustment
File: signal_generation_trade_management.py:95-97, 987-1034, 1955-2010
Problem: Trades against MA21 slope direction had poor performance
<source lang="python">
- CONFIGURATION:
enable_ma_direction_filter: bool = True
- DIRECTION LOGIC COMPLETE REWRITE (_determine_signal_direction):
- BEFORE: Used alignment score threshold (>= 60%)
- AFTER: Uses MA21 slope as primary direction source
def _determine_signal_direction(self, timeframe_analysis, market_state, market_data=None):
"""MA21 SLOPE ALIGNMENT DIRECTION LOGIC (trade_philosophy.pdf)""" trend_direction = getattr(market_state, 'trend_direction', None)
if trend_direction == "up":
return Direction.LONG # MA21 rising → only LONG
elif trend_direction == "down":
return Direction.SHORT # MA21 declining → only SHORT
else:
# Fallback to MTF primary_direction if MA21 is flat/unknown
return getattr(timeframe_analysis, 'primary_direction', None)
- SCORE ADJUSTMENT (lines 987-1034):
- +15% bonus if trade direction aligns with MA21 slope
- -10% penalty if trade direction against MA21 slope
</source>
TICKET-20: Probability Zone Filter + ATR Trailing
File: signal_generation_trade_management.py:99-109, 628-817, trade_execution_engine.py:57-63, 295-340
Problem: Needed trade_philosophy.pdf concepts in code
<source lang="python">
- NEW CONFIGURATION (Probability Zones):
enable_probability_zone_filter: bool = True probability_zone_swing_lookback: int = 20 probability_zone_min_range: float = 20.0 three_finger_spread_threshold: float = 0.02 crash_bar_multiplier: float = 2.0 sideways_mean_reversion: bool = True
- NEW CONFIGURATION (ATR Trailing):
trailing_stop_method: str = "ATR_MULTIPLE" # or "FIXED_POINTS" atr_period: int = 14 atr_multiplier: float = 2.0 # 2x ATR = trailing distance
- NEW ATR CALCULATION in trade_execution_engine.py:295-340
def _calculate_atr(self, five_min_data: Dict) -> None:
"""Calculate Average True Range for dynamic trailing stop."""
# TrueRange = max(high-low, abs(high-prev_close), abs(low-prev_close))
# ATR = SMA(TrueRange, period)
true_ranges = []
for i in range(1, len(high_vals)):
tr = max(
high_vals[i] - low_vals[i], # Current high - low
abs(high_vals[i] - close_vals[i-1]), # High - previous close
abs(low_vals[i] - close_vals[i-1]) # Low - previous close
)
true_ranges.append(tr)
self.current_atr = sum(true_ranges) / len(true_ranges)
</source>
TICKET-21: Color Change Filter (DISABLED)
File: signal_generation_trade_management.py:111-118
Problem: color_change.pdf patterns too restrictive for automation
<source lang="python">
- CONFIGURATION (disabled):
enable_color_change_filter: bool = False # DISABLED - too restrictive color_change_ma_proximity: float = 20.0 # Points to consider "near" MA require_ma_bounce: bool = False # Too strict color_change_require_in_zones: bool = True
- NOTE: ColorChangeDetector class still exists for future use
- Implements liquidity sweep patterns:
- - LONG: Green bar sweeps BELOW red bar low + bounce from 21 MA
- - SHORT: Red bar sweeps ABOVE green bar high + rejection from 21 MA
</source>
TICKET-24: Creeper Move Entry Technique Fix
File: signal_generation_trade_management.py:2037-2050
Problem: strategy_engine.py bug - uses wrong entry technique for creeper moves
<source lang="python">
- STRATEGY ENGINE BUG FOUND:
- backtesting_standalone/src/strategy_engine.py lines 3338-3380
- checks bos_detected FIRST but doesn't consider is_creeper_move.
- This causes BOS entries in creeper moves (inappropriate).
- FIX: Check creeper FIRST before BOS
if is_creeper:
# Creeper gets -50 penalty + 0.7x target from setup_quality_detection # But uses NEAR_MA as technique (NOT green_bar/red_bar!) return EntryTechnique.NEAR_MA # Strategy engine fallback
</source>
TICKET-25: Market State 5MIN → 1HOUR
File: market_state_analysis.py:191-360
Problem: Creeper threshold 0.5% calibrated for 1H bars (~0.8-1.2% range), but code used 5MIN (~0.16% range) = 98.9% false positives!
<source lang="python">
- BEFORE:
required_tf = TimeframeValue.FIVE_MIN
- AFTER:
def validate_input(self, input_data: Dict, context: ComponentContext):
""" TICKET-25 FIX: Market state analysis uses 1-HOUR data (not 5MIN) to match strategy_engine.py behavior for creeper/railroad detection. """ primary_tf = TimeframeValue.ONE_HOUR fallback_tf = TimeframeValue.FIVE_MIN
if primary_tf in input_data and input_data[primary_tf] is not None:
required_tf = primary_tf
elif fallback_tf in input_data and input_data[fallback_tf] is not None:
required_tf = fallback_tf
# Warning: 5MIN fallback may cause inaccurate creeper detection
def _get_primary_timeframe(self, input_data: Dict) -> TimeframeValue:
""" TICKET-25 FIX: Use ONE_HOUR for market state analysis.
The 0.5% creeper threshold is calibrated for 1-HOUR bars (~0.8-1.2% typical range), not 5-MIN bars (~0.16% typical range). Using 5MIN caused 98.9% false positives. """ primary_tf = TimeframeValue.ONE_HOUR # Was FIVE_MIN
</source>
PDF-FIX: MA Buffer Points
File: signal_generation_trade_management.py:67-68
Source: Fab Four concept - "MA is a zone, not a thin line"
<source lang="python">
- BEFORE:
ma_buffer_points: float = 5.000000 # Too tight!
- AFTER:
- PDF-FIX: MA is a "zone, not a thin line" (Fab Four) - use wider buffer
ma_buffer_points: float = 25.000000 # 21 MA zone width </source>
PDF-FIX: Take Profit Calculation Rewrite
File: signal_generation_trade_management.py:1620-1677
Problem: Old method used strategy.entry_exit_settings with FIXED_POINTS, ATR_MULTIPLE, etc. PDF philosophy uses 50% of prior move.
<source lang="python">
- BEFORE: Used strategy config with multiple methods
def _calculate_take_profit(self, strategy, entry_price, stop_loss_price, direction):
profit_target_method = strategy.entry_exit_settings.profit_target_method
if profit_target_method == ProfitTargetMethod.FIXED_POINTS:
profit_points = strategy.entry_exit_settings.profit_target_points or 25
return entry_price + profit_points # etc.
elif profit_target_method == ProfitTargetMethod.ATR_MULTIPLE:
# ... ATR logic
- AFTER: PDF-based 50% mean reversion target
def _calculate_take_profit(self, strategy, entry_price, stop_loss_price, direction, market_data=None):
"""PDF-FIX: Target = 50% of prior move (trade_philosophy.pdf)"""
if market_data:
ma21_series = tf_data.get("ma21")
if ma21_series is not None:
ma_21 = float(ma21_series.iloc[-1])
# Calculate 50% move toward MA
distance_to_ma = abs(entry_price - ma_21)
target_distance = distance_to_ma * 0.5
# But ensure minimum target based on risk
risk_distance = abs(entry_price - stop_loss_price)
min_target = risk_distance * 1.5 # At least 1.5:1 R:R
target_distance = max(target_distance, min_target)
return entry_price + target_distance # (or minus for SHORT)
# Fallback to 2:1 R:R if no MA data risk_distance = abs(entry_price - stop_loss_price) return entry_price + (risk_distance * 2)
</source>
PDF-FIX: CRASH SHORT Detection (Direction Override)
File: signal_generation_trade_management.py:700-750
Logic: In sideways market at top zone, big RED bar breaking below 21 MA overrides LONG direction to SHORT
<source lang="python">
- NEW CODE: Crash SHORT detection within probability zone filter
- Per PDF: In top third, big RED bar breaking 21 MA = SHORT signal
is_red_bar = current_close < current_open is_big_bar = current_range > avg_range * 1.2 # 1.2x average = significant bar breaks_below_ma = current_close < (ma_21 - 25.0) # Below MA zone was_above_ma = current_open > (ma_21 - 25.0) is_top_zone = zone_result.zone in [ProbabilityZone.TOP_THIRD, ProbabilityZone.TOP_HALF] is_sideways = market_state_str in ["creeper", "creeper_move", "sideways", "consolidation"]
- CRASH SHORT DETECTION: Override LONG to SHORT in sideways markets
if is_sideways and is_top_zone and is_big_bar and is_red_bar and breaks_below_ma and was_above_ma:
self.logger.debug(f"CRASH SHORT DETECTED: Overriding {direction} to SHORT")
direction = Direction.SHORT
is_crash_short = True # Flag to skip color change filter
</source>
PDF-FIX: Stop Loss Calculation (NEAR_MA technique)
File: signal_generation_trade_management.py:1490-1530
Problem: Old code placed stop relative to MA value. New code uses structure low/high.
<source lang="python">
- BEFORE: Stop below/above MA value
if entry_technique == EntryTechnique.NEAR_MA_LONG:
if ma21_values is not None:
ma_value = ma21_values[-1]
buffer_points = self.config.ma_buffer_points
stop_loss = ma_value - buffer_points # Below MA
- AFTER: Stop below structure low (more robust)
if entry_technique == EntryTechnique.NEAR_MA_LONG:
# PDF-FIX: Stop should be below recent structure low, not just below MA
# When price is already below MA (LONG per new direction logic),
# stop below MA would be above entry = wrong!
lookback = self.config.structure_lookback # 5 bars
if len(low_values) >= lookback:
structure_low = min(low_values[-lookback:])
stop_loss = structure_low - self.config.ma_buffer_points
# Ensure stop is below entry
if stop_loss >= entry_price:
stop_loss = entry_price - self.config.default_stop_distance
</source>
GRADING FIX: Score-to-Grade Recalculation
File: signal_generation_trade_management.py:1054, 1132-1170
Problem: Grade assigned from capped original score instead of adjusted score
<source lang="python">
- BEFORE:
setup_quality=setup_quality.grade, # Grade from CAPPED original score (79) setup_score=setup_quality.score, # Original score (79)
- Problem: Score=79 → Grade=A, but if adjustment makes it 89, should be A+!
- AFTER:
setup_quality=self._calculate_grade_from_score(adjusted_score), # RECALCULATED setup_score=adjusted_score, # MA-adjusted score
- NEW METHOD ADDED:
def _calculate_grade_from_score(self, score: float) -> SetupQualityGrade:
"""GRADING FIX: Calculate grade from FINAL adjusted score.""" if score >= 90: return SetupQualityGrade.A_PLUS elif score >= 80: return SetupQualityGrade.A elif score >= 70: return SetupQualityGrade.B elif score >= 60: return SetupQualityGrade.C elif score >= 50: return SetupQualityGrade.D else: return SetupQualityGrade.F
</source>
7.3 New File: probability_zone_analysis.py (974 lines)
This file implements concepts from trade_philosophy.pdf:
Enums Defined
<source lang="python"> class ProbabilityZone(Enum):
TOP_THIRD = "top_third" # 80% continuation probability TOP_HALF = "top_half" # 65% continuation probability BOTTOM_HALF = "bottom_half" # 35% continuation probability BOTTOM_THIRD = "bottom_third" # 15% continuation (85% reversal)
class MarketStructure(Enum):
UPTREND = "uptrend" # Price > 21 MA > 200 MA DOWNTREND = "downtrend" # Price < 21 MA < 200 MA SIDEWAYS = "sideways" # No clear structure THREE_FINGER_SPREAD_BULL = "..." # Large spread (bearish signal) THREE_FINGER_SPREAD_BEAR = "..." # Large spread (bullish signal)
class PullbackType(Enum):
HEALTHY_PULLBACK = "healthy_pullback" # 45-degree drift, holds above halfway COLLAPSE = "collapse" # Vertical drop, breaks deeply NO_PULLBACK = "no_pullback" # Price at or near highs BOUNCE = "bounce" # Bounce after crash (short opportunity)
class ColorChangeType(Enum):
BULLISH_REVERSAL = "bullish_reversal" # Green bar takes out red bar high BEARISH_REVERSAL = "bearish_reversal" # Red bar takes out green bar low NO_PATTERN = "no_pattern"
</source>
Key Classes
ProbabilityZoneResult- Analysis result with zone, probabilities, MA structureColorChangeResult- Pattern detection resultProbabilityZoneConfig- Configuration with thresholdsProbabilityZoneAnalyzer- Main analyzer classColorChangeDetector- Detects liquidity sweep patterns
Configuration Defaults
<source lang="python"> @dataclass class ProbabilityZoneConfig:
top_third_probability: float = 0.80 # 80% continuation top_half_probability: float = 0.65 # 65% continuation bottom_half_probability: float = 0.35 # 35% continuation bottom_third_probability: float = 0.15 # 15% continuation
swing_lookback_bars: int = 20 # Bars for swing H/L crash_bar_multiplier: float = 2.0 # Bar > 2x avg = crash three_finger_min_spread_pct: float = 0.02 # 2% spread threshold
</source>
7.4 timeframe_conversion.py Changes
File: src/core/timeframe_conversion.py
Changes
- Emoji removal - All emoji characters replaced with
[OK],[FAIL],[CONV], etc. - Date column fix - Handle 'date' as alias for 'timestamp'
- 1m skip - Don't overwrite source file when saving
- Mixed timestamp format - Use
format='mixed'for pandas parsing
<source lang="python">
- Date column alias:
if 'date' in source_df.columns and 'timestamp' not in source_df.columns:
source_df = source_df.rename(columns={'date': 'timestamp'})
logger.info("[FIX] Renamed 'date' column to 'timestamp'")
- Skip 1m overwrite:
if timeframe == '1m':
logger.info(f"[SKIP] 1m: Source file preserved (not overwritten)") continue
- Mixed timestamp parsing:
pd.to_datetime(df['timestamp'], format='mixed') </source>
7.5 main.py Changes
File: src/main.py
Changes
- Hour filter configuration - TICKET-12 support
- Emoji removal - Console output cleanup
<source lang="python">
- TICKET-12: Hour Filter configuration:
enable_hour_filter=strategy_config.get('enable_hour_filter', False), blocked_hours=strategy_config.get('blocked_hours', [])
print(f" TICKET-12 Hour Filter: Enabled={signal_generator_config.enable_hour_filter}, "
f"Blocked Hours={signal_generator_config.blocked_hours}")
</source>
7.6 Complete File Tree
services/backtest/src/ ├── components/ │ ├── market_state_analysis.py # TICKET-25: 5MIN → 1HOUR │ ├── multi_timeframe_analysis.py # Whitespace cleanup │ ├── probability_zone_analysis.py # NEW: trade_philosophy.pdf (974 lines) │ ├── setup_quality_detection.py # TICKET-5: position sizing │ ├── signal_generation_trade_management.py # TICKET-7,8,10,11,12,13,18,19,20,21,24 + GRADING FIX │ └── trade_execution_engine.py # TICKET-9,13,15,17,20: trailing/breakeven/ATR ├── core/ │ ├── data_structures.py # TICKET-15: current_stop_price field │ └── timeframe_conversion.py # Date fix, 1m skip, emoji removal └── main.py # TICKET-12 config, emoji removal services/backtest/scripts/ # 50+ NEW analysis scripts ├── analyze_alignment_pnl.py ├── analyze_alignment_pnl_v2.py ├── analyze_aplus.py ├── analyze_by_hour.py ├── analyze_combined_filters.py ├── analyze_crash_short_potential.py ├── analyze_hour_filter.py ├── analyze_hourly_pnl.py ├── analyze_long_short_detail.py ├── analyze_longs.py ├── analyze_ma_alignment.py ├── analyze_ma_slope_filter.py ├── analyze_morning_times.py ├── analyze_pdf_results.py ├── analyze_quick_exits.py ├── analyze_red_bar_rally.py ├── analyze_shorts.py ├── analyze_stop_exits.py ├── analyze_stop_loss_detail.py ├── analyze_test_summary.py ├── analyze_trade_bias.py ├── analyze_trading_hours_filter.py ├── analyze_trend_direction_at_entry.py ├── aplus_vs_a_analysis.py ├── check_all_data.py ├── check_costs.py ├── check_data_range.py ├── check_price_trend.py ├── comprehensive_analysis.py ├── comprehensive_backtest_analysis.py ├── comprehensive_summary.py ├── cost_impact_analysis.py ├── detailed_monthly.py ├── diagnose_color_change.py ├── diagnose_direction_bias.py ├── framework_comparison_analysis.py ├── generate_test_summary.py ├── grade_and_equity.py ├── monthly_breakdown.py ├── no_morning_analysis.py ├── path_to_profitability.py ├── philosophy_v1_blend_analysis.py ├── root_cause_summary.py ├── short_market_state_alignment.py ├── short_technique_analysis.py ├── simulate_target_rr.py ├── simulate_target_rr_v2.py ├── stop_out_deep_analysis.py ├── strategy_engine_gap_analysis.py ├── test_summary.py └── v1_optimization_impact_analysis.py
Part 8: Strategy Configuration
8.1 76D Parameter Vector
The strategy uses regime-aware configuration with 76 parameters:
- 44 Global Parameters - Constant across all market regimes
- 32 Regime Parameters - 8 parameters × 4 market regimes
File: src/config/strategy_config.py
Key Parameters
| Parameter | Default | Description |
|---|---|---|
primary_ma_period |
21 | Main trend MA |
secondary_ma_period |
200 | Long-term trend MA |
default_stop_distance |
40 pts | Minimum stop distance |
default_risk_reward |
2.0 | Target R:R ratio |
ma_buffer_points |
25 pts | MA zone width |
creeper_move_penalty |
-50 | Score reduction |
railroad_trend_bonus |
+15 | Score increase |
Part 9: Decision Trees
9.1 Should I Trade This Setup?
START: Signal Generated │ ▼ [Is hour blocked (9, 22, 23)?] │ Yes → SKIP (loss-making hours) │ No ↓ ▼ [Is MA21 direction aligned with trade?] │ LONG + MA rising? → Continue │ SHORT + MA falling? → Continue │ No alignment → SKIP │ ▼ [Is price in probability zone?] │ LONG + Bottom Third? → Continue (80% odds) │ SHORT + Top Third? → Continue (80% odds) │ Wrong zone → SKIP │ ▼ [Calculate Setup Quality Score] │ ├─ Apply 5-factor scoring (TF 30%, Trend 20%, Entry 15%, KeyLevel 20%, R:R 15%) ├─ Apply penalties (creeper -50, MA struggle -30, etc.) ├─ Apply institutional fight multiplier (0.7x) ├─ Apply MA direction adjustment (+15% aligned, -10% against) │ ▼ [Recalculate Grade from ADJUSTED score] │ ├─ A+ or A → Auto-trade eligible (2 lots for A+, 1 lot for A) ├─ B to D → Manual review (1 lot) ├─ F → Skip or minimal size │ ▼ [Execute Trade with Position Size]
9.2 Where to Place Stop?
START: Trade Direction Determined │ ▼ [Is Fab Four zone tight and close?] │ Yes → Place stop BEYOND Fab Four zone │ No ↓ ▼ [Can I afford stop beyond Fab Four?] │ Yes → Use EVENT stop (beyond Fab Four) │ No → Use MAXIMUM loss stop ($6000 max) │ ▼ [ENFORCE minimum 40pt stop (TICKET-13)] │ ▼ [Once trade moves in favor:] │ ├─ 25 pts profit → Move stop to breakeven (TICKET-17) ├─ 20+ pts profit → Start trailing (TICKET-15/20) ├─ Use ATR-based trail distance (2x ATR, min 15 pts) │ ▼ [Stop Placement Complete]
Part 10: Code Review Guidelines
10.1 What Reviewers Should Check
- Code correctness (syntax, bugs)
- Integration (doesn't break other components)
- Test coverage
- Precision (6-decimal places per spec)
- Performance (no excessive logging)
10.2 What Reviewers Should NOT Question
- The penalty values themselves (-50, -30, etc.)
- Why 21 MA and not 20 MA
- Why specific thresholds (these are trading decisions)
- Why certain hours are blocked
10.3 Trading Authority Principle
Rikk has final authority on:
- Entry/exit conditions
- Threshold values
- Setup definitions
- What constitutes a valid signal
- Penalty/bonus amounts
Engineers have authority on:
- Code quality and structure
- Performance optimization
- Error handling
- System integration
- Test coverage
Part 11: Quick Reference Cards
Card 1: Probability Zones
TOP THIRD = 80% continuation (SHORT zone) TOP HALF = 65% continuation BOTTOM HALF = 35% continuation BOTTOM THIRD = 15% continuation (LONG zone with 85% reversal odds)
Card 2: Setup Quality Formula
Score = (TF_align × 0.30) + (Trend × 0.20) + (Entry × 0.15) + (KeyLevel × 0.20) + (R:R × 0.15) Then apply: - Creeper detected? → Score - 50 - MA struggle? → Score - 30 - No two-day trend? → Score - 30 - Wrong phase? → Score - 25 - Institutional fight? → Score × 0.7 - MA direction aligned? → Score + 15% - MA direction against? → Score - 10% Finally: Recalculate grade from ADJUSTED score (not original!)
Card 3: Profit Taking
1. Enter with 2 lots 2. Set stop above high (short) / below low (long) 3. Wait for follow-through bar (2nd bar after opposite color) 4. Take profit on 1 lot 5. Move stop to break-even 6. Wait for next follow-through bar 7. Take profit on remaining lot 8. Done
Card 4: All Code Changes Summary
| Change | One-Line Summary |
|---|---|
| TICKET-5 | Position sizing: 200 lots → 2/1 lots (realistic for Rs 1L capital) |
| TICKET-7 | R:R preservation: shift stop/target with slippage |
| TICKET-8 | CTT rate: 0.05% → 0.005% per leg (5x fix) |
| TICKET-9 | Market impact: cap slippage at 2 pts max |
| TICKET-10 | Entry technique: align with strategy_engine.py priority order |
| TICKET-11 | BOS stops: 5 → 10 bar lookback + 5pt buffer |
| TICKET-12 | Hour filter: block hours 9, 22, 23 (loss-making) |
| TICKET-13 | Min stop: 20 → 40 pts (noise protection) |
| TICKET-15 | Trailing stop: add current_stop_price tracking |
| TICKET-17 | Breakeven: move stop to entry at 25pt profit |
| TICKET-18 | Creeper handling: let through (don't block), use penalty |
| TICKET-19 | MA direction: use slope as primary direction source + score adjustment |
| TICKET-20 | Probability zones + ATR trailing (trade_philosophy.pdf) |
| TICKET-21 | Color change: disabled (too restrictive) |
| TICKET-24 | Creeper entry: use NEAR_MA technique (not green/red bar) |
| TICKET-25 | Market state: 5MIN → 1HOUR for creeper detection |
| GRADING FIX | Recalculate grade from adjusted score (not capped original) |
| PDF-FIX: MA Buffer | 5 → 25 pts (MA is zone, not line) |
| PDF-FIX: Take Profit | Rewrite to 50% of distance-to-MA (mean reversion) |
| PDF-FIX: Crash SHORT | Direction override: sideways + top zone + big red bar → SHORT |
| PDF-FIX: Stop Loss | NEAR_MA stop uses structure low (not MA value) |
| NEW: __post_init__ | Initialize blocked_hours list after dataclass creation |
| NEW: _calculate_grade_from_score | New method to recalculate grade from adjusted score |
| NEW: probability_zone_analysis.py | 974-line new file implementing trade_philosophy.pdf |
| NEW: 50+ analysis scripts | Scripts for hypothesis building and analysis |
Last Updated: 2026-01-05 | Branch: rikk_mtf_backtest001 | Validated: 19-month backtest