feat: read consolidated order books from Python - #132
Conversation
Time Submission Status
Submit or update total time with: Add time on top of previous submission with: See available commands to help comply with our Guidelines. |
|
Warning Review limit reached
Next review available in: 38 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe PR adds consolidated binary-market order-book retrieval to the Go binding and Python client. It introduces typed response structures, native and inverse liquidity fields, crossed-book reporting, API documentation, and unit and live-network tests. ChangesConsolidated order-book retrieval
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant GoBinding
participant SDKGo
participant MarketNode
Client->>GoBinding: request consolidated order book
GoBinding->>SDKGo: retrieve query and outcome frame
SDKGo->>MarketNode: read consolidated ladder
MarketNode-->>SDKGo: return bids, asks, and crossed status
SDKGo-->>GoBinding: return order-book data
GoBinding-->>Client: return typed JSON response
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/test_order_book_live.py`:
- Around line 168-170: Update the stability check in the test around yes_book
and no_book to fetch the NO order book a second time and require both the YES
and NO snapshots to remain equal before asserting reflection. Preserve the
existing consolidated order book calls and reflection assertions, but ensure
transient updates to either outcome frame cause the test to retry rather than
fail.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: bd0af301-491c-434f-911d-3e503f08d02d
⛔ Files ignored due to path filters (1)
go.sumis excluded by!**/*.sum
📒 Files selected for processing (7)
bindings/bindings.godocs/api-reference.mdgo.modsrc/trufnetwork_sdk_py/client.pytests/test_consolidated_order_book.pytests/test_order_book.pytests/test_order_book_live.py
|
@holdex pr submit-time 4h |
`get_full_market_depth(query_id)` returns aggregated volume per price level for **both** outcomes from one read, each level tagged with the outcome it rests on. Related, no closing keyword: - <truflation/website#4445> - <truflation/website#4387> #4445 is the Problem this action was written for and is already closed, by the sdk-go and sdk-js PRs that consumed it. Python's consolidated book was reading through it from the moment the preceding PR landed the sdk-go pin, so the job story is satisfied without this. What is left is the direct accessor, which is API parity rather than the problem. ## Why `get_market_depth` answers for one outcome, so comparing the two sides of a market takes two calls at two independent points in time. An order landing between them appears on one side and not the other, and the pair then describes a market state that never existed. One statement is one snapshot, and that whole class of artifact goes away. The node has carried `get_full_market_depth` since trufnetwork/node#1413 and it is live on mainnet. sdk-js and sdk-go both expose it. Python was the last SDK reading a market two calls at a time. ## What is in it - `get_full_market_depth(query_id)` on `TNClient`, returning `FullDepthLevel` rows — `outcome`, `price`, `buy_volume`, `sell_volume`. Same aggregation as `get_market_depth`, for the whole market instead of one outcome. Rows arrive YES first then NO, price ascending within each. - `GetFullMarketDepth` in `bindings/bindings.go`, delegating to sdk-go. - Unit tests pinning the wrapper: forwarding, decoding, and that the outcome tag survives. The tag is the only thing separating a YES level from the NO level at the same price, so the fixture puts a sell at 60 on each side — lose the tag and the two rows are indistinguishable. - A live check that the whole-market read and two per-outcome reads describe the same book, plus the live discovery scan switched over to it: "is this market two-sided?" is exactly the question this action exists to answer, so it now costs one read per market rather than two. ## Testing `go build ./...` and `go vet ./bindings/` clean, bindings rebuilt with `make gopy_build`. - 105 offline tests pass. - `tests/test_order_book.py` passes against a local node (44 tests). - `TN_LIVE_NODE_URL=https://gateway.mainnet.truf.network pytest tests/test_order_book_live.py` — 5 passed against mainnet, including the new equivalence check. The equivalence check is the one that matters. If the two reads ever disagreed on anything but timing, every consolidated ladder built on the new one would be wrong while the per-outcome reads stayed right, and nothing offline would notice. It re-reads to confirm the market held still before asserting, so a moving book skips rather than fails. ## What is not in it `get_market_depth` is untouched and stays the right call when you want one outcome — a depth chart, or a bot quoting a single side. `get_consolidated_order_book` needs no change: it already reads through this action inside sdk-go, from the pin #132 brought in. This adds the direct accessor for callers who want the raw both-outcome depth, which is what sdk-js and sdk-go each expose alongside their consolidated method. ## Note on the base branch Was stacked on #132 while that was open. #132 has since merged, so this now targets `main` and carries a single commit. - <#132>
get_consolidated_order_book(query_id, outcome=True)returns one outcome's bookwith the opposite outcome's quotes folded in, so a Python caller sees the
liquidity that is actually executable rather than one side of it.
resolves: https://github.com/truflation/website/issues/4388
Why
A binary market's two books are two views of one position, and the matching
engine fills across them. A resting SELL NO at 93c is a standing bid for YES at
7c: a trader hits it by selling YES, both parties sell, and the chain burns the
share pair.
get_market_depthon the YES outcome cannot show that quote, so themarket reads thinner than it is.
How much thinner is not a rounding error. On mainnet market 423 while this was
being written, the best YES ask was 16c with 3,443 shares behind it — 3,374 of
them resting in the NO book. The YES ladder alone showed 69.
sdk-js and sdk-go both carry this already. Python was the last SDK without it.
What is in it
get_consolidated_order_book(query_id, outcome=True)onTNClient, withConsolidatedOrderBookandConsolidatedLevel.outcomeframes the prices;the NO-framed book is the YES-framed book reflected, so one call answers
either tab.
GetConsolidatedOrderBookinbindings/bindings.go, delegating to sdk-go andhanding the book back as JSON. The folding rule stays in sdk-go, so Python, Go
and TypeScript agree on what a market's executable ladder is instead of
carrying three copies of a rule that has to stay identical.
v0.7.5-0.20260806143829-879be314b9a4, the commit carryingboth consolidation PRs. The consequence worth knowing: the book comes from a
single
get_full_market_depthread, so both sides are one snapshot of thechain and
is_crosseddescribes a state the book was really in rather than anartifact of reading the two sides a moment apart.
nativeandinverseseparately, not just their sum. Mintand burn fire only when the two prices sum to exactly 100, while a direct
same-outcome match crosses, so one order fills every native level past its
limit plus exactly one inverse level. A caller quoting a fill needs the
split to get that right.
DepthLevelcorrected to the shapeget_market_depthactually returns —price,buy_volume,sell_volume, where it claimedprice,total_amount. Type hints only, nothing in the tree read it, and the new livetest reads those keys.
Testing
go build ./...andgo vet ./bindings/clean, bindings rebuilt withmake gopy_build.tests/test_order_book.pypasses against a local node (44 tests).TN_LIVE_NODE_URL=https://gateway.mainnet.truf.network pytest tests/test_order_book_live.py— 4 passed against mainnet.
The unit tests monkeypatch the binding, so they pin the wrapper's contract —
argument forwarding, decoding, the crossed flag, the empty book — and nothing
more. What they cannot show is that the SDK reads the chain the right way round:
a fold that put NO bids on the bid side instead of the ask side would satisfy
every one of them while pointing traders at quotes that cannot fill.
That is what
tests/test_order_book_live.pycovers. It finds a market quotingboth outcomes, then checks each consolidated level against the raw ladders: a
bid's native volume must be a YES buy at that price and its inverse volume a NO
sell at 100 minus it, asks the mirror, and the NO-framed book must be the
YES-framed book reflected. It asserts relationships between two reads, never
numbers, because live books move. Gated on
TN_LIVE_NODE_URLin the style oftest_forecast_live.py, so CI skips it.What is not in it
get_market_depthis untouched and stays the right call when you want oneoutcome — a depth chart, or a bot quoting a single side.
No Python copy of the folding rule.
forecast.pyhas its own consolidation forthe bucket-forecast path, but that one works on
BucketDepthassembled from raworder reads rather than on aggregated depth, so it is a separate piece of code
with its own tests and is untouched here.
The types are importable from
trufnetwork_sdk_py.client, not re-exported fromthe package root, matching
DepthLevel,BestPricesand the rest of theorder-book types.
On the sdk-go pin
sdk-go has no tag carrying the consolidation work yet, so this pins the commit.
That is the normal shape of a bump here — seven of the last eight sdk-go bumps
in this repo pinned a pseudo-version, and a tag replaced it later.
Mainnet already serves
get_full_market_depth, which is why the live test aboveruns green against it. A network still on an older node will not.
Follow-ups
get_market_forecaststill reads each bucket's YES and NO books separately andcarries the same cross-height exposure this removes. It consumes individual
orders rather than aggregated depth, so moving it is its own change with its own
review, in sdk-go first. Nothing tracks it yet.
Summary by CodeRabbit
New Features
Documentation