Dashboard Automation with Claude

Turn any Python financial model into a fully interactive, self-contained HTML dashboard, all generated by Claude from a single structured prompt.

Overview

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.

Why this works: financial dashboards follow predictable patterns (KPI cards, tables, charts, toggles). Once you give Claude a well-structured prompt that specifies the layout, color theme, and computation logic, it can generate thousands of lines of clean, working code in a single pass.

Prerequisites

You'll need three tools installed before starting. Each one plays a distinct role in the workflow.

Claude

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 Code
VS Code

Free 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 Code
Git

Version 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 Git

1

Start from an existing Python model

Dashboard 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.


2

Open the project in VS Code and launch Claude

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 ..


3

Write the blueprint prompt

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 (click to expand, ~350 lines)
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.


4

Ask Claude to execute the blueprint

With the prompt saved and the Python model in the same folder, tell Claude something like:

"Read 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.


5

Iterate: Claude handles the tweaks

Once the first version is live, refinements are quick. Ask Claude things like:

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.


6

Extend the dashboard with new modules

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.

What the constraint simulator does

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 prompt (click to expand)
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.


Conclusion

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 workflow in one sentence: write the math once in Python, describe every new feature as a detailed prompt, let Claude do the engineering, and use Git to review every change. Everything on this website was built that way.

Where to go from here

The same workflow generated every interactive tool on this site. Explore the dashboards built with this approach:

David Arias, CFA
Written and Modelled by

David Arias, CFA

Licensed portfolio manager with 4+ years of experience, specializing in emerging markets private debt, derivatives, and quantitative finance.

Let's Connect