Module Algostream_strategy.Pairs_mean_reversion

Reference Strategy.S: market-neutral pairs mean reversion.

This is the first code in the project that acts on a Pairs.Snapshot.signal. It consumes the signal the pairs classifier already produces rather than reimplementing the classification — Pairs.Mean_reversion owns the hysteretic entry/exit/stop band logic, and duplicating it here would give two state machines that drift apart.

On Long_spread the strategy buys the y leg and sells beta_hedge · β of the x leg; on Short_spread, the mirror. Exit flattens both legs. Position size targets a fixed gross notional, capped as a fraction of NAV.

Screens. A signal is ignored unless the snapshot is ready, is cointegrated, has an ADF p-value at or below max_adf_pvalue, an absolute rolling correlation at or above min_abs_corr, and a half-life inside [min_half_life_bars, max_half_life_bars]. A pair that fails a screen while a position is open is flattened rather than held — the relationship the trade was predicated on has stopped being demonstrable.

Idempotence. The classifier repeats Long_spread for as long as the z-score sits past the band, which is many ticks. The strategy records the signal it last acted on per pair and returns no actions until the signal changes, so one crossing produces one entry.

type params = {
  1. target_gross_notional : float;
    (*

    per pair, in quote currency

    *)
  2. max_gross_pct_of_nav : float;
    (*

    hard cap on per-pair gross exposure

    *)
  3. beta_hedge : float;
    (*

    1.0 = beta-neutral, 0.0 = dollar-neutral

    *)
  4. min_half_life_bars : float;
  5. max_half_life_bars : float;
  6. max_adf_pvalue : float;
  7. min_abs_corr : float;
  8. use_limit_orders : float;
    (*

    < 0.5 market, otherwise limit at the near touch, passive

    *)
}

Everything else — default_params, params_of_assoc, param_bounds, create, on_event, on_stop, diagnostics — comes from Strategy.S. params is exposed concretely so callers can build one directly instead of going through the assoc list.

include Strategy.S with type params := params
val name : string
val version : string

Parameters

val default_params : params
val params_of_assoc : (string * float) list -> (params, string) Stdlib.result

Validate and build params from the optimizer's flat representation. Return Error with a human-readable reason rather than clamping silently — a search that quietly clamps reports scores for points it never actually evaluated.

val params_to_assoc : params -> (string * float) list
val param_bounds : (string * float * float) list

(name, lo, hi) per tunable dimension. algostream.optimization builds its default search space from this.

Lifecycle

type state
val create : params:params -> symbols:string list -> state
val subscriptions : state -> Strategy.subscription list
val on_event : state -> Context.t -> Event.t -> Action.t list

The callback. Returns the actions the strategy wants taken; the engine gates them against risk limits, assigns order ids, applies latency and routes them.

val on_stop : state -> Context.t -> Action.t list

Called once after the last market event. Emit flattening orders here if the strategy wants to end flat; the engine can also flatten unconditionally via its own config.

val diagnostics : state -> (string * float) list

Free-form counters surfaced in the backtest result — signals generated, entries skipped by a screen, and so on. Invaluable when a strategy does nothing and you need to know which gate closed.