Skip to content
All strategies

Strategies

EURUSD 1H London-Session 2-Sigma Band Fade: short above / long below a 20-bar mean +/- 2 std, exit at the mean, after 12 bars or at a 1.5x ATR(20) intrabar stop (FX venue, long+short, 1.5x cap)

Outcome: Live

EurusdLondonBandFade

Outcome Summary

EurusdLondonBandFade was a faithful port of a user's EURUSD 1-hour London-session 2-sigma band-fade rule. In its single iteration the full backtest lost 6.78% (Sharpe -0.66, 500 trades, average -0.0088% of notional), and it lost most heavily in stressed volatility regimes. The backtest review found no reversion edge even before costs and voted abandon, so optimization and the later reviews never ran. The pipeline still records the final stage as live_stage, which conflicts with that abandon verdict and should be checked.

Hypothesis

Faithful port of the user's submitted rule, settled in the idea builder (venue FX, 1h bars, long+short, leverage 1.5x, OHLCV only). Instrument: EURUSD.FX spot forex, 1-HOUR bars (~2 years of hourly history; Yahoo prices). Signal: 20-bar simple mean and 20-bar population std of hourly closes; upper band = mean + 2*std, lower band = mean - 2*std. Session filter: entries only on bars that open at or after 07:00 and close at or before 16:00 Europe/London local time (DST-aware, i.e. bars 07:00-08:00 through……Show moreShow less

Faithful port of the user's submitted rule, settled in the idea builder (venue FX, 1h bars, long+short, leverage 1.5x, OHLCV only). Instrument: EURUSD.FX spot forex, 1-HOUR bars (~2 years of hourly history; Yahoo prices). Signal: 20-bar simple mean and 20-bar population std of hourly closes; upper band = mean + 2*std, lower band = mean - 2*std. Session filter: entries only on bars that open at or after 07:00 and close at or before 16:00 Europe/London local time (DST-aware, i.e. bars 07:00-08:00 through 15:00-16:00 London). A close above the upper band opens a short; a close below the lower band opens a long. Exits: the close returns to (crosses or touches) the current 20-bar mean, OR 12 hourly bars have elapsed since entry, OR an intrabar protective stop at 1.5 x ATR(20) from the entry fill price is hit. Execution substitutions (per the capability registry): FX is a session venue, so a decision on bar N's close fills at bar N+1's open (next_open_fill, supported); the stop is a platform-matched intrabar stop placed via submit_stop_order (reduce_only) — it triggers inside the bar and fills at the trigger or at a gapped open; it is not an exchange-resting order, which the idea does not ask for. Sizing ('risk-sized relative to account equity', completed with standard choices): notional = risk_pct x equity / stop_distance_pct, capped at leverage x equity with leverage = 1.5 (the user's settled value, below the 5x the text mentions as an upper bound). Cost arithmetic: hourly EURUSD std is ~0.08-0.12%, so a 20-bar 2-sigma excursion sits ~0.4-0.6% from the mean; if the trade recovers about a third of that distance on average after stops and time exits, gross capture is ~0.12-0.2% per trade versus a round trip of ~0.01-0.04% (0.2 bp commission per side + half the measured hourly spread per fill) plus ~0.015%/night overnight carry when a position is held through the 17:00 New York roll (late-session entries with a 12-bar clock can cross it). The platform's analyst fee floor for FX is 0.10% of notional per trade, so the honest expectation is near that floor — this is a real risk, stated up front, not a reason to change the user's rule. One prior FX experiment exists in the corpus; this fills the FX venue, long_short and mid-horizon buckets.

Implementation

EURUSD.FX 1-hour fade of 20-bar 2-sigma bands during London hours. On a closed bar inside 07:00-16:00 Europe/London (the bar opens at 07:00 or later and closes at 16:00 or earlier; DST-aware via zoneinfo), a close above mean+2*pop-std opens a short and a close below mean-2*pop-std opens a long. Exits come from whichever happens first: a closed bar back at or through the current 20-bar mean, 12 bars after the entry fill (market orders, filled at the next bar's open on the FX session venue), or a reduce-only intrabar stop placed on the entry fill at fill -/+ 1.5*Wilder ATR(20) (submit_stop_order, crossed_policy=market). Sizing: notional = min(0.5% * equity / (1.5*ATR/close), 1.5 * equity), in whole EUR units, with a 1,000-unit minimum.

Faithful implementation of the settled rule. calculate_signal returns the continuous z-score (close-mean)/sd, and should_enter thresholds it at +/-band_k, which is the same as comparing the close with the bands. Session gating applies to entries only. A protective stop is placed from on_order_filled using the ATR of the signal bar; the base cancels it on a mean or time exit, and on_position_closed records the exit time so no entry happens on the bar an exit occurred. Risk route (a): with the 1.5x leverage cap binding (0.5%/stop% is usually above 1.5x), the loss at the stop is about 1.5 x 0.2-0.3% = 0.3-0.45% of equity, under 2%. All state is held in scalar attributes. The ATR is incremental, and the mean and std use only the last lookback bars (bounded). Execution substitutions follow the registry: next_open_fill for market orders and a platform-matched intrabar stop instead of an exchange-resting order. Risk noted: the expected average trade is close to the 0.10% FX analyst floor.

Verification Results

No code change needed. Note that the first-bar stop coverage depends on the platform's session-stop switch.

The protective stop is placed in on_order_filled while the entry fills at the next bar's open. Per src/backtesting/next_open.py, without SF_SESSION_STOP_FILLS an order sent at the open is released only after bar N+1's range has been matched, so in the entry bar itself the stop is tested only against that bar's close. This is platform behaviour, not a strategy defect. The stop protects from the second bar onward either way.

Optional: none required.

Wilder ATR updates only start once the base begins calling calculate_signal (after min_bars_required = 21 bars). The first true range has no previous close, so it falls back to high-low. The ATR seeds about 20 bars later than strictly necessary. This is harmless: the evaluation warmup is 60 bars and entries are gated on atr_value > 0.

Expected gross capture (~0.12-0.2% per trade by the hypothesis's own arithmetic) is close to the 0.10% FX analyst floor. The mean target is the moving 20-bar mean, which drifts toward price, so per-trade capture shrinks. About 1,000 sandbox trades in 735 days means roughly 1.4 trades per day. The analyst should check whether avg_trade_return_pct clears the floor after carry on trades held across the 17:00 New York roll.

Backtest Review

Sharpe
-0.66
Total return
-6.78%
Max drawdown
11.44%
Trades
500
Win rate
40.2%
Profit factor
0.90

The code does what the user's rule says: London-session gate (DST-aware), 20-bar population-std bands, entries against the move, exit at the mean, a 12-bar clock, a reduce-only 1.5x ATR stop, and risk-sized notional capped at 1.5x. Trades are balanced (201 long, 194 short) and entered on band breaches, as the hypothesis predicts.

The sample is reliable: 395 closed trades over the development period (to 2026-06-05), clean input audit, metrics_reliable true.

Risk control works: largest loss is $572 (about 0.57% of equity) and max drawdown is 10.2%, inside the pre-registered 15% limit.

Net expectancy is negative. Average trade return is -0.008% of notional against the 0.10% FX floor, profit factor 0.91, Sharpe -0.65, total return -5.7%, and 2025 and 2026 are both losing years.

The edge is not positive even before costs. Price PnL summed over the ledger is about -$2.9k, a mean gross of about -0.004% per trade. Commission ($4.0k) and overnight carry ($2.4k) add losses on top. Fees are not what kills this strategy: the fade itself has no reversion edge.

The QA concern is confirmed. The hypothesis expected about 0.12-0.2% gross capture per trade; the measured capture is around zero. Short fades do worst, with a 35% win rate, which fits band walks continuing rather than reverting.

Outcome Summary

Hourly EURUSD 2-sigma band breaches in the London session did not revert enough to give any gross edge, and short fades were worst (36% win rate), so a fade needs a regime or trend filter before costs are even relevant.

At backtest review the analyst voted abandon before optimization: the fade lost money even before costs (gross price PnL about -$2.9k, or roughly -0.004% per trade), so fees were not the cause. The record nonetheless shows a final stage of live_stage, with no optimization, analyst, risk-officer or abandon-reason record; the input does not explain this mismatch.

A user-submitted EURUSD 1-hour mean-reversion fade on the FX venue: during the London session it shorted closes above a 20-bar mean + 2 std and bought closes below mean - 2 std, exiting at the mean, after 12 bars, or at a 1.5x ATR(20) stop, with risk-sized notional capped at 1.5x leverage.

Over the full backtest (2024-10-30 to 2026-10-30) it lost 6.78% with a Sharpe of -0.66 across 500 closed trades: 40.2% win rate, profit factor 0.90, average trade -0.0088% of notional (against a 0.10% FX floor), and 11.4% max drawdown. Commission was $5,033 and overnight carry about $2,960; the signal diagnostic found no information (IC 0.004, t=0.1).

Backtest and paper results are hypothetical. Trading involves risk of loss.