Skip to content

Trading

Market makers place and adjust two-sided limit orders through the CLOB REST API. The Rust SDK takes care of EIP-712 signing, HMAC authentication, and batch submission so you can focus on pricing logic.


The fundamental market-making action is placing a bid below your fair value estimate and an ask above it.

use openfish_client_sdk::clob::types::Side;
use openfish_client_sdk::types::dec;
let token_id = "3409705850427531082723332342151729...".parse()?;
// Post a bid at 0.48
let bid = client.limit_order()
.token_id(token_id)
.price(dec!(0.48))
.size(dec!(1000))
.side(Side::Buy)
.build().await?;
let signed_bid = client.sign(&signer, bid).await?;
client.post_order(signed_bid).await?;
// Post an ask at 0.52
let ask = client.limit_order()
.token_id(token_id)
.price(dec!(0.52))
.size(dec!(1000))
.side(Side::Sell)
.build().await?;
let signed_ask = client.sign(&signer, ask).await?;
client.post_order(signed_ask).await?;

The post_orders endpoint accepts up to 15 orders in one request. Batching cuts down on round-trip latency and lets you refresh multiple price levels in a single atomic call.

let mut signed_orders = Vec::new();
for (price, side) in [
(dec!(0.48), Side::Buy),
(dec!(0.47), Side::Buy),
(dec!(0.52), Side::Sell),
(dec!(0.53), Side::Sell),
] {
let order = client.limit_order()
.token_id(token_id)
.price(price)
.size(dec!(500))
.side(side)
.build().await?;
signed_orders.push(client.sign(&signer, order).await?);
}
let response = client.post_orders(signed_orders).await?;

Always prefer post_orders over multiple individual post_order calls.


TypeBehaviourWhen to Use
GTCRests on the book until filled or cancelledDefault for passive quoting
GTDAutomatically expires at a specified Unix timestampExpire quotes before known events or catalysts
FOKMust fill entirely and immediately, otherwise the whole order is killedAggressive rebalancing — all or nothing
FAKFills whatever is immediately available, kills the remainderRebalancing where partial fills are acceptable

GTC and GTD are the primary tools for passive market making. FOK and FAK are intended for taking liquidity when rebalancing inventory.

use chrono::{TimeDelta, Utc};
use openfish_client_sdk::clob::types::OrderType;
let order = client.limit_order()
.token_id(token_id)
.price(dec!(0.50))
.size(dec!(1000))
.side(Side::Buy)
.order_type(OrderType::GTD)
.expiration(Utc::now() + TimeDelta::hours(1))
.build().await?;
let signed = client.sign(&signer, order).await?;
client.post_order(signed).await?;

Several methods exist for pulling resting orders off the book.

// Cancel a single order by ID
client.cancel_order(order_id).await?;
// Cancel all orders in a specific market
client.cancel_market_orders(&cancel_market_request).await?;
// Cancel every open order across all markets (kill switch)
client.cancel_all_orders().await?;

Wire cancel_all_orders into your emergency kill switch. Trigger it without hesitation when you observe abnormal fill patterns, position limit violations, or connectivity problems.


Each market enforces a minimum tick size. Orders priced off-grid are rejected.

let resp = client.tick_size(token_id).await?;
// resp.minimum_tick_size: TickSize::Tenth | Hundredth | Thousandth | TenThousandth

Check the tick size before quoting a new market.


use openfish_client_sdk::clob::types::request::OrdersRequest;
// Fetch a single order
let order = client.order(order_id).await?;
// List all open orders in a market
let request = OrdersRequest::builder()
.market("0xbd31dc8a...".parse()?)
.build();
let orders = client.orders(&request, None).await?;

For real-time fill notifications, subscribe to the WebSocket user channel rather than polling.


Makers pay zero fees on Openfish. Taker fees vary by market category and fund the Maker Rebates program. Markets with fees enabled have fees_enabled: true in the market object.


  • Quote both sides — Two-sided liquidity earns maximum rewards and rebates.
  • Skew on inventory — Shift your quotes when inventory becomes unbalanced on one side.
  • Cancel stale quotes — Pull orders immediately when market conditions change.
  • Use GTD for events — Auto-expire quotes before known catalysts to prevent stale exposure.
  • Batch orders — Submit multiple quotes in a single post_orders call.
  • WebSocket for data — Subscribe to real-time feeds instead of polling REST endpoints.
  • Size limits — Never quote more than your available token balance.
  • Price guards — Validate prices against the book midpoint; reject outliers.
  • Kill switch — Call cancel_all_orders on errors or position breaches.
  • Monitor fills — Use the WebSocket user channel for real-time fill notifications.