HKUDS/Vibe-Trading · error · ValueError

coupon_frequency=0 marks a zero-coupon bond, but coupon_rate

Error message

coupon_frequency=0 marks a zero-coupon bond, but coupon_rate={self.coupon_rate!r} is non-zero

What it means

Bond.__post_init__ enforces consistency: coupon_frequency=0 marks a zero-coupon bond, so a non-zero coupon_rate alongside frequency 0 is contradictory and rejected. A truthy coupon_rate with frequency 0 triggers the error.

Source

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

            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)
        if self.maturity_date is not None:
            object.__setattr__(
                self,
                "maturity_date",
                normalize_date(self.maturity_date, field_name="maturity_date"),
            )
            if self.inception_date is not None and self.maturity_date < self.inception_date:
                raise ValueError(
                    f"maturity_date {self.maturity_date} precedes inception_date "
                    f"{self.inception_date}"
                )

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Set coupon_frequency to the actual pay frequency (>=1) when coupon_rate is non-zero
  2. Or set coupon_rate=None/0.0 when the bond truly is zero-coupon
  3. Avoid defaults that pick frequency=0; make both fields explicit at construction

Example fix

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

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

Strategy: validation

Validate before calling

cr = row.get('coupon_rate')
freq = int(row.get('coupon_frequency', 0) or 0)
if cr and freq == 0:
    freq = 2  # or raise: inconsistent source data
Bond(..., coupon_rate=cr, coupon_frequency=freq)

Type guard

def coupon_fields_consistent(rate, freq) -> bool:
    return rate is None or not rate or int(freq) != 0

Prevention

When it happens

Trigger: Bond(..., coupon_rate=0.05, coupon_frequency=0) — e.g. a defaulted frequency of 0 combined with a real coupon from the data, or coupon_rate=0.0 with frequency 2 is fine but the inverse combination is not.

Common situations: Constructor defaults setting coupon_frequency=0 while caller supplies coupon_rate; partial updates where one field is refreshed and the other left at its zero-coupon default; merging records from sources with different conventions.

Related errors


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