A small FastAPI service that parses trade journal CSV exports and returns structured performance stats as JSON. Extracted from a browser-based trading dashboard, where this same parsing logic previously ran only client-side.
The original dashboard's CSV parsing (dual export format support, time normalization, month/year inference) was tightly coupled to the frontend — it only worked inside the browser tab it was written for. This service pulls that logic out into a standalone, testable, reusable API that any client can call.
Upload a CSV file, get back parsed trades + aggregate stats.
Request: multipart/form-data, field name file, a .csv file
Response:
{
"stats": {
"total_trades": 42,
"wins": 24,
"losses": 15,
"breakevens": 3,
"win_rate": 57.14,
"net_rr": 18.5,
"average_rr": 0.44,
"best_trade_rr": 3.2,
"worst_trade_rr": -1.5,
"good_trades": 38,
"bad_trades": 4,
"by_month": [
{ "month": "2026-02", "trades": 12, "wins": 7, "losses": 4, "breakevens": 1, "win_rate": 58.33, "net_rr": 5.2 }
]
},
"trades": [ /* full list of parsed trade objects */ ]
}Health check.
Redirects to /docs — so opening the bare deployed URL lands on the interactive API docs instead of a raw JSON response.
- New format — Notion-exported journal (headers begin with
Property). Handles Notion relation-link stripping, military-time entry/exit fields, and infers the year for each month (correctly handling December → January rollovers). - Old format — legacy export with a combined date+time column, kept for backward compatibility with older journal exports.
The test_data/ folder has two ready-to-use CSVs, one per supported format, so you can try the API immediately without a real trading journal export:
journal_all.csv— new (Notion) format, 64 synthetic trades across 5 months. Named with_allsince that's the convention the original dashboard app looks for when multiple files are present.journal_legacy.csv— old/legacy format, a small 4-trade sample, included specifically to demonstrate the parser's dual-format auto-detection.
Both are synthetic, anonymized data — not a real trading history.
To try one via the interactive docs: run the server (see below), open /docs, expand POST /parse, click "Try it out", upload either file from test_data/, and click Execute.
Or via curl:
curl -X POST http://localhost:8000/parse -F "file=@test_data/journal_all.csv"pip install -r requirements.txt
uvicorn app.main:app --reloadInteractive API docs: http://localhost:8000/docs
docker build -t trade-parser-api .
docker run -p 8000:8000 trade-parser-apiapp/
main.py # FastAPI app, /parse endpoint, request/response wiring
parser.py # CSV parsing logic (ported from the original dashboard)
stats.py # Aggregate stats computed from parsed trades
models.py # Pydantic request/response models
test_data/
journal_all.csv # new format sample (64 trades)
journal_legacy.csv # legacy format sample (4 trades)
- Times are parsed as-is from the source data; no timezone conversion is applied.
- Weekend rows (from the new-format journal) are skipped, since the underlying markets don't trade on weekends.