Functional Weave
Code in TypeScript

invest.bond-yield@1.0.2

vectors.json

8,396 bytes · view raw

[
 {
  "name": "Microsoft's YIELD example: 5.75% to 2016 at 95.04287 on 30/360 is 6.5%",
  "args": [
   "2008-02-15",
   "2016-11-15",
   575,
   "95.04287",
   2,
   "30-360"
  ],
  "expect": {
   "yieldBasisPoints": 650,
   "yieldToMaturity": "0.065000007",
   "currentYieldBasisPoints": 605,
   "accruedInterest": "1.437500",
   "dirtyPrice": "96.480370",
   "previousCoupon": "2007-11-15",
   "nextCoupon": "2008-05-15",
   "couponsRemaining": 18
  }
 },
 {
  "name": "the same bond on actual/actual: 92 of 182 days accrued",
  "args": [
   "2008-02-15",
   "2016-11-15",
   575,
   "95.04287",
   2,
   "act-act"
  ],
  "expect": {
   "yieldBasisPoints": 650,
   "yieldToMaturity": "0.065001821",
   "currentYieldBasisPoints": 605,
   "accruedInterest": "1.453297",
   "dirtyPrice": "96.496167",
   "previousCoupon": "2007-11-15",
   "nextCoupon": "2008-05-15",
   "couponsRemaining": 18
  }
 },
 {
  "name": "a gilt-style 4.25% semi-annual on actual/actual",
  "args": [
   "2025-09-23",
   "2027-12-07",
   425,
   "101.5",
   2,
   "act-act"
  ],
  "expect": {
   "yieldBasisPoints": 353,
   "yieldToMaturity": "0.035347152",
   "currentYieldBasisPoints": 419,
   "accruedInterest": "1.254098",
   "dirtyPrice": "102.754098",
   "previousCoupon": "2025-06-07",
   "nextCoupon": "2025-12-07",
   "couponsRemaining": 5
  }
 },
 {
  "name": "settling on a coupon date accrues nothing",
  "args": [
   "2025-12-07",
   "2027-12-07",
   425,
   "101.5",
   2,
   "act-act"
  ],
  "expect": {
   "yieldBasisPoints": 347,
   "yieldToMaturity": "0.034672155",
   "currentYieldBasisPoints": 419,
   "accruedInterest": "0.000000",
   "dirtyPrice": "101.500000",
   "previousCoupon": "2025-12-07",
   "nextCoupon": "2026-06-07",
   "couponsRemaining": 4
  }
 },
 {
  "name": "at par on a coupon date the yield is the coupon, exactly",
  "args": [
   "2026-06-15",
   "2036-06-15",
   450,
   "100",
   2,
   "30-360"
  ],
  "expect": {
   "yieldBasisPoints": 450,
   "yieldToMaturity": "0.045000000",
   "currentYieldBasisPoints": 450,
   "accruedInterest": "0.000000",
   "dirtyPrice": "100.000000",
   "previousCoupon": "2026-06-15",
   "nextCoupon": "2026-12-15",
   "couponsRemaining": 20
  }
 },
 {
  "name": "inside the last period: Excel's simple-yield formula",
  "args": [
   "2027-09-01",
   "2027-12-07",
   425,
   "99.8",
   2,
   "act-act"
  ],
  "expect": {
   "yieldBasisPoints": 496,
   "yieldToMaturity": "0.049649871",
   "currentYieldBasisPoints": 426,
   "accruedInterest": "0.998634",
   "dirtyPrice": "100.798634",
   "previousCoupon": "2027-06-07",
   "nextCoupon": "2027-12-07",
   "couponsRemaining": 1
  }
 },
 {
  "name": "inside the last period on 30/360",
  "args": [
   "2027-09-01",
   "2027-12-07",
   425,
   "99.8",
   2,
   "30-360"
  ],
  "expect": {
   "yieldBasisPoints": 496,
   "yieldToMaturity": "0.049607276",
   "currentYieldBasisPoints": 426,
   "accruedInterest": "0.991667",
   "dirtyPrice": "100.791667",
   "previousCoupon": "2027-06-07",
   "nextCoupon": "2027-12-07",
   "couponsRemaining": 1
  }
 },
 {
  "name": "annual coupons",
  "args": [
   "2026-03-10",
   "2031-06-15",
   300,
   "97.25",
   1,
   "30-360"
  ],
  "expect": {
   "yieldBasisPoints": 358,
   "yieldToMaturity": "0.035802953",
   "currentYieldBasisPoints": 308,
   "accruedInterest": "2.208333",
   "dirtyPrice": "99.458333",
   "previousCoupon": "2025-06-15",
   "nextCoupon": "2026-06-15",
   "couponsRemaining": 6
  }
 },
 {
  "name": "quarterly coupons",
  "args": [
   "2026-01-20",
   "2029-04-15",
   500,
   "102.125",
   4,
   "act-act"
  ],
  "expect": {
   "yieldBasisPoints": 429,
   "yieldToMaturity": "0.042930500",
   "currentYieldBasisPoints": 490,
   "accruedInterest": "0.069444",
   "dirtyPrice": "102.194444",
   "previousCoupon": "2026-01-15",
   "nextCoupon": "2026-04-15",
   "couponsRemaining": 13
  }
 },
 {
  "name": "a month-end maturity keeps coupons on month ends: 31 December, not 30th",
  "args": [
   "2026-01-15",
   "2030-06-30",
   400,
   "98",
   2,
   "act-act"
  ],
  "expect": {
   "yieldBasisPoints": 450,
   "yieldToMaturity": "0.044996644",
   "currentYieldBasisPoints": 408,
   "accruedInterest": "0.165746",
   "dirtyPrice": "98.165746",
   "previousCoupon": "2025-12-31",
   "nextCoupon": "2026-06-30",
   "couponsRemaining": 9
  }
 },
 {
  "name": "a zero-coupon bond: (100/90)^(1/5) - 1",
  "args": [
   "2026-01-01",
   "2031-01-01",
   0,
   "90",
   1,
   "act-act"
  ],
  "expect": {
   "yieldBasisPoints": 213,
   "yieldToMaturity": "0.021295688",
   "currentYieldBasisPoints": 0,
   "accruedInterest": "0.000000",
   "dirtyPrice": "90.000000",
   "previousCoupon": "2026-01-01",
   "nextCoupon": "2027-01-01",
   "couponsRemaining": 5
  }
 },
 {
  "name": "a price above every future payment is a negative yield",
  "args": [
   "2026-01-01",
   "2028-01-01",
   0,
   "105",
   1,
   "act-act"
  ],
  "expect": {
   "yieldBasisPoints": -241,
   "yieldToMaturity": "-0.024099927",
   "currentYieldBasisPoints": 0,
   "accruedInterest": "0.000000",
   "dirtyPrice": "105.000000",
   "previousCoupon": "2026-01-01",
   "nextCoupon": "2027-01-01",
   "couponsRemaining": 2
  }
 },
 {
  "name": "a distressed price",
  "args": [
   "2026-02-01",
   "2036-02-01",
   500,
   "50",
   2,
   "30-360"
  ],
  "expect": {
   "yieldBasisPoints": 1470,
   "yieldToMaturity": "0.146961743",
   "currentYieldBasisPoints": 1000,
   "accruedInterest": "0.000000",
   "dirtyPrice": "50.000000",
   "previousCoupon": "2026-02-01",
   "nextCoupon": "2026-08-01",
   "couponsRemaining": 20
  }
 },
 {
  "name": "settlement on maturity",
  "args": [
   "2026-01-01",
   "2026-01-01",
   500,
   "100",
   2,
   "30-360"
  ],
  "expectError": "settlement must be before maturity"
 },
 {
  "name": "a frequency Excel does not accept",
  "args": [
   "2026-01-01",
   "2030-01-01",
   500,
   "100",
   3,
   "30-360"
  ],
  "expectError": "frequency must be 1, 2 or 4"
 },
 {
  "name": "a negative coupon",
  "args": [
   "2026-01-01",
   "2030-01-01",
   -1,
   "100",
   2,
   "30-360"
  ],
  "expectError": "couponBasisPoints must be a whole number from 0 to 100000"
 },
 {
  "name": "a fractional coupon in basis points",
  "args": [
   "2026-01-01",
   "2030-01-01",
   412.5,
   "100",
   2,
   "30-360"
  ],
  "expectError": "couponBasisPoints must be a whole number from 0 to 100000"
 },
 {
  "name": "a price that is not a decimal",
  "args": [
   "2026-01-01",
   "2030-01-01",
   500,
   "par",
   2,
   "30-360"
  ],
  "expectError": "cleanPrice must be a positive decimal with at most 6 places"
 },
 {
  "name": "a price with seven decimal places",
  "args": [
   "2026-01-01",
   "2030-01-01",
   500,
   "99.1234567",
   2,
   "30-360"
  ],
  "expectError": "cleanPrice must be a positive decimal with at most 6 places"
 },
 {
  "name": "a price of zero",
  "args": [
   "2026-01-01",
   "2030-01-01",
   500,
   "0.000",
   2,
   "30-360"
  ],
  "expectError": "cleanPrice must be greater than zero"
 },
 {
  "name": "an unsupported day count",
  "args": [
   "2026-01-01",
   "2030-01-01",
   500,
   "100",
   2,
   "act-360"
  ],
  "expectError": "basis must be 30-360 or act-act"
 },
 {
  "name": "an impossible date",
  "args": [
   "2026-02-30",
   "2030-01-01",
   500,
   "100",
   2,
   "30-360"
  ],
  "expectError": "is not a real calendar date"
 },
 {
  "name": "a price no yield above -50% a period explains",
  "args": [
   "2026-01-01",
   "2028-01-01",
   0,
   "1000",
   1,
   "act-act"
  ],
  "expectError": "the price implies a yield below -50% a coupon period"
 },
 {
  "name": "more than 100 years to maturity",
  "args": [
   "2000-01-01",
   "2101-01-01",
   500,
   "100",
   1,
   "act-act"
  ],
  "expectError": "maturity must be within 100 years of settlement"
 },
 {
  "name": "30/360 bond basis can count past a month-end coupon",
  "args": [
   "2025-08-30",
   "2030-08-31",
   500,
   "100",
   2,
   "30-360"
  ],
  "expectError": "by the 30/360 count settlement is not before the next coupon date"
 },
  {"name": "a price with a trailing newline", "args": ["2026-01-01", "2030-01-01", 500, "100\n", 2, "30-360"], "expectError": "cleanPrice must be a positive decimal with at most 6 places"},
  {"name": "a decimal price with a trailing newline", "args": ["2026-01-01", "2030-01-01", 500, "99.5\n", 2, "30-360"], "expectError": "cleanPrice must be a positive decimal with at most 6 places"}
]