Skip to content

Adding a Strategy

Every strategy in Gordon follows a strict promotion flow from initial research to live trading. No shortcuts. A strategy that skips validation steps will eventually fail in production.

Promotion flow

Research idea (gordon-lab, Python)
    |
    v
Level 1 — Signal quality gate
    |  Sharpe > 0.5, walk-forward consistency > 50%
    v
Level 2 — Realistic simulation
    |  Max DD < 30%, positive CAGR, 1-bar delay enforced
    v
Walk-forward validation (rolling windows, no random splits)
    v
Ablation analysis (every component must prove lift)
    v
Multi-asset validation (BTC, ETH, SOL minimum)
    v
Rust implementation in gordon-strategy
    v
Backtest via gordon-manager BFF (byte-parity gate)
    v
Binance testnet validation (30+ trades minimum)
    v
Micro live allocation (progressive)
    v
Full live allocation

File layout (mandatory)

The gordon-strategy CLAUDE.md mandates a specific directory structure. Every strategy must follow it, no exceptions:

gordon-strategy/
└── src/
    └── engines/
        └── <name>/
            ├── mod.rs       # Strategy implementation (required)
            ├── tests.rs     # Unit + parity tests (required)
            └── fixtures.rs  # Golden-file fixtures (if needed)

No flat <name>.rs peers at the parent level. No orphan fixture files outside the strategy folder.

Implement the Strategy trait

rust
use gordon_strategy::{Strategy, MarketContext, Signal, SizeHint, StrategyError, StrategyParams};

pub struct MyStrategy {
    params: MyStrategyParams,
    // internal state
}

impl Strategy for MyStrategy {
    fn name(&self) -> &'static str { "my_strategy" }
    fn params_schema() -> serde_json::Value { /* JSON schema */ }

    fn evaluate(
        &mut self,
        ctx: &MarketContext,
    ) -> Result<Option<Signal>, StrategyError> {
        // pure math over ctx.klines, ctx.funding_rates, ctx.open_interest
        // emit Signal only on entry/flip, not every bar
        // SizeHint::VolatilityTarget or SizeHint::Fixed
    }
}

Key constraints from gordon-strategy invariants:

  • No I/O, no HTTP, no exchange dependency, no tokio::spawn
  • No #[allow(...)] — fix the warning
  • Deterministic: same candles + same params must produce byte-identical Signal sequences
  • No SystemTime::now(), no rand::*, no non-deterministic iteration on the evaluation path

Register in StrategyRegistry

Add the strategy to gordon-strategy/src/engines/registry.rs:

rust
registry.register("my_strategy", || Box::new(MyStrategy::new(Default::default())));

Both gordon-bot (live loop) and gordon-manager (backtest replay) share this registry at startup.

Walk-forward validation pipeline (gordon-lab)

Research and validation happen in gordon-lab (Python). Provide gordon-lab with:

  1. Strategy parameters — the tuned values from research
  2. Signal logic — a Python reference implementation of the entry/exit conditions
  3. Risk parameters — stop loss formula, position sizing rules

gordon-lab runs:

  • Level 1: signal quality test (no risk management, full allocation, measure Sharpe + WF consistency)
  • Level 2: realistic simulation with the risk management system (stop loss, ATR-based sizing, 1-bar delay)
  • Walk-forward: rolling windows, never random splits
  • Ablation: test each overlay and filter independently

Pass criteria before proceeding to Rust implementation:

  • Sharpe > 0.5
  • Walk-forward consistency > 50%
  • Max drawdown < 30%
  • Positive CAGR
  • Multi-asset validation (BTC, ETH, SOL at minimum)

Backtest via gordon-manager BFF

After implementing in Rust, verify the backtest output matches gordon-lab's Python output:

bash
curl -X POST http://localhost:8083/backtest \
  -H "Authorization: Bearer dev-operator-token" \
  -H "Content-Type: application/json" \
  -d '{
    "strategy": "my_strategy",
    "symbol": "BTCUSDT",
    "timeframe": "1d",
    "from_ts": 1704067200000,
    "to_ts": 1735689600000,
    "params": { ... }
  }'

The BacktestExecution engine uses the same Strategy::evaluate path as the live bot. If the backtest output differs from gordon-lab's Python output, fix the Rust implementation — not the Python reference.

Binance testnet validation

Before any live capital:

  1. Deploy the strategy to a testnet gordon-bot container (configure BINANCE_TESTNET=true)
  2. Run for at least 30 trades
  3. Compare fill behavior, SL triggering, and PnL to backtest expectations
  4. Review logs for anomalies via gordon-console or Grafana

There is no paper-trading mode in v7. Binance testnet is the forward-test gate.

Promote to live

After testnet validation is clean:

  1. Start with a micro allocation (1% of account)
  2. Monitor for 2–4 weeks against testnet behavior
  3. Scale to full allocation once live behavior matches expectations

Checklist

Before submitting a strategy for production promotion:

  • [ ] Level 1 test passed (Sharpe > 0.5, WF consistency > 50%)
  • [ ] Level 2 test passed (max DD < 30%, positive CAGR, 1-bar delay)
  • [ ] Walk-forward validated with rolling windows (not random splits)
  • [ ] Ablation analysis completed (every component justified)
  • [ ] Multi-asset validation (BTC, ETH, SOL minimum)
  • [ ] Strategy contract documented (parameters, signal logic, risk params)
  • [ ] Results net-of-fees (all results include transaction costs and funding)
  • [ ] Rust implementation follows mandatory directory layout
  • [ ] Registered in StrategyRegistry
  • [ ] Byte-parity test passes (cargo test in gordon-strategy)
  • [ ] Binance testnet validation complete (30+ trades)

Gordon — keep compounding without blowing up