Complete breakdown of both engines ยท 5-year backtest ยท 3,774 total trades ยท All interactive charts
Complete breakdown of both engines: how scores are calculated, what drives them, and what the backtests reveal
The ADA Oracle runs two independent scoring engines from a single shared module (shared/engine.mjs) with configuration profiles in shared/engine-config.mjs. Every function in the engine accepts an optional config object โ when omitted, it falls back to Swing defaults, ensuring backward compatibility.
Swing Engine โ the original. Primary timeframe is Daily candles, structure from 4H, anchor from Weekly. Targets 7โ14 day holds.
Daily Engine โ the faster sibling. Primary timeframe is 4H candles, structure from 1H, entry precision from 15m. Targets 24โ48 hour holds.
Both engines consume the same raw candle data (ADA/USD and BTC/USD across Weekly, Daily, 4H, 1H, and 15m timeframes), but weight it very differently.
| Timeframe | Rows Available | Swing Engine Role | Daily Engine Role |
|---|---|---|---|
| Weekly (1W) | 441 candles (~8.5 yrs) | Anchor โ highest-weight Hurst + candle patterns (30 pts) | Background โ minimal Hurst weight (7%) |
| Daily (1D) | 3,085 candles (~8.5 yrs) | Primary โ main indicators, scoring, SL/TP targets | Anchor โ secondary Hurst input (23%) |
| 4-Hour (4H) | 18,504 candles | Structure โ fractal levels, Hurst input (26%) | Primary โ main indicators, scoring, SL/TP targets |
| 1-Hour (1H) | 74,015 candles | Momentum โ 6-bar momentum check, MACD cross | Structure โ dominant Hurst input (35%) |
| 15-Minute | 58,368 candles | Not used | Entry precision โ fractal levels, EMA20, BB, VWAP |
| BTC (all TFs) | Same as ADA | BTC Sentinel correlation (separate system) | BTC Sentinel correlation (separate system) |
When the Swing engine runs calcTFIndicators on Daily data to compute SMA20, SMA50, RSI, MACD, BB, and fractals, those become its primary scoring inputs. The Daily engine does exactly the same thing but on 4H data โ making the Daily engine inherently faster to react but more prone to noise.
Both engines produce a score from 0โ100 by evaluating LONG and SHORT independently, then picking the stronger direction. The score is the sum of 8 weighted components, each capped at a component maximum. The system runs bsCfg(false) for LONG and bsCfg(true) for SHORT, takes the higher total, and that determines direction.
| Component | Swing Max | Daily Max | What It Measures |
|---|---|---|---|
| RSI | 20 | 22 | Mean-reversion signal (oversold/overbought zones) |
| MACD | 15 | 12 | Momentum confirmation (histogram cross + direction) |
| Trend | 15 | 12 | Price position relative to SMA-fast and SMA-slow |
| Volume | 10 | 12 | Current volume vs 20/30-bar average |
| Signal | 25 | 22 | Which of the 4 signal types is firing (the biggest single component) |
| Bollinger | 10 | 12 | Price position within Bollinger Bands |
| Timing | 5 | 8 | Day-of-week (Swing) or Hour-of-day (Daily) |
| Candle | 8 | 8 | Pattern recognition across timeframes |
| TOTAL | 108 | 108 | Theoretical max (capped at 100) |
The theoretical max exceeds 100, but Math.min(s, 100) clamps the output. In practice, scores rarely approach 100 because not all components fire simultaneously.
Before any component score is added, it's multiplied by one of three regime-adaptive weights derived from the Hurst Exponent:
hBias = (H - 0.5) ร 2 // ranges from -1.0 (reverting) to +1.0 (trending)
momMult = clamp(1 + hBias ร 1.2, 0.3, 1.8) // boosts momentum signals in trends
revMult = clamp(1 - hBias ร 1.2, 0.3, 1.8) // boosts reversal signals in reversion
confMult = clamp(0.5 + |H - 0.5| ร 3, 0.4, 1.3) // boosts confirmation in strong regimes
What this means in practice:
In a trending regime (H = 0.65): momMult = 1.36, revMult = 0.64, confMult = 0.95. Momentum signals (MACD cross, squeeze release) get a 36% boost. RSI oversold/overbought signals get a 36% penalty. The engine is saying "don't fight the trend."
In a reverting regime (H = 0.35): momMult = 0.64, revMult = 1.36, confMult = 0.95. The exact opposite โ RSI bounces get the boost, momentum signals get the penalty.
In random walk (H = 0.50): all multipliers near 1.0. No regime edge, everything at face value.
Calculated on the engine's primary timeframe (Daily for Swing, 4H for Daily). Uses a 14-period RSI.
Swing Engine scoring rules (for LONG): - RSI < 25 โ 20 pts ร revMult ("deeply oversold") - RSI < 30 โ 16 pts ร revMult ("oversold") - RSI < 35 โ 10 pts ร revMult ("near oversold") - RSI 55โ70 AND price > SMA50 โ 14 pts ร momMult ("bullish momentum") - Everything else โ 0 pts ("neutral")
Daily Engine differences: - Wider oversold zone: RSI < 28 (not 25) for max points (22 pts) - Wider overbought zone: RSI > 72 (not 75) for max points - The bullish momentum zone is 55โ68 (not 55โ70) โ slightly tighter
Why the zones differ: The Daily engine operates on 4H data which is noisier than Daily candles. The wider oversold/overbought thresholds compensate โ a 4H RSI of 28 represents a similar extremity to a Daily RSI of 25.
Standard MACD (12/26/9) calculated on the primary timeframe.
Two scoring conditions:
1. Cross signal (max pts): MACD crosses above zero while price is above SMA-slow (LONG) or below (SHORT). This is the sig.mb / sig.msBear detection.
2. Improving histogram (partial pts): MACD histogram increasing but hasn't crossed yet.
Swing: 15/8 pts. Daily: 12/6 pts. Both multiplied by momMult.
Position relative to two moving averages: - SMA-fast: SMA20 for Swing, SMA30 for Daily - SMA-slow: SMA50 for Swing, SMA72 for Daily
Three scenarios: 1. Price AND SMA-fast both above SMA-slow โ full trend points (ร momMult) 2. Price above SMA-fast only โ partial points (ร momMult) 3. Price below lower BB โ BB reversal points (ร revMult)
The Daily engine uses SMA30/SMA72 instead of SMA20/SMA50 because on 4H candles, SMA30 โ 5 days and SMA72 โ 12 days โ similar calendar-time windows to the Swing's SMA20/SMA50 on daily candles.
Current bar volume compared to the rolling average (20-bar for Swing, 30-bar for Daily): - Volume > 2ร avg โ max pts (ร confMult) - Volume > 1.5ร avg โ above-normal pts - Volume > 1ร avg โ normal pts - Below average โ 0 pts
Multiplied by confMult because volume confirms conviction in either direction.
Daily engine max is 12 pts (vs Swing's 10) โ volume matters more on shorter timeframes because thin-volume 4H bars frequently produce false signals.
This is the most impactful component. It checks which of the engine's 4 signal types is currently active:
| Priority | Signal | Swing Pts | Daily Pts | Multiplier | Description |
|---|---|---|---|---|---|
| 1 | Momentum Breakout / Bearish Momentum | 25 | 22 | momMult | MACD cross above zero + price above SMA50 |
| 2 | Squeeze Release (Long/Short) | 22 | 20 | momMult | BB compressed inside Keltner Channel, then released with directional MACD |
| 3 | Oversold Bounce / Overbought Rejection | 18 | 16 | revMult | RSI below ob_rsi threshold (30 for Swing, 32 for Daily) |
| 4 | Squeeze Building | 8 | 7 | confMult | BB still inside KC, no release yet |
Critical design choice: These are checked in priority order with if/else if โ only ONE signal can score at a time. A momentum breakout during a squeeze release only scores momentum (the higher value). This prevents double-counting but means the system can miss compounding signals.
detect function)The detect function runs on the primary TF indicators and produces boolean flags:
ob: RSI < ob_rsi (30 Swing, 32 Daily)
mb: MACD crosses above zero AND price > SMA50 (Momentum Breakout)
sbLong: Squeeze just released AND MACD > 0
overbought: RSI > (100 - ob_rsi) โ 70 Swing, 68 Daily
msBear: MACD crosses below zero AND price < SMA50
sbShort: Squeeze just released AND MACD < 0
sq: BB inside Keltner Channel (volatility compressed)
The squeeze detection (sq) works by comparing Bollinger Bands to Keltner Channels:
Keltner Upper = EMA20 + (kc ร ATR) where kc = 1.5 for both engines
Keltner Lower = EMA20 - (kc ร ATR)
Squeeze = BB_lower > KC_lower AND BB_upper < KC_upper
Release = was in squeeze last bar AND not in squeeze now
Price position within the Bollinger Bands as a percentage:
bp = (close - BB_lower) / (BB_upper - BB_lower)
For LONG: bp < 0.1 โ max pts (at lower band), bp < 0.3 โ partial pts (near lower band) For SHORT: bp > 0.9 โ max pts, bp > 0.7 โ partial pts
Multiplied by revMult โ this is fundamentally a mean-reversion signal.
Swing Engine: Day of week scoring: - Friday/Saturday: 5 pts (weekend drift patterns) - MonโWed: 3 pts (weekday liquidity) - Others: 0 pts
Daily Engine: Hour of day (UTC) scoring: - 08:00โ14:00: 8 pts (London + early NY โ peak liquidity) - 14:00โ18:00: 6 pts (NY afternoon) - 18:00โ22:00: 4 pts (NY evening) - 22:00โ03:00: 2 pts (Asia quiet) - 03:00โ06:00: 0 pts (dead zone) - 06:00โ08:00: 3 pts (early Europe)
This component matters more for the Daily engine (max 8 vs max 5) because on 24โ48h holds, when you enter relative to liquidity windows directly impacts fill quality and slippage.
The detectCandles function runs on each timeframe and identifies: hammer, inverted hammer, shooting star, hanging man, bullish/bearish engulfing, doji variants, morning star, and evening star.
Each confirming pattern adds points weighted by timeframe:
Swing: W=6, D=4, 4H=3, 1H=2. A Weekly hammer is worth 3ร an hourly one. Daily: 4H=5, 1H=4, 15m=3, D=3, W=1. The weighting flips โ 4H candle patterns dominate because that's the primary timeframe.
Raw points are summed, multiplied by revMult, and capped at 8.
The raw score maps to an action tier:
| Score Range | Swing Tier | Daily Tier | Action | Leverage |
|---|---|---|---|---|
| 0โ19 | WAIT | WAIT | No trade | 0x |
| 20โ34 | PRIME | PRIME | Small position, wide stops | Swing: 1x, Daily: 2x |
| 35โ49 | STRONG | STRONG | Standard position | Swing: 2x, Daily: 3x |
| 50โ64 | CAUTION | CAUTION | Full conviction | Swing: 3x, Daily: 5x |
| 65โ79 | OVERHEATING | OVERHEATING | High conviction | Swing: 5x, Daily: 10x |
| 80โ100 | OVERHEATING | OVERHEATING | Maximum conviction | Swing: 7x, Daily: 15x |
Position sizing follows this sequence: capital โ percentage allocation โ margin โ leverage โ exposure.
Step 1: Get effective capital (Strike balance โ CIP-30 wallet โ default 1000 ADA)
Step 2: Convert to USD: acctUsd = adaCapital ร currentPrice
Step 3: Risk percentage: pp = min(score/100 ร posCap, posCap)
Step 4: Margin (collateral): psUsd = acctUsd ร pp
Step 5: Leveraged position: lpUsd = psUsd ร leverageMultiplier
Step 6: Convert back to ADA for display
The posCap parameter is the maximum percentage of capital risked per trade:
- Swing: 0.25 (25% max)
- Daily: 0.20 (20% max)
How pp (position percentage) scales with score:
The formula pp = min(score/100 ร posCap, posCap) means position size scales linearly with score:
| Score | Swing pp | Daily pp | What This Means |
|---|---|---|---|
| 20 | 5.0% | 4.0% | PRIME โ small position |
| 35 | 8.75% | 7.0% | STRONG โ moderate |
| 50 | 12.5% | 10.0% | CAUTION โ half of max |
| 65 | 16.25% | 13.0% | OVERHEATING โ approaching max |
| 80 | 20.0% | 16.0% | OVERHEATING โ near max |
| 100 | 25.0% | 20.0% | Theoretical max |
Example with โณ1,000 at ADA = $0.70, Swing score 50:
acctUsd = 1000 ร 0.70 = $700
pp = min(50/100 ร 0.25, 0.25) = 0.125
psUsd = $700 ร 0.125 = $87.50 โ margin of ~โณ125
leverage = 3x (score 50โ64)
lpUsd = $87.50 ร 3 = $262.50 โ position of ~โณ375
After the trade suggestion calculates the risk:reward ratio, if R:R < 1.0, position size gets proportionally reduced:
if (rrr > 0 && rrr < 1) {
dampenFactor = max(0.2, rrr);
adjustedPositionSize = max(1, round(positionSize ร dampenFactor));
}
A trade with 0.6 R:R gets its position cut to 60% of normal. A trade with 0.15 R:R gets floored at 20%. This prevents the engine from taking full-size positions on bad risk:reward setups.
Stop loss uses a 3-way minimum (for LONGs, maximum for SHORTs) to pick the tightest reasonable stop:
scoreAtrBase (Swing): score โฅ 65 โ 1.2, โฅ 50 โ 1.5, โฅ 35 โ 1.8, else โ 2.2
scoreAtrBase (Daily): score โฅ 65 โ 1.0, โฅ 50 โ 1.2, โฅ 35 โ 1.5, else โ 1.8
stopHurstMod = clamp(1 - hBias ร 0.4, 0.7, 1.4)
atrMult = scoreAtrBase ร stopHurstMod
SL(LONG) = entry - atrMult ร ATR
SL(SHORT) = entry + atrMult ร ATR
Why higher scores get tighter stops: A score of 65+ means the engine has high conviction โ multiple signals confirm the direction. In that context, a move of even 1.2ร ATR against you means the thesis is broken. At score 20, the engine is less certain, so it gives the trade 2.2ร ATR of room to breathe.
How Hurst adjusts the stop: - Trending (H = 0.65): stopHurstMod = 0.88 โ tighter stop. In trends, price moves directionally โ a reversal of even a small distance likely means the trend is over. - Reverting (H = 0.35): stopHurstMod = 1.12 โ wider stop. In mean-reversion, price oscillates โ you need room for the whipsaw before the reversion plays out.
Each signal type has a fixed percentage stop from the engine settings:
| Signal | Swing % | Daily % |
|---|---|---|
| Momentum Breakout / Bearish Momentum | 15% | 5% |
| Squeeze Release | 15% | 5% |
| Oversold Bounce / Overbought Rejection | 10% | 3.5% |
The Daily engine's stops are much tighter because the trades are shorter-duration. A 15% stop on a 14-day swing trade gives the position weeks to recover; a 15% stop on a 24-hour daily trade would be absurd.
entry ร 1.15 for SHORT, entry ร 0.85 for LONG)st.maxSL/100)After computing the initial SL, both engines check the nearest fractal level:
// Swing LONG: if nearest bullish fractal is between entry and SL, snap to just below it
if (bullF && bullF < entry && bullF > sl) sl = bullF ร 0.99;
// Daily LONG: same but tighter buffer
if (bullF && bullF < entry && bullF > sl) sl = bullF ร 0.995;
This is structurally intelligent โ fractals represent actual support/resistance levels where price previously reversed. Placing the stop just beyond a fractal means you're saying "if this structural level breaks, the trade is dead." The Swing engine gives 1% buffer below the fractal; the Daily engine gives 0.5%.
The system takes whichever method produces the tightest stop:
SL(LONG) = max(entry - atrMultรATR, entryร(1-slPct), entryร(1-maxSL))
SL(SHORT) = min(entry + atrMultรATR, entryร(1+slPct), entryร(1+maxSL))
Then the fractal adjustment can nudge it slightly wider if a structural level sits between entry and SL.
Take profit logic branches based on score:
The engine searches for structural targets:
Far targets (for trending regimes): SMA50, Fib 0.618, Fib 0.5, opposite fractal Near targets (for reverting regimes): SMA20, BB midline, opposite fractal
The selection depends on regime: - H > 0.55 (trending): pick the furthest of the far targets โ let winners run - H < 0.45 (reverting): pick the nearest of the near targets โ take profit quickly - Random: pick the second-nearest from all combined targets
The raw distance is then modulated by tpHurstMod:
tpHurstMod (Swing) = clamp(1 + hBias ร 0.5, 0.6, 1.6)
tpHurstMod (Daily) = clamp(1 + hBias ร 0.4, 0.6, 1.4)
tp = entry + rawDistance ร tpHurstMod (for LONG)
In a strong trend (H=0.65): tpHurstMod = 1.15 โ target extended 15% further. In strong reversion (H=0.35): tpHurstMod = 0.85 โ target pulled 15% closer.
The TP reward multiplier also scales with score:
| Score | Swing Mult | Daily Mult |
|---|---|---|
| โฅ 65 | 2.5 | 2.0 |
| โฅ 50 | 1.8 | 1.5 |
| โฅ 35 | 1.2 | 1.0 |
| < 35 | 0.8 | 0.8 |
No structural target search. TP is calculated purely as a multiple of the risk distance:
riskDist = entry - sl (for LONG)
tp = entry + riskDist ร tpRewardMult ร tpHurstMod
At score 35 with neutral Hurst, a LONG with 3% SL gets a 3.6% TP (1.2 ร 1.0 ร 3%).
Leverage in both engines is score-gated, meaning higher conviction (more signals confirming) unlocks higher leverage. The logic is:
Low conviction (20โ34) โ minimal leverage. The engine sees some edge but isn't certain. Swing allows 1x (spot only, no leverage), Daily allows 2x. You're trading with your own capital plus minimal borrowed funds.
High conviction (65+) โ significant leverage. Multiple signals confirm the direction, stops are tight, the regime supports the trade type. Swing caps at 7x, Daily allows up to 15x.
Why Daily leverage is higher: Daily engine trades have tighter percentage stops (3.5โ5% vs 10โ15%). When your stop is 3.5% away, 10x leverage means a ~35% account hit on a loss. When your stop is 15% away (Swing), 10x leverage means a 150% account hit โ liquidation territory. The leverage tiers are calibrated so that leverage ร typical_stop_distance stays within survivable ranges.
The position size (margin/collateral) and leverage work together:
Total Exposure = Margin ร Leverage
Max Loss = Total Exposure ร Stop Distance
Because position percentage already scales with score (higher score โ larger margin), AND leverage scales with score (higher score โ higher leverage), exposure scales quadratically with conviction:
| Score | Swing Margin % | Swing Lev | Combined Exposure Factor |
|---|---|---|---|
| 25 | 6.25% | 1x | 6.25% of capital |
| 40 | 10% | 2x | 20% of capital |
| 55 | 13.75% | 3x | 41.25% of capital |
| 70 | 17.5% | 5x | 87.5% of capital |
At score 70, the Swing engine is exposing 87.5% of your capital to this single trade. This is deliberate โ the engine only reaches 70+ when 6+ scoring components align โ but it means a OVERHEATING tier trade that goes wrong is catastrophic.
The max loss is capped by a separate stop-distance measure:
sp = min(1.5 ร ATR/price ร 100, 15) // Swing
sp = min(1.2 ร ATR/price ร 100, 8) // Daily
mlUsd = leveragedPositionUSD ร sp/100
This isn't the same as the trade suggestion's SL โ it's a worst-case envelope using ATR as the risk ruler.
The Hurst Exponent (H) measures whether a time series is trending (H > 0.5), mean-reverting (H < 0.5), or random (H โ 0.5). The engine uses Rescaled Range (R/S) analysis across multiple log-spaced window sizes, then regresses log(R/S) against log(window size) to extract H.
The system doesn't compute a single Hurst value. It calculates H across multiple timeframes and windows, then takes a weighted average:
Swing Engine Hurst Windows (D+W dominant, 52%):
| Timeframe | Window | Weight | What It Captures |
|---|---|---|---|
| Weekly | 100 bars | 10% | ~2 years of weekly regime |
| Weekly | 50 bars | 8% | ~1 year of weekly regime |
| Daily | 100 bars | 22% | ~5 months of daily regime (largest weight) |
| Daily | 50 bars | 12% | ~2.5 months of daily regime |
| 4H | 200 bars | 18% | ~33 days of 4H regime |
| 4H | 100 bars | 8% | ~17 days of 4H regime |
| 1H | 500 bars | 15% | ~21 days of hourly regime |
| 1H | 200 bars | 7% | ~8 days of hourly regime |
Daily Engine Hurst Windows (1H+4H dominant, 70%):
| Timeframe | Window | Weight | What It Captures |
|---|---|---|---|
| 1H | 300 bars | 25% | ~12.5 days (largest weight) |
| 1H | 150 bars | 10% | ~6 days |
| 4H | 200 bars | 22% | ~33 days |
| 4H | 100 bars | 13% | ~17 days |
| Daily | 100 bars | 15% | ~5 months |
| Daily | 50 bars | 8% | ~2.5 months |
| Weekly | 100 bars | 5% | ~2 years |
| Weekly | 50 bars | 2% | ~1 year |
The Daily engine adds smoothingAlpha: 0.4 to prevent rapid regime flip-flopping on noisy 1H data:
smoothedH = rawH ร 0.4 + previousH ร 0.6
This means the Daily engine's regime changes more slowly than its raw data suggests โ each new computation only moves the needle 40% toward the new value. Without this, 1H noise could cause the regime to flip between "trending" and "reverting" multiple times per day.
| H Value | Regime | Trading Implication |
|---|---|---|
| > 0.60 | trending | Strong directional bias. Momentum signals boosted. Tighter stops, further targets. |
| 0.55โ0.60 | mild_trend | Some directional persistence. Moderate momentum boost. |
| 0.45โ0.55 | random | No statistical edge from regime. All multipliers near 1.0. |
| 0.40โ0.45 | mild_revert | Some mean-reversion tendency. Moderate reversal boost. |
| < 0.40 | reverting | Strong mean-reversion. Reversal signals boosted. Wider stops, closer targets. |
When the main signal detection finds a squeeze (BB inside KC), the squeezeBias function runs a separate scoring pipeline to predict which direction the squeeze will release. This is independent of the main score โ it provides directional guidance before the squeeze fires.
The squeeze bias score ranges from -100 to +100, with its own component set:
Swing Engine Squeeze Bias weights: - Weekly candle patterns: ยฑ30 pts (very heavy โ a Weekly hammer during a squeeze is a strong directional signal) - Daily candle patterns: ยฑ15 pts - 4H candle patterns: ยฑ8 pts - RSI zones: up to ยฑ25 pts - MACD direction: up to ยฑ18 pts - SMA20 position: ยฑ10 pts - SMA50 position: ยฑ15 pts - 1H momentum (6-bar direction count): ยฑ12 pts - 1H MACD: ยฑ10 pts - Regime boost (H > 0.55 + directional MACD): ยฑ8 pts
Daily Engine Squeeze Bias weights: - 4H candle patterns: ยฑ25 pts (primary TF gets highest weight) - 1H candle patterns: ยฑ18 pts - 15m candle patterns: ยฑ10 pts - Daily candle patterns: ยฑ12 pts - Weekly candle patterns: ยฑ5 pts (minimal) - RSI: up to ยฑ22 pts - MACD: up to ยฑ20 pts - SMA-fast/slow: ยฑ12 pts each - 1H momentum: ยฑ15 pts - 1H MACD: ยฑ12 pts - Regime boost: ยฑ10 pts
The confidence is calculated as min(100, |score| ร 1.2). A score of ยฑ50 gives 60% confidence.
When the score is between 20โ64, the engine doesn't just enter at market price. It searches for confluence zones โ price levels where multiple technical factors cluster together.
The engine collects potential entry levels from: 1. Fractals across timeframes (weighted by TF importance, with recency decay) 2. Trend levels (SMA/EMA values on each TF) 3. Bollinger Band edges (lower for LONG, upper for SHORT) 4. Fibonacci retracements (0.236, 0.382, 0.5, 0.618, 0.786) 5. VWAP on each timeframe
Each level gets a weight (from config) and a recency factor: - Fractal formed โค5 bars ago: recency = 1.0 - 6โ10 bars ago: 0.8 - 11โ20 bars ago: 0.6 - 20+ bars ago: 0.4
Levels within a tolerance window (0.5% for Swing, 0.3% for Daily) get clustered:
clusterScore = factorCount ร 10 + density + (5 - min(distance, 5))
Where: - factorCount = number of distinct factor types in the cluster (fractal, trend, VWAP, etc.) - density = sum of weight ร recency for all levels in the cluster - distance = how far the cluster center is from current price (in %)
The best cluster becomes the entry price โ the idea is that price levels where fractals, moving averages, VWAP, and Fibonacci all converge represent strong support/resistance zones with high probability of holding.
| Score | Min Factors Required | Max Distance Allowed |
|---|---|---|
| โฅ 65 | 1 factor | Any distance (market entry) |
| โฅ 50 | 1 factor (Swing: 1.5%, Daily: 0.5%) | Tight limit |
| โฅ 35 | 2 factors (Swing: 2.5%, Daily: 1.0%) | Moderate |
| โฅ 20 | 3 factors (Swing: 3.5%, Daily: 1.5%) | Wide search |
At score 65+, the engine always enters at market โ it doesn't wait for confluence. At score 20, it demands 3 different factor types agree on a price level within 3.5% (Swing) or 1.5% (Daily) of current price.
Fractals are the foundation everything else is built on. They define where price has historically reversed, creating a map of support and resistance that the confluence entry, stop loss placement, and take profit targeting all reference.
A 5-candle pattern that identifies a local high or low โ a point where price changed direction. Developed by Bill Williams.
Bearish fractal (resistance): The middle candle's high is higher than the 2 candles before AND the 2 candles after it.
Bullish fractal (support): The middle candle's low is lower than the 2 candles before AND the 2 candles after it.
// Bearish: High[i] > High[i-1] AND High[i] > High[i-2] AND High[i] > High[i+1] AND High[i] > High[i+2]
// Bullish: Low[i] < Low[i-1] AND Low[i] < Low[i-2] AND Low[i] < Low[i+1] AND Low[i] < Low[i+2]
Key detail: a fractal requires 2 candles on EACH SIDE to confirm. You can never identify one in real-time โ there's always a 2-bar confirmation lag. On Daily, that's 2 days. On 4H, 8 hours. On 15m, 30 minutes.
Analysis of all 4H candle data (18,504 bars) shows fractal levels hold with meaningful statistical edge:
Bullish fractals (support)
2,503 total identified
Bearish fractals (resistance)
2,433 total identified
The engine assigns recency weights (1.0 โ 0.4) to fractals based on age. The data validates this:
| Timeframe | Bars | Bearish | Bullish | Frequency |
|---|---|---|---|---|
| Weekly | 441 | 55 | 63 | 1 every 3.7 bars |
| Daily | 3,085 | 397 | 426 | 1 every 3.7 bars |
| 4-Hour | 18,504 | 2,433 | 2,503 | 1 every 3.7 bars |
| 1-Hour | 74,015 | 9,584 | 9,563 | 1 every 3.9 bars |
| 15-Min | 58,368 | 7,264 | 7,286 | 1 every 4.0 bars |
The ~3.7 bar frequency is a mathematical property of the 5-candle pattern. It means every timeframe constantly generates fresh levels for the engine to work with.
Not all fractals are equal. A Weekly fractal represents institutional-grade structure. A 15m fractal held for maybe an hour.
Swing engine weights
Daily engine weights
The hierarchy flips between engines. Swing trusts Weekly most (7โ14d holds). Daily trusts 4H most (24โ48h holds). Each engine's primary timeframe gets the highest fractal weight.
Fractals participate in 4 stages of every trade:
1. Confluence entry โ Fractals are one of 5 factor types in the clustering algorithm. For LONGs, bullish fractals (support) below price are collected. For SHORTs, bearish fractals above. Contribution = weight ร recency.
2. Stop loss snap โ After the ATR-based stop is calculated, the engine checks if a fractal sits between entry and SL. If so, the stop snaps to just beyond the fractal (1% buffer Swing, 0.5% Daily). Logic: if the most recent structural level breaks, the thesis is dead.
3. Take profit targeting โ For score โฅ50 trades, the opposite fractal becomes a TP candidate. LONG trades target bearish fractals above entry (where sellers previously rejected price). SHORT trades target bullish fractals below.
4. Real-time insights โ When price is within 2% of a fractal level, the engine generates a warning: "Near resistance at $X" or "Near support at $X." Available to the user and to Nova-Kali for coaching.
The mathematical term "fractal" means patterns that repeat at different scales. The ADA Oracle implements this literally โ the same 5-candle reversal logic runs on Weekly, Daily, 4H, 1H, and 15m charts. When multiple timeframes produce a fractal near the same price level, that's the self-similar structure converging. The confluence system detects this convergence and identifies it as a high-probability entry zone โ a place where the market's fractal geometry agrees across scales that this price matters.
| Metric | Swing (5yr, 321 trades) | Daily (5yr, 3,453 trades) |
|---|---|---|
| Win Rate | 57.9% | 63.8% |
| Profit Factor | 1.50 | 2.08 |
| Total PnL | 531.54 | 2,901.71 |
| Avg PnL/Trade | +1.66 | +0.84 |
| Avg Hold Duration | 4.7 days | 9.9 hours |
| TP Hit Rate | 38.3% (123/321) | 38.9% (1,344/3,453) |
| SL Hit Rate | 34.6% (111/321) | 22.1% (764/3,453) |
| Hold Expired Rate | 26.8% (86/321) | 38.9% (1,344/3,453) |
This is the most important finding. In both engines, lower scores produce better results:
Swing โ score vs performance
Daily โ score vs performance
Swing Engine by Score Band:
| Score Band | Tier | Trades | Win Rate | Avg PnL | Profit Factor |
|---|---|---|---|---|---|
| 20โ34 | PRIME | 133 | 67.7% | +2.15 | 1.77 |
| 35โ49 | STRONG | 107 | 58.9% | +1.98 | 1.63 |
| 50โ64 | CAUTION | 58 | 50.0% | +1.93 | 1.51 |
| 65โ79 | OVERHEATING | 19 | 15.8% | -4.10 | 0.35 |
| 80โ100 | OVERHEATING | 4 | 25.0% | -0.09 | 0.98 |
Daily Engine by Score Band:
| Score Band | Tier | Trades | Win Rate | Avg PnL | Profit Factor |
|---|---|---|---|---|---|
| 20โ34 | PRIME | 1,236 | 73.6% | +1.20 | 3.27 |
| 35โ49 | STRONG | 1,221 | 64.4% | +0.79 | 2.03 |
| 50โ64 | CAUTION | 604 | 56.8% | +0.68 | 1.69 |
| 65โ79 | OVERHEATING | 339 | 41.3% | +0.05 | 1.04 |
| 80โ100 | OVERHEATING | 53 | 43.4% | +0.57 | 1.37 |
Why this happens: When the score is high (65+), it means many components fired simultaneously. But the scoring system awards points for conditions that are often late โ by the time RSI is deeply oversold AND MACD has crossed AND price is below both SMAs AND volume is spiking AND a candlestick pattern forms... the move is often exhausted. The high score catches the signal at its peak conviction, which is frequently the peak of the move itself.
PRIME trades (20โ34) work because they catch the beginning of setups. Only 2โ3 factors align, but that early entry gives the trade room to develop. Wider stops at PRIME tier provide exactly the patience these trades need, and under the April 2026 edge-weighted inversion PRIME now receives the highest leverage (8x Daily / 3x Swing) โ the system sizing commitment now matches the edge.
Swing Engine:
| Regime | Trades | Win Rate | Avg PnL | Profit Factor |
|---|---|---|---|---|
| mild_trend | 139 | 63.3% | +2.71 | 2.00 |
| random | 48 | 58.3% | +0.59 | 1.19 |
| trending | 134 | 52.2% | +0.94 | 1.23 |
Daily Engine:
| Regime | Trades | Win Rate | Avg PnL | Profit Factor |
|---|---|---|---|---|
| trending | 1,105 | 64.0% | +0.97 | 2.17 |
| random | 624 | 66.0% | +0.83 | 2.47 |
| mild_trend | 1,724 | 62.8% | +0.76 | 1.92 |
Key insight for Swing: mild_trend (H 0.55โ0.60) massively outperforms strong trending (H > 0.60). The PF is 2.00 vs 1.23. This makes sense โ in mild trends, the engine gets the benefit of directional persistence without the problem of extreme moves that blow past stops. Strong trends produce larger moves but also larger reversals when they end.
Key insight for Daily: random regime (H 0.45โ0.55) has the highest profit factor (2.47). The Daily engine's bounce-dominant signal mix (96.7% of trades are bounce signals) actually works best when the market is oscillating without strong directional bias โ that's exactly what mean-reversion thrives on.
Swing Engine:
| Signal | Trades | Win Rate | PF | Note |
|---|---|---|---|---|
| lean | 249 | 62.7% | 1.71 | Default when no specific signal fires |
| bounce_long | 17 | 64.7% | 2.59 | Small sample, strong performance |
| bounce_short | 30 | 36.7% | 0.85 | Negative expectancy |
| sq_long | 3 | 66.7% | 2.64 | Too few trades to judge |
| sq_short | 7 | 42.9% | 0.91 | Slightly negative |
| mom_short | 14 | 14.3% | 0.29 | Very bad โ only 2 of 14 won |
| mom_long | 1 | 100% | โ | Single trade, not meaningful |
Daily Engine:
| Signal | Trades | Win Rate | PF |
|---|---|---|---|
| bounce | 3,339 | 64.6% | 2.14 |
| momentum | 114 | 40.4% | 1.16 |
Critical finding: Momentum signals dramatically underperform bounce signals in both engines. In the Daily engine, momentum trades win only 40.4% of the time vs 64.6% for bounce trades. In the Swing engine, mom_short has a 14.3% win rate โ it won 2 of 14 trades. The momentum signal (MACD cross + price beyond SMA-slow) is consistently catching moves too late.
The Daily engine has an isGo boolean flag. The backtest reveals:
| Flag | Trades | Win Rate | Total PnL |
|---|---|---|---|
| isGo = FALSE | 1,915 | 69.2% | +2,006.47 |
| isGo = TRUE | 1,538 | 57.0% | +895.24 |
Trades where isGo is FALSE perform dramatically better. The flag is inverted โ it's meant to signal "go" conditions but actually signals worse setups.
| Direction | Swing WR | Swing PF | Daily WR | Daily PF |
|---|---|---|---|---|
| LONG | 55.9% | 1.45 | 61.7% | 1.91 |
| SHORT | 59.3% | 1.53 | 66.5% | 2.35 |
SHORT trades outperform LONG in both engines. ADA/USD's tendency to drift lower or revert from pumps may explain this โ the asset has more consistent downside structure than upside follow-through.
Swing: TP hits delivered +1,353.89 PnL, SL hits cost -1,011.65 PnL. Hold-expired trades averaged +2.20 PnL (positive โ meaning price typically moves in the right direction even when it doesn't reach TP).
Daily: TP hits delivered +4,511.95, SL hits cost -2,246.90. Hold-expired trades are the interesting case โ 1,344 trades (38.9%) expired their hold period with an average positive outcome, contributing +636.39 total PnL. The engine is right about direction most of the time, but the TP targets are often set too aggressively for the hold period to reach them.
| RRR Range | Swing WR | Swing PnL | Daily WR | Daily PnL |
|---|---|---|---|---|
| 0.5โ1.0 | 67.7% | +286.09 | 73.6% | +1,475.80 |
| 1.0โ1.5 | 58.9% | +211.54 | 64.4% | +961.54 |
| 1.5โ2.0 | 50.0% | +82.92 | 56.7% | +417.30 |
| 2.0โ3.0 | 32.6% | -49.01 | 41.6% | +47.07 |
The inverse pattern holds for RRR too โ lower R:R trades (which map directly to lower-score PRIME/STRONG tiers) have the best win rates and profit factors. The 2.0โ3.0 RRR bucket in Swing is negative expectancy, confirming that the ambitious targets set for OVERHEATING-tier trades rarely get hit.
The most profitable combinations:
Swing โ regime ร tier
Daily โ regime ร tier
Swing โ Best setups: 1. mild_trend ร PRIME: 64 trades, 71.9% WR, +161.70 PnL 2. mild_trend ร STRONG: 39 trades, 61.5% WR, +93.28 PnL 3. mild_trend ร CAUTION: 30 trades, 50.0% WR, +96.17 PnL
Swing โ Worst setups: 1. trending ร OVERHEATING: 8 trades, 0.0% WR, -69.56 PnL (every single trade lost) 2. random ร OVERHEATING: 9 trades, 11.1% WR, -34.15 PnL
Daily โ Best setups: 1. mild_trend ร PRIME: 604 trades, 73.5% WR, +706.27 PnL 2. trending ร PRIME: 392 trades, 74.0% WR, +504.12 PnL 3. random ร PRIME: 240 trades, 73.3% WR, +268.92 PnL
Daily โ Worst setups: 1. random ร OVERHEATING: 85 trades, 37.6% WR, -9.36 PnL (the only negative regimeรtier combo) 2. mild_trend ร OVERHEATING: 218 trades, 42.7% WR, +11.83 PnL (barely positive)
The pattern is clear: PRIME tier dominates across all regimes. OVERHEATING tier is the primary drag on both engines, with OVERHEATING in non-trending regimes being actively destructive.
PRIME is the edge. Both engines generate the majority of profits from PRIME-tier (score 20โ34) trades. Daily PRIME: 3.27 PF, 73.6% WR. Swing PRIME: 1.77 PF, 67.7% WR.
OVERHEATING tier is the drag. Swing OVERHEATING: 0.35 PF, 15.8% WR. Daily OVERHEATING: 1.04 PF, 41.3% WR. This is driven by tight stops (2.54% SL distance) that get eaten by normal noise (SL hit rate of 49.7%), and ambitious targets (5.41% TP distance) that rarely get reached (TP hit rate of 17.1%).
The score paradox is structural, not a bug. Higher scores require more conditions to align simultaneously, which only happens at extremes. By the time everything confirms, the move is often over.
Bounce signals vastly outperform momentum signals. Daily bounce: 64.6% WR, 2.14 PF. Daily momentum: 40.4% WR, 1.16 PF. The MACD-cross-plus-trend-confirmation pattern catches moves too late.
Mild trend is the sweet spot for Swing (2.00 PF vs trending's 1.23). Random walk is the sweet spot for Daily (2.47 PF) because the engine's bounce-dominant strategy is inherently mean-reverting.
The isGo flag is inverted. FALSE trades outperform TRUE trades by 12% win rate and 2.2ร the total PnL.
SHORT outperforms LONG in both engines, suggesting ADA/USD has stronger downside structure for this system.
Position sizing scales quadratically โ both margin percentage AND leverage increase with score. While leverage doesn't change the probability of winning (the backtest PnL is purely the percentage price move, independent of leverage), it amplifies the dollar impact. In live trading, a OVERHEATING-tier loss at 15x leverage costs 38% of margin vs 5% at 2x for the same stop distance.
The real edge is SL/TP geometry, not leverage. Controlled testing (same signal, same regime, different tier) confirms the WR gap persists and is driven by: (a) stop width โ PRIME's 3.29% SL distance survives noise that triggers OVERHEATING's 2.54% SL (SL hit rate: 12% vs 49.7%); (b) target proximity โ PRIME's TP is 2.81% away and gets hit 54% of the time vs OVERHEATING's 5.41% TP that gets hit 17%; (c) limit entry advantage โ confluence-placed limits at structural levels vs market orders chasing price.
Hold-expired trades are net positive โ the engine correctly predicts direction ~64% of the time, but TP targets are often too ambitious for the hold period.
The Daily engine is the compounding vehicle. 2.08 PF and 2,901 total PnL vs Swing's 1.50 PF and 531 PnL, with 10ร the trade count. The Daily engine's faster cycle time and tighter risk management produce superior compounding.
PRIME's edge survives fill rate adjustment. PRIME signals only fill ~81% of the time (limit orders waiting for price), but even after penalizing for the 19% miss rate, PRIME produces +0.778 PnL per signal โ still 6ร better than OVERHEATING's +0.124. The higher win rate on filled trades is partly a selection effect (limit fills require price to come to you, which selects for better entries), but the edge is real, not an artifact.
The backtest results in sections 12โ14 assume every signal that fires also fills. In reality, the confluence entry system creates a fundamental split in how trades enter the market:
Backtest WR vs filled-only WR
PnL per signal (fill-adjusted)
PRIME and STRONG are letting the trade come to them. CAUTION and OVERHEATING are chasing the current move. This distinction matters enormously for interpreting the win rate numbers.
To measure the actual fill rates, a simulation was run against real 4H candle data (Daily engine) and Daily candle data (Swing engine). For each signal, the simulation checked whether price reached the limit entry level within the first ~24 hours (Daily engine) or ~3 days (Swing engine) of the hold window.
Daily Engine Fill Rates:
| Tier | Signals | Estimated Fills | Fill Rate | Unfilled | Filled WR | Backtest WR |
|---|---|---|---|---|---|---|
| PRIME | 1,236 | 1,001 | 81.0% | 235 (19%) | 68.0% | 73.6% |
| STRONG | 1,221 | 1,062 | 87.0% | 159 (13%) | 59.5% | 64.4% |
| CAUTION | 604 | 536 | 88.7% | 68 (11%) | 52.2% | 56.8% |
| OVERHEATING | 392 | 392 | 100% | 0 (0%) | 41.6% | 41.6% |
Swing Engine Fill Rates:
| Tier | Signals | Estimated Fills | Fill Rate | Unfilled | Filled WR | Backtest WR |
|---|---|---|---|---|---|---|
| PRIME | 133 | 105 | 78.9% | 28 (21%) | 61.9% | 67.7% |
| STRONG | 107 | 92 | 86.0% | 15 (14%) | 52.2% | 58.9% |
| CAUTION | 58 | 50 | 86.2% | 8 (14%) | 44.0% | 50.0% |
| OVERHEATING | 23 | 23 | 100% | 0 (0%) | 17.4% | 17.4% |
The backtest WR and the filled-only WR differ because of which trades fill. When a PRIME limit order is set 2.5% below market and price actually falls to hit it, that deep retrace means the move against the trade direction was substantial. Some of those deep moves continue and hit the stop loss. The unfilled trades โ the 19% that never triggered โ are disproportionately the ones where price moved in the intended direction without giving the limit entry. Those would have been winners, but the order never executed.
In other words: the unfilled trades have an implicit selection bias toward winning outcomes. By filtering them out, the remaining filled trades carry a slightly lower win rate.
The right metric is not WR alone โ it's PnL per signal fired, accounting for the probability that the signal actually fills:
Effective PnL per signal = Avg PnL per filled trade ร Fill rate
Daily Engine โ PnL Per Signal (fill-adjusted):
| Tier | Avg PnL/Fill | Fill Rate | Effective PnL/Signal | Total Est PnL |
|---|---|---|---|---|
| PRIME | +1.197 | 81.0% | +0.778 | +961 |
| STRONG | +0.787 | 87.0% | +0.630 | +768 |
| CAUTION | +0.683 | 88.7% | +0.663 | +400 |
| OVERHEATING | +0.124 | 100% | +0.124 | +49 |
Key finding: PRIME still wins. Even after accounting for the 19% of signals that never fill, PRIME produces +0.778 PnL per signal fired โ higher than STRONG (+0.630), CAUTION (+0.663), and dramatically higher than OVERHEATING (+0.124). The quality advantage of patient limit entries more than compensates for the fill gap.
The gap between backtest PnL and fill-adjusted PnL represents missed opportunity, not actual losses:
| Tier | Backtest PnL | Fill-Adjusted PnL | Missed Opportunity |
|---|---|---|---|
| PRIME | +1,479 | +961 | -518 (35% of backtest PnL) |
| STRONG | +961 | +768 | -193 (20%) |
| CAUTION | +413 | +400 | -13 (3%) |
| OVERHEATING | +49 | +49 | 0 (0%) |
PRIME leaves ~518 PnL on the table from unfilled orders over 5 years. But that's not a loss โ it's money that was never risked. The 1,001 trades that did fill generated +961 PnL with a 68.0% WR. Compare that to OVERHEATING's 392 trades generating only +49 PnL with 41.6% WR and full capital exposure on every single one.
Think of it this way:
PRIME/STRONG = fishing with a baited hook. You only catch fish that come to your spot. ~80% of casts result in a bite, and when the fish bites, you land it ~65% of the time. You miss some fish that swim past without biting, but every fight is in your favor because the fish committed to your level.
CAUTION/OVERHEATING = casting a net. You catch everything in the water โ nothing gets past you. But only ~45โ55% of what you catch is worth keeping. You spend significant effort (and capital) processing fish that turn out to be losses.
The fill-adjusted analysis confirms that the inverse score paradox is real, not an artifact of limit order selection bias. Even when you penalize PRIME for its lower fill rate, it still produces 6ร more PnL per signal than OVERHEATING.
A critical clarification: the backtest PnL is purely the percentage price move โ (exitPrice - entryPrice) / entryPrice ร 100. Leverage is not factored in. A PRIME trade at 2x leverage and a OVERHEATING trade at 15x leverage that experience the same price movement produce identical backtest PnL.
SL/TP distances by tier
Exit reasons โ where WR gap comes from
Drag score and Hurst to see how stops and targets shift. Higher scores โ tighter stops + further targets โ lower win rate.
This means every win rate, profit factor, and PnL number in this report is independent of leverage. The tier performance differences are NOT caused by leverage amplifying losses at OVERHEATING tier. They are caused by the SL/TP geometry that the scoring system assigns to each tier.
Leverage doesn't change the probability of a trade winning or losing โ if liquidation stays outside the stop boundary, the trade exits at the stop regardless of leverage level. What leverage changes is the dollar consequence:
| Scenario | Stop Distance | At 2x | At 15x |
|---|---|---|---|
| SL hit on โณ100 margin | 2.54% | โณ5.08 loss (5.1% of margin) | โณ38.10 loss (38.1% of margin) |
| TP hit on โณ100 margin | 5.41% | โณ10.82 gain | โณ81.15 gain |
The probability of the stop being hit is identical at 2x and 15x. But the dollar impact scales linearly with leverage. This matters for capital preservation and drawdown management in live trading, but it is separate from edge.
A controlled experiment isolated the real causes. Holding signal type (bounce) and regime (mild_trend) constant, only the tier differs:
| Tier | WR | SL Hit Rate | TP Hit Rate | SL Distance | TP Distance | RRR |
|---|---|---|---|---|---|---|
| PRIME | 73.5% | 12.0% | 54.0% | 3.29% (wide) | 2.81% (close) | 0.85 |
| STRONG | 64.2% | 19.0% | 37.5% | 3.10% | 3.31% | 1.07 |
| CAUTION | 52.7% | 31.3% | 25.2% | 2.83% | 4.54% | 1.61 |
| OVERHEATING | 42.2% | 49.7% | 17.1% | 2.54% (tight) | 5.41% (far) | 2.13 |
The pattern holds across all three regimes (mild_trend, trending, random). The three drivers are:
The scoring system assigns wider ATR multipliers to lower scores:
Score 20-34 (PRIME): ATR mult = 1.8 ร stopHurstMod โ SL ~3.3% away
Score 65+ (OVERHEATING): ATR mult = 1.0 ร stopHurstMod โ SL ~2.5% away
ADA/USD routinely oscillates 2โ3% on intrabar noise within a 4H candle. A 2.54% stop sits inside this noise band โ normal volatility triggers it before the trade thesis has time to play out. A 3.29% stop sits outside the noise band, giving the trade room to breathe.
The result: PRIME's SL hit rate is 12.0%. OVERHEATING's is 49.7%. Nearly half of all OVERHEATING-tier trades die to noise before the directional thesis can develop. This is the single largest driver of the WR gap.
The scoring system assigns more ambitious TP multipliers to higher scores:
Score 20-34 (PRIME): TP reward mult = 0.8 โ TP ~2.8% away
Score 65+ (OVERHEATING): TP reward mult = 2.0 โ TP ~5.4% away
On a 24โ48 hour hold, price easily travels 2.8% in the right direction. It rarely travels 5.4%. PRIME's TP gets hit 54% of the time. OVERHEATING's TP gets hit 17% of the time. The remaining trades expire their hold window, and while hold-expired trades are net positive, they contribute less PnL than clean TP hits.
PRIME and STRONG enter via the confluence system โ limit orders placed at levels where fractals, moving averages, VWAP, and Fibonacci levels cluster. Price reaching these levels means it has retraced to structural support/resistance, where bounce probability is highest.
OVERHEATING enters at market price, wherever price happens to be when the score hits 65+. There's no structural advantage to the entry level itself. The trade is chasing the current price rather than waiting for price to come to a high-probability level.
This geometry creates a fundamental tradeoff that the backtest resolves clearly:
PRIME wins 73.5% of the time but each win averages +2.81% (the TP distance). Losses average -3.29%. With a 0.85 RRR, PRIME needs to win >55% to be profitable โ and it wins 73.5%.
OVERHEATING wins 42.2% of the time but each win averages +5.41% (the TP distance). Losses average -2.54%. With a 2.13 RRR, OVERHEATING only needs to win >32% to be profitable โ and it wins 42.2%, which technically clears the bar but barely.
The math favors PRIME because the WR surplus (73.5% vs the 55% breakeven) is enormous, while OVERHEATING's WR surplus (42.2% vs the 32% breakeven) is slim. PRIME has a massive margin of safety; OVERHEATING is always flirting with breakeven.
The leverage cap discussed in Section 18 was shipped in the April 2026 edge-weighted inversion โ and for the right reason. Capping OVERHEATING-tier leverage from 10โ15x down to 1x doesn't change the win rate or the backtest PnL. What it changes is the dollar impact when OVERHEATING-tier trades lose. Since OVERHEATING loses 49.7% of the time by stop loss, reducing the leverage reduces the absolute capital destroyed per loss. The edge doesn't change; the capital preservation does. Simultaneously, PRIME tier was raised from 2x to 8x leverage on Daily, matching the system's sizing commitment to where the edge actually lives.
Beyond static tier performance, transitions between tiers reveal where the sharpest edges hide:
| Transition | Trades | Win Rate | Total PnL | Why It Works |
|---|---|---|---|---|
| OVERHEATING โ PRIME | 118 | 78.0% | +176 | Score cooling from extremes means the overextension is resolving. PRIME entry catches the reversal with wide stops and low leverage. |
| CAUTION โ PRIME | 151 | 77.5% | +209 | Same pattern โ conviction dropping means the prior signal is fading, and the new PRIME trade catches the reversion. |
| STRONG โ PRIME | 452 | 72.1% | +485 | The highest-volume profitable transition. Steady downward drift in score = market settling into a range, which is bounce territory. |
| Transition | Trades | Win Rate | Total PnL | Why It Fails |
|---|---|---|---|---|
| PRIME โ OVERHEATING | 94 | 38.3% | +8 | Score jumping from 20โ34 to 65+ in one cycle means many indicators flipped simultaneously โ usually a spike reaction that reverses. |
| STRONG โ OVERHEATING | 147 | 40.8% | -15 | Only negative-expectancy transition. The upward jump to OVERHEATING catches the tail end of moves. |
| CAUTION โ OVERHEATING | 113 | 43.4% | +51 | Barely profitable. More of the same pattern โ chasing confirmation at the peak. |
Downward transitions outperform upward transitions. When the score drops (especially into PRIME), the next trade benefits from early entry and wider stops โ and under the April 2026 inversion, PRIME now receives the highest leverage, so the system captures these transitions with appropriately-sized positions. When the score jumps up (especially into OVERHEATING), the trade enters late and uses tight stops that get stopped out. Historically this combined with heavy leverage to amplify the loss; the inversion capped OVERHEATING at 1x to neutralize that dollar-impact.
These recommendations are based purely on what the 5-year backtest data shows. Each change addresses a specific, quantified problem.
Shipped as part of the April 16, 2026 edge-weighted leverage inversion. The values below reflect the original proposal. The actual shipped version goes further, inverting leverage across all four tiers. See prelude at top of this report.
The Problem (as originally identified): OVERHEATING tier (score 65+) had the worst win rate in both engines but deployed the highest leverage. In Swing, OVERHEATING trades win 15.8% of the time with what was then 5โ7x leverage. In Daily, OVERHEATING trades win 41.6% with what was then 10โ15x leverage. While leverage doesn't change the probability of winning (the backtest PnL is purely percentage price movement), it dramatically amplifies the dollar consequence. A OVERHEATING-tier Daily stop loss of 2.54% at 15x leverage destroyed 38% of margin per trade โ and the stop gets hit 49.7% of the time. This was a capital preservation problem, not an edge problem.
Historical Logic (pre-April 2026):
SWING_LEVERAGE:
score 20-34 โ 1x (tier was LEAN)
score 35-49 โ 2x (tier was PREPARE)
score 50-64 โ 3x (tier was ACTIVE)
score 65-79 โ 5x (tier was HIGH)
score 80+ โ 7x (tier was HIGH)
DAILY_LEVERAGE:
score 20-34 โ 2x (tier was LEAN)
score 35-49 โ 3x (tier was PREPARE)
score 50-64 โ 5x (tier was ACTIVE)
score 65-79 โ 10x (tier was HIGH)
score 80+ โ 15x (tier was HIGH)
Shipped Logic (post-April 2026):
SWING_LEVERAGE (edge-weighted inversion):
20-34 (PRIME) โ 3x (was 1x)
35-49 (STRONG) โ 2x (unchanged)
50-64 (CAUTION) โ 1x (was 3x)
65-100 (OVERHEATING) โ 1x (was 5-7x)
DAILY_LEVERAGE (edge-weighted inversion):
20-34 (PRIME) โ 8x (was 2x)
35-49 (STRONG) โ 5x (was 3x)
50-64 (CAUTION) โ 3x (was 5x)
65-100 (OVERHEATING) โ 1x (was 10-15x)
What This Affects: - This does NOT change the win rate, SL hit rate, or backtest PnL โ those are determined by the SL/TP geometry (Section 16), not leverage - What it changes is the real-dollar impact in live trading: OVERHEATING tier margin destruction drops from 38% per stop (at the old 15x) to ~2.5% per stop (at the new 1x) on the same 2.54% move - OVERHEATING tier exposure drops from 97.5% to 6.5% of capital (Daily at score 75) - Simultaneously, PRIME tier โ the highest-edge tier โ now gets the largest position allocation (8x Daily, 3x Swing) - Simulated over 5 years of backtest data: 2.11ร total return with less than half the drawdown vs. the previous leverage assignment
The Problem: Momentum signals (MACD cross + price beyond SMA-slow) fire 114 times in the Daily backtest with a 40.4% win rate and 1.16 PF. Bounce signals fire 3,339 times with 64.6% WR and 2.14 PF. The Daily engine is fundamentally a bounce/mean-reversion system โ momentum signals drag its performance.
The problem is worse in specific regimes: momentum signals in random regime have a 26.9% WR (-14.54 PnL). The momentum signal tries to catch trend continuation in a market that isn't trending.
Current Logic:
// In detect():
sig.mb = MACD crosses above zero AND price > SMA-slow โ fires momentum signal
// In score():
if (sig.mb) pts = 22 ร momMult // Full 22 points for momentum
Proposed Logic โ Option A (Regime Gate):
// Only allow momentum signals when Hurst confirms trending
if (sig.mb && hurst.H > 0.55) pts = 22 ร momMult // Momentum only in trending regimes
else if (sig.mb) pts = 8 ร confMult // Downgrade to "building" level in non-trending
Proposed Logic โ Option B (Score Weight Reduction):
// Reduce momentum signal max from 22 to 12, keep bounce at 16
signal: { max: 22, momentumPts: 12, squeezePts: 20, bouncePts: 16, buildingPts: 7 }
What This Affects: - Option A eliminates momentum signals in random/reverting regimes where they have 26.9% and ~35% WR. In trending regimes where momentum has 34.6% WR, the signal still fires but with full conviction only when H > 0.55 - Option B reduces the score contribution of momentum signals from 22 to 12, making it harder for a momentum signal alone to push a trade into CAUTION/OVERHEATING tier. Under the April 2026 inversion, the reduced score keeps the position at the larger PRIME/STRONG leverage bands instead of dropping it into the 1x-3x CAUTION/OVERHEATING range - Neither option changes bounce signals, which are the engine's actual edge - Estimated PnL impact: +30โ60 points by avoiding or downsizing the 114 momentum trades that collectively contributed only +26 PnL (essentially break-even with massive variance)
The Problem: The isGo boolean in the Daily engine is backward. Trades where isGo = FALSE have 69.2% WR and +2,006 PnL. Trades where isGo = TRUE have 57.0% WR and +895 PnL. The flag is meant to indicate favorable conditions but actually indicates unfavorable ones.
Current Logic:
isGo: [computed as a boolean in the backtest โ exact logic in engine produces inverted results]
Proposed Logic:
// Invert the flag
isGo = !isGo // or fix the underlying condition that produces the inversion
What This Affects:
- If used as a trade filter (only trade when isGo is correct), this would filter out the lower-WR trades. The 1,538 trades currently marked TRUE but performing worse would be re-evaluated
- If used as a position sizing modifier (reduce size when isGo is unfavorable), it would reduce exposure on the weaker 57% WR subset
- This is likely the single highest-impact code fix: one boolean flip potentially shifts ~1,100 PnL from marginal trades to properly-sized trades
- Note: need to trace exactly where isGo is computed in the engine to understand what condition it's checking and why it's inverted
The Problem: The best trading edge in the system (OVERHEATING โ PRIME at 78.0% WR) is invisible to the user. The dashboard shows current tier but not what the previous tier was or the historical performance of that specific transition.
Current Logic:
// Dashboard shows: current tier, current score, current direction
// No transition context
Proposed Logic:
// Track previous tier in blob state
previousTier: 'OVERHEATING'
currentTier: 'PRIME'
transitionLabel: 'OVERHEATING โ PRIME'
transitionWinRate: 78.0
// Display in trade suggestion UI:
"Tier shifted from OVERHEATING to PRIME โ this transition has 78.0% historical WR"
What This Affects:
- Zero impact on the engine's scoring or signal logic
- Gives the user (and Nova-Kali) critical context about the quality of the current setup
- Can be used by Nova-Kali to add conviction to coaching: "The score just dropped from 68 to 28. This OVERHEATING โ PRIME transition has historically been the single best entry point with a 78% win rate. Consider this a strong setup."
- Implementation: store previousTier per engine in the blob state, compare on each 15-minute cycle, calculate display values from a static lookup table of transition stats
The Problem: Position sizing is purely score-based but regime strongly affects outcome. A score of 50 in mild_trend produces very different results than score 50 in random (for Swing) or trending (for Daily). The current system doesn't account for this.
Current Logic:
pp = min(score/100 ร posCap, posCap) // Score is the only input to position sizing
Proposed Logic:
// Add regime quality multiplier
regimeQuality = {
// Swing engine
swing: { mild_trend: 1.2, trending: 0.85, random: 0.7 },
// Daily engine
daily: { random: 1.2, trending: 1.1, mild_trend: 0.95 }
}
pp = min(score/100 ร posCap ร regimeQuality[engine][regime], posCap)
What This Affects:
- In Swing mild_trend (PF 2.00), positions are sized up 20% โ capitalizing on the engine's best regime
- In Swing trending (PF 1.23), positions are sized down 15% โ protecting against the engine's weaker regime
- In Daily random (PF 2.47), positions sized up 20%
- The adjustment is multiplicative with the score-based sizing, so it refines the allocation without changing the scoring or signal logic
- Estimated impact: shifts ~5โ10% of capital from lower-PF regimes to higher-PF regimes, improving risk-adjusted returns without adding any new signals
The Problem: The R:R dampener already reduces position size when R:R < 1.0, but OVERHEATING-tier trades with R:R between 1.0 and 1.5 still deploy full size with high leverage. The backtest shows the 2.0โ3.0 RRR bucket in Swing has negative expectancy (-49 PnL) and the 1.5โ2.0 bucket is barely positive. The ambitious TP targets at OVERHEATING tier (2.5x reward mult in Swing) produce wide R:R values that rarely get hit.
Current Logic:
// R:R dampener only fires below 1.0
if (rrr > 0 && rrr < 1) { dampenFactor = max(0.2, rrr); ... }
Proposed Logic:
// Tighter R:R floor for OVERHEATING tier
if (tier === 'OVERHEATING' && rrr > 0 && rrr < 1.5) {
dampenFactor = max(0.3, rrr / 1.5);
// Reduce position proportionally
}
// Keep existing dampener for other tiers
else if (rrr > 0 && rrr < 1.0) { dampenFactor = max(0.2, rrr); }
What This Affects: - OVERHEATING tier trades with R:R 1.0โ1.5 get position sizing reduced proportionally (a 1.0 R:R OVERHEATING trade gets cut to 67% of full size) - This specifically targets the weakest part of the weakest tier โ OVERHEATING trades where the TP is ambitious but the backtest shows the target rarely gets hit before hold expiration - Combined with Improvement 1 (leverage cap), this double-gates OVERHEATING tier: less leverage AND smaller positions when the R:R doesn't justify full commitment
| Improvement | Scoring Logic | Signal Detection | Position Sizing | Leverage | UI/Dashboard |
|---|---|---|---|---|---|
| 1. Cap OVERHEATING leverage | โ | โ | โ | YES | โ |
| 2. Filter momentum signals | โ | YES | Indirectly (lower scores) | Indirectly | โ |
| 3. Invert isGo flag | โ | YES | Indirectly | โ | โ |
| 4. Tier transition display | โ | โ | โ | โ | YES |
| 5. Regime-gated sizing | โ | โ | YES | โ | โ |
| 6. R:R floor for OVERHEATING | โ | โ | YES | โ | โ |
Important note on leverage improvements: The leverage cap (Improvement 1) and R:R floor (Improvement 6) do not change the backtest PnL, since the backtest measures percentage price movement only. Their impact is on live-trading dollar P&L, drawdown, and capital survival. The estimates below reflect this distinction.
Backtest PnL improvements (from signal/sizing changes): - Momentum signal drag reduced (Improvement 2) โ ~+40 PnL - isGo filter applied (Improvement 3) โ ~+200 PnL (conservative estimate based on filtering only the worst isGo=TRUE trades in random regime) - Regime-gated sizing (Improvement 5) โ ~+50 PnL
Live-trading capital preservation improvements (from leverage changes): - OVERHEATING tier dollar losses reduced ~70% (Improvement 1) โ margin destruction per stop drops from 38% to 7.6% at the Daily engine's typical OVERHEATING stop distance - R:R floor prevents oversized positions on OVERHEATING trades with marginal risk:reward (Improvement 6)
Conservative backtest PnL improvement: ~+290 over 5 years. The larger impact is in live-trading capital preservation โ reducing max drawdown by downsizing the highest-leverage, lowest-probability trades (OVERHEATING tier stops out 49.7% of the time).
The Swing engine would see proportional improvements, especially from the leverage cap (Improvement 1), which limits dollar destruction on the 0% WR trending ร OVERHEATING trades (currently 8 trades that all stopped out at 5โ7x leverage).
| Priority | Improvement | Effort | Risk | Impact |
|---|---|---|---|---|
| 1 | Cap OVERHEATING leverage | 10 min โ change 6 numbers in engine-config.mjs | Zero โ no logic changes | High โ immediate dollar-loss reduction in live trading |
| 2 | Invert isGo flag | 30 min โ trace the flag, flip the boolean | Low โ binary change | High โ 12% WR improvement on affected trades |
| 3 | Tier transition display | 2 hrs โ add previousTier to blob, lookup table, UI | Zero โ display only | Medium โ improves decision quality |
| 4 | Filter momentum signals (Daily) | 1 hr โ add regime gate to signal scoring | Low โ affects 3.3% of trades | Medium โ removes the weakest signals |
| 5 | Regime-gated sizing | 1 hr โ add multiplier to levCalc functions | Low โ multiplicative adjustment | Medium โ improves capital allocation |
| 6 | R:R floor for OVERHEATING | 30 min โ extend dampener logic | Low โ tighter gate on existing mechanism | Low-Medium โ refines OVERHEATING tier sizing |