Appearance

Personalize your experience

Mode
Accent Color
Direction
MarketplacestrategiesOU Half-Life Reversion
QuaScript

OU Half-Life Reversion

Admin's ChoiceFree

Fades z-score stretches only when the half-life is short

0.0(0)
0 installs 0 0 comments
Q
159 listings

The QuaTick team's QuaScript analyses, run by the native C++ engine.

View Profile
quascriptstrategymean-reversionornstein-uhlenbeck
Free
Backtest this strategy Open in QuaTick IDE Edit in IDE
Version
Updated
Compatible

Live engine preview

QuaScriptQuaScript

The QuaScript engine's own output on the chart the terminal uses. Change the settings or edit the code and press Run to recompute it.

OU Half-Life Reversionengine result
Loading the engine result…
quatick-charts · QuaScript engine on synthetic 1-minute bars · drag to pan · scroll to zoom · Run executes the script on the engine (⌘/Ctrl+Enter while editing)

About

Trades mean reversion only when the market is measurably mean-reverting. An Ornstein-Uhlenbeck fit gives the fair value, the z-score of price against it and the half-life of deviations; a stretched z-score is faded only while the half-life is short enough to pay within a few bars, and the trade is given twice the half-life to work. Entry: z beyond the entry level and turning back, half-life below the limit. Exit: target at the fitted mean, stop beyond the stretch, or a time stop at twice the half-life. Risk: 1 % per trade. Method: Uhlenbeck and Ornstein (1930), AR(1) half-life.


Settings

Fit window: 120

Entry z-score: 2

Maximum half-life (bars): 30

Stop (ATR): 2.5


Measured backtests

Run by the backtest engine on public candles, 2025-11-01 to 2026-11-01, with the script's own commission and slippage, on BTCUSDT 1h (Binance) and ETHUSDT 1h (Binance). The figures per market are in the Measured backtest panel below. Past results do not predict future returns.


Backtest it yourself

Open it on the backtesting page to run it on any symbol, interval and date range, then use Monte Carlo, optimisation, walk-forward and robustness tests.

Strategy Rules

Entry Conditions

  • ▸ z beyond the entry level and turning back, half-life below the limit

Exit Conditions

  • ▸ target at the fitted mean, stop beyond the stretch, or a time stop at twice the half-life

Measured backtest

BTCUSDT 1h (Binance), 2025-11-01 to 2026-11-01 · backtest engine, script costs included

Backtest this strategy

-4.0%

Return

-0.60

Sharpe

-7.8%

Max DD

48.0%

Win Rate

50

Trades

MarketTradesReturnSharpeMax DDWin rateProfit factor
BTCUSDT 1h50-4.0%-0.60-7.8%48.0%0.81
ETHUSDT 1h466.2%1.11-3.6%56.5%1.40

Past results on historical data do not predict future returns.

Source Code

Edit in IDE
quascript
1strategy "OU Half-Life Reversion" {
2 overlay = true
3 capital = 100000
4 commission = 0.05
5 slippage = 1
6 sizing = risk_percent
7 max_leverage = 1
8 size = 1
9 pyramiding = 1
10}
11/// Trades mean reversion only when the market is measurably mean-reverting. An Ornstein-Uhlenbeck
12/// fit gives the fair value, the z-score of price against it and the half-life of deviations;
13/// a stretched z-score is faded only while the half-life is short enough to pay within a few
14/// bars, and the trade is given twice the half-life to work.
15/// Entry: z beyond the entry level and turning back, half-life below the limit. Exit: target at
16/// the fitted mean, stop beyond the stretch, or a time stop at twice the half-life.
17/// Risk: 1 % per trade. Method: Uhlenbeck and Ornstein (1930), AR(1) half-life.
18use "viz/premium" as pr
19len = param.int(120, title = "Fit window", min = 30, max = 2000, group = "Model")
20z_in = param.float(2.0, title = "Entry z-score", min = 0.5, max = 4, step = 0.1, group = "Entry")
21hl_max = param.float(30, title = "Maximum half-life (bars)", min = 2, max = 500, group = "Entry")
22stop_k = param.float(2.5, title = "Stop (ATR)", min = 0.5, max = 8, step = 0.1, group = "Risk")
23(m, hl, z) = hft.ou(close, len)
24a = ta.atr(20)
25ok = hl < hl_max and hl > 0
26flat = position.size == 0
27if flat and ok and z[1] < -z_in and z > z[1] {
28 order.market("L", side.long)
29 exit.bracket("L exit", entry = "L", stop = close - stop_k * a, target = m)
30}
31if flat and ok and z[1] > z_in and z < z[1] {
32 order.market("S", side.short)
33 exit.bracket("S exit", entry = "S", stop = close + stop_k * a, target = m)
34}
35persist time_limit = 0
36if flat { time_limit := math.round(math.max(2 * math.coalesce(hl, 10), 3)) }
37if position.size != 0 and position.bars_held >= time_limit { exit.close_all() }
38risk.max_drawdown(25)
39render.line(m, "OU mean", color = ok ? color.emerald.300 : color.slate.500, width = 2)
40render.mark(flat and ok and z[1] < -z_in and z > z[1], shape = shape.triangle_up, at = anchor.below_bar, color = color.emerald.300, text = "Revert", title = "Long setup")
41render.mark(flat and ok and z[1] > z_in and z < z[1], shape = shape.triangle_down, at = anchor.above_bar, color = color.rose.300, text = "Revert", title = "Short setup")
42persist t = draw.table.create(anchor.top_right, 5, 2)
43if bar.is_last {
44 pr.heading(t, 2, "OU Half-Life Reversion", "Strategy · Ornstein-Uhlenbeck")
45 pr.row(t, 2, "Half-life", math.is_na(hl) ? "none" : str.format("{:.1f} bars", hl), ok ? pr.BULL : pr.LABEL)
46 pr.row(t, 3, "Z-score", str.format("{:+.2f}", z), math.abs(z) > z_in ? pr.WARN : color.white)
47 pr.row(t, 4, "Fair value", str.format("{:.2f}", m), color.white)
48}
49 
Read-only preview. Open in the QuaTick IDE to edit and deploy.

Reviews

No reviews yet. Be the first to review this listing.

Discussion

Sign in to join the discussion. Your posts and replies show up on your profile.