Skip to content

feat: read consolidated order books from Python - #132

Merged
MicBun merged 2 commits into
mainfrom
feat/consolidated-order-book-python
Aug 7, 2026
Merged

MicBun merged 2 commits into
mainfrom
feat/consolidated-order-book-python

Conversation

@MicBun

@MicBun MicBun commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

get_consolidated_order_book(query_id, outcome=True) returns one outcome's book
with 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_depth on the YES outcome cannot show that quote, so the
market 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) on TNClient, with
    ConsolidatedOrderBook and ConsolidatedLevel. outcome frames the prices;
    the NO-framed book is the YES-framed book reflected, so one call answers
    either tab.
  • GetConsolidatedOrderBook in bindings/bindings.go, delegating to sdk-go and
    handing 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.
  • sdk-go moved to v0.7.5-0.20260806143829-879be314b9a4, the commit carrying
    both consolidation PRs. The consequence worth knowing: the book comes from a
    single get_full_market_depth read, so both sides are one snapshot of the
    chain and is_crossed describes a state the book was really in rather than an
    artifact of reading the two sides a moment apart.
  • Each level keeps native and inverse separately, not just their sum. Mint
    and 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.
  • DepthLevel corrected to the shape get_market_depth actually returns —
    price, buy_volume, sell_volume, where it claimed price,
    total_amount. Type hints only, nothing in the tree read it, and the new live
    test reads those keys.

Testing

go build ./... and go vet ./bindings/ clean, bindings rebuilt with
make gopy_build.

  • 102 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
    — 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.py covers. It finds a market quoting
both 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_URL in the style of
test_forecast_live.py, so CI skips it.

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.

No Python copy of the folding rule. forecast.py has its own consolidation for
the bucket-forecast path, but that one works on BucketDepth assembled from raw
order 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 from
the package root, matching DepthLevel, BestPrices and the rest of the
order-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 above
runs green against it. A network still on an older node will not.

Follow-ups

get_market_forecast still reads each bucket's YES and NO books separately and
carries 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

    • Added consolidated order book retrieval for binary markets.
    • View executable bids and asks with native and inverse liquidity, query metadata, and crossed-book status.
    • Supports selecting either outcome frame, with an initialized empty response when no data is available.
    • Updated depth levels to distinguish buy and sell volumes.
  • Documentation

    • Added API reference documentation and usage examples for consolidated order books.

@MicBun MicBun self-assigned this Aug 7, 2026
@holdex

holdex Bot commented Aug 7, 2026

Copy link
Copy Markdown

Time Submission Status

Member # Time Running Total Status Last Update
MicBun 4h ✅ Submitted Aug 7, 2026, 4:02 PM

Submit or update total time with:

@holdex pr submit-time 2h

Add time on top of previous submission with:

@holdex pr add-time 1h30m

See available commands to help comply with our Guidelines.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@MicBun, you've reached your PR review limit, so we couldn't start this review.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0acf33b6-7707-40b8-adc7-6ef171686830

📥 Commits

Reviewing files that changed from the base of the PR and between ee264c2 and 8092b93.

📒 Files selected for processing (1)
  • tests/test_order_book_live.py
📝 Walkthrough

Walkthrough

The 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.

Changes

Consolidated order-book retrieval

Layer / File(s) Summary
Binding retrieval and serialization
bindings/bindings.go, go.mod
The Go binding retrieves consolidated order-book data through the updated SDK dependency and serializes query metadata, bid and ask levels, native and inverse quantities, and crossed status.
Python client contract and wrapper
src/trufnetwork_sdk_py/client.py, tests/test_consolidated_order_book.py, tests/test_order_book.py
The Python client adds ConsolidatedLevel, ConsolidatedOrderBook, and get_consolidated_order_book. DepthLevel separates buy and sell volumes. Tests verify forwarding, outcome frames, response parsing, empty responses, and public declarations.
API documentation and live validation
docs/api-reference.md, tests/test_order_book_live.py
The documentation defines consolidation and ordering semantics. Gated live tests validate folded liquidity, inverse volumes, stable snapshots, and YES/NO price reflection.

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
Loading

Possibly related PRs

Suggested labels: enhancement

Suggested reviewers: pr-time-tracker

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding Python support for reading consolidated order books.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/consolidated-order-book-python

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between e8c10b9 and ee264c2.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (7)
  • bindings/bindings.go
  • docs/api-reference.md
  • go.mod
  • src/trufnetwork_sdk_py/client.py
  • tests/test_consolidated_order_book.py
  • tests/test_order_book.py
  • tests/test_order_book_live.py

Comment thread tests/test_order_book_live.py Outdated
@MicBun

MicBun commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

@holdex pr submit-time 4h

@MicBun
MicBun merged commit 2433d4f into main Aug 7, 2026
6 checks passed
@MicBun
MicBun deleted the feat/consolidated-order-book-python branch August 7, 2026 16:02
MicBun added a commit that referenced this pull request Aug 7, 2026
`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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant