Pro Feature. Requires a Pro or Ultra subscription. Get started at api.mathematicalcompany.com
Change Point Detection
Horizon implements Bayesian Online Change Point Detection (BOCPD) using the Adams-MacKay algorithm, entirely in Rust. The detector maintains a full run-length distribution and updates it in O(T) per observation, making it suitable for real-time trading pipelines.Bayesian BOCPD
Full posterior over run lengths. No fixed window size or threshold tuning required.
O(T) Online Updates
Per-observation update cost is linear in the current run length. Pruning keeps it bounded.
Conjugate Prior
Normal-Inverse-Gamma conjugate prior for Gaussian observations. Exact Bayesian inference.
Pipeline Integration
Drop
hz.bocpd_detector() into any pipeline. Injects change probability and run lengths into ctx.params.BocpdDetector
The core change point detector. It maintains a posterior distribution over run lengths (how many observations since the last change point) and updates it with each new observation.Constructor
| Parameter | Type | Default | Description |
|---|---|---|---|
hazard_rate | float | 250.0 | Expected number of observations between change points. Higher = fewer expected changes. |
mu0 | float | 0.0 | Prior mean for the Normal-Inverse-Gamma conjugate |
kappa0 | float | 1.0 | Prior precision scaling. Higher = stronger prior on the mean. |
alpha0 | float | 1.0 | Shape parameter for the Inverse-Gamma variance prior |
beta0 | float | 1.0 | Rate parameter for the Inverse-Gamma variance prior |
The hazard rate controls sensitivity. A hazard rate of 250 means the detector expects roughly one change point every 250 observations. Lower values make the detector more sensitive (more false positives); higher values make it more conservative.
update()
Process a single observation and update the run-length distribution.| Parameter | Type | Description |
|---|---|---|
observation | float | New data point (e.g., price, return, or spread value) |
BocpdResult object.
BocpdResult Type
| Field | Type | Description |
|---|---|---|
change_probability | float | Posterior probability that a change point occurred at this observation |
most_likely_run_length | int | Most probable run length (observations since last change) |
run_length_distribution | list[float] | Full posterior over run lengths (sums to 1.0) |
State Access Methods
| Method | Returns | Description |
|---|---|---|
run_length_distribution() | list[float] | Current posterior over run lengths |
most_likely_run_length() | int | MAP estimate of the current run length |
change_probability() | float | P(change point) at the most recent observation |
reset() | None | Reset detector to prior state, clearing all history |
Pipeline Integration
hz.bocpd_detector
Creates a pipeline function that runs BOCPD on each tick and injects change point statistics intoctx.params.
| Parameter | Type | Default | Description |
|---|---|---|---|
feed | str | None | Feed name to read prices from. None = first available. |
hazard_rate | float | 250.0 | Expected observations between change points |
mu0 | float | 0.0 | Prior mean |
kappa0 | float | 1.0 | Prior precision scaling |
alpha0 | float | 1.0 | Inverse-Gamma shape |
beta0 | float | 1.0 | Inverse-Gamma rate |
threshold | float | 0.5 | Change probability threshold for flagging a change point |
param_name | str | "bocpd" | Key in ctx.params |
Injected Parameters
| Key | Type | Description |
|---|---|---|
ctx.params["bocpd"]["change_probability"] | float | Posterior probability of a change point |
ctx.params["bocpd"]["run_length"] | int | Most likely run length |
ctx.params["bocpd"]["is_change_point"] | bool | True if change_probability exceeds threshold |
ctx.params["bocpd"]["n_changes"] | int | Cumulative count of detected change points |
Examples
Regime Change Detection
Use BOCPD to detect structural breaks in prediction market prices:BOCPD with Markov Regime Detection
Combine BOCPD with HMM for robust regime classification:Mathematical Background
Adams-MacKay Algorithm
Adams-MacKay Algorithm
BOCPD maintains a distribution over run lengths r_t (the number of observations since the last change point). At each step:
- Growth probability:
P(r_t = r_{t-1} + 1)— the run continues - Change probability:
P(r_t = 0)— a new segment begins
Normal-Inverse-Gamma Conjugate
Normal-Inverse-Gamma Conjugate
Within each run, observations are modeled as draws from a Gaussian with unknown mean and variance. The Normal-Inverse-Gamma prior (mu0, kappa0, alpha0, beta0) is conjugate, meaning the posterior after observing data has the same parametric form with updated parameters:
- kappa_n = kappa0 + n
- mu_n = (kappa0 * mu0 + sum(x)) / kappa_n
- alpha_n = alpha0 + n/2
- beta_n = beta0 + 0.5 * (sum(x^2) - kappa_n * mu_n^2 + kappa0 * mu0^2)
Hazard Rate Selection
Hazard Rate Selection
The hazard rate lambda sets the prior expected run length. For prediction markets:
- lambda = 50-100: Sensitive to rapid shifts (news events, poll releases)
- lambda = 200-500: Moderate sensitivity for daily regime changes
- lambda = 1000+: Conservative, only detects major structural breaks