Skip to content

Commit

Permalink
♻️ installments: refactor split interface
Browse files Browse the repository at this point in the history
  • Loading branch information
cruzdanilo committed Mar 20, 2024
1 parent f4ec550 commit 15b3ddf
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 60 deletions.
29 changes: 0 additions & 29 deletions src/installments/fromAmounts.ts

This file was deleted.

34 changes: 20 additions & 14 deletions src/installments/split.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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) {
Expand Down
9 changes: 3 additions & 6 deletions src/interest-rate-model/fixedRate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
) {
Expand All @@ -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;
}

Expand Down
8 changes: 1 addition & 7 deletions src/interest-rate-model/fixedRates.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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);
});
}

Expand Down
16 changes: 12 additions & 4 deletions test/installments.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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", () => {
Expand All @@ -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);
Expand Down

0 comments on commit 15b3ddf

Please sign in to comment.