Turn any Python financial model into a fully interactive, self-contained HTML dashboard, all generated by Claude from a single structured prompt.
Every interactive dashboard you see on this website, from the MVO portfolio optimizer to the Black–Scholes calculators and the Monte Carlo simulator, was built the same way: a Python model on one side, a detailed prompt on the other, and Claude in between.
This guide walks through the full workflow. You'll install three tools, open an existing Python model, write a structured prompt describing the dashboard you want, and let Claude generate a single self-contained HTML file with embedded charts, interactivity, and data. No server, no framework, no build step.
You'll need three tools installed before starting. Each one plays a distinct role in the workflow.
The A.I model that writes the dashboard code. Install Claude Code (CLI) to use Claude directly inside VS Code, where it can read your files and write new ones.
Install Claude CodeFree code editor from Microsoft. This is your workspace: the place where your Python files live, where Claude operates, and where you'll review the generated HTML.
Install VS CodeVersion control system. Tracks every change Claude makes to your project, so you can roll back, compare versions, or push your work to GitHub for deployment.
Install GitDashboard automation works best when you already have a working Python model that implements the math correctly. Claude's job isn't to invent the pricing logic. Its role is to wrap your existing logic in a presentation layer: charts, tables, layout, and interactivity.
For this walkthrough we use the Black–Scholes option pricing model. The full Python implementation, covering the pricing formulas, arbitrage bounds, and the implied volatility solver via Brent's method, is already documented and available:
Clone or copy that script into a new folder. That folder becomes the project Claude will work inside.
Open your project folder in VS Code (File → Open Folder). Then launch
Claude Code from the integrated terminal by typing claude. Once authenticated,
Claude has full read/write access to every file in the folder.
Initialize Git in the project (git init) before giving Claude instructions.
Every change Claude makes becomes a diff you can inspect, and if something goes wrong you
can revert with git restore ..
This is the core of the workflow. Claude builds what you describe, so a vague prompt produces a vague dashboard. A well-structured blueprint prompt specifies architecture, data layer, computation engine, dashboard sections, interactivity, and visual theme in explicit detail.
Treat the prompt like a Python script header: long, structured, and section-numbered. Below is the exact blueprint prompt used to generate the MVO Portfolio Optimizer dashboard on this site. You can adapt the same structure for any model (Black–Scholes, Monte Carlo, binomial tree) by swapping the computation engine and dashboard sections.
BLUEPRINT PROMPT — MEAN-VARIANCE OPTIMIZATION (MVO) PORTFOLIO DASHBOARD
=========================================================================
Build a self-contained HTML dashboard generated by a single Python script.
The Python script fetches data, runs all computations, generates Plotly charts,
and outputs one .html file with embedded data, charts, and interactive JavaScript.
No server required — the HTML works standalone in any browser.
IMPORTANT: All computations in this dashboard are derived from the academic
blueprint in "blueprint_calculations.ipynb". That notebook implements the Merton
model for portfolio optimization from first principles. You MUST read the
blueprint notebook first — it contains all the formulas, objective functions,
and optimization logic that the computation engine is built on. The dashboard
extends that blueprint into a production tool with real ETF data and multiple
time windows. Below is a summary of the theory from the blueprint.
================================================================================
0. THEORETICAL FOUNDATION — THE MERTON MODEL (from blueprint_calculations.ipynb)
================================================================================
The blueprint notebook teaches portfolio theory following the Markowitz
mean-variance framework as extended by Robert Merton. The key concepts:
PORTFOLIO OF n ASSETS:
Expected return vector: er = [er_1, er_2, ..., er_n]'
Weights vector: w = [w_1, w_2, ..., w_n]' where sum(w_i) = 1
Variance-covariance matrix: S (n x n symmetric, annualized)
Portfolio expected return: Er_p = er' * w
Portfolio variance: sigma_p^2 = w' * S * w
Portfolio volatility: sigma_p = sqrt(sigma_p^2)
Covariance between two portfolios: sigma_p1_p2 = w1' * S * w2
MERTON PROPOSITION 1 — EFFICIENT PORTFOLIO WEIGHTS:
Given a constant c (related to the risk-free rate):
w = S^{-1} * (er - c) / sum(S^{-1} * (er - c))
This gives the tangency (maximum Sharpe ratio) portfolio for a given c.
The blueprint computes two efficient portfolios with c1=0 and c2=0.05
to demonstrate that different risk-free assumptions yield different
optimal allocations.
MERTON PROPOSITION 2 — ENVELOPE PORTFOLIOS:
Any combination of two envelope portfolios is also an envelope portfolio:
w = w1 * lambda + w2 * (1 - lambda)
This means the entire efficient frontier can be traced by combining any
two efficient portfolios with varying lambda. The blueprint uses this
to sweep lambda from -2 to 8 and plots the resulting parabola.
GLOBAL MINIMUM VARIANCE PORTFOLIO (GMVP):
The portfolio with the lowest possible volatility:
w_gmvp = [1,1,...] * S^{-1} / sum([1,1,...] * S^{-1})
This is the leftmost point on the efficient frontier.
The blueprint computes this analytically and verifies it matches the
numerical optimization result.
OPTIMIZATION WITH CONSTRAINTS (scipy):
The blueprint introduces constrained optimization using scipy.optimize.minimize:
Objective functions defined in the blueprint:
f_negative_sharpe(w, er, S, r) = -(er' * w - r) / sqrt(w' * S * w)
f_variance(w, er, S, r) = w' * S * w
Constraints:
Equality: sum(w) = 1
Inequality: w >= 0 (long-only, no short selling)
Bounds: (0, 1) per weight for constrained, (-1, 1) for unconstrained
The blueprint traces the efficient frontier by varying the risk-free rate
parameter r from -0.95 to 0.25 in steps of 0.01 and re-optimizing at each
step. Each optimization finds the max-Sharpe portfolio for that r, and the
collection of (volatility, return) points traces the frontier curve.
This is the same target-sweep approach used in the dashboard, but the
dashboard sweeps target returns directly rather than sweeping r.
STATISTICS COMPUTED IN THE BLUEPRINT:
- Log returns: log(P_t / P_{t-1})
- Annualized expected returns: mean(log_returns) * 252
- Annualized variance: var(log_returns) * 252
- Annualized volatility: std(log_returns) * sqrt(252)
- Annualized covariance matrix: cov(log_returns) * 252
- Correlation matrix: corrcoef(log_returns)
- Sharpe ratio: (er - rf) / vol
The dashboard extends the blueprint by:
- Using real ETF data instead of 3 indices (Eurostoxx, S&P, Nikkei)
- Adding VaR, CVaR, skewness, kurtosis, max drawdown metrics
- Supporting multiple time windows (3Y, 5Y, 10Y)
- Adding a constrained portfolio variant (25% max weight per asset)
- Fetching actual risk-free rates from FRED instead of hardcoded constants
- Auto-opening the generated HTML in the browser
================================================================================
1. ARCHITECTURE
================================================================================
Single Python file (e.g. mvo_model.py) that:
- Downloads ETF price data from Yahoo Finance (yfinance)
- Downloads risk-free rates from FRED API (fredapi or requests)
- Computes statistics, optimizes portfolios, generates Plotly figures
- Embeds everything into one HTML string with inline <style> and <script>
- Writes the HTML file to disk
The output HTML includes:
- Plotly.js loaded from CDN (plotly-2.35.0.min.js)
- html2canvas + jsPDF for client-side PDF export
- Inter font from Google Fonts
- All data embedded as JSON in <script> variables
- All interactivity in vanilla JavaScript (no framework)
================================================================================
2. DATA LAYER
================================================================================
ASSET CLASSES:
Define a fixed list of ETF dictionaries, each with: ticker, name, color.
These are hardcoded — no asset selection UI needed.
The 10 default asset classes:
SPY — US Large Cap
IWM — US Small Cap
EFA — Developed ex-US Equity
VWO — Emerging Markets Equity
GLD — Gold
TLT — US Long Bonds
HYG — High Yield Bonds
EMB — EM Government Bonds
CEMB — EM Corporate Bonds
IYR — Real Estate
Assign a distinct color to each for consistent chart rendering.
RISK-FREE RATE:
Fetch from FRED API using series DGS3, DGS5, DGS10 for each window.
Convert from percentage to decimal. Fallback to a hardcoded value if API fails.
PRICE DATA:
Download adjusted close prices from Yahoo Finance for all tickers.
Date range: from (end_date - window_years * 365 days) to end_date.
Allow a TARGET_DATE parameter (None = today, or a specific date for historical MVO).
ROLLING WINDOWS:
Compute everything for multiple lookback windows: 3-Year, 5-Year, 10-Year.
Each window has its own risk-free rate matched to the duration.
The user toggles between windows with a button bar at the top of the dashboard.
================================================================================
3. COMPUTATION ENGINE
================================================================================
IMPORTANT: The entire computation engine must be built by reading and following
the blueprint_calculations.ipynb notebook. That notebook contains:
- All the mathematical formulas (portfolio return, variance, covariance)
- The objective functions for optimization (f_negative_sharpe, f_variance)
- The constraint setup for scipy.optimize.minimize
- The efficient frontier sweep methodology
- The GMVP (Global Minimum Variance Portfolio) derivation
- The Capital Market Line construction
Read the blueprint notebook first and implement every computation based on it.
The notebook uses 3 indices (Eurostoxx, S&P, Nikkei) as a teaching example —
the dashboard applies the same math to real ETF data with more assets.
The blueprint covers: log returns, annualized statistics, covariance matrices,
Merton Proposition 1 (efficient portfolio weights via S^{-1}), Proposition 2
(envelope portfolio combinations), GMVP, constrained vs unconstrained
optimization with scipy SLSQP, and frontier curve generation.
Beyond what the blueprint covers, the dashboard should also compute:
- Skewness and kurtosis of daily returns
- Maximum drawdown per asset
- VaR 95% (historical): -np.percentile(daily_returns, 5) * 100
- CVaR 95%: -mean(returns below the VaR threshold) * 100
- A constrained portfolio variant with a maximum weight cap (e.g. 25% per asset)
- Multiple time windows (3Y, 5Y, 10Y) with window-matched risk-free rates
- Fetching real risk-free rates from FRED instead of hardcoded constants
These extensions follow naturally from the blueprint's framework and can be
added on top of the core computations defined in the notebook.
================================================================================
4. DASHBOARD SECTIONS
================================================================================
The HTML is structured as a series of <div class="sec"> sections.
Each section contains content for all 3 windows (3Y, 5Y, 10Y) wrapped in
<div class="wc wc-3Y active">, <div class="wc wc-5Y">, etc.
Only the active window is visible (CSS: .wc{display:none} .wc.active{display:block}).
HEADER:
- Title: "Multi-Asset MVO Portfolio Optimizer"
- Subtitle: lists selected ETF tickers, data end date, asset count
- Window toggle bar: [3 Years] [5 Years] [10 Years] buttons
MODEL NOTES:
Numbered list explaining:
1. What MVO is (Markowitz 1952, Merton extension)
2. This is asset allocation, not stock picking
3. Uses historical returns (backward-looking)
KPI CARDS (3 cards in a grid):
1. Long-Only Max Sharpe: Sharpe value + delta arrow vs SPY (or main benchmark)
2. Constrained Max Sharpe: Sharpe value + delta arrow vs SPY
3. Long-Only Min Variance: Sharpe value + delta arrow vs SPY
Delta shows green up-arrow if better than benchmark, red down-arrow if worse.
Each card shows Return% and Volatility% in the label line.
ASSET STATISTICS:
Table showing per-asset statistics for the fixed set of asset classes.
Columns: Asset | Ticker | Ann.Return | Ann.Vol | Sharpe | VaR95 | CVaR95 | Skewness | Kurtosis | Max DD
PORTFOLIO SUMMARY TABLE:
Table with rows for each portfolio (LO Max Sharpe, LO Min Var, CO Max Sharpe, CO Min Var)
Columns: Portfolio | Return | Volatility | Sharpe | VaR95 | CVaR95 | Weight per asset
SECTION 1 — EFFICIENT FRONTIER:
Plotly chart showing:
- Long-Only frontier line (royalblue)
- Constrained 25% frontier line (darkorange)
- Star markers for Max Sharpe portfolios
- Diamond markers for Min Variance portfolios
- Individual asset scatter points with ticker labels
- Capital Market Line (gray dashed) from Rf through tangency
SECTION 1b — CUMULATIVE RETURNS:
Line chart showing growth of $1 for each selected asset.
One trace per asset with its assigned color.
Hover mode: x-unified (crosshair).
SECTION 2 — ALLOCATION COMPARISON:
Three horizontal bar charts side by side (Plotly subplots):
- Long-Only Max Sharpe allocation
- Constrained 25% Max Sharpe allocation
- Long-Only Min Variance allocation
Only show assets with weight > 0.5%. Bars sorted by weight ascending (largest at top).
SECTION 3 — CORRELATION MATRIX:
Heatmap with RdYlGn colorscale, values from -1 to 1.
Text annotations showing correlation coefficients rounded to 2 decimals.
Explicit colorscale array and autocolorscale:false to prevent color issues
when the asset selection changes. Clear innerHTML before replotting.
SECTION 4 — ALLOCATION VS TARGET RETURN:
Two stacked area charts side by side:
- Long-Only frontier: how allocation shifts as target return rises
- Constrained frontier: same with 25% cap
Red dashed vertical line at Max Sharpe return with annotation.
SECTION 7 — PORTFOLIO COMPOSITION ANALYSIS:
Three tables per window explaining the optimizer's decisions:
1. Included Assets (>0.5% weight):
Columns: Asset | Weight | Return | Vol | Sharpe | Role
Role is determined by logic:
- SR > 1.0 → "High Sharpe — primary return driver"
- SR > 0.5 → "Good risk-adjusted return"
- Vol < 8% → "Low volatility diversifier"
- Max corr with other included < 0.4 → "Low correlation diversifier"
- Else → "Balanced risk/return contribution"
2. Near-Threshold (0.01%–0.5% weight):
Columns: Asset | Weight | Sharpe | Why marginal
Reason: high correlation with included, low Sharpe, or marginal diversification benefit
3. Excluded Assets (top 5 by Sharpe):
Columns: Asset | Sharpe | Return | Vol | Reason
Reason: negative return, high correlation, low Sharpe, or inferior risk/return
================================================================================
5. JAVASCRIPT INTERACTIVITY
================================================================================
All JS is embedded in the HTML inside <script> tags.
Data passed from Python as JSON variables (embedded as const in the script).
WINDOW SWITCHING:
switchWindow(wn, btn) toggles .wc divs and button active state.
Triggers a resize event for Plotly charts.
PDF EXPORT:
Uses html2canvas to capture the entire page as a canvas.
Converts to PDF with jsPDF. Button fixed at bottom-right corner.
AUTO-OPEN:
After writing the HTML file to disk, the Python script should automatically
open it in the default browser using webbrowser.open().
================================================================================
6. FORMATTING SPECIFICATION
================================================================================
FONTS:
Primary: 'Inter' (loaded from Google Fonts CDN)
Fallback: Roboto, 'Source Sans Pro', sans-serif
Monospace (for data): 'SF Mono', 'Consolas', 'Menlo', monospace
DARK THEME COLOR PALETTE:
Backgrounds:
Body: #0B0F14
Section cards: #11161D
Inner panels / inputs: #151B23
Table header: #1A212B
Hover rows: #1F2630
Text:
Primary (headings): #E6EDF3
Secondary (body): #C9D1D9
Muted (labels): #9DA7B3
Dim (footnotes): #6E7681
Borders:
Card borders: #242B36
Input/panel borders: #2A323C
Accent / Interactive:
Primary accent: #4DA3FF (buttons, links, active states)
Accent hover: #3B8FE6
Active button text: #0B0F14 (dark text on accent bg)
Semantic:
Success / positive: #2ECC71
Error / negative: #FF5C5C
Warning: #F5A623
Gold (rank #1): #FFD700
Badges:
Green bg: rgba(46,204,113,.15) text: #2ECC71
Red bg: rgba(255,92,92,.15) text: #FF5C5C
PLOTLY CHART THEME:
template: 'plotly_dark'
paper_bgcolor: '#11161D'
plot_bgcolor: '#11161D'
font: family 'Inter, Roboto, sans-serif', color '#C9D1D9'
hoverlabel: bgcolor '#1A212B', bordercolor '#2A323C', font color '#E6EDF3'
Modebar: hidden by default, appears on chart hover (CSS opacity transition)
SECTION STYLING:
Each section card:
background: #11161D
border-radius: 10px
box-shadow: 0 2px 8px rgba(0,0,0,.25)
padding: 22px
margin-bottom: 22px
border: 1px solid #242B36
Section titles (h2):
border-left: 3px solid #4DA3FF
background: linear-gradient(90deg, rgba(77,163,255,.08) 0%, transparent 60%)
padding-left: 14px
KPI CARDS:
Grid: 3 columns, gap 12px
Each card:
background: #11161D
border-radius: 8px
border: 1px solid #242B36
border-left: 4px solid (accent color — green/blue/orange per card)
Value: font-size 1.5em, font-weight 700, color #E6EDF3
Label: font-size .72em, color #6E7681
Delta arrows: font-size .65em
Positive: green #2ECC71 with ▲
Negative: red #FF5C5C with ▼
WINDOW TOGGLE BUTTONS:
Pill group (first: left radius, last: right radius)
Border: 2px solid #4DA3FF
Inactive: transparent bg, #4DA3FF text
Active: #4DA3FF bg, #0B0F14 text
Hover (inactive): #1F2630 bg
TABLES:
Width 100%, border-collapse, font-size .8em
Header: background #1A212B, color #C9D1D9
Cells: padding 6px, border-bottom 1px solid #242B36, color #9DA7B3
Even rows: background #151B23
Hover: background #1F2630
BUTTONS:
Primary (opt-btn): background #4DA3FF, color #0B0F14, border-radius 6px
Hover: background #3B8FE6
Export (fixed bottom-right): same style with box-shadow and z-index 999
RESPONSIVE:
Max-width: 1300px centered
Grid columns collapse at 900px breakpoint
Tables have horizontal scroll wrapper (overflow-x: auto)
The prompt is organized like a Python script: numbered sections, explicit specifications,
no ambiguity. Save yours inside the project folder (for example as blueprint_prompt.txt)
so Claude can reference it directly.
With the prompt saved and the Python model in the same folder, tell Claude something like:
blueprint_prompt.txt and bs_model.py. Build a single
Python script that follows the blueprint exactly and generates a self-contained HTML
dashboard. Output the HTML file next to the script."
Claude will read both files, write the full generator script (data layer, computations, Plotly figures, embedded HTML/JS), and save it. Run the script, and you have a production-ready dashboard, usually on the first attempt.
Once the first version is live, refinements are quick. Ask Claude things like:
#1E56B8 and update all charts to match."Because every change is committed through Git, you can diff the new version against the previous one in seconds and roll back if anything breaks. This is what makes the workflow durable: it isn't one-shot code generation, it's a tight iteration loop between you and the model.
At this point, you should now be looking at a fully working MVO dashboard in your browser, complete with the efficient frontier, KPI cards, allocation charts, correlation matrix, and everything else. Exactly what the blueprint described, generated from one prompt.
The workflow doesn't stop there. The real power of this approach is that every new capability you want to add is just another blueprint prompt. You describe the module, point Claude at the existing dashboard, and it extends the code while keeping everything else intact.
As a concrete example, here's how I added an Interactive Constraint Simulator on top of the base MVO dashboard.
The base dashboard already shows the unconstrained max-Sharpe portfolio. By construction, that's the optimal risk-adjusted allocation given the covariance matrix and expected returns. So what happens if you don't want to hold 40% in gold? Or if your mandate requires at least 20% in bonds?
The constraint simulator lets you set a minimum and maximum weight per asset and re-runs the optimization under those bounds. The result is a direct visualization of a fundamental trade-off in portfolio construction:
The prompt below is what I sent Claude to build this module. Same structure as the MVO blueprint: layout, validation logic, JavaScript optimizer specs, visual styling. Claude implemented the whole thing in a single pass, covering both the Python side for HTML generation and the JavaScript side for the in-browser projected gradient optimizer.
CONSTRAINT SIMULATOR — DETAILED SPECIFICATION
===============================================
Add an Interactive Constraint Simulator section to the dashboard.
This section lets the user set minimum and maximum allocation bounds
for each asset class, then re-optimizes the portfolio under those
constraints and shows how it compares to the unconstrained baseline.
This must be added both to the Python code (HTML generation) and to
the JavaScript (client-side optimization and chart rendering).
================================================================================
1. PURPOSE
================================================================================
The simulator answers: "What happens to my portfolio if I require at least X%
in bonds, or cap equities at Y%?" The user sets min/max per asset, clicks
Optimize, and instantly sees the new Sharpe ratio, allocation, and efficient
frontier — all compared against the unconstrained baseline.
================================================================================
2. LAYOUT (two rows, each with two columns)
================================================================================
The section has a 2-column grid layout (class "cg"), stacked in two rows.
ROW 1 — LEFT COLUMN: Constraint Inputs
A list of input rows, one per asset class. Each row contains:
[Asset Name (colored)] Min [__0__] % Max [_100_] %
- Asset name is displayed in its assigned color for visual identification
- Min input: number field, default 0, range 0-100, step 1
- Max input: number field, default 100, range 0-100, step 1
- Both inputs use the dark theme styling (bg #151B23, border #2A323C)
- Input IDs follow pattern: mn_{window}_{assetIndex} and mx_{window}_{assetIndex}
- Below the inputs: an "Optimize" button (class opt-btn, accent blue)
ROW 1 — RIGHT COLUMN: Results Panel
Two sub-panels stacked vertically:
A) Result Card (class "rb"):
Shows after clicking Optimize:
- Title: "Constrained Portfolio"
- Return: XX.XX% | Volatility: XX.XX%
- Sharpe: X.XXX (large, bold)
- vs Baseline: badge showing delta
Green badge if Sharpe improved: "+0.XXX (+X.X%)"
Red badge if Sharpe decreased: "-0.XXX (-X.X%)"
- Baseline reference: "Baseline (Long-Only): X.XXX"
B) Allocation Bar Chart (Plotly):
Horizontal bar chart showing:
- Constrained weights as colored bars (one per asset)
- Baseline weights as diamond markers overlaid
- Red dotted lines at min/max bounds for each asset
- Title: "Constrained Max Sharpe (SR=X.XXX) vs Baseline (X.XXX)"
- Height: 420px
ROW 2 — LEFT COLUMN: Efficient Frontier Comparison
Plotly chart showing:
- Baseline long-only frontier (royalblue line)
- Custom constrained frontier (amber/orange line, only if constraints differ)
- Capital Market Line (gray dashed)
- Baseline Max Sharpe marker (royalblue star)
- Baseline Min Variance marker (royalblue diamond)
- Custom Max Sharpe marker (amber star, only if constrained)
- Individual asset scatter points with ticker labels
- Title: "Efficient Frontier: Baseline vs Custom"
- Height: 450px
ROW 2 — RIGHT COLUMN: Allocation vs Target Return
Stacked area chart showing how asset allocation shifts across the
custom-constrained frontier:
- X-axis: Achieved Return (%)
- Y-axis: Allocation (%), range 0-100
- One colored area per asset, stacked
- Red dashed vertical line at Max Sharpe return
- Annotation showing "Max SR=X.XXX" at the vertical line
- Title: "Custom: Allocation vs Target Return"
- Height: 450px
- If not enough feasible points: show "Not enough feasible points" message
================================================================================
3. VALIDATION
================================================================================
Before running optimization, validate:
- For each asset: min must be <= max (otherwise show error with asset name)
- Sum of all minimums must be <= 100% (otherwise impossible to satisfy)
- Display errors in the result panel with red border and red text
If all constraints are at defaults (min=0, max=100 for every asset),
skip the optimization and use the precomputed baseline weights directly.
================================================================================
4. JAVASCRIPT OPTIMIZATION ENGINE
================================================================================
Since the optimization runs client-side in the browser, implement these
functions in JavaScript (cannot use scipy in the browser):
PORTFOLIO MATH HELPERS:
pR(w, D): portfolio return = sum(w[i] * D.mean_ret[i])
pV(w, D): portfolio volatility = sqrt(sum over i,j of w[i]*w[j]*D.cov_mat[i][j])
pS(w, D): portfolio Sharpe = (pR(w,D) - D.rf) / pV(w,D)
WEIGHT PROJECTION (project function):
Given weights w, min bounds mn, max bounds mx:
1. Clamp each w[i] to [mn[i], mx[i]]
2. Iteratively redistribute excess/deficit to maintain sum(w) = 1
3. Repeat up to 500 iterations until sum is within 1e-10 of 1.0
MAX SHARPE OPTIMIZER (optMS):
Projected gradient ascent on Sharpe ratio:
1. Start from multiple initial points (equal-weight, tilted toward highest-return asset)
2. For each starting point, run 10,000 gradient steps:
- Compute gradient of Sharpe w.r.t. weights analytically:
g[i] = (mean_ret[i] * vol - (ret - rf) * (sum_j cov[i][j] * w[j]) / vol) / vol^2
- Step: w[i] += learning_rate * g[i]
- Learning rate: 0.008 / (1 + iteration * 0.0005) (decaying)
- Project weights back onto feasible set after each step
3. Keep the solution with highest Sharpe across all starting points
MIN VARIANCE OPTIMIZER (optMinVar):
Projected gradient descent on variance:
1. Start from equal-weight (clamped to bounds)
2. Run 8,000 gradient steps:
- Gradient: g[i] = 2 * sum_j(cov[i][j] * w[j])
- Step: w[i] -= learning_rate * g[i]
- Learning rate: 0.005 / (1 + iteration * 0.001)
- Project after each step
TARGET RETURN OPTIMIZER (optTargetReturn):
Augmented Lagrangian method:
1. Minimize variance subject to portfolio return = target
2. Use Lagrange multiplier lambda and quadratic penalty mu=200
3. Gradient: g[i] = 2*sW[i] + (lambda + 2*mu*retError) * mean_ret[i]
4. Update lambda: lambda += mu * retError * 0.004
5. Run 8,000 iterations with decaying learning rate
FRONTIER COMPUTATION (computeFrontier):
1. Find min-variance portfolio (left anchor)
2. Compute maximum feasible return given the bounds
3. Generate 50 target returns between anchors
4. For each target, run optTargetReturn
5. Filter out infeasible or non-monotonic points
6. Return arrays of returns, volatilities, and weight vectors
================================================================================
5. DATA REQUIREMENTS
================================================================================
The simulator needs per-window data passed from Python as JSON:
SIM[window] = {
names: [...], // asset names
tickers: [...], // ticker symbols
colors: [...], // hex colors
mean_ret: [...], // annualized expected returns (decimal)
cov_mat: [[...]], // annualized covariance matrix
rf: float, // risk-free rate (decimal)
n: int, // number of assets
baseline_weights: [...], // unconstrained LO Max Sharpe weights
baseline_sharpe: float, // unconstrained LO Max Sharpe Sharpe ratio
baseline_ef_vols: [...], // baseline efficient frontier volatilities (%)
baseline_ef_rets: [...], // baseline efficient frontier returns (%)
baseline_ms: {vol, ret, sharpe}, // Max Sharpe portfolio stats
baseline_mv: {vol, ret, sharpe}, // Min Variance portfolio stats
asset_vols: [...], // individual asset volatilities (%)
asset_rets: [...], // individual asset returns (%)
}
================================================================================
6. INITIALIZATION
================================================================================
On page load:
- Build the constraint input rows for each window (3Y, 5Y, 10Y)
- Run runOpt() for each window with default values (min=0, max=100)
- This pre-populates the result panel and charts with baseline data
The inputs are rebuilt per window because each window may have different
assets available (though with fixed assets this is the same set).
================================================================================
7. VISUAL STYLE
================================================================================
Follow the same dark theme as the rest of the dashboard:
- Input rows: flex layout with gap, label width ~150px
- Labels colored per asset (using each asset's assigned color)
- Inputs: 65px wide, centered text, dark bg (#151B23), border #2A323C
- Result card: bg #151B23, border #242B36, border-radius 8px, padding 16px
- Result title: color #4DA3FF
- Badge: green bg rgba(46,204,113,.15) for positive, red rgba(255,92,92,.15) for negative
- All Plotly charts use the standard dark layout:
template plotly_dark, paper/plot_bgcolor #11161D, font Inter #C9D1D9
- Constraint bound lines on allocation chart: red dotted (#ef4444 and #991b1b)
Same IDE, same Git workflow. The new section plugs into the existing dashboard without touching any of the baseline logic, and the constraint simulator is now live on the MVO dashboard. Try capping an asset you know the optimizer loves and you'll see Sharpe drop, then watch the optimizer shift capital toward the runner-up.
You've now seen the full loop: a Python model, a structured blueprint prompt, Claude generating the dashboard, and a second prompt extending it with a new analytical module. The same pattern applies to any financial model you want to productize.
Starting from an existing Python implementation is enough to build a derivatives calculator, a portfolio optimizer, a backtesting engine, a risk dashboard, or anything else you can describe in detail. Each new capability you want to add is just another blueprint: write the spec, hand it to Claude, review the diff, commit, repeat. That iteration loop is what turns a one-off model into a living product that keeps improving as your ideas sharpen.
The same workflow generated every interactive tool on this site. Explore the dashboards built with this approach: