| name | docs-writing |
| description | Conventions for writing and maintaining stochastic-rs documentation pages under website/content/docs/. Eight page templates (process / distribution / copula / pricer / calibrator / estimator / AI surrogate / concept), frontmatter schema, KaTeX gotchas, meta.json sidebar wiring, doctest-backed examples, and the audit-script contract. Invoke whenever a new public type ships and needs a docs page, or when fixing rot in existing pages. |
Docs writing โ stochastic-rs
The docs site lives under website/ (Fumadocs + Next.js + MDX). Sections
with a single page live as a flat website/content/docs/<section>.mdx;
sections with multiple pages live as website/content/docs/<section>/<name>.mdx
plus a meta.json sidebar manifest. Currently only getting-started/
and concepts/ are folder-form; everything else is flat.
This SKILL is the per-page authoring contract. The audit script
(website/scripts/docs-audit.ts) enforces every rule under ยง6 and ยง10.
1. Trigger map โ which template to use
Adding pub struct โ Page goes under โ Use template
โโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโ
process โ expand processes.mdx (or split ยง3.1 Process
into processes/<subcat>/ if it
outgrows a single page)
distribution โ expand distributions.mdx ยง3.2 Distribution
copula โ expand copulas.mdx ยง3.6 Copula
option pricer โ expand quant.mdx (or quant/<โฆ>) ยง3.3 Pricer
calibrator โ expand quant.mdx ยง3.4 Calibrator
stats estimator โ expand stats.mdx ยง3.5 Estimator
NN surrogate โ expand ai.mdx ยง3.7 AI surrogate
trait / cross-cut โ concepts/ ยง3.8 Concept
end-to-end use case โ expand tutorials.mdx ยง3.9 Tutorial
When a section page outgrows a single MDX file (โ 600 lines), promote
it to folder-form: create <section>/index.mdx (move the overview
content here) plus per-feature <section>/<name>.mdx pages, add a
<section>/meta.json sidebar manifest, and remove <section>.mdx.
If a new type does not fit any of these slots, stop and ask โ adding
a new top-level section is a sidebar decision, not a per-page one.
2. Frontmatter schema (mandatory)
Every page begins with frontmatter validated by zod in
website/source.config.ts. Required keys:
---
title: <human-readable name>
description: <one sentence, 20-160 chars โ used for OG meta + search snippet>
category: process | distribution | copula | estimator | pricer | calibrator | concept | tutorial | reference | ai
subcategory: <free string โ e.g. diffusion, jump, fourier>
crate: stochastic-rs-<sub>
module_path: stochastic_rs::stochastic::diffusion::ou
since: 2.0.0
status: stable | experimental | deprecated
features: []
references:
- author: "Heston, S."
year: 1993
title: "A closed-form solution for options with stochastic volatility..."
doi: "10.1093/rfs/6.2.327"
arxiv: ""
url: ""
---
Lints (audit script will fail the build):
description length 20โ160 characters.
module_path must resolve in the workspace (the audit greps the Rust
source for the corresponding pub struct / pub fn).
status: deprecated requires a replaced_by: key pointing at the
successor page slug.
references empty array is allowed only on concept pages. Every
numerical / model page must cite at least one paper.
3. The page templates
Each section below is a copy-paste-ready skeleton. Replace the angle-bracketed
fields, keep the section ordering, do not invent extra top-level headers.
3.1 Process page (โ80 pages โ by far the most common)
---
title: <Process name (acronym)>
description: <one-sentence description, 20-160 chars>
category: process
subcategory: <diffusion | jump | volatility | interest | rough | noise>
crate: stochastic-rs-stochastic
module_path: stochastic_rs::stochastic::<subcategory>::<snake_case_name>
since: 2.0.0
status: stable
features: []
references:
- { author: "...", year: ..., title: "...", doi: "..." }
---
# <Full process name>
> <Two-sentence summary. State the modelling intent + the mathematical
> family in one breath.>
## SDE
$$ dX_t = \mu(X_t, t)\,dt + \sigma(X_t, t)\,dW_t,\quad X_0 = x_0 $$
<Optional: closed-form transition density / characteristic function /
moment generating function โ only if it exists.>
## Constructor
| Parameter | Type | Description |
|-----------|-------------|----------------------------------------------|
| `<arg>` | `T` | <one-line meaning + units / range> |
| `n` | `usize` | Number of steps |
| `x0` | `Option<T>` | Initial value (defaults to <doc default>) |
| `t` | `Option<T>` | Horizon (defaults to <doc default>) |
## Examples
<Tabs items={['Rust', 'Python']}>
<Tab value="Rust">
<RustExample path="tests/doctest_<name>_quickstart.rs" />
</Tab>
<Tab value="Python">
```python
from stochastic_rs import <PyName>
p = <PyName>(<keyword args>)
path = p.sample() # numpy.ndarray, shape (n,)
Properties
- Stationary: <yes / no โ and conditions>
- Markov: <yes / no>
- Closed-form transition: <yes / no>
- Mean / variance (when applicable): $\mathbb{E}[X_t] = \dots$, $\text{Var}(X_t) = \dots$
- Acceleration: <CPU SIMD / CUDA / Metal / none>
Calibration / estimation
<Cross-link to relevant pages โ never inline calibration code here.>
See also
References
<Either rendered automatically from frontmatter references:, or
hand-written <PaperRef /> blocks for the rare paper without DOI.>
### 3.2 Distribution page (19 pages)
```mdx
---
title: <Distribution name>
description: <one-sentence>
category: distribution
crate: stochastic-rs-distributions
module_path: stochastic_rs::distributions::<snake>::Simd<Name>
since: 2.0.0
status: stable
features: []
references: [...]
---
# <Distribution name>
> <Two-sentence summary.>
## Density
$$ f(x; \theta) = \dots,\quad x \in <\text{support}> $$
## CDF
$$ F(x; \theta) = \dots $$
## Characteristic function
$$ \varphi(t) = \dots $$
## Moments (closed-form via `DistributionExt`)
| Moment | Formula |
|---------------|---------------------------------------------------|
| Mean | $\dots$ |
| Variance | $\dots$ |
| Skewness | $\dots$ (or "**not implemented** โ see notes") |
| Excess kurtosis | $\dots$ |
> **DistributionExt status note**: per `memory/project_distribution_ext_status.md`,
> 18/19 distributions ship closed-form `DistributionExt`. If a specific moment
> falls into the 5 "no closed-form" cases, mark it explicitly here as
> `unimplemented!` and link to the issue tracking the fix. Never leave it as
> a silent zero.
## Examples
<Tabs items={['Rust', 'Python']}>
<Tab value="Rust"><RustExample path="tests/doctest_<dist>_quickstart.rs" /></Tab>
<Tab value="Python">
```python
from stochastic_rs import <PyName>
import numpy as np
d = <PyName>(<keyword args>, seed=42)
xs = d.sample(10_000)
Sampling strategy
<Pick one and explain in 2-3 sentences:>
- Transformation (closed-form $F^{-1}(U)$)
- Ziggurat
- Rejection / inversion
- Composition
KS-test reference
<Cite the integration test file under stochastic-rs-distributions/tests/ that
validates the sampler against the analytic CDF.>
References
### 3.3 Pricer page (~30 pages)
```mdx
---
title: <Pricer name>
description: <one-sentence>
category: pricer
subcategory: <closed-form | fourier | monte-carlo | finite-difference | lattice | lsm | malliavin>
crate: stochastic-rs-quant
module_path: stochastic_rs::quant::pricing::<snake>::<PricerName>
since: 2.0.0
status: stable
features: []
references: [...]
---
# <Pricer name>
> <Model + payoff + method, in one sentence.>
## Model
<SDE / characteristic function / payoff. Use $\LaTeX$.>
## Method
<Closed-form / Carr-Madan / Lewis / MC / Crank-Nicolson / Bermudan-LSM / etc.>
## Constructor
| Parameter | Type | Description |
|-----------|------|-------------|
| ... | ... | ... |
## Examples
<Tabs items={['Rust', 'Python']}>
<Tab value="Rust"><RustExample path="tests/doctest_<pricer>_quickstart.rs" /></Tab>
<Tab value="Python">...</Tab>
</Tabs>
## Greeks
<Either:>
- โ
All first- and second-order Greeks via `GreeksExt` (delta, gamma, vega,
theta, rho, vanna, charm, volga, veta) โ single-pass override available
for MC pricers per `greeks-pattern` SKILL.
- โ ๏ธ Partial โ only <list>.
- โ Not implemented โ track at <issue link>.
## Variance reduction (MC pricers only)
- Antithetic: โ
/ โ
- Control variate: โ
/ โ
- Quasi-MC (Sobol / Halton): โ
/ โ
- MLMC: โ
/ โ
## Calibration
<Link to the matching calibrator page if applicable.>
## Complexity
$O(\text{steps} \times \text{paths})$ for MC, $O(N \log N)$ for FFT, etc.
## References
3.4 Calibrator page (~12 pages)
---
title: <Calibrator name>
description: <one-sentence>
category: calibrator
crate: stochastic-rs-quant
module_path: stochastic_rs::quant::calibration::<snake>::<Name>
since: 2.0.0
status: stable
features: []
references: [...]
---
# <Calibrator name>
> <Model + market data + optimiser, in one sentence.>
## Model
<Brief. Cross-link to the pricer page that defines the model fully.>
## Market data
<What is required: strike grid, IV grid, OTM-only, full smile, ATM only, etc.>
## Optimiser
<Levenberg-Marquardt (argmin), Differential Evolution, NMLE, NMLE-CEKF, Cui
analytic Jacobian, etc. โ one paragraph on why.>
## `CalibrationResult` fields
| Field | Type | Meaning |
|----------------|-------------|-------------------------------|
| `params` | `<T>::Params` | Calibrated parameter struct |
| `rmse` | `T` | Final fit error |
| `iterations` | `usize` | Optimiser iteration count |
| `success` | `bool` | Convergence flag |
## Example
<Rust example only โ Python calibration example goes in
`tutorials/<model>-calibration.mdx`.>
## Typical RMSE
<Order-of-magnitude expectation on synthetic data, plus a note on real-data
caveats.>
## See also
- [<pricer>](/docs/quant/pricing/<...>) โ the matching pricer
## References
3.5 Estimator page (~30 pages)
---
title: <Estimator name>
description: <one-sentence>
category: estimator
subcategory: <mle | realized | normality | stationarity | econometrics | filtering | hurst | tail | spectral>
crate: stochastic-rs-stats
module_path: stochastic_rs::stats::<snake>::<Name>
since: 2.0.0
status: stable
features: []
references: [...] # **mandatory** โ papers must be cited verbatim
---
# <Estimator name>
> <Estimand + method, in one sentence.>
## Estimand
$$ \widehat{\theta} = \dots $$
## Method
<Brief description. Per `feedback_implementation.md`, the implementation
follows the cited paper exactly โ link to the file:line where the
formulas are anchored.>
## Result struct
| Field | Type | Meaning |
|---------------|---------|----------------------------------------------|
| `point` | `T` | Point estimate |
| `se` | `T` | Standard error |
| `ci_lower` | `T` | 95% confidence interval lower bound |
| `ci_upper` | `T` | 95% confidence interval upper bound |
| `p_value` | `T` | <parametric / bootstrap โ call it out> |
## Asymptotic distribution
<Either: closed-form (e.g. $\sqrt{n}(\hat\theta-\theta) \to \mathcal N$),
or "bootstrap with `B = ...` resamples".>
## Example
<Rust + Python tabs.>
## Validation
<Cross-link to the integration test that compares against the paper's
numerical examples (e.g. Fukasawa intraday Table 1).>
## References
3.6 Copula page (10 pages)
---
title: <Copula name>
description: <one-sentence>
category: copula
subcategory: <bivariate | multivariate | empirical>
crate: stochastic-rs-copulas
module_path: stochastic_rs::copulas::<snake>::<Name>
since: 2.0.0
status: stable
features: []
references: [...]
---
# <Copula name>
> <Family + dependence intuition, in one sentence.>
## Definition
$$ C(u, v; \theta) = \dots $$
## Parameter range
$\theta \in <\text{interval}>$, with limits:
- $\theta \to <\text{lower}>$ โ <independence / countermonotonic / ...>
- $\theta \to <\text{upper}>$ โ <comonotonic>
## Dependence measures
| Measure | Formula |
|-----------------|------------------------|
| Kendall's $\tau$| $\dots$ |
| Spearman's $\rho$| $\dots$ |
| Tail dependence | $\lambda_L = \dots,\ \lambda_U = \dots$ |
## Sampling algorithm
<E.g. Marshall-Olkin, conditional inversion. Cite the algorithm.>
## Example
<Rust + Python tabs.>
## References
3.7 AI surrogate page (3 pages)
Per vol-surrogate-nn SKILL. Required sections:
1. Model spec (StochVolModelSpec โ input dims, output dims)
2. Scaler (BoundedScaler / StandardScaler โ pre/post norm)
3. Training set (gzip-npy file path, generator script, sample count)
4. Architecture (layers, activation, hidden width)
5. Training (optimiser, loss, epochs, batch size)
6. Inference (predict_surface integration with ImpliedVolSurface::from_flat_iv_grid)
7. Round-trip test (train_save_load_<model>)
8. Benchmark (vs Fourier / closed-form baseline; speed + accuracy)
9. References
3.8 Concept page (~8 pages)
Free-form. Required ingredients:
- One-paragraph elevator pitch
- Why it exists (the pain it solves)
- Worked example with
<RustExample>
- Decision-table for "when to use which alternative" (e.g.
f32 vs f64,
ProcessExt::sample() vs sample_par())
- Cross-links to the SKILLs that operationalise the concept
3.9 Tutorial page (8โ10 pages)
Long-form, end-to-end, narrative. Required structure:
1. What you'll build (screenshot or static IV-surface plot)
2. Prerequisites (which sub-crates, which features)
3. Setup
4. Step 1 ... Step N (each โค 30 lines of code, with prose)
5. Result (numerical output, plot)
6. Where to go next (3 cross-links)
4. Cross-linking conventions
- Internal links: always relative, no leading
/docs/ prefix duplication.
Use [OU](/docs/processes/diffusion/ou). Fumadocs MDX validates these at
build time โ broken links fail CI.
- docs.rs links: don't hand-write them. Frontmatter
module_path is
rendered by <DocsRsLink /> automatically in the page header.
- Issue / PR links: use full URL once, then
(see #123) for repeats
in the same page.
- Paper links: prefer DOI. Fall back to arXiv. Avoid raw publisher URLs
(paywalls rot).
5. KaTeX gotchas
Fumadocs uses remark-math + rehype-katex. The two recurring traps:
- Inline vs block. Inline
$E[X]$, block $$ \dots $$ on its own line
with blank lines either side. Otherwise MDX may treat the $ as
prose punctuation.
- Underscores in
$$ ... $$. KaTeX treats _ correctly inside math
blocks, but MDX can mistake _x_ for italics. Fix: wrap the block
with the <Math> component, or escape (\_).
- Multi-line aligned environments need
& for column separators and
\\ for row breaks โ align* works:
$$
\begin{aligned}
dX_t &= \theta(\mu - X_t)\,dt + \sigma\,dW_t \\
X_0 &= x_0
\end{aligned}
$$
- No
\mathbb{...} collisions. KaTeX ships \mathbb{R, N, Z, Q, C}
only; \mathbb{P} works post-v0.16. If using older KaTeX, fall back
to \Pr.
6. meta.json (sidebar)
Each section directory has a meta.json declaring sidebar order. Example
from processes/diffusion/meta.json:
{
"title": "Diffusion processes",
"pages": [
"overview",
"---Single-factor---",
"ou", "gbm", "gbm-log", "cir", "cev", "ckls", "ait-sahalia",
"---Bounded support---",
"jacobi", "fjacobi", "logistic", "verhulst", "kimura", "pearson",
"---Special---",
"regime-switching", "fouque", "feller", "feller-root", "three-half"
]
}
---Section--- strings render as non-clickable group headers. The audit
script ensures every mdx file in the directory appears in pages โ
forgetting a new file is a hard error.
7. The <RustExample> component (doctest-backed examples)
For hero pages (top-50 traffic โ landing, OU, GBM, Heston, BSM, ...), the
Rust block is not inline. It is <RustExample path="..." />, which
inlines a file from the workspace. The contract:
- The referenced file must exist under
tests/doctest_*.rs (or
examples/) and pass cargo test --workspace.
- The file's first comment line is
// docs: <slug> โ the audit script
uses this to verify the page-to-test mapping.
- The file ships only runnable code โ no
unimplemented!, no todo!,
no unwrap() on user input.
For long-tail pages, inline fenced rust blocks are fine. Annotate each
with // last-checked: <commit-sha>. The quarterly audit flags blocks
where the corresponding source has changed since the pinned sha.
8. References block (auto-rendered)
The frontmatter references: array is rendered into a ## References
section by the page layout. Hand-written ## References headings are
ignored unless the page is a concept (which legitimately has none in
frontmatter).
Citation format:
- DOI present โ
Author (year). Title. doi:10.xxxx/yyy
- arXiv present โ
Author (year). Title. arXiv:NNNN.NNNNN
- url only โ
Author (year). Title. <url>
The audit pings DOIs / arXiv IDs quarterly. Broken links produce a row
in website/audit/AUDIT_<YYYY-MM-DD>.md.
9. Adding a new page โ checklist
[ ] Pick the right template from ยง3
[ ] Create the file at website/content/docs/<section>/<name>.mdx
[ ] Fill frontmatter โ schema is enforced (ยง2)
[ ] Math: prefer block $$ โฆ $$, escape $ in prose
[ ] Examples: <Tabs> with Rust + Python (ยง3.x)
[ ] Cross-links: ยง4 conventions
[ ] Add the slug to the parent meta.json (ยง6)
[ ] Optional: add a doctest under tests/doctest_<slug>.rs (ยง7)
[ ] Run `bun lint:mdx` (frontmatter schema check)
[ ] Run `bun build` (Fumadocs link check)
[ ] If new doctest: `cargo test --test 'doctest_<slug>'`
10. Audit script โ what it enforces
website/scripts/docs-audit.ts runs in CI on every PR and as a quarterly
cron. Hard failures (block CI):
- Frontmatter zod schema violation
module_path does not resolve in the workspace
- Public type added in
src/ with no matching MDX page
- MDX file missing from its directory's
meta.json
<RustExample path="..." /> points at a non-existent file
- Broken internal Markdown link
Soft warnings (report-only):
last-checked sha older than 90 days while source changed
- DOI / arXiv URL no longer resolves
- Description outside 20โ160 chars (warn at 80% of bound)
Output: website/audit/AUDIT_<YYYY-MM-DD>.md.
11. Bun cheatsheet (the workspace uses Bun, not pnpm/npm)
cd website
bun install
bun run dev
bun run build
bun run lint
bun run lint:mdx
bun run audit
bun run python:parity
bun run bench:publish
bun add <pkg>
bun add -d <pkg>
bunx <bin> ...
If a script bypasses Bun (e.g. CI calls node scripts/foo.ts), prefer
bun run scripts/foo.ts โ Bun's TS runtime is part of the workspace
contract.
12. Long-tail rules (small but load-bearing)
- No emoji in body text (frontmatter
status icons rendered by the
layout are fine). Per project convention.
- No
// --- separator banners (feedback_no_section_separators.md).
- No statrs in code examples (
feedback_no_statrs_distributions.md).
- Convert relative dates to absolute (
Today โ 2026-05-10) the same
way the memory system does. Future-readers thank you.
- Capitalisation: titles use sentence case, not Title Case.
("Heston model", not "Heston Model".) Acronyms stay capitalised
(
OU, GBM, CIR, SABR).
- First-person plural is fine ("we use the Cui Jacobian"); first-person
singular is not.
- Hungarian comments: never. The site is English-only.