How the Fib entry process
works in v3

A walkthrough of what happens between a z-score crossing threshold and a real order placed at the broker — including the 27 May fix.

Dual FX Macro Model v3 · post-Fib fix 27 May 2026
PHASE 01

The threshold cross

Every signal check, v3 computes a fresh z-score from the yield-spread model and compares it to a hard-coded threshold. Cross the threshold inside session, and the next phase begins.

For EURUSD, the z-score comes from a rolling-OLS lag model on US-DE 2Y spread changes, evaluated on 15-minute bars. For USDJPY, it's a 30-day rolling z-score of the US-JP 2Y spread level, evaluated hourly. Different signal pipelines, same downstream logic.

The check fires on cadence — EURUSD every 15 minutes at :01, :16, :31, :46, USDJPY hourly at :02. If z stays below threshold, the check logs "No signal" and exits. If z crosses threshold, we move to the session test.

EURUSD threshold
±2.75σ
USDJPY threshold
±2.00σ
EU session
09:00–18:00 EET
UJ session
07:00–17:00 EET
Positive z (z > 0) means the spread has widened relative to its rolling mean — this is a LONG signal. Negative z is SHORT. The direction lives in state["position_signal"] as +1 or −1 respectively.
PHASE 02

Session, armed, and the silent fail problemFixed 27 May

Before the model arms, v3 runs three filters. Two are necessary; one was silently dropping valid signals.

The filters run in order. Each must pass before the next is evaluated:

# pseudocode of v3's signal-handling sequence if |z| < threshold: → "No signal" — exit if not in_session(): → "outside session — skipping" if already armed: → "fast polling running" compute Fib pull from last 15M bar if pull <= 0.0001: → silent return ← BUG otherwise: SIGNAL DETECTED → arm fast polling
!
The bug The Fib pull guard fires when the candidate bar's range is below 1 pip. Reasonable in principle — you can't compute a Fibonacci retracement on a bar with no range. But v3 was reading the in-progress bar — the one that just opened seconds before the signal check ran. A bar with 60 seconds of tick data routinely had a range below 1 pip, even when the underlying signal was strong. Two signals on 27 May (z=+2.81 and z=+3.70) silently failed this guard with zero log output.
The fix Two changes shipped 27 May. First: v3 now reads df.iloc[-2] — the just-completed prior 15M bar with a full window of price action — instead of df.iloc[-1], the in-progress current bar. Second: if the guard ever fires again, the log will record the z-score, direction, pull in pips, and full bar OHLC. No more silent misses, ever.
PHASE 03

The Fibonacci pullback geometry

The model doesn't enter at the threshold cross. It waits for a 78.6% retracement of the signal bar's price action — a deeper pullback that improves entry pricing without sacrificing fill rate.

0.786 is the deepest commonly-used Fibonacci ratio. The model uses it to wait for a near-full retracement of the signal bar's range before entering. For a LONG signal, this means waiting for price to dip back down toward the bar's low. For a SHORT, waiting for price to rally up toward the bar's high.

The retracement target is computed from the just-completed bar's close, high, and low. Once armed, fast-polling starts at 1-second cadence, waiting up to 6 hours for price to tap the target.

DIAGRAM 01
Fib target for a LONG signal (z > +2.75)
1.16500 (high) 1.16450 (close) 1.16411 (target) 1.16400 (low) High (NOT used for LONG) Close (bc) ← anchor here Low / wick bottom (bl) pull (C−L) TARGET = bc − 0.786 × pull Price dips down from close… taps target → fills LONG. ⚠ Fib uses CLOSE → LOW only. Ignore the open and the high. Vertical axis: price · Horizontal axis: time → 6-hour arm window
DIAGRAM 02
Fib target for a SHORT signal — real trade #1675593149 (29 May)
1.16745 1.16732 1.16686 1.16477 High / wick top (bh) Close (bc) ← anchor here Open (NOT used) pull (H−C) 5.9p TARGET = 1.16686 + 0.786 × 0.00059 = 1.16732 Price rallies up from close… taps 1.16732 → fills SHORT. (Took 7 min.) ⚠ The Fib uses CLOSE → HIGH only. NOT open → high. NOT full candle. On this up-candle, close = body top. On a down-candle, close = body bottom. Either way: anchor the CLOSE, ignore the open. Vertical axis: price · Horizontal axis: time → 6-hour arm window
LONG · z > +2.75

Wait for the dip

pull = bc − bl
target = bc − fib × pull
  • Pull = bar close minus bar low
  • Target sits between close and low
  • Price must dip down to fill
  • Filled price < signal close (better entry)
SHORT · z < −2.75

Wait for the rally

pull = bh − bc
target = bc + fib × pull
  • Pull = bar high minus bar close
  • Target sits between close and high
  • Price must rally up to fill
  • Filled price > signal close (better entry)
PHASE 04

Worked example — verified live trade

Every number below is taken straight from the v3 log for trade #1675593149 on 29 May 2026. This is a fully verified SHORT — bar values, target, fill, and slip all confirmed against the running system.

Trade #1675593149 — EURUSD SHORT (29 May 2026)

Signal check time
2026-05-29 16:01 UTC
z-score computed
−3.3158 past threshold of −2.75 ✓
Direction
SHORT (z < 0 → −1)
Signal bar used
df.iloc[-2] = last COMPLETED 15M bar (post-27-May fix)
Bar OHLC (15M)
O=1.16477 · H=1.16745 · L=1.16468 · C=1.16686
Pull (H − C)
0.00059 = 5.9 pips · > 1.0p guard ✓
Target = bc + 0.786 × pull
1.16732 = 1.16686 + 0.786 × 0.00059
Arm fast polling
SHORT @ 1.16732 · 1-second polling · 6h window
Target hit
1.16733 — 7 minutes after signal, price rallied up to tap target
Entry slip
+0.1p (target 1.16732, fill 1.16733 — near-perfect)
TP price · 0.20% of entry
1.16500 = 1.16733 × 0.9980
SL price · 0.25% of entry
1.17025 = 1.16733 × 1.0025
Position lots
2.86 z=−3.32 → still 1× band (1.5× starts at 3.50) · risk $715
Outcome
Open at time of writing — position live, ticket 1675593149
DRAW IT YOURSELF
How to replicate the model's Fib on your own chart

This is the part that trips people up. The model does NOT draw the Fib across the whole bar (high to low). It uses only the upper portion for a SHORT — from the body close up to the high (wick top). Most charting tools default to spanning the full candle, which puts the 0.786 level at a completely different price.

H 1.16745 C 1.16686 L 1.16468 0% = close (1.16686) 100% = high 0.786 level = TARGET 1.16732 78.6% of the way from close → high Anchor your Fib tool: 0% at CLOSE (1.16686), 100% at HIGH (1.16745). NOT high-to-low.

SHORT recipe:
1. Anchor 0% at the body CLOSE (1.16686)
2. Anchor 100% at the wick HIGH (1.16745)
3. Read the 0.786 level → 1.16732
4. That's the model's entry target.

For a LONG, flip it: 0% at close, 100% at the wick LOW, read the 0.786 level below the close.

Why your hand-drawn Fib didn't line up: if you anchored wick-to-body (high→close) instead of close-to-high, OR spanned the full candle (high→low) instead of just the close-to-high portion, the 0.786 ratio lands on a different price. The model uses only the close-to-extreme span — for SHORT that's close→high, for LONG that's close→low.
PHASE 05

The armed state

Once armed, v3 polls the broker every second waiting for price to tap the Fib target. This is where speed matters.

Fast polling runs in a daemon thread separate from the signal check scheduler. Every second, v3 queries mt5.symbol_info_tick() for the latest bid/ask and compares to the armed target. The check is direction-aware: for LONG, it watches the ask to dip to target; for SHORT, the bid to rally to target.

The polling cadence was bumped from 5 seconds to 1 second after the first v3-era trade slipped 5 pips on a fast move. Sub-second checks would cost CPU without meaningfully improving fills since broker tick streams update at roughly 100ms cadence anyway.

If 6 hours pass without a tap, the arm expires. No trade entered. No telegram. v3 logs "6h expiry" and disarms cleanly.

Polling cadence
1second
Arm window
6hours
Fib coefficient
0.786
Slip target
≤ 1pip
Slip is reported in every order-placed telegram alert. Positive slip on a LONG means filled BELOW target (cheaper entry — good). Positive slip on a SHORT means filled ABOVE target (better entry — good). Negative slip means filled past target in the wrong direction — usually 0.1–0.5p of broker-side execution delay. Anything > 2p suggests a fast-market event worth reviewing.
PHASE 06

Order placement and brackets

When price taps the target, v3 places a market order with TP and SL brackets attached at the broker. The brackets are the safety net — they work even if v3 crashes.

TP and SL are computed as fixed percentages of the actual fill price (not the Fib target). For EURUSD: TP at 0.20% in favor, SL at 0.25% against. For USDJPY: TP at 0.70%, SL at 0.40%. Same percentages used in the validated backtest.

The brackets live at the broker. If v3 disconnects, crashes, or the VPS reboots, MT5 still enforces TP/SL automatically. v3's job at that point is just to detect that the position closed (via positions_get) and write the close to trade history with the real P&L from MT5's deal records.

EURUSD

TP · 0.20% · SL · 0.25%

LONG:
TP = entry × 1.0020
SL = entry × 0.9975

SHORT:
TP = entry × 0.9980
SL = entry × 1.0025
USDJPY

TP · 0.70% · SL · 0.40%

LONG:
TP = entry × 1.0070
SL = entry × 0.9960

SHORT:
TP = entry × 0.9930
SL = entry × 1.0040
Hold-limit failsafe Beyond TP/SL, v3 also enforces a maximum hold time: 52 hours for EURUSD, 24 hours for USDJPY. If neither bracket fires within that window, v3 closes the position at market on the next signal check. This caps duration risk on positions that stagnate without resolving the spread divergence.
SUMMARY

The full path, end-to-end

[v3 signal check fires] compute z-score from yield-spread model if |z| < thresholdEXIT "No signal" if outside session windowEXIT "outside session" if already armedEXIT "polling running" read last completed 15M bar (df.iloc[-2]) compute pull (C−L for LONG, H−C for SHORT) if pull <= 1 pipLOG + EXIT (post-27-May fix) target = close ± 0.786 × pull SIGNAL DETECTED → telegram alert sent arm fast polling (1-sec cadence, 6h window) while armed: if price taps targetplace order if 6h elapseddisarm (no trade) order placed with TP/SL brackets at broker slip diagnostic logged + telegram alert on next signal check: if broker shows position closedlog P&L, telegram if hold-limit reachedclose at market if z-exit triggered (EU only)close at market
Dual FX Macro Model v3 · Formula A · live since 14 May 2026
Memorandum issued 27 May 2026 following Fib bar selection fix.