Personal project · 2025

MockMarket.

Stack
React · Node · C++ · Docker
Year
2025
Role
Solo · design to deploy
Source
github.com/alexgaffen/MockMarket ↗
Services
3 containers
Starting cash
$100,000
Signal
Buy · Sell · Hold
Data window
60d prices · 7d news
01

A trading floor with nothing to lose

Mock Market is a paper-trading site: sign in with Google, get $100,000 of virtual cash, and buy and sell real tickers at live prices. I built it to practise investing without paying for the lessons, and to build a real multi-service system end to end instead of another single-page app.

Search finds any listed company, holdings keep their average cost, and the portfolio re-prices itself every minute. Every ticker also has an Analyze button that returns a Buy, Sell or Hold call with its reasoning, built from a C++ indicator engine and an LLM reading the news.

$100KVirtual cash
60sRe-price interval
3Services
Mock Market portfolio page with a buy panel and a holdings table
Portfolio view: holdings, average cost and live equity, re-priced every 60 seconds.
02

Three services, one network

Each part runs in its own container on one Docker Compose network. The React client owns the UI and talks to Firebase for Google sign-in and the user’s Firestore document: cash balance plus holdings. Everything that needs an API key goes through the Express API, so no secret ever reaches the browser. It proxies Finnhub for search, quotes and logos, pulls price history from Yahoo Finance, and runs the analysis.

The maths lives in a C++ service with no web framework at all: a raw TCP socket that parses just enough HTTP to read a JSON body of prices and answer with the indicators. Keeping it separate means the numeric code can be swapped or scaled without touching the API.

DOCKER COMPOSE · INVESTOR-NETFirebaseGOOGLE SIGN-IN · FIRESTOREFinnhub · YahooQUOTES · NEWS · HISTORYGemini 2.5 FlashJSON-MODE VERDICTReact clientUI · PORTFOLIO · :80Express APIORCHESTRATION · :5000C++ engineRSI · SMA · BANDS · :8080RESTTCP
Service map. Solid boxes are the three containers; dashed boxes are the managed services they call.
03

Hard math, soft news

Analyze runs a five-step pipeline for each ticker:

  1. Express pulls 60 days of daily closes from Yahoo Finance and the last week of company news from Finnhub.
  2. The closes go to the C++ engine as JSON over a socket.
  3. The engine computes a 14-day RSI with Wilder smoothing, the 20-day SMA and Bollinger Bands at two standard deviations, and returns them as a ready-to-read block of text.
  4. Gemini 2.5 Flash, prompted as a portfolio manager, weighs those numbers against the five latest headlines.
  5. It answers in JSON mode with a decision and four bullets of reasoning, which the client shows beside a buy box.

The split plays to each side’s strengths. Deterministic code does the arithmetic, which language models are unreliable at, and the model does what code can’t: read the news. The prompt spells out the rules of thumb (RSI over 70 is overbought, under 30 oversold) and lets strong news override them.

Analyze dialog for GOOG showing a BUY call and four bullets of reasoning
The Analyze view: the call, the price, and four bullets citing the indicators and the news.
04

Any dependency can fail. The page can’t.

One analysis touches four outside services, so each one has a defined fallback. The user always gets an answer, with a plain note about what was missing, never an error page.

DependencyFailureWhat the user gets
C++ engineOffline or refusesAnalysis continues on news alone, and says so
GeminiAPI error or bad JSONA safe HOLD with “review the indicators” bullets
Yahoo FinanceNo price historyUNKNOWN with a data-error note; no AI call made
Finnhub newsRequest fails“No specific news found”; analysis still runs
Finnhub searchRequest failsPortfolio search falls back to a local symbol match
05

Containers, Compose and CI

Each service has its own Dockerfile and image. One docker compose up builds all three and starts them on a shared bridge network, the API after the engine, with API keys passed in from the environment and never committed.

A GitHub Actions workflow builds every image on each push and pull request to main, so a broken Dockerfile shows up in review, not on deploy day.

What I’d harden next

  • Add health checks so the API waits for the engine to be ready, not just started.
  • Move the client’s hard-coded API address into per-environment config.
  • Settle trades server-side in a Firestore transaction. Today the client reads, then writes, so two open tabs could spend the same cash.
  • Emit structured logs and per-dependency latency, so the fallbacks above show up on a dashboard, not only in the UI.
06

See it run

Screen recording of Mock Market in use.
Source codeView Mock Market on GitHub↗

More work
to explore.

← Back to Portfolio