Fergus Thomas
All projects

Live Marketplace Price Tracker

In progress
FastAPIPostgreSQLDocker
Live Marketplace Price Tracker preview

A full-stack price-tracking tool for Old School RuneScape's Grand Exchange, built on the OSRS Wiki's live pricing API. A scheduled background job continuously pulls price data for roughly 4,450 tradeable items into PostgreSQL, and a React frontend gives fast, searchable access to it. Each item has its own historical price and trading volume charts, so you can track how it's moved over time. The app also surfaces the margin between buy and sell price for each item, making it easy to spot which items are actually worth flipping on the Grand Exchange right now rather than just seeing raw prices.

Roadmap

3 of 22 complete

A sequenced plan for turning the price table into a trading tool, ordered so each phase unblocks the next.

Phase 0

Land what's already in flight

3/6

Finish the quota fix and close the blast radius before building anything new on top of it.

  1. Done

    Ship the batching branch

    The bulk upsert rewrite, gzip, the history cache, /healthz and the lifespan migration. Done when a poll emits 5 INSERT statements, not 4,450.

  2. Done

    Repoint the keep-alive at /healthz

    A cron ping hitting a database-backed route every 10 minutes defeats scale-to-zero on its own, no matter how fast ingestion gets.

  3. Done

    Cap the Neon blast radius

    Set max autoscale to 0.25 CU on the existing compute, then add a consumption quota as a tripwire. Turns a worst case of $1,238/month into $19.

  4. In progress

    Logo and Favicon to replace default Vite

    Use claude code design to create new logo and favicon to replace default images.

  5. Planned

    Bound page and page_size

    Both are currently unbounded. ?page_size=100000 pulls the whole table. Neither needs an attacker, just a curious person with the URL.

  6. Planned

    Cache /api/prices/latest in process

    Same bounded-LRU pattern as the history endpoint, keyed on query parameters. Decouples request volume from database load entirely.

Phase 1

Make the numbers true

0/3

Correctness before features. Everything downstream ranks on these numbers, so they have to be right first.

  1. Planned

    Grand Exchange tax in the margin

    2% of the sale price, rounded down, capped at 5,000,000 per item, with an exempt-item list. Rank on post-tax margin, not the raw spread.

  2. Planned

    ROI as a column

    A 50,000gp margin on a 2.7M item and on a 300k item are not the same trade. Percentage return is what makes them comparable.

  3. Planned

    First tests

    The tax calculation is pure arithmetic and _build_records is a pure function. The easiest possible place to start, with no database container.

Phase 2

Turn the table into a tool

0/5

This is the phase that answers “why not just use the wiki”. Almost all of the data is already on disk.

  1. Planned

    Alembic — do this first

    Blocks everything else in this phase. create_all only creates missing tables; it will never add a column to an existing one.

  2. Planned

    Ingest the fields you already download

    mappingsData.json carries limit, highalch, lowalch, value and members. You use icon and id. Buy limits exist for 4,126 of 4,589 items.

  3. Planned

    Potential profit per 4 hours

    Post-tax margin × buy limit. The metric flippers actually rank by, and almost nothing surfaces it well.

  4. Planned

    Volume, and a staleness filter

    A wide spread on four trades a day is a trap, and a margin on an item last traded three days ago is fiction.

  5. Planned

    Alch profit view

    highalch − instasell − nature rune price. A second product for a different player, from data already in the table.

Phase 3

Make the repo read well

0/5

Cheap, quick, and it is the first thing a reviewer forms an impression from.

  1. Planned

    ruff plus a pre-commit hook

    Three modules are indented with tabs, one with spaces. It reads as three people who never spoke. Fix it once, automate it, stop thinking about it.

  2. Planned

    GitHub Actions

    Run the Phase 1 tests and the linter on every push. A green badge in the README is worth more than another feature bullet.

  3. Planned

    Structured logging instead of print

    Log poll duration and row count so the next regression shows up as a number, not a bill.

  4. Planned

    Self-host the item icons

    Icons are hotlinked straight from the wiki, so someone else's bandwidth pays for every page load — and a wiki that starts blocking hotlinking would silently break every icon in the table. All 4,589 are a few hundred bytes each, so caching them is a handful of megabytes that Vercel serves from its CDN for free. Removes a dependency you don't control.

  5. Planned

    Resolve item_time_stamp

    Defined in models.py, given a schema, never written to and never read. Dead schema reads as abandoned work.

Phase 4

History, cheaply

0/3

The “find an edge in the market” goal, sized to fit the budget.

  1. Planned

    Daily aggregates, not full timeseries

    One row per item per day rather than every five-minute tick. About 200 MB a year — roughly 7 cents a month. Raw ticks would blow the budget in weeks.

  2. Planned

    Spread versus its own 30-day norm

    Not “this margin is big” but “this margin is unusually big for this item”. The genuinely novel bit.

  3. Planned

    Sparkline per row

    Cheap once the daily table exists, and it makes the list scannable without opening a chart for every item.