Claim-bound orders on Binance Agent OS
Everything on this page reflects the shipped code at main. No command here is hypothetical.
Install
Python 3.11 or newer. Zero runtime dependencies, the whole gateway is standard library.
# from the repository root python3 -m venv .venv .venv/bin/pip install -e ".[test,quality]" .venv/bin/pytest -q # 11,414 tests, ~16s, no network
The qualto command is installed on your PATH by the same step.
Quickstart
The full loop, read-only first, then one zero-cost proof cycle:
# 1. gateway check: initialize, tools/list, account read. read-only. qualto smoke # 2. draft a claim from a mandate. no order is placed. qualto propose --mandate "buy 5 USDT of BNB" --symbol BNBUSDT # 3. place, attest, prove, cancel. real order, zero cost pattern. qualto claim --claim-file my-claim.json \ --receipts-file runtime/receipts.jsonl \ --confirm-live-write \ --cancel-after-attestation # 4. re-verify any receipts file against live Binance. read-only. qualto verify --receipts-file runtime/receipts.jsonl
--cancel-after-attestation exercises the entire bind, dual readback, attestation, and cancellation cycle with no fill. Every live proof in this repository used it.Prerequisites
| Requirement | Detail |
|---|---|
| Binance Agentic sub-account | Funded. This is the structural envelope: the agent can never touch main-account funds. |
| Supported host credential | A registered host (for example the Codex CLI with the Binance connector authenticated). Set QUALTO_CODEX_CREDENTIALS_FILE to your credential path. |
| Optional: Hermes | Only for the bundled agent loop (qualto propose / qualto agent). Override with QUALTO_HERMES_BIN and QUALTO_HERMES_PYTHON. |
Qualto reads the credential per request, in memory, and never writes it to logs or receipts. If it is missing, malformed, or expired, every command fails closed with no order sent.
The claim contract
A claim is a strict JSON object. Exactly these nine fields, nothing more, nothing less. Unknown fields are rejected before anything is sent.
{
"claimId": "qualto-claim-<12 lowercase alphanumerics>",
"mandate": "what the operator asked for",
"symbol": "BNBUSDT",
"side": "BUY | SELL",
"orderType": "LIMIT | MARKET",
"quantity": "0.009",
"price": "600", // null for MARKET
"status": "NEW | FILLED | PARTIALLY_FILLED | CANCELED",
"reason": "the agent's honest reasoning, 1-1000 chars"
}Field rules
| Field | Rule |
|---|---|
claimId | Format qualto-claim- plus 12 lowercase alphanumerics. Single-use per session: a replay is rejected before placement and logged. |
quantity, price | Positive finite decimal strings. Never floats. Sent to Binance exactly as written. |
price | Required for LIMIT, must be null for MARKET. |
status | The status the claim commits to at readback. NEW for resting limit intent, FILLED for market intent. |
mandate, reason | 1 to 500 and 1 to 1000 characters. The mandate is the human instruction; the reason is the agent's stated justification. |
Claim binding
Binding happens at placement. The harness, never the LLM, sends the order with newClientOrderId = claimId. From that moment the claim's ID lives inside Binance's own order record, visible in Binance order history.
At readback the order is fetched twice from Binance Agent OS: by orderId and by origClientOrderId. Both readbacks must return the same order. Then six fields are diffed:
| Field | Match rule |
|---|---|
| claimId | exact, against the order's client order ID |
| symbol, side, status | exact |
| quantity | exact |
| price | within 0.5% of the claimed price |
Verdicts
| Verdict | Meaning | Session effect |
|---|---|---|
| PROVED | All six fields matched on a live dual readback | none |
| UNPROVED | The exchange could not prove the claim: readback failure, disagreement, or field mismatch | session BLOCKED |
| PARTIAL | Identity matched and the order is partially filled. The filled quantity is recorded. | none |
| PENDING | A claimed market order is still NEW. Retried up to 3 times, 2 seconds apart, before a final verdict. | none until retries exhaust |
Session states
| State | Entered when | Can write orders? |
|---|---|---|
CREATED → CONNECTED → ACTIVE | Normal start | yes, when ACTIVE |
BLOCKED | Any UNPROVED verdict, failed cancellation, or unresolved cleanup | no. further placement refused |
ERROR | Agent or execution failure before attestation | no |
CLOSED | Session close | no, terminal |
From BLOCKED, an operator can still cancel a known order (cleanup is allowed, new writes are not), then recover the session explicitly. The agent cannot unblock itself.
CLI reference
qualto smoke
Read-only gateway check: MCP initialize, tools/list, account read. Prints client ID, protocol version, visible tool count, and balance record count.
qualto propose
| Flag | Default | Meaning |
|---|---|---|
--mandate | required | Free-text operator instruction, 1-500 characters. |
--symbol | BNBUSDT | Uppercase Binance spot symbol. |
Fetches live price and balance, asks the bundled LLM loop for one claim, validates it, prints the claim JSON. Places no order.
qualto claim
| Flag | Default | Meaning |
|---|---|---|
--claim-file | required | Path to a claim JSON object. |
--receipts-file | runtime/receipts.jsonl | Append-only JSONL receipt path. |
--confirm-live-write | off | Authorizes the live order request. Without it: exit 2, nothing sent. |
--cancel-after-attestation | off | Cancels the proved order immediately after dual readback. |
qualto agent
One command: mandate → generated claim → placed order → attestation → optional cancellation. Flags of propose and claim, plus:
| Flag | Meaning |
|---|---|
--disconnect-before-order | Severs the gateway after claim generation, proving the negative path: UNPROVED, BLOCKED, then reconnect and recover. |
qualto cleanup
Recovers an orphan order by claim ID and cancels the exact exchange order. Use when a placement's outcome is unknown. Requires --confirm-live-write.
qualto verify
Re-verifies every claim attestation in a receipts file against live Binance state. Read-only, calls only spot.getOrder. Prints a per-claim match table.
Receipt events
Receipts are append-only JSONL. Every line is timestamped and written with fsync before the flow continues. The verdict field appears only on attestation and cancellation events; lifecycle events use outcome.
| Event | Written when | Key fields |
|---|---|---|
order_submitted | Immediately before the placement call | claim, orderRequest |
claim_attestation | After dual readback and field diff | claim, attestation, both readbacks |
order_cancellation | After a proved cancellation | order, verdict |
order_cleanup | Orphan resolution attempt | outcome: resolved / unresolved |
claim_rejected | Claim ID replay attempt | claimId, reason |
gateway_recovery | Reconnect and session recovery | claimId, reason |
session_error | Session enters ERROR | errorType (class name only, never the message) |
Exit codes
| Code | Meaning |
|---|---|
0 | Success. For write commands: verdict PROVED. |
1 | Failure or unproved verdict. Read the reason on stderr. |
2 | Live-write confirmation missing. Nothing was sent. |
Verification
Qualto's receipts are convenience. The proof is the Binance order record.
| Binance order | Claim ID | Verdict |
|---|---|---|
| 12565050896 | qualto-claim-56eda03b6069 | PROVED then CANCELED |
| 12565013192 | qualto-claim-live00000002 | PROVED then CANCELED |
Both orders remain in Binance order history with the claim IDs above as their client order IDs. To re-check any receipts file against the exchange right now:
qualto verify --receipts-file runtime/receipts.jsonl # claimId orderId recorded live matched # qualto-claim-56eda03b6069 12565050896 PROVED PROVED true
Security model
Tool allowlist
Binance Agent OS exposes 366 tools. Qualto's gateway admits exactly 6: spot.getAccount, spot.tickerPrice, spot.newOrder, spot.getOrder, spot.myTrades, spot.deleteOrder. Everything else is rejected client-side before a request exists. No withdrawals, no transfers, no futures, no margin.
Who can place orders
Only the harness, only claim-bound, only after --confirm-live-write. The LLM has no tools, receives the prompt over stdin, and its output is validated against the strict claim schema with the harness-assigned claim ID enforced.
Credential handling
The host credential is loaded per request, kept in memory for that request, never logged, never in receipts, never in error messages. Expiry is checked on every call. Missing, malformed, or expired: fail closed, no order.
Agent skill
Qualto ships as a SKILL.md-compatible agent skill that teaches any agent to trade claim-bound: never place raw orders, always go through the harness, never soften an UNPROVED, stop when the session locks.
# Claude Code cp -r qualto/skills/qualto-trading ~/.claude/skills/ # Qwen Code cp -r qualto/skills/qualto-trading ~/.qwen/skills/
Limits
Single session, spot only, one symbol per claim. Attestation is per-order, not per-strategy. Qualto does not judge whether a trade was smart, only whether it happened exactly as claimed. The claim file, not the mandate, is the binding contract. Third-party agent OAuth identities are not yet admitted by Binance; Qualto runs on the supported host-credential path.