Securities Platform

Phase 1 Documentation — Data Layer, Database & REST API
224 GB
Database Size
1.6B
Tick Data Rows
1,000
Securities
5 Years
History Depth
9
API Endpoints

1. Overview

A securities data platform built on TimescaleDB (PostgreSQL 16 + time-series extension) with a FastAPI REST layer. The database contains 224 GB of realistic financial data spanning 1,000 securities across 5 years (2020–2025).

Technology Stack

Database

TimescaleDB (PostgreSQL 16) running in Docker. Hypertable for tick data with 1-month chunk intervals.

API

FastAPI (Python) with SQLAlchemy. Auto-generated Swagger docs. 9 REST endpoints.

Data Generator

Python scripts using NumPy/Pandas. SQL server-side generation for tick data via generate_series.

Infrastructure

Docker Compose for database orchestration. Python venv for API dependencies.

Variance Scenarios Embedded in Data

2. Data Summary

Scale Tables (224 GB)

TableRowsDescription
tick_data1,600,268,67020-second intraday ticks (TimescaleDB hypertable)
pricing_history1,369,000Daily OHLCV for 1,000 securities, 5 years
holdings_history1,040,440Daily portfolio snapshots across 7 portfolios
transactions786,091Trade history (~50 trades/security/year)
variance_quarterly29,40021 quarterly attribution snapshots

Core Reference Tables

TableRowsDescription
securities_master1,000550 equity, 290 bonds, 90 derivatives, 45 structured, 25 other
issuer209Issuers across 15 countries
portfolio73 Equity, 2 Fixed Income, 1 Mixed, 1 Hedge
holdings_period_1156Snapshot at 2024-12-31
holdings_period_2167Snapshot at 2025-03-31
pricing_period_1120Period 1 prices (original 120 securities)
pricing_period_2120Period 2 prices
variance_summary169Period-over-period attribution
risk_metrics74VaR, beta, convexity
credit_ratings61Rating history with upgrades/downgrades
fx_rates26Currency rates to USD
corporate_actions12Splits, dividends, coupons, maturities

3. Project Structure

securities-platform/
├── api/
│   ├── main.py                  # FastAPI app entry point
│   ├── deps.py                  # Database session dependency
│   ├── models/
│   │   └── schemas.py           # Pydantic response schemas
│   └── routers/
│       ├── securities.py        # /api/securities endpoints
│       ├── portfolios.py        # /api/portfolios, /api/holdings
│       ├── variance.py          # /api/variance endpoints
│       └── reference.py         # /api/corporate-actions, /api/fx-rates
├── database/
│   ├── schema.sql               # Core table definitions (13 tables)
│   ├── schema_scale.sql         # Scale tables (tick_data, pricing_history, etc.)
│   ├── indexes.sql              # Performance indexes
│   └── loader.py                # CSV-to-PostgreSQL loader
├── generator/
│   ├── generate_data.py         # Original 120-security generator
│   └── generate_scale_data.py   # Scale generator (1,000 securities, 224 GB)
├── docker/
│   └── docker-compose.yml       # TimescaleDB service
├── data/
│   ├── schema/                  # Source schema files
│   └── generated/               # Generated CSV files
├── .env                         # Environment configuration
├── .env.example                 # Template
└── requirements.txt             # Python dependencies

4. Getting Started

Prerequisites

Step 1: Start the Database

cd /Users/imac/securities-platform
docker compose -f docker/docker-compose.yml up -d

Verify it's running:

docker exec securities-db psql -U securities_user -d securities_platform \
  -c "SELECT pg_size_pretty(pg_database_size(current_database()));"

Step 2: Start the API Server

cd /Users/imac/securities-platform
source venv/bin/activate
uvicorn api.main:app --host 0.0.0.0 --port 8000

The API will be available at http://localhost:8000.

Step 3: Verify

curl http://localhost:8000/api/health
# {"status":"ok","service":"securities-platform","version":"1.0.0"}

Interactive API Docs

When the API is running, FastAPI provides auto-generated interactive documentation:

5. API Reference

Base URL: http://localhost:8000

GET /api/health
Health check. Returns service status.
curl http://localhost:8000/api/health

# Response:
{"status": "ok", "service": "securities-platform", "version": "1.0.0"}

Securities

GET /api/securities
List securities with optional filters.
ParameterTypeDefaultDescription
security_typestringEQUITY, BOND, OPTION, FUTURE, CLO, ABS
sectorstringIssuer sector: Technology, Healthcare, Financials, etc.
currencystringUSD, EUR, GBP, JPY, etc.
limitint100Max results (up to 500)
offsetint0Pagination offset
# All equities (first 10)
curl "http://localhost:8000/api/securities?security_type=EQUITY&limit=10"

# Technology sector in USD
curl "http://localhost:8000/api/securities?sector=Technology&currency=USD"

# Page 2
curl "http://localhost:8000/api/securities?limit=100&offset=100"
GET /api/securities/{security_id}
Get a single security by ID.
curl http://localhost:8000/api/securities/SEC-0001

# Response:
{
  "security_id": "SEC-0001",
  "isin": "US0000000001",
  "ticker": "TECH_A",
  "security_name": "TechCorp Alpha",
  "security_type": "EQUITY",
  "asset_class": "Equity",
  "currency": "USD",
  "exchange": "NYSE",
  "issuer_id": "ISS-001",
  "status": "ACTIVE"
}

Portfolios

GET /api/portfolios
List all 7 portfolios.
curl http://localhost:8000/api/portfolios
GET /api/holdings
List holdings for a portfolio at a given period.
ParameterTypeDefaultDescription
portfolio_idstringe.g. PORT-001
periodint11 = 2024-12-31, 2 = 2025-03-31
limitint100Max results (up to 500)
# PORT-001 holdings at period 1
curl "http://localhost:8000/api/holdings?portfolio_id=PORT-001&period=1"

# Period 2 top holdings by market value
curl "http://localhost:8000/api/holdings?period=2&limit=20"

Variance Attribution

GET /api/variance/portfolio/{portfolio_id}
Portfolio-level totals + security-level attribution breakdown.
curl http://localhost:8000/api/variance/portfolio/PORT-001

# Response:
{
  "summary": {
    "portfolio_id": "PORT-001",
    "total_mv_start": 125000000.00,
    "total_mv_end": 131500000.00,
    "total_price_effect": 4200000.00,
    "total_qty_effect": 1100000.00,
    "total_fx_effect": -300000.00,
    "total_corp_action_effect": 1500000.00,
    "total_pnl": 6500000.00
  },
  "details": [ ... ]
}
GET /api/variance/top-movers
Securities with the largest absolute P&L impact.
ParameterTypeDefaultDescription
limitint20Max results (up to 100)
curl "http://localhost:8000/api/variance/top-movers?limit=5"

Reference Data

GET /api/corporate-actions
List corporate actions (splits, dividends, coupons, maturities).
ParameterTypeDefaultDescription
security_idstringFilter by security
action_typestringSTOCK_SPLIT, DIVIDEND, COUPON, MATURITY
curl http://localhost:8000/api/corporate-actions
curl "http://localhost:8000/api/corporate-actions?action_type=DIVIDEND"
GET /api/fx-rates
List FX exchange rates.
ParameterTypeDefaultDescription
from_currencystringFilter by source currency (e.g. EUR)
rate_datestringFilter by date (e.g. 2024-12-31)
curl http://localhost:8000/api/fx-rates
curl "http://localhost:8000/api/fx-rates?from_currency=GBP"

6. Direct Database Access

Connect via psql

# Through Docker
docker exec -it securities-db psql -U securities_user -d securities_platform

# From host
psql -h localhost -p 5432 -U securities_user -d securities_platform
# Password: securities_pass

Connection String

postgresql://securities_user:securities_pass@localhost:5432/securities_platform

Useful Queries

Database Info

-- Database size
SELECT pg_size_pretty(pg_database_size(current_database()));

-- Row counts for all tables
SELECT schemaname, relname, n_live_tup
FROM pg_stat_user_tables
WHERE schemaname = 'public'
ORDER BY n_live_tup DESC;

-- Table sizes
SELECT tablename,
       pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename))
FROM pg_tables
WHERE schemaname = 'public'
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;

Securities & Pricing

-- Securities by type
SELECT security_type, count(*) FROM securities_master GROUP BY security_type;

-- Daily price for a security
SELECT price_date, close_price, volume
FROM pricing_history
WHERE security_id = 'SEC-0001'
ORDER BY price_date DESC
LIMIT 10;

-- Intraday ticks for a security on a specific day
SELECT tick_time, price, bid, ask, volume
FROM tick_data
WHERE security_id = 'SEC-0001'
  AND tick_time >= '2025-03-28 09:30:00'
  AND tick_time <  '2025-03-28 16:00:00'
ORDER BY tick_time;

Portfolio & Holdings

-- Portfolio holdings on a date
SELECT h.security_id, sm.security_name, h.quantity, h.market_value, h.weight
FROM holdings_history h
JOIN securities_master sm ON h.security_id = sm.security_id
WHERE h.portfolio_id = 'PORT-001'
  AND h.as_of_date = '2025-03-31'
ORDER BY h.market_value DESC
LIMIT 20;

-- Quarterly variance for a portfolio
SELECT quarter_label, SUM(total_pnl) as total_pnl,
       SUM(price_effect) as price_eff, SUM(fx_effect) as fx_eff
FROM variance_quarterly
WHERE portfolio_id = 'PORT-001'
GROUP BY quarter_label
ORDER BY quarter_label;

Transactions

-- Top 10 trades by value
SELECT t.security_id, sm.security_name, t.txn_type,
       t.quantity, t.price, t.quantity * t.price as trade_value
FROM transactions t
JOIN securities_master sm ON t.security_id = sm.security_id
ORDER BY t.quantity * t.price DESC
LIMIT 10;

7. Database Schema

Core Tables schema.sql

TableColumns
issuerissuer_id, issuer_name, country, sector, industry, lei
securities_mastersecurity_id, isin, cusip, sedol, ticker, security_name, security_type, asset_class, currency, exchange, issuer_id (FK), issue_date, maturity_date, coupon_rate, status
portfolioportfolio_id, portfolio_name, portfolio_type, benchmark, inception_date, currency
holdings_period_1/2holding_id, security_id, portfolio_id, quantity, market_value, weight, cost_basis, accrued_interest, as_of_date
pricing_period_1/2pricing_id, security_id, price_date, open_price, high_price, low_price, close_price, volume, vwap
transactionstxn_id, security_id, portfolio_id, txn_type, quantity, price, txn_date, settlement_date, fees, currency
fx_ratesrate_id, from_currency, to_currency, rate_date, rate
credit_ratingsrating_id, issuer_id, rating, rating_date, agency, action
corporate_actionsaction_id, security_id, action_type, ex_date, record_date, payment_date, ratio, amount
risk_metricsmetric_id, security_id, portfolio_id, var_95, var_99, beta, convexity, as_of_date, ...
variance_summarysummary_id, portfolio_id, security_id, period_from, period_to, mv_start, mv_end, price_effect, qty_effect, fx_effect, corp_action_effect, total_pnl

Scale Tables schema_scale.sql

TableColumnsNotes
pricing_historysecurity_id, price_date, open_price, high_price, low_price, close_price, volume, vwapStandard table
tick_datatick_time, security_id, price, bid, ask, bid_size, ask_size, volume, trade_count, vwapTimescaleDB hypertable, 1-month chunks
holdings_historysecurity_id, portfolio_id, as_of_date, quantity, market_value, weight, cost_basis, accrued_interestStandard table
variance_quarterlyportfolio_id, security_id, quarter_label, period_from, period_to, mv_start, mv_end, price_effect, qty_effect, fx_effect, corp_action_effect, total_pnlStandard table

Indexes

-- Core indexes (indexes.sql)
idx_sm_type, idx_sm_currency, idx_sm_issuer, idx_sm_isin, idx_sm_cusip, idx_sm_sedol
idx_hp1_security, idx_hp1_portfolio, idx_hp2_security, idx_hp2_portfolio
idx_pp1_security, idx_pp2_security
idx_txn_security, idx_txn_portfolio, idx_txn_date
idx_cr_issuer, idx_ca_security, idx_rm_security, idx_rm_portfolio
idx_vs_portfolio, idx_vs_security

-- Scale indexes
idx_ph_sec_date   ON pricing_history(security_id, price_date)
idx_ph_date       ON pricing_history(price_date)
idx_ph_sid        ON pricing_history(security_id)
idx_td_sec        ON tick_data(security_id, tick_time DESC)
idx_hh_port_date  ON holdings_history(portfolio_id, as_of_date)
idx_hh_sec        ON holdings_history(security_id)
idx_vq_port       ON variance_quarterly(portfolio_id, quarter_label)

8. Configuration

Environment Variables (.env)

VariableDefaultDescription
POSTGRES_USERsecurities_userDatabase username
POSTGRES_PASSWORDsecurities_passDatabase password
POSTGRES_DBsecurities_platformDatabase name
POSTGRES_HOSTlocalhostDatabase host
POSTGRES_PORT5432Database port
API_HOST0.0.0.0API bind address
API_PORT8000API port

Python Dependencies

fastapi==0.115.6
uvicorn[standard]==0.34.0
sqlalchemy==2.0.36
psycopg2-binary==2.9.10
pandas==2.2.3
numpy==2.2.1
openpyxl==3.1.5
alembic==1.14.0
python-dotenv==1.0.1

9. Regenerating Data

To regenerate from scratch (warning: takes ~6 hours for full dataset):

cd /Users/imac/securities-platform
source venv/bin/activate

# Step 1: Generate original 120-security base data
python generator/generate_data.py

# Step 2: Load base data into database
python database/loader.py

# Step 3: Generate scale data (1,000 securities, 224 GB)
python generator/generate_scale_data.py

The scale generator is resumable — if interrupted, it will skip completed phases and continue tick data from the last completed security.