Skip to content
GeneratedGenerated from the built extension by scripts/generate_python_api_reference.py; CI fails if it is stale.

openquant.synthetic_bt

Compiled from Rust (crates/pyopenquant). Signatures and types are from the shipped .pyi stubs; descriptions are the functions’ own docstrings, as help() shows them. All Python modules.

Functions

calibrate_ou_params

calibrate_ou_params(prices: Sequence[float]) -> Any

Fit a discrete Ornstein-Uhlenbeck (AR(1)) process to a price series.

AFML §13.5.1, step 1. OLS of P_t on P_{t-1} gives P_t = intercept + phi * P_{t-1} + sigma * eps_t; sigma is the sample standard deviation of the residuals and equilibrium is intercept / (1 - phi), or the mean price when phi is within 1e-12 of 1.

Parameters

prices : list[float]

Price levels, oldest first; at least three, all finite.

Returns

dict[str, Any]

Keys phi, intercept, equilibrium, sigma, r_squared (floats) and stationary (bool, abs(phi) < 1, true even for phi = 0.999).

Raises

ValueError

If there are fewer than three prices, a price is not finite, the lagged prices are constant, or the fit is exact (zero residual deviation).

detect_no_stable_optimum

detect_no_stable_optimum(
    response_surface: Sequence[tuple[float, float, float, float, float, float, float]],
    estimated_phi: float,
    random_walk_phi_threshold: float,
    min_peak_margin: float,
    min_surface_std: float,
    min_best_sharpe: float,
) -> Any

Flag a PT/SL response surface that lacks a stable optimum (the flattening of AFML §13.6).

The verdict is true when the process is near a random walk (abs(estimated_phi) >= random_walk_phi_threshold) and the peak is weak (best - median Sharpe < min_peak_margin) or the surface flat (Sharpe standard deviation < min_surface_std), or when the best Sharpe is below min_best_sharpe and the peak is weak. The thresholds are this library's heuristics, not AFML's; the Rust defaults are 0.97, 0.20, 0.10 and 0.30.

Parameters

response_surface : list[tuple[float, float, float, float, float, float, float]]

One (profit_taking, stop_loss, sharpe, mean_return, std_return, win_rate, avg_holding_steps) row per grid point; only sharpe enters the diagnosis.

estimated_phi : float

The AR(1) coefficient of the process, e.g. from calibrate_ou_params.

random_walk_phi_threshold : float

abs(phi) at or above this counts as near a random walk.

min_peak_margin : float

Best-minus-median Sharpe below this is a weak peak.

min_surface_std : float

Sharpe standard deviation below this is a flat surface.

min_best_sharpe : float

Best Sharpe below this is a weak best rule.

Returns

dict[str, Any]

Keys no_stable_optimum (bool), reason (str), best_sharpe, median_sharpe, peak_margin, surface_std and estimated_phi (floats).

Raises

ValueError

If response_surface is empty.

evaluate_rule_on_paths

evaluate_rule_on_paths(
    paths: Sequence[Sequence[float]],
    profit_taking: float,
    stop_loss: float,
    max_holding_steps: int,
    annualization_factor: float,
) -> Any

Evaluate one profit-taking/stop-loss rule on every path.

AFML §13.5.1, step 3. Each path is a long entry at its first price; the trade exits at the first step (up to min(max_holding_steps, len(path) - 1)) whose PnL is >= profit_taking or <= -stop_loss, otherwise at that last step. Barriers are in price units from the entry. The Sharpe ratio is mean_return / std_return * sqrt(annualization_factor) over per-trade PnL, regardless of holding time (0 when the deviation is 0).

Parameters

paths : list[list[float]]

Price paths, e.g. from generate_ou_paths; each needs at least two finite points.

profit_taking : float

Profit-taking width in price units; must be > 0.

stop_loss : float

Stop-loss width in price units, as a positive number; must be > 0.

max_holding_steps : int

Maximum holding period in steps; must be > 0.

annualization_factor : float

Multiplier whose square root scales the Sharpe ratio (1.0 for per trade); finite, > 0.

Returns

dict[str, Any]

Keys profit_taking, stop_loss, sharpe, mean_return (mean per-trade PnL), std_return (sample standard deviation of PnL), win_rate (share of trades with positive PnL) and avg_holding_steps, all floats.

Raises

ValueError

If paths is empty, a barrier is not positive, max_holding_steps is 0, annualization_factor is not finite and positive, a path has fewer than two points, or a path contains a non-finite value.

generate_ou_paths

generate_ou_paths(
    phi: float,
    intercept: float,
    equilibrium: float,
    sigma: float,
    r_squared: float | None = None,
    stationary: bool | None = None,
    initial_price: float = 100.0,
    n_paths: int = 1000,
    horizon: int = 252,
    seed: int = 42,
) -> list[list[float]]

Simulate O-U price paths from given process parameters.

AFML §13.5.1, step 2. Each path starts at initial_price and evolves as P_t = intercept + phi * P_{t-1} + sigma * eps_t with standard-normal innovations from an RNG seeded with seed, so the same seed reproduces the same paths. Only intercept, phi and sigma drive the simulation; when building parameters by hand keep intercept = (1 - phi) * equilibrium. Pass initial_price, n_paths, horizon and seed by keyword; their defaults are those of run_synthetic_otr_workflow.

Parameters

phi : float

Autoregressive coefficient.

intercept : float

Regression intercept.

equilibrium : float

Long-run mean; must be finite but is not used by the simulation.

sigma : float

Innovation standard deviation in price units; must be >= 0.

r_squared : float | None, default None

Deprecated: passing it emits a DeprecationWarning. It is a diagnostic of the calibration regression, not a parameter of the process, so it cannot affect the paths (AFML §13.5.1 simulates from the fitted coefficients and residual deviation alone).

stationary : bool | None, default None

Deprecated: passing it emits a DeprecationWarning. It is abs(phi) < 1, which phi already determines. To simulate a calibrated fit, pass its phi, intercept, equilibrium and sigma only.

initial_price : float, default 100.0

Entry price of every path.

n_paths : int, default 1000

Number of paths; must be > 0.

horizon : int, default 252

Points per path, including the entry; must be >= 2.

seed : int, default 42

Seed of the simulation RNG.

Returns

list[list[float]]

n_paths paths of horizon prices each, each starting at initial_price.

Raises

ValueError

If initial_price is not finite, n_paths is 0, horizon < 2, phi, intercept, equilibrium or sigma is not finite, or sigma is negative.

run_synthetic_otr_workflow

run_synthetic_otr_workflow(
    historical_prices: Sequence[float],
    initial_price: float = 100.0,
    n_paths: int = 1000,
    horizon: int = 252,
    seed: int = 42,
    profit_taking_grid: Sequence[float] | None = None,
    stop_loss_grid: Sequence[float] | None = None,
    max_holding_steps: int = 252,
    annualization_factor: float = 252.0,
    random_walk_phi_threshold: float | None = None,
    min_peak_margin: float | None = None,
    min_surface_std: float | None = None,
    min_best_sharpe: float | None = None,
) -> Any

End-to-end optimal-trading-rule search on synthetic O-U paths.

AFML §13.4-13.5 (Snippets 13.1-13.2): calibrates an O-U process on historical_prices (calibrate_ou_params), simulates n_paths paths from initial_price (generate_ou_paths), evaluates every (profit_taking, stop_loss) pair of the grids (evaluate_rule_on_paths), and diagnoses the surface (detect_no_stable_optimum, with the calibrated phi). Barriers are in price units; every rule is a long entry.

Parameters

historical_prices : list[float]

Price levels to calibrate on, oldest first; at least three, all finite.

initial_price : float, default 100.0

Entry price of every simulated path.

n_paths : int, default 1000

Number of simulated paths.

horizon : int, default 252

Points per path, including the entry; must be >= 2.

seed : int, default 42

Seed of the simulation RNG.

profit_taking_grid : list[float] | None, default None

Profit-taking widths to try, each finite and > 0. None uses 0.25, 0.5, ..., 5.0.

stop_loss_grid : list[float] | None, default None

Stop-loss widths to try, as positive numbers (distance below the entry), each finite and > 0. None uses 0.25, 0.5, ..., 5.0.

max_holding_steps : int, default 252

Maximum holding period in steps (capped by the path length).

annualization_factor : float, default 252.0

Multiplier whose square root scales each rule's Sharpe ratio.

random_walk_phi_threshold : float | None, default None

See detect_no_stable_optimum; None uses the Rust default 0.97.

min_peak_margin : float | None, default None

See detect_no_stable_optimum; None uses the Rust default 0.20.

min_surface_std : float | None, default None

See detect_no_stable_optimum; None uses the Rust default 0.10.

min_best_sharpe : float | None, default None

See detect_no_stable_optimum; None uses the Rust default 0.30.

Returns

dict[str, Any]

Keys params (dict as returned by calibrate_ou_params), best_rule (dict with profit_taking and stop_loss), best_point (dict as returned by evaluate_rule_on_paths), response_surface (list of such dicts, one per grid point, sorted by Sharpe then mean return, best first) and diagnostics (dict as returned by detect_no_stable_optimum).

Raises

ValueError

If a grid is empty or has a value that is not finite and positive, or calibration, simulation or evaluation rejects its input (see calibrate_ou_params, generate_ou_paths and evaluate_rule_on_paths).

search_optimal_trading_rule

search_optimal_trading_rule(
    phi: float,
    intercept: float,
    equilibrium: float,
    sigma: float,
    r_squared: float,
    stationary: bool,
    paths: Sequence[Sequence[float]],
    profit_taking_grid: Sequence[float],
    stop_loss_grid: Sequence[float],
    max_holding_steps: int,
    annualization_factor: float,
    random_walk_phi_threshold: float,
    min_peak_margin: float,
    min_surface_std: float,
    min_best_sharpe: float,
) -> Any

Search a PT/SL grid on given paths and diagnose the response surface.

AFML §13.5.1 (Snippets 13.1-13.2). Evaluates every (profit_taking, stop_loss) pair of the grids with evaluate_rule_on_paths, sorts the surface by Sharpe ratio (then mean return), and runs detect_no_stable_optimum with phi as the estimated coefficient. The process parameters are otherwise only echoed back in the result's params.

Parameters

phi : float

AR(1) coefficient used by the stability diagnosis.

intercept : float

Echoed in params.

equilibrium : float

Echoed in params.

sigma : float

Echoed in params.

r_squared : float

Echoed in params.

stationary : bool

Echoed in params.

paths : list[list[float]]

Price paths, e.g. from generate_ou_paths; each needs at least two finite points.

profit_taking_grid : list[float]

Profit-taking widths to try, in price units, each > 0.

stop_loss_grid : list[float]

Stop-loss widths to try, as positive numbers in price units, each > 0.

max_holding_steps : int

Maximum holding period in steps; must be > 0.

annualization_factor : float

Multiplier whose square root scales each rule's Sharpe ratio; finite and > 0.

random_walk_phi_threshold : float

See detect_no_stable_optimum.

min_peak_margin : float

See detect_no_stable_optimum.

min_surface_std : float

See detect_no_stable_optimum.

min_best_sharpe : float

See detect_no_stable_optimum.

Returns

dict[str, Any]

Keys params, best_rule, best_point, response_surface and diagnostics, as for run_synthetic_otr_workflow.

Raises

ValueError

If either grid is empty, a grid value is not positive, or evaluate_rule_on_paths rejects its input.