Module Algostream_strategy.Strategy

The strategy contract.

The layers below produce every ingredient of a statistical-arbitrage decision — cointegration, spread z-scores, hedge ratios, GARCH, VaR — but nothing that decided anything. Pairs.Snapshot.signal was computed, published, and read by exactly one caller: Snapshot.to_string, which printed it. This module type is where the decision goes.

Deliberately dependency-light. algostream.strategy does not depend on algostream.backtest, and it does not depend on algostream.infrastructure.event_bus. A live runner can therefore implement against S without linking the fill simulator, and the backtest can drive it without an event bus. That is the whole reason this is its own library.

Rules an implementation must honour. These are contract, not style:

Every tunable is a float and must be reachable through params_of_assoc / params_to_assoc, because that pair is how algostream.optimization moves through the parameter space without knowing the concrete params type. An integer dimension is a float with an integrality check inside params_of_assoc. A parameter not reachable that way is a parameter the optimizer cannot tune.

type subscription =
  1. | Symbol of string
  2. | Bars of {
    1. symbol : string;
    2. interval_ns : int64;
    }
  3. | Pair of {
    1. pair : Algostream_pairs.Pair_id.t;
    2. y_symbol : string;
    3. x_symbol : string;
    }
    (*

    The engine drives a Pairs.Per_pair.t inline for this pair and emits Event.Pair_snapshot.

    *)
  4. | Timer_every of {
    1. interval_ns : int64;
    2. tag : string;
    }

What market data the engine must feed this strategy.

module type S = sig ... end
type packed = (module S)

First-class packed strategy, for code that holds a heterogeneous collection.

Helpers for implementations

val require : (string * float) list -> string -> (float, string) Stdlib.result

Look up a required parameter, returning a uniform error message when absent.

val require_in : (string * float) list -> string -> lo:float -> hi:float -> (float, string) Stdlib.result

As require, plus an inclusive range check.

val require_int : (string * float) list -> string -> (int, string) Stdlib.result

As require, plus a check that the value is integral.