Back to projects

Product question

How can complex portfolio analysis become understandable without pretending that a projection is advice?

The prototype makes assumptions visible, separates analysis from decisions and keeps user data local. It is designed as an exploration of the product and technical questions—not as a live advisory service.

Next.jsReactTypeScriptRechartsPostgreSQLFastAPI

Portfolio analytics

Net worth, allocation, returns, drawdowns, benchmark comparison and risk metrics.

Projection engine

Deterministic scenarios, Monte Carlo percentile bands and target-achievement analysis.

Data workflows

Manual entries, CSV exports and an Interactive Brokers import preview with duplicate detection.

Local persistence

A PostgreSQL-backed local prototype with API routes and a separate FastAPI contract scaffold.

Implementation map

One interface, three clear layers.

The current local build uses Next.js API routes for persistence. The Python service remains a scaffold for a future backend split.

InterfaceDashboard and workflow viewscomponents/WealthApp.tsx
Domain logicAnalytics and projectionslib/finance.ts
PersistenceLocal APIs and PostgreSQLapp/api/* · db/schema.sql
Future service boundaryFastAPI contract scaffoldbackend/app/main.py

Selected code

The implementation behind the product claims.

Short excerpts from the supplied project code. They are included to make the work inspectable without publishing the full private repository.

TypeScriptlib/finance.ts
export function calculateRiskMetrics(history, benchmark, baseCurrency) {
  const portfolioReturns = calculateReturns(
    mapHistoryToCurrency(history, baseCurrency)
      .map((point) => point.portfolio)
  );

  const annualizedVolatility =
    standardDeviation(portfolioReturns) * Math.sqrt(12);

  const sharpe =
    annualizedVolatility === 0
      ? 0
      : (annualizedReturn - riskFreeRate) / annualizedVolatility;

  return { annualizedReturn, annualizedVolatility, sharpe, maxDrawdown };
}
Pythonbackend/app/main.py
@app.post("/projections/run")
def run_projection(request: ProjectionRequest) -> dict[str, object]:
    return {
        "scenario_id": request.scenario_id,
        "points": deterministic_projection(request),
    }

What the build exposed

Financial decision support is mostly a trust-design problem.

  • Assumptions, currencies and data freshness must remain visible.
  • A single point forecast is less honest than scenarios and percentile bands.
  • Imports need preview, validation and duplicate handling before they change the portfolio.
  • LLM-generated interpretation should explain analysis, not turn uncertainty into automated advice.

This project is for informational and educational purposes only. It does not provide financial, investment, tax or legal advice.