refactor(series,greeks): eliminate .unwrap()/.expect() (#320)#354
Merged
joaquinbejar merged 5 commits intomainfrom Apr 17, 2026
Merged
refactor(series,greeks): eliminate .unwrap()/.expect() (#320)#354joaquinbejar merged 5 commits intomainfrom
joaquinbejar merged 5 commits intomainfrom
Conversation
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`.
- `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.
There was a problem hiding this comment.
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_optionseriesfallible (Result<..., ChainError>) and propagate errors via?, with an init-step-preserving behavior for empty/insufficient historical data. - Replace fallible
Decimalsqrt usage inn(x)with a precomputed1/sqrt(2π)constant; simplifybig_n(x)conversion handling and surfaceNormal::newconstruction errors. - Refactor
OptionSeriescustom 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.
Codecov Report❌ Patch coverage is
🚀 New features to boost your workflow:
|
- 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.
Owner
Author
|
Thanks for the review. All three findings are addressed in 387ee94. |
7 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
.unwrap()/.expect()fromsrc/series/andsrc/greeks/per the M1 — Panic-Free Core milestone (22 sites total across 3 files).generator_optionseriesfallible (Vec<Step<...>>→Result<Vec<Step<...>>, ChainError>), matching the precedent set by PR M1: Eliminate .unwrap()/.expect() from src/chains/ (#317) #348 forgenerator_optionchain/generator_positiveand aligning with the fallibleRandomWalk::new/Simulator::newconstructors from PR fix(simulation,pricing): fallible RandomWalk/Simulator constructors + Telegraph guard (#349, #351) #352.(Decimal::TWO * PI).sqrt().unwrap()inn(x)with a precomputed1/sqrt(2π)constant; collapsesbig_n(x)'s redundantis_none()+unwrap()pair into alet-else.OptionSeriesserdeSerialize::serializechain-map closure to surface inner errors viaserde::ser::Error::custominstead of panicking.generator_optionseriesHistorical branch: empty / insufficient prices now returnOk(vec![init_step])instead ofvec![], matching the contains-init-step contract used by the rest of the chain-generator family.Per-pattern transformations
walker.brownian(walk_params).unwrap()(and 8 siblings)walker.brownian(walk_params)?(after sig change)(Decimal::TWO * PI).sqrt().unwrap()dec!(0.3989422804014326779399461)precomputed1/sqrt(2π)constantDecimal::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()afteris_none()early-returnlet Some(x_f64) = x.to_f64() else { return Err(...); };date.get_date_string().unwrap()in serde closureforloop with.map_err(serde::ser::Error::custom)?.unwrap()examples# fn run() -> Result<(), Box<dyn Error>>shim with?Test plan
awk '/#\[cfg\(test\)\]/{exit} {print}' <file> \| grep -E '\.unwrap\(\)\|\.expect\('acrosssrc/series/**/*.rsandsrc/greeks/**/*.rscargo clippy --all-targets --all-features --workspace -- -D warningscargo fmt --all --checkcargo test --all-features --workspace(3724 lib + 416 integration + plotly)cargo build --releasemake pre-push(fix + fmt + lint-fix + test + readme + doc)test_generator_optionseries_historical_empty_pricesand_insufficient_pricesto reflect the new contains-init-step contract (steps.len == 1 instead of is_empty)Public-surface notes
generator_optionseries: signature change fromVec<Step<...>>toResult<Vec<Step<...>>, ChainError>. Breaking minor (acceptable on 0.16.x — same precedent as PR M1: Eliminate .unwrap()/.expect() from src/chains/ (#317) #348 / fix(simulation,pricing): fallible RandomWalk/Simulator constructors + Telegraph guard (#349, #351) #352). Only one in-tree caller (examples/examples_simulation/src/bin/random_walk_build_series.rs), updated in this PR.n(x)andbig_n(x)keep the same signature; behaviour unchanged on the happy path (the precomputed constant is verified to 24+ decimal places against the existing PDF reference tests).OptionSeriesserdeSerializeimpl signature unchanged; behaviour now surfaces date-formatting errors as serde errors instead of panicking.Closes #320