fundamental-js 1.0.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/dist/index.cjs +360 -0
- package/dist/index.d.ts +121 -0
- package/dist/index.mjs +339 -0
- package/package.json +40 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
absoluteReturn: () => absoluteReturn,
|
|
24
|
+
amortizationSchedule: () => amortizationSchedule,
|
|
25
|
+
annualizedReturn: () => annualizedReturn,
|
|
26
|
+
cagr: () => cagr,
|
|
27
|
+
emi: () => emi,
|
|
28
|
+
fv: () => fv,
|
|
29
|
+
inflationAdjustedValue: () => inflationAdjustedValue,
|
|
30
|
+
irr: () => irr,
|
|
31
|
+
lumpsumFutureValue: () => lumpsumFutureValue,
|
|
32
|
+
maxDrawdown: () => maxDrawdown,
|
|
33
|
+
npv: () => npv,
|
|
34
|
+
prepaymentImpact: () => prepaymentImpact,
|
|
35
|
+
pv: () => pv,
|
|
36
|
+
realReturn: () => realReturn,
|
|
37
|
+
rebalance: () => rebalance,
|
|
38
|
+
requiredLumpsumForGoal: () => requiredLumpsumForGoal,
|
|
39
|
+
requiredMonthlyInvestmentForGoal: () => requiredMonthlyInvestmentForGoal,
|
|
40
|
+
safeNum: () => safeNum,
|
|
41
|
+
sharpe: () => sharpe,
|
|
42
|
+
sipFutureValue: () => sipFutureValue,
|
|
43
|
+
sortino: () => sortino,
|
|
44
|
+
swpPlan: () => swpPlan,
|
|
45
|
+
volatility: () => volatility,
|
|
46
|
+
weightedReturn: () => weightedReturn
|
|
47
|
+
});
|
|
48
|
+
module.exports = __toCommonJS(index_exports);
|
|
49
|
+
function safeNum(v, fallback = 0) {
|
|
50
|
+
if (v === null || v === void 0 || isNaN(v) || !isFinite(v)) return fallback;
|
|
51
|
+
return v;
|
|
52
|
+
}
|
|
53
|
+
function emi(principal, annualRate, months) {
|
|
54
|
+
principal = safeNum(principal, 0);
|
|
55
|
+
months = safeNum(months, 1);
|
|
56
|
+
annualRate = safeNum(annualRate, 0);
|
|
57
|
+
if (principal <= 0 || months <= 0) return { emi: 0, totalPayment: 0, totalInterest: 0 };
|
|
58
|
+
if (annualRate === 0) {
|
|
59
|
+
const e2 = principal / months;
|
|
60
|
+
return { emi: e2, totalPayment: principal, totalInterest: 0 };
|
|
61
|
+
}
|
|
62
|
+
const r = annualRate / 12;
|
|
63
|
+
const factor = Math.pow(1 + r, months);
|
|
64
|
+
const e = principal * r * factor / (factor - 1);
|
|
65
|
+
const totalPayment = e * months;
|
|
66
|
+
return { emi: safeNum(e), totalPayment: safeNum(totalPayment), totalInterest: safeNum(totalPayment - principal) };
|
|
67
|
+
}
|
|
68
|
+
function amortizationSchedule(params) {
|
|
69
|
+
let { principal, annualRate, months, extraPaymentMonthly = 0, extraPayments = [] } = params;
|
|
70
|
+
principal = safeNum(principal, 0);
|
|
71
|
+
annualRate = safeNum(annualRate, 0);
|
|
72
|
+
months = safeNum(months, 1);
|
|
73
|
+
if (principal <= 0 || months <= 0) return { emi: 0, schedule: [], totalInterest: 0, totalPaid: 0, payoffMonth: 0 };
|
|
74
|
+
const r = annualRate / 12;
|
|
75
|
+
const baseEmi = annualRate === 0 ? principal / months : principal * r * Math.pow(1 + r, months) / (Math.pow(1 + r, months) - 1);
|
|
76
|
+
const extraMap = new Map(extraPayments.map((ep) => [ep.month, ep.amount]));
|
|
77
|
+
let balance = principal;
|
|
78
|
+
const schedule = [];
|
|
79
|
+
let totalInterest = 0;
|
|
80
|
+
let totalPaid = 0;
|
|
81
|
+
for (let m = 1; m <= months && balance > 0.01; m++) {
|
|
82
|
+
const interest = balance * r;
|
|
83
|
+
let principalPortion = baseEmi - interest;
|
|
84
|
+
const extra = extraPaymentMonthly + (extraMap.get(m) || 0);
|
|
85
|
+
if (principalPortion + extra >= balance) {
|
|
86
|
+
principalPortion = balance;
|
|
87
|
+
const payment2 = principalPortion + interest;
|
|
88
|
+
totalInterest += interest;
|
|
89
|
+
totalPaid += payment2;
|
|
90
|
+
schedule.push({
|
|
91
|
+
month: m,
|
|
92
|
+
payment: payment2,
|
|
93
|
+
principalPaid: principalPortion,
|
|
94
|
+
interestPaid: interest,
|
|
95
|
+
extraPayment: 0,
|
|
96
|
+
balance: 0
|
|
97
|
+
});
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
balance -= principalPortion + extra;
|
|
101
|
+
if (balance < 0) balance = 0;
|
|
102
|
+
const payment = baseEmi + extra;
|
|
103
|
+
totalInterest += interest;
|
|
104
|
+
totalPaid += payment;
|
|
105
|
+
schedule.push({
|
|
106
|
+
month: m,
|
|
107
|
+
payment,
|
|
108
|
+
principalPaid: principalPortion + extra,
|
|
109
|
+
interestPaid: interest,
|
|
110
|
+
extraPayment: extra,
|
|
111
|
+
balance
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
emi: baseEmi,
|
|
116
|
+
schedule,
|
|
117
|
+
totalInterest,
|
|
118
|
+
totalPaid,
|
|
119
|
+
payoffMonth: schedule.length
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function prepaymentImpact(params) {
|
|
123
|
+
const { principal, annualRate, months, prepayments } = params;
|
|
124
|
+
const original = emi(principal, annualRate, months);
|
|
125
|
+
const withPrepay = amortizationSchedule({
|
|
126
|
+
principal,
|
|
127
|
+
annualRate,
|
|
128
|
+
months,
|
|
129
|
+
extraPayments: prepayments
|
|
130
|
+
});
|
|
131
|
+
return {
|
|
132
|
+
original: { emi: original.emi, totalInterest: original.totalInterest, totalPaid: original.totalPayment },
|
|
133
|
+
new: {
|
|
134
|
+
emi: withPrepay.emi,
|
|
135
|
+
totalInterest: withPrepay.totalInterest,
|
|
136
|
+
totalPaid: withPrepay.totalPaid,
|
|
137
|
+
payoffMonth: withPrepay.payoffMonth
|
|
138
|
+
},
|
|
139
|
+
savings: {
|
|
140
|
+
interestSaved: original.totalInterest - withPrepay.totalInterest,
|
|
141
|
+
monthsSaved: months - withPrepay.payoffMonth
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function sipFutureValue(params) {
|
|
146
|
+
const { monthlyInvestment, annualRate, months, stepUpPercentAnnual = 0, investmentAt = "end" } = params;
|
|
147
|
+
const r = annualRate / 12;
|
|
148
|
+
let totalInvested = 0;
|
|
149
|
+
let futureValue = 0;
|
|
150
|
+
let currentMonthly = monthlyInvestment;
|
|
151
|
+
for (let m = 1; m <= months; m++) {
|
|
152
|
+
if (stepUpPercentAnnual > 0 && m > 1 && (m - 1) % 12 === 0) {
|
|
153
|
+
currentMonthly *= 1 + stepUpPercentAnnual / 100;
|
|
154
|
+
}
|
|
155
|
+
totalInvested += currentMonthly;
|
|
156
|
+
if (investmentAt === "begin") {
|
|
157
|
+
futureValue = (futureValue + currentMonthly) * (1 + r);
|
|
158
|
+
} else {
|
|
159
|
+
futureValue = futureValue * (1 + r) + currentMonthly;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
futureValue,
|
|
164
|
+
totalInvested,
|
|
165
|
+
estimatedGains: futureValue - totalInvested
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
function lumpsumFutureValue(principal, annualRate, years) {
|
|
169
|
+
const futureValue = principal * Math.pow(1 + annualRate, years);
|
|
170
|
+
return { futureValue, gains: futureValue - principal };
|
|
171
|
+
}
|
|
172
|
+
function swpPlan(params) {
|
|
173
|
+
const { initialCorpus, monthlyWithdrawal, annualRate, months, inflationRateAnnual = 0, withdrawalAt = "end" } = params;
|
|
174
|
+
const r = annualRate / 12;
|
|
175
|
+
let balance = initialCorpus;
|
|
176
|
+
let totalWithdrawn = 0;
|
|
177
|
+
let currentWithdrawal = monthlyWithdrawal;
|
|
178
|
+
const schedule = [];
|
|
179
|
+
for (let m = 1; m <= months; m++) {
|
|
180
|
+
if (inflationRateAnnual > 0 && m > 1 && (m - 1) % 12 === 0) {
|
|
181
|
+
currentWithdrawal *= 1 + inflationRateAnnual / 100;
|
|
182
|
+
}
|
|
183
|
+
let interest;
|
|
184
|
+
let withdrawal = currentWithdrawal;
|
|
185
|
+
if (withdrawalAt === "begin") {
|
|
186
|
+
withdrawal = Math.min(withdrawal, balance);
|
|
187
|
+
balance -= withdrawal;
|
|
188
|
+
interest = balance * r;
|
|
189
|
+
balance += interest;
|
|
190
|
+
} else {
|
|
191
|
+
interest = balance * r;
|
|
192
|
+
balance += interest;
|
|
193
|
+
withdrawal = Math.min(withdrawal, balance);
|
|
194
|
+
balance -= withdrawal;
|
|
195
|
+
}
|
|
196
|
+
totalWithdrawn += withdrawal;
|
|
197
|
+
schedule.push({
|
|
198
|
+
month: m,
|
|
199
|
+
withdrawal,
|
|
200
|
+
interestEarned: interest,
|
|
201
|
+
endingBalance: Math.max(0, balance)
|
|
202
|
+
});
|
|
203
|
+
if (balance <= 0) break;
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
endingCorpus: Math.max(0, balance),
|
|
207
|
+
totalWithdrawn,
|
|
208
|
+
schedule
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function inflationAdjustedValue(value, inflationRateAnnual, years) {
|
|
212
|
+
return value / Math.pow(1 + inflationRateAnnual, years);
|
|
213
|
+
}
|
|
214
|
+
function realReturn(nominalRateAnnual, inflationRateAnnual) {
|
|
215
|
+
return (1 + nominalRateAnnual) / (1 + inflationRateAnnual) - 1;
|
|
216
|
+
}
|
|
217
|
+
function requiredMonthlyInvestmentForGoal(params) {
|
|
218
|
+
const { goalAmountFuture, annualRate, months, stepUpPercentAnnual = 0, investmentAt = "end" } = params;
|
|
219
|
+
let lo = 0;
|
|
220
|
+
let hi = goalAmountFuture;
|
|
221
|
+
for (let i = 0; i < 100; i++) {
|
|
222
|
+
const mid = (lo + hi) / 2;
|
|
223
|
+
const result = sipFutureValue({
|
|
224
|
+
monthlyInvestment: mid,
|
|
225
|
+
annualRate,
|
|
226
|
+
months,
|
|
227
|
+
stepUpPercentAnnual,
|
|
228
|
+
investmentAt
|
|
229
|
+
});
|
|
230
|
+
if (result.futureValue < goalAmountFuture) {
|
|
231
|
+
lo = mid;
|
|
232
|
+
} else {
|
|
233
|
+
hi = mid;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
requiredMonthlyInvestment: (lo + hi) / 2,
|
|
238
|
+
assumptions: `Rate: ${(annualRate * 100).toFixed(1)}% p.a., Duration: ${months} months${stepUpPercentAnnual ? `, Step-up: ${stepUpPercentAnnual}% annually` : ""}`
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
function requiredLumpsumForGoal(goalAmountFuture, annualRate, years) {
|
|
242
|
+
return goalAmountFuture / Math.pow(1 + annualRate, years);
|
|
243
|
+
}
|
|
244
|
+
function cagr(beginValue, endValue, years) {
|
|
245
|
+
beginValue = safeNum(beginValue, 0);
|
|
246
|
+
endValue = safeNum(endValue, 0);
|
|
247
|
+
years = safeNum(years, 1);
|
|
248
|
+
if (beginValue <= 0 || years <= 0) return 0;
|
|
249
|
+
return Math.pow(endValue / beginValue, 1 / years) - 1;
|
|
250
|
+
}
|
|
251
|
+
function absoluteReturn(beginValue, endValue) {
|
|
252
|
+
beginValue = safeNum(beginValue, 0);
|
|
253
|
+
endValue = safeNum(endValue, 0);
|
|
254
|
+
if (beginValue <= 0) return 0;
|
|
255
|
+
return (endValue - beginValue) / beginValue;
|
|
256
|
+
}
|
|
257
|
+
function annualizedReturn(beginValue, endValue, days) {
|
|
258
|
+
beginValue = safeNum(beginValue, 0);
|
|
259
|
+
endValue = safeNum(endValue, 0);
|
|
260
|
+
days = safeNum(days, 1);
|
|
261
|
+
if (beginValue <= 0 || days <= 0) return 0;
|
|
262
|
+
return Math.pow(endValue / beginValue, 365 / days) - 1;
|
|
263
|
+
}
|
|
264
|
+
function pv(rate, nper, pmt, fv2 = 0, type = 0) {
|
|
265
|
+
if (rate === 0) return -(pmt * nper + fv2);
|
|
266
|
+
const factor = Math.pow(1 + rate, nper);
|
|
267
|
+
return -(pmt * (1 + rate * type) * ((factor - 1) / rate) + fv2) / factor;
|
|
268
|
+
}
|
|
269
|
+
function fv(rate, nper, pmt, pvVal = 0, type = 0) {
|
|
270
|
+
if (rate === 0) return -(pvVal + pmt * nper);
|
|
271
|
+
const factor = Math.pow(1 + rate, nper);
|
|
272
|
+
return -(pvVal * factor + pmt * (1 + rate * type) * ((factor - 1) / rate));
|
|
273
|
+
}
|
|
274
|
+
function volatility(returns, periodsPerYear = 252) {
|
|
275
|
+
if (returns.length < 2) return 0;
|
|
276
|
+
const mean = returns.reduce((s, r) => s + r, 0) / returns.length;
|
|
277
|
+
const variance = returns.reduce((s, r) => s + (r - mean) ** 2, 0) / (returns.length - 1);
|
|
278
|
+
return Math.sqrt(variance) * Math.sqrt(periodsPerYear);
|
|
279
|
+
}
|
|
280
|
+
function sharpe(returns, riskFreeRateAnnual = 0, periodsPerYear = 252) {
|
|
281
|
+
const mean = returns.reduce((s, r) => s + r, 0) / returns.length;
|
|
282
|
+
const annualReturn = mean * periodsPerYear;
|
|
283
|
+
const vol = volatility(returns, periodsPerYear);
|
|
284
|
+
if (vol === 0) return 0;
|
|
285
|
+
return (annualReturn - riskFreeRateAnnual) / vol;
|
|
286
|
+
}
|
|
287
|
+
function sortino(returns, riskFreeRateAnnual = 0, periodsPerYear = 252) {
|
|
288
|
+
const mean = returns.reduce((s, r) => s + r, 0) / returns.length;
|
|
289
|
+
const rfPerPeriod = riskFreeRateAnnual / periodsPerYear;
|
|
290
|
+
const downside = returns.filter((r) => r < rfPerPeriod);
|
|
291
|
+
if (downside.length === 0) return Infinity;
|
|
292
|
+
const downsideVariance = downside.reduce((s, r) => s + (r - rfPerPeriod) ** 2, 0) / downside.length;
|
|
293
|
+
const downsideDev = Math.sqrt(downsideVariance) * Math.sqrt(periodsPerYear);
|
|
294
|
+
const annualReturn = mean * periodsPerYear;
|
|
295
|
+
if (downsideDev === 0) return 0;
|
|
296
|
+
return (annualReturn - riskFreeRateAnnual) / downsideDev;
|
|
297
|
+
}
|
|
298
|
+
function maxDrawdown(values) {
|
|
299
|
+
if (values.length < 2) return { maxDrawdown: 0, peakIndex: 0, troughIndex: 0 };
|
|
300
|
+
let peak = values[0];
|
|
301
|
+
let maxDd = 0;
|
|
302
|
+
let peakIdx = 0;
|
|
303
|
+
let troughIdx = 0;
|
|
304
|
+
let currentPeakIdx = 0;
|
|
305
|
+
for (let i = 1; i < values.length; i++) {
|
|
306
|
+
if (values[i] > peak) {
|
|
307
|
+
peak = values[i];
|
|
308
|
+
currentPeakIdx = i;
|
|
309
|
+
}
|
|
310
|
+
const dd = (peak - values[i]) / peak;
|
|
311
|
+
if (dd > maxDd) {
|
|
312
|
+
maxDd = dd;
|
|
313
|
+
peakIdx = currentPeakIdx;
|
|
314
|
+
troughIdx = i;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return { maxDrawdown: maxDd, peakIndex: peakIdx, troughIndex: troughIdx };
|
|
318
|
+
}
|
|
319
|
+
function npv(rate, cashflows) {
|
|
320
|
+
return cashflows.reduce((sum, cf, i) => sum + cf / Math.pow(1 + rate, i), 0);
|
|
321
|
+
}
|
|
322
|
+
function irr(cashflows, guess = 0.1) {
|
|
323
|
+
let rate = guess;
|
|
324
|
+
const maxIter = 1e3;
|
|
325
|
+
const tol = 1e-10;
|
|
326
|
+
for (let i = 0; i < maxIter; i++) {
|
|
327
|
+
let f = 0;
|
|
328
|
+
let df = 0;
|
|
329
|
+
for (let j = 0; j < cashflows.length; j++) {
|
|
330
|
+
const denom = Math.pow(1 + rate, j);
|
|
331
|
+
f += cashflows[j] / denom;
|
|
332
|
+
if (j > 0) df -= j * cashflows[j] / Math.pow(1 + rate, j + 1);
|
|
333
|
+
}
|
|
334
|
+
if (Math.abs(f) < tol) return rate;
|
|
335
|
+
if (Math.abs(df) < tol) break;
|
|
336
|
+
rate -= f / df;
|
|
337
|
+
}
|
|
338
|
+
let lo = -0.99;
|
|
339
|
+
let hi = 10;
|
|
340
|
+
for (let i = 0; i < 1e3; i++) {
|
|
341
|
+
const mid = (lo + hi) / 2;
|
|
342
|
+
const f = cashflows.reduce((s, cf, j) => s + cf / Math.pow(1 + mid, j), 0);
|
|
343
|
+
if (Math.abs(f) < tol) return mid;
|
|
344
|
+
const fLo = cashflows.reduce((s, cf, j) => s + cf / Math.pow(1 + lo, j), 0);
|
|
345
|
+
if (f * fLo > 0) lo = mid;
|
|
346
|
+
else hi = mid;
|
|
347
|
+
}
|
|
348
|
+
return (lo + hi) / 2;
|
|
349
|
+
}
|
|
350
|
+
function weightedReturn(returns, weights) {
|
|
351
|
+
if (returns.length !== weights.length || returns.length === 0) return 0;
|
|
352
|
+
return returns.reduce((s, r, i) => s + r * weights[i], 0);
|
|
353
|
+
}
|
|
354
|
+
function rebalance(targetWeights, currentValues) {
|
|
355
|
+
if (targetWeights.length !== currentValues.length || targetWeights.length === 0) return { trades: [], newValues: [], total: 0 };
|
|
356
|
+
const total = currentValues.reduce((s, v) => s + v, 0);
|
|
357
|
+
const newValues = targetWeights.map((w) => w * total);
|
|
358
|
+
const trades = newValues.map((nv, i) => nv - currentValues[i]);
|
|
359
|
+
return { trades, newValues, total };
|
|
360
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
export declare function safeNum(v: number, fallback?: number): number;
|
|
2
|
+
export declare function emi(principal: number, annualRate: number, months: number): {
|
|
3
|
+
emi: number;
|
|
4
|
+
totalPayment: number;
|
|
5
|
+
totalInterest: number;
|
|
6
|
+
};
|
|
7
|
+
export declare function amortizationSchedule(params: {
|
|
8
|
+
principal: number;
|
|
9
|
+
annualRate: number;
|
|
10
|
+
months: number;
|
|
11
|
+
extraPaymentMonthly?: number;
|
|
12
|
+
extraPayments?: {
|
|
13
|
+
month: number;
|
|
14
|
+
amount: number;
|
|
15
|
+
}[];
|
|
16
|
+
}): {
|
|
17
|
+
emi: number;
|
|
18
|
+
schedule: {
|
|
19
|
+
month: number;
|
|
20
|
+
payment: number;
|
|
21
|
+
principalPaid: number;
|
|
22
|
+
interestPaid: number;
|
|
23
|
+
extraPayment: number;
|
|
24
|
+
balance: number;
|
|
25
|
+
}[];
|
|
26
|
+
totalInterest: number;
|
|
27
|
+
totalPaid: number;
|
|
28
|
+
payoffMonth: number;
|
|
29
|
+
};
|
|
30
|
+
export declare function prepaymentImpact(params: {
|
|
31
|
+
principal: number;
|
|
32
|
+
annualRate: number;
|
|
33
|
+
months: number;
|
|
34
|
+
prepayments: {
|
|
35
|
+
month: number;
|
|
36
|
+
amount: number;
|
|
37
|
+
}[];
|
|
38
|
+
mode: "reduceTenure" | "reduceEmi";
|
|
39
|
+
}): {
|
|
40
|
+
original: {
|
|
41
|
+
emi: number;
|
|
42
|
+
totalInterest: number;
|
|
43
|
+
totalPaid: number;
|
|
44
|
+
};
|
|
45
|
+
new: {
|
|
46
|
+
emi: number;
|
|
47
|
+
totalInterest: number;
|
|
48
|
+
totalPaid: number;
|
|
49
|
+
payoffMonth: number;
|
|
50
|
+
};
|
|
51
|
+
savings: {
|
|
52
|
+
interestSaved: number;
|
|
53
|
+
monthsSaved: number;
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
export declare function sipFutureValue(params: {
|
|
57
|
+
monthlyInvestment: number;
|
|
58
|
+
annualRate: number;
|
|
59
|
+
months: number;
|
|
60
|
+
stepUpPercentAnnual?: number;
|
|
61
|
+
investmentAt?: "begin" | "end";
|
|
62
|
+
}): {
|
|
63
|
+
futureValue: number;
|
|
64
|
+
totalInvested: number;
|
|
65
|
+
estimatedGains: number;
|
|
66
|
+
};
|
|
67
|
+
export declare function lumpsumFutureValue(principal: number, annualRate: number, years: number): {
|
|
68
|
+
futureValue: number;
|
|
69
|
+
gains: number;
|
|
70
|
+
};
|
|
71
|
+
export declare function swpPlan(params: {
|
|
72
|
+
initialCorpus: number;
|
|
73
|
+
monthlyWithdrawal: number;
|
|
74
|
+
annualRate: number;
|
|
75
|
+
months: number;
|
|
76
|
+
inflationRateAnnual?: number;
|
|
77
|
+
withdrawalAt?: "begin" | "end";
|
|
78
|
+
}): {
|
|
79
|
+
endingCorpus: number;
|
|
80
|
+
totalWithdrawn: number;
|
|
81
|
+
schedule: {
|
|
82
|
+
month: number;
|
|
83
|
+
withdrawal: number;
|
|
84
|
+
interestEarned: number;
|
|
85
|
+
endingBalance: number;
|
|
86
|
+
}[];
|
|
87
|
+
};
|
|
88
|
+
export declare function inflationAdjustedValue(value: number, inflationRateAnnual: number, years: number): number;
|
|
89
|
+
export declare function realReturn(nominalRateAnnual: number, inflationRateAnnual: number): number;
|
|
90
|
+
export declare function requiredMonthlyInvestmentForGoal(params: {
|
|
91
|
+
goalAmountFuture: number;
|
|
92
|
+
annualRate: number;
|
|
93
|
+
months: number;
|
|
94
|
+
stepUpPercentAnnual?: number;
|
|
95
|
+
investmentAt?: "begin" | "end";
|
|
96
|
+
}): {
|
|
97
|
+
requiredMonthlyInvestment: number;
|
|
98
|
+
assumptions: string;
|
|
99
|
+
};
|
|
100
|
+
export declare function requiredLumpsumForGoal(goalAmountFuture: number, annualRate: number, years: number): number;
|
|
101
|
+
export declare function cagr(beginValue: number, endValue: number, years: number): number;
|
|
102
|
+
export declare function absoluteReturn(beginValue: number, endValue: number): number;
|
|
103
|
+
export declare function annualizedReturn(beginValue: number, endValue: number, days: number): number;
|
|
104
|
+
export declare function pv(rate: number, nper: number, pmt: number, fv?: number, type?: number): number;
|
|
105
|
+
export declare function fv(rate: number, nper: number, pmt: number, pvVal?: number, type?: number): number;
|
|
106
|
+
export declare function volatility(returns: number[], periodsPerYear?: number): number;
|
|
107
|
+
export declare function sharpe(returns: number[], riskFreeRateAnnual?: number, periodsPerYear?: number): number;
|
|
108
|
+
export declare function sortino(returns: number[], riskFreeRateAnnual?: number, periodsPerYear?: number): number;
|
|
109
|
+
export declare function maxDrawdown(values: number[]): {
|
|
110
|
+
maxDrawdown: number;
|
|
111
|
+
peakIndex: number;
|
|
112
|
+
troughIndex: number;
|
|
113
|
+
};
|
|
114
|
+
export declare function npv(rate: number, cashflows: number[]): number;
|
|
115
|
+
export declare function irr(cashflows: number[], guess?: number): number;
|
|
116
|
+
export declare function weightedReturn(returns: number[], weights: number[]): number;
|
|
117
|
+
export declare function rebalance(targetWeights: number[], currentValues: number[]): {
|
|
118
|
+
trades: number[];
|
|
119
|
+
newValues: number[];
|
|
120
|
+
total: number;
|
|
121
|
+
};
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
function safeNum(v, fallback = 0) {
|
|
3
|
+
if (v === null || v === void 0 || isNaN(v) || !isFinite(v)) return fallback;
|
|
4
|
+
return v;
|
|
5
|
+
}
|
|
6
|
+
function emi(principal, annualRate, months) {
|
|
7
|
+
principal = safeNum(principal, 0);
|
|
8
|
+
months = safeNum(months, 1);
|
|
9
|
+
annualRate = safeNum(annualRate, 0);
|
|
10
|
+
if (principal <= 0 || months <= 0) return { emi: 0, totalPayment: 0, totalInterest: 0 };
|
|
11
|
+
if (annualRate === 0) {
|
|
12
|
+
const e2 = principal / months;
|
|
13
|
+
return { emi: e2, totalPayment: principal, totalInterest: 0 };
|
|
14
|
+
}
|
|
15
|
+
const r = annualRate / 12;
|
|
16
|
+
const factor = Math.pow(1 + r, months);
|
|
17
|
+
const e = principal * r * factor / (factor - 1);
|
|
18
|
+
const totalPayment = e * months;
|
|
19
|
+
return { emi: safeNum(e), totalPayment: safeNum(totalPayment), totalInterest: safeNum(totalPayment - principal) };
|
|
20
|
+
}
|
|
21
|
+
function amortizationSchedule(params) {
|
|
22
|
+
let { principal, annualRate, months, extraPaymentMonthly = 0, extraPayments = [] } = params;
|
|
23
|
+
principal = safeNum(principal, 0);
|
|
24
|
+
annualRate = safeNum(annualRate, 0);
|
|
25
|
+
months = safeNum(months, 1);
|
|
26
|
+
if (principal <= 0 || months <= 0) return { emi: 0, schedule: [], totalInterest: 0, totalPaid: 0, payoffMonth: 0 };
|
|
27
|
+
const r = annualRate / 12;
|
|
28
|
+
const baseEmi = annualRate === 0 ? principal / months : principal * r * Math.pow(1 + r, months) / (Math.pow(1 + r, months) - 1);
|
|
29
|
+
const extraMap = new Map(extraPayments.map((ep) => [ep.month, ep.amount]));
|
|
30
|
+
let balance = principal;
|
|
31
|
+
const schedule = [];
|
|
32
|
+
let totalInterest = 0;
|
|
33
|
+
let totalPaid = 0;
|
|
34
|
+
for (let m = 1; m <= months && balance > 0.01; m++) {
|
|
35
|
+
const interest = balance * r;
|
|
36
|
+
let principalPortion = baseEmi - interest;
|
|
37
|
+
const extra = extraPaymentMonthly + (extraMap.get(m) || 0);
|
|
38
|
+
if (principalPortion + extra >= balance) {
|
|
39
|
+
principalPortion = balance;
|
|
40
|
+
const payment2 = principalPortion + interest;
|
|
41
|
+
totalInterest += interest;
|
|
42
|
+
totalPaid += payment2;
|
|
43
|
+
schedule.push({
|
|
44
|
+
month: m,
|
|
45
|
+
payment: payment2,
|
|
46
|
+
principalPaid: principalPortion,
|
|
47
|
+
interestPaid: interest,
|
|
48
|
+
extraPayment: 0,
|
|
49
|
+
balance: 0
|
|
50
|
+
});
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
balance -= principalPortion + extra;
|
|
54
|
+
if (balance < 0) balance = 0;
|
|
55
|
+
const payment = baseEmi + extra;
|
|
56
|
+
totalInterest += interest;
|
|
57
|
+
totalPaid += payment;
|
|
58
|
+
schedule.push({
|
|
59
|
+
month: m,
|
|
60
|
+
payment,
|
|
61
|
+
principalPaid: principalPortion + extra,
|
|
62
|
+
interestPaid: interest,
|
|
63
|
+
extraPayment: extra,
|
|
64
|
+
balance
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
emi: baseEmi,
|
|
69
|
+
schedule,
|
|
70
|
+
totalInterest,
|
|
71
|
+
totalPaid,
|
|
72
|
+
payoffMonth: schedule.length
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function prepaymentImpact(params) {
|
|
76
|
+
const { principal, annualRate, months, prepayments } = params;
|
|
77
|
+
const original = emi(principal, annualRate, months);
|
|
78
|
+
const withPrepay = amortizationSchedule({
|
|
79
|
+
principal,
|
|
80
|
+
annualRate,
|
|
81
|
+
months,
|
|
82
|
+
extraPayments: prepayments
|
|
83
|
+
});
|
|
84
|
+
return {
|
|
85
|
+
original: { emi: original.emi, totalInterest: original.totalInterest, totalPaid: original.totalPayment },
|
|
86
|
+
new: {
|
|
87
|
+
emi: withPrepay.emi,
|
|
88
|
+
totalInterest: withPrepay.totalInterest,
|
|
89
|
+
totalPaid: withPrepay.totalPaid,
|
|
90
|
+
payoffMonth: withPrepay.payoffMonth
|
|
91
|
+
},
|
|
92
|
+
savings: {
|
|
93
|
+
interestSaved: original.totalInterest - withPrepay.totalInterest,
|
|
94
|
+
monthsSaved: months - withPrepay.payoffMonth
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function sipFutureValue(params) {
|
|
99
|
+
const { monthlyInvestment, annualRate, months, stepUpPercentAnnual = 0, investmentAt = "end" } = params;
|
|
100
|
+
const r = annualRate / 12;
|
|
101
|
+
let totalInvested = 0;
|
|
102
|
+
let futureValue = 0;
|
|
103
|
+
let currentMonthly = monthlyInvestment;
|
|
104
|
+
for (let m = 1; m <= months; m++) {
|
|
105
|
+
if (stepUpPercentAnnual > 0 && m > 1 && (m - 1) % 12 === 0) {
|
|
106
|
+
currentMonthly *= 1 + stepUpPercentAnnual / 100;
|
|
107
|
+
}
|
|
108
|
+
totalInvested += currentMonthly;
|
|
109
|
+
if (investmentAt === "begin") {
|
|
110
|
+
futureValue = (futureValue + currentMonthly) * (1 + r);
|
|
111
|
+
} else {
|
|
112
|
+
futureValue = futureValue * (1 + r) + currentMonthly;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
futureValue,
|
|
117
|
+
totalInvested,
|
|
118
|
+
estimatedGains: futureValue - totalInvested
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function lumpsumFutureValue(principal, annualRate, years) {
|
|
122
|
+
const futureValue = principal * Math.pow(1 + annualRate, years);
|
|
123
|
+
return { futureValue, gains: futureValue - principal };
|
|
124
|
+
}
|
|
125
|
+
function swpPlan(params) {
|
|
126
|
+
const { initialCorpus, monthlyWithdrawal, annualRate, months, inflationRateAnnual = 0, withdrawalAt = "end" } = params;
|
|
127
|
+
const r = annualRate / 12;
|
|
128
|
+
let balance = initialCorpus;
|
|
129
|
+
let totalWithdrawn = 0;
|
|
130
|
+
let currentWithdrawal = monthlyWithdrawal;
|
|
131
|
+
const schedule = [];
|
|
132
|
+
for (let m = 1; m <= months; m++) {
|
|
133
|
+
if (inflationRateAnnual > 0 && m > 1 && (m - 1) % 12 === 0) {
|
|
134
|
+
currentWithdrawal *= 1 + inflationRateAnnual / 100;
|
|
135
|
+
}
|
|
136
|
+
let interest;
|
|
137
|
+
let withdrawal = currentWithdrawal;
|
|
138
|
+
if (withdrawalAt === "begin") {
|
|
139
|
+
withdrawal = Math.min(withdrawal, balance);
|
|
140
|
+
balance -= withdrawal;
|
|
141
|
+
interest = balance * r;
|
|
142
|
+
balance += interest;
|
|
143
|
+
} else {
|
|
144
|
+
interest = balance * r;
|
|
145
|
+
balance += interest;
|
|
146
|
+
withdrawal = Math.min(withdrawal, balance);
|
|
147
|
+
balance -= withdrawal;
|
|
148
|
+
}
|
|
149
|
+
totalWithdrawn += withdrawal;
|
|
150
|
+
schedule.push({
|
|
151
|
+
month: m,
|
|
152
|
+
withdrawal,
|
|
153
|
+
interestEarned: interest,
|
|
154
|
+
endingBalance: Math.max(0, balance)
|
|
155
|
+
});
|
|
156
|
+
if (balance <= 0) break;
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
endingCorpus: Math.max(0, balance),
|
|
160
|
+
totalWithdrawn,
|
|
161
|
+
schedule
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
function inflationAdjustedValue(value, inflationRateAnnual, years) {
|
|
165
|
+
return value / Math.pow(1 + inflationRateAnnual, years);
|
|
166
|
+
}
|
|
167
|
+
function realReturn(nominalRateAnnual, inflationRateAnnual) {
|
|
168
|
+
return (1 + nominalRateAnnual) / (1 + inflationRateAnnual) - 1;
|
|
169
|
+
}
|
|
170
|
+
function requiredMonthlyInvestmentForGoal(params) {
|
|
171
|
+
const { goalAmountFuture, annualRate, months, stepUpPercentAnnual = 0, investmentAt = "end" } = params;
|
|
172
|
+
let lo = 0;
|
|
173
|
+
let hi = goalAmountFuture;
|
|
174
|
+
for (let i = 0; i < 100; i++) {
|
|
175
|
+
const mid = (lo + hi) / 2;
|
|
176
|
+
const result = sipFutureValue({
|
|
177
|
+
monthlyInvestment: mid,
|
|
178
|
+
annualRate,
|
|
179
|
+
months,
|
|
180
|
+
stepUpPercentAnnual,
|
|
181
|
+
investmentAt
|
|
182
|
+
});
|
|
183
|
+
if (result.futureValue < goalAmountFuture) {
|
|
184
|
+
lo = mid;
|
|
185
|
+
} else {
|
|
186
|
+
hi = mid;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
requiredMonthlyInvestment: (lo + hi) / 2,
|
|
191
|
+
assumptions: `Rate: ${(annualRate * 100).toFixed(1)}% p.a., Duration: ${months} months${stepUpPercentAnnual ? `, Step-up: ${stepUpPercentAnnual}% annually` : ""}`
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
function requiredLumpsumForGoal(goalAmountFuture, annualRate, years) {
|
|
195
|
+
return goalAmountFuture / Math.pow(1 + annualRate, years);
|
|
196
|
+
}
|
|
197
|
+
function cagr(beginValue, endValue, years) {
|
|
198
|
+
beginValue = safeNum(beginValue, 0);
|
|
199
|
+
endValue = safeNum(endValue, 0);
|
|
200
|
+
years = safeNum(years, 1);
|
|
201
|
+
if (beginValue <= 0 || years <= 0) return 0;
|
|
202
|
+
return Math.pow(endValue / beginValue, 1 / years) - 1;
|
|
203
|
+
}
|
|
204
|
+
function absoluteReturn(beginValue, endValue) {
|
|
205
|
+
beginValue = safeNum(beginValue, 0);
|
|
206
|
+
endValue = safeNum(endValue, 0);
|
|
207
|
+
if (beginValue <= 0) return 0;
|
|
208
|
+
return (endValue - beginValue) / beginValue;
|
|
209
|
+
}
|
|
210
|
+
function annualizedReturn(beginValue, endValue, days) {
|
|
211
|
+
beginValue = safeNum(beginValue, 0);
|
|
212
|
+
endValue = safeNum(endValue, 0);
|
|
213
|
+
days = safeNum(days, 1);
|
|
214
|
+
if (beginValue <= 0 || days <= 0) return 0;
|
|
215
|
+
return Math.pow(endValue / beginValue, 365 / days) - 1;
|
|
216
|
+
}
|
|
217
|
+
function pv(rate, nper, pmt, fv2 = 0, type = 0) {
|
|
218
|
+
if (rate === 0) return -(pmt * nper + fv2);
|
|
219
|
+
const factor = Math.pow(1 + rate, nper);
|
|
220
|
+
return -(pmt * (1 + rate * type) * ((factor - 1) / rate) + fv2) / factor;
|
|
221
|
+
}
|
|
222
|
+
function fv(rate, nper, pmt, pvVal = 0, type = 0) {
|
|
223
|
+
if (rate === 0) return -(pvVal + pmt * nper);
|
|
224
|
+
const factor = Math.pow(1 + rate, nper);
|
|
225
|
+
return -(pvVal * factor + pmt * (1 + rate * type) * ((factor - 1) / rate));
|
|
226
|
+
}
|
|
227
|
+
function volatility(returns, periodsPerYear = 252) {
|
|
228
|
+
if (returns.length < 2) return 0;
|
|
229
|
+
const mean = returns.reduce((s, r) => s + r, 0) / returns.length;
|
|
230
|
+
const variance = returns.reduce((s, r) => s + (r - mean) ** 2, 0) / (returns.length - 1);
|
|
231
|
+
return Math.sqrt(variance) * Math.sqrt(periodsPerYear);
|
|
232
|
+
}
|
|
233
|
+
function sharpe(returns, riskFreeRateAnnual = 0, periodsPerYear = 252) {
|
|
234
|
+
const mean = returns.reduce((s, r) => s + r, 0) / returns.length;
|
|
235
|
+
const annualReturn = mean * periodsPerYear;
|
|
236
|
+
const vol = volatility(returns, periodsPerYear);
|
|
237
|
+
if (vol === 0) return 0;
|
|
238
|
+
return (annualReturn - riskFreeRateAnnual) / vol;
|
|
239
|
+
}
|
|
240
|
+
function sortino(returns, riskFreeRateAnnual = 0, periodsPerYear = 252) {
|
|
241
|
+
const mean = returns.reduce((s, r) => s + r, 0) / returns.length;
|
|
242
|
+
const rfPerPeriod = riskFreeRateAnnual / periodsPerYear;
|
|
243
|
+
const downside = returns.filter((r) => r < rfPerPeriod);
|
|
244
|
+
if (downside.length === 0) return Infinity;
|
|
245
|
+
const downsideVariance = downside.reduce((s, r) => s + (r - rfPerPeriod) ** 2, 0) / downside.length;
|
|
246
|
+
const downsideDev = Math.sqrt(downsideVariance) * Math.sqrt(periodsPerYear);
|
|
247
|
+
const annualReturn = mean * periodsPerYear;
|
|
248
|
+
if (downsideDev === 0) return 0;
|
|
249
|
+
return (annualReturn - riskFreeRateAnnual) / downsideDev;
|
|
250
|
+
}
|
|
251
|
+
function maxDrawdown(values) {
|
|
252
|
+
if (values.length < 2) return { maxDrawdown: 0, peakIndex: 0, troughIndex: 0 };
|
|
253
|
+
let peak = values[0];
|
|
254
|
+
let maxDd = 0;
|
|
255
|
+
let peakIdx = 0;
|
|
256
|
+
let troughIdx = 0;
|
|
257
|
+
let currentPeakIdx = 0;
|
|
258
|
+
for (let i = 1; i < values.length; i++) {
|
|
259
|
+
if (values[i] > peak) {
|
|
260
|
+
peak = values[i];
|
|
261
|
+
currentPeakIdx = i;
|
|
262
|
+
}
|
|
263
|
+
const dd = (peak - values[i]) / peak;
|
|
264
|
+
if (dd > maxDd) {
|
|
265
|
+
maxDd = dd;
|
|
266
|
+
peakIdx = currentPeakIdx;
|
|
267
|
+
troughIdx = i;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return { maxDrawdown: maxDd, peakIndex: peakIdx, troughIndex: troughIdx };
|
|
271
|
+
}
|
|
272
|
+
function npv(rate, cashflows) {
|
|
273
|
+
return cashflows.reduce((sum, cf, i) => sum + cf / Math.pow(1 + rate, i), 0);
|
|
274
|
+
}
|
|
275
|
+
function irr(cashflows, guess = 0.1) {
|
|
276
|
+
let rate = guess;
|
|
277
|
+
const maxIter = 1e3;
|
|
278
|
+
const tol = 1e-10;
|
|
279
|
+
for (let i = 0; i < maxIter; i++) {
|
|
280
|
+
let f = 0;
|
|
281
|
+
let df = 0;
|
|
282
|
+
for (let j = 0; j < cashflows.length; j++) {
|
|
283
|
+
const denom = Math.pow(1 + rate, j);
|
|
284
|
+
f += cashflows[j] / denom;
|
|
285
|
+
if (j > 0) df -= j * cashflows[j] / Math.pow(1 + rate, j + 1);
|
|
286
|
+
}
|
|
287
|
+
if (Math.abs(f) < tol) return rate;
|
|
288
|
+
if (Math.abs(df) < tol) break;
|
|
289
|
+
rate -= f / df;
|
|
290
|
+
}
|
|
291
|
+
let lo = -0.99;
|
|
292
|
+
let hi = 10;
|
|
293
|
+
for (let i = 0; i < 1e3; i++) {
|
|
294
|
+
const mid = (lo + hi) / 2;
|
|
295
|
+
const f = cashflows.reduce((s, cf, j) => s + cf / Math.pow(1 + mid, j), 0);
|
|
296
|
+
if (Math.abs(f) < tol) return mid;
|
|
297
|
+
const fLo = cashflows.reduce((s, cf, j) => s + cf / Math.pow(1 + lo, j), 0);
|
|
298
|
+
if (f * fLo > 0) lo = mid;
|
|
299
|
+
else hi = mid;
|
|
300
|
+
}
|
|
301
|
+
return (lo + hi) / 2;
|
|
302
|
+
}
|
|
303
|
+
function weightedReturn(returns, weights) {
|
|
304
|
+
if (returns.length !== weights.length || returns.length === 0) return 0;
|
|
305
|
+
return returns.reduce((s, r, i) => s + r * weights[i], 0);
|
|
306
|
+
}
|
|
307
|
+
function rebalance(targetWeights, currentValues) {
|
|
308
|
+
if (targetWeights.length !== currentValues.length || targetWeights.length === 0) return { trades: [], newValues: [], total: 0 };
|
|
309
|
+
const total = currentValues.reduce((s, v) => s + v, 0);
|
|
310
|
+
const newValues = targetWeights.map((w) => w * total);
|
|
311
|
+
const trades = newValues.map((nv, i) => nv - currentValues[i]);
|
|
312
|
+
return { trades, newValues, total };
|
|
313
|
+
}
|
|
314
|
+
export {
|
|
315
|
+
absoluteReturn,
|
|
316
|
+
amortizationSchedule,
|
|
317
|
+
annualizedReturn,
|
|
318
|
+
cagr,
|
|
319
|
+
emi,
|
|
320
|
+
fv,
|
|
321
|
+
inflationAdjustedValue,
|
|
322
|
+
irr,
|
|
323
|
+
lumpsumFutureValue,
|
|
324
|
+
maxDrawdown,
|
|
325
|
+
npv,
|
|
326
|
+
prepaymentImpact,
|
|
327
|
+
pv,
|
|
328
|
+
realReturn,
|
|
329
|
+
rebalance,
|
|
330
|
+
requiredLumpsumForGoal,
|
|
331
|
+
requiredMonthlyInvestmentForGoal,
|
|
332
|
+
safeNum,
|
|
333
|
+
sharpe,
|
|
334
|
+
sipFutureValue,
|
|
335
|
+
sortino,
|
|
336
|
+
swpPlan,
|
|
337
|
+
volatility,
|
|
338
|
+
weightedReturn
|
|
339
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fundamental-js",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Comprehensive financial calculator library for investing, loans, and personal finance. Includes EMI, SIP, SWP, amortization, CAGR, IRR, NPV, risk metrics, and more.",
|
|
5
|
+
"main": "dist/index.cjs",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.cjs"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"keywords": [
|
|
19
|
+
"finance",
|
|
20
|
+
"financial",
|
|
21
|
+
"calculator",
|
|
22
|
+
"emi",
|
|
23
|
+
"sip",
|
|
24
|
+
"swp",
|
|
25
|
+
"amortization",
|
|
26
|
+
"cagr",
|
|
27
|
+
"irr",
|
|
28
|
+
"npv",
|
|
29
|
+
"sharpe",
|
|
30
|
+
"sortino",
|
|
31
|
+
"volatility",
|
|
32
|
+
"investment",
|
|
33
|
+
"loan",
|
|
34
|
+
"returns"
|
|
35
|
+
],
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "node build.mjs"
|
|
39
|
+
}
|
|
40
|
+
}
|