TradeReady.io
Framework Guides

OpenClaw Integration

Wire AgentExchange into OpenClaw via skill.md or typed @openclaw.tool SDK wrappers

Download .md

This guide shows you how to wire AgentExchange into an OpenClaw agent in under 10 minutes. AgentExchange exposes its full capability set as a skill.md file — the native format OpenClaw reads to give an LLM agent access to tools.

What You Get

After following this guide your OpenClaw agent will be able to:

  • Fetch live prices for any of 600+ Binance trading pairs
  • Place, monitor, and cancel market / limit / stop-loss / take-profit orders
  • Read account balances, open positions, and portfolio summary
  • Pull performance analytics (Sharpe ratio, drawdown, win rate)
  • Stream real-time prices over WebSocket
  • Reset its trading session to restart a strategy cleanly

All on simulated funds against real Binance market data.

Prerequisites

RequirementVersion
Python3.10+
OpenClawlatest (pip install openclaw)
AgentExchange serverrunning (see quickstart)
AgentExchange Python SDKoptional but recommended (pip install agentexchange)
docker compose up -d
curl http://localhost:8000/health
# {"status":"ok"}

Step 1 — Register an Account

curl -s -X POST http://localhost:8000/api/v1/auth/register \
  -H "Content-Type: application/json" \
  -d '{"display_name": "MyOpenClawBot", "starting_balance": "10000.00"}'
{
  "account_id": "a1b2c3d4-...",
  "api_key": "ak_live_...",
  "api_secret": "sk_live_...",
  "starting_balance": "10000.00"
}

Save api_secret now — it is shown only once.


Step 2 — Point OpenClaw at the Skill File

docs/skill.md is the canonical LLM-readable instruction file for AgentExchange. It contains the full API reference, authentication instructions, error codes, and trading workflows.

# agent.yaml
name: trading-agent
model: claude-3-5-sonnet  # or any OpenClaw-supported model

skills:
  - path: /path/to/agent-exchange/docs/skill.md
    name: agentexchange
    description: >
      Simulated crypto exchange with real-time Binance prices.
      Trade 600+ pairs with virtual funds. Full REST + WebSocket API.

env:
  AGENTEXCHANGE_API_KEY: "ak_live_..."
  AGENTEXCHANGE_BASE_URL: "http://localhost:8000"

If your AgentExchange instance is deployed, point directly at the hosted skill file:

skills:
  - url: https://your-deployed-host/docs/skill.md
    name: agentexchange
    description: Simulated crypto exchange with live Binance data.
{
  "name": "trading-agent",
  "model": "claude-3-5-sonnet",
  "skills": [
    {
      "path": "./docs/skill.md",
      "name": "agentexchange",
      "description": "Simulated crypto exchange. 600+ pairs, virtual funds, live Binance prices."
    }
  ],
  "env": {
    "AGENTEXCHANGE_API_KEY": "ak_live_...",
    "AGENTEXCHANGE_BASE_URL": "http://localhost:8000"
  }
}

Step 3 — Inject Credentials

The skill file tells the agent to include X-API-Key: YOUR_API_KEY on every request. Inject the actual key at runtime:

import os
import openclaw

agent = openclaw.Agent.from_config("agent.yaml")

agent.add_context(
    "Your AgentExchange credentials:\n"
    f"  API Key: {os.environ['AGENTEXCHANGE_API_KEY']}\n"
    f"  Base URL: {os.environ['AGENTEXCHANGE_BASE_URL']}\n"
    "Always include `X-API-Key: <your key>` on every HTTP request.\n"
    "Never expose the api_secret in responses."
)

result = agent.run("Check the current BTC price and buy 0.01 BTC if it's below $65,000.")
print(result)

Step 4 — Run the Agent

import os
import openclaw

agent = openclaw.Agent.from_config("agent.yaml")
agent.add_context(
    f"API Key: {os.environ['AGENTEXCHANGE_API_KEY']}\n"
    f"Base URL: {os.environ['AGENTEXCHANGE_BASE_URL']}"
)

result = agent.run(
    "Check my balance, find the coin with the highest 24h change, "
    "and buy $200 worth at market price. Then set a 5% stop-loss."
)
print(result)
session = agent.start_session()

session.send("What's my current portfolio value?")
session.send("Which of my positions is performing best?")
session.send("Sell half of my best-performing position.")
import time

session = agent.start_session()
session.send(
    "You are a momentum trading bot. Every 5 minutes:\n"
    "1. Scan all prices for coins up >3% in 24h.\n"
    "2. Buy the top candidate if you have enough balance and no current position.\n"
    "3. Set a stop-loss at -5% and take-profit at +10%.\n"
    "4. Report what you did."
)

while True:
    response = session.receive()
    print(response)
    time.sleep(300)

For tighter integration — typed responses, automatic retries, WebSocket streaming — wrap the Python SDK as OpenClaw tools. This is more robust than letting the LLM construct raw HTTP calls.

import os
import openclaw
from decimal import Decimal
from agentexchange import AgentExchangeClient
from agentexchange.exceptions import AgentExchangeError, RateLimitError

_client = AgentExchangeClient(
    api_key=os.environ["AGENTEXCHANGE_API_KEY"],
    api_secret=os.environ["AGENTEXCHANGE_API_SECRET"],
    base_url=os.environ.get("AGENTEXCHANGE_BASE_URL", "http://localhost:8000"),
)


@openclaw.tool(description="Get the current price of a trading pair, e.g. BTCUSDT")
def get_price(symbol: str) -> dict:
    price = _client.get_price(symbol)
    return {
        "symbol": price.symbol,
        "price": str(price.price),
        "timestamp": price.timestamp.isoformat(),
    }


@openclaw.tool(description="Get account balances for all assets")
def get_balance() -> dict:
    balance = _client.get_balance()
    return {
        "total_equity_usdt": str(balance.total_equity_usdt),
        "balances": [
            {"asset": b.asset, "available": str(b.available), "total": str(b.total)}
            for b in balance.balances
        ],
    }


@openclaw.tool(description="Place a market, limit, stop_loss, or take_profit order")
def place_order(
    symbol: str,
    side: str,
    order_type: str,
    quantity: str,
    price: str = None,
    trigger_price: str = None,
) -> dict:
    """
    Args:
        symbol: Trading pair, e.g. 'BTCUSDT'
        side: 'buy' or 'sell'
        order_type: 'market', 'limit', 'stop_loss', or 'take_profit'
        quantity: Quantity as decimal string e.g. '0.01'
        price: Required for limit orders. Decimal string.
        trigger_price: Required for stop_loss / take_profit. Decimal string.
    """
    kwargs = {}
    if price:
        kwargs["price"] = Decimal(price)
    if trigger_price:
        kwargs["trigger_price"] = Decimal(trigger_price)
    order = _client.place_order(
        symbol=symbol, side=side, order_type=order_type,
        quantity=Decimal(quantity), **kwargs,
    )
    return {
        "order_id": str(order.order_id),
        "status": order.status,
        "executed_price": str(order.executed_price) if order.executed_price else None,
        "slippage_pct": str(order.slippage_pct) if order.slippage_pct else None,
    }


@openclaw.tool(description="Get full portfolio summary including PnL and ROI")
def get_portfolio() -> dict:
    pf = _client.get_portfolio()
    return {
        "total_equity": str(pf.total_equity),
        "roi_pct": str(pf.roi_pct),
        "unrealized_pnl": str(pf.unrealized_pnl),
        "realized_pnl": str(pf.realized_pnl),
        "available_cash": str(pf.available_cash),
    }


@openclaw.tool(description="Get performance metrics: Sharpe ratio, win rate, drawdown. period: 1d/7d/30d/all")
def get_performance(period: str = "all") -> dict:
    perf = _client.get_performance(period=period)
    return {
        "sharpe_ratio": str(perf.sharpe_ratio),
        "win_rate": str(perf.win_rate),
        "max_drawdown_pct": str(perf.max_drawdown_pct),
        "total_trades": perf.total_trades,
        "profit_factor": str(perf.profit_factor),
    }


@openclaw.tool(description="Reset account to a fresh session with the given starting balance (USDT string)")
def reset_account(starting_balance: str = "10000.00") -> dict:
    session = _client.reset_account(starting_balance=Decimal(starting_balance))
    return {
        "session_id": str(session.session_id),
        "starting_balance": str(session.starting_balance),
    }


# Build agent with SDK tools
agent = openclaw.Agent(
    model="claude-3-5-sonnet",
    tools=[get_price, get_balance, place_order, get_portfolio, get_performance, reset_account],
    system=(
        "You are a crypto trading agent operating on the AgentExchange simulated exchange. "
        "All funds are virtual — real Binance prices, no real risk. "
        "Always check balance before placing orders. "
        "Always set a stop-loss after opening a position."
    ),
)

result = agent.run("Run a momentum scan and buy the strongest coin today.")
print(result)

Step 6 — Add WebSocket Streaming

For agents that need to react to live price movements rather than polling:

import threading
from agentexchange import AgentExchangeWS

latest_prices: dict[str, str] = {}

ws = AgentExchangeWS(
    api_key=os.environ["AGENTEXCHANGE_API_KEY"],
    base_url=os.environ.get("AGENTEXCHANGE_WS_URL", "ws://localhost:8000"),
)


@ws.on_ticker("BTCUSDT")
def on_btc_price(msg):
    latest_prices["BTCUSDT"] = msg["data"]["price"]


@ws.on_ticker("ETHUSDT")
def on_eth_price(msg):
    latest_prices["ETHUSDT"] = msg["data"]["price"]


@ws.on_order_update()
def on_order(msg):
    data = msg["data"]
    print(f"Order {data['order_id']}{data['status']} @ {data.get('executed_price', 'N/A')}")


# Start in background thread
ws_thread = threading.Thread(target=ws.run_forever, daemon=True)
ws_thread.start()


@openclaw.tool(description="Get the most recently streamed price from the live feed")
def get_streamed_price(symbol: str) -> dict:
    price = latest_prices.get(symbol.upper())
    if not price:
        return {"error": f"No streamed price yet for {symbol}. Subscribe first or use get_price."}
    return {"symbol": symbol.upper(), "price": price, "source": "websocket"}

Full agent.yaml Example

name: agentexchange-trading-bot
model: claude-3-5-sonnet

skills:
  - path: ./docs/skill.md
    name: agentexchange
    description: >
      Simulated crypto exchange with real-time Binance prices.
      REST API at /api/v1. Auth via X-API-Key header.

env:
  AGENTEXCHANGE_API_KEY: "${AGENTEXCHANGE_API_KEY}"
  AGENTEXCHANGE_API_SECRET: "${AGENTEXCHANGE_API_SECRET}"
  AGENTEXCHANGE_BASE_URL: "http://localhost:8000"
  AGENTEXCHANGE_WS_URL: "ws://localhost:8000"

system_prompt: |
  You are a trading agent on the AgentExchange platform.
  Base URL: ${AGENTEXCHANGE_BASE_URL}
  API Key: ${AGENTEXCHANGE_API_KEY}
  Include header X-API-Key: ${AGENTEXCHANGE_API_KEY} on every request.
  All values of type quantity or price must be decimal strings (e.g. "0.01", not 0.01).
  Always call GET /account/balance before placing any order.
  Always place a stop-loss immediately after opening a new position.

memory:
  enabled: true
  backend: local

max_iterations: 50
timeout_seconds: 120

Error Handling Reference

Error CodeRecommended Agent Behavior
INSUFFICIENT_BALANCECall GET /account/balance, reduce quantity, retry
RATE_LIMIT_EXCEEDEDRead X-RateLimit-Reset, wait until that timestamp, retry
DAILY_LOSS_LIMITStop placing orders; wait until 00:00 UTC
INVALID_SYMBOLCall GET /market/pairs to get the correct symbol, retry
INVALID_QUANTITYCheck min_qty and step_size for the pair
ORDER_REJECTEDCheck position limits and open order count
PRICE_NOT_AVAILABLERetry after 2–3 seconds
INTERNAL_ERRORRetry with exponential back-off: 1s, 2s, 4s, 8s, max 60s

If you use the SDK tool wrappers from Step 5, these errors surface as typed Python exceptions (RateLimitError, InsufficientBalanceError, etc.) that are caught inside each tool function before they reach the LLM.


Troubleshooting

Agent says "I cannot access the API"

  • Verify the backend is running: curl http://localhost:8000/health
  • Confirm the api_key in the agent context matches a registered account
  • Check that base_url does not have a trailing slash

PRICE_NOT_AVAILABLE on startup

  • The Binance ingestion service needs ~30 seconds to stream initial prices after a cold start. Wait and retry.

Agent constructs malformed request bodies

  • Add to the system prompt: "All quantity and price fields must be decimal strings, e.g. "0.01", never bare numbers."
  • Use the typed SDK tools from Step 5 — they handle serialization automatically.

Rate limit hit during scans

  • GET /market/prices returns all 600+ prices in a single call. Use it instead of looping over /market/price/{symbol}.

Next Steps

On this page