Prices & Orderbook
All trading on Openfish flows through a Central Limit Order Book (CLOB). The platform does not impose prices — they arise naturally from the interplay of buy and sell orders submitted by participants.
Prices Are Probabilities
Section titled “Prices Are Probabilities”Outcome share prices sit on a spectrum from $0.00 to $1.00, each value functioning as the crowd’s probability estimate for that outcome.
| Price | Implied Probability |
|---|---|
| $0.25 | 25% chance |
| $0.50 | 50% chance |
| $0.75 | 75% chance |
The price shown on the market page is typically the midpoint between the highest bid and the lowest ask. If only one side of the book has orders, the best available price on that side is displayed instead.
Example
Section titled “Example”Suppose the best bid for “Yes” sits at $0.34 and the best ask at $0.40:
Displayed price = ($0.34 + $0.40) / 2 = $0.37 (37% implied probability)Execution will not happen at exactly $0.37. Buyers pay the ask ($0.40) and sellers receive the bid ($0.34). The midpoint serves only as a reference.
The In-Memory Order Book
Section titled “The In-Memory Order Book”Openfish’s matching engine keeps a fully in-memory order book per token, relying on Rust’s BTreeMap for deterministic ordering:
Structure
Section titled “Structure”OrderBook (per token_id) Bids: BTreeMap<Reverse<Decimal>, VecDeque<OrderEntry>> -- Sorted by price DESCENDING (highest bid first) -- Within each price level: FIFO queue (earliest order first)
Asks: BTreeMap<Decimal, VecDeque<OrderEntry>> -- Sorted by price ASCENDING (lowest ask first) -- Within each price level: FIFO queue (earliest order first)Each OrderEntry stored on the book contains:
| Field | Type | Description |
|---|---|---|
order_id | UUID | Unique identifier for the order |
owner | String | API key of the order owner |
maker_address | String | Ethereum address that signed the order |
price | Decimal | Limit price |
remaining_size | Decimal | Unfilled quantity remaining |
order_type | String | GTC, GTD, FOK, or FAK |
expiration | i64 | Unix timestamp for GTD expiration (0 for non-GTD) |
created_at | DateTime | Timestamp of order creation |
Price-Time Priority
Section titled “Price-Time Priority”The engine applies strict price-time priority when matching:
- Price comes first: Orders with better prices match before those with worse prices. For bids, higher is better. For asks, lower is better.
- Time breaks ties: When two orders share the same price, the one placed earlier fills first (FIFO via
VecDeque).
Concretely, a bid at $0.55 always takes precedence over a bid at $0.50. Two bids at $0.55 execute in the order they were received.
Key Price Metrics
Section titled “Key Price Metrics”Several derived price metrics are available through the CLOB API:
Best Bid and Best Ask
Section titled “Best Bid and Best Ask”The highest price any buyer currently offers (best bid) and the lowest price any seller currently accepts (best ask).
# Get the current price (best bid/ask) for a tokencurl "https://api.openfish.fun/price?token_id=YOUR_TOKEN_ID"Midpoint
Section titled “Midpoint”The arithmetic mean of the best bid and best ask. Falls back to the single available side when only bids or only asks exist.
midpoint = (best_bid + best_ask) / 2curl "https://api.openfish.fun/midpoint?token_id=YOUR_TOKEN_ID"Spread
Section titled “Spread”The gap between the best ask and best bid. Narrower spreads signal deeper liquidity and lower trading friction.
spread = best_ask - best_bidcurl "https://api.openfish.fun/spread?token_id=YOUR_TOKEN_ID"Batch Queries
Section titled “Batch Queries”To reduce round trips, multiple tokens can be queried in a single call:
# Batch midpointscurl -X POST "https://api.openfish.fun/midpoints" \ -H "Content-Type: application/json" \ -d '["token_id_1", "token_id_2", "token_id_3"]'
# Batch spreadscurl -X POST "https://api.openfish.fun/spreads" \ -H "Content-Type: application/json" \ -d '["token_id_1", "token_id_2"]'Order Book Depth
Section titled “Order Book Depth”You can retrieve the full ladder of resting orders, aggregated by price level:
curl "https://api.openfish.fun/book?token_id=YOUR_TOKEN_ID"The response shows bid and ask levels with their cumulative sizes:
{ "bids": [ { "price": "0.55", "size": "150" }, { "price": "0.54", "size": "200" }, { "price": "0.50", "size": "500" } ], "asks": [ { "price": "0.58", "size": "100" }, { "price": "0.60", "size": "300" }, { "price": "0.65", "size": "250" } ]}Always inspect the book depth before submitting large orders. A sizable order may sweep through multiple price levels, resulting in a worse average fill price.
Tick Sizes
Section titled “Tick Sizes”Three tick sizes are available — each defines the smallest allowed price increment for orders in that market:
| Tick Size | Example Prices | Typical Use |
|---|---|---|
| 0.01 | 0.01, 0.02, …, 0.99 | Standard markets with moderate volume |
| 0.001 | 0.001, 0.002, …, 0.999 | Higher precision for liquid markets |
| 0.0001 | 0.0001, 0.0002, …, 0.9999 | Maximum precision for very liquid markets |
A market’s tick size is fixed at creation. Orders submitted at prices that do not align with the tick size will be rejected.
# Check the tick size for a specific tokencurl "https://api.openfish.fun/tick-size?token_id=YOUR_TOKEN_ID"Price Discovery
Section titled “Price Discovery”A freshly created market starts with an empty book. The first price materializes when overlapping buy and sell interest appears:
- One participant posts a bid to buy Yes at $0.60
- Another participant posts a bid to buy No — functionally equivalent to offering to sell Yes at $0.40
- Because $0.60 (Yes bid) + $0.40 (No bid) = $1.00, the engine can match them
The match splits $1.00 USDC.e into 1 Yes token and 1 No token, delivering each to the respective buyer. That first fill establishes the market’s initial price.
From that point on, prices shift continuously as new orders arrive and interact with resting liquidity.
Price Snapshots
Section titled “Price Snapshots”Every 60 seconds, the CLOB server captures a price snapshot — recording the implied price (midpoint) for each active token. These snapshots persist in PostgreSQL and feed the price history endpoints:
# Get price history for a tokencurl "https://api.openfish.fun/prices-history?token_id=YOUR_TOKEN_ID"
# Batch price historycurl -X POST "https://api.openfish.fun/batch-prices-history" \ -H "Content-Type: application/json" \ -d '["token_id_1", "token_id_2"]'How Trades Execute
Section titled “How Trades Execute”The CLOB follows a hybrid-decentralized model:
- Off-chain matching — The Rust matching engine pairs compatible orders in memory with sub-millisecond latency
- On-chain settlement — Matched trades are forwarded to the Exchange contract on Polygon for atomic, trustless settlement
This design delivers the responsiveness of a centralized exchange while preserving the transparency and security guarantees of blockchain-based settlement. Users never give up custody of their assets.
No Size Limits
Section titled “No Size Limits”There is no cap on trade size. Any two willing counterparties can match regardless of volume. That said, large orders can move the market significantly — check the book depth before placing a sizable trade.
Persistence and Recovery
Section titled “Persistence and Recovery”The in-memory book is the authoritative source for live order state. When the CLOB server starts, it rebuilds every order book from LIVE orders stored in PostgreSQL. During a graceful shutdown, current implied prices are written back to the database.
A scheduled engine restart takes place every Tuesday at 7:00 AM ET (11:00 UTC). During this window the matching engine drains all books and reconstructs them from the database.
Next Steps
Section titled “Next Steps”- Order Lifecycle — Understand how orders flow through the matching engine
- Positions & Tokens — Learn about outcome tokens and position management