feat: add GET /spreads/:asset route to calculate bid-ask spreads for … - #95
feat: add GET /spreads/:asset route to calculate bid-ask spreads for …#95Johnsource-hub wants to merge 2 commits into
Conversation
|
@Johnsource-hub Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
Miracle656
left a comment
There was a problem hiding this comment.
Thanks for tackling #93 — the route skeleton is a good start, but a few things need fixing before it can merge:
1. The bid/ask are inverted (correctness bug). Right now bid = MAX(price) and ask = MIN(price), so bid ≥ ask always, which makes spreadBps = ((ask - bid) / ask) * 10000 always negative. A bid-ask spread needs ask ≥ bid. Also, taking max/min over all historical trade prices isn't a bid-ask spread — that's a price range. A real spread needs quote/order-book data (best bid vs best ask) or, if you only have trade points, it should be defined and named accordingly. Please rework the calculation so it returns a meaningful, non-negative spread.
2. It isn't actually registered. src/index.ts wires up routes via registerRESTRoutes/registerPriceRoutes/registerCandleRoutes/etc., but there's no registerSpreadsRoutes(app) call (and no import). As-is the endpoint is unreachable — please add the import + await registerSpreadsRoutes(app) alongside the others.
3. Check the SQL column names. Unquoted assetA = $1 OR assetB = $1 folds to asseta/assetb in Postgres and won't match the actual columns. Look at how the other raw queries reference price_points and match that exactly (quote the identifier if the column is camelCase).
4. Add the test. The checklist notes tests are still to be added — please include at least one covering a normal spread and the empty/no-data case.
Once the spread math is correct, the route is registered, and there's a test, this is good to go. 👍
|
everything done |
|
Thanks for the update — the bid/ask sign is fixed now (
Also note it's still computing max−min over all price points, which is a price range rather than a true bid-ask spread — fine if that's the intent, but worth documenting it as such. Once it's registered and tested I'll re-review. |
Miracle656
left a comment
There was a problem hiding this comment.
Sorry this has sat so long. Re-reviewed against current main — the two blockers from before are both still open, and one is a correctness bug rather than a style point.
1. The route is never registered
The PR adds src/routes/spreads.ts exporting registerSpreadsRoutes, and nothing calls it. registerSpreadsRoutes appears nowhere in src/index.ts or src/api/*, so GET /spreads/:asset returns 404 — the code cannot run as shipped.
2. The query mixes price directions, so the numbers are not comparable
PricePoint stores a directional price: assetA, assetB, and one price meaning A denominated in B. This filter takes both sides:
WHERE "asset_a" = $1 OR "asset_b" = $1So for XLM it pools rows where price ≈ 0.2 (XLM priced in USDC) with rows where price ≈ 5 (USDC priced in XLM). Those are reciprocals of each other, and MIN/MAX across the mixture produces a "spread" in the thousands of bps that reflects nothing real.
Fixing it means either constraining to one direction (asset_a = $1), or inverting the price on rows where the asset is the counter before aggregating.
3. MIN/MAX is a price range, not a bid/ask
Even with direction fixed, MIN(price) and MAX(price) over the whole table are the lowest and highest prices ever recorded — a historical range. A bid/ask spread is the best buy and best sell available right now, from the order book. Two consequences:
- The value drifts permanently wider as history accumulates, and never recovers.
- There is no time bound at all, so a price from months ago sets today's "bid".
If the intent is an order-book spread, it needs SDEX offer data rather than price_points. If the intent is observed price dispersion over a window, that is a legitimate and useful endpoint — but it should say so, take a window parameter, and not be called bid/ask.
Also
No test accompanies the route, and spreadBps divides by ask without guarding the bid > ask case that the direction mixing can produce.
Happy to help scope this if you would rather narrow it to the dispersion version — that is a smaller, honest endpoint and it would land.
#closes
#93
New route added and registered.
Basic error handling implemented.
Documentation and inline comments included.
Tests (to be added) cover functionality.
No new dependencies introduced.