> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kuru.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Market Making Guide

> Build a market making bot step-by-step with the Python SDK

## 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).

<Tip>
  Make sure you've completed the [Quick Start](/sdk/py-sdk-quickstart) before following this guide — you'll need a configured `KuruClient` with funded margin.
</Tip>

## Step 1 - Start the Client

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

```python theme={null}
await client.start()
```

## Step 2 - Set Order Callback

Set an order callback to track your order lifecycle:

```python theme={null}
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)
```

<Note>
  Alternatively, read from `client.orders_manager.processed_orders_queue` instead of using callbacks.
</Note>

## Step 3 - Subscribe to Real-time Orderbook Data

```python theme={null}
from decimal import Decimal
from kuru_sdk_py.feed.orderbook_ws import FrontendOrderbookUpdate

best_bid: Decimal | None = None
best_ask: Decimal | None = None

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

client.set_orderbook_callback(on_orderbook)
await client.subscribe_to_orderbook()
```

Prices and sizes in `FrontendOrderbookUpdate` are pre-normalized to human-readable `Decimal` values. No manual conversion is needed.

## Step 4 - Cancel and Place Quotes

Build a grid of orders, cancel stale ones, and send them in a single batch transaction:

```python theme={null}
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:

| Option      | Behavior                                                 |
| ----------- | -------------------------------------------------------- |
| `"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):

```python theme={null}
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:

| Status                   | Meaning           |
| ------------------------ | ----------------- |
| `ORDER_PLACED`           | Confirmed on book |
| `ORDER_PARTIALLY_FILLED` | Size reduced      |
| `ORDER_FULLY_FILLED`     | Size goes to 0    |
| `ORDER_CANCELLED`        | Removed 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:

```python theme={null}
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](/sdk/py-sdk-reference) for order types, configuration presets, and production guidance.
