Building a trading bot
This guide walks through a minimal bot on the Loaf dev environment: create an account, issue an API key, inspect a market and its order book, place a limit order, then cancel it. A short strategy sketch at the end shows how you might extend the same flow into a simple quoting loop.
Environment: Examples use https://dev.loafmarkets.com as the API base and placeholder asset sunset-villa (propertyId 42). Do not run this tutorial against production with real funds until you have tested on dev and reviewed compliance requirements.
What you will build
| Step | Goal |
|---|---|
| 1 | Sign up on dev.loafmarkets.com |
| 2 | Create an API key in the API settings UI |
| 3 | Load market metadata (Info API) and subscribe to the order book over WebSocket |
| 4 | Submit a limit order (HTTP shape + signing requirements) |
| 5 | Cancel that order |
Prerequisites
- A modern browser and terminal with
curlandjq(optional, for shell examples) - Node.js 20+ if you run the TypeScript snippets
- Basic familiarity with REST and environment variables
Configuration
Set these once for the rest of the guide:
export LOAF_API_BASE="https://dev.loafmarkets.com"
export LOAF_TOKEN_NAME="sunset-villa"
export LOAF_PROPERTY_ID=42| Variable | Purpose |
|---|---|
LOAF_API_BASE | Dev API host (all paths are under /api/...) |
LOAF_TOKEN_NAME | Lowercase property token symbol for Info routes |
LOAF_PROPERTY_ID | Numeric property ID for Orders routes |
LOAF_API_KEY | Programmatic key from the API settings UI (step 2) |
LOAF_PRIVY_JWT | Short-lived session token for order placement (see step 4) |
Authenticated read requests use the API key:
export LOAF_API_KEY="lm_dev_xxxxxxxx" # shown once when you create the key1. Create your Loaf account
- Open https://dev.loafmarkets.com/ .
- Choose Sign up and register with email or connect a Web3 wallet (Privy). Either path creates a self-custodial Loaf account suitable for API trading.
- Complete any onboarding prompts in the app (wallet setup, profile, and KYC if you plan to move fiat or trade size that requires verification).
Bots are available to all account types on dev; production may impose additional checks for onramp, offramp, or wholesale flows. See Portfolio and Offerings in the API reference when you need those capabilities.
Store recovery and wallet credentials securely. Loaf does not hold your keys; lost access cannot be reset through support in the same way as a centralized exchange password.
2. Create an API key
Programmatic access uses an API key you manage in the product UI—not your login password.
- Sign in on dev and go to https://dev.loafmarkets.com/api .
- Create a key with a clear label (for example
trading-bot-dev). - Copy the secret immediately; it is shown only once. Export it as
LOAF_API_KEY.
You can rotate keys by revoking old ones in the same UI. HTTP key management is not part of the public API reference—the UI is the recommended path for your first bot.
Use the API key in Authorization: Bearer $LOAF_API_KEY for read endpoints in this guide. Placing and cancelling orders still require a Privy session JWT and a session-signer signature on the order payload—see step 4.
3. Read the market and orders on the book
Before quoting or crossing the spread, load market context (Info API) and the central limit order book (resting bids and asks) for your token.
Market metadata
GET /api/info/:tokenName/header returns display fields, identifiers (including propertyId), and trading-related header data for the property page.
import axios from 'axios';
const base = process.env.LOAF_API_BASE ?? 'https://dev.loafmarkets.com';
const tokenName = process.env.LOAF_TOKEN_NAME ?? 'sunset-villa';
const { data: header } = await axios.get(
`${base}/api/info/${tokenName}/header`,
{ headers: { Authorization: `Bearer ${process.env.LOAF_API_KEY}` } },
);
console.log('propertyId:', header.propertyId);
console.log('header:', header);Use header.propertyId to confirm it matches LOAF_PROPERTY_ID before sending orders.
GET /api/info/:tokenName/overview adds longer-form property content (useful for bots that gate trading on disclosure or status flags):
curl -sS "$LOAF_API_BASE/api/info/$LOAF_TOKEN_NAME/overview" \
-H "Authorization: Bearer $LOAF_API_KEY" | jq .See Info for full parameter and error tables.
Orders on the book
Resting bids and asks are streamed on the Loaf WebSocket API. After connecting, subscribe to orderbook:{propertyId} using the propertyId from the Info header above. Updates use type orderbook_update with bids and asks arrays (each level has price, quantity, and orderId).
Example message shape:
{
"type": "orderbook_update",
"propertyId": 42,
"bids": [{ "price": 124.0, "quantity": 5.0, "orderId": 8801 }],
"asks": [{ "price": 126.0, "quantity": 3.0, "orderId": 8802 }]
}Use the book to choose prices: join the best bid, improve by one tick, or passively quote inside the spread. Your own open orders may appear in the book until they fill or you cancel them. Until WebSocket wiring is in place, you can reconcile resting orders via History (GET /api/history/orders).
4. Place an order
Order placement is a three-step flow: nonce → sign → submit.
POST /api/orders/nonce→ receivenonceanddeadline- Sign the order fields with your Privy session signer
POST /api/orders/with the signed body → receiveorderId
4a. Request a nonce
Each order needs a fresh nonce from POST /api/orders/nonce using your Privy JWT (obtain from your logged-in Loaf session or integration that exposes the session token—not the API key).
const { data: noncePayload } = await axios.post(
`${base}/api/orders/nonce`,
undefined,
{ headers: { Authorization: `Bearer ${process.env.LOAF_PRIVY_JWT}` } },
);
const { nonce, deadline } = noncePayload;4b. Build and sign the order
Construct a limit order below the best ask for a tutorial buy so it rests on the book instead of crossing (adjust prices using step 3 book data).
const order = {
propertyId: Number(process.env.LOAF_PROPERTY_ID ?? 42),
price: 120.0,
quantity: 1,
side: 'BUY' as const,
type: 'LIMIT' as const,
timeInForce: 'GTC' as const,
deadline: 0,
nonce,
};
// Sign `order` with your Privy session signer before POSTing.Signing is required. Retail orders must be signed with the Privy session signer before submission. The snippets above show the HTTP contract; in production use the Loaf web client signer flow or your app’s embedded wallet integration. Submitting unsigned bodies will fail validation.
For a market order, set "type": "MARKET" and "price": 0. See Orders — Create order.
4c. Submit the order
const { data: placed } = await axios.post(`${base}/api/orders/`, order, {
headers: { Authorization: `Bearer ${process.env.LOAF_PRIVY_JWT}` },
});
if (!placed.success) {
throw new Error(placed.errorMessage ?? 'Order rejected');
}
const orderId = placed.orderId;
console.log('placed orderId:', orderId);Save orderId from the response (example: 9001). Re-fetch the book from step 3 to see your order listed among resting bids.
5. Cancel the order
Cancel with POST /api/orders/cancel and the orderId from step 4. Use the same Privy JWT as placement.
const { data: cancelled } = await axios.post(
`${base}/api/orders/cancel`,
{ orderId: placed.orderId },
{ headers: { Authorization: `Bearer ${process.env.LOAF_PRIVY_JWT}` } },
);
console.log(cancelled);Confirm cancellation by polling GET /api/history/orders until the order shows cancelled, or until your orderId disappears from the WebSocket book feed.
Strategy sketch
Once the tutorial steps work, a simple market-making sketch might look like this:
- Subscribe to
orderbook:{propertyId}(or poll History while building WebSocket support). - Compute a quote: e.g. best bid + tick for your bid, best ask − tick for your ask, sized to a max inventory cap.
- Reconcile your resting orders (compare book entries to your last known
orderIds). - Replace stale quotes: cancel via
POST /api/orders/cancel, request a new nonce, sign, andPOST /api/orders/. - Stop on risk events: wide spread, missing book side, failed sign, or
success: falsefrom the API.
Control loop: fetch book → if spread is acceptable, cancel stale quotes → nonce, sign, place → sleep → repeat. If the spread is too wide or the book is empty, sleep and alert instead of quoting.
Hardening checklist for anything beyond dev:
- Idempotency: track nonces and
orderIds locally; never reuse a nonce. - Secrets: API key in a vault; Privy JWT short-lived; never commit
.env. - Observability: log HTTP status,
errorMessage, and book snapshots on failures. - Compliance: KYC, position limits, and jurisdiction rules before production capital.
Reference
| Topic | Link |
|---|---|
| Info (header, overview) | Info API |
| Nonce, place, cancel | Orders API |
| Your fills and history | History API |
| Dev app | dev.loafmarkets.com |
| API keys UI | dev.loafmarkets.com/api |