
Financial Calculator
- 12 installs
- 82 repo stars
- Updated August 2, 2026
- aaaaqwq/claude-code-skills
financial-calculator is a Claude Code skill that computes future value, present value, discount, markup, and compound interest, with a CLI, Python API, and web UI.
About
financial-calculator is a Claude Code skill for financial math including future value, present value, discount and markup pricing, and compound interest. It provides a CLI, a Python API, and an interactive Flask web UI, plus comparison tables across rates and time periods. A developer or operator uses it to project investment growth, price products, or compare loan and discount scenarios. Formula details are documented in a bundled references file.
- 7 calculators: future value, present value, discount, markup, compound interest, and tables
- Ships both a CLI and an interactive Flask web UI
- Generates comparison tables across multiple rates, periods, and discounts
Financial Calculator by the numbers
- 12 all-time installs (skills.sh)
- Ranked #781 of 1,106 Finance & Trading skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
financial-calculator capabilities & compatibility
Free; requires only Python 3.7+ and Flask for the optional web UI, no API keys.
- Capabilities
- future value · present value · compound interest · discount calc · markup calc
- Use cases
- data analysis
- Pricing
- Free
What financial-calculator says it does
Advanced financial calculator with future value tables, present value, discount calculations, markup pricing, and compound interest.
7 calculator types with intuitive tabs
npx skills add https://github.com/aaaaqwq/claude-code-skills --skill financial-calculatorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 12 |
|---|---|
| repo stars | ★ 82 |
| Last updated | August 2, 2026 |
| Repository | aaaaqwq/claude-code-skills ↗ |
What it does
An operator projects investment growth or prices products by running future-value, discount, and markup calculations and comparison tables.
Who is it for?
Developers and operators running investment projections, pricing math, and rate/period comparison tables.
Skip if: Live market data or trading, since it only computes deterministic financial formulas from inputs.
When should I use this skill?
When calculating investment growth, pricing strategies, loan values, discounts, or comparing financial scenarios.
What you get
Accurate financial computations and comparison tables via CLI, Python, or an interactive web UI.
- Financial calculations
- Comparison tables
- Interactive web UI
By the numbers
- 7 calculator types
- 4 compounding frequencies (annual, quarterly, monthly, daily)
- Default web UI port 5050
Files
Financial Calculator
Comprehensive financial calculations including future value, present value, discount/markup pricing, compound interest, and comparative tables.
Quick Start
CLI Usage
# Future Value
python3 scripts/calculate.py fv 10000 0.05 10 12
# PV=$10,000, Rate=5%, Years=10, Monthly compounding
# Present Value
python3 scripts/calculate.py pv 20000 0.05 10 12
# FV=$20,000, Rate=5%, Years=10, Monthly compounding
# Discount
python3 scripts/calculate.py discount 100 20
# Price=$100, Discount=20%
# Markup
python3 scripts/calculate.py markup 100 30
# Cost=$100, Markup=30%
# Future Value Table
python3 scripts/calculate.py fv_table 10000 0.03 0.05 0.07 --periods 1 5 10 20
# Principal=$10,000, Rates=3%,5%,7%, Periods=1,5,10,20 years
# Discount Table
python3 scripts/calculate.py discount_table 100 10 15 20 25 30
# Price=$100, Discounts=10%,15%,20%,25%,30%Web UI
Launch the interactive calculator:
./scripts/launch_ui.sh [port]
# Default port: 5050
# Opens at: http://localhost:5050
# Auto-creates venv and installs Flask if neededOr manually:
cd skills/financial-calculator
python3 -m venv venv # First time only
venv/bin/pip install flask # First time only
venv/bin/python scripts/web_ui.py [port]Features:
- 7 calculator types with intuitive tabs
- Real-time calculations
- Interactive tables
- Beautiful gradient UI
- Mobile-responsive design
Calculators
1. Future Value (FV)
Calculate what an investment will be worth in the future with compound interest.
Use cases:
- Investment growth projections
- Savings account growth
- Retirement planning
Inputs:
- Principal amount
- Annual interest rate (%)
- Time period (years)
- Compounding frequency (annual/quarterly/monthly/daily)
2. Present Value (PV)
Calculate the current value of a future amount (discounted value).
Use cases:
- Loan valuation
- Bond pricing
- Investment analysis
Inputs:
- Future value
- Annual discount rate (%)
- Time period (years)
- Compounding frequency
3. Discount Calculator
Calculate final price after applying percentage discount.
Use cases:
- Retail pricing
- Sale calculations
- Cost savings analysis
Inputs:
- Original price
- Discount percentage
Outputs:
- Discount amount
- Final price
- Savings percentage
4. Markup Calculator
Calculate selling price from cost and markup percentage.
Use cases:
- Product pricing
- Profit margin calculation
- Business pricing strategy
Inputs:
- Cost price
- Markup percentage
Outputs:
- Markup amount
- Selling price
- Profit margin (as % of selling price)
5. Compound Interest
Detailed breakdown of compound interest calculations.
Use cases:
- Interest analysis
- Effective rate comparison
- Loan interest calculation
Outputs:
- Final amount
- Total interest earned
- Effective annual rate
6. Future Value Table
Generate comparison table across multiple rates and time periods.
Use cases:
- Investment scenario comparison
- Rate shopping
- Long-term planning
Features:
- Add multiple interest rates
- Add multiple time periods
- View all combinations in sortable table
- See total gain and gain percentage
7. Discount Table
Compare multiple discount percentages for the same price.
Use cases:
- Bulk pricing strategies
- Promotional planning
- Price comparison
Features:
- Add multiple discount percentages
- See all discount scenarios
- Compare final prices and savings
Installation
Requires Python 3.7+ and Flask:
pip install flaskOr with venv:
python3 -m venv venv
source venv/bin/activate
pip install flaskPython API
Import the calculation module:
from calculate import (
future_value,
present_value,
discount_amount,
markup_price,
compound_interest,
generate_fv_table,
generate_discount_table
)
# Calculate FV
fv = future_value(
present_value=10000,
rate=0.05, # 5% as decimal
periods=10,
compound_frequency=12 # Monthly
)
# Generate table
table = generate_fv_table(
principal=10000,
rates=[0.03, 0.05, 0.07], # As decimals
periods=[1, 5, 10, 20]
)Formulas
See references/formulas.md for detailed mathematical formulas, examples, and use cases for all calculations.
Tips
Rate Format:
- CLI: Use decimals (0.05 for 5%)
- Web UI: Use percentages (5 for 5%)
- Python API: Use decimals (0.05 for 5%)
Compounding Frequencies:
- 1 = Annual
- 4 = Quarterly
- 12 = Monthly
- 365 = Daily
Table Generation: Best practices for meaningful comparisons:
- FV tables: Use 3-5 rates, 4-6 time periods
- Discount tables: Use 5-10 discount percentages
- Keep tables focused for easier analysis
Performance:
- Web UI calculations are instant
- Tables with >100 combinations may take a few seconds
- CLI is fastest for single calculations
Common Workflows
Investment Planning
1. Use FV Calculator to project single investment 2. Generate FV Table to compare different rates 3. Check Compound Interest for detailed breakdown
Pricing Strategy
1. Use Markup Calculator to set selling price 2. Generate Discount Table to plan promotions 3. Compare margins and final prices
Loan Analysis
1. Use PV Calculator to value loan 2. Check Compound Interest for total interest cost 3. Generate FV Table to compare loan terms
{
"owner": "tarigha",
"slug": "financial-calculator",
"displayName": "Financial Calculator Pro",
"latest": {
"version": "1.0.0",
"publishedAt": 1770314147256,
"commit": "https://github.com/openclaw/skills/commit/ee5577e28c4a867e4814ae0860e76a466e21464a"
},
"history": []
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Financial Calculator Pro</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
h1 {
color: white;
text-align: center;
margin-bottom: 30px;
font-size: 2.5em;
text-shadow: 2px 2px 4px rgba(0,0,0,0.2);
}
.tabs {
display: flex;
gap: 10px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.tab {
background: rgba(255,255,255,0.2);
color: white;
border: none;
padding: 12px 24px;
border-radius: 10px;
cursor: pointer;
font-size: 16px;
transition: all 0.3s;
}
.tab:hover {
background: rgba(255,255,255,0.3);
}
.tab.active {
background: white;
color: #667eea;
}
.calculator-card {
background: white;
border-radius: 20px;
padding: 30px;
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
display: none;
}
.calculator-card.active {
display: block;
}
.form-group {
margin-bottom: 20px;
}
.time-inputs {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: #333;
}
input, select {
width: 100%;
padding: 12px;
border: 2px solid #e0e0e0;
border-radius: 10px;
font-size: 16px;
transition: border-color 0.3s;
}
input:focus, select:focus {
outline: none;
border-color: #667eea;
}
button.calculate {
width: 100%;
padding: 15px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 10px;
font-size: 18px;
font-weight: 600;
cursor: pointer;
transition: transform 0.2s;
}
button.calculate:hover {
transform: translateY(-2px);
}
button.calculate:active {
transform: translateY(0);
}
.result {
margin-top: 30px;
padding: 20px;
background: #f8f9fa;
border-radius: 10px;
display: none;
}
.result.show {
display: block;
}
.result-item {
display: flex;
justify-content: space-between;
padding: 10px 0;
border-bottom: 1px solid #e0e0e0;
}
.result-item:last-child {
border-bottom: none;
}
.result-label {
font-weight: 600;
color: #666;
}
.result-value {
font-weight: 700;
color: #667eea;
font-size: 1.1em;
}
.chart-container {
position: relative;
height: 400px;
margin: 30px 0;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
th, td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #e0e0e0;
}
th {
background: #f8f9fa;
font-weight: 600;
color: #333;
position: sticky;
top: 0;
}
tr:hover {
background: #f8f9fa;
}
.table-wrapper {
max-height: 500px;
overflow-y: auto;
margin-top: 20px;
}
.input-group {
display: flex;
gap: 10px;
margin-bottom: 15px;
}
.input-group input {
flex: 1;
}
.add-btn {
padding: 10px 20px;
background: #667eea;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
}
.tag {
display: inline-block;
padding: 6px 12px;
background: #e0e7ff;
color: #667eea;
border-radius: 6px;
margin: 4px;
font-size: 14px;
}
.tags-container {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 15px;
}
.split-view {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30px;
}
@media (max-width: 768px) {
.split-view {
grid-template-columns: 1fr;
}
.time-inputs {
grid-template-columns: 1fr;
}
}
.summary-box {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 20px;
border-radius: 10px;
margin-bottom: 20px;
}
.summary-item {
display: flex;
justify-content: space-between;
padding: 10px 0;
}
.summary-label {
font-size: 14px;
opacity: 0.9;
}
.summary-value {
font-size: 20px;
font-weight: 700;
}
</style>
</head>
<body>
<div class="container">
<h1>🧮 Financial Calculator Pro</h1>
<div class="tabs">
<button class="tab active" onclick="showTab('future-value')">Future Value</button>
<button class="tab" onclick="showTab('present-value')">Present Value</button>
<button class="tab" onclick="showTab('discount')">Discount</button>
<button class="tab" onclick="showTab('markup')">Markup</button>
<button class="tab" onclick="showTab('compound')">Compound Interest</button>
<button class="tab" onclick="showTab('fv-table')">Comparison Table</button>
<button class="tab" onclick="showTab('growth-chart')">Growth Chart</button>
</div>
<!-- Future Value Calculator -->
<div id="future-value" class="calculator-card active">
<h2>Future Value Calculator</h2>
<p style="color: #666; margin-bottom: 20px;">Calculate what your investment will be worth in the future.</p>
<div class="form-group">
<label>Principal Amount ($)</label>
<input type="number" id="fv-principal" value="10000" step="0.01">
</div>
<div class="form-group">
<label>Annual Interest Rate (%)</label>
<input type="number" id="fv-rate" value="5" step="0.01">
</div>
<div class="form-group">
<label>Time Period</label>
<div class="time-inputs">
<div>
<label>Years</label>
<input type="number" id="fv-years" value="10" step="1" min="0">
</div>
<div>
<label>Months</label>
<input type="number" id="fv-months" value="0" step="1" min="0" max="11">
</div>
</div>
</div>
<div class="form-group">
<label>Compound Frequency</label>
<select id="fv-frequency">
<option value="1">Annually</option>
<option value="4">Quarterly</option>
<option value="12" selected>Monthly</option>
<option value="365">Daily</option>
</select>
</div>
<button class="calculate" onclick="calculateFV()">Calculate</button>
<div id="fv-result" class="result">
<div class="summary-box">
<div class="summary-item">
<span class="summary-label">Future Value</span>
<span class="summary-value" id="fv-value">$0.00</span>
</div>
<div class="summary-item">
<span class="summary-label">Total Gain</span>
<span class="summary-value" id="fv-gain">$0.00</span>
</div>
<div class="summary-item">
<span class="summary-label">Gain Percentage</span>
<span class="summary-value" id="fv-gain-pct">0.00%</span>
</div>
</div>
<div class="chart-container">
<canvas id="fv-chart"></canvas>
</div>
<h3>Growth Timeline</h3>
<div class="table-wrapper">
<table id="fv-timeline"></table>
</div>
</div>
</div>
<!-- Present Value Calculator -->
<div id="present-value" class="calculator-card">
<h2>Present Value Calculator</h2>
<p style="color: #666; margin-bottom: 20px;">Calculate the current value of a future amount.</p>
<div class="form-group">
<label>Future Value ($)</label>
<input type="number" id="pv-future" value="20000" step="0.01">
</div>
<div class="form-group">
<label>Annual Discount Rate (%)</label>
<input type="number" id="pv-rate" value="5" step="0.01">
</div>
<div class="form-group">
<label>Time Period</label>
<div class="time-inputs">
<div>
<label>Years</label>
<input type="number" id="pv-years" value="10" step="1" min="0">
</div>
<div>
<label>Months</label>
<input type="number" id="pv-months" value="0" step="1" min="0" max="11">
</div>
</div>
</div>
<div class="form-group">
<label>Compound Frequency</label>
<select id="pv-frequency">
<option value="1">Annually</option>
<option value="4">Quarterly</option>
<option value="12" selected>Monthly</option>
<option value="365">Daily</option>
</select>
</div>
<button class="calculate" onclick="calculatePV()">Calculate</button>
<div id="pv-result" class="result">
<div class="summary-box">
<div class="summary-item">
<span class="summary-label">Present Value</span>
<span class="summary-value" id="pv-value">$0.00</span>
</div>
<div class="summary-item">
<span class="summary-label">Future Value</span>
<span class="summary-value" id="pv-future-display">$0.00</span>
</div>
<div class="summary-item">
<span class="summary-label">Discount Amount</span>
<span class="summary-value" id="pv-discount">$0.00</span>
</div>
</div>
<div class="chart-container">
<canvas id="pv-chart"></canvas>
</div>
</div>
</div>
<!-- Discount Calculator -->
<div id="discount" class="calculator-card">
<h2>Discount Calculator</h2>
<p style="color: #666; margin-bottom: 20px;">Calculate the final price after discount.</p>
<div class="form-group">
<label>Original Price ($)</label>
<input type="number" id="discount-price" value="100" step="0.01">
</div>
<div class="form-group">
<label>Discount Percentage (%)</label>
<input type="number" id="discount-percent" value="20" step="0.01">
</div>
<button class="calculate" onclick="calculateDiscount()">Calculate</button>
<div id="discount-result" class="result">
<div class="summary-box">
<div class="summary-item">
<span class="summary-label">Final Price</span>
<span class="summary-value" id="discount-final">$0.00</span>
</div>
<div class="summary-item">
<span class="summary-label">You Save</span>
<span class="summary-value" id="discount-amount">$0.00</span>
</div>
</div>
<div class="chart-container">
<canvas id="discount-chart"></canvas>
</div>
<h3>Multiple Discount Scenarios</h3>
<div class="table-wrapper">
<table id="discount-scenarios"></table>
</div>
</div>
</div>
<!-- Markup Calculator -->
<div id="markup" class="calculator-card">
<h2>Markup Calculator</h2>
<p style="color: #666; margin-bottom: 20px;">Calculate selling price from cost and markup.</p>
<div class="form-group">
<label>Cost ($)</label>
<input type="number" id="markup-cost" value="100" step="0.01">
</div>
<div class="form-group">
<label>Markup Percentage (%)</label>
<input type="number" id="markup-percent" value="30" step="0.01">
</div>
<button class="calculate" onclick="calculateMarkup()">Calculate</button>
<div id="markup-result" class="result">
<div class="summary-box">
<div class="summary-item">
<span class="summary-label">Selling Price</span>
<span class="summary-value" id="markup-selling">$0.00</span>
</div>
<div class="summary-item">
<span class="summary-label">Markup Amount</span>
<span class="summary-value" id="markup-amount">$0.00</span>
</div>
<div class="summary-item">
<span class="summary-label">Profit Margin</span>
<span class="summary-value" id="markup-margin">0.00%</span>
</div>
</div>
<div class="chart-container">
<canvas id="markup-chart"></canvas>
</div>
<h3>Markup vs Margin Comparison</h3>
<div class="table-wrapper">
<table id="markup-table"></table>
</div>
</div>
</div>
<!-- Compound Interest Calculator -->
<div id="compound" class="calculator-card">
<h2>Compound Interest Calculator</h2>
<p style="color: #666; margin-bottom: 20px;">Detailed compound interest breakdown with growth visualization.</p>
<div class="form-group">
<label>Principal ($)</label>
<input type="number" id="compound-principal" value="10000" step="0.01">
</div>
<div class="form-group">
<label>Annual Interest Rate (%)</label>
<input type="number" id="compound-rate" value="5" step="0.01">
</div>
<div class="form-group">
<label>Time Period</label>
<div class="time-inputs">
<div>
<label>Years</label>
<input type="number" id="compound-years" value="10" step="1" min="0">
</div>
<div>
<label>Months</label>
<input type="number" id="compound-months" value="0" step="1" min="0" max="11">
</div>
</div>
</div>
<div class="form-group">
<label>Compound Frequency</label>
<select id="compound-frequency">
<option value="1">Annually</option>
<option value="4">Quarterly</option>
<option value="12" selected>Monthly</option>
<option value="365">Daily</option>
</select>
</div>
<button class="calculate" onclick="calculateCompound()">Calculate</button>
<div id="compound-result" class="result">
<div class="summary-box">
<div class="summary-item">
<span class="summary-label">Final Amount</span>
<span class="summary-value" id="compound-final">$0.00</span>
</div>
<div class="summary-item">
<span class="summary-label">Total Interest</span>
<span class="summary-value" id="compound-interest">$0.00</span>
</div>
<div class="summary-item">
<span class="summary-label">Effective Annual Rate</span>
<span class="summary-value" id="compound-effective">0.00%</span>
</div>
</div>
<div class="chart-container">
<canvas id="compound-chart"></canvas>
</div>
<h3>Year-by-Year Breakdown</h3>
<div class="table-wrapper">
<table id="compound-breakdown"></table>
</div>
</div>
</div>
<!-- Comparison Table -->
<div id="fv-table" class="calculator-card">
<h2>Investment Comparison Table</h2>
<p style="color: #666; margin-bottom: 20px;">Compare different rates and time periods side-by-side.</p>
<div class="form-group">
<label>Principal ($)</label>
<input type="number" id="table-principal" value="10000" step="0.01">
</div>
<div class="form-group">
<label>Interest Rates (%) - Add multiple</label>
<div class="input-group">
<input type="number" id="table-rate-input" value="5" step="0.01">
<button class="add-btn" onclick="addRate()">Add Rate</button>
</div>
<div id="rates-tags" class="tags-container">
<span class="tag">5%</span>
</div>
</div>
<div class="form-group">
<label>Time Periods (years) - Add multiple</label>
<div class="input-group">
<input type="number" id="table-period-input" value="10" step="1">
<button class="add-btn" onclick="addPeriod()">Add Period</button>
</div>
<div id="periods-tags" class="tags-container">
<span class="tag">10 years</span>
</div>
</div>
<button class="calculate" onclick="generateFVTable()">Generate Comparison</button>
<div id="fv-table-result" class="result">
<div class="table-wrapper">
<table id="fv-table-content"></table>
</div>
</div>
</div>
<!-- Growth Chart -->
<div id="growth-chart" class="calculator-card">
<h2>Multi-Rate Growth Chart</h2>
<p style="color: #666; margin-bottom: 20px;">Visualize and compare multiple investment scenarios.</p>
<div class="form-group">
<label>Principal ($)</label>
<input type="number" id="chart-principal" value="10000" step="0.01">
</div>
<div class="form-group">
<label>Compare Interest Rates (%)</label>
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px;">
<input type="number" id="chart-rate1" value="3" step="0.01" placeholder="Rate 1">
<input type="number" id="chart-rate2" value="5" step="0.01" placeholder="Rate 2">
<input type="number" id="chart-rate3" value="7" step="0.01" placeholder="Rate 3">
</div>
</div>
<div class="form-group">
<label>Time Period (years)</label>
<input type="number" id="chart-years" value="20" step="1" min="1">
</div>
<div class="form-group">
<label>Compound Frequency</label>
<select id="chart-frequency">
<option value="1">Annually</option>
<option value="4">Quarterly</option>
<option value="12" selected>Monthly</option>
<option value="365">Daily</option>
</select>
</div>
<button class="calculate" onclick="generateGrowthChart()">Generate Chart</button>
<div id="chart-result" class="result">
<div class="chart-container" style="height: 500px;">
<canvas id="multi-growth-chart"></canvas>
</div>
<h3>Final Values Comparison</h3>
<div class="table-wrapper">
<table id="chart-comparison-table"></table>
</div>
</div>
</div>
</div>
<script>
let rates = [5];
let periods = [10];
let chartInstances = {};
function showTab(tabName) {
document.querySelectorAll('.calculator-card').forEach(card => {
card.classList.remove('active');
});
document.querySelectorAll('.tab').forEach(tab => {
tab.classList.remove('active');
});
document.getElementById(tabName).classList.add('active');
event.target.classList.add('active');
}
function formatCurrency(value) {
return '$' + parseFloat(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
function getTotalYears(years, months) {
return parseFloat(years) + (parseFloat(months) / 12);
}
function destroyChart(chartId) {
if (chartInstances[chartId]) {
chartInstances[chartId].destroy();
delete chartInstances[chartId];
}
}
// Future Value
async function calculateFV() {
const principal = document.getElementById('fv-principal').value;
const rate = document.getElementById('fv-rate').value;
const years = document.getElementById('fv-years').value;
const months = document.getElementById('fv-months').value;
const frequency = document.getElementById('fv-frequency').value;
const totalYears = getTotalYears(years, months);
const response = await fetch('/api/future-value', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({principal, rate, years: totalYears, frequency})
});
const data = await response.json();
if (data.success) {
const fv = data.future_value;
const gain = fv - principal;
const gainPct = (gain / principal) * 100;
document.getElementById('fv-value').textContent = formatCurrency(fv);
document.getElementById('fv-gain').textContent = formatCurrency(gain);
document.getElementById('fv-gain-pct').textContent = gainPct.toFixed(2) + '%';
document.getElementById('fv-result').classList.add('show');
// Generate timeline chart
await generateFVTimeline(principal, rate, totalYears, frequency);
}
}
async function generateFVTimeline(principal, rate, years, frequency) {
const timeline = await fetch('/api/growth-timeline', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({principal, rate, years, frequency})
});
const data = await timeline.json();
if (data.success) {
// Draw chart
destroyChart('fv-chart');
const ctx = document.getElementById('fv-chart').getContext('2d');
chartInstances['fv-chart'] = new Chart(ctx, {
type: 'line',
data: {
labels: data.timeline.map(d => 'Year ' + d.year),
datasets: [{
label: 'Balance',
data: data.timeline.map(d => d.balance),
borderColor: '#667eea',
backgroundColor: 'rgba(102, 126, 234, 0.1)',
fill: true,
tension: 0.4
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { display: false }
},
scales: {
y: {
ticks: {
callback: function(value) {
return '$' + value.toLocaleString();
}
}
}
}
}
});
// Draw table
const table = document.getElementById('fv-timeline');
let html = '<thead><tr><th>Year</th><th>Balance</th><th>Interest Earned</th><th>Total Interest</th></tr></thead><tbody>';
data.timeline.forEach(row => {
html += `<tr>
<td>${row.year}</td>
<td>${formatCurrency(row.balance)}</td>
<td>${formatCurrency(row.interest)}</td>
<td>${formatCurrency(row.total_interest)}</td>
</tr>`;
});
html += '</tbody>';
table.innerHTML = html;
}
}
// Present Value
async function calculatePV() {
const future_value = document.getElementById('pv-future').value;
const rate = document.getElementById('pv-rate').value;
const years = document.getElementById('pv-years').value;
const months = document.getElementById('pv-months').value;
const frequency = document.getElementById('pv-frequency').value;
const totalYears = getTotalYears(years, months);
const response = await fetch('/api/present-value', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({future_value, rate, years: totalYears, frequency})
});
const data = await response.json();
if (data.success) {
const pv = data.present_value;
const discount = future_value - pv;
document.getElementById('pv-value').textContent = formatCurrency(pv);
document.getElementById('pv-future-display').textContent = formatCurrency(future_value);
document.getElementById('pv-discount').textContent = formatCurrency(discount);
document.getElementById('pv-result').classList.add('show');
// Draw chart
destroyChart('pv-chart');
const ctx = document.getElementById('pv-chart').getContext('2d');
chartInstances['pv-chart'] = new Chart(ctx, {
type: 'doughnut',
data: {
labels: ['Present Value', 'Discount'],
datasets: [{
data: [pv, discount],
backgroundColor: ['#667eea', '#e0e7ff']
}]
},
options: {
responsive: true,
maintainAspectRatio: false
}
});
}
}
// Discount
async function calculateDiscount() {
const price = document.getElementById('discount-price').value;
const discount = document.getElementById('discount-percent').value;
const response = await fetch('/api/discount', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({price, discount})
});
const data = await response.json();
if (data.success) {
const r = data.result;
document.getElementById('discount-final').textContent = formatCurrency(r.final_price);
document.getElementById('discount-amount').textContent = formatCurrency(r.discount_amount);
document.getElementById('discount-result').classList.add('show');
// Draw chart
destroyChart('discount-chart');
const ctx = document.getElementById('discount-chart').getContext('2d');
chartInstances['discount-chart'] = new Chart(ctx, {
type: 'bar',
data: {
labels: ['Original Price', 'Final Price'],
datasets: [{
label: 'Price',
data: [r.original_price, r.final_price],
backgroundColor: ['#764ba2', '#667eea']
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
ticks: {
callback: function(value) {
return '$' + value;
}
}
}
}
}
});
// Generate scenarios table
const scenarios = [10, 15, 20, 25, 30, 35, 40, 50];
const table = document.getElementById('discount-scenarios');
let html = '<thead><tr><th>Discount %</th><th>Discount Amount</th><th>Final Price</th><th>Savings</th></tr></thead><tbody>';
scenarios.forEach(pct => {
const discountAmt = price * (pct / 100);
const finalPrice = price - discountAmt;
html += `<tr>
<td>${pct}%</td>
<td>${formatCurrency(discountAmt)}</td>
<td>${formatCurrency(finalPrice)}</td>
<td>${formatCurrency(discountAmt)}</td>
</tr>`;
});
html += '</tbody>';
table.innerHTML = html;
}
}
// Markup
async function calculateMarkup() {
const cost = document.getElementById('markup-cost').value;
const markup = document.getElementById('markup-percent').value;
const response = await fetch('/api/markup', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({cost, markup})
});
const data = await response.json();
if (data.success) {
const r = data.result;
document.getElementById('markup-selling').textContent = formatCurrency(r.selling_price);
document.getElementById('markup-amount').textContent = formatCurrency(r.markup_amount);
document.getElementById('markup-margin').textContent = r.margin_percent.toFixed(2) + '%';
document.getElementById('markup-result').classList.add('show');
// Draw chart
destroyChart('markup-chart');
const ctx = document.getElementById('markup-chart').getContext('2d');
chartInstances['markup-chart'] = new Chart(ctx, {
type: 'bar',
data: {
labels: ['Cost', 'Selling Price'],
datasets: [{
label: 'Amount',
data: [r.cost, r.selling_price],
backgroundColor: ['#764ba2', '#667eea']
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
ticks: {
callback: function(value) {
return '$' + value;
}
}
}
}
}
});
// Generate comparison table
const markups = [10, 20, 30, 40, 50, 75, 100];
const table = document.getElementById('markup-table');
let html = '<thead><tr><th>Markup %</th><th>Selling Price</th><th>Margin %</th><th>Profit</th></tr></thead><tbody>';
markups.forEach(m => {
const sellingPrice = parseFloat(cost) * (1 + m/100);
const profit = sellingPrice - cost;
const margin = (profit / sellingPrice) * 100;
html += `<tr>
<td>${m}%</td>
<td>${formatCurrency(sellingPrice)}</td>
<td>${margin.toFixed(2)}%</td>
<td>${formatCurrency(profit)}</td>
</tr>`;
});
html += '</tbody>';
table.innerHTML = html;
}
}
// Compound Interest
async function calculateCompound() {
const principal = document.getElementById('compound-principal').value;
const rate = document.getElementById('compound-rate').value;
const years = document.getElementById('compound-years').value;
const months = document.getElementById('compound-months').value;
const frequency = document.getElementById('compound-frequency').value;
const totalYears = getTotalYears(years, months);
const response = await fetch('/api/compound-interest', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({principal, rate, years: totalYears, frequency})
});
const data = await response.json();
if (data.success) {
const r = data.result;
document.getElementById('compound-final').textContent = formatCurrency(r.final_amount);
document.getElementById('compound-interest').textContent = formatCurrency(r.total_interest);
document.getElementById('compound-effective').textContent = (r.effective_annual_rate * 100).toFixed(2) + '%';
document.getElementById('compound-result').classList.add('show');
// Generate timeline for chart
await generateCompoundBreakdown(principal, rate, totalYears, frequency);
}
}
async function generateCompoundBreakdown(principal, rate, years, frequency) {
const timeline = await fetch('/api/growth-timeline', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({principal, rate, years, frequency})
});
const data = await timeline.json();
if (data.success) {
// Draw chart
destroyChart('compound-chart');
const ctx = document.getElementById('compound-chart').getContext('2d');
chartInstances['compound-chart'] = new Chart(ctx, {
type: 'bar',
data: {
labels: data.timeline.map(d => 'Year ' + d.year),
datasets: [
{
label: 'Principal',
data: data.timeline.map(d => principal),
backgroundColor: '#764ba2'
},
{
label: 'Interest',
data: data.timeline.map(d => d.total_interest),
backgroundColor: '#667eea'
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
x: { stacked: true },
y: {
stacked: true,
ticks: {
callback: function(value) {
return '$' + value.toLocaleString();
}
}
}
}
}
});
// Draw table
const table = document.getElementById('compound-breakdown');
let html = '<thead><tr><th>Year</th><th>Balance</th><th>Interest Earned</th><th>Total Interest</th></tr></thead><tbody>';
data.timeline.forEach(row => {
html += `<tr>
<td>${row.year}</td>
<td>${formatCurrency(row.balance)}</td>
<td>${formatCurrency(row.interest)}</td>
<td>${formatCurrency(row.total_interest)}</td>
</tr>`;
});
html += '</tbody>';
table.innerHTML = html;
}
}
// Table Management
function addRate() {
const rateInput = document.getElementById('table-rate-input');
const rate = parseFloat(rateInput.value);
if (!rates.includes(rate)) {
rates.push(rate);
updateRatesTags();
}
}
function updateRatesTags() {
const container = document.getElementById('rates-tags');
container.innerHTML = rates.map(r => `<span class="tag">${r}%</span>`).join('');
}
function addPeriod() {
const periodInput = document.getElementById('table-period-input');
const period = parseInt(periodInput.value);
if (!periods.includes(period)) {
periods.push(period);
updatePeriodsTags();
}
}
function updatePeriodsTags() {
const container = document.getElementById('periods-tags');
container.innerHTML = periods.map(p => `<span class="tag">${p} years</span>`).join('');
}
async function generateFVTable() {
const principal = document.getElementById('table-principal').value;
const response = await fetch('/api/fv-table', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({principal, rates, periods})
});
const data = await response.json();
if (data.success) {
const table = document.getElementById('fv-table-content');
let html = '<thead><tr><th>Rate</th><th>Period</th><th>Future Value</th><th>Total Gain</th><th>Gain %</th></tr></thead><tbody>';
data.table.forEach(row => {
html += `<tr>
<td>${row.rate_percent.toFixed(2)}%</td>
<td>${row.period_years} years</td>
<td>${formatCurrency(row.future_value)}</td>
<td>${formatCurrency(row.total_gain)}</td>
<td>${row.gain_percent.toFixed(2)}%</td>
</tr>`;
});
html += '</tbody>';
table.innerHTML = html;
document.getElementById('fv-table-result').classList.add('show');
}
}
// Growth Chart
async function generateGrowthChart() {
const principal = document.getElementById('chart-principal').value;
const rate1 = document.getElementById('chart-rate1').value;
const rate2 = document.getElementById('chart-rate2').value;
const rate3 = document.getElementById('chart-rate3').value;
const years = document.getElementById('chart-years').value;
const frequency = document.getElementById('chart-frequency').value;
const rates = [rate1, rate2, rate3].filter(r => r);
const response = await fetch('/api/multi-growth', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({principal, rates, years, frequency})
});
const data = await response.json();
if (data.success) {
// Draw chart
destroyChart('multi-growth-chart');
const ctx = document.getElementById('multi-growth-chart').getContext('2d');
const colors = ['#667eea', '#764ba2', '#48bb78'];
const datasets = data.series.map((series, i) => ({
label: series.rate.toFixed(2) + '% Rate',
data: series.values,
borderColor: colors[i],
backgroundColor: colors[i] + '20',
fill: false,
tension: 0.4
}));
chartInstances['multi-growth-chart'] = new Chart(ctx, {
type: 'line',
data: {
labels: Array.from({length: parseInt(years) + 1}, (_, i) => 'Year ' + i),
datasets: datasets
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { position: 'top' }
},
scales: {
y: {
ticks: {
callback: function(value) {
return '$' + value.toLocaleString();
}
}
}
}
}
});
// Draw comparison table
const table = document.getElementById('chart-comparison-table');
let html = '<thead><tr><th>Rate</th><th>Final Value</th><th>Total Gain</th><th>Gain %</th></tr></thead><tbody>';
data.series.forEach(series => {
const finalValue = series.values[series.values.length - 1];
const gain = finalValue - principal;
const gainPct = (gain / principal) * 100;
html += `<tr>
<td>${series.rate.toFixed(2)}%</td>
<td>${formatCurrency(finalValue)}</td>
<td>${formatCurrency(gain)}</td>
<td>${gainPct.toFixed(2)}%</td>
</tr>`;
});
html += '</tbody>';
table.innerHTML = html;
document.getElementById('chart-result').classList.add('show');
}
}
</script>
</body>
</html>
Financial Formulas Reference
Future Value (FV)
Calculate the future value of an investment with compound interest.
Formula:
FV = PV × (1 + r/n)^(n×t)Where:
FV= Future ValuePV= Present Value (initial investment)r= Annual interest rate (as decimal)n= Number of times interest is compounded per yeart= Number of years
Example:
Initial investment: $10,000
Annual rate: 5% (0.05)
Time: 10 years
Compounding: Monthly (12)
FV = 10,000 × (1 + 0.05/12)^(12×10)
FV = 10,000 × (1.004167)^120
FV = $16,470.09Present Value (PV)
Calculate the present value (what a future amount is worth today).
Formula:
PV = FV / (1 + r/n)^(n×t)Where:
PV= Present ValueFV= Future Valuer= Annual discount rate (as decimal)n= Compounding frequency per yeart= Number of years
Example:
Future value: $20,000
Discount rate: 5% (0.05)
Time: 10 years
Compounding: Monthly (12)
PV = 20,000 / (1 + 0.05/12)^(12×10)
PV = 20,000 / 1.6470
PV = $12,139.13Discount Calculation
Calculate final price after applying a percentage discount.
Formula:
Discount Amount = Original Price × (Discount % / 100)
Final Price = Original Price - Discount AmountExample:
Original Price: $100
Discount: 20%
Discount Amount = 100 × 0.20 = $20
Final Price = 100 - 20 = $80Markup Calculation
Calculate selling price from cost and markup percentage.
Formula:
Markup Amount = Cost × (Markup % / 100)
Selling Price = Cost + Markup Amount
Profit Margin = (Markup Amount / Selling Price) × 100Example:
Cost: $100
Markup: 30%
Markup Amount = 100 × 0.30 = $30
Selling Price = 100 + 30 = $130
Profit Margin = (30 / 130) × 100 = 23.08%Compound Interest
Calculate detailed compound interest breakdown.
Formula:
Final Amount = P × (1 + r/n)^(n×t)
Total Interest = Final Amount - P
Effective Annual Rate = (1 + r/n)^n - 1Where:
P= Principal (initial amount)r= Annual interest rate (as decimal)n= Compounding frequencyt= Time in years
Example:
Principal: $10,000
Rate: 5% (0.05)
Time: 10 years
Compounding: Monthly (12)
Final Amount = 10,000 × (1 + 0.05/12)^(12×10) = $16,470.09
Total Interest = 16,470.09 - 10,000 = $6,470.09
Effective Rate = (1 + 0.05/12)^12 - 1 = 5.12%Annuity Future Value
Calculate future value of a series of equal payments.
Formula:
FV = PMT × [((1 + r)^n - 1) / r]Where:
FV= Future Value of annuityPMT= Payment amount per periodr= Interest rate per periodn= Number of periods
Example:
Monthly payment: $500
Annual rate: 6% (0.06)
Monthly rate: 0.06/12 = 0.005
Time: 5 years = 60 months
FV = 500 × [((1.005)^60 - 1) / 0.005]
FV = 500 × 69.77
FV = $34,885Annuity Present Value
Calculate present value of a series of equal future payments.
Formula:
PV = PMT × [(1 - (1 + r)^-n) / r]Where:
PV= Present Value of annuityPMT= Payment amount per periodr= Interest rate per periodn= Number of periods
Example:
Monthly payment: $1,000
Annual rate: 6% (0.06)
Monthly rate: 0.06/12 = 0.005
Time: 10 years = 120 months
PV = 1,000 × [(1 - (1.005)^-120) / 0.005]
PV = 1,000 × 90.07
PV = $90,073.45Compounding Frequencies
Common compounding frequencies:
| Frequency | Periods per Year (n) |
|---|---|
| Annually | 1 |
| Semi-annually | 2 |
| Quarterly | 4 |
| Monthly | 12 |
| Weekly | 52 |
| Daily | 365 |
| Continuously | Use e^(rt) formula |
Common Use Cases
Investment Growth
Use Future Value to see how investments grow over time with compound interest.
Loan Present Value
Use Present Value to determine what monthly payments are worth in today's dollars.
Retail Discounts
Use Discount Calculator for sale prices and savings.
Business Pricing
Use Markup Calculator to price products based on cost and desired profit margin.
Savings Planning
Use FV Tables to compare different interest rates and time horizons.
Retirement Planning
Use Annuity formulas for regular contributions or withdrawals.
#!/usr/bin/env python3
"""
Financial Calculator - Core calculation engine
"""
import math
from typing import Dict, List, Tuple
def future_value(present_value: float, rate: float, periods: int,
compound_frequency: int = 1) -> float:
"""
Calculate future value with compound interest.
Args:
present_value: Initial investment/principal
rate: Annual interest rate (as decimal, e.g., 0.05 for 5%)
periods: Number of years
compound_frequency: Compounding per year (1=annual, 12=monthly, 365=daily)
Returns:
Future value
"""
rate_per_period = rate / compound_frequency
total_periods = periods * compound_frequency
return present_value * (1 + rate_per_period) ** total_periods
def present_value(future_value: float, rate: float, periods: int,
compound_frequency: int = 1) -> float:
"""
Calculate present value (discounted value).
Args:
future_value: Future amount
rate: Annual discount rate (as decimal)
periods: Number of years
compound_frequency: Compounding per year
Returns:
Present value
"""
rate_per_period = rate / compound_frequency
total_periods = periods * compound_frequency
return future_value / ((1 + rate_per_period) ** total_periods)
def discount_amount(original_price: float, discount_percent: float) -> Dict[str, float]:
"""
Calculate discount amount and final price.
Args:
original_price: Original price
discount_percent: Discount percentage (e.g., 20 for 20%)
Returns:
Dict with discount_amount, final_price, savings_percent
"""
discount_amount = original_price * (discount_percent / 100)
final_price = original_price - discount_amount
return {
'original_price': original_price,
'discount_percent': discount_percent,
'discount_amount': discount_amount,
'final_price': final_price,
'savings_percent': discount_percent
}
def markup_price(cost: float, markup_percent: float) -> Dict[str, float]:
"""
Calculate selling price from cost and markup percentage.
Args:
cost: Original cost
markup_percent: Markup percentage (e.g., 30 for 30%)
Returns:
Dict with cost, markup_amount, selling_price, margin_percent
"""
markup_amount = cost * (markup_percent / 100)
selling_price = cost + markup_amount
margin_percent = (markup_amount / selling_price) * 100 if selling_price > 0 else 0
return {
'cost': cost,
'markup_percent': markup_percent,
'markup_amount': markup_amount,
'selling_price': selling_price,
'margin_percent': margin_percent
}
def compound_interest(principal: float, rate: float, periods: int,
compound_frequency: int = 1) -> Dict[str, float]:
"""
Calculate compound interest details.
Args:
principal: Initial amount
rate: Annual interest rate (as decimal)
periods: Number of years
compound_frequency: Compounding per year
Returns:
Dict with principal, final_amount, total_interest, effective_rate
"""
final_amount = future_value(principal, rate, periods, compound_frequency)
total_interest = final_amount - principal
# Effective annual rate
effective_rate = (1 + rate / compound_frequency) ** compound_frequency - 1
return {
'principal': principal,
'rate': rate,
'periods': periods,
'compound_frequency': compound_frequency,
'final_amount': final_amount,
'total_interest': total_interest,
'effective_annual_rate': effective_rate
}
def generate_fv_table(principal: float, rates: List[float],
periods: List[int]) -> List[Dict]:
"""
Generate future value table for multiple rates and periods.
Args:
principal: Initial amount
rates: List of annual rates (as decimals, e.g., [0.03, 0.05, 0.07])
periods: List of periods in years (e.g., [1, 5, 10, 20])
Returns:
List of dicts with rate, period, future_value
"""
results = []
for rate in rates:
for period in periods:
fv = future_value(principal, rate, period)
results.append({
'rate_percent': rate * 100,
'period_years': period,
'future_value': fv,
'total_gain': fv - principal,
'gain_percent': ((fv - principal) / principal) * 100
})
return results
def generate_discount_table(original_price: float,
discounts: List[float]) -> List[Dict]:
"""
Generate discount table for multiple discount percentages.
Args:
original_price: Original price
discounts: List of discount percentages (e.g., [10, 20, 30])
Returns:
List of dicts with discount details
"""
results = []
for discount in discounts:
result = discount_amount(original_price, discount)
results.append(result)
return results
def annuity_future_value(payment: float, rate: float, periods: int) -> float:
"""
Calculate future value of annuity (series of equal payments).
Args:
payment: Payment amount per period
rate: Interest rate per period (as decimal)
periods: Number of periods
Returns:
Future value of annuity
"""
if rate == 0:
return payment * periods
return payment * (((1 + rate) ** periods - 1) / rate)
def annuity_present_value(payment: float, rate: float, periods: int) -> float:
"""
Calculate present value of annuity.
Args:
payment: Payment amount per period
rate: Interest rate per period (as decimal)
periods: Number of periods
Returns:
Present value of annuity
"""
if rate == 0:
return payment * periods
return payment * ((1 - (1 + rate) ** -periods) / rate)
# CLI interface for quick calculations
if __name__ == "__main__":
import sys
import json
if len(sys.argv) < 2:
print("Usage: calculate.py <command> [args...]")
print("\nCommands:")
print(" fv <principal> <rate> <years> [frequency]")
print(" pv <future_value> <rate> <years> [frequency]")
print(" discount <price> <percent>")
print(" markup <cost> <percent>")
print(" fv_table <principal> <rates...> --periods <periods...>")
print(" discount_table <price> <percents...>")
sys.exit(1)
command = sys.argv[1]
if command == "fv":
pv = float(sys.argv[2])
rate = float(sys.argv[3])
years = int(sys.argv[4])
freq = int(sys.argv[5]) if len(sys.argv) > 5 else 1
result = future_value(pv, rate, years, freq)
print(json.dumps({'future_value': result}, indent=2))
elif command == "pv":
fv = float(sys.argv[2])
rate = float(sys.argv[3])
years = int(sys.argv[4])
freq = int(sys.argv[5]) if len(sys.argv) > 5 else 1
result = present_value(fv, rate, years, freq)
print(json.dumps({'present_value': result}, indent=2))
elif command == "discount":
price = float(sys.argv[2])
percent = float(sys.argv[3])
result = discount_amount(price, percent)
print(json.dumps(result, indent=2))
elif command == "markup":
cost = float(sys.argv[2])
percent = float(sys.argv[3])
result = markup_price(cost, percent)
print(json.dumps(result, indent=2))
elif command == "fv_table":
principal = float(sys.argv[2])
# Find --periods flag
periods_idx = sys.argv.index('--periods') if '--periods' in sys.argv else -1
if periods_idx == -1:
print("Error: --periods flag required")
sys.exit(1)
rates = [float(r) for r in sys.argv[3:periods_idx]]
periods = [int(p) for p in sys.argv[periods_idx+1:]]
results = generate_fv_table(principal, rates, periods)
print(json.dumps(results, indent=2))
elif command == "discount_table":
price = float(sys.argv[2])
discounts = [float(d) for d in sys.argv[3:]]
results = generate_discount_table(price, discounts)
print(json.dumps(results, indent=2))
else:
print(f"Unknown command: {command}")
sys.exit(1)
#!/bin/bash
# Launch the financial calculator web UI
# Usage: ./launch_ui.sh [port]
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SKILL_DIR="$(dirname "$SCRIPT_DIR")"
PORT="${1:-5050}"
cd "$SKILL_DIR"
# Check if venv exists, create if not
if [ ! -d "venv" ]; then
echo "Creating virtual environment..."
python3 -m venv venv
venv/bin/pip install flask --quiet
fi
# Launch the web UI
echo ""
echo "🧮 Financial Calculator"
echo "📊 Open: http://localhost:$PORT"
echo "Press Ctrl+C to stop"
echo ""
venv/bin/python scripts/web_ui.py "$PORT"
#!/usr/bin/env python3
"""
Financial Calculator - Web UI Server
Launch with: python3 web_ui.py [port]
"""
from flask import Flask, render_template, request, jsonify
import os
import sys
# Add scripts directory to path to import calculate module
sys.path.insert(0, os.path.dirname(__file__))
import calculate
app = Flask(__name__,
template_folder='../assets',
static_folder='../assets')
@app.route('/')
def index():
"""Serve the main calculator page"""
return render_template('calculator.html')
@app.route('/api/future-value', methods=['POST'])
def api_future_value():
"""Calculate future value"""
data = request.json
try:
result = calculate.future_value(
present_value=float(data['principal']),
rate=float(data['rate']) / 100, # Convert percentage to decimal
periods=int(data['years']),
compound_frequency=int(data.get('frequency', 1))
)
return jsonify({'success': True, 'future_value': result})
except Exception as e:
return jsonify({'success': False, 'error': str(e)}), 400
@app.route('/api/present-value', methods=['POST'])
def api_present_value():
"""Calculate present value"""
data = request.json
try:
result = calculate.present_value(
future_value=float(data['future_value']),
rate=float(data['rate']) / 100,
periods=int(data['years']),
compound_frequency=int(data.get('frequency', 1))
)
return jsonify({'success': True, 'present_value': result})
except Exception as e:
return jsonify({'success': False, 'error': str(e)}), 400
@app.route('/api/discount', methods=['POST'])
def api_discount():
"""Calculate discount"""
data = request.json
try:
result = calculate.discount_amount(
original_price=float(data['price']),
discount_percent=float(data['discount'])
)
return jsonify({'success': True, 'result': result})
except Exception as e:
return jsonify({'success': False, 'error': str(e)}), 400
@app.route('/api/markup', methods=['POST'])
def api_markup():
"""Calculate markup"""
data = request.json
try:
result = calculate.markup_price(
cost=float(data['cost']),
markup_percent=float(data['markup'])
)
return jsonify({'success': True, 'result': result})
except Exception as e:
return jsonify({'success': False, 'error': str(e)}), 400
@app.route('/api/compound-interest', methods=['POST'])
def api_compound_interest():
"""Calculate compound interest details"""
data = request.json
try:
result = calculate.compound_interest(
principal=float(data['principal']),
rate=float(data['rate']) / 100,
periods=int(data['years']),
compound_frequency=int(data.get('frequency', 1))
)
return jsonify({'success': True, 'result': result})
except Exception as e:
return jsonify({'success': False, 'error': str(e)}), 400
@app.route('/api/fv-table', methods=['POST'])
def api_fv_table():
"""Generate future value table"""
data = request.json
try:
rates = [float(r) / 100 for r in data['rates']] # Convert to decimals
periods = [int(p) for p in data['periods']]
result = calculate.generate_fv_table(
principal=float(data['principal']),
rates=rates,
periods=periods
)
return jsonify({'success': True, 'table': result})
except Exception as e:
return jsonify({'success': False, 'error': str(e)}), 400
@app.route('/api/discount-table', methods=['POST'])
def api_discount_table():
"""Generate discount table"""
data = request.json
try:
discounts = [float(d) for d in data['discounts']]
result = calculate.generate_discount_table(
original_price=float(data['price']),
discounts=discounts
)
return jsonify({'success': True, 'table': result})
except Exception as e:
return jsonify({'success': False, 'error': str(e)}), 400
@app.route('/api/growth-timeline', methods=['POST'])
def api_growth_timeline():
"""Generate year-by-year growth timeline"""
data = request.json
try:
principal = float(data['principal'])
rate = float(data['rate']) / 100
years = float(data['years'])
frequency = int(data.get('frequency', 1))
timeline = []
total_interest = 0
# Generate timeline for each year
num_years = int(years) + 1
for year in range(num_years):
if year == 0:
balance = principal
interest = 0
else:
balance = calculate.future_value(principal, rate, year, frequency)
prev_balance = calculate.future_value(principal, rate, year - 1, frequency) if year > 1 else principal
interest = balance - prev_balance
total_interest += interest
timeline.append({
'year': year,
'balance': round(balance, 2),
'interest': round(interest, 2),
'total_interest': round(total_interest, 2)
})
return jsonify({'success': True, 'timeline': timeline})
except Exception as e:
return jsonify({'success': False, 'error': str(e)}), 400
@app.route('/api/multi-growth', methods=['POST'])
def api_multi_growth():
"""Generate multi-rate growth comparison"""
data = request.json
try:
principal = float(data['principal'])
rates = [float(r) / 100 for r in data['rates']]
years = int(data['years'])
frequency = int(data.get('frequency', 1))
series = []
for rate in rates:
values = []
for year in range(years + 1):
if year == 0:
value = principal
else:
value = calculate.future_value(principal, rate, year, frequency)
values.append(round(value, 2))
series.append({
'rate': rate * 100,
'values': values
})
return jsonify({'success': True, 'series': series})
except Exception as e:
return jsonify({'success': False, 'error': str(e)}), 400
if __name__ == '__main__':
port = int(sys.argv[1]) if len(sys.argv) > 1 else 5050
print(f"\n🧮 Financial Calculator")
print(f"📊 Open: http://localhost:{port}")
print(f"Press Ctrl+C to stop\n")
app.run(host='0.0.0.0', port=port, debug=False)
Related skills
FAQ
What can it calculate?
Future value, present value, discount, markup, compound interest, and future-value and discount comparison tables.
Does it have a UI?
Yes, a Flask web UI launched with launch_ui.sh (default port 5050) plus a CLI and Python API.