nautechsystems/nautilus_trader · error
Price must be greater than 1.0 for lay liability calculation
Error message
Price must be greater than 1.0 for lay liability calculation, was {price} What it means
check_odds_gt_one rejects odds of 1.0 or below when constructing a bet from a lay liability. Lay liability is computed via 1/odds arithmetic, which is only meaningful (and non-negative/finite) for odds strictly greater than 1.0. The guard bails with the offending price embedded in the message.
Source
Thrown at crates/model/src/data/bet.rs:752
///
/// The side is also inverted (BUY becomes SELL and vice versa).
///
/// # Errors
///
/// Returns an error if `probability` is 1.0 or its inverse is zero.
pub fn inverse_probability_to_bet(
probability: Decimal,
volume: Decimal,
side: OrderSide,
) -> anyhow::Result<Bet> {
check_probability_invertible(probability)?;
let inverse_probability = checked_sub(Decimal::ONE, probability)?;
probability_to_bet(inverse_probability, volume, side.opposite())
}
fn check_odds_gt_one(price: Decimal) -> anyhow::Result<()> {
if price <= Decimal::ONE {
anyhow::bail!("Price must be greater than 1.0 for lay liability calculation, was {price}");
}
Ok(())
}
fn check_nonzero_denominator(value: Decimal, name: &str) -> anyhow::Result<()> {
if value.is_zero() {
anyhow::bail!("invalid {name}: must be non-zero")
}
Ok(())
}
fn checked_add(lhs: Decimal, rhs: Decimal) -> anyhow::Result<Decimal> {
lhs.checked_add(rhs)
.ok_or_else(|| anyhow::anyhow!("Decimal overflow adding {lhs} and {rhs}"))
}
fn checked_sub(lhs: Decimal, rhs: Decimal) -> anyhow::Result<Decimal> {
lhs.checked_sub(rhs)View on GitHub (pinned to 18893faf8b)
Solutions
- Pass decimal odds strictly greater than 1.0; validate the value before constructing the bet.
- Check whether a probability is being passed where decimal odds are required and convert (odds = 1/probability) first.
- Reject or skip records with odds <= 1.0 at ingestion instead of passing them into from_liability_checked.
Example fix
// before
let bet = Bet::from_liability_checked(Decimal::ZERO, liability)?; // odds <= 1.0
// after
anyhow::ensure!(odds > Decimal::ONE, "lay odds must be > 1.0, got {odds}");
let bet = Bet::from_liability_checked(odds, liability)?; Defensive patterns
Strategy: validation
Validate before calling
// Rust
use rust_decimal::Decimal;
fn valid_lay_odds(p: Decimal) -> bool { p > Decimal::ONE }
// Python
# assert price > 1.0, f"lay odds must be > 1.0, got {price}" Try / catch
// Rust
match Bet::from_liability_checked(price, liability) {
Ok(b) => b,
Err(e) if e.to_string().starts_with("Price must be greater than 1.0") => skip_record(price),
Err(e) => return Err(e),
} Prevention
- Distinguish probabilities (0..1) from decimal odds (>1) with distinct types or field names.
- Sanity-check feed data ranges at ingestion and drop placeholder odds of 1.0.
- Reject non-positive prices before any liability math.
When it happens
Trigger: Calling Bet::from_liability_checked (or its internal check_odds_gt_one) with price <= Decimal::ONE, e.g. odds of exactly 1.0, 0.0, or a negative value.
Common situations: Feeds delivering pre-race odds of 1.0 as a placeholder; unit errors where probability (0-1) is passed where decimal odds (>=1) are expected; uninitialized/zero price fields.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- invalid probability: must not be 1.0 (inverse would be zero)
- invalid OrderSide: must be Buy or Sell, was {side}
- {e}
- Polymarket market BUY amount {} pUSD truncates to zero at {L
- Liability-based betting is only applicable for Lay side.
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/2d39e159339455d6.
Report an issue: GitHub.