Skip to content

Strategies

A strategy in Gordon is pure math: candles and market context go in, a Signal comes out. There is no order management, no position sizing, and no risk logic inside a strategy. Those concerns are handled by separate layers. The strategy crate (gordon-strategy) has no I/O, no database, no exchange dependency — it is a library of deterministic functions over gordon_domain::types::*.

Strategy trait

rust
pub trait Strategy: Send + Sync {
    fn name(&self) -> &str;
    fn params_schema(&self) -> serde_json::Value;
    fn evaluate(&self, ctx: &MarketContext) -> Result<Signal, StrategyError>;
}

MarketContext carries the candle window, funding rate snapshot, open interest snapshot, and any precomputed indicators the strategy needs. Signal is an enum: Long, Short, Flat, or Hold (no change from current position).

Both gordon-bot (live loop) and gordon-manager (backtest replay) call Strategy::evaluate through the same code path. This is the backtest=live invariant.

Signal pipeline

Every signal passes through the same pipeline before it becomes an intent:

LayerResponsibility
Entry signalStrategy-specific indicator logic fires.
Overlay pipelineFunding z-score, OI regime, vol-targeting vetoes filter low-conviction signals.
Long-only filterOptional per-bot config — shorts are dropped before reaching the bus.
SizeHintVolatility targeting scales the notional.
OrderIntentEmitted to intents.executor if not vetoed.

Validated strategies

Three strategies have completed walk-forward ablation and are candidates for live deployment:

Supertrend 3.0

ATR-derived price-action bands. The indicator flips direction when price crosses the band. Best timeframes: D1 to 8h. Walk-forward Sharpe: 0.90.

EWMAC ensemble

Exponentially weighted moving average crossover with four speed pairs combined. Short-horizon pairs (fast) and long-horizon pairs (slow) are averaged. Best timeframes: 6h to 1h. Walk-forward Sharpe: 0.90.

PSAR tight

Parabolic Stop and Reverse with a tight acceleration factor. Best timeframes: W1 to D1. Walk-forward Sharpe range: 0.69 to 1.43 depending on pair and timeframe.

Retired strategy

Donchian ensemble — median walk-forward Sharpe 0.08 across all timeframes. Not worth the complexity. Removed from the validated set in the v4 retrospective. Code is retained in the crate for historical reference but the strategy is not registered in StrategyRegistry for new bots.

Strategies under validation (4.6.x)

Eleven strategies were added in the 4.6.x line and are currently in validation. Walk-forward ablation results are pending:

StrategyType
BasisRevertStrategyBasis reversion (spot vs perp)
BollingerBreakoutStrategyBollinger band breakout
FundingArbStrategyFunding rate arbitrage
HurstRegimeSwitcherStrategyHurst exponent regime detection
OpeningRangeBreakoutStrategyOpening range breakout
PairsMrStrategyPairs mean reversion
RsiMeanReversionStrategyRSI mean reversion
TsMomStrategyTime-series momentum
VolumeSpikeBreakoutStrategyVolume spike breakout
VpinImpulseStrategyVPIN-triggered impulse
VwapStretchMrStrategyVWAP stretch mean reversion

None of these strategies enter StrategyRegistry as production candidates until they pass the walk-forward protocol.

Strategy directory layout

Every strategy lives under strategies/<name>/mod.rs even for single-file implementations. Sibling files (fixtures.rs, tests.rs) sit next to mod.rs inside the strategy folder. No flat <name>.rs peers; no orphan fixture files at the parent level.

Backtest=live invariant

The single most important invariant in the crate: the same Strategy::evaluate code path is called by gordon-bot's live loop and gordon-manager's backtest replay. There is exactly one implementation per strategy. Parameterize, never fork.

This invariant is enforced by a CI gate: gordon-strategy/tests/it_backtest_live_byte_parity.rs (DP-19). The test routes a 200-candle BTCUSDT/1h fixture through both a BacktestExecution-driven replay and a hand-rolled evaluate_tick mock harness, then asserts the resulting intent sequence is byte-identical on (side, qty, sl_price, entry_price, candle_ts).

StrategyRegistry

gordon-bot and gordon-manager both construct a StrategyRegistry at startup. The registry maps strategy names (as stored in bot_configs.strategy_name) to Box<dyn Strategy> instances. A bot config that references an unknown strategy name will fail to start with a registry lookup error — not silently skip signals.

Invariants

  • No I/O in gordon-strategy. No database, no HTTP, no tokio tasks beyond the async_trait veneer.
  • No gordon-exchange dependency. Strategies consume already-fetched candles and snapshots expressed as gordon_domain::types::*.
  • BacktestExecution is deterministic: no SystemTime::now(), no rand::*, no non-deterministic HashMap iteration on the fill path.
  • Backtest=live byte-parity is a CI gate, not an aspirational comment.
  • Walk-forward only — no random train/test splits on time series.
  • Overlays — funding z-score, OI regime, GEX, macro, vol-targeting vetoes, sentiment regime.
  • BacktestingBacktestExecution, walk-forward protocol, ablation.
  • Execution — how OrderIntent flows from gordon-bot to gordon-executor.
  • /strategies/ — per-strategy detail pages.

Gordon — keep compounding without blowing up