Skip to content

rollroyces/ELI_DEAL

Repository files navigation

HK ELI Portfolio Manager (YFinance + Flask)

An interactive web app for managing Hong Kong Equity-Linked Investment (ELI) deals with live price updates from Yahoo Finance via a Flask API. Create and edit ELI deals (single or basket up to 4 HK stocks), visualize allocations and performance with charts, and track barrier risks and alerts. Backend persists deals in MySQL.

Demo screenshots

  • UI built with a clean dashboard, deals management, analytics, alerts, and settings views.

Features

  • Add, edit, delete HK ELI deals (1–4 underlying HK stocks per deal)
  • Live HK stock price refresh from Yahoo Finance (via yfinance)
  • Resilient price fetching — exponential backoff on rate limits
  • Finnhub fallback for HK stock prices when yfinance is unavailable
  • Persistent price cache (SQLite) survives server restarts
  • Automatic deal status calculation — barrier breach, approaching maturity, settled
  • API key protection for write endpoints (optional, configurable)
  • Restricted CORS — configurable allowed origins
  • Risk analytics: HK market concentration, barrier risk exposure, diversification
  • Charts: allocation doughnut, performance bars, stock-count distribution
  • Alerts: barrier proximity/breach, approaching maturity, notifications popover
  • Import/Export: CSV and JSON; sync and load deals from MySQL
  • Docker support for easy deployment

Tech stack

  • Frontend: Vanilla JS (app.js), HTML (index.html), CSS (style.css), Chart.js, PapaParse, Font Awesome
  • Backend: Python Flask (server.py), Flask-CORS, SQLAlchemy, MySQL, yfinance, Finnhub API

Project structure

ELI_DEAL/
  index.html         # App shell and views (Dashboard, Deals, Analytics, Alerts, Settings)
  style.css          # Design system + application styles
  app.js             # Frontend state, UI rendering, charts, alerts, API calls
  server.py          # Flask API: deals CRUD, price endpoints, DB models
  yf_resilient.py    # yfinance rate-limit resilient wrapper with Finnhub fallback
  finnhub_client.py  # Finnhub API client for HK stock price fallback
  tests/
    test_api.py      # Unit tests for API endpoints
  Dockerfile         # Docker image for the API server
  docker-compose.yml # Full stack (API + MySQL) deployment
  .env.example       # Environment variable template
  requirements.txt   # Python dependencies

Prerequisites

  • Python 3.10+
  • MySQL 8.x (or compatible)
  • Node is NOT required (static frontend)

Python packages

Install with pip (recommended to use a virtualenv):

pip install -r requirements.txt

Configuration

Environment variables

The backend reads config from environment variables. Copy .env.example and fill in your values:

cp .env.example .env
Variable Required Description
DB_USER MySQL username
DB_PASS MySQL password
DB_HOST MySQL host (e.g. 127.0.0.1)
DB_PORT MySQL port (default 3306)
DB_NAME Database name (e.g. hk_eli_db)
CORS_ORIGINS Comma-separated allowed origins
ELI_API_KEY API key for write protection
CACHE_TTL_SECONDS Price cache TTL (default 180s)
FINNHUB_API_KEY Finnhub API key for price fallback

Setting up the database

CREATE DATABASE IF NOT EXISTS hk_eli_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

Running

Option 1: Local (Docker)

docker-compose up --build

This starts both MySQL and the Flask API on port 8000.

Option 2: Local (manual)

# 1. Set environment variables
export DB_USER=myuser
export DB_PASS=mypassword
export DB_HOST=127.0.0.1
export DB_PORT=3306
export DB_NAME=hk_eli_db

# 2. Install dependencies
pip install -r requirements.txt

# 3. Start the Flask API
python server.py

This starts the API at http://localhost:8000 and auto-creates tables if they don't exist.

3) Open the frontend

Open index.html in a browser (double-click or serve statically). By default, the frontend points to http://localhost:8000.

If you need to change the API base URL, edit app.js:

const API_BASE_URL = 'http://localhost:8000';

For hosting the frontend elsewhere, set API_BASE_URL accordingly or switch to window.location.origin when both are served together.


API Endpoints

Method Endpoint Auth Description
GET /api/status No Health check
GET /api/price?symbol=0700.HK No Single price
POST /api/prices No Batch prices {symbols: [...]}
GET /api/deals No List deals
POST /api/deals ✅ Key Create deal
PUT /api/deals/:id ✅ Key Update deal
DELETE /api/deals/:id ✅ Key Delete deal
POST /api/deals/sync ✅ Key Batch sync
POST /api/deals/import ✅ Key Import JSON/CSV

Auth: If ELI_API_KEY is set, write endpoints require X-API-Key header or api_key query param.

API Key

Generate a secure key:

python -c "import secrets; print(secrets.token_hex(32))"

Examples

# Health check
curl http://localhost:8000/api/status

# Batch prices
curl -X POST http://localhost:8000/api/prices \
  -H 'Content-Type: application/json' \
  -d '{"symbols":["0700.HK","9988.HK"]}'

# Create deal (with API key)
curl -X POST http://localhost:8000/api/deals \
  -H 'Content-Type: application/json' \
  -H 'X-API-Key: your-secret-key' \
  -d '{
    "dealName":"ELI Demo",
    "investmentDate":"2025-08-29",
    "maturityDate":"2026-05-01",
    "nominalAmount":100000,
    "purchasePrice":98000,
    "numberOfStocks":2,
    "couponRate":12.5,
    "settlementMethod":"Worst Performer Physical",
    "issuer":"Credit Suisse",
    "underlyingAssets":[
      {"symbol":"0941.HK","name":"China Mobile","strikePrice":86.35,"barrierLevel":86.35,"weight":0.5},
      {"symbol":"2883.HK","name":"China Oilfield Services Limited","strikePrice":6.74,"barrierLevel":6.74,"weight":0.5}
    ]
  }'

Usage guide

  1. Go to the "ELI Deals" view and click "Add New HK ELI Deal".
  2. Select 1–4 HK stocks, input strike and barrier. Weights are supported for baskets.
  3. Save — the app persists to DB and refreshes live prices.
  4. Use "Portfolio Analytics" for charts; "Alerts" for barrier/maturity alerts; "Settings" to refresh now, change intervals, and import/export.

Import/Export:

  • Export CSV/JSON from Settings.
  • Import CSV/JSON using the "Import" buttons. The app will parse and refresh prices.
  • "Save to Database" and "Load from Database" sync UI with MySQL.

Auto-refresh:

  • Prices auto-refresh based on the "Auto Refresh Interval" (default 4 hours). Manual refresh is available in Settings.

HKEX Market hours indicator:

  • Shows whether HKEX is currently open based on HKT time.

Running tests

cd /path/to/ELI_DEAL
python -m pytest tests/ -v

Tests use an in-memory SQLite database — no real MySQL needed.


Data model (simplified)

Deal

  • id (int, PK)
  • dealName (string)
  • investmentDate (date)
  • maturityDate (date)
  • nominalAmount (float)
  • purchasePrice (float)
  • numberOfStocks (int, 1–4)
  • couponRate (float)
  • settlementMethod (string)
  • issuer (string)
  • status (string: auto-calculated)

DealAsset

  • deal_id (FK -> Deal, ondelete=CASCADE)
  • symbol (e.g. 0700.HK)
  • name (string)
  • strikePrice (float)
  • barrierLevel (float)
  • weight (float 0..1)

Note: Frontend runtime fields (currentPrice, currentValue, pnl) are calculated client-side from live prices.

Auto status logic

Status is automatically calculated based on:

  1. Maturity date passed → Settled
  2. Any underlying stock at or below barrier level → Knock-in Triggered
  3. Within 30 days of maturity → Approaching Maturity
  4. Otherwise → Active

Security

  • Never commit real DB credentials. Use environment variables or .env file.
  • API key: Set ELI_API_KEY to protect write endpoints from unauthorized access.
  • CORS: Restrict via CORS_ORIGINS env var (default: localhost:8000).
  • debug=False: Production-mode Flask (no debug console).
  • Backend runs on port 8000 (frontend connects via API).

Deployment notes

  • Frontend: Any static host (GitHub Pages, Netlify, S3) can serve index.html, style.css, and app.js.
  • Backend: Use Docker for easy deployment, or run behind gunicorn/uvicorn + nginx.
  • MySQL: Ensure network connectivity between API server and MySQL instance.

License

MIT


Disclaimer

This application is for educational and portfolio tracking purposes only and does not constitute financial advice. Market data may be delayed or inaccurate; verify independently before making investment decisions.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors