Skip to content

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.


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.

PriceImplied Probability
$0.2525% chance
$0.5050% chance
$0.7575% 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.

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.


Openfish’s matching engine keeps a fully in-memory order book per token, relying on Rust’s BTreeMap for deterministic ordering:

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:

FieldTypeDescription
order_idUUIDUnique identifier for the order
ownerStringAPI key of the order owner
maker_addressStringEthereum address that signed the order
priceDecimalLimit price
remaining_sizeDecimalUnfilled quantity remaining
order_typeStringGTC, GTD, FOK, or FAK
expirationi64Unix timestamp for GTD expiration (0 for non-GTD)
created_atDateTimeTimestamp of order creation

The engine applies strict price-time priority when matching:

  1. Price comes first: Orders with better prices match before those with worse prices. For bids, higher is better. For asks, lower is better.
  2. 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.


Several derived price metrics are available through the CLOB API:

The highest price any buyer currently offers (best bid) and the lowest price any seller currently accepts (best ask).

Terminal window
# Get the current price (best bid/ask) for a token
curl "https://api.openfish.fun/price?token_id=YOUR_TOKEN_ID"

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) / 2
Terminal window
curl "https://api.openfish.fun/midpoint?token_id=YOUR_TOKEN_ID"

The gap between the best ask and best bid. Narrower spreads signal deeper liquidity and lower trading friction.

spread = best_ask - best_bid
Terminal window
curl "https://api.openfish.fun/spread?token_id=YOUR_TOKEN_ID"

To reduce round trips, multiple tokens can be queried in a single call:

Terminal window
# Batch midpoints
curl -X POST "https://api.openfish.fun/midpoints" \
-H "Content-Type: application/json" \
-d '["token_id_1", "token_id_2", "token_id_3"]'
# Batch spreads
curl -X POST "https://api.openfish.fun/spreads" \
-H "Content-Type: application/json" \
-d '["token_id_1", "token_id_2"]'

You can retrieve the full ladder of resting orders, aggregated by price level:

Terminal window
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.


Three tick sizes are available — each defines the smallest allowed price increment for orders in that market:

Tick SizeExample PricesTypical Use
0.010.01, 0.02, …, 0.99Standard markets with moderate volume
0.0010.001, 0.002, …, 0.999Higher precision for liquid markets
0.00010.0001, 0.0002, …, 0.9999Maximum 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.

Terminal window
# Check the tick size for a specific token
curl "https://api.openfish.fun/tick-size?token_id=YOUR_TOKEN_ID"

A freshly created market starts with an empty book. The first price materializes when overlapping buy and sell interest appears:

  1. One participant posts a bid to buy Yes at $0.60
  2. Another participant posts a bid to buy No — functionally equivalent to offering to sell Yes at $0.40
  3. 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.


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:

Terminal window
# Get price history for a token
curl "https://api.openfish.fun/prices-history?token_id=YOUR_TOKEN_ID"
# Batch price history
curl -X POST "https://api.openfish.fun/batch-prices-history" \
-H "Content-Type: application/json" \
-d '["token_id_1", "token_id_2"]'

The CLOB follows a hybrid-decentralized model:

  1. Off-chain matching — The Rust matching engine pairs compatible orders in memory with sub-millisecond latency
  2. 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.

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.


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.