From 15b3ddf1400c0f8aedc99e3a83165c1861f1c99a Mon Sep 17 00:00:00 2001 From: danilo neves cruz Date: Wed, 20 Mar 2024 12:45:15 -0300 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20installments:=20refactor?= =?UTF-8?q?=20split=20interface?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/installments/fromAmounts.ts | 29 ----------------------- src/installments/split.ts | 34 ++++++++++++++++----------- src/interest-rate-model/fixedRate.ts | 9 +++---- src/interest-rate-model/fixedRates.ts | 8 +------ test/installments.test.ts | 16 +++++++++---- 5 files changed, 36 insertions(+), 60 deletions(-) delete mode 100644 src/installments/fromAmounts.ts diff --git a/src/installments/fromAmounts.ts b/src/installments/fromAmounts.ts deleted file mode 100644 index fa039e0..0000000 --- a/src/installments/fromAmounts.ts +++ /dev/null @@ -1,29 +0,0 @@ -import sqrt from "../fixed-point-math/sqrt.js"; -import WAD, { SQ_WAD, TWO_WAD } from "../fixed-point-math/WAD.js"; -import fixedRate, { INTERVAL, type IRMParameters } from "../interest-rate-model/fixedRate.js"; - -export default function fromAmounts( - amounts: readonly bigint[], - totalAssets: bigint, - firstMaturity: number, - maxPools: number, - uFixed: readonly bigint[], - uFloating: bigint, - uGlobal: bigint, - parameters: IRMParameters, - timestamp: number, -) { - const sqFNatPools = (BigInt(maxPools) * SQ_WAD) / parameters.fixedAllocation; - const fNatPools = sqrt(sqFNatPools * WAD); - const natPools = ((TWO_WAD - sqFNatPools) * SQ_WAD) / (fNatPools * (WAD - fNatPools)); - - let uGlobalAfter = uGlobal; - return uFixed.map((uFixedBefore, index) => { - const amount = amounts[index]!; // eslint-disable-line @typescript-eslint/no-non-null-assertion - const maturity = firstMaturity + index * INTERVAL; - const uFixedAfter = amount ? uFixedBefore + (amount * WAD - 1n) / totalAssets + 1n : uFixedBefore; - if (amount) uGlobalAfter += (amount * WAD - 1n) / totalAssets + 1n; - const rate = fixedRate(maturity, maxPools, uFixedAfter, uFloating, uGlobalAfter, parameters, timestamp, natPools); - return amount + (amount * rate * BigInt(maturity - timestamp)) / (WAD * 365n * 86_400n); - }); -} diff --git a/src/installments/split.ts b/src/installments/split.ts index c397b79..316c44e 100644 --- a/src/installments/split.ts +++ b/src/installments/split.ts @@ -1,14 +1,13 @@ import WAD from "../fixed-point-math/WAD.js"; import expWad from "../fixed-point-math/expWad.js"; import lnWad from "../fixed-point-math/lnWad.js"; -import type { IRMParameters } from "../interest-rate-model/fixedRate.js"; +import fixedRate, { INTERVAL, type IRMParameters } from "../interest-rate-model/fixedRate.js"; import abs from "../vector/abs.js"; import fill from "../vector/fill.js"; import mean from "../vector/mean.js"; import mulDivUp from "../vector/mulDivUp.js"; import sub from "../vector/sub.js"; import sum from "../vector/sum.js"; -import fromAmounts from "./fromAmounts.js"; export default function splitInstallments( totalAmount: bigint, @@ -32,28 +31,35 @@ export default function splitInstallments( uGlobalAfter < WAD ? (scaleFactor * expWad((power * lnWad(WAD - uGlobalAfter)) / WAD)) / WAD : 1n, 10n ** 15n, ); + let iterations = 0; + let rates: bigint[] = []; + let installments: bigint[] = []; let amounts = fill(uFixed.length, (totalAmount - 1n) / BigInt(uFixed.length) + 1n); let error = 0n; do { if (iterations++ >= maxIterations) throw new Error("MAX_ITERATIONS_EXCEEDED"); - const installments = fromAmounts( - amounts, - totalAssets, - firstMaturity, - maxPools, - uFixed, - uFloating, - uGlobal, - parameters, - timestamp, - ); + let uGlobalAccumulator = uGlobal; + rates = uFixed.map((uFixedBefore, index) => { + const amount = amounts[index]!; // eslint-disable-line @typescript-eslint/no-non-null-assertion + const maturity = firstMaturity + index * INTERVAL; + const uFixedAfter = amount ? uFixedBefore + (amount * WAD - 1n) / totalAssets + 1n : uFixedBefore; + if (amount) uGlobalAccumulator += (amount * WAD - 1n) / totalAssets + 1n; + return fixedRate(maturity, maxPools, uFixedAfter, uFloating, uGlobalAccumulator, parameters, timestamp); + }); + installments = rates.map((rate, index) => { + const amount = amounts[index]!; // eslint-disable-line @typescript-eslint/no-non-null-assertion + const maturity = firstMaturity + index * INTERVAL; + return amount + (amount * rate * BigInt(maturity - timestamp)) / (WAD * 365n * 86_400n); + }); + const diffs = sub(installments, mean(installments)); amounts = sub(amounts, mulDivUp(diffs, weight, WAD)); amounts = mulDivUp(amounts, totalAmount, sum(amounts)); error = mean(mulDivUp(abs(diffs), weight, WAD)); } while (error >= tolerance); - return amounts; + + return { amounts, installments, rates }; } function max(a: bigint, b: bigint) { diff --git a/src/interest-rate-model/fixedRate.ts b/src/interest-rate-model/fixedRate.ts index 4b9b6fd..a38e1a1 100644 --- a/src/interest-rate-model/fixedRate.ts +++ b/src/interest-rate-model/fixedRate.ts @@ -15,7 +15,6 @@ export default function fixedRate( uGlobal: bigint, parameters: IRMParameters, timestamp = Math.floor(Date.now() / 1000), - natPools?: bigint, base = baseRate(uFloating, uGlobal, parameters), z?: bigint, ) { @@ -27,11 +26,9 @@ export default function fixedRate( if (z === undefined) { const fixedFactor = (BigInt(maxPools) * uFixed * SQ_WAD) / (uGlobal * fixedAllocation); - if (natPools == undefined) { - const sqFNatPools = (BigInt(maxPools) * SQ_WAD) / fixedAllocation; - const fNatPools = sqrt(sqFNatPools * WAD); - natPools = ((TWO_WAD - sqFNatPools) * SQ_WAD) / (fNatPools * (WAD - fNatPools)); - } + const sqFNatPools = (BigInt(maxPools) * SQ_WAD) / fixedAllocation; + const fNatPools = sqrt(sqFNatPools * WAD); + const natPools = ((TWO_WAD - sqFNatPools) * SQ_WAD) / (fNatPools * (WAD - fNatPools)); z = (natPools * sqrt(fixedFactor * WAD)) / WAD + ((WAD - natPools) * fixedFactor) / WAD - WAD; } diff --git a/src/interest-rate-model/fixedRates.ts b/src/interest-rate-model/fixedRates.ts index 175f318..4d14e73 100644 --- a/src/interest-rate-model/fixedRates.ts +++ b/src/interest-rate-model/fixedRates.ts @@ -1,5 +1,3 @@ -import WAD, { SQ_WAD, TWO_WAD } from "../fixed-point-math/WAD.js"; -import sqrt from "../fixed-point-math/sqrt.js"; import type IRMParameters from "./Parameters.js"; import baseRate from "./baseRate.js"; import fixedRate, { INTERVAL } from "./fixedRate.js"; @@ -14,13 +12,9 @@ export default function fixedRates( timestamp = Math.floor(Date.now() / 1000), ) { const base = baseRate(uFloating, uGlobal, parameters); - const sqFNatPools = (BigInt(maxPools) * SQ_WAD) / parameters.fixedAllocation; - const fNatPools = sqrt(sqFNatPools * WAD); - const natPools = ((TWO_WAD - sqFNatPools) * SQ_WAD) / (fNatPools * (WAD - fNatPools)); - return uFixed.map((uFixedBefore, index) => { const maturity = firstMaturity + index * INTERVAL; - fixedRate(maturity, maxPools, uFixedBefore, uFloating, uGlobal, parameters, timestamp, natPools, base); + fixedRate(maturity, maxPools, uFixedBefore, uFloating, uGlobal, parameters, timestamp, base); }); } diff --git a/test/installments.test.ts b/test/installments.test.ts index 2ea347c..0b6ab38 100644 --- a/test/installments.test.ts +++ b/test/installments.test.ts @@ -8,7 +8,6 @@ import mean from "../src/vector/mean"; import mulDivDown from "../src/vector/mulDivDown"; import sum from "../src/vector/sum"; -import fromAmounts from "../src/installments/fromAmounts"; import splitInstallments from "../src/installments/split"; describe("installments", () => { @@ -22,15 +21,24 @@ describe("installments", () => { (totalAmount, uFixed, uFloating, uGlobal) => { const maxPools = 13; const timestamp = 0; + const firstMaturity = INTERVAL; const totalAssets = 1_000_000n * WAD; uFloating = (uFloating * uGlobal) / WAD; totalAmount = (totalAmount * totalAssets * (WAD - uGlobal)) / SQ_WAD; if (sum(uFixed) > 0n) uFixed = mulDivDown(uFixed, uGlobal - uFloating, sum(uFixed)); - const common = [totalAssets, INTERVAL, maxPools, uFixed, uFloating, uGlobal, parameters, timestamp] as const; - const amounts = splitInstallments(totalAmount, ...common); - const installments = fromAmounts(amounts, ...common); + const { amounts, installments } = splitInstallments( + totalAmount, + totalAssets, + firstMaturity, + maxPools, + uFixed, + uFloating, + uGlobal, + parameters, + timestamp, + ); expect(amounts).toHaveLength(uFixed.length); expect(sum(amounts)).toBeGreaterThanOrEqual(totalAmount);