Skip to main content

Overview

A typical market making bot has four concurrent loops:
  1. Market data ingestion - Subscribe to the orderbook WebSocket for best bid/ask.
  2. Quote generation - Decide where to quote (spreads, levels, sizes) based on your model + inventory.
  3. Execution - Atomically cancel + place quotes using client.place_orders().
  4. Order lifecycle & fills - Listen to on-chain events to track placements, fills, and cancellations.
The SDK handles (1), (3), and (4). You supply (2).
Make sure you’ve completed the Quick Start before following this guide — you’ll need a configured KuruClient with funded margin.

Step 1 - Start the Client

client.start() performs EIP-7702 authorization and connects to the RPC WebSocket for on-chain event tracking.
await client.start()

Step 2 - Set Order Callback

Set an order callback to track your order lifecycle:
from kuru_sdk_py.manager.order import Order, OrderStatus

active_cloids: set[str] = set()

async def on_order(order: Order) -> None:
    if order.status in (OrderStatus.ORDER_PLACED, OrderStatus.ORDER_PARTIALLY_FILLED):
        active_cloids.add(order.cloid)
    if order.status in (OrderStatus.ORDER_CANCELLED, OrderStatus.ORDER_FULLY_FILLED):
        active_cloids.discard(order.cloid)

client.set_order_callback(on_order)
Alternatively, read from client.orders_manager.processed_orders_queue instead of using callbacks.

Step 3 - Subscribe to Real-time Orderbook Data

from kuru_sdk_py.feed.orderbook_ws import KuruFrontendOrderbookClient, FrontendOrderbookUpdate

best_bid: float | None = None
best_ask: float | None = None

async def on_orderbook(update: FrontendOrderbookUpdate) -> None:
    global best_bid, best_ask
    if update.b:
        best_bid = KuruFrontendOrderbookClient.format_websocket_price(update.b[0][0])
    if update.a:
        best_ask = KuruFrontendOrderbookClient.format_websocket_price(update.a[0][0])

client.set_orderbook_callback(on_orderbook)
await client.subscribe_to_orderbook()
WebSocket price/size units: The frontend orderbook WebSocket sends prices in 10^18 format (regardless of market precision) and sizes in the market’s size_precision format. Use these helpers to convert:
  • KuruFrontendOrderbookClient.format_websocket_price(raw_price_1e18)float
  • KuruFrontendOrderbookClient.format_websocket_size(raw_size, market_config.size_precision)float

Step 4 - Cancel and Place Quotes

Build a grid of orders, cancel stale ones, and send them in a single batch transaction:
import time
from kuru_sdk_py.manager.order import Order, OrderType, OrderSide

def build_grid(mid: float) -> list[Order]:
    ts = int(time.time() * 1000)
    spread_bps = 10  # 0.10%
    size = 5.0

    bid = mid * (1 - spread_bps / 10_000)
    ask = mid * (1 + spread_bps / 10_000)

    return [
        Order(cloid=f"bid-{ts}", order_type=OrderType.LIMIT, side=OrderSide.BUY, price=bid, size=size),
        Order(cloid=f"ask-{ts}", order_type=OrderType.LIMIT, side=OrderSide.SELL, price=ask, size=size),
    ]

orders = []
orders += [Order(cloid=c, order_type=OrderType.CANCEL) for c in active_cloids]
orders += build_grid(mid=100.0)

txhash = await client.place_orders(
    orders,
    post_only=True,           # maker-only (recommended)
    price_rounding="default",  # buy rounds down, sell rounds up
)

Tick Size and Rounding

On-chain prices must align to market_config.tick_size. When you pass float prices to place_orders(), the SDK converts them to integers using price_precision and then rounds to tick size. price_rounding options:
OptionBehavior
"default"Round down for buys, up for sells (recommended)
"down"Round down for both sides
"up"Round up for both sides
"none"No tick rounding (only if you already quantize yourself)

Step 5 - Cancel All Orders

To cancel all active orders for the market (useful for circuit breakers or graceful shutdown):
await client.cancel_all_active_orders_for_market()
This cancels every order you have on the book for this market in a single transaction, regardless of whether you’re tracking them locally.

Step 6 - React to Fills and Inventory

The SDK delivers order status updates via the callback or queue:
StatusMeaning
ORDER_PLACEDConfirmed on book
ORDER_PARTIALLY_FILLEDSize reduced
ORDER_FULLY_FILLEDSize goes to 0
ORDER_CANCELLEDRemoved from book
Typical market maker reactions:
  • Record fills and PnL
  • Adjust inventory targets
  • Skew spreads based on inventory (long base → tighten asks, widen bids)
  • Refresh quotes more aggressively after fills
Compute inventory from margin balances:
margin_base_wei, margin_quote_wei = await client.user.get_margin_balances()
base = margin_base_wei / (10 ** market_config.base_token_decimals)
quote = margin_quote_wei / (10 ** market_config.quote_token_decimals)

Next Steps

See the Reference for order types, configuration presets, and production guidance.