Skip to content

fix(tdbe): reject non-positive spot/strike and size u8 layout fields correctly#812

Merged
userFRM merged 1 commit into
mainfrom
fix/core-numeric-safety
Jun 16, 2026
Merged

fix(tdbe): reject non-positive spot/strike and size u8 layout fields correctly#812
userFRM merged 1 commit into
mainfrom
fix/core-numeric-safety

Conversation

@userFRM

@userFRM userFRM commented Jun 16, 2026

Copy link
Copy Markdown
Owner

Two numeric-safety fixes in the core crate

Non-positive spot/strike no longer emit NaN or null Greeks

Black-Scholes is undefined for spot <= 0 or strike <= 0: the (spot / strike).ln() term goes non-finite and strike == 0 is an outright divide-by-zero. The existing degeneracy guard only checked vol and time, so a non-positive spot or strike flowed straight into d1/d2, and the fields that did not route through the NaN-to-zero clamp serialised to JSON null.

  • The fallible public entry points all_greeks and implied_volatility (the surface every SDK / CLI / MCP funnels through) now reject a non-positive or non-finite spot/strike up front with Error::Config, naming the offending value.
  • The infallible compute_full_bundle_with_iv treats the same inputs as degenerate and returns an all-zero bundle rather than producing NaN/Inf.

The positivity test is written as value > 0.0, so a NaN spot or strike also routes to the rejection / degenerate path.

u8 sized correctly in the FPSS layout-assert generator

The C-layout generator gave u8 a size and alignment of 4 by sharing the i32 match arm. That is harmless today only because the single u8 field is followed by an 8-aligned field, but any future u8-before-smaller-aligned field would have baked a wrong offset into a generated static_assert. The arm is split so u8 is 1/1 and i32 is 4/4. The other arms each map one logical width, so no further conflation remained to fix.

Test evidence

cargo test -p thetadatadx --lib tdbe::greeks — 26 passed, 0 failed. New tests:

  • all_greeks_errors_on_nonpositive_spot
  • all_greeks_errors_on_nonpositive_strike
  • implied_volatility_errors_on_nonpositive_spot
  • implied_volatility_errors_on_nonpositive_strike
  • compute_full_bundle_zeros_on_nonpositive_spot_or_strike

Each checks spot/strike of 0.0 and negatives return a clean Error::Config (no NaN, no panic), and that the infallible bundle path returns an all-zero, all-finite bundle.

Also verified: cargo fmt --all -- --check clean, cargo build -p thetadatadx succeeds (exercises the layout generator's static_assert emission via build.rs), cargo clippy -p thetadatadx --lib -- -D warnings clean.

🤖 Generated with Claude Code

…correctly

Black-Scholes is undefined for spot <= 0 or strike <= 0: the (spot / strike).ln() term goes non-finite and strike == 0 is an outright divide-by-zero. The fallible public entry points all_greeks and implied_volatility now reject a non-positive (or non-finite) spot or strike with Error::Config naming the offending value, so no SDK, CLI, or MCP surface can serialise NaN or null Greeks. The infallible compute_full_bundle_with_iv treats the same inputs as degenerate and returns an all-zero bundle rather than emitting NaN.

The FPSS C-layout generator sized u8 fields as 4/4 by sharing the i32 match arm. The arm is split so u8 is 1/1 and i32 is 4/4, which keeps the generated offset static_assert correct for any future u8-before-smaller-aligned field; the build.rs run that emits the asserts still compiles.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@userFRM userFRM enabled auto-merge (squash) June 16, 2026 09:19
@userFRM userFRM merged commit dcbcce6 into main Jun 16, 2026
43 checks passed
@userFRM userFRM deleted the fix/core-numeric-safety branch June 16, 2026 10:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants