Appearance
Personalize your experience
Spectral Cycle Timing
FreeBuys cycle troughs, sells crests while a cycle is locked
Live engine preview
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.
About
Times entries with the market's dominant cycle. Complex demodulation measures power at 16 cycle lengths; when one length clearly dominates (a cycle lock), the phase of that cycle tells where price sits in it. The strategy buys cycle troughs and sells cycle crests in the direction of the slower trend, and leaves when half a cycle has passed. Entry: cycle lock, the detrended cycle wave turning up from below zero (trough) with price above the trend EMA, or down from above zero (crest) with price below it. Exit: half the dominant period, an ATR stop, or a target at the cycle amplitude. Risk: 1 % per trade. Method: complex demodulation (Bloomfield, 1976).
Settings
Demodulation smoothing: 48
Shortest period: 10
Period step: 3
Lock share: 0.14
Trend EMA: 150
Stop (ATR): 2
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
- ▸ cycle lock, the detrended cycle wave turning up from below zero (trough) with price above the trend EMA, or down from above zero (crest) with price below it
Exit Conditions
- ▸ half the dominant period, an ATR stop, or a target at the cycle amplitude
Measured backtest
BTCUSDT 1h (Binance), 2025-11-01 to 2026-11-01 · backtest engine, script costs included
-24.0%
Return
-2.51
Sharpe
-25.3%
Max DD
37.9%
Win Rate
116
Trades
| Market | Trades | Return | Sharpe | Max DD | Win rate | Profit factor |
|---|---|---|---|---|---|---|
| BTCUSDT 1h | 116 | -24.0% | -2.51 | -25.3% | 37.9% | 0.57 |
| ETHUSDT 1h | 171 | -13.5% | -1.06 | -20.5% | 49.1% | 0.82 |
Past results on historical data do not predict future returns.
Source Code
Edit in IDE| 1 | strategy "Spectral Cycle Timing" { |
| 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 | /// Times entries with the market's dominant cycle. Complex demodulation measures power at 16 cycle |
| 12 | /// lengths; when one length clearly dominates (a cycle lock), the phase of that cycle tells where |
| 13 | /// price sits in it. The strategy buys cycle troughs and sells cycle crests in the direction of |
| 14 | /// the slower trend, and leaves when half a cycle has passed. |
| 15 | /// Entry: cycle lock, the detrended cycle wave turning up from below zero (trough) with price above |
| 16 | /// the trend EMA, or down from above zero (crest) with price below it. Exit: half the dominant |
| 17 | /// period, an ATR stop, or a target at the cycle amplitude. Risk: 1 % per trade. |
| 18 | /// Method: complex demodulation (Bloomfield, 1976). |
| 19 | use "viz/premium" as pr |
| 20 | smooth = param.int(48, title = "Demodulation smoothing", min = 8, max = 400, group = "Spectrum") |
| 21 | p_min = param.int(10, title = "Shortest period", min = 4, max = 80, group = "Spectrum") |
| 22 | p_step = param.int(3, title = "Period step", min = 1, max = 20, group = "Spectrum") |
| 23 | lock = param.float(0.14, title = "Lock share", min = 0.07, max = 0.5, step = 0.01, group = "Spectrum") |
| 24 | trend = param.int(150, title = "Trend EMA", min = 20, max = 1000, group = "Trend") |
| 25 | stop_k = param.float(2.0, title = "Stop (ATR)", min = 0.5, max = 8, step = 0.1, group = "Risk") |
| 26 | x = close - ta.sma(close, smooth) |
| 27 | best = 0.0 |
| 28 | best_k = 0 |
| 29 | total = 0.0 |
| 30 | for k in 0..=15 { |
| 31 | per = p_min + k * p_step |
| 32 | ang = 2 * math.pi * bar.index / per |
| 33 | re = ta.ema(x * math.cos(ang), smooth) |
| 34 | im = ta.ema(x * math.sin(ang), smooth) |
| 35 | p = math.coalesce(re * re + im * im, 0) |
| 36 | total := total + p |
| 37 | if p > best { |
| 38 | best := p |
| 39 | best_k := k |
| 40 | } |
| 41 | } |
| 42 | share = total > 0 ? best / total : 0 |
| 43 | period = p_min + best_k * p_step |
| 44 | // the cycle wave: price detrended over 20 bars and smoothed over 5, read against the locked cycle |
| 45 | wave = ta.ema(close - ta.sma(close, 20), 5) |
| 46 | t = ta.ema(close, trend) |
| 47 | a = ta.atr(20) |
| 48 | locked = share >= lock |
| 49 | trough = locked and wave[1] < wave[2] and wave > wave[1] and wave < 0 |
| 50 | crest = locked and wave[1] > wave[2] and wave < wave[1] and wave > 0 |
| 51 | flat = position.size == 0 |
| 52 | if flat and trough and close > t { |
| 53 | order.market("L", side.long) |
| 54 | exit.bracket("L exit", entry = "L", stop = close - stop_k * a, target = close + 2 * math.abs(wave) + a) |
| 55 | } |
| 56 | if flat and crest and close < t { |
| 57 | order.market("S", side.short) |
| 58 | exit.bracket("S exit", entry = "S", stop = close + stop_k * a, target = close - 2 * math.abs(wave) - a) |
| 59 | } |
| 60 | persist half = 10 |
| 61 | if flat { half := math.max(math.round(period / 2), 3) } |
| 62 | if position.size != 0 and position.bars_held >= half { exit.close_all() } |
| 63 | risk.max_drawdown(25) |
| 64 | render.line(t, "Trend EMA", color = color.amber.300, width = 1.5) |
| 65 | render.mark(trough and close > t, shape = shape.circle, at = anchor.below_bar, color = color.cyan.300, size = 0.6, title = "Cycle trough") |
| 66 | render.mark(crest and close < t, shape = shape.circle, at = anchor.above_bar, color = color.fuchsia.300, size = 0.6, title = "Cycle crest") |
| 67 | persist tb = draw.table.create(anchor.top_right, 5, 2) |
| 68 | if bar.is_last { |
| 69 | pr.heading(tb, 2, "Spectral Cycle Timing", "Strategy · dominant-cycle entries") |
| 70 | pr.row(tb, 2, "Dominant period", str.format("{} bars", period), color.white) |
| 71 | pr.row(tb, 3, "Power share", str.format("{:.0%}", share), locked ? pr.BULL : pr.LABEL) |
| 72 | pr.row(tb, 4, "Phase", wave > 0 ? "Upper half" : "Lower half", color.white) |
| 73 | } |
| 74 |
Reviews
No reviews yet. Be the first to review this listing.