M1: Eliminate .unwrap()/.expect() from src/strategies/ (#316)#347
M1: Eliminate .unwrap()/.expect() from src/strategies/ (#316)#347joaquinbejar merged 10 commits intomainfrom
Conversation
…iants Extend StrategyError with three typed variants needed by the strategies/ panic-free refactor (issue #316): - NumericConversion { value: f64 } for failed f64 → Decimal conversions at the numeric kernel boundary. - MissingGreek { name: &'static str } for options that lack a greek the caller requires (delta-neutral validators, etc.). - EmptyCollection { context: String } for unexpectedly empty option chains, break-even point sets, or profit ranges. Add #[cold] #[inline(never)] constructors. Wire ProbabilityError::From arms for the three new variants so the existing conversion stays total.
Tighten the `Optimizable::create_strategy` trait signature in
src/strategies/base.rs from `-> Self::Strategy` to
`-> Result<Self::Strategy, StrategyError>`. The default impl now returns
`OperationError(NotSupported)` instead of `unimplemented!()`.
Update all 14 concrete impls (bear/bull call/put spreads, iron condor/
butterfly, call/long/short butterfly, long/short strangle, long/short
straddle, poor_mans_covered_call) to:
- Wrap the constructed strategy in `Ok(...)`.
- Replace `.unwrap()` on `OptionData::call_bid` / `call_ask` /
`put_bid` / `put_ask` with `.ok_or_else(...)?` mapped to
`StrategyError::operation_not_supported(..., "missing <quote> ...")`.
- Replace `.expect("Invalid position")` on builder helpers
(`add_position`, `update_break_even_points`) with `?`.
- Add a `# Errors` section to the doc comment.
Update all ~40 callers:
- Inside `filter_combinations` filter closures: convert
`let strategy = ...; strategy.validate() && ...` to a `match`
returning `false` on `Err(_)`.
- Inside `find_optimal` for-loops: extract the strategy via `match`
with `tracing::warn!(error = %e, "skipping invalid strategy
combination"); continue;` on `Err`.
- Test callers (`#[cfg(test)]`) propagate via `.unwrap()` /
`.expect(...)` per project rules (out of scope of issue #316 grep).
- The two intentionally-bad-leg tests (`call_butterfly`,
`iron_butterfly`, `iron_condor`) now assert `.is_err()` rather than
catching a panic.
Existing `panic!` / `unreachable!` / `unimplemented!` are intentionally
left in place — they belong to issue #292.
`cargo build --all-targets --all-features` clean.
`cargo test --lib strategies::` 1202 passed.
Eliminates ~40 of the 236 production `.unwrap()/.expect()` sites under
src/strategies/ (236 → 196). Issue #316 progress.
Eliminate every production .unwrap() / .expect() from src/strategies/base.rs, short_strangle.rs, and long_strangle.rs. Highlights: - base.rs::is_valid_optimal_option (DeltaRange arm) rewritten with is_some_and so it can no longer panic on missing greeks. - base.rs sort comparators use unwrap_or(Ordering::Equal) with a SAFETY comment; Decimal/Positive are total-ordered, f64 fallback is stable. - base.rs::get_total_cost / get_net_cost / get_net_premium_received switched to ?-propagation via for-loops (signatures unchanged). - short_strangle.rs and long_strangle.rs delta-neutral filter closures bind greeks once and short-circuit to false on None. - find_optimal loops in both strangles match the new fallible create_strategy and continue with tracing::warn! on Err. - get_profit_area / get_profit_ratio map Decimal::from_f64 NaN/Inf to StrategyError::numeric_conversion(...). API change (intentional, M1 panic-free milestone): - ShortStrangle::new(...) and LongStrangle::new(...) now return Result<Self, StrategyError> (was Self). The constructors used to .expect(...) on their own builder helpers; that pattern is replaced with ? propagation. All in-tree callers (tests, examples, dependent strategies) updated. Build: cargo build --all-targets --all-features clean. Tests: cargo test --lib strategies:: 1202 passed; 0 failed. Production unwrap/expect under src/strategies/: 196 -> 138 (58 eliminated this commit). Issue #316 progress.
…ites)
Eliminate every production .unwrap() / .expect() from
src/strategies/iron_condor.rs, iron_butterfly.rs, and custom.rs.
Highlights:
- iron_condor / iron_butterfly: sort comparators in get_strategy use
unwrap_or(Ordering::Equal); find_optimal loops match/continue with
tracing::warn! on unscorable metrics; Decimal::from_f64 in
get_profit_area maps to StrategyError::numeric_conversion(...);
fee_per_leg bound once via ? in create_strategy.
- custom.rs::refine_break_even_point() rewritten so calculate_profit_at
short-circuits via .ok()? instead of panicking; eliminated a latent
duplicated f_x recompute.
- custom.rs::calculate_pnl / calculate_pnl_at_expiration rewritten as
for-loop with ?; probability for_each(...).unwrap() blocks turned
into for ... { ... ? } chains.
API changes (intentional, M1 panic-free milestone):
- IronCondor::new(...) now returns Result<Self, StrategyError> (was
Self). Drops three .expect("Invalid <leg>") and one
.expect("Unable to update break even points").
- IronButterfly::new(...) same change, same rationale.
- CustomStrategy::new(...) now returns Result<Self, StrategyError> (was
Self). Drops .expect("Unable to update break even points"). The
panic!("Invalid strategy: No positions provided") invariant remains
(issue #292) and is documented in # Panics.
All in-tree callers (tests, examples, dependent strategies) updated.
Build: cargo build --all-targets --all-features clean.
Tests: cargo test --lib strategies:: 1202 passed; 0 failed.
Production unwrap/expect under src/strategies/: 138 -> 103
(35 eliminated this commit). Issue #316 progress.
…ites)
Eliminate every production .unwrap() / .expect() from
src/strategies/{call_butterfly, poor_mans_covered_call, short_straddle,
long_straddle}.rs.
Highlights:
- Sort comparators in get_strategy paths use
unwrap_or(Ordering::Equal).
- find_optimal loops match/continue with tracing::warn! on unscorable
metrics.
- Decimal::from_f64 in get_profit_area / get_profit_ratio maps to
StrategyError::numeric_conversion(...).
- call_butterfly::get_profit_ratio chains self.get_max_loss() via ?.
API changes (intentional, M1 panic-free milestone):
- CallButterfly::new(...), PoorMansCoveredCall::new(...),
ShortStraddle::new(...), and LongStraddle::new(...) now return
Result<Self, StrategyError> (were Self). Drops
.expect("Invalid <leg>") and
.expect("Unable to update break even points") from each constructor
body. All in-tree call sites (tests, examples, dependent strategies,
benches) updated.
Build: cargo build --all-targets --all-features clean.
Tests: cargo test --lib strategies:: 1202 passed; 0 failed.
Production unwrap/expect under src/strategies/: 103 -> 71
(32 eliminated this commit). Issue #316 progress.
…(44)
Eliminate every production .unwrap() / .expect() from
src/strategies/{bull,bear}_{call,put}_spread.rs,
{long,short}_butterfly_spread.rs, and graph.rs.
Highlights:
- Sort comparators in every spread's get_strategy path use
unwrap_or(Ordering::Equal) with a SAFETY comment.
- find_optimal loops match/continue with tracing::warn! on unscorable
metrics.
- graph.rs: range.first()/last().unwrap() rewritten as if-let guard;
calculate_profit_at(&price).unwrap() in the per-price loop matches
and continues with tracing::warn! on Err; current_sign.unwrap()
bindings folded into match/let-else patterns.
API changes (intentional, M1 panic-free milestone):
- BullCallSpread::new(...), BearCallSpread::new(...),
BullPutSpread::new(...), BearPutSpread::new(...),
LongButterflySpread::new(...), and ShortButterflySpread::new(...)
now return Result<Self, StrategyError> (were Self). Drops
.expect("Invalid <leg>") and
.expect("Unable to update break even points") from each constructor
body. All in-tree call sites (tests, examples, dependent strategies,
benches) updated.
Build: cargo build --all-targets --all-features clean.
Tests: cargo test --lib strategies:: 1202 passed; 0 failed.
Production unwrap/expect under src/strategies/: 71 -> 27
(44 eliminated this commit). Issue #316 progress.
…tons Eliminate the final 27 production .unwrap() / .expect() under src/strategies/. Total panic-free count for the milestone: 0. Highlights: - probabilities/core.rs: HashMap .values().next().unwrap() → StrategyError::empty_collection(...); get_break_even_points / get_best_range / get_profit_ratio chained via ?. Decimal::to_f64() in expected_value rewritten as a for-loop with NumericConversion fallback. - probabilities/utils.rs: risk_free.to_f64() bound once per arm with ?-propagation into ProbabilityError::StdError; f2du! and big_n() unwraps replaced by ? (existing #[from] DecimalError chain). - delta_neutral/optimizer.rs: best_plan.as_ref().unwrap() rewritten with is_none_or; partial_cmp(...).unwrap() sort comparators → unwrap_or(Ordering::Equal) with SAFETY comment. - delta_neutral/model.rs: per-leg option.delta().unwrap() folded into a for-loop with ? and bound once (was duplicated greek call). - Single-leg / spot-paired strategies (long/short_call, long/short_put, protective_put, covered_call, collar): add_position(...).expect(...) → ?; Positive::new(0.001).unwrap() in simulator path → ?; ProgressStyle::default_bar().template(...).expect(...) → map_err to SimulationError::OtherError. - Doctests in probabilities/mod.rs rewrapped with `# fn run() -> Result<(), Box<dyn std::error::Error>>` to keep ? working after the constructor / analyze_probabilities API tightening. API changes (intentional, M1 panic-free milestone): - LongCall::new(...), LongPut::new(...) (private), ShortCall::new(...) (private), ShortPut::new(...), ProtectivePut::new(...), CoveredCall::new(...), and Collar::new(...) now return Result<Self, StrategyError> (were Self). The trio of single-name spot-paired constructors (protective_put / covered_call / collar) also drop their #[must_use] (Result already carries it). All workspace callers (tests, examples, benches) updated. Build: cargo build --all-targets --all-features clean. Tests: cargo test --lib strategies:: 1202 passed; 0 failed. Doctests: probabilities 6 passed. ACCEPTANCE GREP — production unwrap/expect under src/strategies/: 0. Issue #316 implementation complete.
… fmt+clippy - Add tests_panic_free_variants module in src/error/strategies.rs covering NumericConversion (NaN, Inf), MissingGreek (constructor + display), and EmptyCollection (str + String inputs), plus end-to-end routing through ProbabilityError::From. - Apply cargo fmt --all and cargo clippy --fix --all-targets --all-features --workspace to the post-refactor strategy files. No behavioural changes — purely formatting and clippy-suggested simplifications (collapsing match arms into single expressions, splitting builder calls onto fresh lines). Acceptance grep on src/strategies/: 0 production .unwrap()/.expect() sites remain. cargo build --all-targets --all-features: clean. cargo clippy --all-targets --all-features --workspace -- -D warnings: clean. cargo test --lib (default features): 3723 passed; 0 failed; 5 ignored. Note: tests/unit visualization tests (visualization::plotly_render_test::*) require the plotly-static Chrome/Kaleido binary to render PNG/SVG and fail in environments where it is unavailable. Those failures pre-date this PR (verified on main prior to branching) and are unrelated to the panic-free refactor.
- Update version references across library documentation, README, and `Cargo.toml` to reflect v0.16.0. - Refactor: simplify match arm in `delta_neutral::optimizer.rs`.
There was a problem hiding this comment.
Pull request overview
This PR advances the “panic-free core” initiative by eliminating production .unwrap()/.expect() usage under src/strategies/ and making key strategy construction/optimization paths fallible via Result<_, StrategyError>.
Changes:
- Introduces new typed
StrategyErrorvariants and uses them to replace prior panics/unwraps atf64↔Decimal, missing greeks, and empty-collection boundaries. - Makes
Optimizable::create_strategyand many strategy constructors returnResult, updating in-tree callers (tests/examples/benches) accordingly. - Reworks several optimization/sorting/graph/probability code paths to avoid panics by using
?,ok_or_else, and safe comparator fallbacks.
Reviewed changes
Copilot reviewed 94 out of 177 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/strategies/simple/strategy_short_strangle.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/simple/strategy_short_straddle.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/simple/strategy_short_butterfly_spread.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/simple/strategy_poor_mans_covered_call.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/simple/strategy_long_strangle.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/simple/strategy_long_straddle.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/simple/strategy_long_butterfly_spread.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/simple/strategy_iron_condor.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/simple/strategy_iron_butterfly.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/simple/strategy_graph.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/simple/strategy_call_butterfly.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/simple/strategy_bull_put_spread.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/simple/strategy_bull_call_spread.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/simple/strategy_bear_put_spread.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/simple/strategy_bear_call_spread.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/protective_put_test.rs | Update helper to unwrap new fallible constructor. |
| tests/unit/strategies/custom_test.rs | Update helpers to unwrap new fallible constructor. |
| tests/unit/strategies/optimal_center/strategy_short_strangle.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/optimal_center/strategy_short_straddle.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/optimal_center/strategy_short_butterfly_spread.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/optimal_center/strategy_poor_mans_covered_call.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/optimal_center/strategy_long_strangle.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/optimal_center/strategy_long_straddle.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/optimal_center/strategy_long_butterfly_spread.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/optimal_center/strategy_iron_condor.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/optimal_center/strategy_iron_butterfly.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/optimal_center/strategy_call_butterfly.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/optimal_center/strategy_bull_put_spread.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/optimal_center/strategy_bull_call_spread.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/optimal_center/strategy_bear_put_spread.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/optimal_center/strategy_bear_call_spread.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/optimal/strategy_short_strangle.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/optimal/strategy_short_straddle.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/optimal/strategy_short_butterfly_spread.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/optimal/strategy_poor_mans_covered_call.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/optimal/strategy_long_strangle.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/optimal/strategy_long_straddle.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/optimal/strategy_long_butterfly_spread.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/optimal/strategy_iron_condor.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/optimal/strategy_iron_butterfly.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/optimal/strategy_call_butterfly.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/optimal/strategy_bull_put_spread.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/optimal/strategy_bull_call_spread.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/optimal/strategy_bear_put_spread.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/optimal/strategy_bear_call_spread.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/delta/strategy_short_strangle.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/delta/strategy_short_straddle.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/delta/strategy_short_butterfly_spread.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/delta/strategy_poor_mans_covered_call.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/delta/strategy_long_strangle.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/delta/strategy_long_straddle.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/delta/strategy_long_butterfly_spread.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/delta/strategy_iron_condor.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/delta/strategy_iron_butterfly.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/delta/strategy_call_butterfly.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/delta/strategy_bull_put_spread.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/delta/strategy_bull_call_spread.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/delta/strategy_bear_put_spread.rs | Propagate fallible constructor via ?. |
| tests/unit/strategies/delta/strategy_bear_call_spread.rs | Propagate fallible constructor via ?. |
| src/strategies/short_strangle.rs | Make constructor fallible; remove unwraps; harden optimizer/scoring. |
| src/strategies/short_straddle.rs | Make constructor fallible; remove unwraps; harden optimizer/scoring. |
| src/strategies/short_butterfly_spread.rs | Make constructor fallible; remove unwraps; harden optimizer/scoring. |
| src/strategies/poor_mans_covered_call.rs | Make constructor fallible; remove unwraps; harden optimizer/scoring. |
| src/strategies/long_strangle.rs | Make constructor fallible; remove unwraps; harden optimizer/scoring. |
| src/strategies/long_straddle.rs | Make constructor fallible; remove unwraps; harden optimizer/scoring. |
| src/strategies/call_butterfly.rs | Make constructor fallible; remove unwraps; harden optimizer/scoring. |
| src/strategies/bull_put_spread.rs | Make constructor fallible; remove unwraps; harden optimizer/scoring. |
| src/strategies/bull_call_spread.rs | Make constructor fallible; remove unwraps; harden optimizer/scoring. |
| src/strategies/bear_put_spread.rs | Make constructor fallible; remove unwraps; harden optimizer/scoring. |
| src/strategies/iron_butterfly.rs | Make constructor fallible; remove unwraps; harden optimizer/scoring. |
| src/strategies/protective_put.rs | Make constructor fallible; remove break-even unwrap/expect. |
| src/strategies/covered_call.rs | Make constructor fallible; remove break-even unwrap/expect. |
| src/strategies/collar.rs | Make constructor fallible; remove break-even unwrap/expect. |
| src/strategies/custom.rs | Make constructor fallible; remove unwraps across optimization/probability/pnl. |
| src/strategies/short_put.rs | Make constructor fallible; remove simulation/template and small-positive unwraps. |
| src/strategies/short_call.rs | Make constructor fallible; remove simulation/template and small-positive unwraps. |
| src/strategies/long_call.rs | Make constructor fallible; remove simulation/template and small-positive unwraps. |
| src/strategies/long_put.rs | Make constructor fallible; remove simulation/template and small-positive unwraps. |
| src/strategies/long_butterfly_spread.rs | Make constructor fallible; remove unwraps; harden optimizer/scoring. |
| src/strategies/graph.rs | Avoid panics during range/profit plotting; skip unscorable points. |
| src/strategies/base.rs | Remove unwraps in shared strategy helpers; make default create_strategy fallible. |
| src/strategies/probabilities/mod.rs | Update rustdoc examples to propagate new fallible APIs. |
| src/strategies/probabilities/utils.rs | Remove unwraps around Decimal/f64 conversions and distribution helpers. |
| src/strategies/probabilities/core.rs | Remove unwraps in probability analysis; add empty-collection error paths. |
| src/strategies/delta_neutral/optimizer.rs | Remove unwraps; safe comparisons; avoid best-plan unwrap. |
| src/strategies/delta_neutral/model.rs | Remove unwraps when collecting per-position deltas. |
| src/error/strategies.rs | Add new typed StrategyError variants plus cold constructors and tests. |
| src/error/probability.rs | Map new StrategyError variants into ProbabilityError. |
| examples/examples_strategies_delta/src/bin/strategy_short_strangle_extended_delta.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_delta/src/bin/strategy_short_strangle_delta.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_delta/src/bin/strategy_short_straddle_extended_delta.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_delta/src/bin/strategy_short_straddle_delta.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_delta/src/bin/strategy_short_butterfly_spread_extended_delta.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_delta/src/bin/strategy_short_butterfly_spread_delta.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_delta/src/bin/strategy_pmcc_extended_delta.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_delta/src/bin/strategy_pmcc_delta.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_delta/src/bin/strategy_long_strangle_extended_delta.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_delta/src/bin/strategy_long_strangle_delta.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_delta/src/bin/strategy_long_straddle_extended_delta.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_delta/src/bin/strategy_long_straddle_delta.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_delta/src/bin/strategy_long_butterfly_spread_extended_delta.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_delta/src/bin/strategy_long_butterfly_spread_delta.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_delta/src/bin/strategy_iron_condor_extended_delta.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_delta/src/bin/strategy_iron_condor_delta.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_delta/src/bin/strategy_iron_butterfly_extended_delta.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_delta/src/bin/strategy_iron_butterfly_delta.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_delta/src/bin/strategy_call_butterfly_extended_delta.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_delta/src/bin/strategy_call_butterfly_delta.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_delta/src/bin/strategy_bull_put_spread_extended_delta.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_delta/src/bin/strategy_bull_put_spread_delta.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_delta/src/bin/strategy_bull_call_spread_extended_delta.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_delta/src/bin/strategy_bull_call_spread_delta.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_delta/src/bin/strategy_bear_put_spread_extended_delta.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_delta/src/bin/strategy_bear_put_spread_delta.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_delta/src/bin/strategy_bear_call_spread_extended_delta.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_delta/src/bin/strategy_bear_call_spread_delta.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_best/src/bin/strategy_short_strangle_best_ratio.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_best/src/bin/strategy_short_strangle_best_area.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_best/src/bin/strategy_short_straddle_best_ratio.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_best/src/bin/strategy_short_straddle_best_area.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_best/src/bin/strategy_short_butterfly_spread_best_ratio.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_best/src/bin/strategy_short_butterfly_spread_best_area.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_best/src/bin/strategy_poor_mans_covered_call_best_ratio.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_best/src/bin/strategy_poor_mans_covered_call_best_area.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_best/src/bin/strategy_long_strangle_best_ratio.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_best/src/bin/strategy_long_strangle_best_area.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_best/src/bin/strategy_long_straddle_best_ratio.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_best/src/bin/strategy_long_straddle_best_area.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_best/src/bin/strategy_long_butterfly_spread_best_ratio.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_best/src/bin/strategy_long_butterfly_spread_best_area.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_best/src/bin/strategy_iron_condor_best_ratio.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_best/src/bin/strategy_iron_condor_best_area.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_best/src/bin/strategy_iron_butterfly_best_ratio.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_best/src/bin/strategy_iron_butterfly_best_area.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_best/src/bin/strategy_call_butterfly_best_ratio.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_best/src/bin/strategy_call_butterfly_best_area.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_best/src/bin/strategy_bull_put_spread_best_ratio.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_best/src/bin/strategy_bull_put_spread_best_area.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_best/src/bin/strategy_bull_call_spread_best_ratio.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_best/src/bin/strategy_bull_call_spread_best_area.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_best/src/bin/strategy_bear_put_spread_best_ratio.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_best/src/bin/strategy_bear_put_spread_best_area.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_best/src/bin/strategy_bear_call_spread_best_ratio.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies_best/src/bin/strategy_bear_call_spread_best_area.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies/src/bin/strategy_short_strangle_delta_simple.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies/src/bin/strategy_short_strangle.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies/src/bin/strategy_short_straddle.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies/src/bin/strategy_short_butterfly_spread.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies/src/bin/strategy_poor_mans_covered_call.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies/src/bin/strategy_long_strangle.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies/src/bin/strategy_long_straddle.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies/src/bin/strategy_long_butterfly_spread.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies/src/bin/strategy_iron_condor.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies/src/bin/strategy_iron_butterfly.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies/src/bin/strategy_graph.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies/src/bin/strategy_custom_simple.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies/src/bin/strategy_custom_short_strangle.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies/src/bin/strategy_custom_dax.rs | Use .unwrap() to adapt to fallible constructor. |
| examples/examples_strategies/src/bin/strategy_custom_complex.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies/src/bin/strategy_call_butterfly.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies/src/bin/strategy_bull_put_spread.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies/src/bin/strategy_bull_call_spread.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies/src/bin/strategy_bear_put_spread.rs | Propagate fallible constructor via ?. |
| examples/examples_strategies/src/bin/strategy_bear_call_spread.rs | Propagate fallible constructor via ?. |
| examples/examples_simulation/src/bin/strategy_simulator.rs | Propagate fallible constructor via ?. |
| examples/examples_simulation/src/bin/short_put_strategy_simulation.rs | Propagate fallible constructor via ?. |
| examples/examples_simulation/src/bin/long_call_strategy_simulation.rs | Propagate fallible constructor via ?. |
| examples/examples_chain/src/bin/option_chain_raw_delta.rs | Propagate fallible constructor via ?. |
| examples/examples_chain/src/bin/option_chain_raw.rs | Propagate fallible constructor via ?. |
| examples/examples_chain/src/bin/option_chain_ger40.rs | Propagate fallible constructor via ?. |
| examples/examples_chain/src/bin/creator.rs | Propagate fallible constructor via ?. |
| benches/model/strategy.rs | Adapt benchmarks to fallible constructors via .unwrap(). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| let mut expected_value = 0.0_f64; | ||
| for (price, prob) in range.iter().zip(probabilities.iter()) { | ||
| let profit_dec = self.calculate_profit_at(price)?; | ||
| let profit = profit_dec | ||
| .to_f64() | ||
| .ok_or_else(|| StrategyError::numeric_conversion(0.0))?; | ||
| expected_value += profit * prob.to_f64(); |
There was a problem hiding this comment.
expected_value maps a Decimal -> f64 conversion failure to StrategyError::numeric_conversion(0.0), which (a) hard-codes an unrelated value (0.0) into the error and (b) uses an error variant/message that describes the opposite direction (f64 -> Decimal). Please return an error that preserves the failing profit_dec (or introduce a dedicated DecimalToF64Conversion/contextful variant) so callers get an accurate diagnostic.
Three doctests at the public-API surface still called the old
infallible constructors and were left chaining methods on the new
Result return value:
- src/lib.rs (Working with Trading Strategies): BullCallSpread::new
now requires `?` to extract the strategy.
- src/lib.rs (Custom Strategy Creation): CustomStrategy::new gains
.expect("valid custom strategy") so the trailing get_title() call
resolves.
- src/strategies/mod.rs (Bull Call Spread + Iron Condor examples):
BullCallSpread::new and IronCondor::new gain .expect(...) for the
same reason.
- src/strategies/bull_put_spread.rs::filter_combinations example:
BullPutSpread::new gains .expect("valid bull put spread") so the
filter_combinations call works on the resolved strategy.
cargo test --doc: 203 passed; 0 failed; 61 ignored.
`make readme` (cargo readme) picks up the BullCallSpread::new(...)? in the "Working with Trading Strategies" example added in PR #347 to match the new fallible constructor. Trivial 4-line diff.
Closes #316.
Summary
Remove every production
.unwrap()and.expect()fromsrc/strategies/(M1 — Panic-Free Core, 0.16.0). Total: 236 sites across 29 files → 0.Acceptance grep is clean:
Approach
StrategyErrorwith three typed variants (NumericConversion { value: f64 },MissingGreek { name: &'static str },EmptyCollection { context: String }) plus#[cold]constructors.Optimizable::create_strategy(...)from-> Self::Strategyto-> Result<Self::Strategy, StrategyError>(default impl now returnsOperationError(NotSupported)instead ofunimplemented!()).::new(...)fromSelftoResult<Self, StrategyError>for the strategies whose constructors used.expect("Invalid <leg>")/.expect("Unable to update break even points"). All in-tree callers (tests, examples, benches, dependent strategies) updated.?propagation.Option→.ok_or_else(StrategyError::...)?mapped to the most specific variant.partial_cmp(...).unwrap()):unwrap_or(Ordering::Equal)with SAFETY comment.is_valid_optimal_option: rewritten withis_some_andso no panic on missing greeks.refine_break_even_point() -> Option<Positive>: short-circuits via.ok()?.panic!/unreachable!/unimplemented!/assert!are intentionally left in place — they belong to issue #292.Public API impact (intentional)
The following constructors changed return type from
SelftoResult<Self, StrategyError>:ShortStrangle::new,LongStrangle::new,IronCondor::new,IronButterfly::new,CustomStrategy::new,CallButterfly::new,PoorMansCoveredCall::new,ShortStraddle::new,LongStraddle::new,BullCallSpread::new,BearCallSpread::new,BullPutSpread::new,BearPutSpread::new,LongButterflySpread::new,ShortButterflySpread::new,LongCall::new,LongPut::new(private),ShortCall::new(private),ShortPut::new,ProtectivePut::new,CoveredCall::new,Collar::new.Plus trait-level:
Optimizable::create_strategyis now fallible (covers all impls).Verification
cargo build --all-targets --all-features— clean.cargo clippy --all-targets --all-features --workspace -- -D warnings— clean.cargo fmt --all --check— clean.cargo test --lib(default features) — 3723 passed; 0 failed; 5 ignored.cargo test --features plotly --lib— 3719 passed; 0 failed; 5 ignored.cargo build --release— clean.tests/unit/visualization::plotly_render_test::*PNG/SVG render tests fail in environments without the plotly-static Chrome/Kaleido binary; this is pre-existing and unrelated.Commit map
feat(error): add NumericConversion, MissingGreek, EmptyCollection variantsrefactor(strategies): make Optimizable::create_strategy fallible(40 sites)refactor(strategies): panic-free base + short/long strangle (58 sites)refactor(strategies): panic-free iron condor/butterfly + custom (35 sites)refactor(strategies): panic-free butterflies + pmcc + straddles (32 sites)refactor(strategies): panic-free spreads + butterfly_spreads + graph (44)refactor(strategies): panic-free probabilities, delta_neutral, singletons (27)test(strategies): add negative tests for new StrategyError variants + fmt+clippyTest plan
cargo test --lib --all-featurescleancargo clippy --all-targets --all-features --workspace -- -D warningscleancargo fmt --all --checkcleancargo build --releaseclean