@thomasgorisse/finance-mcp 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.
Files changed (37) hide show
  1. package/LICENSE +21 -0
  2. package/PRIVACY.md +41 -0
  3. package/README.md +58 -0
  4. package/TERMS.md +47 -0
  5. package/dist/disclaimer.d.ts +8 -0
  6. package/dist/disclaimer.d.ts.map +1 -0
  7. package/dist/disclaimer.js +14 -0
  8. package/dist/disclaimer.js.map +1 -0
  9. package/dist/index.d.ts +13 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +187 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/tools/calculate-budget.d.ts +28 -0
  14. package/dist/tools/calculate-budget.d.ts.map +1 -0
  15. package/dist/tools/calculate-budget.js +137 -0
  16. package/dist/tools/calculate-budget.js.map +1 -0
  17. package/dist/tools/calculate-retirement.d.ts +35 -0
  18. package/dist/tools/calculate-retirement.d.ts.map +1 -0
  19. package/dist/tools/calculate-retirement.js +130 -0
  20. package/dist/tools/calculate-retirement.js.map +1 -0
  21. package/dist/tools/compare-investments.d.ts +52 -0
  22. package/dist/tools/compare-investments.d.ts.map +1 -0
  23. package/dist/tools/compare-investments.js +94 -0
  24. package/dist/tools/compare-investments.js.map +1 -0
  25. package/dist/tools/inflation-calculator.d.ts +34 -0
  26. package/dist/tools/inflation-calculator.d.ts.map +1 -0
  27. package/dist/tools/inflation-calculator.js +116 -0
  28. package/dist/tools/inflation-calculator.js.map +1 -0
  29. package/dist/tools/simulate-loan.d.ts +39 -0
  30. package/dist/tools/simulate-loan.d.ts.map +1 -0
  31. package/dist/tools/simulate-loan.js +106 -0
  32. package/dist/tools/simulate-loan.js.map +1 -0
  33. package/dist/tools/tax-optimizer.d.ts +53 -0
  34. package/dist/tools/tax-optimizer.d.ts.map +1 -0
  35. package/dist/tools/tax-optimizer.js +191 -0
  36. package/dist/tools/tax-optimizer.js.map +1 -0
  37. package/package.json +51 -0
@@ -0,0 +1,130 @@
1
+ import { withDisclaimer } from "../disclaimer.js";
2
+ function round2(n) {
3
+ return Math.round(n * 100) / 100;
4
+ }
5
+ export function calculateRetirement(input) {
6
+ const { currentAge, retirementAge, lifeExpectancy = 85, currentSavings, desiredMonthlyIncome, expectedReturn = 5, inflationRate = 2, expectedPension = 0, currentMonthlySavings = 0, } = input;
7
+ if (currentAge >= retirementAge)
8
+ throw new Error("Current age must be less than retirement age");
9
+ if (retirementAge >= lifeExpectancy)
10
+ throw new Error("Retirement age must be less than life expectancy");
11
+ if (desiredMonthlyIncome <= 0)
12
+ throw new Error("Desired monthly income must be positive");
13
+ const yearsToRetirement = retirementAge - currentAge;
14
+ const yearsInRetirement = lifeExpectancy - retirementAge;
15
+ // Inflation-adjusted monthly need at retirement
16
+ const inflationMultiplier = Math.pow(1 + inflationRate / 100, yearsToRetirement);
17
+ const inflationAdjustedMonthlyNeed = round2(desiredMonthlyIncome * inflationMultiplier);
18
+ const inflationAdjustedPension = round2(expectedPension * inflationMultiplier);
19
+ const monthlyGapAfterPension = round2(inflationAdjustedMonthlyNeed - inflationAdjustedPension);
20
+ // Total needed at retirement (present value of annuity during retirement)
21
+ // Using real return during retirement
22
+ const realReturnRetirement = (expectedReturn - inflationRate) / 100;
23
+ const monthlyRealReturn = realReturnRetirement / 12;
24
+ let totalNeededAtRetirement;
25
+ if (monthlyRealReturn === 0) {
26
+ totalNeededAtRetirement = round2(monthlyGapAfterPension * yearsInRetirement * 12);
27
+ }
28
+ else {
29
+ totalNeededAtRetirement = round2(monthlyGapAfterPension *
30
+ ((1 - Math.pow(1 + monthlyRealReturn, -(yearsInRetirement * 12))) / monthlyRealReturn));
31
+ }
32
+ // Project current savings + contributions to retirement
33
+ const monthlyReturn = expectedReturn / 100 / 12;
34
+ let projectedSavings = currentSavings;
35
+ for (let m = 0; m < yearsToRetirement * 12; m++) {
36
+ projectedSavings = projectedSavings * (1 + monthlyReturn) + currentMonthlySavings;
37
+ }
38
+ projectedSavings = round2(projectedSavings);
39
+ const shortfall = round2(Math.max(0, totalNeededAtRetirement - projectedSavings));
40
+ const onTrack = shortfall <= 0;
41
+ // Required monthly savings to meet goal
42
+ let requiredMonthlySavings;
43
+ const targetGrowth = totalNeededAtRetirement - currentSavings * Math.pow(1 + monthlyReturn, yearsToRetirement * 12);
44
+ if (monthlyReturn === 0) {
45
+ requiredMonthlySavings = round2(Math.max(0, targetGrowth / (yearsToRetirement * 12)));
46
+ }
47
+ else {
48
+ const fvFactor = (Math.pow(1 + monthlyReturn, yearsToRetirement * 12) - 1) / monthlyReturn;
49
+ requiredMonthlySavings = round2(Math.max(0, targetGrowth / fvFactor));
50
+ }
51
+ const summary = formatRetirementSummary({
52
+ yearsToRetirement,
53
+ yearsInRetirement,
54
+ inflationAdjustedMonthlyNeed,
55
+ monthlyGapAfterPension,
56
+ totalNeededAtRetirement,
57
+ projectedSavingsAtRetirement: projectedSavings,
58
+ shortfall,
59
+ requiredMonthlySavings,
60
+ currentMonthlySavings,
61
+ onTrack,
62
+ summary: "",
63
+ currentAge,
64
+ retirementAge,
65
+ lifeExpectancy,
66
+ expectedReturn,
67
+ inflationRate,
68
+ expectedPension,
69
+ desiredMonthlyIncome,
70
+ currentSavings,
71
+ });
72
+ return {
73
+ yearsToRetirement,
74
+ yearsInRetirement,
75
+ inflationAdjustedMonthlyNeed,
76
+ monthlyGapAfterPension,
77
+ totalNeededAtRetirement,
78
+ projectedSavingsAtRetirement: projectedSavings,
79
+ shortfall,
80
+ requiredMonthlySavings,
81
+ currentMonthlySavings,
82
+ onTrack,
83
+ summary,
84
+ };
85
+ }
86
+ function formatRetirementSummary(r) {
87
+ const fmt = (n) => n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
88
+ const status = r.onTrack ? "ON TRACK" : "ACTION NEEDED";
89
+ const lines = [
90
+ `## Retirement Planning — ${status}`,
91
+ "",
92
+ "### Parameters",
93
+ `| | Value |`,
94
+ `|--|-------|`,
95
+ `| Current age | ${r.currentAge} |`,
96
+ `| Retirement age | ${r.retirementAge} |`,
97
+ `| Life expectancy | ${r.lifeExpectancy} |`,
98
+ `| Years to retirement | ${r.yearsToRetirement} |`,
99
+ `| Years in retirement | ${r.yearsInRetirement} |`,
100
+ `| Desired monthly income (today) | ${fmt(r.desiredMonthlyIncome)} EUR |`,
101
+ `| Expected pension | ${fmt(r.expectedPension)} EUR/month |`,
102
+ `| Current savings | ${fmt(r.currentSavings)} EUR |`,
103
+ `| Expected return | ${r.expectedReturn}% |`,
104
+ `| Inflation | ${r.inflationRate}% |`,
105
+ "",
106
+ "### Projections",
107
+ `| | Value |`,
108
+ `|--|-------|`,
109
+ `| Monthly need at retirement (inflation-adjusted) | ${fmt(r.inflationAdjustedMonthlyNeed)} EUR |`,
110
+ `| Monthly gap after pension | ${fmt(r.monthlyGapAfterPension)} EUR |`,
111
+ `| Total needed at retirement | ${fmt(r.totalNeededAtRetirement)} EUR |`,
112
+ `| Projected savings at retirement | ${fmt(r.projectedSavingsAtRetirement)} EUR |`,
113
+ `| Shortfall | ${fmt(r.shortfall)} EUR |`,
114
+ "",
115
+ "### Action Plan",
116
+ `| | Value |`,
117
+ `|--|-------|`,
118
+ `| Current monthly savings | ${fmt(r.currentMonthlySavings)} EUR |`,
119
+ `| **Required monthly savings** | **${fmt(r.requiredMonthlySavings)} EUR** |`,
120
+ ];
121
+ if (!r.onTrack) {
122
+ const gap = round2(r.requiredMonthlySavings - r.currentMonthlySavings);
123
+ lines.push("", `You need to save an additional **${fmt(gap)} EUR/month** to reach your retirement goal.`, "", "Consider:", "- Increasing monthly contributions gradually", "- Seeking higher-return investments (with appropriate risk)", "- Adjusting retirement age or income expectations", "- Maximizing employer pension matching");
124
+ }
125
+ else {
126
+ lines.push("", "You are on track to meet your retirement goals. Continue your current savings plan", "and review annually to stay aligned.");
127
+ }
128
+ return withDisclaimer(lines.join("\n"));
129
+ }
130
+ //# sourceMappingURL=calculate-retirement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"calculate-retirement.js","sourceRoot":"","sources":["../../src/tools/calculate-retirement.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ export interface InvestmentOption {
2
+ /** Name of the investment (e.g. "Livret A", "S&P 500 ETF", "Real Estate") */
3
+ name: string;
4
+ /** Expected annual return as percentage */
5
+ expectedReturn: number;
6
+ /** Risk level 1-10 (1 = very safe, 10 = very risky) */
7
+ riskLevel: number;
8
+ /** Annual fees/costs as percentage (e.g. 0.2 for 0.2%) */
9
+ annualFees?: number;
10
+ /** Whether gains are taxed (for simplified comparison) */
11
+ taxable?: boolean;
12
+ /** Liquidity: how easily you can withdraw */
13
+ liquidity?: "immediate" | "days" | "weeks" | "months" | "years";
14
+ }
15
+ export interface CompareInvestmentsInput {
16
+ /** Initial investment amount */
17
+ initialAmount: number;
18
+ /** Monthly contribution */
19
+ monthlyContribution?: number;
20
+ /** Investment horizon in years */
21
+ horizonYears: number;
22
+ /** Annual inflation rate for real return calc (default: 2%) */
23
+ inflationRate?: number;
24
+ /** Investment options to compare */
25
+ options: InvestmentOption[];
26
+ /** French flat tax rate (default: 30% PFU) */
27
+ taxRate?: number;
28
+ }
29
+ export interface InvestmentProjection {
30
+ name: string;
31
+ nominalValue: number;
32
+ realValue: number;
33
+ totalContributed: number;
34
+ nominalGain: number;
35
+ realGain: number;
36
+ netAfterTax: number;
37
+ effectiveAnnualReturn: number;
38
+ riskLevel: number;
39
+ liquidity: string;
40
+ }
41
+ export interface CompareInvestmentsResult {
42
+ horizonYears: number;
43
+ initialAmount: number;
44
+ monthlyContribution: number;
45
+ inflationRate: number;
46
+ projections: InvestmentProjection[];
47
+ bestNominal: string;
48
+ bestRiskAdjusted: string;
49
+ summary: string;
50
+ }
51
+ export declare function compareInvestments(input: CompareInvestmentsInput): CompareInvestmentsResult;
52
+ //# sourceMappingURL=compare-investments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compare-investments.d.ts","sourceRoot":"","sources":["../../src/tools/compare-investments.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,gBAAgB;IAC/B,6EAA6E;IAC7E,IAAI,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,cAAc,EAAE,MAAM,CAAC;IACvB,uDAAuD;IACvD,SAAS,EAAE,MAAM,CAAC;IAClB,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0DAA0D;IAC1D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,6CAA6C;IAC7C,SAAS,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;CACjE;AAED,MAAM,WAAW,uBAAuB;IACtC,gCAAgC;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,2BAA2B;IAC3B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kCAAkC;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,+DAA+D;IAC/D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oCAAoC;IACpC,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,8CAA8C;IAC9C,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,wBAAwB;IACvC,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,oBAAoB,EAAE,CAAC;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;CACjB;AAMD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,GAAG,wBAAwB,CAsF3F"}
@@ -0,0 +1,94 @@
1
+ import { withDisclaimer } from "../disclaimer.js";
2
+ function round2(n) {
3
+ return Math.round(n * 100) / 100;
4
+ }
5
+ export function compareInvestments(input) {
6
+ const { initialAmount, monthlyContribution = 0, horizonYears, inflationRate = 2, options, taxRate = 30, } = input;
7
+ if (initialAmount < 0)
8
+ throw new Error("Initial amount cannot be negative");
9
+ if (horizonYears <= 0)
10
+ throw new Error("Horizon must be positive");
11
+ if (options.length === 0)
12
+ throw new Error("At least one investment option required");
13
+ const totalContributed = round2(initialAmount + monthlyContribution * 12 * horizonYears);
14
+ const projections = [];
15
+ for (const opt of options) {
16
+ const netReturn = (opt.expectedReturn - (opt.annualFees || 0)) / 100;
17
+ const monthlyReturn = netReturn / 12;
18
+ const monthlyInflation = inflationRate / 100 / 12;
19
+ // Compound with monthly contributions
20
+ let nominalValue = initialAmount;
21
+ for (let m = 0; m < horizonYears * 12; m++) {
22
+ nominalValue = nominalValue * (1 + monthlyReturn) + monthlyContribution;
23
+ }
24
+ nominalValue = round2(nominalValue);
25
+ // Real value adjusted for inflation
26
+ const realValue = round2(nominalValue / Math.pow(1 + inflationRate / 100, horizonYears));
27
+ const nominalGain = round2(nominalValue - totalContributed);
28
+ const realGain = round2(realValue - totalContributed);
29
+ // Tax on gains (simplified: French PFU flat tax)
30
+ const taxOnGains = opt.taxable !== false ? round2(Math.max(0, nominalGain) * taxRate / 100) : 0;
31
+ const netAfterTax = round2(nominalValue - taxOnGains);
32
+ const effectiveAnnualReturn = round2((Math.pow(nominalValue / (totalContributed || 1), 1 / horizonYears) - 1) * 100);
33
+ projections.push({
34
+ name: opt.name,
35
+ nominalValue,
36
+ realValue,
37
+ totalContributed,
38
+ nominalGain,
39
+ realGain,
40
+ netAfterTax,
41
+ effectiveAnnualReturn,
42
+ riskLevel: opt.riskLevel,
43
+ liquidity: opt.liquidity || "unknown",
44
+ });
45
+ }
46
+ // Sort by nominal value descending
47
+ projections.sort((a, b) => b.nominalValue - a.nominalValue);
48
+ const bestNominal = projections[0].name;
49
+ // Risk-adjusted: return / risk
50
+ const bestRA = [...projections].sort((a, b) => b.effectiveAnnualReturn / b.riskLevel - a.effectiveAnnualReturn / a.riskLevel)[0].name;
51
+ const summary = formatComparisonSummary({
52
+ horizonYears,
53
+ initialAmount,
54
+ monthlyContribution,
55
+ inflationRate,
56
+ projections,
57
+ bestNominal,
58
+ bestRiskAdjusted: bestRA,
59
+ });
60
+ return {
61
+ horizonYears,
62
+ initialAmount,
63
+ monthlyContribution,
64
+ inflationRate,
65
+ projections,
66
+ bestNominal,
67
+ bestRiskAdjusted: bestRA,
68
+ summary,
69
+ };
70
+ }
71
+ function formatComparisonSummary(r) {
72
+ const fmt = (n) => n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
73
+ const lines = [
74
+ `## Investment Comparison`,
75
+ "",
76
+ `| Parameter | Value |`,
77
+ `|-----------|-------|`,
78
+ `| Initial investment | ${fmt(r.initialAmount)} EUR |`,
79
+ `| Monthly contribution | ${fmt(r.monthlyContribution)} EUR |`,
80
+ `| Horizon | ${r.horizonYears} years |`,
81
+ `| Assumed inflation | ${r.inflationRate}% |`,
82
+ "",
83
+ `### Projections`,
84
+ "",
85
+ `| Investment | Nominal Value | Real Value | Net After Tax | Gain | Risk | Liquidity |`,
86
+ `|------------|--------------|------------|---------------|------|------|-----------|`,
87
+ ];
88
+ for (const p of r.projections) {
89
+ lines.push(`| ${p.name} | ${fmt(p.nominalValue)} | ${fmt(p.realValue)} | ${fmt(p.netAfterTax)} | ${fmt(p.nominalGain)} | ${p.riskLevel}/10 | ${p.liquidity} |`);
90
+ }
91
+ lines.push("", `### Verdict`, `- **Best nominal return:** ${r.bestNominal}`, `- **Best risk-adjusted return:** ${r.bestRiskAdjusted}`, "", "Note: Past performance does not guarantee future results. Risk levels are indicative.");
92
+ return withDisclaimer(lines.join("\n"));
93
+ }
94
+ //# sourceMappingURL=compare-investments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compare-investments.js","sourceRoot":"","sources":["../../src/tools/compare-investments.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAwDlD,SAAS,MAAM,CAAC,CAAS;IACvB,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAA8B;IAC/D,MAAM,EACJ,aAAa,EACb,mBAAmB,GAAG,CAAC,EACvB,YAAY,EACZ,aAAa,GAAG,CAAC,EACjB,OAAO,EACP,OAAO,GAAG,EAAE,GACb,GAAG,KAAK,CAAC;IAEV,IAAI,aAAa,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IAC5E,IAAI,YAAY,IAAI,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;IACnE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAErF,MAAM,gBAAgB,GAAG,MAAM,CAAC,aAAa,GAAG,mBAAmB,GAAG,EAAE,GAAG,YAAY,CAAC,CAAC;IACzF,MAAM,WAAW,GAA2B,EAAE,CAAC;IAE/C,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,MAAM,SAAS,GAAG,CAAC,GAAG,CAAC,cAAc,GAAG,CAAC,GAAG,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC;QACrE,MAAM,aAAa,GAAG,SAAS,GAAG,EAAE,CAAC;QACrC,MAAM,gBAAgB,GAAG,aAAa,GAAG,GAAG,GAAG,EAAE,CAAC;QAElD,sCAAsC;QACtC,IAAI,YAAY,GAAG,aAAa,CAAC;QACjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,YAAY,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;YAC3C,YAAY,GAAG,YAAY,GAAG,CAAC,CAAC,GAAG,aAAa,CAAC,GAAG,mBAAmB,CAAC;QAC1E,CAAC;QACD,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;QAEpC,oCAAoC;QACpC,MAAM,SAAS,GAAG,MAAM,CAAC,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,aAAa,GAAG,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC;QAEzF,MAAM,WAAW,GAAG,MAAM,CAAC,YAAY,GAAG,gBAAgB,CAAC,CAAC;QAC5D,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,GAAG,gBAAgB,CAAC,CAAC;QAEtD,iDAAiD;QACjD,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,CAAC,GAAG,OAAO,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAChG,MAAM,WAAW,GAAG,MAAM,CAAC,YAAY,GAAG,UAAU,CAAC,CAAC;QAEtD,MAAM,qBAAqB,GAAG,MAAM,CAClC,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,GAAG,CAAC,gBAAgB,IAAI,CAAC,CAAC,EAAE,CAAC,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,CAC/E,CAAC;QAEF,WAAW,CAAC,IAAI,CAAC;YACf,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,YAAY;YACZ,SAAS;YACT,gBAAgB;YAChB,WAAW;YACX,QAAQ;YACR,WAAW;YACX,qBAAqB;YACrB,SAAS,EAAE,GAAG,CAAC,SAAS;YACxB,SAAS,EAAE,GAAG,CAAC,SAAS,IAAI,SAAS;SACtC,CAAC,CAAC;IACL,CAAC;IAED,mCAAmC;IACnC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC;IAE5D,MAAM,WAAW,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACxC,+BAA+B;IAC/B,MAAM,MAAM,GAAG,CAAC,GAAG,WAAW,CAAC,CAAC,IAAI,CAClC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,qBAAqB,GAAG,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,qBAAqB,GAAG,CAAC,CAAC,SAAS,CACxF,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAEV,MAAM,OAAO,GAAG,uBAAuB,CAAC;QACtC,YAAY;QACZ,aAAa;QACb,mBAAmB;QACnB,aAAa;QACb,WAAW;QACX,WAAW;QACX,gBAAgB,EAAE,MAAM;KACzB,CAAC,CAAC;IAEH,OAAO;QACL,YAAY;QACZ,aAAa;QACb,mBAAmB;QACnB,aAAa;QACb,WAAW;QACX,WAAW;QACX,gBAAgB,EAAE,MAAM;QACxB,OAAO;KACR,CAAC;AACJ,CAAC;AAED,SAAS,uBAAuB,CAAC,CAA4C;IAC3E,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,CAAC,OAAO,EAAE,EAAE,qBAAqB,EAAE,CAAC,EAAE,qBAAqB,EAAE,CAAC,EAAE,CAAC,CAAC;IAE7G,MAAM,KAAK,GAAG;QACZ,0BAA0B;QAC1B,EAAE;QACF,uBAAuB;QACvB,uBAAuB;QACvB,0BAA0B,GAAG,CAAC,CAAC,CAAC,aAAa,CAAC,QAAQ;QACtD,4BAA4B,GAAG,CAAC,CAAC,CAAC,mBAAmB,CAAC,QAAQ;QAC9D,eAAe,CAAC,CAAC,YAAY,UAAU;QACvC,yBAAyB,CAAC,CAAC,aAAa,KAAK;QAC7C,EAAE;QACF,iBAAiB;QACjB,EAAE;QACF,uFAAuF;QACvF,sFAAsF;KACvF,CAAC;IAEF,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;QAC9B,KAAK,CAAC,IAAI,CACR,KAAK,CAAC,CAAC,IAAI,MAAM,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,SAAS,SAAS,CAAC,CAAC,SAAS,IAAI,CACpJ,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,IAAI,CACR,EAAE,EACF,aAAa,EACb,8BAA8B,CAAC,CAAC,WAAW,EAAE,EAC7C,oCAAoC,CAAC,CAAC,gBAAgB,EAAE,EACxD,EAAE,EACF,uFAAuF,CACxF,CAAC;IAEF,OAAO,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AAC1C,CAAC"}
@@ -0,0 +1,34 @@
1
+ export interface InflationInput {
2
+ /** Amount in today's money */
3
+ amount: number;
4
+ /** Number of years */
5
+ years: number;
6
+ /** Annual inflation rate as percentage (default: 2%) */
7
+ inflationRate?: number;
8
+ /** Direction: "future" = what will this amount buy in N years, "past" = what was this worth N years ago */
9
+ direction?: "future" | "past";
10
+ /** Optional: compare multiple inflation scenarios */
11
+ scenarios?: number[];
12
+ }
13
+ export interface InflationScenario {
14
+ inflationRate: number;
15
+ futureNominal: number;
16
+ realPurchasingPower: number;
17
+ purchasingPowerLoss: number;
18
+ purchasingPowerLossPercent: number;
19
+ }
20
+ export interface InflationResult {
21
+ amount: number;
22
+ years: number;
23
+ direction: string;
24
+ baseInflationRate: number;
25
+ scenarios: InflationScenario[];
26
+ yearByYear: {
27
+ year: number;
28
+ purchasingPower: number;
29
+ cumulativeLoss: number;
30
+ }[];
31
+ summary: string;
32
+ }
33
+ export declare function inflationCalculator(input: InflationInput): InflationResult;
34
+ //# sourceMappingURL=inflation-calculator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inflation-calculator.d.ts","sourceRoot":"","sources":["../../src/tools/inflation-calculator.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC7B,8BAA8B;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,sBAAsB;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,wDAAwD;IACxD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2GAA2G;IAC3G,SAAS,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC9B,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,0BAA0B,EAAE,MAAM,CAAC;CACpC;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,iBAAiB,EAAE,CAAC;IAC/B,UAAU,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAChF,OAAO,EAAE,MAAM,CAAC;CACjB;AAMD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,cAAc,GAAG,eAAe,CA6E1E"}
@@ -0,0 +1,116 @@
1
+ import { withDisclaimer } from "../disclaimer.js";
2
+ function round2(n) {
3
+ return Math.round(n * 100) / 100;
4
+ }
5
+ export function inflationCalculator(input) {
6
+ const { amount, years, inflationRate = 2, direction = "future", scenarios: extraScenarios = [], } = input;
7
+ if (amount <= 0)
8
+ throw new Error("Amount must be positive");
9
+ if (years <= 0)
10
+ throw new Error("Years must be positive");
11
+ if (inflationRate < 0)
12
+ throw new Error("Inflation rate cannot be negative");
13
+ const allRates = [inflationRate, ...extraScenarios.filter((r) => r !== inflationRate)];
14
+ const scenarioResults = [];
15
+ for (const rate of allRates) {
16
+ if (direction === "future") {
17
+ // What you need in the future to have the same purchasing power
18
+ const futureNominal = round2(amount * Math.pow(1 + rate / 100, years));
19
+ const realPurchasingPower = round2(amount / Math.pow(1 + rate / 100, years));
20
+ const purchasingPowerLoss = round2(amount - realPurchasingPower);
21
+ const purchasingPowerLossPercent = round2((purchasingPowerLoss / amount) * 100);
22
+ scenarioResults.push({
23
+ inflationRate: rate,
24
+ futureNominal,
25
+ realPurchasingPower,
26
+ purchasingPowerLoss,
27
+ purchasingPowerLossPercent,
28
+ });
29
+ }
30
+ else {
31
+ // What this amount was worth in the past
32
+ const futureNominal = amount;
33
+ const realPurchasingPower = round2(amount * Math.pow(1 + rate / 100, years));
34
+ const purchasingPowerLoss = round2(realPurchasingPower - amount);
35
+ const purchasingPowerLossPercent = round2((purchasingPowerLoss / realPurchasingPower) * 100);
36
+ scenarioResults.push({
37
+ inflationRate: rate,
38
+ futureNominal,
39
+ realPurchasingPower,
40
+ purchasingPowerLoss,
41
+ purchasingPowerLossPercent,
42
+ });
43
+ }
44
+ }
45
+ // Year-by-year for base rate
46
+ const yearByYear = [];
47
+ for (let y = 1; y <= Math.min(years, 50); y++) {
48
+ const pp = round2(amount / Math.pow(1 + inflationRate / 100, y));
49
+ yearByYear.push({
50
+ year: y,
51
+ purchasingPower: pp,
52
+ cumulativeLoss: round2(amount - pp),
53
+ });
54
+ }
55
+ const summary = formatInflationSummary({
56
+ amount,
57
+ years,
58
+ direction,
59
+ baseInflationRate: inflationRate,
60
+ scenarios: scenarioResults,
61
+ yearByYear,
62
+ });
63
+ return {
64
+ amount,
65
+ years,
66
+ direction,
67
+ baseInflationRate: inflationRate,
68
+ scenarios: scenarioResults,
69
+ yearByYear,
70
+ summary,
71
+ };
72
+ }
73
+ function formatInflationSummary(r) {
74
+ const fmt = (n) => n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
75
+ const dirLabel = r.direction === "future" ? "Future Impact" : "Historical Comparison";
76
+ const lines = [
77
+ `## Inflation Calculator — ${dirLabel}`,
78
+ "",
79
+ `| Parameter | Value |`,
80
+ `|-----------|-------|`,
81
+ `| Amount | ${fmt(r.amount)} EUR |`,
82
+ `| Period | ${r.years} years |`,
83
+ `| Direction | ${r.direction} |`,
84
+ "",
85
+ ];
86
+ if (r.scenarios.length > 1) {
87
+ lines.push("### Scenario Comparison", "");
88
+ lines.push("| Inflation Rate | Purchasing Power | Loss | Loss % |");
89
+ lines.push("|---------------|-----------------|------|--------|");
90
+ for (const s of r.scenarios) {
91
+ lines.push(`| ${s.inflationRate}% | ${fmt(s.realPurchasingPower)} EUR | ${fmt(s.purchasingPowerLoss)} EUR | ${s.purchasingPowerLossPercent}% |`);
92
+ }
93
+ }
94
+ else {
95
+ const s = r.scenarios[0];
96
+ if (r.direction === "future") {
97
+ lines.push(`At **${s.inflationRate}%** annual inflation over **${r.years} years**:`, "", `- **${fmt(r.amount)} EUR today** will only buy what **${fmt(s.realPurchasingPower)} EUR** buys today`, `- You would need **${fmt(s.futureNominal)} EUR** in ${r.years} years to maintain the same purchasing power`, `- Purchasing power loss: **${fmt(s.purchasingPowerLoss)} EUR** (${s.purchasingPowerLossPercent}%)`);
98
+ }
99
+ else {
100
+ lines.push(`At **${s.inflationRate}%** annual inflation over **${r.years} years**:`, "", `- **${fmt(r.amount)} EUR** ${r.years} years ago had the purchasing power of **${fmt(s.realPurchasingPower)} EUR** today`, `- Inflation eroded **${fmt(s.purchasingPowerLoss)} EUR** in value (${s.purchasingPowerLossPercent}%)`);
101
+ }
102
+ }
103
+ // Show abbreviated year-by-year
104
+ const showYears = r.yearByYear.length <= 10
105
+ ? r.yearByYear
106
+ : [...r.yearByYear.slice(0, 5), ...r.yearByYear.slice(-5)];
107
+ lines.push("", "### Year-by-Year Purchasing Power", "", "| Year | Purchasing Power | Cumulative Loss |", "|------|-----------------|-----------------|");
108
+ for (const y of showYears) {
109
+ lines.push(`| ${y.year} | ${fmt(y.purchasingPower)} EUR | ${fmt(y.cumulativeLoss)} EUR |`);
110
+ }
111
+ if (r.yearByYear.length > 10) {
112
+ lines.push("| ... | ... | ... |");
113
+ }
114
+ return withDisclaimer(lines.join("\n"));
115
+ }
116
+ //# sourceMappingURL=inflation-calculator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inflation-calculator.js","sourceRoot":"","sources":["../../src/tools/inflation-calculator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ export interface LoanInput {
2
+ /** Loan type: mortgage, car, student, personal */
3
+ type: "mortgage" | "car" | "student" | "personal";
4
+ /** Principal amount in currency units */
5
+ principal: number;
6
+ /** Annual interest rate as percentage (e.g. 3.5 for 3.5%) */
7
+ annualRate: number;
8
+ /** Loan term in years */
9
+ termYears: number;
10
+ /** Optional: monthly insurance rate as percentage of principal (e.g. 0.03 for 0.03%) */
11
+ insuranceRate?: number;
12
+ /** Whether to include full amortization table (default: false, shows first 12 + last 12 rows) */
13
+ fullTable?: boolean;
14
+ }
15
+ export interface AmortizationRow {
16
+ month: number;
17
+ payment: number;
18
+ principal: number;
19
+ interest: number;
20
+ insurance: number;
21
+ remainingBalance: number;
22
+ }
23
+ export interface LoanResult {
24
+ type: string;
25
+ principal: number;
26
+ annualRate: number;
27
+ termYears: number;
28
+ termMonths: number;
29
+ monthlyPayment: number;
30
+ monthlyInsurance: number;
31
+ totalMonthlyPayment: number;
32
+ totalInterest: number;
33
+ totalInsurance: number;
34
+ totalCost: number;
35
+ amortizationTable: AmortizationRow[];
36
+ summary: string;
37
+ }
38
+ export declare function simulateLoan(input: LoanInput): LoanResult;
39
+ //# sourceMappingURL=simulate-loan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"simulate-loan.d.ts","sourceRoot":"","sources":["../../src/tools/simulate-loan.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,SAAS;IACxB,kDAAkD;IAClD,IAAI,EAAE,UAAU,GAAG,KAAK,GAAG,SAAS,GAAG,UAAU,CAAC;IAClD,yCAAyC;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,6DAA6D;IAC7D,UAAU,EAAE,MAAM,CAAC;IACnB,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,wFAAwF;IACxF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iGAAiG;IACjG,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,eAAe,EAAE,CAAC;IACrC,OAAO,EAAE,MAAM,CAAC;CACjB;AAMD,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,GAAG,UAAU,CAmFzD"}
@@ -0,0 +1,106 @@
1
+ import { withDisclaimer } from "../disclaimer.js";
2
+ function round2(n) {
3
+ return Math.round(n * 100) / 100;
4
+ }
5
+ export function simulateLoan(input) {
6
+ const { type, principal, annualRate, termYears, insuranceRate = 0, fullTable = false } = input;
7
+ if (principal <= 0)
8
+ throw new Error("Principal must be positive");
9
+ if (annualRate < 0)
10
+ throw new Error("Annual rate cannot be negative");
11
+ if (termYears <= 0)
12
+ throw new Error("Term must be positive");
13
+ const termMonths = termYears * 12;
14
+ const monthlyRate = annualRate / 100 / 12;
15
+ const monthlyInsurance = round2((principal * (insuranceRate / 100)) / 12);
16
+ let monthlyPayment;
17
+ if (monthlyRate === 0) {
18
+ monthlyPayment = round2(principal / termMonths);
19
+ }
20
+ else {
21
+ monthlyPayment = round2((principal * monthlyRate * Math.pow(1 + monthlyRate, termMonths)) /
22
+ (Math.pow(1 + monthlyRate, termMonths) - 1));
23
+ }
24
+ const totalMonthlyPayment = round2(monthlyPayment + monthlyInsurance);
25
+ const table = [];
26
+ let balance = principal;
27
+ let totalInterest = 0;
28
+ let totalInsurancePaid = 0;
29
+ for (let month = 1; month <= termMonths; month++) {
30
+ const interestPortion = round2(balance * monthlyRate);
31
+ const principalPortion = round2(monthlyPayment - interestPortion);
32
+ balance = round2(Math.max(0, balance - principalPortion));
33
+ totalInterest = round2(totalInterest + interestPortion);
34
+ totalInsurancePaid = round2(totalInsurancePaid + monthlyInsurance);
35
+ table.push({
36
+ month,
37
+ payment: totalMonthlyPayment,
38
+ principal: principalPortion,
39
+ interest: interestPortion,
40
+ insurance: monthlyInsurance,
41
+ remainingBalance: balance,
42
+ });
43
+ }
44
+ const totalCost = round2(principal + totalInterest + totalInsurancePaid);
45
+ // For display: show first 12 + last 12 unless fullTable requested
46
+ let displayTable = table;
47
+ if (!fullTable && termMonths > 24) {
48
+ displayTable = [...table.slice(0, 12), ...table.slice(-12)];
49
+ }
50
+ const summary = formatLoanSummary({
51
+ type,
52
+ principal,
53
+ annualRate,
54
+ termYears,
55
+ termMonths,
56
+ monthlyPayment,
57
+ monthlyInsurance,
58
+ totalMonthlyPayment,
59
+ totalInterest,
60
+ totalInsurance: totalInsurancePaid,
61
+ totalCost,
62
+ amortizationTable: displayTable,
63
+ });
64
+ return {
65
+ type,
66
+ principal,
67
+ annualRate,
68
+ termYears,
69
+ termMonths,
70
+ monthlyPayment,
71
+ monthlyInsurance,
72
+ totalMonthlyPayment,
73
+ totalInterest,
74
+ totalInsurance: totalInsurancePaid,
75
+ totalCost,
76
+ amortizationTable: displayTable,
77
+ summary,
78
+ };
79
+ }
80
+ function formatLoanSummary(r) {
81
+ const lines = [
82
+ `## Loan Simulation: ${r.type.charAt(0).toUpperCase() + r.type.slice(1)}`,
83
+ "",
84
+ `| Parameter | Value |`,
85
+ `|-----------|-------|`,
86
+ `| Principal | ${r.principal.toLocaleString("en-US", { style: "currency", currency: "EUR" })} |`,
87
+ `| Annual rate | ${r.annualRate}% |`,
88
+ `| Term | ${r.termYears} years (${r.termMonths} months) |`,
89
+ `| Monthly payment (principal + interest) | ${r.monthlyPayment.toLocaleString("en-US", { style: "currency", currency: "EUR" })} |`,
90
+ `| Monthly insurance | ${r.monthlyInsurance.toLocaleString("en-US", { style: "currency", currency: "EUR" })} |`,
91
+ `| **Total monthly payment** | **${r.totalMonthlyPayment.toLocaleString("en-US", { style: "currency", currency: "EUR" })}** |`,
92
+ `| Total interest paid | ${r.totalInterest.toLocaleString("en-US", { style: "currency", currency: "EUR" })} |`,
93
+ `| Total insurance paid | ${r.totalInsurance.toLocaleString("en-US", { style: "currency", currency: "EUR" })} |`,
94
+ `| **Total cost** | **${r.totalCost.toLocaleString("en-US", { style: "currency", currency: "EUR" })}** |`,
95
+ "",
96
+ "### Amortization Schedule (excerpt)",
97
+ "",
98
+ "| Month | Payment | Principal | Interest | Insurance | Balance |",
99
+ "|-------|---------|-----------|----------|-----------|---------|",
100
+ ];
101
+ for (const row of r.amortizationTable) {
102
+ lines.push(`| ${row.month} | ${row.payment.toFixed(2)} | ${row.principal.toFixed(2)} | ${row.interest.toFixed(2)} | ${row.insurance.toFixed(2)} | ${row.remainingBalance.toFixed(2)} |`);
103
+ }
104
+ return withDisclaimer(lines.join("\n"));
105
+ }
106
+ //# sourceMappingURL=simulate-loan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"simulate-loan.js","sourceRoot":"","sources":["../../src/tools/simulate-loan.ts"],"names":[],"mappings":"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"}