{
  "format_version": "1.0.0",
  "basis_name": "Open Treasury Yield Basis",
  "basis_version": "1.1.0",
  "hash_rule": "lowercase SHA-256 hex of the UTF-8 bytes of JSON.stringify(input)",
  "vectors": [
    {
      "id": "spec-eutbl-30-day",
      "description": "The EUTBL worked example from section 6.1, including both published annualisations.",
      "input": {
        "as_of_date": "2026-07-30",
        "source": { "id": "vector.eutbl", "carries_timestamp": true, "max_age_days": 5 },
        "observations": [
          { "as_of_date": "2026-06-29", "value": "1.05408500", "decimals": 8 },
          { "as_of_date": "2026-07-04", "value": "1.05439566", "decimals": 8 },
          { "as_of_date": "2026-07-09", "value": "1.05470633", "decimals": 8 },
          { "as_of_date": "2026-07-14", "value": "1.05501700", "decimals": 8 },
          { "as_of_date": "2026-07-19", "value": "1.05532766", "decimals": 8 },
          { "as_of_date": "2026-07-24", "value": "1.05563833", "decimals": 8 },
          { "as_of_date": "2026-07-29", "value": "1.05594900", "decimals": 8 }
        ]
      },
      "input_sha256": "0f92d1fb2d2548304345bbfab4fe913ed7d4126726354077579a74cea01f5305",
      "expected": { "ok": true, "yield_basis": "measured", "window": { "start_date": "2026-06-29", "end_date": "2026-07-29", "actual_days": 30, "distinct_observations": 7, "largest_gap_days": 5, "max_age_days": 5 }, "period_return_pct": 0.176836, "yield_pct": 2.151503, "yield_pct_continuous": 2.174814, "annualisation": "simple", "day_count": "actual/365" }
    },
    {
      "id": "window-28-days",
      "description": "The lower admissible window boundary is accepted.",
      "input": {
        "as_of_date": "2026-01-30",
        "source": { "id": "vector.28", "carries_timestamp": true, "max_age_days": 5 },
        "observations": [
          { "as_of_date": "2026-01-01", "value": "100.00", "decimals": 2 },
          { "as_of_date": "2026-01-05", "value": "100.04", "decimals": 2 },
          { "as_of_date": "2026-01-09", "value": "100.08", "decimals": 2 },
          { "as_of_date": "2026-01-13", "value": "100.12", "decimals": 2 },
          { "as_of_date": "2026-01-17", "value": "100.16", "decimals": 2 },
          { "as_of_date": "2026-01-21", "value": "100.20", "decimals": 2 },
          { "as_of_date": "2026-01-25", "value": "100.24", "decimals": 2 },
          { "as_of_date": "2026-01-29", "value": "100.28", "decimals": 2 }
        ]
      },
      "input_sha256": "dbd47c1d155b0799d0d21a63cc11297396c932e52a7f5e2f4297eb0ac63c0100",
      "expected": { "ok": true, "yield_basis": "measured", "window": { "start_date": "2026-01-01", "end_date": "2026-01-29", "actual_days": 28, "distinct_observations": 8, "largest_gap_days": 4, "max_age_days": 5 }, "period_return_pct": 0.28, "yield_pct": 3.65, "yield_pct_continuous": 3.71743, "annualisation": "simple", "day_count": "actual/365" }
    },
    {
      "id": "window-32-days",
      "description": "The upper admissible window boundary is accepted.",
      "input": {
        "as_of_date": "2026-03-06",
        "source": { "id": "vector.32", "carries_timestamp": true, "max_age_days": 5 },
        "observations": [
          { "as_of_date": "2026-02-01", "value": "100.00", "decimals": 2 },
          { "as_of_date": "2026-02-05", "value": "100.04", "decimals": 2 },
          { "as_of_date": "2026-02-09", "value": "100.08", "decimals": 2 },
          { "as_of_date": "2026-02-13", "value": "100.12", "decimals": 2 },
          { "as_of_date": "2026-02-17", "value": "100.16", "decimals": 2 },
          { "as_of_date": "2026-02-21", "value": "100.20", "decimals": 2 },
          { "as_of_date": "2026-02-25", "value": "100.24", "decimals": 2 },
          { "as_of_date": "2026-03-01", "value": "100.28", "decimals": 2 },
          { "as_of_date": "2026-03-05", "value": "100.32", "decimals": 2 }
        ]
      },
      "input_sha256": "16eaa957ceb235613c9b7fba9b0edb75689f5f8c0e28a0f0e8678c2752a6c334",
      "expected": { "ok": true, "yield_basis": "measured", "window": { "start_date": "2026-02-01", "end_date": "2026-03-05", "actual_days": 32, "distinct_observations": 9, "largest_gap_days": 4, "max_age_days": 5 }, "period_return_pct": 0.32, "yield_pct": 3.65, "yield_pct_continuous": 3.71743, "annualisation": "simple", "day_count": "actual/365" }
    },
    {
      "id": "window-27-days-rejected",
      "description": "A 27-day series is below the lower boundary and produces no figure.",
      "input": {
        "as_of_date": "2026-01-29",
        "source": { "id": "vector.27", "carries_timestamp": true, "max_age_days": 5 },
        "observations": [
          { "as_of_date": "2026-01-01", "value": "100.00", "decimals": 2 },
          { "as_of_date": "2026-01-05", "value": "100.04", "decimals": 2 },
          { "as_of_date": "2026-01-09", "value": "100.08", "decimals": 2 },
          { "as_of_date": "2026-01-13", "value": "100.12", "decimals": 2 },
          { "as_of_date": "2026-01-17", "value": "100.16", "decimals": 2 },
          { "as_of_date": "2026-01-21", "value": "100.20", "decimals": 2 },
          { "as_of_date": "2026-01-25", "value": "100.24", "decimals": 2 },
          { "as_of_date": "2026-01-28", "value": "100.27", "decimals": 2 }
        ]
      },
      "input_sha256": "eb6a5879da071c61084a46515ce06378fc1ceb0363214fd5db565134540b44f1",
      "expected": { "ok": false, "yield_basis": "unavailable", "reason_code": "NO_ADMISSIBLE_WINDOW", "reason": "the recorded series spans 27 days, below the 28-day minimum window" }
    },
    {
      "id": "mixed-decimals",
      "description": "Every observation carries its own decimals; no shared token precision is assumed.",
      "input": {
        "as_of_date": "2026-06-01",
        "source": { "id": "vector.decimals", "carries_timestamp": true, "max_age_days": 5 },
        "observations": [
          { "as_of_date": "2026-05-01", "value": "100.000000", "decimals": 6 },
          { "as_of_date": "2026-05-06", "value": "100.05000000", "decimals": 8 },
          { "as_of_date": "2026-05-11", "value": "100.100000000000000000", "decimals": 18 },
          { "as_of_date": "2026-05-16", "value": "100.15000", "decimals": 5 },
          { "as_of_date": "2026-05-21", "value": "100.200000", "decimals": 6 },
          { "as_of_date": "2026-05-26", "value": "100.25000000", "decimals": 8 },
          { "as_of_date": "2026-05-31", "value": "100.300000000000000000", "decimals": 18 }
        ]
      },
      "input_sha256": "6fbe70a9e1633e6516da9cf58d0b09322772c7b0d4b9bb564eb7612686384154",
      "expected": { "ok": true, "yield_basis": "measured", "window": { "start_date": "2026-05-01", "end_date": "2026-05-31", "actual_days": 30, "distinct_observations": 7, "largest_gap_days": 5, "max_age_days": 5 }, "period_return_pct": 0.3, "yield_pct": 3.65, "yield_pct_continuous": 3.71743, "annualisation": "simple", "day_count": "actual/365" }
    },
    {
      "id": "supply-accrual-passive-holder",
      "description": "A verified passive-holder balance is the observable for supply accrual.",
      "input": {
        "as_of_date": "2026-08-01",
        "source": { "id": "vector.passive", "carries_timestamp": true, "max_age_days": 5, "family": "supply_accrual", "observable": "holder_balance", "passivity_verified": true },
        "observations": [
          { "as_of_date": "2026-07-01", "value": "47940547.349210462890046092", "decimals": 18 },
          { "as_of_date": "2026-07-06", "value": "47960547.349210462890046092", "decimals": 18 },
          { "as_of_date": "2026-07-11", "value": "47980547.349210462890046092", "decimals": 18 },
          { "as_of_date": "2026-07-16", "value": "48000547.349210462890046092", "decimals": 18 },
          { "as_of_date": "2026-07-21", "value": "48020547.349210462890046092", "decimals": 18 },
          { "as_of_date": "2026-07-26", "value": "48040547.349210462890046092", "decimals": 18 },
          { "as_of_date": "2026-07-31", "value": "48060547.349210462890046092", "decimals": 18 }
        ]
      },
      "input_sha256": "cb4f57d968d260ae6ddeacc668cac79c8c2150cac4d93260c69e9ed738b3cb95",
      "expected": { "ok": true, "yield_basis": "measured", "window": { "start_date": "2026-07-01", "end_date": "2026-07-31", "actual_days": 30, "distinct_observations": 7, "largest_gap_days": 5, "max_age_days": 5 }, "period_return_pct": 0.25031, "yield_pct": 3.045439, "yield_pct_continuous": 3.092287, "annualisation": "simple", "day_count": "actual/365" }
    },
    {
      "id": "closest-window-wins",
      "description": "Several windows qualify; the one closest to 30 days is selected.",
      "input": {
        "as_of_date": "2026-04-03",
        "source": { "id": "vector.selection", "carries_timestamp": true, "max_age_days": 5 },
        "observations": [
          { "as_of_date": "2026-03-01", "value": "100.00", "decimals": 2 },
          { "as_of_date": "2026-03-03", "value": "100.01", "decimals": 2 },
          { "as_of_date": "2026-03-05", "value": "100.02", "decimals": 2 },
          { "as_of_date": "2026-03-07", "value": "100.03", "decimals": 2 },
          { "as_of_date": "2026-03-09", "value": "100.04", "decimals": 2 },
          { "as_of_date": "2026-03-11", "value": "100.05", "decimals": 2 },
          { "as_of_date": "2026-03-13", "value": "100.06", "decimals": 2 },
          { "as_of_date": "2026-03-15", "value": "100.07", "decimals": 2 },
          { "as_of_date": "2026-03-17", "value": "100.08", "decimals": 2 },
          { "as_of_date": "2026-03-19", "value": "100.09", "decimals": 2 },
          { "as_of_date": "2026-03-21", "value": "100.10", "decimals": 2 },
          { "as_of_date": "2026-03-23", "value": "100.11", "decimals": 2 },
          { "as_of_date": "2026-03-25", "value": "100.12", "decimals": 2 },
          { "as_of_date": "2026-03-27", "value": "100.13", "decimals": 2 },
          { "as_of_date": "2026-03-29", "value": "100.14", "decimals": 2 },
          { "as_of_date": "2026-03-31", "value": "100.15", "decimals": 2 },
          { "as_of_date": "2026-04-02", "value": "100.16", "decimals": 2 }
        ]
      },
      "input_sha256": "43708e881fbcbee2e806fdea63791b5429dd98baa295bf8634c2be12d74817a5",
      "expected": { "ok": true, "yield_basis": "measured", "window": { "start_date": "2026-03-03", "end_date": "2026-04-02", "actual_days": 30, "distinct_observations": 16, "largest_gap_days": 2, "max_age_days": 5 }, "period_return_pct": 0.149985, "yield_pct": 1.824818, "yield_pct_continuous": 1.841569, "annualisation": "simple", "day_count": "actual/365" }
    },
    {
      "id": "gap-over-max-age-rejected",
      "description": "A six-day interior gap exceeds the declared five-day source maximum.",
      "input": {
        "as_of_date": "2026-06-01",
        "source": { "id": "vector.gap", "carries_timestamp": true, "max_age_days": 5 },
        "observations": [
          { "as_of_date": "2026-05-01", "value": "100.00", "decimals": 2 },
          { "as_of_date": "2026-05-07", "value": "100.06", "decimals": 2 },
          { "as_of_date": "2026-05-11", "value": "100.10", "decimals": 2 },
          { "as_of_date": "2026-05-15", "value": "100.14", "decimals": 2 },
          { "as_of_date": "2026-05-19", "value": "100.18", "decimals": 2 },
          { "as_of_date": "2026-05-23", "value": "100.22", "decimals": 2 },
          { "as_of_date": "2026-05-27", "value": "100.26", "decimals": 2 },
          { "as_of_date": "2026-05-31", "value": "100.30", "decimals": 2 }
        ]
      },
      "input_sha256": "f3c27c59087bf7ed60468f2bd79d0f734bed946c736522f689212cef1b134f77",
      "expected": { "ok": false, "yield_basis": "unavailable", "reason_code": "NO_ADMISSIBLE_WINDOW", "reason": "no 28-32 day window satisfies the gap and observation-count rules of §5.3" }
    }
  ]
}
