Skip to content

chore: Buy orders match cross price with sell orders#1335

Merged
MicBun merged 4 commits intomainfrom
matchingCrossPrice
Mar 12, 2026
Merged

chore: Buy orders match cross price with sell orders#1335
MicBun merged 4 commits intomainfrom
matchingCrossPrice

Conversation

@MicBun
Copy link
Member

@MicBun MicBun commented Mar 12, 2026

resolves: https://github.com/truflation/website/issues/3465

Summary by CodeRabbit

  • New Features

    • Order matching now supports overlapping buy/sell orders across price levels and executes matches when buy_price >= sell_price.
    • Buyers receive automatic price-improvement refunds when buy_price > sell_price; seller payouts use the sell_price.
  • Docs

    • Updated matching semantics, parameter notes, and comments to reflect price-crossing behavior.
  • Tests

    • Added price-crossing and multi-price sweep tests, reorganized test categories, and adjusted test scenarios/prices to cover new behavior.

@MicBun MicBun requested a review from pr-time-tracker March 12, 2026 04:25
@MicBun MicBun self-assigned this Mar 12, 2026
@holdex
Copy link

holdex bot commented Mar 12, 2026

Time Submission Status

Member Status Time Action Last Update
MicBun ✅ Submitted 4h 30min Update time Mar 12, 2026, 11:19 AM

You can submit time with the command. Example:

@holdex pr submit-time 15m

See available commands to help comply with our Guidelines.

@coderabbitai
Copy link

coderabbitai bot commented Mar 12, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 06561795-f2c9-441a-933b-0ae897c3cbf8

📥 Commits

Reviewing files that changed from the base of the PR and between 4ce69c0 and 8feb205.

📒 Files selected for processing (1)
  • tests/streams/order_book/queries_test.go

📝 Walkthrough

Walkthrough

Replaces exact-price direct matching with price-crossing logic: match_direct/selects best bid and ask, requires buy_price ≥ sell_price, executes trades at sell_price, issues buyer refunds for price improvement, updates collateral/payments/tx_impact and ob_positions, and adds price-crossing and sweep tests.

Changes

Cohort / File(s) Summary
Core Matching Logic
internal/migrations/032-order-book-actions.sql
Rewrote match_direct / match_orders to pick best bid/ask, allow matches when buy_price ≥ sell_price, execute at sell_price, compute buyer refunds and P&L, route collateral/payments using sell_price, and update/delete ob_positions with new price semantics and clarified comments.
Matching Engine Tests
tests/streams/order_book/matching_engine_test.go
Added price-crossing tests and helpers: testDirectMatchPriceCrossing and testDirectMatchPriceCrossingSweep; reorganized test categories; new tests validate execution price, buyer refunds, sweep behavior, holdings, residual order state, and balance impacts.
Query Test Adjustment
tests/streams/order_book/queries_test.go
Adjusted a buy-order price in testGetUserPositionsMixed (45 → 35) to avoid unintended price-crossing; comments updated to explain price constraints.

Sequence Diagram(s)

sequenceDiagram
    participant Buyer
    participant OrderBook
    participant Seller
    participant Ledger
    Buyer->>OrderBook: place buy order (price = buy_price)
    Seller->>OrderBook: place sell order (price = sell_price)
    OrderBook->>OrderBook: select best_bid, best_ask
    alt buy_price ≥ sell_price
        OrderBook->>Ledger: execute trade at sell_price (transfer collateral)
        OrderBook->>Buyer: refund (buy_price - sell_price)
        OrderBook->>Ledger: record tx_impact and P&L
        OrderBook->>OrderBook: update/delete ob_positions for buyer/seller
    else
        OrderBook->>Buyer: no match (orders remain)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • pr-time-tracker

Poem

"I nibble through the book with eager paws,
When bids hop high and asks lower laws.
A little refund bounces back with cheer,
Ledgers balanced, carrots kept near.
Hooray — price-crossing makes trades appear! 🐇"

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: implementing price-crossing matching logic for buy and sell orders in the order book system.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch matchingCrossPrice

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 and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/migrations/032-order-book-actions.sql (1)

455-512: ⚠️ Potential issue | 🟠 Major

Pick the best crossed pair with distinct participants here.

match_direct() now selects best ask and best bid independently, but unlike match_mint() and match_burn() it never excludes sell_participant_id = buy_participant_id. With price-crossing enabled, that lets one trader self-match their own crossed orders and can skip a real fill behind them. Example: A has ask 55 and bid 60, B has ask 56. This code clears A vs A first, and B never gets the 60 bid. Please choose the best overlapping buyer/seller pair with different participant_ids in the selection itself; a later guard would still leave the crossed book stuck.

🧹 Nitpick comments (1)
internal/migrations/032-order-book-actions.sql (1)

446-447: Consider replacing this recursive whole-book sweep with bounded iteration.

Now that match_direct() ignores $price and keeps recursing until no overlap remains, one placement can walk every crossed pair across all price levels for that outcome. On a deep book that makes a single order responsible for a large recursive action and raises the risk of hitting action-depth / gas limits and rolling the placement back. A bounded loop or batched matcher would be safer here.

Also applies to: 455-607

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/migrations/032-order-book-actions.sql` around lines 446 - 447, The
recursive whole-book sweep in match_direct() can traverse many price levels and
risk deep recursion/gas limits; refactor it to a bounded iterative/batched
approach by replacing the recursive call with a loop that processes a limited
number of matched pairs per invocation (e.g., batch_size N) and returns a
cursor/state when the batch limit is hit so the caller can resume; ensure you
keep the same matching semantics (processing overlaps until none remain) by
looping until either no overlap or batch_size reached, update any callers of
match_direct() to detect the resume state and re-invoke the matcher (or
implement a batched matcher wrapper), and adjust related procedures that relied
on the old recursion to handle partial completion.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/streams/order_book/matching_engine_test.go`:
- Around line 876-932: Change the test to prove price-priority (not FIFO) by
placing the three sell orders in an order that is not sorted by price (e.g.
create seller2 at 50, seller3 at 52, then seller1 at 48 using
callPlaceSplitLimitOrder / callPlaceSellOrder), then place the buy via
callPlaceBuyOrder and after getPositions also check the buyer's balance delta
equals 30*48 + 40*50 + 30*52 (to assert correct refunds and best-price
execution); keep existing checks for buyerYesHoldings and seller3Remaining to
verify holdings and remaining quantity.

---

Nitpick comments:
In `@internal/migrations/032-order-book-actions.sql`:
- Around line 446-447: The recursive whole-book sweep in match_direct() can
traverse many price levels and risk deep recursion/gas limits; refactor it to a
bounded iterative/batched approach by replacing the recursive call with a loop
that processes a limited number of matched pairs per invocation (e.g.,
batch_size N) and returns a cursor/state when the batch limit is hit so the
caller can resume; ensure you keep the same matching semantics (processing
overlaps until none remain) by looping until either no overlap or batch_size
reached, update any callers of match_direct() to detect the resume state and
re-invoke the matcher (or implement a batched matcher wrapper), and adjust
related procedures that relied on the old recursion to handle partial
completion.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 856a9149-396c-459d-b025-a04474e63d65

📥 Commits

Reviewing files that changed from the base of the PR and between 21b1973 and 2455d6e.

📒 Files selected for processing (2)
  • internal/migrations/032-order-book-actions.sql
  • tests/streams/order_book/matching_engine_test.go

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/streams/order_book/matching_engine_test.go (1)

665-667: ⚠️ Potential issue | 🟡 Minor

Section header is inconsistent with test registration.

This header still says "Category E: Edge Cases" but the test registration at line 86-87 now places testNoMatchingOrders under "Category F: Edge Cases". The new price-crossing tests are now Category E.

📝 Suggested fix
 // =============================================================================
-// Category E: Edge Cases
+// Category F: Edge Cases
 // =============================================================================
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/streams/order_book/matching_engine_test.go` around lines 665 - 667, The
section header string is wrong: update the header that currently reads "Category
E: Edge Cases" to match the test registration which places testNoMatchingOrders
under "Category F: Edge Cases"; locate the header near the Category comments
(the block containing "Category E: Edge Cases") and change it to "Category F:
Edge Cases" so it aligns with the registration for testNoMatchingOrders and the
new price-crossing tests remain in Category E.
🧹 Nitpick comments (1)
tests/streams/order_book/matching_engine_test.go (1)

823-835: LGTM on test logic; minor comment inconsistency.

The price-crossing verification is correctly implemented. The calculation 50 × 51 × 10^16 matches the expected cost at sell price. However, the comments reference "TRUF" while the test actually checks USDC balance via getUSDCBalance.

📝 Suggested comment fix
 		// Verify buyer got price improvement refund
-		// Buyer locked: 50 × 52 × 10^16 = 26 × 10^18 (26 TRUF)
-		// Seller received: 50 × 51 × 10^16 = 25.5 × 10^18 (25.5 TRUF)
-		// Buyer refund: 50 × 1 × 10^16 = 0.5 × 10^18 (0.5 TRUF)
-		// Net cost to buyer: 25.5 TRUF (not 26 TRUF)
+		// Buyer locked: 50 × 52 × 10^16 = 26 × 10^18 (26 USDC)
+		// Seller received: 50 × 51 × 10^16 = 25.5 × 10^18 (25.5 USDC)
+		// Buyer refund: 50 × 1 × 10^16 = 0.5 × 10^18 (0.5 USDC)
+		// Net cost to buyer: 25.5 USDC (not 26 USDC)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/streams/order_book/matching_engine_test.go` around lines 823 - 835,
Update the test comments to reflect the correct currency being asserted (USDC)
rather than "TRUF": in the block around buyerBalanceAfter and getUSDCBalance,
change occurrences of "TRUF" in the explanatory lines (the
locked/received/refund/net cost comments) to "USDC" or a neutral unit (e.g.,
"units") so they match the actual balance check using getUSDCBalance and the
expectedCost calculation (expectedCost, balanceDecrease, buyerBalanceBefore,
buyerBalanceAfter).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@tests/streams/order_book/matching_engine_test.go`:
- Around line 665-667: The section header string is wrong: update the header
that currently reads "Category E: Edge Cases" to match the test registration
which places testNoMatchingOrders under "Category F: Edge Cases"; locate the
header near the Category comments (the block containing "Category E: Edge
Cases") and change it to "Category F: Edge Cases" so it aligns with the
registration for testNoMatchingOrders and the new price-crossing tests remain in
Category E.

---

Nitpick comments:
In `@tests/streams/order_book/matching_engine_test.go`:
- Around line 823-835: Update the test comments to reflect the correct currency
being asserted (USDC) rather than "TRUF": in the block around buyerBalanceAfter
and getUSDCBalance, change occurrences of "TRUF" in the explanatory lines (the
locked/received/refund/net cost comments) to "USDC" or a neutral unit (e.g.,
"units") so they match the actual balance check using getUSDCBalance and the
expectedCost calculation (expectedCost, balanceDecrease, buyerBalanceBefore,
buyerBalanceAfter).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 05dda70c-e849-4c7f-8c8b-5636727223ae

📥 Commits

Reviewing files that changed from the base of the PR and between 2455d6e and 8694008.

📒 Files selected for processing (1)
  • tests/streams/order_book/matching_engine_test.go

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
tests/streams/order_book/matching_engine_test.go (1)

665-667: Consider reordering test functions to match category sequence.

The Category F section (line 666) appears before the Category E section (line 744) in the source file, even though Category E logically precedes Category F. For consistency with the test registration order (lines 82-87), consider moving the Category E test implementations before Category F.

Also applies to: 744-746

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/streams/order_book/matching_engine_test.go` around lines 665 - 667, The
Category F block appears before Category E in
tests/streams/order_book/matching_engine_test.go; reorder the code so the
"Category E" test implementations (the functions under the Category E header)
are placed before the "Category F" block to match the test registration order;
move or cut/paste the entire Category E section (including its test functions
and header comment) so it precedes the Category F section, ensuring any related
helper functions or shared variables remain in scope and imports/fixtures are
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/streams/order_book/matching_engine_test.go`:
- Around line 665-667: The Category F block appears before Category E in
tests/streams/order_book/matching_engine_test.go; reorder the code so the
"Category E" test implementations (the functions under the Category E header)
are placed before the "Category F" block to match the test registration order;
move or cut/paste the entire Category E section (including its test functions
and header comment) so it precedes the Category F section, ensuring any related
helper functions or shared variables remain in scope and imports/fixtures are
unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0a0f4a52-6757-4429-b384-113f87b3f738

📥 Commits

Reviewing files that changed from the base of the PR and between 8694008 and 4ce69c0.

📒 Files selected for processing (1)
  • tests/streams/order_book/matching_engine_test.go

@MicBun MicBun merged commit 9b3ee99 into main Mar 12, 2026
8 checks passed
@MicBun MicBun deleted the matchingCrossPrice branch March 12, 2026 13:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant