Functional Weave
Code in Python

lending.daily-interest@1.0.1

vectors.json

8,927 bytes · view raw

[
 {
  "name": "\u00a310,000 at 3.65% for 30 days on ACT/365 is exactly \u00a330",
  "args": [
   [
    {
     "date": "2026-01-01",
     "balance": {
      "minor": 1000000,
      "currency": "GBP"
     }
    }
   ],
   "2026-01-01",
   "2026-01-31",
   365,
   "act-365f",
   "half-up"
  ],
  "expect": {
   "interest": {
    "minor": 3000,
    "currency": "GBP"
   },
   "exact": {
    "numerator": 3000,
    "denominator": 1
   },
   "days": 30
  }
 },
 {
  "name": "a deposit mid-month: the new balance earns from its date",
  "args": [
   [
    {
     "date": "2026-03-01",
     "balance": {
      "minor": 100000,
      "currency": "GBP"
     }
    },
    {
     "date": "2026-03-16",
     "balance": {
      "minor": 600000,
      "currency": "GBP"
     }
    }
   ],
   "2026-03-01",
   "2026-04-01",
   450,
   "act-365f",
   "half-up"
  ],
  "expect": {
   "interest": {
    "minor": 1368,
    "currency": "GBP"
   },
   "exact": {
    "numerator": 99900,
    "denominator": 73
   },
   "days": 31
  }
 },
 {
  "name": "a balance before the period starts is carried in",
  "args": [
   [
    {
     "date": "2025-12-10",
     "balance": {
      "minor": 250000,
      "currency": "GBP"
     }
    },
    {
     "date": "2026-01-20",
     "balance": {
      "minor": 0,
      "currency": "GBP"
     }
    }
   ],
   "2026-01-01",
   "2026-02-01",
   500,
   "act-365f",
   "half-up"
  ],
  "expect": {
   "interest": {
    "minor": 651,
    "currency": "GBP"
   },
   "exact": {
    "numerator": 47500,
    "denominator": 73
   },
   "days": 31
  }
 },
 {
  "name": "changes after the period end are ignored",
  "args": [
   [
    {
     "date": "2026-01-01",
     "balance": {
      "minor": 100000,
      "currency": "GBP"
     }
    },
    {
     "date": "2026-02-10",
     "balance": {
      "minor": 900000,
      "currency": "GBP"
     }
    }
   ],
   "2026-01-01",
   "2026-02-01",
   500,
   "act-365f",
   "half-up"
  ],
  "expect": {
   "interest": {
    "minor": 425,
    "currency": "GBP"
   },
   "exact": {
    "numerator": 31000,
    "denominator": 73
   },
   "days": 31
  }
 },
 {
  "name": "ACT/360 charges more than ACT/365 for the same days",
  "args": [
   [
    {
     "date": "2026-01-01",
     "balance": {
      "minor": 1000000,
      "currency": "GBP"
     }
    }
   ],
   "2026-01-01",
   "2026-01-31",
   365,
   "act-360",
   "half-up"
  ],
  "expect": {
   "interest": {
    "minor": 3042,
    "currency": "GBP"
   },
   "exact": {
    "numerator": 9125,
    "denominator": 3
   },
   "days": 30
  }
 },
 {
  "name": "30/360: February counts as 30 days",
  "args": [
   [
    {
     "date": "2026-02-01",
     "balance": {
      "minor": 1200000,
      "currency": "GBP"
     }
    }
   ],
   "2026-02-01",
   "2026-03-01",
   600,
   "30-360",
   "half-up"
  ],
  "expect": {
   "interest": {
    "minor": 6000,
    "currency": "GBP"
   },
   "exact": {
    "numerator": 6000,
    "denominator": 1
   },
   "days": 28
  }
 },
 {
  "name": "ACT/ACT ISDA across a leap-year boundary",
  "args": [
   [
    {
     "date": "2027-12-01",
     "balance": {
      "minor": 5000000,
      "currency": "GBP"
     }
    }
   ],
   "2027-12-01",
   "2028-02-01",
   400,
   "act-act-isda",
   "half-up"
  ],
  "expect": {
   "interest": {
    "minor": 33926,
    "currency": "GBP"
   },
   "exact": {
    "numerator": 453220000,
    "denominator": 13359
   },
   "days": 62
  }
 },
 {
  "name": "an overdrawn balance accrues negative interest",
  "args": [
   [
    {
     "date": "2026-05-01",
     "balance": {
      "minor": -50000,
      "currency": "GBP"
     }
    }
   ],
   "2026-05-01",
   "2026-06-01",
   1990,
   "act-365f",
   "half-up"
  ],
  "expect": {
   "interest": {
    "minor": -845,
    "currency": "GBP"
   },
   "exact": {
    "numerator": -61690,
    "denominator": 73
   },
   "days": 31
  }
 },
 {
  "name": "the same debt with \"up\" rounds away from zero",
  "args": [
   [
    {
     "date": "2026-05-01",
     "balance": {
      "minor": -50000,
      "currency": "GBP"
     }
    }
   ],
   "2026-05-01",
   "2026-06-01",
   1990,
   "act-365f",
   "up"
  ],
  "expect": {
   "interest": {
    "minor": -846,
    "currency": "GBP"
   },
   "exact": {
    "numerator": -61690,
    "denominator": 73
   },
   "days": 31
  }
 },
 {
  "name": "rounding once: many small changes, one rounding",
  "args": [
   [
    {
     "date": "2026-01-01",
     "balance": {
      "minor": 333,
      "currency": "GBP"
     }
    },
    {
     "date": "2026-01-02",
     "balance": {
      "minor": 334,
      "currency": "GBP"
     }
    },
    {
     "date": "2026-01-03",
     "balance": {
      "minor": 333,
      "currency": "GBP"
     }
    },
    {
     "date": "2026-01-04",
     "balance": {
      "minor": 0,
      "currency": "GBP"
     }
    }
   ],
   "2026-01-01",
   "2026-01-05",
   2500,
   "act-365f",
   "half-up"
  ],
  "expect": {
   "interest": {
    "minor": 1,
    "currency": "GBP"
   },
   "exact": {
    "numerator": 50,
    "denominator": 73
   },
   "days": 4
  }
 },
 {
  "name": "a zero-length period accrues nothing",
  "args": [
   [
    {
     "date": "2026-01-01",
     "balance": {
      "minor": 100000,
      "currency": "GBP"
     }
    }
   ],
   "2026-01-10",
   "2026-01-10",
   500,
   "act-365f",
   "half-up"
  ],
  "expect": {
   "interest": {
    "minor": 0,
    "currency": "GBP"
   },
   "exact": {
    "numerator": 0,
    "denominator": 1
   },
   "days": 0
  }
 },
 {
  "name": "a negative rate charges a positive balance",
  "args": [
   [
    {
     "date": "2026-01-01",
     "balance": {
      "minor": 10000000,
      "currency": "GBP"
     }
    }
   ],
   "2026-01-01",
   "2026-04-01",
   -50,
   "act-365f",
   "half-even"
  ],
  "expect": {
   "interest": {
    "minor": -12329,
    "currency": "GBP"
   },
   "exact": {
    "numerator": -900000,
    "denominator": 73
   },
   "days": 90
  }
 },
 {
  "name": "a whole leap year on ACT/365F is 366/365 of a year's interest",
  "args": [
   [
    {
     "date": "2028-01-01",
     "balance": {
      "minor": 100000,
      "currency": "GBP"
     }
    }
   ],
   "2028-01-01",
   "2029-01-01",
   1000,
   "act-365f",
   "half-up"
  ],
  "expect": {
   "interest": {
    "minor": 10027,
    "currency": "GBP"
   },
   "exact": {
    "numerator": 732000,
    "denominator": 73
   },
   "days": 366
  }
 },
 {
  "name": "an empty balance list is refused",
  "args": [
   [],
   "2026-01-01",
   "2026-02-01",
   500,
   "act-365f",
   "half-up"
  ],
  "expectError": "balances must not be empty"
 },
 {
  "name": "a first balance after the start is refused",
  "args": [
   [
    {
     "date": "2026-01-05",
     "balance": {
      "minor": 100,
      "currency": "GBP"
     }
    }
   ],
   "2026-01-01",
   "2026-02-01",
   500,
   "act-365f",
   "half-up"
  ],
  "expectError": "after fromIso"
 },
 {
  "name": "balances out of order are refused",
  "args": [
   [
    {
     "date": "2026-01-05",
     "balance": {
      "minor": 100,
      "currency": "GBP"
     }
    },
    {
     "date": "2026-01-01",
     "balance": {
      "minor": 200,
      "currency": "GBP"
     }
    }
   ],
   "2026-01-01",
   "2026-02-01",
   500,
   "act-365f",
   "half-up"
  ],
  "expectError": "strictly ascending date order"
 },
 {
  "name": "an end before the start is refused",
  "args": [
   [
    {
     "date": "2026-01-01",
     "balance": {
      "minor": 100,
      "currency": "GBP"
     }
    }
   ],
   "2026-02-01",
   "2026-01-01",
   500,
   "act-365f",
   "half-up"
  ],
  "expectError": "must not be after toIso"
 },
 {
  "name": "mixed currencies are refused",
  "args": [
   [
    {
     "date": "2026-01-01",
     "balance": {
      "minor": 100,
      "currency": "GBP"
     }
    },
    {
     "date": "2026-01-10",
     "balance": {
      "minor": 100,
      "currency": "EUR"
     }
    }
   ],
   "2026-01-01",
   "2026-02-01",
   500,
   "act-365f",
   "half-up"
  ],
  "expectError": "currency mismatch"
 },
 {
  "name": "an impossible date is refused",
  "args": [
   [
    {
     "date": "2026-02-30",
     "balance": {
      "minor": 100,
      "currency": "GBP"
     }
    }
   ],
   "2026-01-01",
   "2026-02-01",
   500,
   "act-365f",
   "half-up"
  ],
  "expectError": "is not a real calendar date"
 },
 {
  "name": "an unknown convention is refused",
  "args": [
   [
    {
     "date": "2026-01-01",
     "balance": {
      "minor": 100,
      "currency": "GBP"
     }
    }
   ],
   "2026-01-01",
   "2026-02-01",
   500,
   "act-365",
   "half-up"
  ],
  "expectError": "unknown day count convention"
 },
 {
  "name": "a rate out of range is refused",
  "args": [
   [
    {
     "date": "2026-01-01",
     "balance": {
      "minor": 100,
      "currency": "GBP"
     }
    }
   ],
   "2026-01-01",
   "2026-02-01",
   100001,
   "act-365f",
   "half-up"
  ],
  "expectError": "annualRateBasisPoints must be between -100000 and 100000"
 }
]