@ducatjs/money 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +17 -8
- package/README.md +346 -43
- package/data/cldr-territories.json +2791 -0
- package/data/crypto-units.json +68 -0
- package/data/rbnf/de.txt +243 -0
- package/data/rbnf/en.txt +166 -0
- package/data/rbnf/es.txt +261 -0
- package/data/rbnf/fr.txt +159 -0
- package/data/rbnf/it.txt +502 -0
- package/data/rbnf/nl.txt +103 -0
- package/data/rbnf/pt.txt +171 -0
- package/dist/adyen-fx.js +1 -1
- package/dist/adyen-fx.js.map +1 -1
- package/dist/allocate.d.ts.map +1 -1
- package/dist/allocate.js +2 -3
- package/dist/allocate.js.map +1 -1
- package/dist/cash.d.ts +15 -0
- package/dist/cash.d.ts.map +1 -1
- package/dist/cash.js +17 -0
- package/dist/cash.js.map +1 -1
- package/dist/convert.d.ts.map +1 -1
- package/dist/convert.js +2 -3
- package/dist/convert.js.map +1 -1
- package/dist/crypto.d.ts +54 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/crypto.js +89 -0
- package/dist/crypto.js.map +1 -0
- package/dist/currencies.d.ts +43 -0
- package/dist/currencies.d.ts.map +1 -0
- package/dist/currencies.js +91 -0
- package/dist/currencies.js.map +1 -0
- package/dist/currency.d.ts +9 -1
- package/dist/currency.d.ts.map +1 -1
- package/dist/currency.js +41 -14
- package/dist/currency.js.map +1 -1
- package/dist/decimal.d.ts +25 -3
- package/dist/decimal.d.ts.map +1 -1
- package/dist/decimal.js +78 -8
- package/dist/decimal.js.map +1 -1
- package/dist/ecb.d.ts +23 -8
- package/dist/ecb.d.ts.map +1 -1
- package/dist/ecb.js +46 -15
- package/dist/ecb.js.map +1 -1
- package/dist/entries/adyen-fx.d.ts +3 -0
- package/dist/entries/adyen-fx.d.ts.map +1 -0
- package/dist/entries/adyen-fx.js +5 -0
- package/dist/entries/adyen-fx.js.map +1 -0
- package/dist/entries/boc.d.ts +3 -0
- package/dist/entries/boc.d.ts.map +1 -0
- package/dist/entries/boc.js +5 -0
- package/dist/entries/boc.js.map +1 -0
- package/dist/entries/boe.d.ts +3 -0
- package/dist/entries/boe.d.ts.map +1 -0
- package/dist/entries/boe.js +5 -0
- package/dist/entries/boe.js.map +1 -0
- package/dist/entries/crypto.d.ts +3 -0
- package/dist/entries/crypto.d.ts.map +1 -0
- package/dist/entries/crypto.js +5 -0
- package/dist/entries/crypto.js.map +1 -0
- package/dist/entries/currencies.d.ts +3 -0
- package/dist/entries/currencies.d.ts.map +1 -0
- package/dist/entries/currencies.js +5 -0
- package/dist/entries/currencies.js.map +1 -0
- package/dist/entries/drizzle-mysql.d.ts +3 -0
- package/dist/entries/drizzle-mysql.d.ts.map +1 -0
- package/dist/entries/drizzle-mysql.js +5 -0
- package/dist/entries/drizzle-mysql.js.map +1 -0
- package/dist/entries/drizzle-pg.d.ts +3 -0
- package/dist/entries/drizzle-pg.d.ts.map +1 -0
- package/dist/entries/drizzle-pg.js +5 -0
- package/dist/entries/drizzle-pg.js.map +1 -0
- package/dist/entries/drizzle-sqlite.d.ts +3 -0
- package/dist/entries/drizzle-sqlite.d.ts.map +1 -0
- package/dist/entries/drizzle-sqlite.js +5 -0
- package/dist/entries/drizzle-sqlite.js.map +1 -0
- package/dist/entries/ecb.d.ts +3 -0
- package/dist/entries/ecb.d.ts.map +1 -0
- package/dist/entries/ecb.js +5 -0
- package/dist/entries/ecb.js.map +1 -0
- package/dist/entries/fed.d.ts +3 -0
- package/dist/entries/fed.d.ts.map +1 -0
- package/dist/entries/fed.js +5 -0
- package/dist/entries/fed.js.map +1 -0
- package/dist/entries/fetch.d.ts +3 -0
- package/dist/entries/fetch.d.ts.map +1 -0
- package/dist/entries/fetch.js +5 -0
- package/dist/entries/fetch.js.map +1 -0
- package/dist/entries/finance.d.ts +3 -0
- package/dist/entries/finance.d.ts.map +1 -0
- package/dist/entries/finance.js +5 -0
- package/dist/entries/finance.js.map +1 -0
- package/dist/entries/historic.d.ts +3 -0
- package/dist/entries/historic.d.ts.map +1 -0
- package/dist/entries/historic.js +5 -0
- package/dist/entries/historic.js.map +1 -0
- package/dist/entries/history.d.ts +3 -0
- package/dist/entries/history.d.ts.map +1 -0
- package/dist/entries/history.js +5 -0
- package/dist/entries/history.js.map +1 -0
- package/dist/entries/hmrc.d.ts +3 -0
- package/dist/entries/hmrc.d.ts.map +1 -0
- package/dist/entries/hmrc.js +5 -0
- package/dist/entries/hmrc.js.map +1 -0
- package/dist/entries/kysely.d.ts +3 -0
- package/dist/entries/kysely.d.ts.map +1 -0
- package/dist/entries/kysely.js +5 -0
- package/dist/entries/kysely.js.map +1 -0
- package/dist/entries/parse.d.ts +3 -0
- package/dist/entries/parse.d.ts.map +1 -0
- package/dist/entries/parse.js +5 -0
- package/dist/entries/parse.js.map +1 -0
- package/dist/entries/pg.d.ts +3 -0
- package/dist/entries/pg.d.ts.map +1 -0
- package/dist/entries/pg.js +5 -0
- package/dist/entries/pg.js.map +1 -0
- package/dist/entries/policy.d.ts +3 -0
- package/dist/entries/policy.d.ts.map +1 -0
- package/dist/entries/policy.js +5 -0
- package/dist/entries/policy.js.map +1 -0
- package/dist/entries/pricing.d.ts +3 -0
- package/dist/entries/pricing.d.ts.map +1 -0
- package/dist/entries/pricing.js +5 -0
- package/dist/entries/pricing.js.map +1 -0
- package/dist/entries/stripe-fx.d.ts +3 -0
- package/dist/entries/stripe-fx.d.ts.map +1 -0
- package/dist/entries/stripe-fx.js +5 -0
- package/dist/entries/stripe-fx.js.map +1 -0
- package/dist/entries/testing-arbitraries.d.ts +3 -0
- package/dist/entries/testing-arbitraries.d.ts.map +1 -0
- package/dist/entries/testing-arbitraries.js +5 -0
- package/dist/entries/testing-arbitraries.js.map +1 -0
- package/dist/entries/testing-index.d.ts +3 -0
- package/dist/entries/testing-index.d.ts.map +1 -0
- package/dist/entries/testing-index.js +5 -0
- package/dist/entries/testing-index.js.map +1 -0
- package/dist/entries/testing-jest.d.ts +3 -0
- package/dist/entries/testing-jest.d.ts.map +1 -0
- package/dist/entries/testing-jest.js +5 -0
- package/dist/entries/testing-jest.js.map +1 -0
- package/dist/entries/testing-vitest.d.ts +3 -0
- package/dist/entries/testing-vitest.d.ts.map +1 -0
- package/dist/entries/testing-vitest.js +5 -0
- package/dist/entries/testing-vitest.js.map +1 -0
- package/dist/entries/valibot.d.ts +3 -0
- package/dist/entries/valibot.d.ts.map +1 -0
- package/dist/entries/valibot.js +5 -0
- package/dist/entries/valibot.js.map +1 -0
- package/dist/entries/wire.d.ts +3 -0
- package/dist/entries/wire.d.ts.map +1 -0
- package/dist/entries/wire.js +5 -0
- package/dist/entries/wire.js.map +1 -0
- package/dist/entries/zod.d.ts +3 -0
- package/dist/entries/zod.d.ts.map +1 -0
- package/dist/entries/zod.js +5 -0
- package/dist/entries/zod.js.map +1 -0
- package/dist/exact.d.ts.map +1 -1
- package/dist/exact.js +3 -3
- package/dist/exact.js.map +1 -1
- package/dist/fetch.d.ts +7 -1
- package/dist/fetch.d.ts.map +1 -1
- package/dist/fetch.js +11 -2
- package/dist/fetch.js.map +1 -1
- package/dist/finance.d.ts +296 -0
- package/dist/finance.d.ts.map +1 -0
- package/dist/finance.js +825 -0
- package/dist/finance.js.map +1 -0
- package/dist/fixed-money.d.ts +12 -0
- package/dist/fixed-money.d.ts.map +1 -0
- package/dist/fixed-money.js +32 -0
- package/dist/fixed-money.js.map +1 -0
- package/dist/fixed.d.ts +26 -9
- package/dist/fixed.d.ts.map +1 -1
- package/dist/fixed.js +65 -48
- package/dist/fixed.js.map +1 -1
- package/dist/fn.d.ts +17 -1
- package/dist/fn.d.ts.map +1 -1
- package/dist/fn.js +17 -1
- package/dist/fn.js.map +1 -1
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +28 -9
- package/dist/format.js.map +1 -1
- package/dist/generated/crypto.d.ts +58 -0
- package/dist/generated/crypto.d.ts.map +1 -0
- package/dist/generated/crypto.js +15 -0
- package/dist/generated/crypto.js.map +1 -0
- package/dist/generated/iso/AED.d.ts +2 -0
- package/dist/generated/iso/AED.d.ts.map +1 -0
- package/dist/generated/iso/AED.js +5 -0
- package/dist/generated/iso/AED.js.map +1 -0
- package/dist/generated/iso/AFN.d.ts +2 -0
- package/dist/generated/iso/AFN.d.ts.map +1 -0
- package/dist/generated/iso/AFN.js +5 -0
- package/dist/generated/iso/AFN.js.map +1 -0
- package/dist/generated/iso/ALL.d.ts +2 -0
- package/dist/generated/iso/ALL.d.ts.map +1 -0
- package/dist/generated/iso/ALL.js +5 -0
- package/dist/generated/iso/ALL.js.map +1 -0
- package/dist/generated/iso/AMD.d.ts +2 -0
- package/dist/generated/iso/AMD.d.ts.map +1 -0
- package/dist/generated/iso/AMD.js +5 -0
- package/dist/generated/iso/AMD.js.map +1 -0
- package/dist/generated/iso/AOA.d.ts +2 -0
- package/dist/generated/iso/AOA.d.ts.map +1 -0
- package/dist/generated/iso/AOA.js +5 -0
- package/dist/generated/iso/AOA.js.map +1 -0
- package/dist/generated/iso/ARS.d.ts +2 -0
- package/dist/generated/iso/ARS.d.ts.map +1 -0
- package/dist/generated/iso/ARS.js +5 -0
- package/dist/generated/iso/ARS.js.map +1 -0
- package/dist/generated/iso/AUD.d.ts +2 -0
- package/dist/generated/iso/AUD.d.ts.map +1 -0
- package/dist/generated/iso/AUD.js +5 -0
- package/dist/generated/iso/AUD.js.map +1 -0
- package/dist/generated/iso/AWG.d.ts +2 -0
- package/dist/generated/iso/AWG.d.ts.map +1 -0
- package/dist/generated/iso/AWG.js +5 -0
- package/dist/generated/iso/AWG.js.map +1 -0
- package/dist/generated/iso/AZN.d.ts +2 -0
- package/dist/generated/iso/AZN.d.ts.map +1 -0
- package/dist/generated/iso/AZN.js +5 -0
- package/dist/generated/iso/AZN.js.map +1 -0
- package/dist/generated/iso/BAM.d.ts +2 -0
- package/dist/generated/iso/BAM.d.ts.map +1 -0
- package/dist/generated/iso/BAM.js +5 -0
- package/dist/generated/iso/BAM.js.map +1 -0
- package/dist/generated/iso/BBD.d.ts +2 -0
- package/dist/generated/iso/BBD.d.ts.map +1 -0
- package/dist/generated/iso/BBD.js +5 -0
- package/dist/generated/iso/BBD.js.map +1 -0
- package/dist/generated/iso/BDT.d.ts +2 -0
- package/dist/generated/iso/BDT.d.ts.map +1 -0
- package/dist/generated/iso/BDT.js +5 -0
- package/dist/generated/iso/BDT.js.map +1 -0
- package/dist/generated/iso/BHD.d.ts +2 -0
- package/dist/generated/iso/BHD.d.ts.map +1 -0
- package/dist/generated/iso/BHD.js +5 -0
- package/dist/generated/iso/BHD.js.map +1 -0
- package/dist/generated/iso/BIF.d.ts +2 -0
- package/dist/generated/iso/BIF.d.ts.map +1 -0
- package/dist/generated/iso/BIF.js +5 -0
- package/dist/generated/iso/BIF.js.map +1 -0
- package/dist/generated/iso/BMD.d.ts +2 -0
- package/dist/generated/iso/BMD.d.ts.map +1 -0
- package/dist/generated/iso/BMD.js +5 -0
- package/dist/generated/iso/BMD.js.map +1 -0
- package/dist/generated/iso/BND.d.ts +2 -0
- package/dist/generated/iso/BND.d.ts.map +1 -0
- package/dist/generated/iso/BND.js +5 -0
- package/dist/generated/iso/BND.js.map +1 -0
- package/dist/generated/iso/BOB.d.ts +2 -0
- package/dist/generated/iso/BOB.d.ts.map +1 -0
- package/dist/generated/iso/BOB.js +5 -0
- package/dist/generated/iso/BOB.js.map +1 -0
- package/dist/generated/iso/BOV.d.ts +2 -0
- package/dist/generated/iso/BOV.d.ts.map +1 -0
- package/dist/generated/iso/BOV.js +5 -0
- package/dist/generated/iso/BOV.js.map +1 -0
- package/dist/generated/iso/BRL.d.ts +2 -0
- package/dist/generated/iso/BRL.d.ts.map +1 -0
- package/dist/generated/iso/BRL.js +5 -0
- package/dist/generated/iso/BRL.js.map +1 -0
- package/dist/generated/iso/BSD.d.ts +2 -0
- package/dist/generated/iso/BSD.d.ts.map +1 -0
- package/dist/generated/iso/BSD.js +5 -0
- package/dist/generated/iso/BSD.js.map +1 -0
- package/dist/generated/iso/BTN.d.ts +2 -0
- package/dist/generated/iso/BTN.d.ts.map +1 -0
- package/dist/generated/iso/BTN.js +5 -0
- package/dist/generated/iso/BTN.js.map +1 -0
- package/dist/generated/iso/BWP.d.ts +2 -0
- package/dist/generated/iso/BWP.d.ts.map +1 -0
- package/dist/generated/iso/BWP.js +5 -0
- package/dist/generated/iso/BWP.js.map +1 -0
- package/dist/generated/iso/BYN.d.ts +2 -0
- package/dist/generated/iso/BYN.d.ts.map +1 -0
- package/dist/generated/iso/BYN.js +5 -0
- package/dist/generated/iso/BYN.js.map +1 -0
- package/dist/generated/iso/BZD.d.ts +2 -0
- package/dist/generated/iso/BZD.d.ts.map +1 -0
- package/dist/generated/iso/BZD.js +5 -0
- package/dist/generated/iso/BZD.js.map +1 -0
- package/dist/generated/iso/CAD.d.ts +2 -0
- package/dist/generated/iso/CAD.d.ts.map +1 -0
- package/dist/generated/iso/CAD.js +5 -0
- package/dist/generated/iso/CAD.js.map +1 -0
- package/dist/generated/iso/CDF.d.ts +2 -0
- package/dist/generated/iso/CDF.d.ts.map +1 -0
- package/dist/generated/iso/CDF.js +5 -0
- package/dist/generated/iso/CDF.js.map +1 -0
- package/dist/generated/iso/CHE.d.ts +2 -0
- package/dist/generated/iso/CHE.d.ts.map +1 -0
- package/dist/generated/iso/CHE.js +5 -0
- package/dist/generated/iso/CHE.js.map +1 -0
- package/dist/generated/iso/CHF.d.ts +2 -0
- package/dist/generated/iso/CHF.d.ts.map +1 -0
- package/dist/generated/iso/CHF.js +5 -0
- package/dist/generated/iso/CHF.js.map +1 -0
- package/dist/generated/iso/CHW.d.ts +2 -0
- package/dist/generated/iso/CHW.d.ts.map +1 -0
- package/dist/generated/iso/CHW.js +5 -0
- package/dist/generated/iso/CHW.js.map +1 -0
- package/dist/generated/iso/CLF.d.ts +2 -0
- package/dist/generated/iso/CLF.d.ts.map +1 -0
- package/dist/generated/iso/CLF.js +5 -0
- package/dist/generated/iso/CLF.js.map +1 -0
- package/dist/generated/iso/CLP.d.ts +2 -0
- package/dist/generated/iso/CLP.d.ts.map +1 -0
- package/dist/generated/iso/CLP.js +5 -0
- package/dist/generated/iso/CLP.js.map +1 -0
- package/dist/generated/iso/CNY.d.ts +2 -0
- package/dist/generated/iso/CNY.d.ts.map +1 -0
- package/dist/generated/iso/CNY.js +5 -0
- package/dist/generated/iso/CNY.js.map +1 -0
- package/dist/generated/iso/COP.d.ts +2 -0
- package/dist/generated/iso/COP.d.ts.map +1 -0
- package/dist/generated/iso/COP.js +5 -0
- package/dist/generated/iso/COP.js.map +1 -0
- package/dist/generated/iso/COU.d.ts +2 -0
- package/dist/generated/iso/COU.d.ts.map +1 -0
- package/dist/generated/iso/COU.js +5 -0
- package/dist/generated/iso/COU.js.map +1 -0
- package/dist/generated/iso/CRC.d.ts +2 -0
- package/dist/generated/iso/CRC.d.ts.map +1 -0
- package/dist/generated/iso/CRC.js +5 -0
- package/dist/generated/iso/CRC.js.map +1 -0
- package/dist/generated/iso/CUP.d.ts +2 -0
- package/dist/generated/iso/CUP.d.ts.map +1 -0
- package/dist/generated/iso/CUP.js +5 -0
- package/dist/generated/iso/CUP.js.map +1 -0
- package/dist/generated/iso/CVE.d.ts +2 -0
- package/dist/generated/iso/CVE.d.ts.map +1 -0
- package/dist/generated/iso/CVE.js +5 -0
- package/dist/generated/iso/CVE.js.map +1 -0
- package/dist/generated/iso/CZK.d.ts +2 -0
- package/dist/generated/iso/CZK.d.ts.map +1 -0
- package/dist/generated/iso/CZK.js +5 -0
- package/dist/generated/iso/CZK.js.map +1 -0
- package/dist/generated/iso/DJF.d.ts +2 -0
- package/dist/generated/iso/DJF.d.ts.map +1 -0
- package/dist/generated/iso/DJF.js +5 -0
- package/dist/generated/iso/DJF.js.map +1 -0
- package/dist/generated/iso/DKK.d.ts +2 -0
- package/dist/generated/iso/DKK.d.ts.map +1 -0
- package/dist/generated/iso/DKK.js +5 -0
- package/dist/generated/iso/DKK.js.map +1 -0
- package/dist/generated/iso/DOP.d.ts +2 -0
- package/dist/generated/iso/DOP.d.ts.map +1 -0
- package/dist/generated/iso/DOP.js +5 -0
- package/dist/generated/iso/DOP.js.map +1 -0
- package/dist/generated/iso/DZD.d.ts +2 -0
- package/dist/generated/iso/DZD.d.ts.map +1 -0
- package/dist/generated/iso/DZD.js +5 -0
- package/dist/generated/iso/DZD.js.map +1 -0
- package/dist/generated/iso/EGP.d.ts +2 -0
- package/dist/generated/iso/EGP.d.ts.map +1 -0
- package/dist/generated/iso/EGP.js +5 -0
- package/dist/generated/iso/EGP.js.map +1 -0
- package/dist/generated/iso/ERN.d.ts +2 -0
- package/dist/generated/iso/ERN.d.ts.map +1 -0
- package/dist/generated/iso/ERN.js +5 -0
- package/dist/generated/iso/ERN.js.map +1 -0
- package/dist/generated/iso/ETB.d.ts +2 -0
- package/dist/generated/iso/ETB.d.ts.map +1 -0
- package/dist/generated/iso/ETB.js +5 -0
- package/dist/generated/iso/ETB.js.map +1 -0
- package/dist/generated/iso/EUR.d.ts +2 -0
- package/dist/generated/iso/EUR.d.ts.map +1 -0
- package/dist/generated/iso/EUR.js +5 -0
- package/dist/generated/iso/EUR.js.map +1 -0
- package/dist/generated/iso/FJD.d.ts +2 -0
- package/dist/generated/iso/FJD.d.ts.map +1 -0
- package/dist/generated/iso/FJD.js +5 -0
- package/dist/generated/iso/FJD.js.map +1 -0
- package/dist/generated/iso/FKP.d.ts +2 -0
- package/dist/generated/iso/FKP.d.ts.map +1 -0
- package/dist/generated/iso/FKP.js +5 -0
- package/dist/generated/iso/FKP.js.map +1 -0
- package/dist/generated/iso/GBP.d.ts +2 -0
- package/dist/generated/iso/GBP.d.ts.map +1 -0
- package/dist/generated/iso/GBP.js +5 -0
- package/dist/generated/iso/GBP.js.map +1 -0
- package/dist/generated/iso/GEL.d.ts +2 -0
- package/dist/generated/iso/GEL.d.ts.map +1 -0
- package/dist/generated/iso/GEL.js +5 -0
- package/dist/generated/iso/GEL.js.map +1 -0
- package/dist/generated/iso/GHS.d.ts +2 -0
- package/dist/generated/iso/GHS.d.ts.map +1 -0
- package/dist/generated/iso/GHS.js +5 -0
- package/dist/generated/iso/GHS.js.map +1 -0
- package/dist/generated/iso/GIP.d.ts +2 -0
- package/dist/generated/iso/GIP.d.ts.map +1 -0
- package/dist/generated/iso/GIP.js +5 -0
- package/dist/generated/iso/GIP.js.map +1 -0
- package/dist/generated/iso/GMD.d.ts +2 -0
- package/dist/generated/iso/GMD.d.ts.map +1 -0
- package/dist/generated/iso/GMD.js +5 -0
- package/dist/generated/iso/GMD.js.map +1 -0
- package/dist/generated/iso/GNF.d.ts +2 -0
- package/dist/generated/iso/GNF.d.ts.map +1 -0
- package/dist/generated/iso/GNF.js +5 -0
- package/dist/generated/iso/GNF.js.map +1 -0
- package/dist/generated/iso/GTQ.d.ts +2 -0
- package/dist/generated/iso/GTQ.d.ts.map +1 -0
- package/dist/generated/iso/GTQ.js +5 -0
- package/dist/generated/iso/GTQ.js.map +1 -0
- package/dist/generated/iso/GYD.d.ts +2 -0
- package/dist/generated/iso/GYD.d.ts.map +1 -0
- package/dist/generated/iso/GYD.js +5 -0
- package/dist/generated/iso/GYD.js.map +1 -0
- package/dist/generated/iso/HKD.d.ts +2 -0
- package/dist/generated/iso/HKD.d.ts.map +1 -0
- package/dist/generated/iso/HKD.js +5 -0
- package/dist/generated/iso/HKD.js.map +1 -0
- package/dist/generated/iso/HNL.d.ts +2 -0
- package/dist/generated/iso/HNL.d.ts.map +1 -0
- package/dist/generated/iso/HNL.js +5 -0
- package/dist/generated/iso/HNL.js.map +1 -0
- package/dist/generated/iso/HTG.d.ts +2 -0
- package/dist/generated/iso/HTG.d.ts.map +1 -0
- package/dist/generated/iso/HTG.js +5 -0
- package/dist/generated/iso/HTG.js.map +1 -0
- package/dist/generated/iso/HUF.d.ts +2 -0
- package/dist/generated/iso/HUF.d.ts.map +1 -0
- package/dist/generated/iso/HUF.js +5 -0
- package/dist/generated/iso/HUF.js.map +1 -0
- package/dist/generated/iso/IDR.d.ts +2 -0
- package/dist/generated/iso/IDR.d.ts.map +1 -0
- package/dist/generated/iso/IDR.js +5 -0
- package/dist/generated/iso/IDR.js.map +1 -0
- package/dist/generated/iso/ILS.d.ts +2 -0
- package/dist/generated/iso/ILS.d.ts.map +1 -0
- package/dist/generated/iso/ILS.js +5 -0
- package/dist/generated/iso/ILS.js.map +1 -0
- package/dist/generated/iso/INR.d.ts +2 -0
- package/dist/generated/iso/INR.d.ts.map +1 -0
- package/dist/generated/iso/INR.js +5 -0
- package/dist/generated/iso/INR.js.map +1 -0
- package/dist/generated/iso/IQD.d.ts +2 -0
- package/dist/generated/iso/IQD.d.ts.map +1 -0
- package/dist/generated/iso/IQD.js +5 -0
- package/dist/generated/iso/IQD.js.map +1 -0
- package/dist/generated/iso/IRR.d.ts +2 -0
- package/dist/generated/iso/IRR.d.ts.map +1 -0
- package/dist/generated/iso/IRR.js +5 -0
- package/dist/generated/iso/IRR.js.map +1 -0
- package/dist/generated/iso/ISK.d.ts +2 -0
- package/dist/generated/iso/ISK.d.ts.map +1 -0
- package/dist/generated/iso/ISK.js +5 -0
- package/dist/generated/iso/ISK.js.map +1 -0
- package/dist/generated/iso/JMD.d.ts +2 -0
- package/dist/generated/iso/JMD.d.ts.map +1 -0
- package/dist/generated/iso/JMD.js +5 -0
- package/dist/generated/iso/JMD.js.map +1 -0
- package/dist/generated/iso/JOD.d.ts +2 -0
- package/dist/generated/iso/JOD.d.ts.map +1 -0
- package/dist/generated/iso/JOD.js +5 -0
- package/dist/generated/iso/JOD.js.map +1 -0
- package/dist/generated/iso/JPY.d.ts +2 -0
- package/dist/generated/iso/JPY.d.ts.map +1 -0
- package/dist/generated/iso/JPY.js +5 -0
- package/dist/generated/iso/JPY.js.map +1 -0
- package/dist/generated/iso/KES.d.ts +2 -0
- package/dist/generated/iso/KES.d.ts.map +1 -0
- package/dist/generated/iso/KES.js +5 -0
- package/dist/generated/iso/KES.js.map +1 -0
- package/dist/generated/iso/KGS.d.ts +2 -0
- package/dist/generated/iso/KGS.d.ts.map +1 -0
- package/dist/generated/iso/KGS.js +5 -0
- package/dist/generated/iso/KGS.js.map +1 -0
- package/dist/generated/iso/KHR.d.ts +2 -0
- package/dist/generated/iso/KHR.d.ts.map +1 -0
- package/dist/generated/iso/KHR.js +5 -0
- package/dist/generated/iso/KHR.js.map +1 -0
- package/dist/generated/iso/KMF.d.ts +2 -0
- package/dist/generated/iso/KMF.d.ts.map +1 -0
- package/dist/generated/iso/KMF.js +5 -0
- package/dist/generated/iso/KMF.js.map +1 -0
- package/dist/generated/iso/KPW.d.ts +2 -0
- package/dist/generated/iso/KPW.d.ts.map +1 -0
- package/dist/generated/iso/KPW.js +5 -0
- package/dist/generated/iso/KPW.js.map +1 -0
- package/dist/generated/iso/KRW.d.ts +2 -0
- package/dist/generated/iso/KRW.d.ts.map +1 -0
- package/dist/generated/iso/KRW.js +5 -0
- package/dist/generated/iso/KRW.js.map +1 -0
- package/dist/generated/iso/KWD.d.ts +2 -0
- package/dist/generated/iso/KWD.d.ts.map +1 -0
- package/dist/generated/iso/KWD.js +5 -0
- package/dist/generated/iso/KWD.js.map +1 -0
- package/dist/generated/iso/KYD.d.ts +2 -0
- package/dist/generated/iso/KYD.d.ts.map +1 -0
- package/dist/generated/iso/KYD.js +5 -0
- package/dist/generated/iso/KYD.js.map +1 -0
- package/dist/generated/iso/KZT.d.ts +2 -0
- package/dist/generated/iso/KZT.d.ts.map +1 -0
- package/dist/generated/iso/KZT.js +5 -0
- package/dist/generated/iso/KZT.js.map +1 -0
- package/dist/generated/iso/LAK.d.ts +2 -0
- package/dist/generated/iso/LAK.d.ts.map +1 -0
- package/dist/generated/iso/LAK.js +5 -0
- package/dist/generated/iso/LAK.js.map +1 -0
- package/dist/generated/iso/LBP.d.ts +2 -0
- package/dist/generated/iso/LBP.d.ts.map +1 -0
- package/dist/generated/iso/LBP.js +5 -0
- package/dist/generated/iso/LBP.js.map +1 -0
- package/dist/generated/iso/LKR.d.ts +2 -0
- package/dist/generated/iso/LKR.d.ts.map +1 -0
- package/dist/generated/iso/LKR.js +5 -0
- package/dist/generated/iso/LKR.js.map +1 -0
- package/dist/generated/iso/LRD.d.ts +2 -0
- package/dist/generated/iso/LRD.d.ts.map +1 -0
- package/dist/generated/iso/LRD.js +5 -0
- package/dist/generated/iso/LRD.js.map +1 -0
- package/dist/generated/iso/LSL.d.ts +2 -0
- package/dist/generated/iso/LSL.d.ts.map +1 -0
- package/dist/generated/iso/LSL.js +5 -0
- package/dist/generated/iso/LSL.js.map +1 -0
- package/dist/generated/iso/LYD.d.ts +2 -0
- package/dist/generated/iso/LYD.d.ts.map +1 -0
- package/dist/generated/iso/LYD.js +5 -0
- package/dist/generated/iso/LYD.js.map +1 -0
- package/dist/generated/iso/MAD.d.ts +2 -0
- package/dist/generated/iso/MAD.d.ts.map +1 -0
- package/dist/generated/iso/MAD.js +5 -0
- package/dist/generated/iso/MAD.js.map +1 -0
- package/dist/generated/iso/MDL.d.ts +2 -0
- package/dist/generated/iso/MDL.d.ts.map +1 -0
- package/dist/generated/iso/MDL.js +5 -0
- package/dist/generated/iso/MDL.js.map +1 -0
- package/dist/generated/iso/MGA.d.ts +2 -0
- package/dist/generated/iso/MGA.d.ts.map +1 -0
- package/dist/generated/iso/MGA.js +5 -0
- package/dist/generated/iso/MGA.js.map +1 -0
- package/dist/generated/iso/MKD.d.ts +2 -0
- package/dist/generated/iso/MKD.d.ts.map +1 -0
- package/dist/generated/iso/MKD.js +5 -0
- package/dist/generated/iso/MKD.js.map +1 -0
- package/dist/generated/iso/MMK.d.ts +2 -0
- package/dist/generated/iso/MMK.d.ts.map +1 -0
- package/dist/generated/iso/MMK.js +5 -0
- package/dist/generated/iso/MMK.js.map +1 -0
- package/dist/generated/iso/MNT.d.ts +2 -0
- package/dist/generated/iso/MNT.d.ts.map +1 -0
- package/dist/generated/iso/MNT.js +5 -0
- package/dist/generated/iso/MNT.js.map +1 -0
- package/dist/generated/iso/MOP.d.ts +2 -0
- package/dist/generated/iso/MOP.d.ts.map +1 -0
- package/dist/generated/iso/MOP.js +5 -0
- package/dist/generated/iso/MOP.js.map +1 -0
- package/dist/generated/iso/MRU.d.ts +2 -0
- package/dist/generated/iso/MRU.d.ts.map +1 -0
- package/dist/generated/iso/MRU.js +5 -0
- package/dist/generated/iso/MRU.js.map +1 -0
- package/dist/generated/iso/MUR.d.ts +2 -0
- package/dist/generated/iso/MUR.d.ts.map +1 -0
- package/dist/generated/iso/MUR.js +5 -0
- package/dist/generated/iso/MUR.js.map +1 -0
- package/dist/generated/iso/MVR.d.ts +2 -0
- package/dist/generated/iso/MVR.d.ts.map +1 -0
- package/dist/generated/iso/MVR.js +5 -0
- package/dist/generated/iso/MVR.js.map +1 -0
- package/dist/generated/iso/MWK.d.ts +2 -0
- package/dist/generated/iso/MWK.d.ts.map +1 -0
- package/dist/generated/iso/MWK.js +5 -0
- package/dist/generated/iso/MWK.js.map +1 -0
- package/dist/generated/iso/MXN.d.ts +2 -0
- package/dist/generated/iso/MXN.d.ts.map +1 -0
- package/dist/generated/iso/MXN.js +5 -0
- package/dist/generated/iso/MXN.js.map +1 -0
- package/dist/generated/iso/MXV.d.ts +2 -0
- package/dist/generated/iso/MXV.d.ts.map +1 -0
- package/dist/generated/iso/MXV.js +5 -0
- package/dist/generated/iso/MXV.js.map +1 -0
- package/dist/generated/iso/MYR.d.ts +2 -0
- package/dist/generated/iso/MYR.d.ts.map +1 -0
- package/dist/generated/iso/MYR.js +5 -0
- package/dist/generated/iso/MYR.js.map +1 -0
- package/dist/generated/iso/MZN.d.ts +2 -0
- package/dist/generated/iso/MZN.d.ts.map +1 -0
- package/dist/generated/iso/MZN.js +5 -0
- package/dist/generated/iso/MZN.js.map +1 -0
- package/dist/generated/iso/NAD.d.ts +2 -0
- package/dist/generated/iso/NAD.d.ts.map +1 -0
- package/dist/generated/iso/NAD.js +5 -0
- package/dist/generated/iso/NAD.js.map +1 -0
- package/dist/generated/iso/NGN.d.ts +2 -0
- package/dist/generated/iso/NGN.d.ts.map +1 -0
- package/dist/generated/iso/NGN.js +5 -0
- package/dist/generated/iso/NGN.js.map +1 -0
- package/dist/generated/iso/NIO.d.ts +2 -0
- package/dist/generated/iso/NIO.d.ts.map +1 -0
- package/dist/generated/iso/NIO.js +5 -0
- package/dist/generated/iso/NIO.js.map +1 -0
- package/dist/generated/iso/NOK.d.ts +2 -0
- package/dist/generated/iso/NOK.d.ts.map +1 -0
- package/dist/generated/iso/NOK.js +5 -0
- package/dist/generated/iso/NOK.js.map +1 -0
- package/dist/generated/iso/NPR.d.ts +2 -0
- package/dist/generated/iso/NPR.d.ts.map +1 -0
- package/dist/generated/iso/NPR.js +5 -0
- package/dist/generated/iso/NPR.js.map +1 -0
- package/dist/generated/iso/NZD.d.ts +2 -0
- package/dist/generated/iso/NZD.d.ts.map +1 -0
- package/dist/generated/iso/NZD.js +5 -0
- package/dist/generated/iso/NZD.js.map +1 -0
- package/dist/generated/iso/OMR.d.ts +2 -0
- package/dist/generated/iso/OMR.d.ts.map +1 -0
- package/dist/generated/iso/OMR.js +5 -0
- package/dist/generated/iso/OMR.js.map +1 -0
- package/dist/generated/iso/PAB.d.ts +2 -0
- package/dist/generated/iso/PAB.d.ts.map +1 -0
- package/dist/generated/iso/PAB.js +5 -0
- package/dist/generated/iso/PAB.js.map +1 -0
- package/dist/generated/iso/PEN.d.ts +2 -0
- package/dist/generated/iso/PEN.d.ts.map +1 -0
- package/dist/generated/iso/PEN.js +5 -0
- package/dist/generated/iso/PEN.js.map +1 -0
- package/dist/generated/iso/PGK.d.ts +2 -0
- package/dist/generated/iso/PGK.d.ts.map +1 -0
- package/dist/generated/iso/PGK.js +5 -0
- package/dist/generated/iso/PGK.js.map +1 -0
- package/dist/generated/iso/PHP.d.ts +2 -0
- package/dist/generated/iso/PHP.d.ts.map +1 -0
- package/dist/generated/iso/PHP.js +5 -0
- package/dist/generated/iso/PHP.js.map +1 -0
- package/dist/generated/iso/PKR.d.ts +2 -0
- package/dist/generated/iso/PKR.d.ts.map +1 -0
- package/dist/generated/iso/PKR.js +5 -0
- package/dist/generated/iso/PKR.js.map +1 -0
- package/dist/generated/iso/PLN.d.ts +2 -0
- package/dist/generated/iso/PLN.d.ts.map +1 -0
- package/dist/generated/iso/PLN.js +5 -0
- package/dist/generated/iso/PLN.js.map +1 -0
- package/dist/generated/iso/PYG.d.ts +2 -0
- package/dist/generated/iso/PYG.d.ts.map +1 -0
- package/dist/generated/iso/PYG.js +5 -0
- package/dist/generated/iso/PYG.js.map +1 -0
- package/dist/generated/iso/QAR.d.ts +2 -0
- package/dist/generated/iso/QAR.d.ts.map +1 -0
- package/dist/generated/iso/QAR.js +5 -0
- package/dist/generated/iso/QAR.js.map +1 -0
- package/dist/generated/iso/RON.d.ts +2 -0
- package/dist/generated/iso/RON.d.ts.map +1 -0
- package/dist/generated/iso/RON.js +5 -0
- package/dist/generated/iso/RON.js.map +1 -0
- package/dist/generated/iso/RSD.d.ts +2 -0
- package/dist/generated/iso/RSD.d.ts.map +1 -0
- package/dist/generated/iso/RSD.js +5 -0
- package/dist/generated/iso/RSD.js.map +1 -0
- package/dist/generated/iso/RUB.d.ts +2 -0
- package/dist/generated/iso/RUB.d.ts.map +1 -0
- package/dist/generated/iso/RUB.js +5 -0
- package/dist/generated/iso/RUB.js.map +1 -0
- package/dist/generated/iso/RWF.d.ts +2 -0
- package/dist/generated/iso/RWF.d.ts.map +1 -0
- package/dist/generated/iso/RWF.js +5 -0
- package/dist/generated/iso/RWF.js.map +1 -0
- package/dist/generated/iso/SAR.d.ts +2 -0
- package/dist/generated/iso/SAR.d.ts.map +1 -0
- package/dist/generated/iso/SAR.js +5 -0
- package/dist/generated/iso/SAR.js.map +1 -0
- package/dist/generated/iso/SBD.d.ts +2 -0
- package/dist/generated/iso/SBD.d.ts.map +1 -0
- package/dist/generated/iso/SBD.js +5 -0
- package/dist/generated/iso/SBD.js.map +1 -0
- package/dist/generated/iso/SCR.d.ts +2 -0
- package/dist/generated/iso/SCR.d.ts.map +1 -0
- package/dist/generated/iso/SCR.js +5 -0
- package/dist/generated/iso/SCR.js.map +1 -0
- package/dist/generated/iso/SDG.d.ts +2 -0
- package/dist/generated/iso/SDG.d.ts.map +1 -0
- package/dist/generated/iso/SDG.js +5 -0
- package/dist/generated/iso/SDG.js.map +1 -0
- package/dist/generated/iso/SEK.d.ts +2 -0
- package/dist/generated/iso/SEK.d.ts.map +1 -0
- package/dist/generated/iso/SEK.js +5 -0
- package/dist/generated/iso/SEK.js.map +1 -0
- package/dist/generated/iso/SGD.d.ts +2 -0
- package/dist/generated/iso/SGD.d.ts.map +1 -0
- package/dist/generated/iso/SGD.js +5 -0
- package/dist/generated/iso/SGD.js.map +1 -0
- package/dist/generated/iso/SHP.d.ts +2 -0
- package/dist/generated/iso/SHP.d.ts.map +1 -0
- package/dist/generated/iso/SHP.js +5 -0
- package/dist/generated/iso/SHP.js.map +1 -0
- package/dist/generated/iso/SLE.d.ts +2 -0
- package/dist/generated/iso/SLE.d.ts.map +1 -0
- package/dist/generated/iso/SLE.js +5 -0
- package/dist/generated/iso/SLE.js.map +1 -0
- package/dist/generated/iso/SOS.d.ts +2 -0
- package/dist/generated/iso/SOS.d.ts.map +1 -0
- package/dist/generated/iso/SOS.js +5 -0
- package/dist/generated/iso/SOS.js.map +1 -0
- package/dist/generated/iso/SRD.d.ts +2 -0
- package/dist/generated/iso/SRD.d.ts.map +1 -0
- package/dist/generated/iso/SRD.js +5 -0
- package/dist/generated/iso/SRD.js.map +1 -0
- package/dist/generated/iso/SSP.d.ts +2 -0
- package/dist/generated/iso/SSP.d.ts.map +1 -0
- package/dist/generated/iso/SSP.js +5 -0
- package/dist/generated/iso/SSP.js.map +1 -0
- package/dist/generated/iso/STN.d.ts +2 -0
- package/dist/generated/iso/STN.d.ts.map +1 -0
- package/dist/generated/iso/STN.js +5 -0
- package/dist/generated/iso/STN.js.map +1 -0
- package/dist/generated/iso/SVC.d.ts +2 -0
- package/dist/generated/iso/SVC.d.ts.map +1 -0
- package/dist/generated/iso/SVC.js +5 -0
- package/dist/generated/iso/SVC.js.map +1 -0
- package/dist/generated/iso/SYP.d.ts +2 -0
- package/dist/generated/iso/SYP.d.ts.map +1 -0
- package/dist/generated/iso/SYP.js +5 -0
- package/dist/generated/iso/SYP.js.map +1 -0
- package/dist/generated/iso/SZL.d.ts +2 -0
- package/dist/generated/iso/SZL.d.ts.map +1 -0
- package/dist/generated/iso/SZL.js +5 -0
- package/dist/generated/iso/SZL.js.map +1 -0
- package/dist/generated/iso/THB.d.ts +2 -0
- package/dist/generated/iso/THB.d.ts.map +1 -0
- package/dist/generated/iso/THB.js +5 -0
- package/dist/generated/iso/THB.js.map +1 -0
- package/dist/generated/iso/TJS.d.ts +2 -0
- package/dist/generated/iso/TJS.d.ts.map +1 -0
- package/dist/generated/iso/TJS.js +5 -0
- package/dist/generated/iso/TJS.js.map +1 -0
- package/dist/generated/iso/TMT.d.ts +2 -0
- package/dist/generated/iso/TMT.d.ts.map +1 -0
- package/dist/generated/iso/TMT.js +5 -0
- package/dist/generated/iso/TMT.js.map +1 -0
- package/dist/generated/iso/TND.d.ts +2 -0
- package/dist/generated/iso/TND.d.ts.map +1 -0
- package/dist/generated/iso/TND.js +5 -0
- package/dist/generated/iso/TND.js.map +1 -0
- package/dist/generated/iso/TOP.d.ts +2 -0
- package/dist/generated/iso/TOP.d.ts.map +1 -0
- package/dist/generated/iso/TOP.js +5 -0
- package/dist/generated/iso/TOP.js.map +1 -0
- package/dist/generated/iso/TRY.d.ts +2 -0
- package/dist/generated/iso/TRY.d.ts.map +1 -0
- package/dist/generated/iso/TRY.js +5 -0
- package/dist/generated/iso/TRY.js.map +1 -0
- package/dist/generated/iso/TTD.d.ts +2 -0
- package/dist/generated/iso/TTD.d.ts.map +1 -0
- package/dist/generated/iso/TTD.js +5 -0
- package/dist/generated/iso/TTD.js.map +1 -0
- package/dist/generated/iso/TWD.d.ts +2 -0
- package/dist/generated/iso/TWD.d.ts.map +1 -0
- package/dist/generated/iso/TWD.js +5 -0
- package/dist/generated/iso/TWD.js.map +1 -0
- package/dist/generated/iso/TZS.d.ts +2 -0
- package/dist/generated/iso/TZS.d.ts.map +1 -0
- package/dist/generated/iso/TZS.js +5 -0
- package/dist/generated/iso/TZS.js.map +1 -0
- package/dist/generated/iso/UAH.d.ts +2 -0
- package/dist/generated/iso/UAH.d.ts.map +1 -0
- package/dist/generated/iso/UAH.js +5 -0
- package/dist/generated/iso/UAH.js.map +1 -0
- package/dist/generated/iso/UGX.d.ts +2 -0
- package/dist/generated/iso/UGX.d.ts.map +1 -0
- package/dist/generated/iso/UGX.js +5 -0
- package/dist/generated/iso/UGX.js.map +1 -0
- package/dist/generated/iso/USD.d.ts +2 -0
- package/dist/generated/iso/USD.d.ts.map +1 -0
- package/dist/generated/iso/USD.js +5 -0
- package/dist/generated/iso/USD.js.map +1 -0
- package/dist/generated/iso/USN.d.ts +2 -0
- package/dist/generated/iso/USN.d.ts.map +1 -0
- package/dist/generated/iso/USN.js +5 -0
- package/dist/generated/iso/USN.js.map +1 -0
- package/dist/generated/iso/UYI.d.ts +2 -0
- package/dist/generated/iso/UYI.d.ts.map +1 -0
- package/dist/generated/iso/UYI.js +5 -0
- package/dist/generated/iso/UYI.js.map +1 -0
- package/dist/generated/iso/UYU.d.ts +2 -0
- package/dist/generated/iso/UYU.d.ts.map +1 -0
- package/dist/generated/iso/UYU.js +5 -0
- package/dist/generated/iso/UYU.js.map +1 -0
- package/dist/generated/iso/UYW.d.ts +2 -0
- package/dist/generated/iso/UYW.d.ts.map +1 -0
- package/dist/generated/iso/UYW.js +5 -0
- package/dist/generated/iso/UYW.js.map +1 -0
- package/dist/generated/iso/UZS.d.ts +2 -0
- package/dist/generated/iso/UZS.d.ts.map +1 -0
- package/dist/generated/iso/UZS.js +5 -0
- package/dist/generated/iso/UZS.js.map +1 -0
- package/dist/generated/iso/VED.d.ts +2 -0
- package/dist/generated/iso/VED.d.ts.map +1 -0
- package/dist/generated/iso/VED.js +5 -0
- package/dist/generated/iso/VED.js.map +1 -0
- package/dist/generated/iso/VES.d.ts +2 -0
- package/dist/generated/iso/VES.d.ts.map +1 -0
- package/dist/generated/iso/VES.js +5 -0
- package/dist/generated/iso/VES.js.map +1 -0
- package/dist/generated/iso/VND.d.ts +2 -0
- package/dist/generated/iso/VND.d.ts.map +1 -0
- package/dist/generated/iso/VND.js +5 -0
- package/dist/generated/iso/VND.js.map +1 -0
- package/dist/generated/iso/VUV.d.ts +2 -0
- package/dist/generated/iso/VUV.d.ts.map +1 -0
- package/dist/generated/iso/VUV.js +5 -0
- package/dist/generated/iso/VUV.js.map +1 -0
- package/dist/generated/iso/WST.d.ts +2 -0
- package/dist/generated/iso/WST.d.ts.map +1 -0
- package/dist/generated/iso/WST.js +5 -0
- package/dist/generated/iso/WST.js.map +1 -0
- package/dist/generated/iso/XAD.d.ts +2 -0
- package/dist/generated/iso/XAD.d.ts.map +1 -0
- package/dist/generated/iso/XAD.js +5 -0
- package/dist/generated/iso/XAD.js.map +1 -0
- package/dist/generated/iso/XAF.d.ts +2 -0
- package/dist/generated/iso/XAF.d.ts.map +1 -0
- package/dist/generated/iso/XAF.js +5 -0
- package/dist/generated/iso/XAF.js.map +1 -0
- package/dist/generated/iso/XCD.d.ts +2 -0
- package/dist/generated/iso/XCD.d.ts.map +1 -0
- package/dist/generated/iso/XCD.js +5 -0
- package/dist/generated/iso/XCD.js.map +1 -0
- package/dist/generated/iso/XCG.d.ts +2 -0
- package/dist/generated/iso/XCG.d.ts.map +1 -0
- package/dist/generated/iso/XCG.js +5 -0
- package/dist/generated/iso/XCG.js.map +1 -0
- package/dist/generated/iso/XOF.d.ts +2 -0
- package/dist/generated/iso/XOF.d.ts.map +1 -0
- package/dist/generated/iso/XOF.js +5 -0
- package/dist/generated/iso/XOF.js.map +1 -0
- package/dist/generated/iso/XPF.d.ts +2 -0
- package/dist/generated/iso/XPF.d.ts.map +1 -0
- package/dist/generated/iso/XPF.js +5 -0
- package/dist/generated/iso/XPF.js.map +1 -0
- package/dist/generated/iso/YER.d.ts +2 -0
- package/dist/generated/iso/YER.d.ts.map +1 -0
- package/dist/generated/iso/YER.js +5 -0
- package/dist/generated/iso/YER.js.map +1 -0
- package/dist/generated/iso/ZAR.d.ts +2 -0
- package/dist/generated/iso/ZAR.d.ts.map +1 -0
- package/dist/generated/iso/ZAR.js +5 -0
- package/dist/generated/iso/ZAR.js.map +1 -0
- package/dist/generated/iso/ZMW.d.ts +2 -0
- package/dist/generated/iso/ZMW.d.ts.map +1 -0
- package/dist/generated/iso/ZMW.js +5 -0
- package/dist/generated/iso/ZMW.js.map +1 -0
- package/dist/generated/iso/ZWG.d.ts +2 -0
- package/dist/generated/iso/ZWG.d.ts.map +1 -0
- package/dist/generated/iso/ZWG.js +5 -0
- package/dist/generated/iso/ZWG.js.map +1 -0
- package/dist/generated/iso4217-numeric.d.ts +3 -0
- package/dist/generated/iso4217-numeric.d.ts.map +1 -0
- package/dist/generated/iso4217-numeric.js +184 -0
- package/dist/generated/iso4217-numeric.js.map +1 -0
- package/dist/generated/rbnf-de.d.ts +3 -0
- package/dist/generated/rbnf-de.d.ts.map +1 -0
- package/dist/generated/rbnf-de.js +5 -0
- package/dist/generated/rbnf-de.js.map +1 -0
- package/dist/generated/rbnf-en.d.ts +3 -0
- package/dist/generated/rbnf-en.d.ts.map +1 -0
- package/dist/generated/rbnf-en.js +5 -0
- package/dist/generated/rbnf-en.js.map +1 -0
- package/dist/generated/rbnf-es.d.ts +3 -0
- package/dist/generated/rbnf-es.d.ts.map +1 -0
- package/dist/generated/rbnf-es.js +5 -0
- package/dist/generated/rbnf-es.js.map +1 -0
- package/dist/generated/rbnf-fr.d.ts +3 -0
- package/dist/generated/rbnf-fr.d.ts.map +1 -0
- package/dist/generated/rbnf-fr.js +5 -0
- package/dist/generated/rbnf-fr.js.map +1 -0
- package/dist/generated/rbnf-it.d.ts +3 -0
- package/dist/generated/rbnf-it.d.ts.map +1 -0
- package/dist/generated/rbnf-it.js +5 -0
- package/dist/generated/rbnf-it.js.map +1 -0
- package/dist/generated/rbnf-nl.d.ts +3 -0
- package/dist/generated/rbnf-nl.d.ts.map +1 -0
- package/dist/generated/rbnf-nl.js +5 -0
- package/dist/generated/rbnf-nl.js.map +1 -0
- package/dist/generated/rbnf-pt.d.ts +3 -0
- package/dist/generated/rbnf-pt.d.ts.map +1 -0
- package/dist/generated/rbnf-pt.js +5 -0
- package/dist/generated/rbnf-pt.js.map +1 -0
- package/dist/generated/territories.d.ts +6 -0
- package/dist/generated/territories.d.ts.map +1 -0
- package/dist/generated/territories.js +275 -0
- package/dist/generated/territories.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/kysely.d.ts.map +1 -1
- package/dist/kysely.js +2 -2
- package/dist/kysely.js.map +1 -1
- package/dist/math.d.ts +41 -0
- package/dist/math.d.ts.map +1 -0
- package/dist/math.js +132 -0
- package/dist/math.js.map +1 -0
- package/dist/money.d.ts +39 -57
- package/dist/money.d.ts.map +1 -1
- package/dist/money.js +116 -128
- package/dist/money.js.map +1 -1
- package/dist/parse.js +7 -7
- package/dist/parse.js.map +1 -1
- package/dist/plugins/adyen.d.ts +2 -0
- package/dist/plugins/adyen.d.ts.map +1 -1
- package/dist/plugins/adyen.js +2 -0
- package/dist/plugins/adyen.js.map +1 -1
- package/dist/plugins/all.d.ts +18 -2
- package/dist/plugins/all.d.ts.map +1 -1
- package/dist/plugins/all.js +19 -2
- package/dist/plugins/all.js.map +1 -1
- package/dist/plugins/allocate.d.ts +1 -0
- package/dist/plugins/allocate.d.ts.map +1 -1
- package/dist/plugins/allocate.js +2 -0
- package/dist/plugins/allocate.js.map +1 -1
- package/dist/plugins/braintree.d.ts +1 -0
- package/dist/plugins/braintree.d.ts.map +1 -1
- package/dist/plugins/braintree.js +2 -0
- package/dist/plugins/braintree.js.map +1 -1
- package/dist/plugins/cash.d.ts +1 -0
- package/dist/plugins/cash.d.ts.map +1 -1
- package/dist/plugins/cash.js +2 -0
- package/dist/plugins/cash.js.map +1 -1
- package/dist/plugins/checkout.d.ts +1 -0
- package/dist/plugins/checkout.d.ts.map +1 -1
- package/dist/plugins/checkout.js +2 -0
- package/dist/plugins/checkout.js.map +1 -1
- package/dist/plugins/convert.d.ts +5 -0
- package/dist/plugins/convert.d.ts.map +1 -1
- package/dist/plugins/convert.js +2 -0
- package/dist/plugins/convert.js.map +1 -1
- package/dist/plugins/core.d.ts +78 -0
- package/dist/plugins/core.d.ts.map +1 -0
- package/dist/plugins/core.js +104 -0
- package/dist/plugins/core.js.map +1 -0
- package/dist/plugins/exact.d.ts +1 -0
- package/dist/plugins/exact.d.ts.map +1 -1
- package/dist/plugins/exact.js +2 -0
- package/dist/plugins/exact.js.map +1 -1
- package/dist/plugins/fixed-money.d.ts +31 -0
- package/dist/plugins/fixed-money.d.ts.map +1 -0
- package/dist/plugins/fixed-money.js +29 -0
- package/dist/plugins/fixed-money.js.map +1 -0
- package/dist/plugins/format.d.ts +5 -0
- package/dist/plugins/format.d.ts.map +1 -1
- package/dist/plugins/format.js +2 -0
- package/dist/plugins/format.js.map +1 -1
- package/dist/plugins/iso.d.ts +2 -0
- package/dist/plugins/iso.d.ts.map +1 -0
- package/dist/plugins/iso.js +9 -0
- package/dist/plugins/iso.js.map +1 -0
- package/dist/plugins/klarna.d.ts +1 -0
- package/dist/plugins/klarna.d.ts.map +1 -1
- package/dist/plugins/klarna.js +2 -0
- package/dist/plugins/klarna.js.map +1 -1
- package/dist/plugins/math.d.ts +20 -0
- package/dist/plugins/math.d.ts.map +1 -0
- package/dist/plugins/math.js +24 -0
- package/dist/plugins/math.js.map +1 -0
- package/dist/plugins/paypal.d.ts +5 -0
- package/dist/plugins/paypal.d.ts.map +1 -1
- package/dist/plugins/paypal.js +6 -0
- package/dist/plugins/paypal.js.map +1 -1
- package/dist/plugins/prisma.d.ts +5 -0
- package/dist/plugins/prisma.d.ts.map +1 -1
- package/dist/plugins/prisma.js +6 -0
- package/dist/plugins/prisma.js.map +1 -1
- package/dist/plugins/stripe.d.ts +5 -0
- package/dist/plugins/stripe.d.ts.map +1 -1
- package/dist/plugins/stripe.js +6 -0
- package/dist/plugins/stripe.js.map +1 -1
- package/dist/plugins/tax.d.ts +1 -0
- package/dist/plugins/tax.d.ts.map +1 -1
- package/dist/plugins/tax.js +6 -0
- package/dist/plugins/tax.js.map +1 -1
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +5 -6
- package/dist/policy.js.map +1 -1
- package/dist/pricing.d.ts +52 -0
- package/dist/pricing.d.ts.map +1 -0
- package/dist/pricing.js +100 -0
- package/dist/pricing.js.map +1 -0
- package/dist/prisma.d.ts.map +1 -1
- package/dist/prisma.js +6 -2
- package/dist/prisma.js.map +1 -1
- package/dist/provider.js +2 -2
- package/dist/provider.js.map +1 -1
- package/dist/rate-text.d.ts +6 -0
- package/dist/rate-text.d.ts.map +1 -1
- package/dist/rate-text.js +24 -1
- package/dist/rate-text.js.map +1 -1
- package/dist/rate.d.ts.map +1 -1
- package/dist/rate.js +2 -3
- package/dist/rate.js.map +1 -1
- package/dist/rbnf.d.ts +29 -0
- package/dist/rbnf.d.ts.map +1 -0
- package/dist/rbnf.js +301 -0
- package/dist/rbnf.js.map +1 -0
- package/dist/scale.d.ts +8 -0
- package/dist/scale.d.ts.map +1 -0
- package/dist/scale.js +34 -0
- package/dist/scale.js.map +1 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +4 -5
- package/dist/schema.js.map +1 -1
- package/dist/territories.d.ts +40 -0
- package/dist/territories.d.ts.map +1 -0
- package/dist/territories.js +71 -0
- package/dist/territories.js.map +1 -0
- package/dist/testing/arbitraries.d.ts.map +1 -1
- package/dist/testing/arbitraries.js +4 -4
- package/dist/testing/arbitraries.js.map +1 -1
- package/dist/testing/index.js +1 -1
- package/dist/testing/index.js.map +1 -1
- package/dist/tiers.d.ts +95 -0
- package/dist/tiers.d.ts.map +1 -0
- package/dist/tiers.js +173 -0
- package/dist/tiers.js.map +1 -0
- package/dist/valibot.d.ts.map +1 -1
- package/dist/valibot.js +4 -1
- package/dist/valibot.js.map +1 -1
- package/dist/wire.d.ts +105 -0
- package/dist/wire.d.ts.map +1 -0
- package/dist/wire.js +212 -0
- package/dist/wire.js.map +1 -0
- package/dist/words/de.d.ts +4 -0
- package/dist/words/de.d.ts.map +1 -0
- package/dist/words/de.js +8 -0
- package/dist/words/de.js.map +1 -0
- package/dist/words/en.d.ts +4 -0
- package/dist/words/en.d.ts.map +1 -0
- package/dist/words/en.js +8 -0
- package/dist/words/en.js.map +1 -0
- package/dist/words/es.d.ts +4 -0
- package/dist/words/es.d.ts.map +1 -0
- package/dist/words/es.js +8 -0
- package/dist/words/es.js.map +1 -0
- package/dist/words/fr.d.ts +4 -0
- package/dist/words/fr.d.ts.map +1 -0
- package/dist/words/fr.js +8 -0
- package/dist/words/fr.js.map +1 -0
- package/dist/words/it.d.ts +4 -0
- package/dist/words/it.d.ts.map +1 -0
- package/dist/words/it.js +8 -0
- package/dist/words/it.js.map +1 -0
- package/dist/words/nl.d.ts +4 -0
- package/dist/words/nl.d.ts.map +1 -0
- package/dist/words/nl.js +8 -0
- package/dist/words/nl.js.map +1 -0
- package/dist/words/pt.d.ts +4 -0
- package/dist/words/pt.d.ts.map +1 -0
- package/dist/words/pt.js +8 -0
- package/dist/words/pt.js.map +1 -0
- package/dist/words.d.ts +63 -0
- package/dist/words.d.ts.map +1 -0
- package/dist/words.js +107 -0
- package/dist/words.js.map +1 -0
- package/dist/zod.d.ts.map +1 -1
- package/dist/zod.js +1 -1
- package/dist/zod.js.map +1 -1
- package/package.json +312 -54
- package/src/adyen-fx.ts +1 -1
- package/src/allocate.ts +4 -3
- package/src/cash.ts +26 -0
- package/src/convert.ts +2 -3
- package/src/crypto.ts +129 -0
- package/src/currencies.ts +123 -0
- package/src/currency.ts +45 -14
- package/src/decimal.ts +98 -10
- package/src/ecb.ts +75 -16
- package/src/entries/adyen-fx.ts +5 -0
- package/src/entries/boc.ts +5 -0
- package/src/entries/boe.ts +5 -0
- package/src/entries/crypto.ts +5 -0
- package/src/entries/currencies.ts +5 -0
- package/src/entries/drizzle-mysql.ts +5 -0
- package/src/entries/drizzle-pg.ts +5 -0
- package/src/entries/drizzle-sqlite.ts +5 -0
- package/src/entries/ecb.ts +5 -0
- package/src/entries/fed.ts +5 -0
- package/src/entries/fetch.ts +5 -0
- package/src/entries/finance.ts +5 -0
- package/src/entries/historic.ts +5 -0
- package/src/entries/history.ts +5 -0
- package/src/entries/hmrc.ts +5 -0
- package/src/entries/kysely.ts +5 -0
- package/src/entries/parse.ts +5 -0
- package/src/entries/pg.ts +5 -0
- package/src/entries/policy.ts +5 -0
- package/src/entries/pricing.ts +5 -0
- package/src/entries/stripe-fx.ts +5 -0
- package/src/entries/testing-arbitraries.ts +5 -0
- package/src/entries/testing-index.ts +5 -0
- package/src/entries/testing-jest.ts +5 -0
- package/src/entries/testing-vitest.ts +5 -0
- package/src/entries/valibot.ts +5 -0
- package/src/entries/wire.ts +5 -0
- package/src/entries/zod.ts +5 -0
- package/src/exact.ts +3 -3
- package/src/fetch.ts +20 -4
- package/src/finance.ts +1219 -0
- package/src/fixed-money.ts +38 -0
- package/src/fixed.ts +74 -66
- package/src/fn.ts +17 -0
- package/src/format.ts +28 -9
- package/src/generated/crypto.ts +15 -0
- package/src/generated/iso/AED.ts +5 -0
- package/src/generated/iso/AFN.ts +5 -0
- package/src/generated/iso/ALL.ts +5 -0
- package/src/generated/iso/AMD.ts +5 -0
- package/src/generated/iso/AOA.ts +5 -0
- package/src/generated/iso/ARS.ts +5 -0
- package/src/generated/iso/AUD.ts +5 -0
- package/src/generated/iso/AWG.ts +5 -0
- package/src/generated/iso/AZN.ts +5 -0
- package/src/generated/iso/BAM.ts +5 -0
- package/src/generated/iso/BBD.ts +5 -0
- package/src/generated/iso/BDT.ts +5 -0
- package/src/generated/iso/BHD.ts +5 -0
- package/src/generated/iso/BIF.ts +5 -0
- package/src/generated/iso/BMD.ts +5 -0
- package/src/generated/iso/BND.ts +5 -0
- package/src/generated/iso/BOB.ts +5 -0
- package/src/generated/iso/BOV.ts +5 -0
- package/src/generated/iso/BRL.ts +5 -0
- package/src/generated/iso/BSD.ts +5 -0
- package/src/generated/iso/BTN.ts +5 -0
- package/src/generated/iso/BWP.ts +5 -0
- package/src/generated/iso/BYN.ts +5 -0
- package/src/generated/iso/BZD.ts +5 -0
- package/src/generated/iso/CAD.ts +5 -0
- package/src/generated/iso/CDF.ts +5 -0
- package/src/generated/iso/CHE.ts +5 -0
- package/src/generated/iso/CHF.ts +5 -0
- package/src/generated/iso/CHW.ts +5 -0
- package/src/generated/iso/CLF.ts +5 -0
- package/src/generated/iso/CLP.ts +5 -0
- package/src/generated/iso/CNY.ts +5 -0
- package/src/generated/iso/COP.ts +5 -0
- package/src/generated/iso/COU.ts +5 -0
- package/src/generated/iso/CRC.ts +5 -0
- package/src/generated/iso/CUP.ts +5 -0
- package/src/generated/iso/CVE.ts +5 -0
- package/src/generated/iso/CZK.ts +5 -0
- package/src/generated/iso/DJF.ts +5 -0
- package/src/generated/iso/DKK.ts +5 -0
- package/src/generated/iso/DOP.ts +5 -0
- package/src/generated/iso/DZD.ts +5 -0
- package/src/generated/iso/EGP.ts +5 -0
- package/src/generated/iso/ERN.ts +5 -0
- package/src/generated/iso/ETB.ts +5 -0
- package/src/generated/iso/EUR.ts +5 -0
- package/src/generated/iso/FJD.ts +5 -0
- package/src/generated/iso/FKP.ts +5 -0
- package/src/generated/iso/GBP.ts +5 -0
- package/src/generated/iso/GEL.ts +5 -0
- package/src/generated/iso/GHS.ts +5 -0
- package/src/generated/iso/GIP.ts +5 -0
- package/src/generated/iso/GMD.ts +5 -0
- package/src/generated/iso/GNF.ts +5 -0
- package/src/generated/iso/GTQ.ts +5 -0
- package/src/generated/iso/GYD.ts +5 -0
- package/src/generated/iso/HKD.ts +5 -0
- package/src/generated/iso/HNL.ts +5 -0
- package/src/generated/iso/HTG.ts +5 -0
- package/src/generated/iso/HUF.ts +5 -0
- package/src/generated/iso/IDR.ts +5 -0
- package/src/generated/iso/ILS.ts +5 -0
- package/src/generated/iso/INR.ts +5 -0
- package/src/generated/iso/IQD.ts +5 -0
- package/src/generated/iso/IRR.ts +5 -0
- package/src/generated/iso/ISK.ts +5 -0
- package/src/generated/iso/JMD.ts +5 -0
- package/src/generated/iso/JOD.ts +5 -0
- package/src/generated/iso/JPY.ts +5 -0
- package/src/generated/iso/KES.ts +5 -0
- package/src/generated/iso/KGS.ts +5 -0
- package/src/generated/iso/KHR.ts +5 -0
- package/src/generated/iso/KMF.ts +5 -0
- package/src/generated/iso/KPW.ts +5 -0
- package/src/generated/iso/KRW.ts +5 -0
- package/src/generated/iso/KWD.ts +5 -0
- package/src/generated/iso/KYD.ts +5 -0
- package/src/generated/iso/KZT.ts +5 -0
- package/src/generated/iso/LAK.ts +5 -0
- package/src/generated/iso/LBP.ts +5 -0
- package/src/generated/iso/LKR.ts +5 -0
- package/src/generated/iso/LRD.ts +5 -0
- package/src/generated/iso/LSL.ts +5 -0
- package/src/generated/iso/LYD.ts +5 -0
- package/src/generated/iso/MAD.ts +5 -0
- package/src/generated/iso/MDL.ts +5 -0
- package/src/generated/iso/MGA.ts +5 -0
- package/src/generated/iso/MKD.ts +5 -0
- package/src/generated/iso/MMK.ts +5 -0
- package/src/generated/iso/MNT.ts +5 -0
- package/src/generated/iso/MOP.ts +5 -0
- package/src/generated/iso/MRU.ts +5 -0
- package/src/generated/iso/MUR.ts +5 -0
- package/src/generated/iso/MVR.ts +5 -0
- package/src/generated/iso/MWK.ts +5 -0
- package/src/generated/iso/MXN.ts +5 -0
- package/src/generated/iso/MXV.ts +5 -0
- package/src/generated/iso/MYR.ts +5 -0
- package/src/generated/iso/MZN.ts +5 -0
- package/src/generated/iso/NAD.ts +5 -0
- package/src/generated/iso/NGN.ts +5 -0
- package/src/generated/iso/NIO.ts +5 -0
- package/src/generated/iso/NOK.ts +5 -0
- package/src/generated/iso/NPR.ts +5 -0
- package/src/generated/iso/NZD.ts +5 -0
- package/src/generated/iso/OMR.ts +5 -0
- package/src/generated/iso/PAB.ts +5 -0
- package/src/generated/iso/PEN.ts +5 -0
- package/src/generated/iso/PGK.ts +5 -0
- package/src/generated/iso/PHP.ts +5 -0
- package/src/generated/iso/PKR.ts +5 -0
- package/src/generated/iso/PLN.ts +5 -0
- package/src/generated/iso/PYG.ts +5 -0
- package/src/generated/iso/QAR.ts +5 -0
- package/src/generated/iso/RON.ts +5 -0
- package/src/generated/iso/RSD.ts +5 -0
- package/src/generated/iso/RUB.ts +5 -0
- package/src/generated/iso/RWF.ts +5 -0
- package/src/generated/iso/SAR.ts +5 -0
- package/src/generated/iso/SBD.ts +5 -0
- package/src/generated/iso/SCR.ts +5 -0
- package/src/generated/iso/SDG.ts +5 -0
- package/src/generated/iso/SEK.ts +5 -0
- package/src/generated/iso/SGD.ts +5 -0
- package/src/generated/iso/SHP.ts +5 -0
- package/src/generated/iso/SLE.ts +5 -0
- package/src/generated/iso/SOS.ts +5 -0
- package/src/generated/iso/SRD.ts +5 -0
- package/src/generated/iso/SSP.ts +5 -0
- package/src/generated/iso/STN.ts +5 -0
- package/src/generated/iso/SVC.ts +5 -0
- package/src/generated/iso/SYP.ts +5 -0
- package/src/generated/iso/SZL.ts +5 -0
- package/src/generated/iso/THB.ts +5 -0
- package/src/generated/iso/TJS.ts +5 -0
- package/src/generated/iso/TMT.ts +5 -0
- package/src/generated/iso/TND.ts +5 -0
- package/src/generated/iso/TOP.ts +5 -0
- package/src/generated/iso/TRY.ts +5 -0
- package/src/generated/iso/TTD.ts +5 -0
- package/src/generated/iso/TWD.ts +5 -0
- package/src/generated/iso/TZS.ts +5 -0
- package/src/generated/iso/UAH.ts +5 -0
- package/src/generated/iso/UGX.ts +5 -0
- package/src/generated/iso/USD.ts +5 -0
- package/src/generated/iso/USN.ts +5 -0
- package/src/generated/iso/UYI.ts +5 -0
- package/src/generated/iso/UYU.ts +5 -0
- package/src/generated/iso/UYW.ts +5 -0
- package/src/generated/iso/UZS.ts +5 -0
- package/src/generated/iso/VED.ts +5 -0
- package/src/generated/iso/VES.ts +5 -0
- package/src/generated/iso/VND.ts +5 -0
- package/src/generated/iso/VUV.ts +5 -0
- package/src/generated/iso/WST.ts +5 -0
- package/src/generated/iso/XAD.ts +5 -0
- package/src/generated/iso/XAF.ts +5 -0
- package/src/generated/iso/XCD.ts +5 -0
- package/src/generated/iso/XCG.ts +5 -0
- package/src/generated/iso/XOF.ts +5 -0
- package/src/generated/iso/XPF.ts +5 -0
- package/src/generated/iso/YER.ts +5 -0
- package/src/generated/iso/ZAR.ts +5 -0
- package/src/generated/iso/ZMW.ts +5 -0
- package/src/generated/iso/ZWG.ts +5 -0
- package/src/generated/iso4217-numeric.ts +184 -0
- package/src/generated/rbnf-de.ts +5 -0
- package/src/generated/rbnf-en.ts +5 -0
- package/src/generated/rbnf-es.ts +5 -0
- package/src/generated/rbnf-fr.ts +5 -0
- package/src/generated/rbnf-it.ts +5 -0
- package/src/generated/rbnf-nl.ts +5 -0
- package/src/generated/rbnf-pt.ts +5 -0
- package/src/generated/territories.ts +277 -0
- package/src/index.ts +3 -0
- package/src/kysely.ts +4 -3
- package/src/math.ts +167 -0
- package/src/money.ts +136 -140
- package/src/parse.ts +7 -7
- package/src/plugins/adyen.ts +2 -0
- package/src/plugins/all.ts +19 -1
- package/src/plugins/allocate.ts +2 -0
- package/src/plugins/braintree.ts +2 -0
- package/src/plugins/cash.ts +2 -0
- package/src/plugins/checkout.ts +2 -0
- package/src/plugins/convert.ts +2 -0
- package/src/plugins/core.ts +195 -0
- package/src/plugins/exact.ts +2 -0
- package/src/plugins/fixed-money.ts +57 -0
- package/src/plugins/format.ts +2 -0
- package/src/plugins/iso.ts +9 -0
- package/src/plugins/klarna.ts +2 -0
- package/src/plugins/math.ts +43 -0
- package/src/plugins/paypal.ts +2 -0
- package/src/plugins/prisma.ts +2 -0
- package/src/plugins/stripe.ts +2 -0
- package/src/plugins/tax.ts +2 -0
- package/src/policy.ts +5 -8
- package/src/pricing.ts +155 -0
- package/src/prisma.ts +6 -1
- package/src/provider.ts +2 -2
- package/src/rate-text.ts +27 -1
- package/src/rate.ts +2 -2
- package/src/rbnf.ts +389 -0
- package/src/scale.ts +42 -0
- package/src/schema.ts +4 -7
- package/src/territories.ts +102 -0
- package/src/testing/arbitraries.ts +4 -4
- package/src/testing/index.ts +1 -1
- package/src/tiers.ts +284 -0
- package/src/valibot.ts +4 -1
- package/src/wire.ts +322 -0
- package/src/words/de.ts +9 -0
- package/src/words/en.ts +9 -0
- package/src/words/es.ts +9 -0
- package/src/words/fr.ts +9 -0
- package/src/words/it.ts +9 -0
- package/src/words/nl.ts +9 -0
- package/src/words/pt.ts +9 -0
- package/src/words.ts +168 -0
- package/src/zod.ts +1 -1
- package/dist/plugins/fixed.d.ts +0 -17
- package/dist/plugins/fixed.d.ts.map +0 -1
- package/dist/plugins/fixed.js +0 -13
- package/dist/plugins/fixed.js.map +0 -1
- package/src/plugins/fixed.ts +0 -29
package/src/finance.ts
ADDED
|
@@ -0,0 +1,1219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@ducatjs/money/finance`: day counts, simple and compound interest, loan schedules, NPV and IRR,
|
|
3
|
+
* and subscription proration, in exact arithmetic. Every amount that is paid or booked is rounded
|
|
4
|
+
* once from its exact value; nothing in between is rounded, and no floats are used. Dates are
|
|
5
|
+
* calendar dates ('YYYY-MM-DD' or `{ year, month, day }`, so a `Temporal.PlainDate` works), never
|
|
6
|
+
* `Date`. Free functions only: nothing is added to Money.
|
|
7
|
+
*/
|
|
8
|
+
import { currencyExponent } from './currency.js';
|
|
9
|
+
import { formatScaled, pow10n, ratio, type Factor, type Ratio } from './decimal.js';
|
|
10
|
+
import { ExactMoney, exact } from './exact.js';
|
|
11
|
+
import { allocate, instalments, type Remainder } from './allocate.js';
|
|
12
|
+
import {
|
|
13
|
+
add,
|
|
14
|
+
compare,
|
|
15
|
+
CurrencyMismatchError,
|
|
16
|
+
make,
|
|
17
|
+
multiplyByRatio,
|
|
18
|
+
subtract,
|
|
19
|
+
sum,
|
|
20
|
+
type Money,
|
|
21
|
+
type RoundingInput,
|
|
22
|
+
} from './money.js';
|
|
23
|
+
import { DEFAULT_ROUNDING } from './rounding.js';
|
|
24
|
+
|
|
25
|
+
// ---------------------------------------------------------------------------------------------
|
|
26
|
+
// Calendar dates
|
|
27
|
+
// ---------------------------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* A calendar date: a 'YYYY-MM-DD' string or an object with `year`, `month` (1-12) and `day`
|
|
31
|
+
* fields, such as a `Temporal.PlainDate` in the ISO calendar. Proleptic Gregorian, years 0 to 9999,
|
|
32
|
+
* no time and no time zone.
|
|
33
|
+
*/
|
|
34
|
+
export type CalendarDate =
|
|
35
|
+
| string
|
|
36
|
+
| { readonly year: number; readonly month: number; readonly day: number };
|
|
37
|
+
|
|
38
|
+
/** A calendar date split into its fields, checked. */
|
|
39
|
+
interface DateFields {
|
|
40
|
+
readonly year: number;
|
|
41
|
+
readonly month: number;
|
|
42
|
+
readonly day: number;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const DATE_RE = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
46
|
+
|
|
47
|
+
function isLeapYear(year: number): boolean {
|
|
48
|
+
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function daysInMonth(year: number, month: number): number {
|
|
52
|
+
if (month === 2) {
|
|
53
|
+
return isLeapYear(year) ? 29 : 28;
|
|
54
|
+
}
|
|
55
|
+
// April, June, September and November have 30 days
|
|
56
|
+
return month === 4 || month === 6 || month === 9 || month === 11 ? 30 : 31;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function daysInYear(year: number): number {
|
|
60
|
+
return isLeapYear(year) ? 366 : 365;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** The fields of a calendar date, or a RangeError for anything that is not a real date. */
|
|
64
|
+
function dateFields(date: CalendarDate): DateFields {
|
|
65
|
+
let year: number;
|
|
66
|
+
let month: number;
|
|
67
|
+
let day: number;
|
|
68
|
+
if (typeof date === 'string') {
|
|
69
|
+
const match = DATE_RE.exec(date);
|
|
70
|
+
if (match === null) {
|
|
71
|
+
throw new RangeError(`Invalid date "${date}"; expected YYYY-MM-DD`);
|
|
72
|
+
}
|
|
73
|
+
year = Number(match[1]);
|
|
74
|
+
month = Number(match[2]);
|
|
75
|
+
day = Number(match[3]);
|
|
76
|
+
} else {
|
|
77
|
+
// a Temporal.PlainDate in another calendar has that calendar's fields, not Gregorian ones
|
|
78
|
+
const calendar = (date as { calendarId?: unknown }).calendarId;
|
|
79
|
+
if (calendar !== undefined && calendar !== 'iso8601') {
|
|
80
|
+
throw new RangeError(`Dates must be in the ISO calendar; got ${String(calendar)}`);
|
|
81
|
+
}
|
|
82
|
+
year = date.year;
|
|
83
|
+
month = date.month;
|
|
84
|
+
day = date.day;
|
|
85
|
+
}
|
|
86
|
+
// years 0-9999, as 'YYYY-MM-DD' writes them, keep every day and second count exact
|
|
87
|
+
const valid =
|
|
88
|
+
Number.isInteger(year) &&
|
|
89
|
+
year >= 0 &&
|
|
90
|
+
year <= 9999 &&
|
|
91
|
+
Number.isInteger(month) &&
|
|
92
|
+
Number.isInteger(day) &&
|
|
93
|
+
month >= 1 &&
|
|
94
|
+
month <= 12 &&
|
|
95
|
+
day >= 1 &&
|
|
96
|
+
day <= daysInMonth(year, month);
|
|
97
|
+
if (!valid) {
|
|
98
|
+
throw new RangeError(`Invalid date ${formatFields(year, month, day)}`);
|
|
99
|
+
}
|
|
100
|
+
return { year, month, day };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function formatFields(year: number, month: number, day: number): string {
|
|
104
|
+
const pad = (value: number) => String(value).padStart(2, '0');
|
|
105
|
+
return `${String(year).padStart(4, '0')}-${pad(month)}-${pad(day)}`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Days from 1970-01-01 to the date, in integers (Howard Hinnant's `days_from_civil`): the year is
|
|
110
|
+
* counted from March, so the leap day falls at the end of the counting year.
|
|
111
|
+
*/
|
|
112
|
+
function dayNumber({ year, month, day }: DateFields): number {
|
|
113
|
+
const shiftedYear = month <= 2 ? year - 1 : year;
|
|
114
|
+
const era = Math.floor(shiftedYear / 400);
|
|
115
|
+
const yearOfEra = shiftedYear - era * 400;
|
|
116
|
+
const monthFromMarch = month > 2 ? month - 3 : month + 9;
|
|
117
|
+
const dayOfYear = Math.floor((153 * monthFromMarch + 2) / 5) + day - 1;
|
|
118
|
+
const dayOfEra = yearOfEra * 365 + Math.floor(yearOfEra / 4) - Math.floor(yearOfEra / 100);
|
|
119
|
+
return era * 146_097 + dayOfEra + dayOfYear - 719_468;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Days from `start` to `end` (end exclusive): negative when `end` is earlier. */
|
|
123
|
+
function daysBetween(start: DateFields, end: DateFields): number {
|
|
124
|
+
return dayNumber(end) - dayNumber(start);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function assertNotBefore(start: DateFields, end: DateFields): void {
|
|
128
|
+
if (dayNumber(end) < dayNumber(start)) {
|
|
129
|
+
throw new RangeError('The end date is before the start date');
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ---------------------------------------------------------------------------------------------
|
|
134
|
+
// Day counts
|
|
135
|
+
// ---------------------------------------------------------------------------------------------
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* A day-count convention, as defined in the 2006 ISDA Definitions §4.16:
|
|
139
|
+
* - 'ACT/360' and 'ACT/365F': actual days over 360 or 365;
|
|
140
|
+
* - 'ACT/ACT ISDA': days in leap years over 366 plus days in other years over 365;
|
|
141
|
+
* - '30/360': Bond Basis, §4.16(f);
|
|
142
|
+
* - '30E/360': Eurobond Basis, §4.16(g);
|
|
143
|
+
* - '30E/360 ISDA': §4.16(h), which treats the last day of February as the 30th except on the
|
|
144
|
+
* termination date: when `end` is the last day of February, say whether it is with `maturity`.
|
|
145
|
+
*/
|
|
146
|
+
export type DayCount =
|
|
147
|
+
| 'ACT/360'
|
|
148
|
+
| 'ACT/365F'
|
|
149
|
+
| 'ACT/ACT ISDA'
|
|
150
|
+
| '30/360'
|
|
151
|
+
| '30E/360'
|
|
152
|
+
| '30E/360 ISDA';
|
|
153
|
+
|
|
154
|
+
/** Options for `yearFraction`. */
|
|
155
|
+
export interface YearFractionOptions {
|
|
156
|
+
/**
|
|
157
|
+
* '30E/360 ISDA' only: whether `end` is the termination (maturity) date, whose last day of
|
|
158
|
+
* February is kept as is. Required when `end` is the last day of February, because the two
|
|
159
|
+
* readings differ by up to two days and neither is a safe default.
|
|
160
|
+
*/
|
|
161
|
+
readonly maturity?: boolean;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function isLastDayOfFebruary({ year, month, day }: DateFields): boolean {
|
|
165
|
+
return month === 2 && day === daysInMonth(year, 2);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** The 30/360 family's day count: 360 × years + 30 × months + days, after adjusting the days. */
|
|
169
|
+
function thirty360Days(
|
|
170
|
+
start: DateFields,
|
|
171
|
+
end: DateFields,
|
|
172
|
+
startDay: number,
|
|
173
|
+
endDay: number,
|
|
174
|
+
): number {
|
|
175
|
+
return 360 * (end.year - start.year) + 30 * (end.month - start.month) + (endDay - startDay);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* The fraction of a year from `start` to `end` under `convention`, as an exact ratio. `end` may not
|
|
180
|
+
* be before `start`. This is not Excel's YEARFRAC: its basis 0 is the US (NASD) 30/360 with
|
|
181
|
+
* end-of-February rules rather than Bond Basis, its basis 1 divides by an average year length
|
|
182
|
+
* rather than splitting days by calendar year as ACT/ACT ISDA does, and it swaps reversed dates.
|
|
183
|
+
*
|
|
184
|
+
* yearFraction('2026-01-15', '2026-07-15', 'ACT/360') // 181/360
|
|
185
|
+
*/
|
|
186
|
+
export function yearFraction(
|
|
187
|
+
start: CalendarDate,
|
|
188
|
+
end: CalendarDate,
|
|
189
|
+
convention: DayCount,
|
|
190
|
+
options?: YearFractionOptions,
|
|
191
|
+
): Ratio {
|
|
192
|
+
const from = dateFields(start);
|
|
193
|
+
const to = dateFields(end);
|
|
194
|
+
assertNotBefore(from, to);
|
|
195
|
+
|
|
196
|
+
switch (convention) {
|
|
197
|
+
case 'ACT/360':
|
|
198
|
+
return ratio(BigInt(daysBetween(from, to)), 360n);
|
|
199
|
+
case 'ACT/365F':
|
|
200
|
+
return ratio(BigInt(daysBetween(from, to)), 365n);
|
|
201
|
+
case 'ACT/ACT ISDA':
|
|
202
|
+
return actualActualIsda(from, to);
|
|
203
|
+
case '30/360': {
|
|
204
|
+
// §4.16(f): D1 = 31 becomes 30; D2 = 31 becomes 30 only when D1 is then 30 (or was 31)
|
|
205
|
+
const startDay = from.day === 31 ? 30 : from.day;
|
|
206
|
+
const endDay = to.day === 31 && startDay === 30 ? 30 : to.day;
|
|
207
|
+
return ratio(BigInt(thirty360Days(from, to, startDay, endDay)), 360n);
|
|
208
|
+
}
|
|
209
|
+
case '30E/360': {
|
|
210
|
+
// §4.16(g): a 31st on either side becomes the 30th
|
|
211
|
+
const startDay = from.day === 31 ? 30 : from.day;
|
|
212
|
+
const endDay = to.day === 31 ? 30 : to.day;
|
|
213
|
+
return ratio(BigInt(thirty360Days(from, to, startDay, endDay)), 360n);
|
|
214
|
+
}
|
|
215
|
+
case '30E/360 ISDA': {
|
|
216
|
+
// §4.16(h): the last day of a month becomes the 30th, except the last day of February when
|
|
217
|
+
// it is the termination date
|
|
218
|
+
const startDay = from.day === 31 || isLastDayOfFebruary(from) ? 30 : from.day;
|
|
219
|
+
let endDay = to.day === 31 ? 30 : to.day;
|
|
220
|
+
if (isLastDayOfFebruary(to)) {
|
|
221
|
+
const maturity = options?.maturity;
|
|
222
|
+
if (typeof maturity !== 'boolean') {
|
|
223
|
+
throw new RangeError(
|
|
224
|
+
"'30E/360 ISDA' ending on the last day of February needs the option maturity: true or false",
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
endDay = maturity ? to.day : 30;
|
|
228
|
+
}
|
|
229
|
+
return ratio(BigInt(thirty360Days(from, to, startDay, endDay)), 360n);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return invalid('day count', convention);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** ACT/ACT ISDA: the days in each calendar year over that year's length, summed exactly. */
|
|
236
|
+
function actualActualIsda(from: DateFields, to: DateFields): Ratio {
|
|
237
|
+
if (from.year === to.year) {
|
|
238
|
+
return ratio(BigInt(daysBetween(from, to)), BigInt(daysInYear(from.year)));
|
|
239
|
+
}
|
|
240
|
+
const startYearEnd = { year: from.year + 1, month: 1, day: 1 };
|
|
241
|
+
const endYearStart = { year: to.year, month: 1, day: 1 };
|
|
242
|
+
const firstYearLength = BigInt(daysInYear(from.year));
|
|
243
|
+
const lastYearLength = BigInt(daysInYear(to.year));
|
|
244
|
+
const firstYearDays = BigInt(daysBetween(from, startYearEnd));
|
|
245
|
+
const lastYearDays = BigInt(daysBetween(endYearStart, to));
|
|
246
|
+
const wholeYears = BigInt(to.year - from.year - 1);
|
|
247
|
+
// firstYearDays/firstYearLength + wholeYears + lastYearDays/lastYearLength over one denominator
|
|
248
|
+
const denominator = firstYearLength * lastYearLength;
|
|
249
|
+
const numerator =
|
|
250
|
+
firstYearDays * lastYearLength + wholeYears * denominator + lastYearDays * firstYearLength;
|
|
251
|
+
return ratio(numerator, denominator);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// ---------------------------------------------------------------------------------------------
|
|
255
|
+
// Rounding helpers
|
|
256
|
+
// ---------------------------------------------------------------------------------------------
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* numerator / denominator major units (denominator > 0) rounded once: with a mode, to the
|
|
260
|
+
* currency's exponent; with a policy, to the policy's scale and increment. The fraction is not
|
|
261
|
+
* reduced first: rounding divides once either way, and a gcd of the thousands-of-bits integers a
|
|
262
|
+
* long schedule or NPV builds would cost more than everything else.
|
|
263
|
+
*/
|
|
264
|
+
function roundQuotient<C extends string>(
|
|
265
|
+
currency: C,
|
|
266
|
+
numerator: bigint,
|
|
267
|
+
denominator: bigint,
|
|
268
|
+
rounding: RoundingInput,
|
|
269
|
+
): Money<C> {
|
|
270
|
+
// one major unit times the fraction, so the rounding applies to the fraction itself
|
|
271
|
+
return multiplyAndRound(make(currency, 1, 0), unreducedRatio(numerator, denominator), rounding);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/** money × factor rounded once, at the currency's exponent or the policy's scale. */
|
|
275
|
+
function multiplyAndRound<C extends string>(
|
|
276
|
+
money: Money<C>,
|
|
277
|
+
factor: Ratio,
|
|
278
|
+
rounding: RoundingInput,
|
|
279
|
+
): Money<C> {
|
|
280
|
+
return multiplyByRatio(money, factor, rounding, currencyExponent(money.currency));
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* A ratio built without a gcd, for rounding once: multiplyByRatio divides by it, which gives the
|
|
285
|
+
* same result for any representation of the fraction.
|
|
286
|
+
*/
|
|
287
|
+
function unreducedRatio(numerator: bigint, denominator: bigint): Ratio {
|
|
288
|
+
const toNumber = (value: bigint) =>
|
|
289
|
+
value <= 9_007_199_254_740_991n && value >= -9_007_199_254_740_991n ? Number(value) : NaN;
|
|
290
|
+
return { num: numerator, den: denominator, n: toNumber(numerator), d: toNumber(denominator) };
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* The most periods a schedule or compounding may have: (1 + r)^n has about n times the bits of
|
|
295
|
+
* 1 + r, so this bounds the integers (a 30-year daily schedule is about 11,000 periods).
|
|
296
|
+
*/
|
|
297
|
+
const MAX_PERIODS = 100_000;
|
|
298
|
+
|
|
299
|
+
function assertPeriods(periods: number, minimum: number): void {
|
|
300
|
+
if (!(Number.isInteger(periods) && periods >= minimum && periods <= MAX_PERIODS)) {
|
|
301
|
+
invalid('periods', periods);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/** Whether `money` is a whole number of `unit`s (both of one currency, unit positive). */
|
|
306
|
+
function isMultipleOf(money: Money, unit: Money): boolean {
|
|
307
|
+
const scale = Math.max(money.scale, unit.scale);
|
|
308
|
+
const minor = BigInt(money.minor) * pow10n(scale - money.scale);
|
|
309
|
+
const unitMinor = BigInt(unit.minor) * pow10n(scale - unit.scale);
|
|
310
|
+
return minor % unitMinor === 0n;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/** Throws a RangeError naming the option and the value it was given. */
|
|
314
|
+
function invalid(name: string, value: unknown): never {
|
|
315
|
+
throw new RangeError(`Invalid ${name} ${String(value)}`);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function assertPositiveInteger(value: number, name: string): void {
|
|
319
|
+
if (!(Number.isSafeInteger(value) && value >= 1)) {
|
|
320
|
+
invalid(name, value);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function assertSameCurrency(money: Money, other: Money): void {
|
|
325
|
+
if (money.currency !== other.currency) {
|
|
326
|
+
throw new CurrencyMismatchError(money.currency, other.currency);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// ---------------------------------------------------------------------------------------------
|
|
331
|
+
// Interest
|
|
332
|
+
// ---------------------------------------------------------------------------------------------
|
|
333
|
+
|
|
334
|
+
/** The accrual period of `interest` and `interestExact`. */
|
|
335
|
+
export interface InterestPeriod extends YearFractionOptions {
|
|
336
|
+
/** The first day of interest. */
|
|
337
|
+
readonly from: CalendarDate;
|
|
338
|
+
/** The day interest runs to (exclusive, as in every day count). */
|
|
339
|
+
readonly to: CalendarDate;
|
|
340
|
+
/** The day-count convention. */
|
|
341
|
+
readonly dayCount: DayCount;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Simple interest, `principal × rate × yearFraction`, exactly. `rate` is an annual rate as a
|
|
346
|
+
* `Factor` ('0.05' for 5%).
|
|
347
|
+
*/
|
|
348
|
+
export function interestExact<C extends string>(
|
|
349
|
+
principal: Money<C>,
|
|
350
|
+
rate: Factor,
|
|
351
|
+
period: InterestPeriod,
|
|
352
|
+
): ExactMoney<C> {
|
|
353
|
+
const fraction = yearFraction(period.from, period.to, period.dayCount, period);
|
|
354
|
+
return exact(principal).multiply(rate).multiply(fraction);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Simple interest, `principal × rate × yearFraction`, rounded once (to the currency's exponent with
|
|
359
|
+
* a mode, or to a policy's scale; default 'halfExpand').
|
|
360
|
+
*
|
|
361
|
+
* interest(money('10000', 'EUR'), '0.05',
|
|
362
|
+
* { from: '2026-01-15', to: '2026-07-15', dayCount: 'ACT/365F' }, 'halfEven') // 247.95 EUR
|
|
363
|
+
*/
|
|
364
|
+
export function interest<C extends string>(
|
|
365
|
+
principal: Money<C>,
|
|
366
|
+
rate: Factor,
|
|
367
|
+
period: InterestPeriod,
|
|
368
|
+
rounding: RoundingInput = DEFAULT_ROUNDING,
|
|
369
|
+
): Money<C> {
|
|
370
|
+
const fraction = yearFraction(period.from, period.to, period.dayCount, period);
|
|
371
|
+
const rateRatio = ratio(rate);
|
|
372
|
+
return multiplyAndRound(
|
|
373
|
+
principal,
|
|
374
|
+
ratio(rateRatio.num * fraction.num, rateRatio.den * fraction.den),
|
|
375
|
+
rounding,
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/** The compounding of `compound`: how many periods, and how many make a year. */
|
|
380
|
+
export interface CompoundOptions {
|
|
381
|
+
/** A non-negative integer number of compounding periods. */
|
|
382
|
+
readonly periods: number;
|
|
383
|
+
/** Compounding periods per year (12 for monthly): the periodic rate is `rate / perYear`. */
|
|
384
|
+
readonly perYear: number;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/** (1 + rate/perYear) as a reduced ratio, checked to be positive. */
|
|
388
|
+
function growthFactor(rate: Factor, perYear: number): Ratio {
|
|
389
|
+
assertPositiveInteger(perYear, 'perYear');
|
|
390
|
+
const rateRatio = ratio(rate);
|
|
391
|
+
const perYearBig = BigInt(perYear);
|
|
392
|
+
const growth = ratio(rateRatio.num + rateRatio.den * perYearBig, rateRatio.den * perYearBig);
|
|
393
|
+
if (growth.num <= 0n) {
|
|
394
|
+
throw new RangeError(`A rate of ${rate} per year loses everything in one period`);
|
|
395
|
+
}
|
|
396
|
+
return growth;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Periodic compounding: `principal × (1 + rate/perYear)^periods` (at most 100,000 periods), an
|
|
401
|
+
* exact integer power, rounded once. Fractional periods and continuous compounding are irrational, so they are not offered.
|
|
402
|
+
*
|
|
403
|
+
* compound(money('1000', 'EUR'), '0.05', { periods: 12, perYear: 12 }, 'halfEven') // 1051.16 EUR
|
|
404
|
+
*/
|
|
405
|
+
export function compound<C extends string>(
|
|
406
|
+
principal: Money<C>,
|
|
407
|
+
rate: Factor,
|
|
408
|
+
options: CompoundOptions,
|
|
409
|
+
rounding: RoundingInput = DEFAULT_ROUNDING,
|
|
410
|
+
): Money<C> {
|
|
411
|
+
const periods = options.periods;
|
|
412
|
+
assertPeriods(periods, 0);
|
|
413
|
+
const growth = growthFactor(rate, options.perYear);
|
|
414
|
+
const exponent = BigInt(periods);
|
|
415
|
+
// a power of a reduced ratio is reduced, so no gcd is needed on the large integers
|
|
416
|
+
const power = unreducedRatio(growth.num ** exponent, growth.den ** exponent);
|
|
417
|
+
return multiplyAndRound(principal, power, rounding);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// ---------------------------------------------------------------------------------------------
|
|
421
|
+
// Amortisation
|
|
422
|
+
// ---------------------------------------------------------------------------------------------
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* How a loan is repaid:
|
|
426
|
+
* - 'annuity' (French): equal payments of interest and principal;
|
|
427
|
+
* - 'linear' (German): equal principal parts, with interest on top, so payments fall;
|
|
428
|
+
* - 'interestOnly' (bullet): interest every period, and the principal with the last payment.
|
|
429
|
+
*/
|
|
430
|
+
export type AmortisationMethod = 'annuity' | 'linear' | 'interestOnly';
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Where an annuity's rounding residual goes: the last payment (the default, which most lenders
|
|
434
|
+
* use), or the last period's interest, which keeps the last payment equal to the others.
|
|
435
|
+
*/
|
|
436
|
+
export type AmortisationResidual = 'lastPayment' | 'lastInterest';
|
|
437
|
+
|
|
438
|
+
/** Options for `amortise`. */
|
|
439
|
+
export interface AmortiseOptions<C extends string> {
|
|
440
|
+
/** The nominal annual rate, as a `Factor` ('0.045' for 4.5%). Not negative. */
|
|
441
|
+
readonly rate: Factor;
|
|
442
|
+
/** Payments per year (12 for monthly): the periodic rate is `rate / perYear`. */
|
|
443
|
+
readonly perYear: number;
|
|
444
|
+
/** The number of payments. */
|
|
445
|
+
readonly periods: number;
|
|
446
|
+
/** How the loan is repaid (default 'annuity'). */
|
|
447
|
+
readonly method?: AmortisationMethod;
|
|
448
|
+
/**
|
|
449
|
+
* How every payment and interest charge is rounded: a mode, to the currency's exponent, or a
|
|
450
|
+
* rounding policy (default 'halfExpand').
|
|
451
|
+
*/
|
|
452
|
+
readonly rounding?: RoundingInput;
|
|
453
|
+
/**
|
|
454
|
+
* An amount still owed after the regular payments, paid with the last one ('annuity' and
|
|
455
|
+
* 'linear' only).
|
|
456
|
+
*/
|
|
457
|
+
readonly balloon?: Money<C>;
|
|
458
|
+
/**
|
|
459
|
+
* The date of the first payment. With `perYear` 1, 2, 3, 4, 6 or 12 the later ones follow
|
|
460
|
+
* monthly, on this date's day of the month, clamped to shorter months (31 January, 28 February,
|
|
461
|
+
* 31 March). That is not the end-of-month rule: from 30 November, quarterly dates are 28 or 29
|
|
462
|
+
* February and then 30 May, not 31 May; pass an anchor of 31 for month ends. With 26 or 52, the
|
|
463
|
+
* payments are every 14 or 7 days.
|
|
464
|
+
*/
|
|
465
|
+
readonly firstPayment?: CalendarDate;
|
|
466
|
+
/**
|
|
467
|
+
* Where an annuity's rounding residual goes (default 'lastPayment'). 'lastInterest' applies to
|
|
468
|
+
* annuities only: linear and interest-only schedules have no residual.
|
|
469
|
+
*/
|
|
470
|
+
readonly residual?: AmortisationResidual;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/** One payment of an amortisation schedule. */
|
|
474
|
+
export interface AmortisationRow<C extends string> {
|
|
475
|
+
/** 1 for the first payment. */
|
|
476
|
+
readonly period: number;
|
|
477
|
+
/** The payment date ('YYYY-MM-DD'), when `firstPayment` is given. */
|
|
478
|
+
readonly date?: string;
|
|
479
|
+
/** Interest plus principal. */
|
|
480
|
+
readonly payment: Money<C>;
|
|
481
|
+
/** This period's interest: the balance before the payment times the periodic rate, rounded. */
|
|
482
|
+
readonly interest: Money<C>;
|
|
483
|
+
/** The part of the payment that repays the loan. */
|
|
484
|
+
readonly principal: Money<C>;
|
|
485
|
+
/** What is still owed after the payment. */
|
|
486
|
+
readonly balance: Money<C>;
|
|
487
|
+
/** Reserved for interest from a day count between payment dates; not set yet. */
|
|
488
|
+
readonly days?: number;
|
|
489
|
+
/** Reserved for interest from a day count between payment dates; not set yet. */
|
|
490
|
+
readonly yearFraction?: Ratio;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/** The result of `amortise`. */
|
|
494
|
+
export interface Amortisation<C extends string> {
|
|
495
|
+
/**
|
|
496
|
+
* The regular payment: for 'annuity' every payment but possibly the last; for 'linear' the
|
|
497
|
+
* first (the largest); for 'interestOnly' the interest payment.
|
|
498
|
+
*/
|
|
499
|
+
readonly payment: Money<C>;
|
|
500
|
+
/** One row per payment. */
|
|
501
|
+
readonly rows: AmortisationRow<C>[];
|
|
502
|
+
/** The sum of the interest column, exactly. */
|
|
503
|
+
readonly totalInterest: Money<C>;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/** The minor unit a rounding produces, for loans whose parts must be multiples of it. */
|
|
507
|
+
function roundingUnit<C extends string>(currency: C, rounding: RoundingInput): Money<C> {
|
|
508
|
+
if (typeof rounding === 'string') {
|
|
509
|
+
return make(currency, 1, currencyExponent(currency));
|
|
510
|
+
}
|
|
511
|
+
return make(currency, rounding.increment, rounding.scale ?? currencyExponent(currency));
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/** The payment dates: monthly steps from the anchor date, clamped to month ends, or 7 or 14 days. */
|
|
515
|
+
function paymentDates(first: CalendarDate, perYear: number, count: number): string[] {
|
|
516
|
+
const anchor = dateFields(first);
|
|
517
|
+
const dates: string[] = [];
|
|
518
|
+
if (perYear === 26 || perYear === 52) {
|
|
519
|
+
const step = perYear === 52 ? 7 : 14;
|
|
520
|
+
const anchorDay = dayNumber(anchor);
|
|
521
|
+
for (let i = 0; i < count; i++) {
|
|
522
|
+
dates.push(dateFromDayNumber(anchorDay + i * step));
|
|
523
|
+
}
|
|
524
|
+
return checkedDates(dates);
|
|
525
|
+
}
|
|
526
|
+
if (!(Number.isSafeInteger(perYear) && 12 % perYear === 0)) {
|
|
527
|
+
// payment dates step by whole months, or by 7 or 14 days
|
|
528
|
+
invalid('perYear for payment dates', perYear);
|
|
529
|
+
}
|
|
530
|
+
const monthStep = 12 / perYear;
|
|
531
|
+
for (let i = 0; i < count; i++) {
|
|
532
|
+
const monthIndex = anchor.month - 1 + i * monthStep;
|
|
533
|
+
const year = anchor.year + Math.floor(monthIndex / 12);
|
|
534
|
+
const month = (monthIndex % 12) + 1;
|
|
535
|
+
const day = Math.min(anchor.day, daysInMonth(year, month));
|
|
536
|
+
dates.push(formatFields(year, month, day));
|
|
537
|
+
}
|
|
538
|
+
return checkedDates(dates);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/** The dates, unless the last (and latest) is past the year 9999, which dates can't hold. */
|
|
542
|
+
function checkedDates(dates: string[]): string[] {
|
|
543
|
+
if (dates[dates.length - 1]!.length !== 10) {
|
|
544
|
+
throw new RangeError('The payment dates run past the year 9999');
|
|
545
|
+
}
|
|
546
|
+
return dates;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
/** The inverse of `dayNumber` (Howard Hinnant's `civil_from_days`). */
|
|
550
|
+
function dateFromDayNumber(days: number): string {
|
|
551
|
+
const shifted = days + 719_468;
|
|
552
|
+
const era = Math.floor(shifted / 146_097);
|
|
553
|
+
const dayOfEra = shifted - era * 146_097;
|
|
554
|
+
const yearOfEra = Math.floor(
|
|
555
|
+
(dayOfEra -
|
|
556
|
+
Math.floor(dayOfEra / 1460) +
|
|
557
|
+
Math.floor(dayOfEra / 36_524) -
|
|
558
|
+
Math.floor(dayOfEra / 146_096)) /
|
|
559
|
+
365,
|
|
560
|
+
);
|
|
561
|
+
const dayOfYear =
|
|
562
|
+
dayOfEra - (365 * yearOfEra + Math.floor(yearOfEra / 4) - Math.floor(yearOfEra / 100));
|
|
563
|
+
const monthFromMarch = Math.floor((5 * dayOfYear + 2) / 153);
|
|
564
|
+
const day = dayOfYear - Math.floor((153 * monthFromMarch + 2) / 5) + 1;
|
|
565
|
+
const month = monthFromMarch < 10 ? monthFromMarch + 3 : monthFromMarch - 9;
|
|
566
|
+
const year = yearOfEra + era * 400 + (month <= 2 ? 1 : 0);
|
|
567
|
+
return formatFields(year, month, day);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* A loan's repayment schedule. Each period's interest is the balance times `rate / perYear`,
|
|
572
|
+
* rounded once; the principal part is the payment minus the interest. The principal parts sum
|
|
573
|
+
* exactly to the loan, the balance never goes negative, and it ends at exactly zero: the rounding
|
|
574
|
+
* residual goes to the last payment (or, with `residual: 'lastInterest'`, to the last interest). If
|
|
575
|
+
* rounded payments clear the loan early, the schedule ends with the payment that clears it, so it
|
|
576
|
+
* can have fewer rows than `periods`. With a zero rate, the payments are
|
|
577
|
+
* `instalments(loan, periods, { remainder: 'toLast' })` (there is no interest to take a residual).
|
|
578
|
+
* The loan and the balloon must be multiples of the rounding's unit (0.01 EUR, or a policy's
|
|
579
|
+
* increment), so that every amount in the schedule is too. At most 100,000 periods.
|
|
580
|
+
*
|
|
581
|
+
* amortise(money('200000', 'EUR'), { rate: '0.045', perYear: 12, periods: 300 }).payment
|
|
582
|
+
* // 1111.66 EUR
|
|
583
|
+
*/
|
|
584
|
+
export function amortise<C extends string>(
|
|
585
|
+
loan: Money<C>,
|
|
586
|
+
options: AmortiseOptions<NoInfer<C>>,
|
|
587
|
+
): Amortisation<C> {
|
|
588
|
+
const periods = options.periods;
|
|
589
|
+
const perYear = options.perYear;
|
|
590
|
+
const method = options.method ?? 'annuity';
|
|
591
|
+
const rounding = options.rounding ?? DEFAULT_ROUNDING;
|
|
592
|
+
const residual = options.residual ?? 'lastPayment';
|
|
593
|
+
const currency = loan.currency;
|
|
594
|
+
const zeroAmount = make(currency, 0, currencyExponent(currency));
|
|
595
|
+
const balloon = options.balloon ?? zeroAmount;
|
|
596
|
+
|
|
597
|
+
assertPeriods(periods, 1);
|
|
598
|
+
assertPositiveInteger(perYear, 'perYear');
|
|
599
|
+
if (!(loan.minor > 0)) {
|
|
600
|
+
throw new RangeError(`The loan must be positive; got ${loan}`);
|
|
601
|
+
}
|
|
602
|
+
assertSameCurrency(loan, balloon);
|
|
603
|
+
if (balloon.minor < 0 || compare(balloon, loan) > 0) {
|
|
604
|
+
throw new RangeError(`The balloon must be between zero and the loan; got ${balloon}`);
|
|
605
|
+
}
|
|
606
|
+
if (options.balloon !== undefined && method === 'interestOnly') {
|
|
607
|
+
throw new RangeError("An 'interestOnly' loan repays all of it at the end; drop the balloon");
|
|
608
|
+
}
|
|
609
|
+
if (method !== 'annuity' && method !== 'linear' && method !== 'interestOnly') {
|
|
610
|
+
invalid('method', method);
|
|
611
|
+
}
|
|
612
|
+
if (residual !== 'lastPayment' && residual !== 'lastInterest') {
|
|
613
|
+
invalid('residual', residual);
|
|
614
|
+
}
|
|
615
|
+
const unit = roundingUnit(currency, rounding);
|
|
616
|
+
if (!isMultipleOf(loan, unit) || !isMultipleOf(balloon, unit)) {
|
|
617
|
+
throw new RangeError(`The loan and the balloon must be multiples of ${unit}`);
|
|
618
|
+
}
|
|
619
|
+
const rateRatio = ratio(options.rate);
|
|
620
|
+
if (rateRatio.num < 0n) {
|
|
621
|
+
throw new RangeError(`The rate must not be negative; got ${options.rate}`);
|
|
622
|
+
}
|
|
623
|
+
// with no interest to round, there is no residual to put in it
|
|
624
|
+
if (residual === 'lastInterest' && (method !== 'annuity' || rateRatio.num === 0n)) {
|
|
625
|
+
throw new RangeError("residual 'lastInterest' applies to annuities with a non-zero rate only");
|
|
626
|
+
}
|
|
627
|
+
const dates =
|
|
628
|
+
options.firstPayment === undefined
|
|
629
|
+
? undefined
|
|
630
|
+
: paymentDates(options.firstPayment, perYear, periods);
|
|
631
|
+
const periodicRate = ratio(rateRatio.num, rateRatio.den * BigInt(perYear));
|
|
632
|
+
|
|
633
|
+
const rows: AmortisationRow<C>[] = [];
|
|
634
|
+
const pushRow = (
|
|
635
|
+
payment: Money<C>,
|
|
636
|
+
interestPart: Money<C>,
|
|
637
|
+
principal: Money<C>,
|
|
638
|
+
balance: Money<C>,
|
|
639
|
+
) => {
|
|
640
|
+
const period = rows.length + 1;
|
|
641
|
+
const row: AmortisationRow<C> =
|
|
642
|
+
dates === undefined
|
|
643
|
+
? { period, payment, interest: interestPart, principal, balance }
|
|
644
|
+
: { period, date: dates[period - 1]!, payment, interest: interestPart, principal, balance };
|
|
645
|
+
rows.push(row);
|
|
646
|
+
};
|
|
647
|
+
|
|
648
|
+
// The principal parts of a linear loan, or of any loan at a zero rate: equal parts that sum to
|
|
649
|
+
// the loan exactly, each a multiple of the rounding's unit, the remainder in the last one.
|
|
650
|
+
const isZeroRate = rateRatio.num === 0n;
|
|
651
|
+
if (method === 'linear' || (method === 'annuity' && isZeroRate)) {
|
|
652
|
+
const parts = instalments(subtract(loan, balloon), periods, {
|
|
653
|
+
remainder: 'toLast',
|
|
654
|
+
unit,
|
|
655
|
+
});
|
|
656
|
+
let balance = loan;
|
|
657
|
+
for (let i = 0; i < periods; i++) {
|
|
658
|
+
const isLast = i === periods - 1;
|
|
659
|
+
const principal = isLast ? balance : parts[i]!;
|
|
660
|
+
const interestPart = multiplyAndRound(balance, periodicRate, rounding);
|
|
661
|
+
balance = subtract(balance, principal);
|
|
662
|
+
pushRow(add(principal, interestPart), interestPart, principal, balance);
|
|
663
|
+
}
|
|
664
|
+
return finish(rows, currency);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
if (method === 'interestOnly') {
|
|
668
|
+
const interestPart = multiplyAndRound(loan, periodicRate, rounding);
|
|
669
|
+
for (let i = 0; i < periods; i++) {
|
|
670
|
+
const isLast = i === periods - 1;
|
|
671
|
+
const principal = isLast ? loan : zeroAmount;
|
|
672
|
+
const balance = isLast ? zeroAmount : loan;
|
|
673
|
+
pushRow(add(principal, interestPart), interestPart, principal, balance);
|
|
674
|
+
}
|
|
675
|
+
return { payment: interestPart, rows, totalInterest: sumInterest(rows, currency) };
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// Annuity: payment = (P·g^n − B)·r / (g^n − 1), with r = a/b and g = 1 + r = (a + b)/b. With
|
|
679
|
+
// G = (a + b)^n and H = b^n that is (P·G − B·H)·a / (b·(G − H)), and P and B are minor units
|
|
680
|
+
// over 10^scale, so the exact payment is one fraction of integers, rounded once.
|
|
681
|
+
const scale = Math.max(loan.scale, balloon.scale);
|
|
682
|
+
const loanMinor = BigInt(loan.minor) * pow10n(scale - loan.scale);
|
|
683
|
+
const balloonMinor = BigInt(balloon.minor) * pow10n(scale - balloon.scale);
|
|
684
|
+
const exponent = BigInt(periods);
|
|
685
|
+
const growthPower = (periodicRate.num + periodicRate.den) ** exponent;
|
|
686
|
+
const basePower = periodicRate.den ** exponent;
|
|
687
|
+
const payment = roundQuotient(
|
|
688
|
+
currency,
|
|
689
|
+
(loanMinor * growthPower - balloonMinor * basePower) * periodicRate.num,
|
|
690
|
+
pow10n(scale) * periodicRate.den * (growthPower - basePower),
|
|
691
|
+
rounding,
|
|
692
|
+
);
|
|
693
|
+
|
|
694
|
+
let balance = loan;
|
|
695
|
+
for (let i = 0; i < periods; i++) {
|
|
696
|
+
const interestPart = multiplyAndRound(balance, periodicRate, rounding);
|
|
697
|
+
const isLast = i === periods - 1;
|
|
698
|
+
const owed = add(balance, interestPart);
|
|
699
|
+
// the last payment, or a regular payment that would clear more than is owed, settles the loan
|
|
700
|
+
if (isLast || compare(payment, owed) >= 0) {
|
|
701
|
+
if (isLast && residual === 'lastInterest') {
|
|
702
|
+
const lastPayment = add(payment, balloon);
|
|
703
|
+
const lastInterest = subtract(lastPayment, balance);
|
|
704
|
+
if (lastInterest.minor < 0) {
|
|
705
|
+
throw new RangeError(
|
|
706
|
+
"The residual exceeds the last interest; use residual 'lastPayment'",
|
|
707
|
+
);
|
|
708
|
+
}
|
|
709
|
+
pushRow(lastPayment, lastInterest, balance, subtract(balance, balance));
|
|
710
|
+
} else {
|
|
711
|
+
pushRow(owed, interestPart, balance, subtract(balance, balance));
|
|
712
|
+
}
|
|
713
|
+
break;
|
|
714
|
+
}
|
|
715
|
+
const principal = subtract(payment, interestPart);
|
|
716
|
+
balance = subtract(balance, principal);
|
|
717
|
+
pushRow(payment, interestPart, principal, balance);
|
|
718
|
+
}
|
|
719
|
+
return { payment, rows, totalInterest: sumInterest(rows, currency) };
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
function sumInterest<C extends string>(rows: readonly AmortisationRow<C>[], currency: C): Money<C> {
|
|
723
|
+
// the rows share one currency, which the generic C cannot show the type checker
|
|
724
|
+
const interestColumn = rows.map((row) => row.interest as Money<string>);
|
|
725
|
+
return sum(interestColumn, currency) as Money<C>;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
function finish<C extends string>(rows: AmortisationRow<C>[], currency: C): Amortisation<C> {
|
|
729
|
+
return { payment: rows[0]!.payment, rows, totalInterest: sumInterest(rows, currency) };
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
// ---------------------------------------------------------------------------------------------
|
|
733
|
+
// NPV and IRR
|
|
734
|
+
// ---------------------------------------------------------------------------------------------
|
|
735
|
+
|
|
736
|
+
/** The flows' minor units at one common scale, checked to share a currency. */
|
|
737
|
+
function flowMinors<C extends string>(
|
|
738
|
+
flows: readonly Money<C>[],
|
|
739
|
+
): { minors: bigint[]; scale: number } {
|
|
740
|
+
if (flows.length === 0) {
|
|
741
|
+
throw new RangeError('At least one cash flow is needed');
|
|
742
|
+
}
|
|
743
|
+
const first = flows[0]!;
|
|
744
|
+
let scale = 0;
|
|
745
|
+
for (const flow of flows) {
|
|
746
|
+
assertSameCurrency(first, flow);
|
|
747
|
+
if (flow.scale > scale) {
|
|
748
|
+
scale = flow.scale;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
const minors = flows.map((flow) => BigInt(flow.minor) * pow10n(scale - flow.scale));
|
|
752
|
+
return { minors, scale };
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* Σ flowₜ · bᵗ · q^(n−t) for a rate of (q − b)/b: the NPV times q^n, by Horner's rule. q^n is
|
|
757
|
+
* positive for a rate above −1, so this has the NPV's sign.
|
|
758
|
+
*/
|
|
759
|
+
function scaledPresentValue(
|
|
760
|
+
minors: readonly bigint[],
|
|
761
|
+
growthNumerator: bigint,
|
|
762
|
+
base: bigint,
|
|
763
|
+
): bigint {
|
|
764
|
+
let accumulator = minors[0]!;
|
|
765
|
+
let basePower = 1n;
|
|
766
|
+
for (let i = 1; i < minors.length; i++) {
|
|
767
|
+
basePower *= base;
|
|
768
|
+
accumulator = accumulator * growthNumerator + minors[i]! * basePower;
|
|
769
|
+
}
|
|
770
|
+
return accumulator;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* The discounted sum of flows at periods 0, 1, 2…, `Σ flowₜ / (1 + rate)ᵗ`, exactly: the textbook
|
|
775
|
+
* NPV, with the first flow (an initial outlay, say) not discounted. `npvExact` is Excel's form. (Not
|
|
776
|
+
* Excel's PV, which is an annuity's present value.)
|
|
777
|
+
*/
|
|
778
|
+
export function discountedSumExact<C extends string>(
|
|
779
|
+
rate: Factor,
|
|
780
|
+
flows: readonly Money<C>[],
|
|
781
|
+
): ExactMoney<C> {
|
|
782
|
+
const [numerator, denominator] = discountedValue(rate, flows, false);
|
|
783
|
+
return new ExactMoney(flows[0]!.currency, numerator, denominator);
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
/**
|
|
787
|
+
* The discounted sum of flows at periods 0, 1, 2…, exact and rounded once (to the currency's
|
|
788
|
+
* exponent with a mode, or to a policy's scale; default 'halfExpand'). The first flow is not
|
|
789
|
+
* discounted; this is `flows[0] + npv(rate, flows.slice(1))`.
|
|
790
|
+
*
|
|
791
|
+
* discountedSum('0.08', [money('-1000', 'EUR'), money('300', 'EUR'), money('400', 'EUR'),
|
|
792
|
+
* money('500', 'EUR')], 'halfEven') // 17.63 EUR
|
|
793
|
+
*/
|
|
794
|
+
export function discountedSum<C extends string>(
|
|
795
|
+
rate: Factor,
|
|
796
|
+
flows: readonly Money<C>[],
|
|
797
|
+
rounding: RoundingInput = DEFAULT_ROUNDING,
|
|
798
|
+
): Money<C> {
|
|
799
|
+
const [numerator, denominator] = discountedValue(rate, flows, false);
|
|
800
|
+
return roundQuotient(flows[0]!.currency, numerator, denominator, rounding);
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/**
|
|
804
|
+
* Excel's NPV, exactly: `Σ flowᵢ / (1 + rate)ⁱ` for i = 1, 2…, so the first flow is at the end of
|
|
805
|
+
* period 1 and is discounted once. Like Excel, an initial outlay at period 0 goes outside:
|
|
806
|
+
* `flows[0] + npv(rate, rest)`, or use `discountedSumExact`.
|
|
807
|
+
*/
|
|
808
|
+
export function npvExact<C extends string>(
|
|
809
|
+
rate: Factor,
|
|
810
|
+
flows: readonly Money<C>[],
|
|
811
|
+
): ExactMoney<C> {
|
|
812
|
+
const [numerator, denominator] = discountedValue(rate, flows, true);
|
|
813
|
+
return new ExactMoney(flows[0]!.currency, numerator, denominator);
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
/**
|
|
817
|
+
* Excel's NPV: flows at the ends of periods 1, 2…, exact and rounded once (to the currency's
|
|
818
|
+
* exponent with a mode, or to a policy's scale; default 'halfExpand'), so it matches Excel to the
|
|
819
|
+
* cent. Like Excel, an initial outlay at period 0 goes outside: `flows[0] + npv(rate, rest)`, or use
|
|
820
|
+
* `discountedSum`, which takes flows from period 0.
|
|
821
|
+
*
|
|
822
|
+
* npv('0.1', [money('-10000', 'USD'), money('3000', 'USD'), money('4200', 'USD'),
|
|
823
|
+
* money('6800', 'USD')]) // 1188.44 USD, as Excel's NPV(10%, -10000, 3000, 4200, 6800)
|
|
824
|
+
*/
|
|
825
|
+
export function npv<C extends string>(
|
|
826
|
+
rate: Factor,
|
|
827
|
+
flows: readonly Money<C>[],
|
|
828
|
+
rounding: RoundingInput = DEFAULT_ROUNDING,
|
|
829
|
+
): Money<C> {
|
|
830
|
+
const [numerator, denominator] = discountedValue(rate, flows, true);
|
|
831
|
+
return roundQuotient(flows[0]!.currency, numerator, denominator, rounding);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
/**
|
|
835
|
+
* The flows' discounted value in major units as an unreduced fraction (denominator positive), the
|
|
836
|
+
* first flow at period 0, or at period 1 when `fromPeriodOne`.
|
|
837
|
+
*/
|
|
838
|
+
function discountedValue(
|
|
839
|
+
rate: Factor,
|
|
840
|
+
flows: readonly Money[],
|
|
841
|
+
fromPeriodOne: boolean,
|
|
842
|
+
): [bigint, bigint] {
|
|
843
|
+
const { minors, scale } = flowMinors(flows);
|
|
844
|
+
const rateRatio = ratio(rate);
|
|
845
|
+
const growthNumerator = rateRatio.num + rateRatio.den;
|
|
846
|
+
if (growthNumerator <= 0n) {
|
|
847
|
+
throw new RangeError(`The rate must be above -1; got ${rate}`);
|
|
848
|
+
}
|
|
849
|
+
let numerator = scaledPresentValue(minors, growthNumerator, rateRatio.den);
|
|
850
|
+
let denominator = growthNumerator ** BigInt(minors.length - 1) * pow10n(scale);
|
|
851
|
+
if (fromPeriodOne) {
|
|
852
|
+
// one more period: divide by 1 + rate = growthNumerator / den
|
|
853
|
+
numerator *= rateRatio.den;
|
|
854
|
+
denominator *= growthNumerator;
|
|
855
|
+
}
|
|
856
|
+
return [numerator, denominator];
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
/** Options for `irr`. */
|
|
860
|
+
export interface IrrOptions {
|
|
861
|
+
/** Decimal places of the result (default 10). */
|
|
862
|
+
readonly digits?: number;
|
|
863
|
+
/**
|
|
864
|
+
* Rates the IRR lies between (inclusive), with NPVs of opposite signs at the two ends, taken at
|
|
865
|
+
* the exact rates given. Needed when the flows change sign more than once, since they can then
|
|
866
|
+
* have several IRRs; it replaces Excel's `guess`.
|
|
867
|
+
*/
|
|
868
|
+
readonly bracket?: readonly [Factor, Factor];
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
/** The result of `irr`: decimal strings with `digits` places. */
|
|
872
|
+
export interface IrrResult {
|
|
873
|
+
/** The IRR floored to `digits` places: the same as `lower`. */
|
|
874
|
+
readonly rate: string;
|
|
875
|
+
/**
|
|
876
|
+
* The IRR rounded down to `digits` places. Without a bracket, the NPV at `lower` has the
|
|
877
|
+
* opposite sign to the NPV at `upper`, or is zero.
|
|
878
|
+
*/
|
|
879
|
+
readonly lower: string;
|
|
880
|
+
/** `lower` plus 10^−digits. */
|
|
881
|
+
readonly upper: string;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
function signOf(value: bigint): -1 | 0 | 1 {
|
|
885
|
+
if (value > 0n) {
|
|
886
|
+
return 1;
|
|
887
|
+
}
|
|
888
|
+
if (value < 0n) {
|
|
889
|
+
return -1;
|
|
890
|
+
}
|
|
891
|
+
return 0;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
/** Sign changes in the flows, zeros skipped, and the sign of the first non-zero flow. */
|
|
895
|
+
function signChanges(minors: readonly bigint[]): { changes: number; firstSign: -1 | 0 | 1 } {
|
|
896
|
+
let changes = 0;
|
|
897
|
+
let firstSign: -1 | 0 | 1 = 0;
|
|
898
|
+
let previous: -1 | 0 | 1 = 0;
|
|
899
|
+
for (const minor of minors) {
|
|
900
|
+
const sign = signOf(minor);
|
|
901
|
+
if (sign === 0) {
|
|
902
|
+
continue;
|
|
903
|
+
}
|
|
904
|
+
if (firstSign === 0) {
|
|
905
|
+
firstSign = sign;
|
|
906
|
+
}
|
|
907
|
+
if (previous !== 0 && sign !== previous) {
|
|
908
|
+
changes++;
|
|
909
|
+
}
|
|
910
|
+
previous = sign;
|
|
911
|
+
}
|
|
912
|
+
return { changes, firstSign };
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
/**
|
|
916
|
+
* Excel's IRR, exactly: the rate at which the NPV of flows at periods 0, 1, 2… (the first value
|
|
917
|
+
* undiscounted, as in Excel's IRR) is zero. The rate is exact to `digits` decimal places (default
|
|
918
|
+
* 10), floored: `rate` is `lower`, the IRR rounded down, and the IRR lies between `lower` and
|
|
919
|
+
* `upper`, 10^−digits apart. It is a polynomial root, irrational in general, so it is found by
|
|
920
|
+
* bisection in exact integer arithmetic on the grid of `digits`-place decimals, with no floats and
|
|
921
|
+
* no Newton steps; each step checks the NPV's sign alone, by Horner's rule on integers.
|
|
922
|
+
*
|
|
923
|
+
* Where Excel guesses, this throws a RangeError. The flows never change sign: there is no IRR
|
|
924
|
+
* (Excel returns #NUM!). They change sign more than once: there can be several IRRs, and Excel
|
|
925
|
+
* returns whichever its Newton iteration from `guess` finds; here a `bracket`, which replaces
|
|
926
|
+
* `guess`, must select one, and the IRR returned is one inside it. The IRR is within 10^−digits of
|
|
927
|
+
* −1, where the NPV is undefined: ask for more digits.
|
|
928
|
+
*
|
|
929
|
+
* irr([money('-1000', 'EUR'), money('300', 'EUR'), money('400', 'EUR'), money('500', 'EUR')],
|
|
930
|
+
* { digits: 8 }) // { rate: '0.08896339', lower: '0.08896339', upper: '0.08896340' }
|
|
931
|
+
*/
|
|
932
|
+
export function irr(flows: readonly Money[], options?: IrrOptions): IrrResult {
|
|
933
|
+
const digits = options?.digits ?? 10;
|
|
934
|
+
if (!(Number.isInteger(digits) && digits >= 0 && digits <= 100)) {
|
|
935
|
+
invalid('digits', digits);
|
|
936
|
+
}
|
|
937
|
+
const { minors } = flowMinors(flows);
|
|
938
|
+
const { changes, firstSign } = signChanges(minors);
|
|
939
|
+
const bracket = options?.bracket;
|
|
940
|
+
if (changes === 0) {
|
|
941
|
+
throw new RangeError('The cash flows never change sign, so they have no IRR');
|
|
942
|
+
}
|
|
943
|
+
if (changes > 1 && bracket === undefined) {
|
|
944
|
+
throw new RangeError(
|
|
945
|
+
`The cash flows change sign ${changes} times, so they can have several IRRs; pass a bracket`,
|
|
946
|
+
);
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
// A rate of m / 10^digits: its growth factor's numerator is 10^digits + m over 10^digits.
|
|
950
|
+
const unit = pow10n(digits);
|
|
951
|
+
const signAt = (grid: bigint): -1 | 0 | 1 =>
|
|
952
|
+
signOf(scaledPresentValue(minors, unit + grid, unit));
|
|
953
|
+
const result = (grid: bigint): IrrResult => {
|
|
954
|
+
// the NPV is undefined at -1, so a lower end there is no answer
|
|
955
|
+
if (grid <= -unit) {
|
|
956
|
+
throw new RangeError(`The IRR is within 10^-${digits} of -1; ask for more digits`);
|
|
957
|
+
}
|
|
958
|
+
const lower = formatScaled(grid, digits);
|
|
959
|
+
return { rate: lower, lower, upper: formatScaled(grid + 1n, digits) };
|
|
960
|
+
};
|
|
961
|
+
|
|
962
|
+
let low: bigint;
|
|
963
|
+
let high: bigint;
|
|
964
|
+
let lowSign: -1 | 0 | 1;
|
|
965
|
+
if (bracket !== undefined) {
|
|
966
|
+
const bracketed = bracketToGrid(bracket, minors, unit, signAt);
|
|
967
|
+
if (typeof bracketed === 'bigint') {
|
|
968
|
+
return result(bracketed);
|
|
969
|
+
}
|
|
970
|
+
[low, high, lowSign] = bracketed;
|
|
971
|
+
} else {
|
|
972
|
+
// One sign change: exactly one IRR above −1 (Descartes' rule in 1/(1 + rate)). The NPV has the
|
|
973
|
+
// first flow's sign for large rates and the last flow's sign near −1.
|
|
974
|
+
const zeroSign = signAt(0n);
|
|
975
|
+
if (zeroSign === 0) {
|
|
976
|
+
return result(0n);
|
|
977
|
+
}
|
|
978
|
+
if (zeroSign === firstSign) {
|
|
979
|
+
// the IRR is negative: between −1 (excluded, where the NPV has the last flow's sign) and 0
|
|
980
|
+
low = -unit;
|
|
981
|
+
high = 0n;
|
|
982
|
+
lowSign = -firstSign as -1 | 1;
|
|
983
|
+
} else {
|
|
984
|
+
// the IRR is positive: double the upper end until the NPV takes the first flow's sign
|
|
985
|
+
low = 0n;
|
|
986
|
+
high = unit;
|
|
987
|
+
lowSign = zeroSign;
|
|
988
|
+
for (;;) {
|
|
989
|
+
const highSign = signAt(high);
|
|
990
|
+
if (highSign === 0) {
|
|
991
|
+
return result(high);
|
|
992
|
+
}
|
|
993
|
+
if (highSign !== lowSign) {
|
|
994
|
+
break;
|
|
995
|
+
}
|
|
996
|
+
low = high;
|
|
997
|
+
high *= 2n;
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
// Bisection on the grid: the bracket's ends stay `digits`-place decimals, so the integers stay
|
|
1003
|
+
// bounded by the digits asked for, and each step halves the bracket.
|
|
1004
|
+
while (high - low > 1n) {
|
|
1005
|
+
const middle = (low + high) / 2n;
|
|
1006
|
+
const middleSign = signAt(middle);
|
|
1007
|
+
if (middleSign === 0) {
|
|
1008
|
+
return result(middle);
|
|
1009
|
+
}
|
|
1010
|
+
if (middleSign === lowSign) {
|
|
1011
|
+
low = middle;
|
|
1012
|
+
} else {
|
|
1013
|
+
high = middle;
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
return result(low);
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
/**
|
|
1020
|
+
* The caller's bracket narrowed to the grid of `unit`ths: the NPV's signs are taken at the exact
|
|
1021
|
+
* bracket ends first, so the root found is one inside the bracket. Returns the grid point at or
|
|
1022
|
+
* below the root when the root is already pinned to one grid step, else the grid bracket to bisect
|
|
1023
|
+
* and the sign at its lower end.
|
|
1024
|
+
*/
|
|
1025
|
+
function bracketToGrid(
|
|
1026
|
+
bracket: readonly [Factor, Factor],
|
|
1027
|
+
minors: readonly bigint[],
|
|
1028
|
+
unit: bigint,
|
|
1029
|
+
signAt: (grid: bigint) => -1 | 0 | 1,
|
|
1030
|
+
): bigint | [bigint, bigint, -1 | 1] {
|
|
1031
|
+
const lowRate = ratio(bracket[0]);
|
|
1032
|
+
const highRate = ratio(bracket[1]);
|
|
1033
|
+
if (!(lowRate.num * highRate.den < highRate.num * lowRate.den)) {
|
|
1034
|
+
throw new RangeError('The bracket must be two increasing rates');
|
|
1035
|
+
}
|
|
1036
|
+
if (lowRate.num + lowRate.den <= 0n) {
|
|
1037
|
+
throw new RangeError('The bracket must be above -1');
|
|
1038
|
+
}
|
|
1039
|
+
const signAtRate = (rate: Ratio) =>
|
|
1040
|
+
signOf(scaledPresentValue(minors, rate.num + rate.den, rate.den));
|
|
1041
|
+
const lowSign = signAtRate(lowRate);
|
|
1042
|
+
const highSign = signAtRate(highRate);
|
|
1043
|
+
// an end that is itself a root: the grid point at or below it
|
|
1044
|
+
if (lowSign === 0) {
|
|
1045
|
+
return floorDivide(lowRate.num * unit, lowRate.den);
|
|
1046
|
+
}
|
|
1047
|
+
if (highSign === 0) {
|
|
1048
|
+
return floorDivide(highRate.num * unit, highRate.den);
|
|
1049
|
+
}
|
|
1050
|
+
if (lowSign === highSign) {
|
|
1051
|
+
throw new RangeError('The NPV has the same sign at both ends of the bracket');
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
// the grid points inside the bracket
|
|
1055
|
+
const gridLow = -floorDivide(-lowRate.num * unit, lowRate.den);
|
|
1056
|
+
const gridHigh = floorDivide(highRate.num * unit, highRate.den);
|
|
1057
|
+
if (gridLow > gridHigh) {
|
|
1058
|
+
// no grid point inside: the root is between the two around the bracket
|
|
1059
|
+
return gridHigh;
|
|
1060
|
+
}
|
|
1061
|
+
const gridLowSign = signAt(gridLow);
|
|
1062
|
+
if (gridLowSign === 0) {
|
|
1063
|
+
return gridLow;
|
|
1064
|
+
}
|
|
1065
|
+
if (gridLowSign !== lowSign) {
|
|
1066
|
+
// the sign changes between the bracket's low end and the first grid point
|
|
1067
|
+
return gridLow - 1n;
|
|
1068
|
+
}
|
|
1069
|
+
const gridHighSign = gridHigh === gridLow ? gridLowSign : signAt(gridHigh);
|
|
1070
|
+
if (gridHighSign === 0) {
|
|
1071
|
+
return gridHigh;
|
|
1072
|
+
}
|
|
1073
|
+
if (gridHighSign === lowSign) {
|
|
1074
|
+
// the sign changes between the last grid point and the bracket's high end
|
|
1075
|
+
return gridHigh;
|
|
1076
|
+
}
|
|
1077
|
+
return [gridLow, gridHigh, lowSign];
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
function floorDivide(dividend: bigint, divisor: bigint): bigint {
|
|
1081
|
+
const quotient = dividend / divisor;
|
|
1082
|
+
// bigint division truncates; step down when a negative quotient left a remainder
|
|
1083
|
+
return dividend % divisor !== 0n && dividend < 0n !== divisor < 0n ? quotient - 1n : quotient;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
// ---------------------------------------------------------------------------------------------
|
|
1087
|
+
// Proration
|
|
1088
|
+
// ---------------------------------------------------------------------------------------------
|
|
1089
|
+
|
|
1090
|
+
/**
|
|
1091
|
+
* A point in time for proration: a calendar date (midnight UTC), or with `unit: 'second'`, an ISO
|
|
1092
|
+
* date-time in UTC ('2026-03-11T09:30:00Z') or a Unix time in whole seconds.
|
|
1093
|
+
*/
|
|
1094
|
+
export type ProrationInstant = CalendarDate | number;
|
|
1095
|
+
|
|
1096
|
+
/** A span of time, `end` exclusive. */
|
|
1097
|
+
export interface ProrationPeriod {
|
|
1098
|
+
readonly start: ProrationInstant;
|
|
1099
|
+
readonly end: ProrationInstant;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
/** What proration counts: whole days (the default) or seconds. */
|
|
1103
|
+
export type ProrationUnit = 'day' | 'second';
|
|
1104
|
+
|
|
1105
|
+
/** Options for `prorate`. */
|
|
1106
|
+
export interface ProrateOptions {
|
|
1107
|
+
/** The billing period the price is for. */
|
|
1108
|
+
readonly period: ProrationPeriod;
|
|
1109
|
+
/** The part of it being charged or credited; inside `period`. */
|
|
1110
|
+
readonly used: ProrationPeriod;
|
|
1111
|
+
/** Count days (the default) or seconds. */
|
|
1112
|
+
readonly unit?: ProrationUnit;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
/** Options for `splitPeriod`. */
|
|
1116
|
+
export interface SplitPeriodOptions {
|
|
1117
|
+
/** The billing period the price is for. */
|
|
1118
|
+
readonly period: ProrationPeriod;
|
|
1119
|
+
/** The change points, in increasing order, strictly inside the period. */
|
|
1120
|
+
readonly at: readonly ProrationInstant[];
|
|
1121
|
+
/** Count days (the default) or seconds. */
|
|
1122
|
+
readonly unit?: ProrationUnit;
|
|
1123
|
+
/** Where indivisible minor units go (default 'largest', as in `allocate`). */
|
|
1124
|
+
readonly remainder?: Remainder;
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
const DATE_TIME_RE = /^(\d{4}-\d{2}-\d{2})T(\d{2}):(\d{2}):(\d{2})Z$/;
|
|
1128
|
+
|
|
1129
|
+
/** The instant as a count of `unit`s since 1970-01-01T00:00:00Z. */
|
|
1130
|
+
function instantCount(instant: ProrationInstant, unit: ProrationUnit): number {
|
|
1131
|
+
if (unit !== 'day' && unit !== 'second') {
|
|
1132
|
+
invalid('unit', unit);
|
|
1133
|
+
}
|
|
1134
|
+
if (typeof instant === 'number') {
|
|
1135
|
+
if (unit !== 'second' || !Number.isSafeInteger(instant)) {
|
|
1136
|
+
throw new RangeError(`Unix time ${instant} needs whole seconds and unit 'second'`);
|
|
1137
|
+
}
|
|
1138
|
+
return instant;
|
|
1139
|
+
}
|
|
1140
|
+
if (typeof instant === 'string' && instant.length > 10) {
|
|
1141
|
+
const match = DATE_TIME_RE.exec(instant);
|
|
1142
|
+
if (unit !== 'second' || match === null) {
|
|
1143
|
+
throw new RangeError(`Invalid instant "${instant}" (YYYY-MM-DDTHH:MM:SSZ, unit 'second')`);
|
|
1144
|
+
}
|
|
1145
|
+
const hours = Number(match[2]);
|
|
1146
|
+
const minutes = Number(match[3]);
|
|
1147
|
+
const seconds = Number(match[4]);
|
|
1148
|
+
if (hours > 23 || minutes > 59 || seconds > 59) {
|
|
1149
|
+
throw new RangeError(`Invalid instant "${instant}"`);
|
|
1150
|
+
}
|
|
1151
|
+
return dayNumber(dateFields(match[1]!)) * 86_400 + hours * 3600 + minutes * 60 + seconds;
|
|
1152
|
+
}
|
|
1153
|
+
const days = dayNumber(dateFields(instant));
|
|
1154
|
+
return unit === 'day' ? days : days * 86_400;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
function spanOf(period: ProrationPeriod, unit: ProrationUnit): { start: number; end: number } {
|
|
1158
|
+
const start = instantCount(period.start, unit);
|
|
1159
|
+
const end = instantCount(period.end, unit);
|
|
1160
|
+
if (!(end > start)) {
|
|
1161
|
+
throw new RangeError('A period must end after it starts');
|
|
1162
|
+
}
|
|
1163
|
+
return { start, end };
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
/**
|
|
1167
|
+
* The part of `price` for the `used` part of a billing `period`: `price × used / period`, counted in
|
|
1168
|
+
* days (or seconds), exact and rounded once (to the currency's exponent with a mode, or to a
|
|
1169
|
+
* policy's scale; default 'halfExpand'). Periods are end-exclusive.
|
|
1170
|
+
*
|
|
1171
|
+
* prorate(money('30', 'EUR'), {
|
|
1172
|
+
* period: { start: '2026-03-01', end: '2026-04-01' },
|
|
1173
|
+
* used: { start: '2026-03-11', end: '2026-04-01' },
|
|
1174
|
+
* }, 'halfEven') // 20.32 EUR (21 of 31 days)
|
|
1175
|
+
*/
|
|
1176
|
+
export function prorate<C extends string>(
|
|
1177
|
+
price: Money<C>,
|
|
1178
|
+
options: ProrateOptions,
|
|
1179
|
+
rounding: RoundingInput = DEFAULT_ROUNDING,
|
|
1180
|
+
): Money<C> {
|
|
1181
|
+
const unit = options.unit ?? 'day';
|
|
1182
|
+
const period = spanOf(options.period, unit);
|
|
1183
|
+
const usedStart = instantCount(options.used.start, unit);
|
|
1184
|
+
const usedEnd = instantCount(options.used.end, unit);
|
|
1185
|
+
if (!(usedStart >= period.start && usedEnd <= period.end && usedEnd >= usedStart)) {
|
|
1186
|
+
throw new RangeError('The used part must lie inside the period');
|
|
1187
|
+
}
|
|
1188
|
+
const fraction = ratio(BigInt(usedEnd - usedStart), BigInt(period.end - period.start));
|
|
1189
|
+
return multiplyAndRound(price, fraction, rounding);
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
/**
|
|
1193
|
+
* Splits one period's price at change points (plan changes), in proportion to the days (or seconds)
|
|
1194
|
+
* in each piece, through `allocate`, so the pieces always sum exactly to the price.
|
|
1195
|
+
*
|
|
1196
|
+
* splitPeriod(money('30', 'EUR'), {
|
|
1197
|
+
* period: { start: '2026-03-01', end: '2026-04-01' },
|
|
1198
|
+
* at: ['2026-03-11', '2026-03-20'],
|
|
1199
|
+
* }) // [9.68 EUR, 8.71 EUR, 11.61 EUR]
|
|
1200
|
+
*/
|
|
1201
|
+
export function splitPeriod<C extends string>(
|
|
1202
|
+
price: Money<C>,
|
|
1203
|
+
options: SplitPeriodOptions,
|
|
1204
|
+
): Money<C>[] {
|
|
1205
|
+
const unit = options.unit ?? 'day';
|
|
1206
|
+
const period = spanOf(options.period, unit);
|
|
1207
|
+
const weights: number[] = [];
|
|
1208
|
+
let previous = period.start;
|
|
1209
|
+
for (const point of options.at) {
|
|
1210
|
+
const count = instantCount(point, unit);
|
|
1211
|
+
if (!(count > previous && count < period.end)) {
|
|
1212
|
+
throw new RangeError('Change points must increase and lie strictly inside the period');
|
|
1213
|
+
}
|
|
1214
|
+
weights.push(count - previous);
|
|
1215
|
+
previous = count;
|
|
1216
|
+
}
|
|
1217
|
+
weights.push(period.end - previous);
|
|
1218
|
+
return allocate(price, weights, { remainder: options.remainder ?? 'largest' });
|
|
1219
|
+
}
|