Jump to content

Connect Leeroopedia MCP: Equip your AI agents to search best practices, build plans, verify code, diagnose failures, and look up hyperparameter defaults.

Heuristic:Avhz RustQuant Finite Difference Grid Sizing

From Leeroopedia



Knowledge Sources
Domains Option_Pricing, Numerical_Methods
Last Updated 2026-02-07 19:00 GMT

Overview

Configure finite difference grids with 5-sigma log-price bounds and appropriate time/price step ratios to ensure stability and accuracy.

Description

RustQuant's `FiniteDifferencePricer` solves the Black-Scholes PDE on a log-price grid. The spatial domain is set to `ln(S) +/- 5 * sigma * sqrt(T)`, covering 5 standard deviations of log-returns under GBM. Three methods are available: Explicit (forward Euler), Implicit (backward Euler with tridiagonal matrix inversion), and Crank-Nicolson (time-centered average). The stability coefficient `x = 0.5 * dt * sigma^2 / dx^2` determines whether the explicit scheme is stable.

Usage

Apply this heuristic when setting `time_steps` and `price_steps` in `FiniteDifferencePricer::new()`. The grid sizing directly affects both accuracy and stability, particularly for the explicit scheme.

The Insight (Rule of Thumb)

  • Spatial Domain: Always use `ln(S) +/- 5*sigma*sqrt(T)`. This covers 99.9999% of the probability mass under GBM, minimizing boundary effects on interior grid values.
  • Explicit Scheme Stability (CFL Condition): Ensure `x = 0.5 * dt * sigma^2 / dx^2 <= 0.5`. Violating this causes explosive oscillations.
  • Practical Grid Resolution: Test suite uses 10,000 time steps x 200-250 price steps for validation accuracy to 3-4 decimal places.
  • Method Selection:
    • Explicit: Simplest, but conditionally stable. Use with fine time grids only.
    • Implicit: Unconditionally stable. Use when stability is a concern. Requires tridiagonal matrix inversion per time step.
    • Crank-Nicolson: Best accuracy-to-cost ratio. O(dt^2 + dx^2) convergence. Recommended default.
  • Trade-off: Finer grids improve accuracy but increase computation quadratically. For quick pricing, 1000 time steps x 100 price steps suffices. For validation, use 10,000 x 250.

Reasoning

The 5-sigma bound ensures that the grid captures essentially all of the probability mass of the terminal stock price distribution. Under GBM, `ln(S_T) ~ N(ln(S_0) + (r - 0.5*sigma^2)*T, sigma^2*T)`, so `ln(S_0) +/- 5*sigma*sqrt(T)` covers the range within which the option's value is non-trivially affected by the PDE solution. Beyond this range, the boundary conditions (call: `S - K*exp(-r*tau)`, put: `K*exp(-r*tau) - S`) dominate.

The stability coefficient `x` arises from the CFL (Courant-Friedrichs-Lewy) condition for parabolic PDEs. When `x > 0.5` in the explicit scheme, the time-stepping amplifies rather than dampens numerical perturbations.

Code Evidence

Grid construction with 5-sigma bounds from `finite_difference_pricer.rs:235-243`:

fn grid(&self) -> (f64, f64, f64, f64) {
    let T: f64 = self.year_fraction();
    let delta_t: f64 = T / (self.time_steps as f64);
    let x_min: f64 = self.initial_price.ln() - 5.0 * self.volatility * T.sqrt();
    let delta_x: f64 = (self.initial_price.ln() + 5.0 * self.volatility * T.sqrt() - x_min)
        / self.price_steps as f64;
    (T, delta_t, delta_x, x_min)
}

Stability coefficients from `finite_difference_pricer.rs:245-250`:

fn coefficients(&self, delta_t: f64, delta_x: f64) -> (f64, f64) {
    (
        0.5 * delta_t * self.volatility.powi(2) / delta_x.powi(2),
        delta_t * (self.risk_free_rate - 0.5 * self.volatility.powi(2)) / (2.0 * delta_x),
    )
}

Input validation assertions from `finite_difference_pricer.rs:65-70`:

assert!(initial_price > 0.0, "initial_price must be positive!");
assert!(strike_price > 0.0, "strike_price must be positive!");
assert!(risk_free_rate > 0.0, "risk_free_rate must be positive!");
assert!(volatility > 0.0, "volatility must be positive!");
assert!(time_steps > 0, "time_steps must be positive!");
assert!(price_steps > 0, "price_steps must be positive!");

Explicit scheme tridiagonal coefficients from `finite_difference_pricer.rs:256-258`:

let sub_diagonal: f64 = x - y;
let diagonal: f64 = 1.0 - 2.0 * x;
let super_diagonal: f64 = x + y;

Related Pages

Page Connections

Double-click a node to navigate. Hold to expand connections.
Principle
Implementation
Heuristic
Environment