Portfolio positions: /traders/{addr}/positions drops or undercounts trades on 18-decimal pairs #676

Closed
opened 2026-08-26 22:01:10 +00:00 by leonardocolucci · 7 comments
leonardocolucci commented 2026-08-26 22:01:10 +00:00 (Migrated from gitlab.com)

Summary

GET /api/v1/traders/{addr}/positions disagrees with GET /api/v1/traders/{addr}/trades, specifically on pairs involving an 18-decimal token. Every 6/6-decimal pair aggregates correctly; the two pairs with an 18-dec asset are wrong.

Evidence (same wallet, both endpoints)

Pair Decimals /positions trade_count /trades count
UST1/cUSTC 6 / 6 37 37 ✓
cLUNC/UST1 6 / 6 67 67 ✓
UST1/SpaceUSD 6 / 6 2 2 ✓
EMBER/CORAL 6 / 6 3 3 ✓
CL8Y-cb/cUSTC 18 / 6 2 7 ✗
UST1/USTR 6 / 18 absent 3 ✗

The 18/6 pair undercounts (stale trade_count, and therefore net position/cost basis); the 6/18 pair is omitted from the positions response entirely despite trades in the feed.

Impact

Open Positions on /portfolio is incomplete/incorrect for the economic mixed-decimal pairs (UST1/USTR, CL8Y-cb/cUSTC) — the same 6-vs-18-decimal class #529 fixed on the contract side.

Likely cause

Decimal scaling / overflow (or an exclusion filter) in the positions materialization for 18-decimal assets. Related: #529.

Repro

For a wallet that has traded UST1/USTR and CL8Y-cb/cUSTC, compare per-pair trade_count in /positions against the count in /trades: 6/6 pairs match, 18-decimal pairs don't. A repro wallet can be shared privately.

## Summary `GET /api/v1/traders/{addr}/positions` disagrees with `GET /api/v1/traders/{addr}/trades`, specifically on pairs involving an **18-decimal token**. Every 6/6-decimal pair aggregates correctly; the two pairs with an 18-dec asset are wrong. ## Evidence (same wallet, both endpoints) | Pair | Decimals | `/positions` trade_count | `/trades` count | |---|---|---|---| | UST1/cUSTC | 6 / 6 | 37 | 37 ✓ | | cLUNC/UST1 | 6 / 6 | 67 | 67 ✓ | | UST1/SpaceUSD | 6 / 6 | 2 | 2 ✓ | | EMBER/CORAL | 6 / 6 | 3 | 3 ✓ | | **CL8Y-cb/cUSTC** | **18 / 6** | **2** | **7** ✗ | | **UST1/USTR** | **6 / 18** | **absent** | **3** ✗ | The 18/6 pair undercounts (stale trade_count, and therefore net position/cost basis); the 6/18 pair is **omitted from the positions response entirely** despite trades in the feed. ## Impact Open Positions on /portfolio is incomplete/incorrect for the economic mixed-decimal pairs (UST1/USTR, CL8Y-cb/cUSTC) — the same 6-vs-18-decimal class #529 fixed on the contract side. ## Likely cause Decimal scaling / overflow (or an exclusion filter) in the positions materialization for 18-decimal assets. Related: #529. ## Repro For a wallet that has traded UST1/USTR and CL8Y-cb/cUSTC, compare per-pair `trade_count` in `/positions` against the count in `/trades`: 6/6 pairs match, 18-decimal pairs don't. A repro wallet can be shared privately.
PlasticDigits commented 2026-08-27 00:35:06 +00:00 (Migrated from gitlab.com)

mentioned in commit 724f514bf3

mentioned in commit 724f514bf3662fa6dc63cd3bd6f16cc03b5ccddf
PlasticDigits commented 2026-08-27 00:35:08 +00:00 (Migrated from gitlab.com)

mentioned in merge request !1180

mentioned in merge request !1180
PlasticDigits commented 2026-08-27 00:36:01 +00:00 (Migrated from gitlab.com)

Implemented on !1180.

trader_positions was NUMERIC(38, 18) (|x| < 10^20). Raw 18-dec amounts (~100 human USTR/CL8Y) failed the upsert after the swap row landed, so /trades stayed complete and /positions undercounted or dropped the pair.

Fix: widen to NUMERIC(78, 18), one-shot SQL replay, poller repair when trade_count still diverges. make verify-issue-676 is green twice.

Needs Coolify migrate + indexer restart before a live retest on the 18-dec wallet.

Implemented on !1180. `trader_positions` was `NUMERIC(38, 18)` (`|x| < 10^20`). Raw 18-dec amounts (~100 human USTR/CL8Y) failed the upsert after the swap row landed, so `/trades` stayed complete and `/positions` undercounted or dropped the pair. Fix: widen to `NUMERIC(78, 18)`, one-shot SQL replay, poller repair when `trade_count` still diverges. `make verify-issue-676` is green twice. Needs Coolify migrate + indexer restart before a live retest on the 18-dec wallet.
PlasticDigits commented 2026-08-27 04:51:40 +00:00 (Migrated from gitlab.com)

mentioned in commit 972d46f9f1

mentioned in commit 972d46f9f12647f2dba2b086cd9ee90f3dd194de
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-08-27 04:51:40 +00:00
PlasticDigits commented 2026-08-27 05:29:51 +00:00 (Migrated from gitlab.com)

mentioned in issue #686

mentioned in issue #686
PlasticDigits commented 2026-08-27 05:29:53 +00:00 (Migrated from gitlab.com)

marked as related to #686

marked as related to #686
PlasticDigits commented 2026-08-27 05:30:22 +00:00 (Migrated from gitlab.com)

Merged via !1180 (972d46f9) onto main 36d64528.

make verify-issue-676 PASS (6/6, Postgres). Coolify leftover: apply 20260827120000_trader_positions_numeric_78.sql, redeploy indexer, then compare /positions vs /trades on UST1/USTR and CL8Y-cb/cUSTC. If trade_count still diverges, cl8y-dex-indexer rebuild-positions. Do not reopen unless P676 is wrong. Post-merge ops: #686.

Merged via !1180 (`972d46f9`) onto `main` `36d64528`. `make verify-issue-676` **PASS** (6/6, Postgres). **Coolify leftover:** apply `20260827120000_trader_positions_numeric_78.sql`, redeploy indexer, then compare `/positions` vs `/trades` on UST1/USTR and CL8Y-cb/cUSTC. If `trade_count` still diverges, `cl8y-dex-indexer rebuild-positions`. Do not reopen unless P676 is wrong. Post-merge ops: #686.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
code/cl8y-dex-terraclassic#676
No description provided.