Overview

Getting started

Add the engine, build a ChainSnapshot, construct candidates with a SpreadBuilder, and rank them — in a few lines.

Add the dependency

FerroSpread is a path dependency on the engine crates within the MorphIQ Labs workspace:

# Cargo.toml
[dependencies]
ferro-spread = { path = "../ferro-spread" }

Pricing is supplied by FerroRisk; FerroSpread constructs and ranks over the FerroRisk parametric IV surface rather than re-deriving option prices.

Construct your first spread

Construction takes a typed ChainSnapshot and a SpreadBuilder policy, and returns a vector of priced SpreadCandidates:

use ferro_spread::{ChainSnapshot, SpreadBuilder, SpreadStrategy};

let chain = ChainSnapshot::from_enriched(quote_snapshot)?;

let policy = SpreadBuilder::default()
    .strategy(SpreadStrategy::BullCallDebit)
    .dte_range(21..=45)
    .target_delta(0.30)
    .min_wing_pts(5.0)
    .max_wing_pts(20.0);

let candidates: Vec<SpreadCandidate> = policy.construct(&chain)?;
// Each candidate carries legs, debit, max profit, max loss,
// breakeven, and a PricingTrace back to the FerroRisk fit.

…then rank

Rankerimposes the trader's priorities — rank by a metric, under hard constraints — and returns a sorted Vec<RankedCandidate>:

use ferro_spread::{Ranker, RankingMetric};

let ranker = Ranker::default()
    .by(RankingMetric::ExpectedValue)
    .constrain(RankingMetric::ProbabilityOfProfit, 0.60..)
    .constrain(RankingMetric::MaxLoss, ..=500.0);

let ranked = ranker.rank(&candidates);
Explainability

Every ranked candidate can be expanded into a typed Explanation — score breakdown, dominant Greeks, comparables, and the assumptions trail. See explain a candidate.

Where to go next

  • Conventions — units, probabilities, and the capital/risk definitions behind the scores.
  • Strategies — the full construction taxonomy.
  • Universe screen — run the construct/rank pipeline across a whole watchlist.