HKUDS/Vibe-Trading · error · ValueError

coupon_rate cannot be negative, got {rate!r}

Error message

coupon_rate cannot be negative, got {rate!r}

What it means

Bond.__post_init__ rejects coupon_rate values below zero after float coercion. Negative coupons are treated as data errors (a coupon rate cannot be a cash outflow for the holder).

Source

Thrown at agent/src/entities/models.py:367

    def __post_init__(self) -> None:
        """Validate the base fields plus bond-specific ranges and consistency.

        Raises:
            ValueError: If ``face_value`` is non-positive, ``coupon_rate`` is
                negative or expressed as a percentage, the coupon frequency
                contradicts the coupon rate, or maturity precedes inception.
        """
        super().__post_init__()
        if self.face_value is not None:
            face = float(self.face_value)
            if face <= 0:
                raise ValueError(f"face_value must be positive, got {self.face_value!r}")
            object.__setattr__(self, "face_value", face)
        if self.coupon_rate is not None:
            rate = float(self.coupon_rate)
            if rate < 0:
                raise ValueError(f"coupon_rate cannot be negative, got {rate!r}")
            if rate > 1.0:
                raise ValueError(
                    f"coupon_rate must be a decimal fraction (0.05 means 5%), "
                    f"got {rate!r}; a value above 1.0 is almost certainly a "
                    "percentage"
                )
            object.__setattr__(self, "coupon_rate", rate)
        frequency = int(self.coupon_frequency)
        if frequency < 0:
            raise ValueError(
                f"coupon_frequency cannot be negative, got {self.coupon_frequency!r}"
            )
        if self.coupon_rate and frequency == 0:
            raise ValueError(
                f"coupon_frequency=0 marks a zero-coupon bond, but coupon_rate="
                f"{self.coupon_rate!r} is non-zero"
            )
        object.__setattr__(self, "coupon_frequency", frequency)

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Pass the unsigned coupon rate
  2. If negative rates are genuinely required (rare exotic contracts), model them outside this Bond class or extend validation consciously
  3. Scrub ingest data for negative rates and route them to an exceptions report

Example fix

# before
Bond(instrument_id='b1', coupon_rate=-0.05)

# after
Bond(instrument_id='b1', coupon_rate=0.05)
Defensive patterns

Strategy: validation

Validate before calling

cr = row.get('coupon_rate')
if cr is not None:
    cr = float(cr)
    if cr < 0:
        raise ValueError(f'negative coupon_rate {cr} in row {row_id}')
Bond(..., coupon_rate=cr)

Type guard

def is_valid_coupon_rate(r) -> bool:
    return r is None or float(r) >= 0

Prevention

When it happens

Trigger: Bond(..., coupon_rate=-0.05), or a feed that encodes issuer-paid vs receiver-paid swap-style conventions with signs. A rate of exactly 0 is allowed (zero-coupon) provided coupon_frequency is also 0.

Common situations: Reusing signed-rate conventions from derivatives pricing; minus signs introduced by spreadsheet formulas or OCR; corrupt rows in vendor files.

Related errors


AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28). Data as JSON: /api/errors/98b6c70921da72e0. Report an issue: GitHub.