Trading
Place market, limit and stop orders — the order body, riskPercentage sizing, and every response shape.
POST /bot/v1/spot/orders
Wellat has pairs that it supports. Find them here before you execute your first trade.
Signed, with a JSON body and a fresh idempotency-key header — a UUID you generate, one per trade intent. Reuse a key only to retry the same trade (see Signal Lifecycle & Recovery).
Order body
Common fields (all order types):
There is no strategy identifier in the body — the strategy is resolved from the API key the request is signed with (see Strategies & Your API Key).
| Field | Type | Rules |
|---|---|---|
symbol | string | required, e.g. "BTCUSDT" |
side | string | "BUY" or "SELL" |
tradeType | string | "MARKET" | "LIMIT" | "STOP_LOSS" | "STOP_LOSS_LIMIT" |
riskPercentage | number | required, > 0 and ≤ 100 — see sizing below |
quantity | number | optional and advisory only — never used for sizing |
Type-specific fields:
tradeType | Required fields | Meaning |
|---|---|---|
MARKET | — | fills immediately at market |
LIMIT | price | rests at price until filled |
STOP_LOSS | stopPrice | market order armed when stopPrice is crossed |
STOP_LOSS_LIMIT | price + stopPrice | limit order at price, armed at stopPrice |
{ "symbol": "BTCUSDT", "side": "BUY", "tradeType": "MARKET", "riskPercentage": 10 }{ "symbol": "BTCUSDT", "side": "BUY", "tradeType": "LIMIT", "riskPercentage": 10, "price": 60000 }{ "symbol": "BTCUSDT", "side": "SELL", "tradeType": "STOP_LOSS", "riskPercentage": 25, "stopPrice": 58000 }{ "symbol": "BTCUSDT", "side": "SELL", "tradeType": "STOP_LOSS_LIMIT", "riskPercentage": 25, "price": 57900, "stopPrice": 58000 }
NotesUSDT must be the quote side of the pair (
BTCUSDTyes; USDT-base pairs are rejected). OnLIMITorders the OCO fields (takeProfitLimit,stopLossTrigger,stopLossLimit) are accepted by validation but rejected at execution until OCO support ships — place the protective stop as a separate signal instead. Body limit: 10 KB (413beyond).
How sizing works — riskPercentage
riskPercentageriskPercentage is the percentage of the free (undeployed) balance committed to the signal:
- For the strategy's track record: a percentage of the strategy's free virtual USDT (buys) or free base asset (sells).
- For each copier: the same percentage of that copier's free balance.
It is not a fraction of total equityIf the strategy is 30% deployed, a
riskPercentage: 10signal commits 10% of the remaining 70% — i.e. 7% of equity.quantity, if sent, is validated for shape but never consulted for sizing. Sizing by percentage is what keeps copier accounts of different sizes proportional to the track record.
Responses
Executed (market order) — 200:
{
"status": true,
"message": "Trade executed successfully",
"data": {
"orderId": "…",
"clientOrderId": "DEMO-<your idempotency-key>",
"symbol": "BTCUSDT",
"side": "BUY",
"quantity": 0.001,
"price": 60000,
"fee": 0.000001,
"feeAsset": "BTC",
"replayed": false,
"orderStatus": "FILLED"
}
}orderStatus is "FILLED" for market signals and "OPEN" for resting placements (limit/stop). replayed: true means this response was served from a previously executed identical signal (same idempotency-key).
Executed placements also carry a collectiveId — the handle for POST /bot/v1/spot/orders/cancel. If you don't store it from the response, you can always recover it from GET /bot/v1/spot/orders (see Signal Lifecycle & Recovery).
Accepted but pending — 202:
{
"status": false,
"message": "Signal accepted, execution pending",
"data": { "signalId": "0198f9…" }
}
202 is not a failureYour signal was durably recorded but the execution result could not be returned in time. Do not send a new idempotency key. Either poll
GET /bot/v1/spot/signals/<signalId>or re-send the same request with the sameidempotency-key— both converge on the one and only execution.
Rejected — 400 with a reason: no LIVE strategy bound to your API key, missing price on a LIMIT, riskPercentage out of range, or a pair that cannot be priced.
Next: Signal Lifecycle & Recovery.
Updated 8 days ago