Use case · Rank
Rank candidates by expected value under constraints
Construction emits everything feasible. Ranker imposes the trader's priorities — rank by expected value, capital efficiency, or a custom composite, with hard constraints on capital, PoP floor, max-loss ceiling, and DTE window. Output is a sorted Vec<RankedCandidate> with the metric and the constraint trace.
When to use it
- You have a constructed candidate set and need it ordered by a single, explicit objective.
- You want hard constraints (capital, PoP, max-loss) applied as typed ranges, not ad-hoc filtering.
- You want the constraint trace retained on each ranked candidate for audit.
Example
use ferro_spread::{Ranker, RankingMetric};
let ranker = Ranker::default()
.by(RankingMetric::ExpectedValue)
.constrain(RankingMetric::CapitalRequired,
0.0..=1_000.0)
.constrain(RankingMetric::ProbabilityOfProfit,
0.60..)
.constrain(RankingMetric::MaxLoss,
..=500.0);
let ranked = ranker.rank(&candidates);
for r in ranked.iter().take(10) {
println!("{} → EV={:.2}, PoP={:.0}%",
r.candidate.label, r.ev, r.pop * 100.0);
}Notes
- Constraints are typed ranges over the same metrics used for ranking — half-open ranges express floors and ceilings.
- The full ordering survives in
Vec<RankedCandidate>; each entry carries its rank, the metric value, and which constraints bound it.
Explain
Expand any ranked candidate into a typed Explanation to see which score components drove its rank.