Pro Feature. Requires a Pro or Ultra subscription. Get started at api.mathematicalcompany.com
Optimal Stopping
When should you exit a prediction market position? Holding too long risks giving back profits; exiting too early leaves money on the table. The Longstaff-Schwartz algorithm solves this as an optimal stopping problem: at each time step, compare the immediate exit value to the expected value of continuing. Horizon implements the full backward-induction algorithm in Rust, including path generation and basis-function regression.Path Generation
hz.generate_gbm_paths() simulates geometric Brownian motion paths for Monte Carlo valuation.Longstaff-Schwartz
hz.longstaff_schwartz() computes the optimal stopping policy via backward regression on simulated paths.Live Decision
hz.should_exit() evaluates the learned policy against current state to produce a hold/exit signal.Pipeline Integration
hz.exit_optimizer() runs the stopping policy each cycle and injects exit signals into your strategy context.hz.generate_gbm_paths
Generate simulated price paths using geometric Brownian motion for use in the Longstaff-Schwartz algorithm.| Parameter | Type | Description |
|---|---|---|
s0 | float | Initial price (e.g., current market probability) |
mu | float | Annualized drift rate |
sigma | float | Annualized volatility |
t | float | Time horizon in days |
n_steps | int | Number of time steps per path |
n_paths | int | Number of Monte Carlo paths to simulate |
seed | int or None | Optional RNG seed. If None, uses entropy-based seed |
list[list[float]]: matrix of shape (n_paths, n_steps + 1) with simulated prices. Prices are clamped to [0.01, 0.99] to respect prediction market bounds.
hz.longstaff_schwartz
Compute the optimal stopping policy using backward regression on simulated paths. At each time step, the algorithm regresses the continuation value against polynomial basis functions of the current state, then compares the immediate exercise value to the fitted continuation value.| Parameter | Type | Description |
|---|---|---|
paths | list[list[float]] | Simulated price paths from generate_gbm_paths() |
entry_price | float | Price at which you entered the position |
transaction_cost | float | Round-trip transaction cost (spread + fees) |
discount_rate | float | Daily discount rate. Use 0.0 for prediction markets (no time value of money) |
basis_degree | int | Polynomial degree for the regression basis (2 or 3 recommended) |
StopPolicy Type
| Field | Type | Description |
|---|---|---|
thresholds | list[float] | Optimal exit threshold at each time step. Exit when price >= threshold[t] |
expected_value | float | Expected value of following the optimal policy from t=0 |
immediate_value | float | Value of exiting immediately at current price |
action | str | "hold" or "exit" based on current state vs. threshold |
confidence | float | Confidence in the recommendation (0.0 to 1.0), based on how far the current price is from the threshold |
coefficients | list[list[float]] | Regression coefficients at each time step (for advanced inspection) |
The
thresholds list has one entry per time step. The threshold typically decreases over time: as expiry approaches, the option to wait becomes less valuable, so you become willing to exit at lower prices.hz.should_exit
Evaluate the learned stopping policy against a current price and time fraction to produce a hold/exit signal. This is the lightweight evaluation function for use in live trading.| Parameter | Type | Description |
|---|---|---|
policy | StopPolicy | Learned stopping policy from longstaff_schwartz() |
current_price | float | Current market price |
time_fraction | float | Fraction of time elapsed (0.0 = start, 1.0 = expiry) |
| Field | Type | Description |
|---|---|---|
action | str | "hold" or "exit" |
confidence | float | Distance from threshold normalized to [0, 1] |
threshold | float | Exit threshold at this time step |
edge | float | current_price - threshold. Positive means price is above the exit threshold |
Pipeline Integration
Thehz.exit_optimizer() pipeline function evaluates the stopping policy each cycle and injects exit signals into ctx.params["exit_signal"].
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
feed | str | required | Feed name to read current price from |
entry_price | float | required | Price at which the position was entered |
transaction_cost | float | 0.02 | Round-trip transaction cost |
horizon_days | float | 30.0 | Time horizon in days until market resolution |
n_paths | int | 10000 | Number of Monte Carlo paths for policy computation |
recompute_every | int | 500 | Recompute the policy every N cycles (uses updated volatility estimate) |
Mathematical Background
Longstaff-Schwartz Algorithm
Longstaff-Schwartz Algorithm
The Longstaff-Schwartz (2001) algorithm solves the American option stopping problem via backward induction on simulated paths:
- Generate N price paths using Monte Carlo simulation.
- At the final time step T, the exercise value is known: max(S_T - K, 0) for a call (or simply S_T - entry for a position exit).
- Moving backward from T-1 to 0: for paths where immediate exercise has positive value, regress the discounted future continuation value against polynomial basis functions of the current price.
- At each step, exercise if the immediate value exceeds the fitted continuation value.
- The optimal stopping time for each path is recorded, and the policy is the set of regression coefficients.
Why Not a Fixed Threshold?
Why Not a Fixed Threshold?
A fixed take-profit threshold ignores the time dimension. Early in a market’s life, a position at 0.60 (entered at 0.50) might be worth holding because there is time for further appreciation. Near expiry, the same position should be exited because the remaining optionality is small. The Longstaff-Schwartz approach produces a time-varying threshold that accounts for this.
Basis Functions
Basis Functions
The regression uses polynomial basis functions of the current price: 1, S, S^2, S^3, and so on. A degree of 3 is usually sufficient. Higher degrees can overfit to Monte Carlo noise. The Rust implementation uses Householder QR decomposition for numerically stable least-squares regression.