Summary

The provided explanation of the workflow

The trading application developed by Jeter and Sakib showcases a well-architected full-stack system combining an Angular frontend with a Spring Boot backend. The sequence diagrams clearly illustrate key workflows such as authentication, portfolio management, stock buying/selling, and watchlist handling. Authentication is implemented using JWT tokens alongside Bcrypt hashing for password security, enabling secure, stateless communication between the client and server. The backend follows best practices with JPA for database operations and Jackson for JSON processing, while Swagger API documentation ensures clarity and ease of testing. Additionally, both the frontend and backend are containerized using Docker, making deployment consistent, scalable, and environment independent.
The team also followed a structured development workflow by using Linear and GitHub Issues along with pull requests on GitHub, helping simulate a production-style environment with organized task tracking and code reviews. On the frontend, the application features a clean and interactive dashboard that visualizes market data, charts, and top movers effectively. The system incorporates stock data seeding, caching, and live price polling to deliver near real-time updates with optimized performance. Features like virtual trading with an initial balance, portfolio tracking, and smooth UI interactions create a realistic trading experience. Overall, the project demonstrates strong system design, secure authentication practices, seamless frontend-backend integration, and modern DevOps practices through Docker, resulting in a polished and production-ready application.

System Architecture

Angular 21 frontend communicates with Spring Boot 4 backend via REST. The backend connects to MySQL and two external APIs — Finnhub for live prices and Yahoo Finance for historical data. All stock prices are cached to reduce API calls.

Angular 21 Frontend Home · Portfolio · Search · Settings Standalone components · Bootstrap 5 · Chart.js HTTP REST / JSON Spring Boot 4 — REST Controllers + Services Auth login · signup User CRUD Account balance · CRUD Holding buy · sell · get Transaction save · get Watchlist add · remove Market quote · history · seed SignupService BCrypt auth AccountService balance ops HoldingService buy/sell logic MarketService cache reads only FinnhubService live sell price YahooFinanceService 1yr OHLC history StockDataSeeder GET /market/seed — Finnhub ×131 symbols (1100ms delay) GET /market/seed/history — Yahoo Finance ×131 symbols (1000ms delay) MySQL — financeDb users id · email · password accounts cash_balance holdings qty · avg_buy_price transactions BUY/SELL · total watchlist symbol per user stock_cache current · open · high · low stock_history_cache symbol · time_interval · history_json LONGTEXT · cached_at · expires_at (240 min TTL) Finnhub API Free tier · 60 req/min · /quote?symbol= Yahoo Finance API Free · no key · User-Agent header required

Authentication & Registration Flow

Full swimlane showing login with all alternative paths — user not found, wrong password, and successful authentication. Also covers registration with success and failure outcomes including auto-account creation with $10,000 starting balance.

User Angular Frontend Spring Boot MySQL Open app Show Login page (/login) Enter email + password POST /api/auth/login SignupService.login() SELECT * FROM users WHERE email=? alt [user not found] throw: User not found (500) Show: Would you like to register? Click Sign up Redirect to /signup alt [wrong password] BCrypt.matches() = false (500) Details incorrect error shown alt [authenticated] Return {userId, email} 200 Save userId to localStorage Redirect to /home dashboard Registration Flow Fill signup form (email, pw, confirm) POST /api/auth/signup SignupService.register() existsByEmail? alt [email already exists] EmailAlreadyExistsException Registration failed — check details alt [registration success] INSERT users row INSERT INTO users INSERT account ($10,000) INSERT INTO accounts You are registered! Redirect to /login $10,000 balance assigned

Full Transaction Workflow

Complete swimlane covering portfolio load, buy, sell, watchlist management, market data with cache hit/miss, 20-second live polling, and admin seeding endpoints.

User Frontend Backend External API Database Load Portfolio Navigate to /portfolio GET /account/{userId} AccountService SELECT accounts GET /holdings/{userId} HoldingService SELECT holdings GET /market/quote/{sym} x n MarketService (cache only) SELECT stock_cache Render dashboard + charts Buy Stock Search symbol + set qty POST /holdings/buy HoldingService.buyStock() SELECT stock_cache price alt [insufficient funds] Insufficient funds (500) Purchase failed error alt [success] Deduct cash balance UPDATE accounts INSERT/UPDATE holdings INSERT transaction (BUY) Success + portfolio reloads Sell Stock Enter qty — click Sell POST /holdings/sell HoldingService.sellStock() Finnhub live price alt [not enough shares] Not enough shares (500) Sell error shown alt [success] Add proceeds to balance UPDATE accounts UPDATE/DELETE holdings INSERT transaction (SELL) Portfolio reloads Watchlist Management Select Add to Watchlist POST /api/watchlist WatchlistController existsByUserIdAndSymbol? alt [already watching] 400 Bad Request Already in watchlist error alt [success] INSERT watchlist row INSERT INTO watchlist Added to watchlist! Click Remove DELETE /watchlist/{id} Delete by ID DELETE FROM watchlist Market Data Select symbol — view chart GET /market/history/{sym} YahooFinanceService SELECT stock_history_cache alt [cache miss / expired 240 min] Fetch Yahoo Finance API Yahoo Finance API INSERT stock_history_cache alt [cache hit] Return cached JSON Render Chart.js line graph Live Price Polling — every 20 seconds GET /market/quote/{symbol} MarketService.getOrFetch() SELECT stock_cache Update price display + chart Admin — Seed Stock Cache GET /market/seed StockDataSeeder.seedAll() Finnhub x 131 symbols INSERT stock_cache GET /market/seed/history StockDataSeeder .seedHistory() Yahoo Finance ×131 symbols INSERT stock_history_cache

Database ERD

All 7 tables with fields, primary keys, foreign keys, and relationships. USERS is the central entity — accounts, holdings, transactions, and watchlist all link via user_id.

USERS 🔑 id : BIGINT AUTO_INCREMENT PK email : VARCHAR(255) UNIQUE NOT NULL password : VARCHAR(255) NOT NULL created_at : DATETIME(6) NOT NULL last_login : DATETIME(6) ACCOUNTS 🔑 id : BIGINT AUTO_INCREMENT PK 🔗 user_id : BIGINT UNIQUE NOT NULL first_name : VARCHAR(255) NOT NULL last_name : VARCHAR(255) NOT NULL cash_balance : DECIMAL(10,2) NOT NULL WATCHLIST 🔑 id : BIGINT AUTO_INCREMENT PK 🔗 user_id : BIGINT NOT NULL stock_symbol : VARCHAR(255) NOT NULL added_at : DATETIME(6) UNIQUE(user_id, stock_symbol) HOLDINGS 🔑 id : BIGINT AUTO_INCREMENT PK 🔗 user_id : BIGINT NOT NULL stock_symbol : VARCHAR(255) NOT NULL quantity : DECIMAL(10,4) NOT NULL avg_buy_price : DECIMAL(10,2) NOT NULL purchased_at : DATETIME(6) TRANSACTIONS 🔑 id : BIGINT AUTO_INCREMENT PK 🔗 user_id : BIGINT NOT NULL stock_symbol : VARCHAR(255) NOT NULL price : DECIMAL(10,2) NOT NULL quantity : DECIMAL(10,4) NOT NULL total_amount : DECIMAL(10,2) (auto) type : ENUM('BUY','SELL') NOT NULL created_at : DATETIME(6) STOCK_CACHE 🔑 symbol : VARCHAR(255) PK company_name : VARCHAR(255) current_price : DECIMAL(10,2) open_price : DECIMAL(10,2) high_price : DECIMAL(10,2) low_price : DECIMAL(10,2) volume : BIGINT updated_at : DATETIME(6) STOCK_HISTORY_CACHE 🔑 id : BIGINT AUTO_INCREMENT PK symbol : VARCHAR(255) NOT NULL time_interval : VARCHAR(255) NOT NULL history_json : LONGTEXT cached_at : DATETIME(6) expires_at : DATETIME(6) UNIQUE(symbol, time_interval) 1 : 1 users.id → accounts.user_id 1 : many users.id → watchlist.user_id 1 : many users.id → holdings.user_id 1 : many users.id → transactions.user_id holdings.stock_symbol → stock_cache.symbol transactions.stock_symbol → stock_cache.symbol watchlist.stock_symbol → stock_cache.symbol 1 : many stock_cache.symbol → history_cache.symbol FK relationship (logical) Logical reference (no FK constraint)