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
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:
| Layer | Responsibility |
|---|---|
| Entry signal | Strategy-specific indicator logic fires. |
| Overlay pipeline | Funding z-score, OI regime, vol-targeting vetoes filter low-conviction signals. |
| Long-only filter | Optional per-bot config — shorts are dropped before reaching the bus. |
| SizeHint | Volatility targeting scales the notional. |
| OrderIntent | Emitted 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:
| Strategy | Type |
|---|---|
BasisRevertStrategy | Basis reversion (spot vs perp) |
BollingerBreakoutStrategy | Bollinger band breakout |
FundingArbStrategy | Funding rate arbitrage |
HurstRegimeSwitcherStrategy | Hurst exponent regime detection |
OpeningRangeBreakoutStrategy | Opening range breakout |
PairsMrStrategy | Pairs mean reversion |
RsiMeanReversionStrategy | RSI mean reversion |
TsMomStrategy | Time-series momentum |
VolumeSpikeBreakoutStrategy | Volume spike breakout |
VpinImpulseStrategy | VPIN-triggered impulse |
VwapStretchMrStrategy | VWAP 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_traitveneer. - No gordon-exchange dependency. Strategies consume already-fetched candles and snapshots expressed as
gordon_domain::types::*. BacktestExecutionis deterministic: noSystemTime::now(), norand::*, no non-deterministicHashMapiteration 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.
Related
- Overlays — funding z-score, OI regime, GEX, macro, vol-targeting vetoes, sentiment regime.
- Backtesting —
BacktestExecution, walk-forward protocol, ablation. - Execution — how
OrderIntentflows from gordon-bot to gordon-executor. - /strategies/ — per-strategy detail pages.