MarketStream is a Java 17 Swing coursework application demonstrating a real-time financial market dashboard built with producer-consumer concurrency, background indicator calculation, dynamic custom chart rendering, validation, monitoring and graceful shutdown.
The application uses hypothetical stock quotes for supported symbols and calculates Simple Moving Average (SMA) and Exponential Moving Average (EMA) values from retained in-memory history.
The project addresses a multithreaded desktop-dashboard problem requiring:
- a responsive Java Swing GUI,
- a dedicated producer thread for generated market data,
- a bounded thread-safe queue,
- a dedicated consumer thread,
- safe Swing EDT updates,
- historical data retention,
- background financial indicator calculation,
- thread-pool management,
- progress, cancellation, monitoring and graceful shutdown.
- Professional black, white and grey Swing dashboard.
- Start/Stop controls for the quote feed.
- Adjustable quote-generation delay.
- Bounded
ArrayBlockingQueuewith backpressure. - Live quote table with formatted prices and changes.
- Thread-safe retained historical data per symbol.
- O(n) SMA and EMA algorithms.
- Configurable background indicator executor.
- Progress reporting and
Futurecancellation. - Dynamic custom chart for Price, SMA and EMA.
- Latest-price marker and automatic chart scaling.
- Input validation with subtle field highlighting.
- Queue-pressure display.
- Non-modal thread monitor dialog.
- Graceful shutdown of producer, consumer, indicator executor and monitor timer.
- Java 17
- Java Swing
- Maven
- FlatLaf
- JUnit 5
ArrayBlockingQueueThreadPoolExecutor- MVC-inspired package separation
- Custom Swing painting
The controller coordinates Swing components and concurrency services. Background services do not access Swing directly; they report events to DashboardController, which marshals UI updates onto the Swing EDT.
See docs/ARCHITECTURE.md for diagrams covering the main runtime architecture, quote flow, indicator flow and shutdown flow.
| Thread | Responsibility |
|---|---|
| Swing EDT | Handles UI events, component mutation, painting and monitor timer events. |
MarketStream-Producer |
Generates stock quotes and inserts them into the queue. |
MarketStream-Consumer |
Removes quotes from the queue and stores history. |
MarketStream-Indicator-* |
Runs background SMA/EMA tasks. |
MarketStream-Shutdown |
Performs bounded service termination waits during window close. |
Start Feed
↓
StockDataProducer generates quotes
↓
QuoteQueueManager stores bounded queue items
↓
StockDataConsumer takes quotes
↓
HistoricalDataStore retains history
↓
Swing EDT updates table and statistics
Indicator workflow:
Calculate Indicators
↓
Swing EDT validates input
↓
HistoricalDataStore returns defensive price snapshot
↓
IndicatorExecutorManager runs IndicatorTask
↓
IndicatorEngine calculates SMA and EMA
↓
IndicatorResult returned
↓
Swing EDT updates result panel and chart
StockDataProducer uses StockPriceGenerator to generate hypothetical quotes for AAPL, GOOG and MSFT. Quotes are placed into QuoteQueueManager, which wraps an ArrayBlockingQueue.
StockDataConsumer removes quotes with take(), stores them in HistoricalDataStore, and notifies the controller. Producer and consumer lifecycle methods are cooperative and interrupt blocking operations during shutdown.
HistoricalDataStore is separate from the queue. The queue is temporary transport; history is retained after consumption for indicator calculations.
The store:
- groups quotes by supported symbol,
- uses
ReentrantReadWriteLock, - returns defensive unmodifiable snapshots,
- enforces a per-symbol retention limit from
UIConstants.MAX_HISTORICAL_QUOTES_PER_SYMBOL.
IndicatorEngine.calculateSma(List<Double>, int) uses a sliding window:
- one running sum,
- add newest price,
- subtract price leaving the window,
- divide by period after a full window exists.
Leading unavailable positions are Double.NaN.
Complexity:
- Time: O(n)
- Auxiliary calculation space: O(1), excluding the returned list
IndicatorEngine.calculateEma(List<Double>, int) uses:
multiplier = 2.0 / (period + 1.0)
The first EMA value is initialized from the SMA of the first complete period. Later values use the recursive EMA formula. Leading unavailable positions are Double.NaN.
Complexity:
- Time: O(n)
- Auxiliary calculation space: O(1), excluding the returned list
IndicatorExecutorManager owns a configurable fixed-size ThreadPoolExecutor. Worker threads are named with the MarketStream-Indicator- prefix.
The configured worker count controls pool capacity for concurrent indicator tasks. A single SMA/EMA calculation is sequential and does not claim to use every worker in the pool.
IndicatorTask implements Callable<IndicatorResult>, reports staged progress and supports cooperative cancellation.
Swing components are updated only from:
- Swing event handlers,
- Swing
Timerevents, - methods invoked through
SwingUtilities.invokeLater, - view methods already called on the EDT.
Producer, consumer and indicator worker classes do not import or mutate Swing components.
StockChartPanel renders real data from IndicatorResult:
- Price line,
- SMA line,
- EMA line,
- latest-price marker,
- dynamic Y-axis scaling,
- latest 60 aligned points,
- NaN-safe indicator segments.
The chart refreshes after an explicit indicator calculation. It does not continuously recalculate indicators for every new quote.
InputValidator validates numeric fields and produces natural-language messages. IndicatorSettingsPanel highlights invalid fields with subtle red styling and tooltips.
ErrorMessageMapper maps expected exceptions to safe GUI messages and avoids exposing stack traces or raw exception class names to the user.
ThreadMonitorDialog is a non-modal Swing dialog available from View → Thread Monitor.
It displays:
- Swing EDT responsibility,
- producer state,
- consumer state,
- queue usage,
- indicator executor pool statistics,
- current indicator task state,
- historical quote count,
- completed indicator task count.
The dialog receives immutable ThreadMonitorSnapshot values from the controller and refreshes with a Swing Timer only while visible.
Window close triggers a safe shutdown path:
- status changes to
SHUTTING_DOWN, - action controls are disabled,
- thread monitor dialog/timer is disposed,
- producer and consumer are stopped,
- active indicator task is cancelled,
- indicator executor is shut down,
- bounded termination waits run on
MarketStream-Shutdown, - the frame is disposed on the Swing EDT.
No production code uses Thread.stop().
src/main/java/com/marketstream/
Main.java
concurrency/ producer, consumer, queue, indicator task and executor
controller/ dashboard coordination and EDT-safe delivery
model/ immutable domain and monitoring models
service/ stock generation, historical storage, indicators
util/ constants, validation, component helpers, error messages
view/ main frame and thread monitor dialog
view/chart/ chart data mapping helpers
view/components/ Swing dashboard panels
src/test/java/com/marketstream/
concurrency/ lifecycle, pipeline, executor and stress tests
model/ immutable model validation tests
service/ algorithm and history tests
util/ validation tests
view/ Swing dialog/component tests
view/chart/ chart mapping tests
Build the project:
mvn clean packageLaunch the GUI:
mvn exec:javaMain class:
com.marketstream.Main
The generated standard Maven JAR is located under target/. It is not configured as a standalone shaded executable JAR.
Run all automated tests:
mvn clean testRun package verification, including tests:
mvn clean packageManual GUI verification should be completed with docs/MANUAL_TEST_CHECKLIST.md.
Use docs/SCREENSHOT_CHECKLIST.md for report screenshots, including:
- initial dashboard,
- feed running,
- validation error,
- background calculation,
- completed result,
- dynamic chart,
- thread monitor,
- test/build terminal output.
- Quotes are hypothetical and generated locally; there is no external stock API.
- Data is stored in memory only; there is no database.
- The chart does not export images.
- There is no CSV or PDF report export.
- The application supports SMA and EMA only.
- Indicator recalculation is explicit, not continuous for every new quote.
- The Maven JAR is not a shaded standalone executable.
See docs/REQUIREMENTS_TRACEABILITY.md for the final requirement-by-requirement implementation matrix.
MarketStream is ready for final coursework verification and release preparation. Automated tests cover core models, algorithms, queue behaviour, producer/consumer lifecycle, historical storage, indicator executor behaviour, chart data mapping and monitoring support. Final manual evidence should be collected using the provided checklists before submission.