Overview
The matching engine utility helps you find counterparty orders in the orderbook that can fill your order. It supports both:
- Direct matching: Buy YES vs Sell YES
- Complementary matching: Buy YES at 60¢ vs Buy NO at 40¢ (prices sum to $1.00)
calculateMatchingOrders
Computes matching counterparty orders from an orderbook for a given order.
import { calculateMatchingOrders } from '@alpha-arcade/sdk';
const matches = calculateMatchingOrders(
orderbook,
isBuying,
isYes,
quantity,
price,
slippageTolerance
);
Parameters
The full orderbook for the market. Contains yes and no sides, each with bids and asks arrays.
Whether the taker is buying (true) or selling (false)
Whether the taker’s position is Yes (true) or No (false)
Desired quantity in microunits (e.g., 1,000,000 for 1 share)
Desired price in microunits (e.g., 500,000 for $0.50)
Maximum acceptable slippage in microunits (e.g., 50,000 for $0.05)
Returns
Array of counterparty matches, sorted by best price, up to the requested quantity.Each CounterpartyMatch contains:
escrowAppId (number): Escrow app ID of the counterparty order
quantity (number): Quantity available to match in microunits
owner (string): Owner address of the counterparty order
price (number): Effective fill price in microunits (accounts for complementary matching)
Types
Orderbook
type Orderbook = {
yes: OrderbookSide;
no: OrderbookSide;
};
type OrderbookSide = {
bids: OrderbookEntry[];
asks: OrderbookEntry[];
};
type OrderbookEntry = {
price: number; // Price in microunits
quantity: number; // Remaining quantity in microunits
escrowAppId: number; // Escrow app ID for this order
owner: string; // Owner address
};
CounterpartyMatch
type CounterpartyMatch = {
escrowAppId: number; // Escrow app ID of the counterparty order
quantity: number; // Quantity available to match in microunits
owner: string; // Owner address of the counterparty order
price: number; // Effective fill price in microunits
};
Matching Algorithm
The function implements the following matching logic:
For Buy Orders
Buying YES:
- Direct: Sell YES orders (asks on the YES side)
- Complementary: Buy NO orders (bids on the NO side), with price converted to
1_000_000 - noPrice
- Sorted by ascending price (best price first)
- Filtered to
price <= targetPrice + slippage
Buying NO:
- Direct: Sell NO orders (asks on the NO side)
- Complementary: Buy YES orders (bids on the YES side), with price converted to
1_000_000 - yesPrice
- Sorted by ascending price (best price first)
- Filtered to
price <= targetPrice + slippage
For Sell Orders
Selling YES:
- Direct: Buy YES orders (bids on the YES side)
- Complementary: Sell NO orders (asks on the NO side), with price converted to
1_000_000 - noPrice
- Sorted by descending price (best price first)
- Filtered to
price >= targetPrice - slippage
Selling NO:
- Direct: Buy NO orders (bids on the NO side)
- Complementary: Sell YES orders (asks on the YES side), with price converted to
1_000_000 - yesPrice
- Sorted by descending price (best price first)
- Filtered to
price >= targetPrice - slippage
Examples
Buy YES at market price
const orderbook: Orderbook = {
yes: {
bids: [
{ price: 580_000, quantity: 500_000, escrowAppId: 101, owner: 'ADDR1...' }
],
asks: [
{ price: 620_000, quantity: 1_000_000, escrowAppId: 102, owner: 'ADDR2...' },
{ price: 630_000, quantity: 500_000, escrowAppId: 103, owner: 'ADDR3...' }
]
},
no: {
bids: [
{ price: 390_000, quantity: 750_000, escrowAppId: 104, owner: 'ADDR4...' }
],
asks: [
{ price: 410_000, quantity: 600_000, escrowAppId: 105, owner: 'ADDR5...' }
]
}
};
// Buy 1.5 YES shares at $0.65 with $0.05 slippage
const matches = calculateMatchingOrders(
orderbook,
true, // isBuying
true, // isYes
1_500_000, // quantity (1.5 shares)
650_000, // price ($0.65)
50_000 // slippage ($0.05)
);
console.log(matches);
// [
// {
// escrowAppId: 104,
// quantity: 750_000,
// owner: 'ADDR4...',
// price: 610_000 // Complementary: 1_000_000 - 390_000
// },
// {
// escrowAppId: 102,
// quantity: 750_000, // Only 750k of the 1M available
// owner: 'ADDR2...',
// price: 620_000 // Direct match
// }
// ]
Sell NO with limit
const orderbook: Orderbook = {
yes: {
bids: [
{ price: 700_000, quantity: 1_000_000, escrowAppId: 201, owner: 'ADDR6...' }
],
asks: []
},
no: {
bids: [
{ price: 310_000, quantity: 500_000, escrowAppId: 202, owner: 'ADDR7...' },
{ price: 305_000, quantity: 500_000, escrowAppId: 203, owner: 'ADDR8...' }
],
asks: []
}
};
// Sell 1 NO share at $0.30 with no slippage (limit order)
const matches = calculateMatchingOrders(
orderbook,
false, // isSelling (isBuying = false)
false, // isNo
1_000_000, // quantity (1 share)
300_000, // price ($0.30)
0 // no slippage
);
console.log(matches);
// [
// {
// escrowAppId: 202,
// quantity: 500_000,
// owner: 'ADDR7...',
// price: 310_000 // Direct match (best price)
// },
// {
// escrowAppId: 203,
// quantity: 500_000,
// owner: 'ADDR8...',
// price: 305_000 // Direct match (second best)
// }
// ]
No matches available
const orderbook: Orderbook = {
yes: {
bids: [{ price: 400_000, quantity: 1_000_000, escrowAppId: 301, owner: 'ADDR9...' }],
asks: [{ price: 600_000, quantity: 1_000_000, escrowAppId: 302, owner: 'ADDR10...' }]
},
no: {
bids: [],
asks: []
}
};
// Try to buy YES at $0.50 with $0.05 slippage
// Best available is $0.60, outside slippage range
const matches = calculateMatchingOrders(
orderbook,
true,
true,
1_000_000,
500_000,
50_000
);
console.log(matches); // => []
Usage with Market Orders
This function is typically used internally by createMarketOrder(), but you can also call it directly to preview matches before placing an order:
import { AlphaClient } from '@alpha-arcade/sdk';
import { calculateMatchingOrders } from '@alpha-arcade/sdk';
const client = new AlphaClient(config);
// Fetch the orderbook
const orderbook = await client.getOrderbook(marketAppId);
// Calculate matches
const matches = calculateMatchingOrders(
orderbook,
true, // buying
true, // YES
1_000_000, // 1 share
650_000, // $0.65
50_000 // $0.05 slippage
);
// Preview the average fill price
if (matches.length > 0) {
const totalQuantity = matches.reduce((sum, m) => sum + m.quantity, 0);
const weightedPrice = matches.reduce((sum, m) => sum + m.price * m.quantity, 0) / totalQuantity;
console.log(`Average fill price: $${(weightedPrice / 1_000_000).toFixed(4)}`);
}
// Place the order with pre-computed matches
await client.createMarketOrder({
marketAppId,
position: 1,
price: 650_000,
quantity: 1_000_000,
isBuying: true,
slippage: 50_000,
matchingOrders: matches // Pass pre-computed matches
});