bazzi24/streamflow

By bazzi24

Updated 3 months ago

Real-time stock market data processing system uses Spark, Kafka, and MySQL

Image
Message queues
API management
Databases & storage
1

694

bazzi24/streamflow repository overview

StreamFlow Banner

StreamFlow — Real-Time Vietnamese Stock Market Dashboard

Python FastAPI React Kafka Apache Spark MySQL Docker License


Features

Bloomberg-style iBoardDense dark terminal board — 27 columns, 9 segment tabs, real-time flash updates
TradingView ChartsCandlestick + volume histogram via lightweight-charts v4 with MA overlays
Market BreadthLive advances / declines for VNINDEX, VN30, HNXIndex, HNX30
Low-latency PipelineSSI WebSocket → Kafka KRaft → MySQL streaming tables → FastAPI → WebSocket
OHLCV Aggregation1m/1d candlesticks computed in real-time by CandlestickConsumer
Star-schema DWSpark ETL → warehouse.dim.* + warehouse.fact.* for historical queries
JWT AuthenticationSecure watchlists, per-user watchlists persisted in api DB
WebSocket Live FeedPer-symbol and market-wide push updates with 3s auto-reconnect
VI / EN Language SwitcherFull i18n via react-i18next — language persisted to localStorage

Quick Start

1. Clone & configure
git clone https://github.com/bazzi24/streamflow.git
cd streamflow
cp .env.example .env

Fill in .env with your SSI credentials and secrets:

consumerID=<your_ssi_consumer_id>
consumerSecret=<your_ssi_consumer_secret>
url=https://fc-data.ssi.com.vn/
stream_url=https://fc-datahub.ssi.com.vn/

# REQUIRED — generate with: python -c "import secrets; print(secrets.token_hex(32))"
SECRET_KEY=<your-secret-key>

# Required credentials — use root for dev, create a limited app user in production
DB_USER=root
DB_PASSWORD=<your_password>

# CORS — comma-separated, no spaces
CORS_ORIGINS=http://localhost:3000
2. Install Python dependencies
uv venv
source .venv/bin/activate
uv sync
3. Download MySQL JDBC driver
mkdir -p lib
curl -L -o lib/mysql-connector-j-8.0.33.jar \
  "https://repo1.maven.org/maven2/com/mysql/mysql-connector-j/8.0.33/mysql-connector-j-8.0.33.jar"
4. Launch everything
docker compose -f docker/docker-compose.yaml up -d

Wait for services to become healthy:

docker compose -f docker/docker-compose.yaml ps
# mysql   healthy
# kafka   running
# api     running
# frontend running

Open http://localhost:3000 — the Bloomberg-style board is live.

First time? Start the Kafka producers to begin ingesting market data:

docker compose -f docker/docker-compose.yaml run --rm producer-trade     # X-TRADE:ALL
docker compose -f docker/docker-compose.yaml run --rm producer-quote    # X-QUOTE:ALL
docker compose -f docker/docker-compose.yaml run --rm producer-index    # MI:ALL
docker compose -f docker/docker-compose.yaml run --rm producer-foreign # R:ALL
docker compose -f docker/docker-compose.yaml run --rm producer-status  # F:ALL

Architecture

StreamFlow Architecture


Tech Stack

LayerTechnology
Data sourceSSI Securities WebSocket API
Message brokerApache Kafka 3.x (KRaft, no ZooKeeper)
Raw storageMySQL 8.0.39 (data DB)
DW storageMySQL 8.0.39 (warehouse DB, star schema)
ETLApache Spark 3.5 (PySpark)
APIFastAPI + Python 3.12 + aiokafka
FrontendReact 18 + Vite + TypeScript + Tailwind CSS
ChartsTradingView lightweight-charts v4
i18nreact-i18next (VI + EN, persisted to localStorage)
ContainersDocker Compose

Services

ServiceDescriptionPort
kafkaKafka KRaft broker9092
kafka-uiKafka UI dashboard8080
mysqlMySQL 8.0.39 (data + warehouse DBs)3306
spark-masterApache Spark master7077, 8082
spark-workerSpark worker (2 cores, 4 GB)
apiFastAPI REST + WebSocket8000
frontendReact SPA served via Nginx80→3000
producerAll 5 Kafka producers (SSI channels)
consumerAll 6 Kafka consumers (5 topics + OHLCV)

API Reference

Base: http://localhost:8000/api/v1

REST Endpoints
EndpointMethodAuthDescription
/auth/registerPOSTRegister user
/auth/loginPOSTLogin → JWT (24h)
/users/meGETJWTCurrent user
/users/me/watchlistGET/PUTJWTWatchlist CRUD
/stocksGETAll symbols + latest prices
/stocks/{symbol}GETSymbol metadata
/stocks/{symbol}/quoteGETLive bid/ask from data DB
/stocks/{symbol}/orderbookGETTop 3 bid/ask levels
/stocks/{symbol}/ohlcvGETIntraday OHLCV (?interval=1m)
/stocks/{symbol}/historyGETDaily OHLCV (?days=30)
/market/overviewGETIndices + top gainers/losers
/healthGETHealth check
WebSocket Endpoints
EndpointAuthDescription
/ws/stocks/{symbol}OptionalPer-symbol live updates
/ws/marketOptionalMarket-wide updates

Message types: price_update · orderbook_update · index_update · candlestick_update


Database Schema

Two MySQL databases — data (raw + reference + candlestick) and warehouse (star-schema DW).

data — Raw + reference + candlestick
TableSource / WriterPK + Index
data_trademarket_data_trade topicPRIMARY KEY (id), INDEX(symbol, trading_date)
data_quotemarket_data_quote topicPRIMARY KEY (id), INDEX(symbol_id, trading_date)
index_dataindex_data topicPRIMARY KEY (id), INDEX(index_id, trading_date)
foreign_roomforeign_room_data topicPRIMARY KEY (id), INDEX(symbol, trading_date)
securities_statussecurities_status topicPRIMARY KEY (id), INDEX(symbol_id, trading_date)
candlestick_1mCandlestickConsumerPRIMARY KEY (symbol, time_start)
candlestick_1dCandlestickConsumerPRIMARY KEY (symbol, trading_date)

candlestick_1m is the source of truth for 1m OHLCV; larger timeframes are derived at query time.

warehouse — Star-schema DW
TableKey / Composite PK columns
datetradingdate_key (date needs backticks)
timetime_key (time needs backticks)
symbolsymbol_key
market_indexindex_key
exchangeexchange_key
tradingsessiontrading_session_key
stockorderbooktradingdate_key, time_key, symbol_key, exchange_key, session_key
stocktradesame as stockorderbook
marketindexsame as above

Environment Variables

All secrets are loaded from .env via env_file in docker-compose. The FastAPI service requires the variables below.

Required (app fails fast if missing)
VariableExamplePurpose
SECRET_KEYb7649d31d... (hex 64-char token)JWT signing — never commit a default
DB_USERroot (dev) / streamflow_app (prod)MySQL username
DB_PASSWORDMySQL password

Generate a secure SECRET_KEY:

python -c "import secrets; print(secrets.token_hex(32))"
Optional (have sensible defaults)
VariableDefaultPurpose
CORS_ORIGINShttp://localhost:3000Comma-separated allowed origins
consumerIDSSI API consumer ID
consumerSecretSSI API consumer secret
urlhttps://fc-data.ssi.com.vn/SSI REST API base
stream_urlhttps://fc-datahub.ssi.com.vn/SSI WebSocket base
RAW_DB_URLjdbc:mysql://mysql:3306/data?...Spark reads data DB
DW_DB_URLjdbc:mysql://mysql:3306/warehouse?...Spark writes warehouse DB
KAFKA_BOOTSTRAP_SERVERSkafka:9092All Kafka clients
SPARK_MASTER_URLspark://spark-master:7077spark-submit target
MYSQL_JAR/streamflow/lib/mysql-connector-j-8.0.33.jarJDBC driver path
Production: Limited-privilege MySQL user

Run this in MySQL to create a read-write app user (replace the password):

-- In docker exec mysql, or via any mysql client:
CREATE USER IF NOT EXISTS 'streamflow_app'@'%' IDENTIFIED BY '<strong-password>';
GRANT SELECT, INSERT, UPDATE, DELETE ON `data`.* TO 'streamflow_app'@'%';
GRANT SELECT, INSERT, UPDATE, DELETE ON `warehouse`.* TO 'streamflow_app'@'%';
FLUSH PRIVILEGES;
-- Then update .env:
-- DB_USER=streamflow_app
-- DB_PASSWORD=<strong-password>

Stopping

docker compose -f docker/docker-compose.yaml down            # stop containers (data persists)
docker compose -f docker/docker-compose.yaml down -v          # stop + remove volumes (data loss)

License

MIT License — bazzi24

Tag summary

Content type

Image

Digest

sha256:5e0294b41

Size

470 MB

Last updated

3 months ago

docker pull bazzi24/streamflow