PAC Investment Analysis Webapp
Project Overview
This project is a web application that demonstrates the long-term impact of investing via a PAC (Capital Accumulation Plan / dollar-cost averaging). Users can set investment parameters, run historical simulations, and visualize outcomes across rolling periods using real market data.
The frontend is built with Next.js and Velite, delivering a responsive UI where users configure:
- Market index (ticker)
- Initial capital
- Monthly contribution (min/max range)
- Investment duration (years)
- Inflation (annual %)
Once parameters are set, the frontend calls a FastAPI backend that runs the core simulation. The engine uses monthly returns derived from Adjusted Close prices to simulate PAC behavior with inflation, and returns:
- Series of period outcomes (invested vs. non-invested final values)
- Probability of negative periods
- Average capital gain (%) across all rolling windows
- A PNG chart of the invested vs. non-invested trajectories
Historical data is sourced from Yahoo Finance (see “Data Pipeline”) to ensure realistic, reproducible simulations.
Architecture at a Glance
-
Frontend: Next.js + Velite
- Collects inputs via controlled forms with client-side validation.
- Calls the backend via typed fetch helpers.
- Renders numerical metrics and dynamic charts.
-
Backend: FastAPI (single-file service)
- Versioned API:
/api/v1
- Endpoints:
GET /api/v1/health
GET /api/v1/tickers
GET /api/v1/pac/simulate
GET /api/v1/pac/summary
GET /api/v1/pac/image
(PNG chart)
- CORS configured for the frontend origin(s).
- Pure ASGI app served behind WSGI (PythonAnywhere) using a thin ASGI→WSGI adapter.
- Versioned API:
-
Data Pipeline (Yahoo Finance)
- Offline ingestion from Yahoo Finance (e.g., via
yfinance
or direct CSV) to a normalized JSON snapshot (all_yahoo_data.json
). - The API reads from the snapshot at runtime (no third-party calls in the hot path).
- Columns are normalized to select a robust price series (prefers Adjusted Close) and compute monthly returns.
- Offline ingestion from Yahoo Finance (e.g., via
This split is deliberate: ingest and normalize once; serve many. It removes runtime dependency on external rate limits, improves latency, and makes results reproducible.
Technical Details
Simulation Model
- Price series: prefers Adjusted Close; falls back to Close if needed.
- Monthly returns: computed from the selected price series.
- PAC schedule: monthly contribution linearly ramps from
pacStart
topacStop
acrossyears
. - Inflation: applied as a monthly debit to both the invested and non-invested baselines.
- Rolling windows: evaluates all valid start months for the chosen horizon; reports distributional outcomes.
REST API Design
- Versioning:
/api/v1/...
to enable non-breaking iteration. - Contract: Pydantic models for request/response; OpenAPI generated by FastAPI.
- Validation: strict query constraints (
ge
,le
) for numeric inputs; ticker whitelist from the datastore. - Idempotent GETs: simulations are read-only and cacheable by parameters.
- CORS: explicit allow-list of frontend origins; credentials disabled unless strictly needed.
- Errors: semantic HTTP codes (
400
for invalid tickers/params); structured JSON problem details. - Performance:
- Lazy, cached load of the snapshot file.
- Prefer pagination for large series (future enhancement on
/simulate
). - Pre-aggregate summaries where feasible.
- Observability:
- Add structured logging (request id, params, latency).
- Health/readiness checks (
/health
) for deployment automation.
- Security:
- Input normalization and strict types; reject unknown params.
- Limit response size for very large payloads (server-side caps).
- Rate-limit public endpoints if exposed on the open internet.
Frontend Engineering
- Typed API client: generate TypeScript types from the OpenAPI schema to keep frontend and backend in lockstep.
- Input hygiene: debounced inputs, sane defaults, and client-side bounds mirroring server constraints.
- Caching: cache
/tickers
and deterministic simulations (query-keyed) to avoid redundant calls. - Charts: pre-format series on the client; keep the PNG endpoint as a fallback (server-side render) for easy export.
Data & Yahoo Finance Integration
- Ingestion job (separate from the web app):
- Fetch tickers and historical OHLCV via Yahoo Finance.
- Normalize schema; prefer Adjusted Close; record the source timestamp.
- Write an immutable, versioned snapshot (e.g.,
all_yahoo_data-YYYYMMDD.json
) and symlinkall_yahoo_data.json
→ latest.
- Quality gates:
- Validate date monotonicity; drop non-trading days with no prices.
- Detect and log missing data; exclude tickers without a usable price column.
- Reproducibility:
- Pin data vintages for experiments.
- Consider columnar formats (Parquet) for faster cold starts on larger datasets.
Why This Design
- Stability under load: no live third-party calls in request path; deterministic CPU-bound simulation.
- Reproducible research: data snapshots make results auditable.
- Evolution-friendly: API versioning, typed contracts, and a clear ingest/serve boundary let you iterate without breaking consumers.