
Personal project · 2025
MockMarket.
- Services
- 3 containers
- Starting cash
- $100,000
- Signal
- Buy · Sell · Hold
- Data window
- 60d prices · 7d news
Project overview
01A 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.

Architecture
02Three 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.
The signal
03Hard math, soft news
Analyze runs a five-step pipeline for each ticker:
- Express pulls 60 days of daily closes from Yahoo Finance and the last week of company news from Finnhub.
- The closes go to the C++ engine as JSON over a socket.
- 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.
- Gemini 2.5 Flash, prompted as a portfolio manager, weighs those numbers against the five latest headlines.
- 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.

Reliability
04Any 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.
| Dependency | Failure | What the user gets |
|---|---|---|
| C++ engine | Offline or refuses | Analysis continues on news alone, and says so |
| Gemini | API error or bad JSON | A safe HOLD with “review the indicators” bullets |
| Yahoo Finance | No price history | UNKNOWN with a data-error note; no AI call made |
| Finnhub news | Request fails | “No specific news found”; analysis still runs |
| Finnhub search | Request fails | Portfolio search falls back to a local symbol match |
Shipping it
05Containers, 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.
Demo
06See it run
More work
to explore.