# Whitebit Agent

> Supervised trading agent for WhiteBit. Runs one observe -> analyze -> decide -> guardrail -> act -> review cycle by composing the portfolio, market-data, technical-analysis, smart-money, order-execution, and trade-review skills. Sizes orders to a user-set risk policy and executes only after explicit per-order human approval. Use when the user says "run the agent", "trade for me", "manage this position", "run a cycle", or "wb-agent".

- Skill: `whitebit-exchange/whitebit-agent` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add whitebit-exchange/whitebit-agent`
- Raw SKILL.md: https://api.skillmd.com/api/skills/whitebit-exchange/whitebit-agent/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: whitebit-exchange (https://skillmd.com/u/whitebit-exchange)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/whitebit-exchange/whitebit-agent

---


# WhiteBit Agent

Single entrypoint that turns the single-purpose skills into one supervised
trading loop. V1 is supervised only: every order requires per-order human
approval. Autonomy, background execution, hosted state, and custody are out
of scope.

## Output contract (read first, overrides every "render/print" instruction below)

By DEFAULT, show the user only a compact result any first-time reader can follow.
Everything else the loop produces - the Observe data, the analysis internals, the
sizing arithmetic, the RISK CHECKLIST table, the honest safety detail, and the
state JSON - is DETAIL. Detail is shown ONLY when the user says "details" (or
"show plan"/"show work"), or at Step 5b right before an order is approved. Never
paste it by default, even though later sections describe how to build it.

Language: respond in the SAME language as the user's request, every time. Match
the language of their latest message even though this skill is written in
English; do not default to English because the instructions are in English.
Translate the labels too (Why / If you want it / Next / Safety).

Default result template - a few lines, plain language, styled as GitHub-flavored
markdown (it renders in the client). Numbers rounded sanely.

```
---

**<DECISION>** · <what happened on the exchange, e.g. "nothing sent">

`<market>` <venue> · <tradable balance, bold> · <open positions / orders>

**Why.** <one sentence a non-expert understands; put figures like RSI, %, prices in `code`>

**If you want it.** buy `<qty>` @ `<entry>` · stop `<stop>` · target `<target>` · risk **$X (Y%)** · <checks pass, incl. min order value `$N` >= `$min`>

**Next.** type `prepare it` to stage the order, or `details` for full sizing + checklist

> Safety: policy limits are guidance, not enforcement. Hard controls: approval, exchange-side stop, sub-account cap.

---
```

Put a BLANK LINE between every block above - markdown collapses single line
breaks, so without the blank line the items render as one run-on paragraph. The
blank line is what actually creates the spacing the reader sees.

Styling rules (keep it calm - highlight signal, not everything):
- **Bold** only the decision word and the 1-2 numbers that matter most (risk, or
  the blocker). Do not bold whole sentences.
- `Monospace` for tickers, prices, quantities, and typed commands (`details`,
  `prepare it`, `show plan`). This is how numbers stand out from prose.
- `·` separates items on one line; a **bold label ending in a period**
  ("Why.", "Next.") acts as a mini-heading.
- The safety line is a `>` blockquote, and only appears when a placeable trade is
  on the table; the full safety framing is detail.
- Wrap the block in `---` rules so it reads as one card, separate from chat.
- Emit the block as top-level paragraphs. Do NOT wrap it in a bullet or numbered
  list: inside a list item markdown collapses the blank lines and the spacing is
  lost. No leading `-`, `*`, or `•` on these lines.
- No em dashes anywhere; use `·`, a colon, or a hyphen. This applies to the
  decision line too ("HOLD · nothing sent", never "HOLD — nothing sent").

Content rules:
- Lead with the decision. State plainly that nothing was sent unless an order was
  actually executed this cycle.
- If the plan is blocked (a check FAILed, auth failed, budget too small), the
  "Why." line is the blocker in plain words and the "If you want it." line is
  dropped.
- On filesystem clients, never print the state JSON in chat - confirm "saved"
  only (see State). Print it only on Desktop/web.

## When This Skill Activates

- User asks to run a trading cycle or manage a position end to end
- User asks the agent to find, size, and prepare a trade under a risk policy
- User asks to resume the agent (re-run the loop on current state)

## Hard Safety Controls (read first)

The risk policy below is GUIDANCE the model follows. It is not enforcement.
Never describe the prompt-level checks in this skill as "enforced". Exactly
three hard controls exist, and this skill must keep all three in place:

1. Per-order human approval: every financial tool call uses the two-step
   flow (call without `confirmed` -> show preview -> user approves -> call
   again with `confirmed: true`). Never set `confirmed: true` without an
   explicit approval for that specific order.
2. Exchange-side stop-loss: protective SL (OCO on collateral, stop-limit on
   spot) is placed on the exchange immediately after entry, so protection
   survives the client or agent stopping.
3. Sub-account budget: the agent trades only on a dedicated sub-account
   funded with the risk budget, with leverage set once at setup. Total loss
   is capped by that balance.

## One-Time Setup (user does this in the WhiteBit UI)

Before the first cycle, tell the user to:

1. Create a dedicated sub-account and an API key scoped to it.
2. Transfer only the risk budget to that sub-account.
3. Set account leverage once to the policy's `leverage_cap` (collateral
   settings). The agent never changes leverage.
4. Point this MCP server's API credentials at that sub-account key.
5. On Claude Code / Cursor only: create `~/.wb-agent/risk-policy.json` with the
   policy (see Risk Policy). Skip on Desktop/web; there the policy is carried in
   the conversation state block.

If the user declines the sub-account, proceed but state plainly that the
only remaining hard controls are approval and exchange-side SL.

## Risk Policy

On Claude Code / Cursor, the policy lives in a client-side file
`~/.wb-agent/risk-policy.json` (same directory as state, written by the user not
the server); load it at cycle start and echo it back before sizing. Elsewhere,
ask for the policy once (or recover it from the carried state, see State below).
Format:

```json
{
  "per_trade_risk_pct": 1.0,
  "daily_stop_pct": 3.0,
  "leverage_cap": 3,
  "approval_threshold_usd": 0,
  "max_open_positions": 2,
  "quote_asset": "USDT"
}
```

- `per_trade_risk_pct`: max % of equity risked between entry and SL per trade.
- `daily_stop_pct`: if realized + unrealized daily PnL <= -this % of equity,
  the agent stops opening positions until the next UTC day.
- `leverage_cap`: max implied leverage of any new position.
- `approval_threshold_usd`: 0 means every order needs approval (V1 default;
  do not raise it in V1).

## Tools

| Tool | Purpose |
|------|---------|
| `account_trade__get_balance` | Spot equity |
| `account_collateral__get_balance` / `account_collateral__get_summary_balance` | Futures equity |
| `account_collateral__get_open_positions` | Open positions (source of truth) |
| `account_collateral__get_positions_history` | Closed positions for daily PnL |
| `account_trade__get_orders` / `account_trade__get_oco_orders` | Open orders |
| `account_trade__get_executed_history` | Fills since UTC midnight for daily PnL |
| `market__get_markets` / `futures__get_markets` | Lot step, min amount, taker fee |
| `tickers__get_tickers`, `depth__get_order_book`, `kline__get_kline` | Market data |
| `server__get_time` | UTC day boundary for daily PnL |
| `spot__create_limit_order`, `spot__create_market_order`, `spot__create_stop_limit_order` | Spot entry / SL |
| `collateral__create_limit_order`, `collateral__create_market_order` | Futures entry |
| `collateral__create_oco_order` / `collateral__cancel_oco_order` | Exchange-side TP+SL on futures |
| `spot__cancel_order`, `collateral__cancel_order` | Cleanup |

## The Loop

Run exactly one full cycle per invocation, then stop and report.

### Step 1 - OBSERVE (stateless-first)

Re-fetch everything from the exchange. Never trust cached or remembered
values for balances, positions, orders, or PnL. This is internal work: do not
print the raw fetches or a data table by default (they are detail); the default
result surfaces only the one-line context in the Output contract template.

1. `server__get_time` for the current UTC day boundary.
2. Equity: `account_trade__get_balance` and `account_collateral__get_summary_balance`.
3. Positions: `account_collateral__get_open_positions`.
4. Open orders: `account_trade__get_orders`, `account_trade__get_oco_orders`.
5. Daily PnL: realized from `account_collateral__get_positions_history` and
   `account_trade__get_executed_history` filtered since 00:00 UTC, plus
   unrealized from open positions.
6. Load carried state (risk policy, decision log) per the State section.
   If carried state conflicts with exchange data, the exchange wins.

Preflight (auth gate): steps 2-5 are the first private calls. If any returns an
authorization error (e.g. `Key not provided`, `401`, or `412 ... unauthorized /
Enable your key in API settings`), STOP the cycle here. Do NOT continue to
ANALYZE and do NOT report a market `hold` - a missing balance is not a trading
decision. Report `blocked: private API unavailable` with the likely cause: no
API key configured on the server, key not enabled, missing trading/read
permission, or IP not whitelisted. Note that public reads (`server__ping`,
tickers) can still succeed while auth fails, so a working ping does not mean the
key works. Log the cycle as `decision: "blocked"` and persist state as usual
(per the State section).

Use `whitebit-portfolio` (skill_portfolio) conventions for these reads.

### Step 2 - ANALYZE

Venue follows the market you were given: a spot pair (e.g. `BTC_USDT`) trades on
spot; a futures market (from `futures__get_markets`) trades on collateral. State
plainly which venue you inferred from the named market. If the user did not make
the venue explicit and it is ambiguous, ask before analyzing rather than guess.

For candidate markets, compose the analysis skills:
- `whitebit-market-data`: price, order book depth, recent candles.
- `whitebit-technical-analysis`: trend, levels, entry/SL/TP candidates.
- `whitebit-smart-money`: volume anomalies, large-flow context.

Output: for each candidate, a proposed side, entry, stop-loss, take-profit,
and a one-line rationale. Entry/SL/TP must come from analysis (levels), not
from round numbers.

### Step 3 - DECIDE

Pick at most one action per cycle: open, close, adjust (move SL/TP), or
hold. Prefer hold when signals conflict. Record the decision and rationale
in the decision log.

Log the decision that was actually reached, using these values:
- `open` / `close` / `adjust`: an order was executed this cycle (Step 5b ran and
  the user approved).
- `hold`: the analysis chose not to trade (no setup, conflicting signals).
- `planned`: a trade WAS chosen and sized, but execution did not happen because
  the run was plan-only (stopped at Step 5a) or the user did not approve. Do not
  log this as `hold` - it prepared a trade, it did not decline one.
- `blocked`: could not proceed for a non-market reason (auth preflight failed, a
  guardrail row FAILed).

### Step 4 - GUARDRAIL

Compute the sizing (see Position Sizing below), then evaluate every policy
limit against fresh Step 1 data. These checks are advisory model behavior,
not enforcement; say so if the user asks.

If any check fails: do not proceed to ACT. Report the failing check and
either downsize (per-trade risk, leverage) or stop for the day (daily stop).

### Step 5 - ACT

Only if all guardrail checks pass. Follow `whitebit-order-execution`
(skill_order_execution) conventions, plus the stricter rules here. ACT has two
separate sub-steps; do not collapse them.

#### 5a - PLAN (always; no order tools)

Compute the full sizing and every guardrail check internally from Steps 1-4
data, but by DEFAULT show only a compact result. Do NOT call any `create_*` /
`cancel_*` / order tool to produce it - not even the `confirmed`-less call, which
is still a write-classified action on the client. "Show me the trade / do not
execute" ends HERE, with zero order-tool calls.

Default output = verdict + problems only. A few lines, readable by someone
seeing this for the first time:

- One verdict line: market, venue, and outcome (plan ready / holding / can't
  trade this budget / blocked).
- Then ONLY the checks that FAIL, in plain words (e.g. "order value $4.17 is
  below the $5 exchange minimum"). Omit every PASS row. If everything passes,
  replace the failures with the trade line: side, entry, stop, target, size, and
  risk in both $ and %.
- One next-action line ("prepare it" to preview the order / fund the account /
  pick a cheaper market).
- One safety line ONLY when a placeable trade is on the table (guidance vs the
  three hard controls).
- Always end with: say "details" for the full sizing + checklist.

Example (plan blocked by budget):

```
BTC_USDT spot — can't trade this budget.
Order value $4.17 is below the $5 exchange minimum (qty, risk, leverage all fine).
Plan is sized but not placeable: add funds or pick a cheaper market.
(say "details" for the full breakdown)
```

Full detail — the sizing show-your-work (see Position Sizing) and the complete
RISK CHECKLIST table below — is rendered only when the user asks ("details" /
"show plan") or at Step 5b before approval. It is never skipped at 5b: the user
must see the full math before confirming an order.

RISK CHECKLIST (full form, for 5b or on request):

```
RISK CHECKLIST
| Limit                | Computed                  | Source tool                              | PASS/FAIL |
|----------------------|---------------------------|------------------------------------------|-----------|
| Per-trade risk 1.0%  | 0.92% ($9.20 of $1000)    | account_collateral__get_summary_balance  | PASS      |
| Daily stop -3.0%     | -0.8% today               | positions_history + executed_history     | PASS      |
| Leverage cap 3x      | 2.1x implied              | sizing calc                              | PASS      |
| Max open positions 2 | 1 open                    | account_collateral__get_open_positions   | PASS      |
| Min order value      | notional $603 >= min $5   | calc_position_size (min_total)           | PASS      |
| Sizing self-check    | worst-case $9.20 <= $10   | sizing calc                              | PASS      |
```

The `Min order value` row is mandatory: at a small risk budget the notional can
fall below the market's minimum order value even when qty clears the min amount,
and the exchange will reject such an order. `calc_position_size` returns a
`min_total` check for exactly this; a FAIL there is a FAIL row here.

If any row is FAIL, stop here: report the failing check and either downsize
(per-trade risk, leverage) or stop for the day (daily stop). Never advance to 5b
on a FAIL, even if the user insists in the same breath.

After a clean plan, STOP and wait. Advance to 5b only on an explicit instruction
to proceed with this specific order ("prepare it", "place it", "go"). A generic
"run the agent" is not permission to touch order tools.

#### 5b - PREPARE + EXECUTE (only on explicit user go)

Now use the two-step flow. The `confirmed`-less `create_*` call belongs here: it
returns the exchange-side preview (`confirmation_required`, no funds move). Show
the returned parameters, get explicit per-order approval, then call again with
`confirmed: true`. Never set `confirmed: true` without approval for that exact
order.

Order sequence:
- Collateral (futures): place entry (limit or market) via the two-step
  flow. After the entry order is accepted/filled, immediately place
  `collateral__create_oco_order` with the TP and SL (also two-step, but
  present it together with the entry approval so the user approves the
  whole bracket once).
- Spot: place entry, then immediately place the SL as
  `spot__create_stop_limit_order` exchange-side. There is no spot OCO tool:
  a resting TP limit order would lock the same base balance as the SL, so
  place the SL exchange-side and manage TP by monitoring (state this to the
  user).

Never leave an entry filled without its exchange-side SL. If the SL
placement fails, alert the user immediately and propose closing the entry.

### Step 6 - REVIEW

1. Verify via `account_trade__get_order` / `account_collateral__get_open_positions`
   that reality matches intent (position size, SL/TP resting).
2. Append a decision-log entry (see schema).
3. Update daily PnL figures.
4. Use `whitebit-trade-review` (skill_trade_review) conventions when the
   user asks how past agent trades performed.
5. Persist state (State below) and give a short report. The report uses the
   same compact style as Step 5a: outcome, current exposure, distance to daily
   stop - a few lines, not a wall of text. Do NOT dump the full state JSON in
   chat on filesystem clients (it is written to `~/.wb-agent/state.json`); just
   confirm it was saved. The full `WB-AGENT-STATE` block is emitted in chat only
   on Desktop/web, where the conversation is the only storage.

## State

Stateless-first: financial facts (balances, positions, orders, PnL) are
never persisted as truth; they are re-fetched every cycle in Step 1. Carried
state holds only the risk policy, the daily PnL baseline, and the decision
log.

Per client:

- Claude Code / Cursor (filesystem tools available): load the risk policy from
  `~/.wb-agent/risk-policy.json`; read runtime state from
  `~/.wb-agent/state.json` at cycle start and write it at cycle end. Both are
  client-side files the user creates once (see One-Time Setup). Do not print the
  fenced block below in chat here - the file on disk is the record; just confirm
  the save.
- Claude Desktop / web (no arbitrary file writes): carry state inside the
  conversation only. At the END of every cycle emit the fenced block below;
  at the START of a cycle, recover state from the most recent block in the
  conversation. If no block is found (new session), ask the user for the
  risk policy again.

End-of-cycle block (mandatory, exact fence label):

````
```json WB-AGENT-STATE
{ ...state.json content... }
```
````

`state.json` schema (lives here; this SKILL.md is the only file the server
embeds, do not reference external schema files):

```json
{
  "version": 1,
  "risk_policy": {
    "per_trade_risk_pct": 1.0,
    "daily_stop_pct": 3.0,
    "leverage_cap": 3,
    "approval_threshold_usd": 0,
    "max_open_positions": 2,
    "quote_asset": "USDT"
  },
  "daily": {
    "utc_date": "2026-08-20",
    "equity_baseline_usd": 1000.0,
    "realized_pnl_usd": -8.0,
    "stopped_out": false
  },
  "positions_snapshot_note": "informational only; exchange is source of truth",
  "decision_log": [
    {
      "ts": "2026-08-20T12:00:00Z",
      "cycle": 4,
      "market": "BTC_USDT",
      "decision": "open|close|adjust|hold|planned|blocked",
      "side": "long",
      "entry": 67000.0,
      "stop_loss": 65900.0,
      "take_profit": 69500.0,
      "qty": 0.0083,
      "risk_usd": 9.2,
      "checklist": "PASS",
      "approved_by_user": true,
      "order_ids": ["12345"],
      "rationale": "one line"
    }
  ]
}
```

## Position Sizing

Primary path: call the deterministic `calc_position_size` MCP tool (inputs:
market, side, entry, stop_loss, risk_usd, optional market_type and leverage_cap;
outputs: rounded qty, notional, worst_case_loss, taker fee, and per-check
PASS/FAIL with a verdict). Use its numbers as the source of truth for sizing;
the tool fetches market precision, min amount, and taker fee itself, so you do
not compute them. If the server does not expose `calc_position_size` (older
version), fall back to the show-your-work method below.

Show-your-work (DETAIL only - render it on "details" or at Step 5b, never in the
default result; see Output contract). When shown, print all of these with numbers
substituted, each on its own line (populate from the tool's output when available):

```
equity            = <from balance tools>            $1000.00
risk$             = equity * per_trade_risk_pct     $10.00
|entry - SL|      = |67000 - 65900|                 $1100
qty_raw           = risk$ / |entry - SL|            0.009090 BTC
lot step / min    = <from market__get_markets or futures__get_markets>
qty_rounded       = round DOWN to lot step          0.0090 BTC
notional          = qty_rounded * entry             $603.00
implied leverage  = notional / equity               0.6x
taker fee est.    = notional * taker_fee            $0.60 (fee from market__get_markets)
worst-case loss   = qty_rounded * |entry - SL| + fees  $10.50
```

Self-check (mandatory, shown as the last checklist row):
`qty_rounded * |entry - SL| <= risk$`. If it fails after rounding, reduce
qty by one lot step and recompute. Also verify `qty_rounded >= market min
amount`, `notional >= market min total` (min order value), and `qty_rounded > 0`;
if any fails, the trade is too small for the budget - report it and hold, do not
place an order the exchange will reject.

Compute from data fetched this cycle; never fabricate a number. If any input
(equity, lot step, fee) was not fetched this cycle, fetch it before sizing. The
arithmetic is shown as detail (per the Output contract), not in the default
result - "not silent" means available on "details"/at 5b, not pasted every time.

## Composability

Calls: `whitebit-portfolio`, `whitebit-market-data`,
`whitebit-technical-analysis`, `whitebit-smart-money`,
`whitebit-order-execution`, `whitebit-trade-review`.
Called by: nothing (top-level entrypoint).

## Definition of Done (per cycle)

- [ ] All balances/positions/orders/PnL re-fetched from the exchange this cycle
- [ ] Risk policy loaded (state file, state block, or asked); echoed only in details or when it changed
- [ ] Default result is the compact verdict block (only failing checks + next step)
- [ ] Full sizing + RISK CHECKLIST (all rows) rendered at 5b or on "details", all rows PASS before execution
- [ ] User explicitly approved before any `confirmed: true` call
- [ ] Exchange-side SL (OCO on collateral, stop-limit on spot) resting after entry
- [ ] Decision log appended; state saved to `~/.wb-agent/state.json` (FS clients) or `WB-AGENT-STATE` block emitted (Desktop/web)
- [ ] Safety framed honestly: guidance vs the three hard controls

