Skip to content

refactor(series,greeks): eliminate .unwrap()/.expect() (#320)#354

Merged
joaquinbejar merged 5 commits intomainfrom
issue-320-panic-free-series-greeks
Apr 17, 2026
Merged

refactor(series,greeks): eliminate .unwrap()/.expect() (#320)#354
joaquinbejar merged 5 commits intomainfrom
issue-320-panic-free-series-greeks

Conversation

@joaquinbejar
Copy link
Copy Markdown
Owner

Summary

  • Removes every production .unwrap() / .expect() from src/series/ and src/greeks/ per the M1 — Panic-Free Core milestone (22 sites total across 3 files).
  • Makes generator_optionseries fallible (Vec<Step<...>>Result<Vec<Step<...>>, ChainError>), matching the precedent set by PR M1: Eliminate .unwrap()/.expect() from src/chains/ (#317) #348 for generator_optionchain / generator_positive and aligning with the fallible RandomWalk::new / Simulator::new constructors from PR fix(simulation,pricing): fallible RandomWalk/Simulator constructors + Telegraph guard (#349, #351) #352.
  • Replaces the runtime fallible (Decimal::TWO * PI).sqrt().unwrap() in n(x) with a precomputed 1/sqrt(2π) constant; collapses big_n(x)'s redundant is_none() + unwrap() pair into a let-else.
  • Refactors the OptionSeries serde Serialize::serialize chain-map closure to surface inner errors via serde::ser::Error::custom instead of panicking.
  • Init-invariant fix in generator_optionseries Historical branch: empty / insufficient prices now return Ok(vec![init_step]) instead of vec![], matching the contains-init-step contract used by the rest of the chain-generator family.

Per-pattern transformations

Before After
walker.brownian(walk_params).unwrap() (and 8 siblings) walker.brownian(walk_params)? (after sig change)
(Decimal::TWO * PI).sqrt().unwrap() dec!(0.3989422804014326779399461) precomputed 1/sqrt(2π) constant
Decimal::from_f64(-11.7).unwrap() dec!(-11.7)
Normal::new(0.0, 1.0).unwrap() (infallible by construction) .map_err(|e| DecimalError::ConversionError { ... })?
x_f64.unwrap() after is_none() early-return let Some(x_f64) = x.to_f64() else { return Err(...); };
date.get_date_string().unwrap() in serde closure for loop with .map_err(serde::ser::Error::custom)?
Doc-comment .unwrap() examples # fn run() -> Result<(), Box<dyn Error>> shim with ?

Test plan

  • Acceptance grep returns 0 — awk '/#\[cfg\(test\)\]/{exit} {print}' <file> \| grep -E '\.unwrap\(\)\|\.expect\(' across src/series/**/*.rs and src/greeks/**/*.rs
  • cargo clippy --all-targets --all-features --workspace -- -D warnings
  • cargo fmt --all --check
  • cargo test --all-features --workspace (3724 lib + 416 integration + plotly)
  • cargo build --release
  • make pre-push (fix + fmt + lint-fix + test + readme + doc)
  • Updated test_generator_optionseries_historical_empty_prices and _insufficient_prices to reflect the new contains-init-step contract (steps.len == 1 instead of is_empty)

Public-surface notes

Closes #320

Change `generator_optionseries` signature from
`Vec<Step<Positive, OptionSeries>>` to
`Result<Vec<Step<Positive, OptionSeries>>, ChainError>`, propagating
errors from the underlying walker, volatility-estimation and
chain-construction primitives via `?`. Same precedent as PR #348 for
`generator_optionchain` / `generator_positive`, and PR #352 already
updated `RandomWalk::new` / `Simulator::new` to accept fallible
generators.

Init invariant fix: when `prices.is_empty()` or insufficient for
Historical walk type, now returns `Ok(vec![init_step])` instead of
`vec![]`, matching the contains-init-step contract used by the other
chain-family generators. Tests updated accordingly.

Removed the dead-code if/else expression at lines 170-174 (its result
was discarded); the cleaned-up form binds `volatility` to the unwrapped
or default value before the loop.

Doc comment switched from `# Panics` to `# Errors`.
#320)

After #320 made the generator fallible, the example can pass it
directly to RandomWalk::new and propagate via `?`.
- `n(x)`: replace `(Decimal::TWO * PI).sqrt().unwrap()` with a
  precomputed `1/sqrt(2π)` constant (`dec!(0.3989422804014326779399461)`)
  and `Decimal::from_f64(-11.7).unwrap()` with `dec!(-11.7)`. No more
  fallible runtime math, no more unwraps.
- `big_n(x)`: collapse the redundant `is_none()` guard + `unwrap()`
  pair into a `let-else`. Surface the (infallible-by-construction)
  `Normal::new(0.0, 1.0)` failure via `map_err` to
  `DecimalError::ConversionError`.
- Doc-comment examples for `d2` and `calculate_delta_neutral_sizes`
  wrapped in `# fn run() -> Result<(), Box<dyn Error>>` shim with `?`.
- Drop now-unused `crate::constants::PI` and `FromPrimitive` imports.
…::Error (#320)

The chain-map serialization closure used `.get_date_string().unwrap()`,
panicking if any expiration date couldn't format. Refactor to a
fallible for-loop that surfaces the inner error via
`map_err(serde::ser::Error::custom)?` — the canonical serde pattern
for inner-error propagation at the boundary.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR advances the “Panic-Free Core” milestone by removing production .unwrap()/.expect() usage in the series and greeks modules, propagating typed errors instead of panicking, and aligning series generation with existing fallible generator patterns used elsewhere in the simulation stack.

Changes:

  • Make generator_optionseries fallible (Result<..., ChainError>) and propagate errors via ?, with an init-step-preserving behavior for empty/insufficient historical data.
  • Replace fallible Decimal sqrt usage in n(x) with a precomputed 1/sqrt(2π) constant; simplify big_n(x) conversion handling and surface Normal::new construction errors.
  • Refactor OptionSeries custom serde serialization to convert date-formatting failures into serde errors instead of panicking.

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated 3 comments.

File Description
src/series/model.rs Refactors OptionSeries serialization to surface date formatting errors via serde::ser::Error::custom.
src/series/generators.rs Removes unwraps by making generator_optionseries return Result and propagating underlying failures; adjusts empty historical behavior to return init step.
src/greeks/utils.rs Removes unwraps in PDF/CDF helpers, adds a precomputed normalisation constant, and improves conversion/error plumbing in big_n.
examples/examples_simulation/src/bin/random_walk_build_series.rs Updates example to pass the now-fallible generator_optionseries directly into RandomWalk::new.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/greeks/utils.rs Outdated
Comment thread src/series/generators.rs
Comment thread src/series/generators.rs
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 17, 2026

Codecov Report

❌ Patch coverage is 81.57895% with 7 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/series/generators.rs 84.00% 4 Missing ⚠️
src/greeks/utils.rs 66.66% 3 Missing ⚠️
Files with missing lines Coverage Δ
src/series/model.rs 78.94% <100.00%> (+0.18%) ⬆️
src/greeks/utils.rs 82.65% <66.66%> (-2.77%) ⬇️
src/series/generators.rs 89.23% <84.00%> (-3.42%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

- src/greeks/utils.rs: rewrite n(x) Calculation Details and Notes
  sections to describe the precomputed NORM_FACTOR constant and the
  exp-underflow shortcut; drop the stale reference to a runtime PI
  constant.
- src/series/generators.rs: rewrite the top-level summary and
  Implementation Details to reflect the new fallible Result return
  type and the contains-init-step contract for empty / insufficient
  Historical input.
- src/series/generators.rs: replace `debug_assert!(steps.len() <= walk_params.size)` (which gets stripped in release) with a runtime
  truncation that emits a `tracing::debug!` event, so production
  builds can't silently produce oversized step vectors.
@joaquinbejar
Copy link
Copy Markdown
Owner Author

Thanks for the review. All three findings are addressed in 387ee94.

@joaquinbejar joaquinbejar merged commit 468b642 into main Apr 17, 2026
13 checks passed
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.

Eliminate .unwrap()/.expect() in src/series/ + src/greeks/

2 participants