JPMorgan Software Engineering Simulation
Completed
Coursework · Data

JPMorgan Software Engineering Simulation

Virtual experience — four functions against a supplied codebase · 2024

A Forage virtual-experience exercise, not client work: JPMorgan publishes starter repos with the interesting parts stubbed out and you fill them in. All three tasks completed against a provided codebase — the market simulator, the React scaffold, the Perspective wiring and the test harness all arrive intact. What the tasks add is a price-feed client that derives mid prices and their ratio from the supplied server, its unit tests, the switch from a single fetch to a streaming interval, and a rebuilt Perspective schema that computes the bid-ask ratio with fixed bounds and emits an alert only on breakout.

Built with
TypeScriptTypeScript
ReactReact
PythonPython
PerspectivePerspective
Project Details

STATUS
Completed
ROLE

Virtual experience — four functions against a supplied codebase

YEAR

2024

TYPE

Coursework · Data

TAGS
Fintech
Data Visualization

A Forage virtual experience programme, not client work. JPMorgan Chase publishes starter repos with the interesting parts stubbed out and you fill them in. Three tasks, all completed.

Being clear about the split matters, because most of what is in these three repositories was written by JPMorgan. The market simulator, the React scaffold, the Perspective wiring and the test harness all arrive intact; what is missing is a handful of functions and a handful of configuration lines, each marked with a comment telling you to update it. What the exercise teaches is not how to build the system but how to read one you did not write and find the four places that matter.

The simulator you are handed

server3.py is the most substantial file in the set and none of it is yours to write, which is why it is worth understanding. It is a self-contained market: a bounded random walk generates a price series, an order generator turns each tick into bids and asks around it, and an order book accumulates them with a per-order age so stale levels expire. Orders clear against each other through a comparison operator passed in as an argument, so the same clearing routine handles both sides of the book by being given operator.ge or its opposite. The simulation covers five years from a market open pinned at half past midnight today, and it can either replay from a generated CSV or run in real time.

The HTTP layer under it is about thirty lines: a @route decorator that stamps a path pattern onto a method, a do_GET that walks the handler class looking for a method whose pattern matches the request path, and a BaseHTTPRequestHandler served on port 8080. One route, /query, returns the current top of book for two stocks as JSON.

The three tasks

Task 1 — the price feed. The provided server3.py is a threaded HTTP market simulator serving quotes on /query, generating a five-year walk with spreads between 2.0 and 6.0 and prices between 60 and 150. The work was on the client side: getDataPoint derives a mid price from top_bid and top_ask, getRatio divides one stock's price by the other, and the poll loop runs 500 requests against the server. Unit tests in client_test.py cover the normal case and the inverted case where the bid comes in above the ask.

That second test is the point of the task. The naive implementation of a price is the ask, or the last trade, and both break when the book crosses. Defining price as the midpoint means the inverted case produces a sane number rather than an exception or a nonsense one, and the two tests assert exactly that by computing the expected midpoint independently of the function under test.

Task 2 — the viewer. A React/TypeScript app wrapping a FINOS perspective-viewer. The table schema is declared up front (stock, top_ask_price, top_bid_price, timestamp), the viewer is configured as a y_line chart pivoted by stock on columns and timestamp on rows, with per-field aggregates. App.tsx was changed from a single fetch to a 100 ms setInterval that streams new data into state continuously and clears itself after 1000 ticks.

The duplicate-row problem in that task is subtler than it looks. Perspective's table is append-only from componentDidUpdate, so if the component holds a growing array and pushes all of it on every tick, each row is inserted again on every render. The fix is upstream in the state update, replacing the array rather than concatenating, and the aggregate settings on the viewer then collapse repeated timestamps rather than drawing them twice.

Task 3 — the alert chart. The schema is rebuilt around what a trader actually watches: price_abc, price_def, ratio, upper_bound, lower_bound and trigger_alert. DataManipulator.generateRow computes both mid prices, divides them, fixes the bounds at plus and minus 5%, and emits trigger_alert only when the ratio breaks out of that band, leaving it undefined otherwise so the alert line stays blank until something is worth looking at. The viewer plots ratio, both bounds and the alert on one axis.

The row also has to choose one timestamp from two quotes, and it takes the later of the pair, so a stale quote on one leg does not drag the series backwards. The stock pivot from task 2 is commented out rather than deleted, because the chart is now one derived series rather than two instruments.

Honest notes

Two rough edges are still in the checked-in code. The columns attribute in task 3 is a string with an unbalanced quote around trigger_alert, which Perspective parses more forgivingly than it should. And DataStreamer opens its XMLHttpRequest with the async flag set to false, so every one of those 100 ms polls blocks the main thread while it waits; that comes with the scaffold and I did not change it, but on a real feed it is the first thing that would have to go.

This is coursework, and the write-up should not pretend otherwise. The engineering content is four small functions and about a dozen configuration lines against a codebase somebody else designed. What it is genuinely good for is the shape of the problem: bid-ask midpoints, a ratio between two instruments, a fixed band around it, and an alert that fires only on breakout.

Project Details

STATUS
Completed
ROLE

Virtual experience — four functions against a supplied codebase

YEAR

2024

TYPE

Coursework · Data

TAGS
Fintech
Data Visualization