Complete guide for automated market makers integrating with the sim-trading platform.
Overview
The Market Maker API lets automated trading systems place and dynamically reprice
two-sided liquidity orders (bid + ask) as a single atomic pair. The core workflow is:
๐ Authenticate
โ
๐ Place bid + ask
โ
โป๏ธ Reprice loop
โ
๐๏ธ Cancel when done
โน๏ธ
All liquidity orders are linked in pairs. Cancelling or filling one side
automatically cancels the other. This is by design โ the platform enforces two-sided markets.
Authentication
All API calls require a Bearer token in the Authorization header.
Authorization: Bearer <token>
Tokens do not expire by session but may be revoked. Re-authenticate if you receive 401.
Base URL
http://<host>:3001
All paths in this document are relative to the base URL.
After a successful reprice, the old order IDs are no longer valid.
Save the new buyOrderId / sellOrderId from the response if you need to track individual orders.
The pairId changes too โ use the new buyOrderId's meta to get the new pairId,
or generate a new pairId yourself and pass it (coming in a future version).
If a pair is filled before your reprice call arrives, the reprice will return a 400
error (pairId has no pending orders). Always handle this case in your bot logic โ
it means a trade occurred and you should re-evaluate your inventory before placing a new pair.
Best Practices
Reprice frequency
Recommended minimum interval: 1 second between reprice calls for the same pair.
Rapid repricing (<500ms) is not blocked but increases margin churn and server load.
For volatile markets, consider wider spreads instead of faster repricing.
pairId management
Generate a new pairId for each pair placement (UUID or timestamp + random).
After a reprice, the server generates a new internal pairId for the new orders. Your original pairId is consumed.
Store the new buyOrderId / sellOrderId from the reprice response to track the live pair.
Margin management
Both sides of a pair require margin. A pair of 0.01 BTC at 70,000 USDT with leverage=1 freezes ~1,400 USDT total.
Reprice releases old margin and freezes new margin atomically โ net change is typically <1 USDT (market price drift).
Monitor availableBalance via GET /api/accounts/me to avoid margin failures.
Error handling
Always handle 400 on reprice (pair already filled or cancelled).
Re-authenticate on 401.
Back off on 500 โ do not retry in a tight loop.
Error Reference
HTTP
Error message
Cause
400
pairId, newBidPrice, newAskPrice ๅฟ ๅกซ
Missing required fields
400
newBidPrice ๅฟ ้ ๅฐๆผ newAskPrice
Inverted spread
400
ๆพไธๅฐ pairId=โฆ ็ pending ๆตๅๆง่จๅฎ
Pair already filled, cancelled, or invalid pairId
400
pairId=โฆ ็่จๅฎไธๅฎๆด
Only one side found (should not happen in normal flow)
400
ๅฏ็จไฟ่ฏ้ไธ่ถณ
Insufficient margin for new orders after reprice
401
Sessionๆ ๆๆๅทฒ่ฟๆ
Token expired or revoked โ re-authenticate
500
LiquidityOrderManager ๆชๅๅงๅ
Server startup issue โ contact platform admin
Market Maker API v1.0 ยท sim-trading-pro ยท
Questions? Contact the platform operator.
External Symbol Price Feed
Push a reference price (last trade price) for a custom symbol. The price is broadcast to all connected clients and used for chart display and order matching. bid/ask spreads are formed by your liquidity orders, not this endpoint.
POST/api/broker/price-feed
Request Body
symbol
string
Symbol code (e.g. XAUUSD)
price
number
Last trade price (reference price)
Legacy format: { bid, ask } is also accepted โ mid price is used automatically.
# Single push
curl -X POST http://HOST:3001/api/broker/price-feed \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"symbol":"XAUUSD","price":3275.50}'
# Continuous push script (bash)
TOKEN="YOUR_TOKEN"
while true; do
PRICE=3275.50 # replace with your data source
curl -s -X POST http://HOST:3001/api/broker/price-feed \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"symbol\":\"XAUUSD\",\"price\":$PRICE}" > /dev/null
sleep 1
done
import requests, time
BASE = "http://HOST:3001"
TOKEN = "YOUR_TOKEN"
HEADERS = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}
def push_price(symbol: str, price: float):
requests.post(f"{BASE}/api/broker/price-feed",
headers=HEADERS,
json={"symbol": symbol, "price": price})
# Example: push every tick from your data source
while True:
price = get_price_from_your_source() # replace with your API
push_price("XAUUSD", price)
time.sleep(0.1) # push every 100ms - backend throttles WS broadcast to 1/sec
Throttling: You can push at any frequency. The platform automatically throttles WebSocket broadcast to 1 price update/sec per symbol and order matching checks to once every 5 sec. K-line candles are updated on every tick in memory.
Create Custom Symbol
Register a new tradeable symbol on the platform. After creation, the symbol appears in the market list immediately (page refresh required on client).
REST APIs for white-label operators to manage client accounts and balances programmatically. All endpoints use X-API-Key header authentication.
Endpoint Summary
Method
Endpoint
Description
Required Scope
Sub-Broker
POST
/api/v1/broker/sub-brokers
Create a sub-broker (broker role)
sub-brokers:create
GET
/api/v1/broker/sub-brokers
List all sub-brokers
sub-brokers:list
POST
/api/v1/broker/clients/{id}/upgrade-to-broker
Upgrade Trader to broker role
sub-brokers:upgrade
Client
GET
/api/v1/broker/balance
Get your broker balance
balance:read
GET
/api/v1/broker/clients
List all clients with balances
clients:list
POST
/api/v1/broker/clients
Create a Trader account
clients:create
GET
/api/v1/broker/clients/{id}/balance
Get client balance
clients:balance
GET
/api/v1/broker/clients/{id}/orders
Get client trade history
clients:orders
GET
/api/v1/broker/clients/{id}/positions
Get client open positions
clients:positions
GET
/api/v1/broker/clients/{id}/withdrawals
Get client withdrawal history
clients:withdrawals
Transfer
POST
/api/v1/broker/clients/{id}/deposit
Deposit DCT to a client
transfer:deposit
POST
/api/v1/broker/clients/{id}/withdraw
Create withdrawal request
transfer:withdraw
GET
/api/v1/broker/transfer/history
List transfer records
transfer:history
Withdrawal / OTC
GET
/api/v1/broker/withdrawal/{id}
Get single withdrawal
withdrawal:read
GET
/api/v1/broker/withdrawals/pending
List pending withdrawals
withdrawal:list
POST
/api/v1/broker/withdrawal/{id}/lock
Lock (OTC fulfill)
withdrawal:lock
POST
/api/v1/broker/withdrawal/{id}/unlock
Release lock
withdrawal:unlock
Use Case: Client registers on your platform → call POST /clients to create their trading account → call POST /deposit after they fund → they trade on your white-label site → call GET /clients/{id}/orders to display their trade history.
Authentication โ API Key
All requests must include your API Key:
X-API-Key: bk_xxxxxxxxxxxxxxxxxxxxxxxx
Generate keys in BrokerPanel → 🔑 API Key.
POST /api/v1/broker/sub-brokers
Create a sub-broker account under your broker. The new account has role=broker and can log in to Broker Portal, manage their own clients, and create further sub-brokers.
๐ก Multi-level hierarchy: Your Platform → White-label Broker → Sub-Broker → Traders.
Each level manages only their own downstream accounts.
POST /api/v1/broker/clients/{user_id}/upgrade-to-broker
Upgrade an existing Trader account to broker role. The account retains its balance, trading history, and existing service relationship. The original service broker becomes the parentBrokerId.
โ Safe upgrade: Even if called by an admin, the parentBrokerId is taken from the original BrokerClientConfig.brokerId โ preserving the service relationship.
POST /api/v1/broker/clients/{user_id}/upgrade-to-broker
Request Body
Field
Type
Required
Description
password
string
optional
Set or update login password for Broker Portal
Request
POST /api/v1/broker/clients/usr_12345/upgrade-to-broker
X-API-Key: bk_your_key_here
Content-Type: application/json
{
"password": "NewBrokerPass123"
}
Response
{
"ok": true,
"user_id": "usr_12345",
"username": "sso_bb6eb7_usr_123",
"role": "broker",
"parent_broker_id": "original-service-broker-uuid",
"brand_root_id": "brand-uuid",
"message": "User upgraded to broker. They can now log in to Broker Portal.",
"broker_portal_url": "/pages/broker-portal.html"
}
// 409 Conflict โ account with this user_id already exists
{"error": "Client already exists: user_id=usr_12345", "username": "sso_bb6eb7_usr_123"}
The platform username is auto-generated from your user_id (format: sso_{brokerShort}_{userId}). If a user later logs in via SSO with the same user_id, they will use the same account.
GET /api/v1/broker/clients
List all Trader accounts under your broker, including their current balances.
const crypto = require('crypto');
function verifyWebhook(rawBody, signature, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(rawBody) // raw string body
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// In your Express webhook handler:
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['x-webhook-signature'];
if (!verifyWebhook(req.body.toString(), sig, WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body);
console.log('Event:', event.event);
res.sendStatus(200);
});
โ ๏ธ Webhook Secret is shown only once when you create an API Key with a Webhook URL.
Save it securely โ it cannot be retrieved later. If lost, delete and re-create the API Key.
Allows brokers to fulfill a user's pending withdrawal off-platform (P2P OTC).
The broker pays the user USDC directly from their own wallet; the platform
monitors the chain and releases the equivalent DCT to the broker's account balance.
โ ๏ธ Prerequisites: The broker account must have a walletAddress bound (used as the from address for on-chain verification).
Withdrawal States
Status
Description
User can cancel?
pending
Cooling period โ broker can lock
โ Yes
broker_locked
Locked by broker โ chain monitoring active
โ No
broker_completed
Chain confirmed, DCT settled to broker
โ
processing
Cooling period ended โ platform handles it
โ No
completed
Platform payout done
โ
Flow
1. User submits withdrawal โ status: pending (cooling period, e.g. 5 min)
2. Broker calls LOCK during cooling period โ status: broker_locked
3. Broker sends USDC from their wallet to the user's wallet address
4. Platform polls chain every 30 min (up to 24h)
5. Match found โ DCT credited to broker's platform balance โ broker_completed
6. No match within timeout โ auto-release back to pending
POST /api/v1/broker/withdrawal/{withdrawal_id}/lock
Lock a pending withdrawal. First-come, first-served โ atomic operation.
Request
POST /api/v1/broker/withdrawal/{withdrawal_id}/lock
X-API-Key: bk_your_key_here
Response
{
"ok": true,
"status": "broker_locked",
"userWalletAddress": "0xUserWallet...", // Send USDC here
"amount": 99, // USDC amount to send (netAmount after fee)
"timeoutHours": 24
}
After locking, send USDC from your broker's bound wallet to userWalletAddress. The platform will detect the transfer automatically.
Chain Verification Rules
from === broker's bound walletAddress
to === user's walletAddress
USDC amount โฅ netAmount (1% tolerance)
Transaction block time โฅ lock time
Error Responses
{ "error": "ๆญค็ณ่ซๅทฒ่ขซๅ ถไปไบคๆๅๆถๅฎ..." } // 409 โ already locked
{ "error": "ๅท้ๆๅทฒ็ตๆ..." } // 400 โ cooling period ended, platform took over
{ "error": "ไบคๆๅๅธณ่ๅฐๆช็ถๅฎ้ขๅ ๅฐๅ..." } // 400 โ broker wallet not bound
POST /api/v1/broker/withdrawal/{withdrawal_id}/unlock
Voluntarily release a locked withdrawal back to pending. Only the broker who locked it can unlock.
POST /api/v1/broker/withdrawal/{withdrawal_id}/unlock
X-API-Key: bk_your_key_here
If you manage your own users and want to give them access to the trading platform,
use SSO (Single Sign-On). Your backend signs a JWT; the user is redirected to our
platform which validates it and creates/logs-in the account automatically.
👤 Your user
→
🔑 Your backend signs JWT
→
🔗 Redirect to SSO URL
→
✓ Auto-login
SSO Endpoint
GEThttps://walltrade.cc/api/auth/sso?token=<JWT>
JWT Payload
{
"user_id": "42", // Your user's unique ID (string or integer)
"tenant_code": "rock-3", // Your broker code (assigned by us)
"email": "[email protected]",
"wallet_address": "0xUserWallet...", // Optional
"logout_redirect_url": "https://your-app.com/logout" // Optional
}
The SSO endpoint performs a browser redirect. Do NOT call it via fetch() โ
you must do a full browser redirect (window.location.href = url or a
server-side 302 redirect).
After Login โ Get Session Token
Once the user lands on the chart page, the session token is available in
localStorage under key auth_token.
Use it for all subsequent API calls.
Always implement reconnection logic. On reconnect, re-send auth and all
subscribe messages โ the server does not remember previous subscriptions.
Widget Embed โ Overview
The ST Trading Widget is a self-contained Web Component that can be embedded in any website.
It provides a full trading UI โ market data, order placement, order management, and account info โ
scoped to a specific broker.
Users who trade through the widget are automatically associated with the embedding broker's account.
You can find your pre-filled embed code in Broker Panel โ Management โ ๐ฆ Widget Embed Code.
Just copy and paste it into your website.
✓
No CORS configuration needed.
The API server allows cross-origin requests from any domain, so the widget works on any external website out of the box.
Sensitive endpoints are protected by JWT bearer tokens, not by CORS origin restrictions.
Attributes
Attribute
Required
Default
Description
broker-uid
โ
โ
Your broker UUID. All user accounts created via this widget are tied to this broker.
api-url
โ
โ
Backend API URL (e.g. https://walltrade.cc). Also used to resolve brand config.
default-symbol
โ
BTCUSDT
The symbol shown when the widget first loads.
width
โ
400px
Widget width. Accepts any CSS value: 360px, 480px, 100%.
theme
โ
dark
Initial theme. Options: dark, light.
sso-token
โ
โ
JWT for silent SSO login. If provided, the user is logged in automatically without seeing the login form.
brand-domain
โ
hostname of api-url
Override domain used to fetch brand config. Normally not needed.
SSO Integration
If your platform already has a login system, pass a signed JWT via sso-token
to log the user in silently. The widget calls POST /api/auth/sso/token with the token.