{
  "tool_id": "art-706-yield-curve-gspread",
  "note": "Synthetic golden vectors F1-F8 (no external data). F1 bond on a knot (spread equals the yield difference exactly). F2 midpoint linear. F3 monotone cubic stays monotone. F4/F5 out-of-range refused naming the range. F6 unsorted tenors refused. F7 fewer than 2 points refused. F8 flat curve: spread identical under both methods. golden_hash pinned via node golden-parity.test.mjs --update. human_sign_off: PENDING.",
  "vectors": [
    {
      "name": "F1_bond_on_knot_linear",
      "note": "Bond maturity exactly on a benchmark knot — the interpolated benchmark yield equals that knot’s yield exactly, so the spread is the exact yield difference.",
      "policy_parameters": {
        "benchmark_points": [
          {
            "tenor_years": 2,
            "yield_pct": 4
          },
          {
            "tenor_years": 5,
            "yield_pct": 4.5
          },
          {
            "tenor_years": 10,
            "yield_pct": 4.8
          }
        ],
        "benchmark_name": "UST par, as supplied",
        "interpolation": "linear",
        "bond": {
          "maturity_years": 5,
          "yield_pct": 5.1
        }
      },
      "output_payload": {
        "curve": {
          "benchmark_name": "UST par, as supplied",
          "method": "linear",
          "method_version": "linear@1",
          "points": [
            {
              "tenor_years": 2,
              "yield_pct": 4
            },
            {
              "tenor_years": 5,
              "yield_pct": 4.5
            },
            {
              "tenor_years": 10,
              "yield_pct": 4.8
            }
          ]
        },
        "benchmark_yield_at_maturity_pct": 4.5,
        "g_spread_bp": 60,
        "assumptions": [
          "Curve assumption: the benchmark curve is built ONLY from the 3 benchmark point(s) the caller supplied under benchmark_name \"UST par, as supplied\" — there is no live Treasury feed, no market data lookup, and no blending with any other source.",
          "Interpolation assumption: the benchmark yield at the bond’s maturity is read from the curve by linear interpolation (method_version linear@1) — inside each section the yield is the straight line between that section’s two benchmark points; within a section the curve is smooth and no extrapolation happens outside [2, 10] years.",
          "Spread assumption: g_spread_bp is (bond.yield_pct − benchmark_yield_at_maturity_pct) × 100 — a yield difference in basis points against the interpolated benchmark yield, not an asset-swap spread and not a Z-spread; the bond’s own cash flows are not repriced.",
          "Exclusions: no bootstrapping of zero rates from coupon instruments (the supplied points are used exactly as given), no day-count or accrual adjustments (tenors are used as plain year numbers), no interpolation of the bond’s own yield, and no live data of any kind."
        ],
        "regulatory_basis": "Standard fixed-income relative-value analytics: the G-spread (a.k.a. yield spread) of a bond against a government benchmark yield curve, computed as the difference between the bond’s yield to maturity and the interpolated benchmark yield at the bond’s maturity (Fabozzi, Bond Markets, Analysis and Strategies). The monotone cubic option implements Fritsch and Carlson (1980).",
        "note": "The benchmark curve is caller-supplied by design: deterministic arithmetic over the given points only. Exclusions: no bootstrapping of zero rates, no day-count, no live Treasury feed."
      },
      "golden_hash": "10a05bdd29371ab8c931db43fd90e9ffd0da8c0cb1e1811d7684dfc65aa44e36"
    },
    {
      "name": "F2_midpoint_linear",
      "note": "Bond maturity at the exact midpoint of the 2y-5y section under linear interpolation — the benchmark yield is the arithmetic mean of the two knot yields.",
      "policy_parameters": {
        "benchmark_points": [
          {
            "tenor_years": 2,
            "yield_pct": 4
          },
          {
            "tenor_years": 5,
            "yield_pct": 4.5
          },
          {
            "tenor_years": 10,
            "yield_pct": 4.8
          }
        ],
        "benchmark_name": "UST par, as supplied",
        "interpolation": "linear",
        "bond": {
          "maturity_years": 3.5,
          "yield_pct": 4.85
        }
      },
      "output_payload": {
        "curve": {
          "benchmark_name": "UST par, as supplied",
          "method": "linear",
          "method_version": "linear@1",
          "points": [
            {
              "tenor_years": 2,
              "yield_pct": 4
            },
            {
              "tenor_years": 5,
              "yield_pct": 4.5
            },
            {
              "tenor_years": 10,
              "yield_pct": 4.8
            }
          ]
        },
        "benchmark_yield_at_maturity_pct": 4.25,
        "g_spread_bp": 60,
        "assumptions": [
          "Curve assumption: the benchmark curve is built ONLY from the 3 benchmark point(s) the caller supplied under benchmark_name \"UST par, as supplied\" — there is no live Treasury feed, no market data lookup, and no blending with any other source.",
          "Interpolation assumption: the benchmark yield at the bond’s maturity is read from the curve by linear interpolation (method_version linear@1) — inside each section the yield is the straight line between that section’s two benchmark points; within a section the curve is smooth and no extrapolation happens outside [2, 10] years.",
          "Spread assumption: g_spread_bp is (bond.yield_pct − benchmark_yield_at_maturity_pct) × 100 — a yield difference in basis points against the interpolated benchmark yield, not an asset-swap spread and not a Z-spread; the bond’s own cash flows are not repriced.",
          "Exclusions: no bootstrapping of zero rates from coupon instruments (the supplied points are used exactly as given), no day-count or accrual adjustments (tenors are used as plain year numbers), no interpolation of the bond’s own yield, and no live data of any kind."
        ],
        "regulatory_basis": "Standard fixed-income relative-value analytics: the G-spread (a.k.a. yield spread) of a bond against a government benchmark yield curve, computed as the difference between the bond’s yield to maturity and the interpolated benchmark yield at the bond’s maturity (Fabozzi, Bond Markets, Analysis and Strategies). The monotone cubic option implements Fritsch and Carlson (1980).",
        "note": "The benchmark curve is caller-supplied by design: deterministic arithmetic over the given points only. Exclusions: no bootstrapping of zero rates, no day-count, no live Treasury feed."
      },
      "golden_hash": "f3dc7fb852d6f848c08218f28d716d8be8fd12866dccd0ad7d5bb2cc9345e148"
    },
    {
      "name": "F3_monotone_cubic_between_knots",
      "note": "Monotone cubic Hermite on strictly increasing data — the interpolated yield stays strictly between the bracketing knot yields (curve stays monotone).",
      "policy_parameters": {
        "benchmark_points": [
          {
            "tenor_years": 2,
            "yield_pct": 4
          },
          {
            "tenor_years": 5,
            "yield_pct": 4.5
          },
          {
            "tenor_years": 10,
            "yield_pct": 4.8
          }
        ],
        "benchmark_name": "UST par, as supplied",
        "interpolation": "monotone_cubic_hermite",
        "bond": {
          "maturity_years": 7,
          "yield_pct": 5.2
        }
      },
      "output_payload": {
        "curve": {
          "benchmark_name": "UST par, as supplied",
          "method": "monotone_cubic_hermite",
          "method_version": "monotone_cubic_hermite@1",
          "points": [
            {
              "tenor_years": 2,
              "yield_pct": 4
            },
            {
              "tenor_years": 5,
              "yield_pct": 4.5
            },
            {
              "tenor_years": 10,
              "yield_pct": 4.8
            }
          ]
        },
        "benchmark_yield_at_maturity_pct": 4.671722,
        "g_spread_bp": 52.827755,
        "assumptions": [
          "Curve assumption: the benchmark curve is built ONLY from the 3 benchmark point(s) the caller supplied under benchmark_name \"UST par, as supplied\" — there is no live Treasury feed, no market data lookup, and no blending with any other source.",
          "Interpolation assumption: the benchmark yield at the bond’s maturity is read from the curve by monotone cubic Hermite interpolation (method_version monotone_cubic_hermite@1, Fritsch–Carlson 1980 tangents) — a piecewise cubic that passes through every benchmark point and preserves the monotone rise/fall pattern of the supplied data; within a section the curve is smooth and no extrapolation happens outside [2, 10] years.",
          "Spread assumption: g_spread_bp is (bond.yield_pct − benchmark_yield_at_maturity_pct) × 100 — a yield difference in basis points against the interpolated benchmark yield, not an asset-swap spread and not a Z-spread; the bond’s own cash flows are not repriced.",
          "Exclusions: no bootstrapping of zero rates from coupon instruments (the supplied points are used exactly as given), no day-count or accrual adjustments (tenors are used as plain year numbers), no interpolation of the bond’s own yield, and no live data of any kind."
        ],
        "regulatory_basis": "Standard fixed-income relative-value analytics: the G-spread (a.k.a. yield spread) of a bond against a government benchmark yield curve, computed as the difference between the bond’s yield to maturity and the interpolated benchmark yield at the bond’s maturity (Fabozzi, Bond Markets, Analysis and Strategies). The monotone cubic option implements Fritsch and Carlson (1980).",
        "note": "The benchmark curve is caller-supplied by design: deterministic arithmetic over the given points only. Exclusions: no bootstrapping of zero rates, no day-count, no live Treasury feed."
      },
      "golden_hash": "76aab2240749f167554490693cb4508cdacb57ecb2b7413a0ea038cb243c0a0b"
    },
    {
      "name": "F4_out_of_range_refused",
      "note": "Bond maturity beyond the longest benchmark tenor — refused naming the range, never extrapolated.",
      "policy_parameters": {
        "benchmark_points": [
          {
            "tenor_years": 2,
            "yield_pct": 4
          },
          {
            "tenor_years": 5,
            "yield_pct": 4.5
          },
          {
            "tenor_years": 10,
            "yield_pct": 4.8
          }
        ],
        "benchmark_name": "UST par, as supplied",
        "interpolation": "linear",
        "bond": {
          "maturity_years": 30,
          "yield_pct": 5.5
        }
      },
      "output_payload": {
        "error": "maturity_outside_curve_range",
        "detail": "bond.maturity_years (30) lies outside the benchmark curve range [2, 10] years. No extrapolation is performed — extend benchmark_points to cover the bond’s maturity.",
        "domain_errors": [
          {
            "code": "MATURITY_OUTSIDE_CURVE_RANGE",
            "text": "bond.maturity_years (30) lies outside the benchmark curve range [2, 10] years. No extrapolation is performed — extend benchmark_points to cover the bond’s maturity."
          }
        ],
        "curve": null,
        "benchmark_yield_at_maturity_pct": null,
        "g_spread_bp": null,
        "assumptions": []
      },
      "golden_hash": "d50d1329ecaa270d96e41661934d60da3525c418ddbf875704db826feb7fa216"
    },
    {
      "name": "F5_below_range_refused",
      "note": "Bond maturity shorter than the shortest benchmark tenor — refused naming the range, never extrapolated.",
      "policy_parameters": {
        "benchmark_points": [
          {
            "tenor_years": 2,
            "yield_pct": 4
          },
          {
            "tenor_years": 5,
            "yield_pct": 4.5
          },
          {
            "tenor_years": 10,
            "yield_pct": 4.8
          }
        ],
        "benchmark_name": "UST par, as supplied",
        "interpolation": "linear",
        "bond": {
          "maturity_years": 1,
          "yield_pct": 4.2
        }
      },
      "output_payload": {
        "error": "maturity_outside_curve_range",
        "detail": "bond.maturity_years (1) lies outside the benchmark curve range [2, 10] years. No extrapolation is performed — extend benchmark_points to cover the bond’s maturity.",
        "domain_errors": [
          {
            "code": "MATURITY_OUTSIDE_CURVE_RANGE",
            "text": "bond.maturity_years (1) lies outside the benchmark curve range [2, 10] years. No extrapolation is performed — extend benchmark_points to cover the bond’s maturity."
          }
        ],
        "curve": null,
        "benchmark_yield_at_maturity_pct": null,
        "g_spread_bp": null,
        "assumptions": []
      },
      "golden_hash": "bfe56d1b9f007cd2a92329f62852e55d168b5d663d654e00531cc14ab508b34f"
    },
    {
      "name": "F6_unsorted_tenors_refused",
      "note": "Unsorted benchmark tenors — refused (strictly increasing required), never silently re-sorted.",
      "policy_parameters": {
        "benchmark_points": [
          {
            "tenor_years": 2,
            "yield_pct": 4
          },
          {
            "tenor_years": 10,
            "yield_pct": 4.8
          },
          {
            "tenor_years": 5,
            "yield_pct": 4.5
          }
        ],
        "benchmark_name": "UST par, as supplied",
        "interpolation": "linear",
        "bond": {
          "maturity_years": 3,
          "yield_pct": 4.6
        }
      },
      "output_payload": {
        "error": "tenors_not_strictly_increasing",
        "detail": "benchmark_points tenors must be strictly increasing; benchmark_points[2].tenor_years (5) is not greater than benchmark_points[1].tenor_years (10). Unsorted or duplicate tenors are refused, never silently re-sorted.",
        "domain_errors": [
          {
            "code": "TENORS_NOT_STRICTLY_INCREASING",
            "text": "benchmark_points tenors must be strictly increasing; benchmark_points[2].tenor_years (5) is not greater than benchmark_points[1].tenor_years (10). Unsorted or duplicate tenors are refused, never silently re-sorted."
          }
        ],
        "curve": null,
        "benchmark_yield_at_maturity_pct": null,
        "g_spread_bp": null,
        "assumptions": []
      },
      "golden_hash": "63d72621e655faf3992557b044dfa4b26fad51706110aecd185b8598f01540b5"
    },
    {
      "name": "F7_single_point_refused",
      "note": "A single benchmark point cannot define a curve — refused.",
      "policy_parameters": {
        "benchmark_points": [
          {
            "tenor_years": 5,
            "yield_pct": 4.5
          }
        ],
        "benchmark_name": "UST par, as supplied",
        "interpolation": "linear",
        "bond": {
          "maturity_years": 5,
          "yield_pct": 5
        }
      },
      "output_payload": {
        "error": "benchmark_points_too_few",
        "detail": "benchmark_points must be an array of at least 2 { tenor_years, yield_pct } objects; got 1.",
        "domain_errors": [
          {
            "code": "BENCHMARK_POINTS_TOO_FEW",
            "text": "benchmark_points must be an array of at least 2 { tenor_years, yield_pct } objects; got 1."
          }
        ],
        "curve": null,
        "benchmark_yield_at_maturity_pct": null,
        "g_spread_bp": null,
        "assumptions": []
      },
      "golden_hash": "54bcc946a07e90829d99e6dc3553b5d424b8d73d0bba260cf02168d83d46e6f5"
    },
    {
      "name": "F8_flat_curve_both_methods",
      "note": "Flat curve — on a flat benchmark the G-spread is independent of the interpolation method (this vector pins the monotone_cubic_hermite arm; the proptest checks the invariance across both methods on every draw).",
      "policy_parameters": {
        "benchmark_points": [
          {
            "tenor_years": 1,
            "yield_pct": 4
          },
          {
            "tenor_years": 5,
            "yield_pct": 4
          },
          {
            "tenor_years": 10,
            "yield_pct": 4
          }
        ],
        "benchmark_name": "Gilt flat, as supplied",
        "interpolation": "monotone_cubic_hermite",
        "bond": {
          "maturity_years": 5.5,
          "yield_pct": 5
        }
      },
      "output_payload": {
        "curve": {
          "benchmark_name": "Gilt flat, as supplied",
          "method": "monotone_cubic_hermite",
          "method_version": "monotone_cubic_hermite@1",
          "points": [
            {
              "tenor_years": 1,
              "yield_pct": 4
            },
            {
              "tenor_years": 5,
              "yield_pct": 4
            },
            {
              "tenor_years": 10,
              "yield_pct": 4
            }
          ]
        },
        "benchmark_yield_at_maturity_pct": 4,
        "g_spread_bp": 100,
        "assumptions": [
          "Curve assumption: the benchmark curve is built ONLY from the 3 benchmark point(s) the caller supplied under benchmark_name \"Gilt flat, as supplied\" — there is no live Treasury feed, no market data lookup, and no blending with any other source.",
          "Interpolation assumption: the benchmark yield at the bond’s maturity is read from the curve by monotone cubic Hermite interpolation (method_version monotone_cubic_hermite@1, Fritsch–Carlson 1980 tangents) — a piecewise cubic that passes through every benchmark point and preserves the monotone rise/fall pattern of the supplied data; within a section the curve is smooth and no extrapolation happens outside [1, 10] years.",
          "Spread assumption: g_spread_bp is (bond.yield_pct − benchmark_yield_at_maturity_pct) × 100 — a yield difference in basis points against the interpolated benchmark yield, not an asset-swap spread and not a Z-spread; the bond’s own cash flows are not repriced.",
          "Exclusions: no bootstrapping of zero rates from coupon instruments (the supplied points are used exactly as given), no day-count or accrual adjustments (tenors are used as plain year numbers), no interpolation of the bond’s own yield, and no live data of any kind."
        ],
        "regulatory_basis": "Standard fixed-income relative-value analytics: the G-spread (a.k.a. yield spread) of a bond against a government benchmark yield curve, computed as the difference between the bond’s yield to maturity and the interpolated benchmark yield at the bond’s maturity (Fabozzi, Bond Markets, Analysis and Strategies). The monotone cubic option implements Fritsch and Carlson (1980).",
        "note": "The benchmark curve is caller-supplied by design: deterministic arithmetic over the given points only. Exclusions: no bootstrapping of zero rates, no day-count, no live Treasury feed."
      },
      "golden_hash": "eff10d2e1df29abc24980e71d1900989b5ee1de20da22d3009179cc85862739b"
    }
  ]
}
