Expand description
Synthetic-data backtesting utilities aligned to AFML Chapter 13.
This module focuses on optimal trading-rule (OTR) search over profit-taking and stop-loss corridors by:
- calibrating an AR(1)/discrete O-U process from historical prices,
- generating many synthetic paths under that calibrated process,
- evaluating a PT/SL mesh on those paths, and
- detecting when the response surface lacks a stable optimum.
This is AFML §13.4–13.5 (Snippets 13.1–13.2): instead of picking barriers on the one
historical path, which overfits, fit a discrete Ornstein–Uhlenbeck process
P_t = intercept + phi P_{t-1} + sigma eps_t, simulate many paths, and read the whole
profit-taking/stop-loss response surface. §13.6’s result is that the surface flattens as
phi approaches 1, which detect_no_stable_optimum flags with this library’s own
heuristic thresholds (not AFML’s).
Conventions:
- Prices are levels, oldest first. Barriers are in price units (not multiples of
sigma), measured from the entry price; a barrier is touched when PnL reaches it (>=). - Every rule is a long entry at the first price of each path; negate the series for a short.
- A rule’s Sharpe ratio is the mean over the standard deviation of per-trade PnL, times
sqrt(annualization_factor), regardless of holding time. - Simulation uses a [
StdRng] seeded by the caller, so results are reproducible.
use openquant::synthetic_backtesting::{
evaluate_rule_on_paths, generate_ou_paths, OuProcessParams, TradingRule,
};
// phi = 0.9 around 100 (intercept = (1 - phi) * equilibrium), entered three points below.
let params = OuProcessParams {
phi: 0.9,
intercept: 10.0,
equilibrium: 100.0,
sigma: 1.0,
r_squared: 0.0,
stationary: true,
};
let paths = generate_ou_paths(params, 97.0, 2_000, 60, 11)?;
assert_eq!(paths, generate_ou_paths(params, 97.0, 2_000, 60, 11)?);
assert!(paths.iter().all(|p| p.len() == 60 && p[0] == 97.0));
// Expecting reversion, a wide stop beats a tight one.
let wide = TradingRule { profit_taking: 2.0, stop_loss: 8.0 };
let tight = TradingRule { profit_taking: 2.0, stop_loss: 0.5 };
let wide = evaluate_rule_on_paths(&paths, wide, 59, 1.0)?;
let tight = evaluate_rule_on_paths(&paths, tight, 59, 1.0)?;
assert!(wide.sharpe > tight.sharpe && wide.win_rate > 0.95);Structs§
- OtrSearch
Result - Result of an optimal-trading-rule search.
- OuProcess
Params - Parameters of the discrete O-U (AR(1)) process
P_t = intercept + phi P_{t-1} + sigma eps. - Rule
Surface Point - A trading rule’s performance across the simulated paths.
- Stability
Criteria - Thresholds for
detect_no_stable_optimum. These are this library’s heuristics, not AFML’s; the defaults arephi >= 0.97, margin 0.20, surface deviation 0.10 and best Sharpe 0.30 (the Python binding uses different defaults). - Stability
Diagnostics - Result of
detect_no_stable_optimum. - Synthetic
Backtest Config - Configuration of
run_synthetic_otr_workflow. - Trading
Rule - A profit-taking / stop-loss pair, both positive widths in price units from the entry.
Enums§
- Synthetic
Backtest Error - Errors returned by the synthetic-backtesting functions.
Functions§
- calibrate_
ou_ params - Fits the discrete O-U process to a price series by OLS of
P_tonP_{t-1}(AFML §13.5.1, step 1). - detect_
no_ stable_ optimum - Flags a response surface without a stable optimum (the flattening of AFML §13.6).
- evaluate_
rule_ on_ paths - Evaluates one profit-taking/stop-loss rule on every path (AFML §13.5.1, step 3).
- generate_
ou_ paths - Simulates
n_pathsO-U paths ofhorizonpoints each, starting atinitial_price(AFML §13.5.1, step 2). - run_
synthetic_ otr_ workflow - End-to-end optimal-trading-rule search: calibrate on
historical_prices, simulate, and search the configured grid (AFML §13.5). - search_
optimal_ trading_ rule - Evaluates every
(profit_taking, stop_loss)combination of the grids onpathsand diagnoses the resulting surface (AFML §13.5.1, Snippets 13.1–13.2).