v1.0.0 to v2.0.0
64 added, 22 removed. Audit A to A.
---
name: execution-cost-model-recalibration-cadence
description: >-
- Quantitative TCA engine for auditing execution transaction cost model (TCM) tracking error (RMSE), detecting systematic prediction bias, and executing recalibration cadences.
+ Quantitative TCA engine for auditing a pre-trade execution cost model's tracking error (RMSE) and systematic prediction bias against realized Implementation Shortfall, and refitting its spread and square-root impact coefficients by ordinary least squares under explicit sample-size and fit-quality gates.
domain: Execution Algorithms
subdomain: Transaction Cost Analysis (TCA) & Model Governance
- tags: ["execution-cost-model", "recalibration-cadence", "tca", "tracking-error", "rmse", "prediction-bias", "almgren-chriss"]
- brokers_frameworks: ["Square-Root Impact Model", "TCA Governance", "Python Dataclasses", "Least-Squares Regression"]
- version: "1.0.0"
+ tags: ["execution-cost-model", "recalibration-cadence", "tca", "tracking-error", "rmse", "prediction-bias", "square-root-impact-model"]
+ brokers_frameworks: ["Square-Root Impact Model", "TCA Governance", "Ordinary Least Squares", "Python Dataclasses"]
+ version: "2.0.0"
author: algo-trading-skills-contributors
license: Apache-2.0
---
## When to Use
- Use this skill in quantitative execution research, Transaction Cost Analysis (TCA), and portfolio management systems. Execution cost models (Almgren-Chriss, Square-Root Impact Rule) predict expected slippage ($\text{IS}_{\text{predicted}}$). As market regimes shift (volatility spikes, tick size changes, fee schedule updates), models experience tracking error drift ($\text{RMSE} > 3.5\text{ bps}$) and systematic prediction bias ($\bar{\epsilon} \ne 0$). This module enforces scheduled (weekly/monthly) and trigger-based model parameter recalibration cadences.
+ Use this skill in quantitative execution research, Transaction Cost Analysis (TCA), and portfolio construction where a pre-trade cost model feeds sizing, venue selection, or algo parameter choice. The audited model is the standard two-term decomposition
+ $$\text{IS}_{\text{pred}} = \eta \cdot \text{Spread}_{\text{bps}} + \gamma \cdot \sigma_{\text{bps}} \cdot \sqrt{\text{Qty} / \text{ADV}}$$
+
+ whose second term is the square-root impact law $I(Q) = Y \sigma \sqrt{Q/V}$ with $Y \sim 0.5\text{–}1.0$ (Tóth et al. 2011; Almgren et al. 2005). As market regimes shift — volatility spikes, tick-size regime changes, fee-schedule updates, venue mix changes — the fitted coefficients go stale, and the model develops tracking error ($\text{RMSE}$) and systematic bias ($\bar{\epsilon} \ne 0$) against realized IS. This module implements the **trigger arm** of a recalibration cadence: measure the drift, decide whether it warrants a refit, and produce refitted coefficients only when the sample can actually support them.
+
+ ## When NOT to Use
+
+ - **As the whole cadence.** This is the trigger arm only. The calendar arm — the scheduled weekly/monthly review, and the annual validation floor RTS 6 Art. 9 places on EU algorithmic trading firms — is a scheduler concern and is not implemented here.
+ - **To promote parameters straight to production.** The refit is scored **in-sample**, on the very trades that triggered it. `post_refit_rmse_bps` is an optimistic lower bound on live tracking error, not a forecast of it. Validate on a held-out or subsequent sample first.
+ - **To diagnose *why* the model drifted.** A tripped threshold is a symptom. If the error came from a venue outage, a fee change, or a handful of outlier parents, refitting bakes that event into $\eta$ and $\gamma$. Screen the sample before refitting; use `execution-slippage-attribution-timing-vs-sizing` to decompose where the shortfall actually came from.
+ - **On a sample that does not span order sizes and spread regimes.** If every trade has near-identical spread and participation, the two regressors are collinear and $\eta$ and $\gamma$ are not separately identifiable — the combined prediction can look fine while each coefficient is arbitrary. The engine refuses such a fit rather than returning it.
+ - **On incomplete executions.** The engine treats its input as an unordered sample of *completed* parent orders. Working or partially-filled orders leak incomplete outcomes into the fit.
+ - **As an optimal-execution scheduler.** This calibrates a *cost estimate*; it does not produce a trade schedule. For the schedule, see `execution-algo-twap-vwap-slicing` and `implementation-shortfall-minimization`.
+
## Prerequisites
- - Trade execution history ($N$ trades with `realized_is_bps`, `predicted_is_bps`, `order_qty`, `adv`, `spread_bps`, `volatility_pct`).
- - Active model parameters ($\eta_{\text{active}}$, $\gamma_{\text{active}}$).
- - Recalibration thresholds ($\text{RMSE}_{\text{max}} = 3.5\text{ bps}$, $|\bar{\epsilon}_{\text{max}}| = 1.5\text{ bps}$).
+ - Trade execution history of **completed** parent orders: `order_qty`, `adv_shares`, `spread_bps`, `volatility_daily_pct`, `realized_is_bps`.
+ - **Units contract.** The square-root law is dimensionally homogeneous — $\sigma$ must be in the same relative unit as the cost it predicts. This module works in bps, so `volatility_daily_pct` is **percent** ($1.5$ means $1.5\%$/day) and is converted internally to bps ($\times 100$). Supplying a decimal fraction ($0.015$) understates the impact term by $100\times$.
+ - Active model parameters ($\eta_{\text{active}}, \gamma_{\text{active}}$).
+ - Governance configuration: tracking-error limit, bias limit, minimum refit sample size. These are **defaults, not industry standards** — see `references/standards.md`.
## Workflow
- 1. **Model Performance Audit**:
- - Compute Tracking Error: $\text{RMSE} = \sqrt{\frac{1}{N} \sum (\text{IS}_{\text{realized}} - \text{IS}_{\text{predicted}})^2}$.
- - Compute Systematic Bias: $\bar{\epsilon} = \frac{1}{N} \sum (\text{IS}_{\text{realized}} - \text{IS}_{\text{predicted}})$.
- 2. **Recalibration Trigger Audit**:
- - If $\text{RMSE} > \text{RMSE}_{\text{max}}$ OR $|\bar{\epsilon}| > |\bar{\epsilon}_{\text{max}}| \implies$ Trigger Recalibration.
- 3. **Parameter Least-Squares Refitting**:
- - Refit impact coefficients ($\eta^*, \gamma^*$) using recent trade sample data:
- $$\text{IS}_{\text{realized}} = \eta \cdot \text{Spread} + \gamma \cdot \sigma \cdot \sqrt{\frac{\text{Qty}}{\text{ADV}}}$$
- 4. **Audit Report Generation**: Output structured `CostModelRecalibrationReport`.
+ 1. **Validate the sample before measuring anything.**
+ - **Decision point — reject non-finite values; never let them reach the metrics.** A single `NaN` in `realized_is_bps` propagates through the mean into both RMSE and bias, and every `nan > threshold` comparison evaluates False. Unvalidated, corrupt data reports the model as *stable* and silently suppresses the recalibration it should have triggered.
+ - Reject non-positive `order_qty` and `adv_shares` (a zero ADV is a division by zero, a negative quantity a domain error on the square root) and implausible daily volatility, which is nearly always a units mistake.
+ 2. **Model Performance Audit** — compute against the *active* parameters:
+ - Tracking error: $\text{RMSE} = \sqrt{\frac{1}{N} \sum (\text{IS}_{\text{realized}} - \text{IS}_{\text{predicted}})^2}$
+ - Systematic bias: $\bar{\epsilon} = \frac{1}{N} \sum (\text{IS}_{\text{realized}} - \text{IS}_{\text{predicted}})$, signed so **positive means the model under-predicts cost**.
+ - **Decision point — compare thresholds on the unrounded metrics.** Rounding to display precision before the comparison creates a dead band: a bias of $+1.502$ bps rounds to $1.50$ and then fails a $|\bar{\epsilon}| > 1.50$ test. Round only when writing the report.
+
+ 3. **Recalibration Trigger Audit**: $\text{RMSE} > \text{RMSE}_{\max}$ **OR** $|\bar{\epsilon}| > |\bar{\epsilon}|_{\max} \implies$ the model has drifted.
+
+ 4. **Sample-sufficiency gate** — a breach is not automatically a refit.
+ - **Decision point — if $N < N_{\min}$, defer.** Emit `RECALIBRATION_DEFERRED_INSUFFICIENT_SAMPLE`, retain the active parameters, and re-audit when the sample grows. Refitting a two-coefficient model on a handful of trades fits noise, and unstable cost coefficients destabilise every downstream portfolio optimisation that consumes them.
+
+ 5. **Parameter Least-Squares Refitting** — solve the no-intercept normal equations in closed form for
+ $$\text{IS}_{\text{realized}} = \eta \cdot \text{Spread} + \gamma \cdot \sigma \cdot \sqrt{\text{Qty}/\text{ADV}}$$
+ - No intercept is fitted: a zero-size order in a zero-spread market has zero modelled cost, and a free intercept would absorb size-dependent impact into a constant.
+ - The active parameters are **not** an input to the fit. Least squares finds the sample optimum; seeding it with the incumbent values would only bias the result toward a model the audit has already rejected.
+ - **Decision point — check the design conditioning before trusting the coefficients.** The engine reports $\det/(S_{11}S_{22})$, which equals $1 - \cos^2$ of the angle between the two regressor columns ($1.0$ orthogonal, $0.0$ collinear). Below the floor, escalate to `RECALIBRATION_REQUIRED_MANUAL_REVIEW` rather than shipping arbitrary coefficients.
+ - **Decision point — a negative fitted coefficient is not a small number, it is a wrong sign.** $\eta < 0$ or $\gamma < 0$ implies wider spreads or larger orders *reduce* cost. Withhold the recommendation and investigate the sample.
+
+ 6. **Validate before promotion**: the report carries `post_refit_rmse_bps` and `post_refit_bias_bps`, both **in-sample**. Treat them as a sanity floor, not evidence the new parameters will hold live. Promote through the same versioning and rollback path as any other model change (`model-versioning-and-rollback`).
+
> Full procedure: see `references/workflows.md`.
> Standards reference: see `references/standards.md`.
> Printable pre-flight checklist: see `assets/checklist.md`.
## Common Pitfalls
- - **Over-Recalibrating on Transient Noise**: Refitting cost models daily on tiny trade sample sizes, introducing high parameter variance and unstable portfolio optimization.
- - **Ignoring Systematic Prediction Bias**: Focusing solely on correlation while ignoring constant under-prediction ($\bar{\epsilon} = +4.0\text{ bps}$), causing portfolio managers to underestimate trading costs.
- - **Using Outdated Historical Trade Windows**: Training models on calm, low-volatility historical data during a major market crash regime.
+ - **Silently auditing corrupt data**: a `NaN` realized IS makes RMSE and bias `NaN`, and `nan > limit` is False on every threshold — so the worst possible data produces the most reassuring verdict, `MODEL_PARAMETER_STABLE`, and no recalibration is raised. Reject non-finite inputs at ingestion.
+ - **Over-recalibrating on transient noise**: refitting daily on a handful of trades produces high parameter variance and unstable portfolio optimisation. Gate the refit on sample size, not just on a threshold breach.
+ - **Rescaling instead of refitting**: multiplying both coefficients by a single ratio of mean realized to mean predicted cost cannot separate $\eta$ from $\gamma$ — it preserves whatever ratio the seed had, no matter what the data says, so a pure impact-regime shift silently reprices spread costs as well. A two-regressor least-squares solve is what "refit" has to mean here.
+ - **Mismatched volatility units**: the square-root law needs $\sigma$ in the same relative unit as the predicted cost. Feeding a decimal fraction where percent is expected shrinks the impact term $100\times$, making a 10%-of-ADV order look like a sub-bps execution and pushing all the fitted cost into the spread coefficient.
+ - **Ignoring systematic prediction bias**: tracking correlation while a constant under-prediction ($\bar{\epsilon} = +4.0$ bps) persists lets portfolio managers systematically underestimate trading costs on every order.
+ - **Trusting an in-sample refit**: the refit is scored on the trades that triggered it, so its RMSE is guaranteed no worse than the incumbent's — that is arithmetic, not evidence. Only out-of-sample performance is evidence.
+ - **Refitting through a one-off event**: a venue outage or fee-schedule change inflates realized cost for a specific reason. Recalibrating on that window bakes a transient into the standing model.
+ - **Using outdated historical trade windows**: training on calm, low-volatility history during a crash regime — or the reverse — produces a model calibrated to a regime that is no longer running.
+ - **Refitting on a sample with no design variation**: all-similar orders make the spread and impact regressors collinear; the fit "succeeds", the combined prediction looks reasonable, and each coefficient is individually meaningless.
## Verification
- - Instantiate `ExecutionCostModelRecalibrationEngine`. Input active parameters ($\eta = 0.5$, $\gamma = 1.0$). Ingest 100 recent trade execution records. Scenario 1: Clean prediction alignment ($\text{RMSE} = 1.2\text{ bps}$, bias = $+0.2\text{ bps}$) $\implies$ verify engine outputs `MODEL_PARAMETER_STABLE`. Scenario 2: Volatility regime shift ($\text{RMSE} = 4.8\text{ bps}$, bias = $+3.1\text{ bps}$) $\implies$ verify engine triggers `RECALIBRATION_RECOMMENDED` and provides refitted parameters ($\eta^*, \gamma^*$).
+ - Instantiate `ExecutionCostModelRecalibrationEngine()` with active parameters $\eta = 0.5$, $\gamma = 1.0$.
+ - **Prediction units**: for a 9,000-share order against 100,000 ADV ($\sqrt{0.09} = 0.3$) at a 2.0 bps spread and $1.5\%$/day volatility, `predict_slippage_bps` must return $0.5 \times 2.0 + 1.0 \times 150 \times 0.3 = 46.0$ bps.
+ - **Scenario 1 — stable**: 60 trades generated exactly at the active parameters $\implies$ RMSE and bias both $0$, status `MODEL_PARAMETER_STABLE`, no recommended parameters.
+ - **Scenario 2 — regime shift**: 60 trades generated at $\gamma = 2.0$ against an active $\gamma = 1.0$ $\implies$ status `RECALIBRATION_RECOMMENDED`, positive bias above the limit, and refitted $(\eta^*, \gamma^*) = (0.5, 2.0)$ recovered exactly.
+ - **Scenario 3 — identifiability**: data generated at $(\eta, \gamma) = (0.2, 3.0)$ must refit to that ratio, not to the ratio of whatever seed the fitter started from.
+ - **Scenario 4 — governance gates**: the same breach observed over 3 trades $\implies$ `RECALIBRATION_DEFERRED_INSUFFICIENT_SAMPLE` with no recommended parameters; an all-identical sample $\implies$ `RECALIBRATION_REQUIRED_MANUAL_REVIEW` on collinearity; a sample fitting to $\eta < 0$ $\implies$ `RECALIBRATION_REQUIRED_MANUAL_REVIEW` with the recommendation withheld.
+ - **Least-squares property checks**: residuals orthogonal to both regressor columns, and `post_refit_rmse_bps` never exceeding the active RMSE.
+ - **Negative checks**: empty history, `NaN`/`inf` realized IS, zero or negative ADV, negative quantity, negative spread or volatility, and implausible volatility must each raise.
- Run `python scripts/test_execution_cost_model_recalibration_cadence.py`.
## Related Skills
- `execution-algo-parameter-optimization-via-backtest`
- `execution-slippage-attribution-timing-vs-sizing`
- ---
+ - `transaction-cost-analysis-tca-integration`
+ - `model-versioning-and-rollback`
+ - `model-staleness-detection`