Case study

Robinhood performance dashboard

A correct record from a messy export.

Takes a raw brokerage CSV and turns it into a correct, per-trade profit-and-loss record by pairing every buy fill with the sell fill that closes it.

The work here is data correctness, not trading. The interesting part is the transaction-pairing algorithm and the pipeline around it. This page documents how the record is built and where naive approaches get it wrong.

CSV Ingest · Transaction Pairing · FIFO · Options Matching · D3 Analytics

Raw export fragment
buy  AAPL  ×10  09:31buy  AAPL  ×5   10:02buy  TSLA  ×2   11:14sell AAPL  ×8   14:20
Resolved
FIFO closes 8 shares against the 09:31 lot. One closed AAPL trade is recorded; 2 shares from that lot and the 10:02 and TSLA lots stay open. P/L is computed only on the closed quantity.

A messy export becomes a correctly paired, per-trade record.

Export gives you

A flat list of fills: buys and sells interleaved across symbols, partial quantities, day trades, and options contracts, in the order they happened.
interleaved fillspartial quantitiesscaled positionsoptions contracts

Correct record

tradeAAPL ×8 (closed)
methodFIFO lot match
cost basisfrom the 09:31 lot
open remaindertracked

The export is a flat log of fills, not a list of trades.

Nothing in the file says which sell closed which buy. That link has to be reconstructed.

Positions are scaled and day-traded across interleaved rows.

One symbol can have many open lots at once. Naive pairing matches the wrong lot and computes the wrong cost basis.

Multiple option contracts share the same underlying.

Matching by symbol alone collapses distinct contracts together and produces P/L that never occurred.

One direction of data flow: a raw export enters, a correct per-trade record leaves. Each stage has a single job and hands a cleaner shape to the next.

01Ingest
inRobinhood CSV export
transformParse and normalize rows with Pandas, typed and sorted by time
outOrdered transaction ledger
02Pairing
inOrdered transaction ledger
transformFIFO matching for stocks, contract-level matching for options
outBuy fills bound to the sell fills that close them
03Per-trade P/L
inMatched fills
transformCompute realized result per closed lot from paired cost basis
outOne inspectable record per closed trade
04Analytics
inPer-trade records
transformServe via FastAPI, render with React and D3
outPer-trade bars, a cumulative line, and a live trades table

Pairing is where correctness is won or lost. The algorithm has to decide, for every closing fill, exactly which open lot it draws from, and hold the rest open. These are the cases it has to get right.

Day tradingBuys and sells of one symbol within a single session, in any order
Position scalingSeveral open lots at different times, closed in parts
Duplicate underlyingsMultiple option contracts written on the same stock
Partial closesA sell that covers less than a lot, leaving a tracked remainder
FIFO for stocksClosing fills draw from the oldest open lot first, so cost basis follows a defined, repeatable rule
Contract-level keysOptions match on the full contract, not the underlying, so distinct contracts never merge
Per-lot quantity ledgerEvery lot carries its own remaining quantity, so partial closes and scaling stay accounted for
Time-ordered replayFills are processed in chronological order, so day trades resolve the same way every run

The record is meant to be inspected, not just read. Every view reads from the same paired trades, and everything recomputes together when the table is sorted or filtered.

Trades table

A sortable, filterable table of closed trades. Changing it drives every statistic and chart on the page.

Per-trade bars

One D3 bar per closed trade, so an individual result can be read back to the fills that produced it.

Cumulative line

A running line over time, drawn from the same records, so the whole and the parts always agree.

Backend

Python, FastAPI, and Pandas: parsing the export, running the pairing algorithm, and serving per-trade records.

Frontend

React and D3.js: the trades table, per-trade bars, and cumulative line, all recomputing from a single source.

The pairing algorithm and the full pipeline are open. Read the code that turns the export into the record.

agent protocol