Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ gcpdeploy.md
.env*
gcpdeploy.md
deploy.sh

.agent/*

# local install
dist-electron
Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ The tool prioritizes tax-efficient withdrawals by:
- Contextual recommendations based on your specific situation
- Withdrawal rate analysis against the 4-5% rule of thumb

### 📈 Portfolio Modeler & Monte Carlo Simulator
- **Visualizer**: see the probability of success for different portfolio allocations
- **Customization**: Build your own portfolio or use preset strategies (Conservative, Moderate, Aggressive) tailored for Accumulation vs. Retirement.
- **Monte Carlo Engine**: Runs 10,000 simulations to project likely (median), unlucky (10th percentile), and lucky (90th percentile) return scenarios based on historical asset data (VTI, VXUS, BND, BNDX).

### 🎨 Modern UI/UX
- Responsive design works on desktop and mobile
- Dark/Light mode toggle
Expand Down Expand Up @@ -236,6 +241,12 @@ The Longevity tab projects assets up to age 100.
* **Depletion Order:** For the longevity visualizer, assets are burned down sequentially: Brokerage → Traditional IRA → Roth IRA → HSA.
* **Income Composition:** Tracks the shifting mix of fixed income (SS, Pension) vs. Portfolio Withdrawals over time to show how the "Paycheck" is constructed.

### 5. Monte Carlo Simulation
The optional Portfolio Modeler uses Geometric Brownian Motion to simulate 10,000 possible market paths for your customized portfolio.
* **Asset Classes:** Uses historical return and volatility data for US Stock (VTI), Int'l Stock (VXUS), US Bond (BND), and Int'l Bond (BNDX).
* **Correlations:** Implements a correlation matrix to account for how assets move in relation to one another (diversification benefit).
* **Outputs:** Proves a statistical "range of outcomes" rather than a single static guess, helping you choose a more robust Annual Return assumption.

## 🤝 Contributing

Contributions are welcome! Here's how you can help:
Expand Down
45 changes: 42 additions & 3 deletions src/components/features/InputSection.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useState, useEffect } from 'react';
import { UserProfile, FilingStatus } from '../../types';
import { HelpCircle, DollarSign, Briefcase, Activity, TrendingUp, PiggyBank, RotateCcw, PlusCircle, AlertTriangle } from 'lucide-react';
import { HelpCircle, DollarSign, Briefcase, Activity, TrendingUp, PiggyBank, RotateCcw, PlusCircle, AlertTriangle, Calculator } from 'lucide-react';
import PortfolioSelectorModal from '../modals/PortfolioSelectorModal';

interface InputSectionProps {
profile: UserProfile;
Expand Down Expand Up @@ -88,6 +89,10 @@ const InputSection: React.FC<InputSectionProps> = ({ profile, setProfile, onRest
const headerClass = "text-xl font-bold text-slate-800 dark:text-white flex items-center gap-2 mb-4";

// const isFutureScenario = (Number(profile.age) || 0) !== profile.baseAge;
const [activeModal, setActiveModal] = useState<'accumulation' | 'retirement' | null>(null);

const accumulationYears = Math.max(5, (profile.age || 65) - (profile.baseAge || 30)); // Min 5 years to show meaningful data
const retirementLongevityYears = Math.max(30, 95 - (profile.age || 65)); // Plan for at least 30 years or until age 95

return (
<div className={containerClass}>
Expand Down Expand Up @@ -319,14 +324,21 @@ const InputSection: React.FC<InputSectionProps> = ({ profile, setProfile, onRest
Market Assumptions (Accumulation)
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<div className="relative">
<label htmlFor="rateOfReturn" className={labelClass}>Annual Return (%)</label>
<PercentageInput
id="rateOfReturn"
value={profile.assumptions.rateOfReturn}
onChange={(val) => handleAssumptionChange('rateOfReturn', val)}
className={inputClass}
/>
<button
onClick={() => setActiveModal('accumulation')}
className="absolute right-2 top-[2.4rem] text-indigo-600 hover:text-indigo-800 bg-indigo-50 hover:bg-indigo-100 p-1 rounded-md transition-colors"
title="Calculate using Portfolio Simulator"
>
<Calculator className="w-4 h-4" />
</button>
</div>
<div>
<label htmlFor="inflationRate" className={labelClass}>Inflation (%)</label>
Expand All @@ -347,14 +359,21 @@ const InputSection: React.FC<InputSectionProps> = ({ profile, setProfile, onRest
Market Assumptions (Retirement)
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<div className="relative">
<label htmlFor="rateOfReturnRetirement" className={labelClass}>Annual Return (%)</label>
<PercentageInput
id="rateOfReturnRetirement"
value={profile.assumptions.rateOfReturnInRetirement}
onChange={(val) => handleAssumptionChange('rateOfReturnInRetirement', val)}
className={inputClass}
/>
<button
onClick={() => setActiveModal('retirement')}
className="absolute right-2 top-[2.4rem] text-teal-600 hover:text-teal-800 bg-teal-50 hover:bg-teal-100 p-1 rounded-md transition-colors"
title="Calculate using Portfolio Simulator"
>
<Calculator className="w-4 h-4" />
</button>
</div>
<div>
<label htmlFor="inflationRateRetirement" className={labelClass}>Inflation (%)</label>
Expand All @@ -367,6 +386,26 @@ const InputSection: React.FC<InputSectionProps> = ({ profile, setProfile, onRest
</div>
</div>
</div>
{activeModal && (
<PortfolioSelectorModal
isOpen={!!activeModal}
onClose={() => setActiveModal(null)}
onConfirm={(rate) => {
if (activeModal === 'accumulation') {
handleAssumptionChange('rateOfReturn', rate);
} else {
handleAssumptionChange('rateOfReturnInRetirement', rate);
}
setActiveModal(null);
}}
simulationDurationYears={
activeModal === 'accumulation'
? accumulationYears
: retirementLongevityYears
}
scenario={activeModal}
/>
)}
</div>
);
};
Expand Down
96 changes: 95 additions & 1 deletion src/components/features/TaxReference.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ import {
Linkedin,
Mail,
Heart,
Sparkles
Sparkles,
Activity
} from 'lucide-react';

interface AccordionSectionProps {
Expand Down Expand Up @@ -688,6 +689,99 @@ const TaxReference: React.FC = () => {
</div>
</AccordionSection>

{/* Portfolio Modeler & Monte Carlo Logic */}
<AccordionSection
title="Portfolio Modeler & Monte Carlo Logic"
icon={<TrendingUp className="w-5 h-5" />}
accentColor="green"
>
<div className="space-y-6">
<p className="text-sm text-slate-600 dark:text-slate-400">
The Portfolio Modeler helps you move beyond guessing a single "Annual Return" number. By simulating thousands of possible market futures, it provides a statistical range of outcomes for your specific asset allocation.
</p>

{/* How It Works */}
<div className={cardClass}>
<div className="flex items-center gap-2 mb-3">
<Activity className="w-5 h-5 text-emerald-600 dark:text-emerald-400" />
<h4 className="font-bold text-slate-800 dark:text-white">How probability works</h4>
</div>
<p className="text-sm text-slate-600 dark:text-slate-400 mb-3">
Markets are volatile. A portfolio averaging 7% long-term might have years of -20% and +30%. This volatility "drag" means your compound annual growth rate (CAGR) is often lower than the simple average.
</p>
<p className="text-sm text-slate-600 dark:text-slate-400 mb-3">
Our engine runs <strong>10,000 simulations</strong> using Geometric Brownian Motion to model this volatility. It outputs a bell curve of results:
</p>
<ul className="space-y-2 text-sm text-slate-700 dark:text-slate-300 ml-2">
<li className="flex items-center gap-2">
<span className="px-2 py-0.5 rounded bg-orange-100 dark:bg-orange-900/30 text-orange-700 dark:text-orange-300 font-bold text-xs">Unlucky (10th Percentile)</span>
<span>A conservative estimate. 90% of the simulated features performed better than this. Use this if you want to be safe.</span>
</li>
<li className="flex items-center gap-2">
<span className="px-2 py-0.5 rounded bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 font-bold text-xs">Median (50th Percentile)</span>
<span>The most likely outcome. Half the simulations were better, half were worse.</span>
</li>
<li className="flex items-center gap-2">
<span className="px-2 py-0.5 rounded bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300 font-bold text-xs">Lucky (90th Percentile)</span>
<span>An optimistic outcome. Only 10% of simulations performed this well.</span>
</li>
</ul>
</div>

{/* Asset Assumptions */}
<div className={cardClass}>
<div className="flex items-center gap-2 mb-3">
<Table className="w-5 h-5 text-blue-600 dark:text-blue-400" />
<h4 className="font-bold text-slate-800 dark:text-white">Asset Assumptions</h4>
</div>
<p className="text-sm text-slate-600 dark:text-slate-400 mb-4">
The simulation uses historical risk/return profiles for four core index fund types.
</p>
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead className={tableHeadClass}>
<tr>
<th className="px-4 py-2 rounded-l-lg">Asset Class</th>
<th className="px-4 py-2">Representative Fund</th>
<th className="px-4 py-2">Modeled Return</th>
<th className="px-4 py-2 rounded-r-lg">Volatility (Std Dev)</th>
</tr>
</thead>
<tbody>
<tr className={tableRowClass}>
<td className="px-4 py-3 font-medium text-slate-800 dark:text-white">US Total Stock Market</td>
<td className={tableCellClass}><span className="font-mono bg-slate-100 dark:bg-slate-700 px-1 rounded">VTI</span></td>
<td className="px-4 py-3 text-emerald-600 dark:text-emerald-400 font-bold">10.5%</td>
<td className={tableCellClass}>15.5%</td>
</tr>
<tr className={tableRowClass}>
<td className="px-4 py-3 font-medium text-slate-800 dark:text-white">Total International Stock</td>
<td className={tableCellClass}><span className="font-mono bg-slate-100 dark:bg-slate-700 px-1 rounded">VXUS</span></td>
<td className="px-4 py-3 text-emerald-600 dark:text-emerald-400 font-bold">8.5%</td>
<td className={tableCellClass}>18.0%</td>
</tr>
<tr className={tableRowClass}>
<td className="px-4 py-3 font-medium text-slate-800 dark:text-white">US Total Bond Market</td>
<td className={tableCellClass}><span className="font-mono bg-slate-100 dark:bg-slate-700 px-1 rounded">BND</span></td>
<td className="px-4 py-3 text-blue-600 dark:text-blue-400 font-bold">4.5%</td>
<td className={tableCellClass}>5.0%</td>
</tr>
<tr className={tableRowClass}>
<td className="px-4 py-3 font-medium text-slate-800 dark:text-white">Total International Bond</td>
<td className={tableCellClass}><span className="font-mono bg-slate-100 dark:bg-slate-700 px-1 rounded">BNDX</span></td>
<td className="px-4 py-3 text-blue-600 dark:text-blue-400 font-bold">4.0%</td>
<td className={tableCellClass}>4.5%</td>
</tr>
</tbody>
</table>
</div>
<p className="text-xs text-slate-500 dark:text-slate-400 mt-3 italic">
* Correlation Matrix assumed: Stocks correlate highly (0.7), Bonds correlate moderately (0.5), and Stock/Bond correlation is low (0.1) for diversification benefits.
</p>
</div>
</div>
</AccordionSection>

{/* About the Creator */}
<AccordionSection
title="About the Creator"
Expand Down
Loading