Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Bot API Reference

opentrader exposes HTTP API on port 8000 (internal Docker network).

  • Base URL (inside openclaw container): http://opentrader:8000
  • Prefix for all routes: /api

Summary

SectionEndpoints
HealthGET /api/health
LicenseGET /api/license/status, POST /api/license/register, POST /api/license/activate
DashboardGET /api/dashboard, POST /api/agent/{name}, GET /api/state
NewsGET /api/news
PortfolioGET /api/portfolio
Bot ActionsPOST /api/trade, POST /api/trade/manual, GET /api/pending-entries, GET /api/pending-entries/check, GET /api/pending-entries/recover, GET /api/trailing, GET /api/status, POST /api/close, POST /api/closeall, POST /api/reset-daily
Scan & SignalPOST /api/mr-combined/scan-and-signal
Trend Single-TFPOST /api/trend/{symbol}, GET /api/trend/{symbol}, GET /api/trend/all, GET /api/trend/scan
Market Multi-TFGET /api/market/{symbol}, GET /api/market/scan, GET /api/market/all
TelegramGET /api/notify
Trade ManagementGET /api/trades, GET /api/trades/history, GET /api/trades/stats, POST /api/trades/sync, GET /api/trades/recover, POST /api/trades/restore
Signal FlowPOST /api/signal, GET /api/signal/list, GET /api/signal/pending, GET /api/signal/{symbol}, POST /api/signal/{symbol}/confirm, POST /api/signal/{symbol}/reject, POST /api/signal/cleanup

Health

GET /api/health

Always available (no license required).

{
  "ok": true,
  "license_status": "active",
  "plan": "free",
  "commit": "a1b2c3d"
}
  • license_status: active or required
  • commit: from env GIT_COMMIT (fallback dev)

License

GET /api/license/status

Returns current machine license state.

POST /api/license/register

Register free license and auto-activate.

Request body:

{ "email": "[email protected]", "name": "Trader Name" }

POST /api/license/activate

Activate existing key.

Request body:

{ "license_key": "OT-XXXX-XXXX-XXXX-XXXX" }

Dashboard

GET /api/dashboard

Serves dashboard HTML.

POST /api/agent/{name}

Update an agent card status (coo, ops, tech, finance, scout).

Request body:

{ "status": "running", "action": "Day trading scan" }
  • status: idle | running | waiting | error

GET /api/state

Returns full dashboard state (agents, log, ts).


News

GET /api/news

Fetches RSS crypto news with in-memory cache.


Portfolio

GET /api/portfolio

Returns balance/holdings from current active exchange adapter.


Bot Actions (license required)

POST /api/trade

Executes a trade via app.opentrader subprocess.

Query params:

ParamRequiredTypeNotes
symbolyesstringe.g. ETHUSDT
directionyesstringbuy/sell
botyesstringBot name
slyesfloatStop-loss %
tpyesfloatTake-profit %
evyesfloatMust be > 25
confidenceyesintMust be >= 8
agentnostringDefault ops

Validation errors:

  • 422 if ev <= 25
  • 422 if confidence < 8

GET /api/trailing

Updates trailing stops.

  • Query: agent (default tech)

GET /api/status

Returns bot status from subprocess.

  • Query: agent (optional)

POST /api/close

Cancels SL/TP OCO then closes a single position at market price.

  • Query: symbol (required) — e.g. BTC, ETH
  • Query: agent (default ops)

POST /api/closeall

Cancels all SL/TP OCO orders and closes all open positions at market price.

  • Query: agent (default ops)

POST /api/reset-daily

Resets trades_today counter in bot state.

Response:

{ "ok": true, "trades_today_before": 2, "trades_today_after": 0 }

POST /api/trade/manual

Place LIMIT entry order at fixed price — OCO orders placed when filled.

Query params:

ParamRequiredTypeNotes
symbolyesstringe.g. ETHUSDT
directionyesstringbuy or sell
entry_pxyesfloatFixed entry price
sl_pctyesfloatStop-loss %
rrnostringRisk:Reward e.g. 1:3 (use with sl_pct)
tp_pctnofloatTake-profit % (required if no rr)
ttl_hoursnofloatMax wait time (default 24h)
agentnostringDefault coo

Mode RR: sl_pct + rr='1:3'tp_pct = sl_pct × 3 Mode Explicit: sl_pct + tp_pct → use directly

GET /api/pending-entries

List pending LIMIT entries waiting to be filled (from SQLite).

{ "pending": [], "count": 0 }

GET /api/pending-entries/check

Poll fill status of all pending entries — place OCO when filled, cancel when TTL expired.

  • Query: agent (default finance)

GET /api/pending-entries/recover

Scan exchange for LIMIT orders with clientOrderId starting with MT_ → reconstruct and restore to SQLite. Use when switching machines or SQLite lost.

Response:

{ "ok": true, "recovered": 2, "skipped": 0, "entries": [...] }

Scan & Signal (license required)

POST /api/mr-combined/scan-and-signal

Runs the MR Combined scan and emits pending signals.

  • Query: agent (default scout)
  • Request body: { "symbols": ["BTC", "ETH"] } (optional; defaults to watchlist)

Shared limiter response (scan pipelines):

{ "ok": true, "signaled": [], "skipped": [], "reason": "daily_limit_reached" }

Trend

POST /api/trend/{symbol}

Stores trend snapshot for a symbol/timeframe.

Request body:

{
  "trend": "uptrend",
  "timeframe": "1h",
  "source": "trend_scan",
  "type": "crypto",
  "score": { "uptrend": 5, "downtrend": 0, "sideway": 2 },
  "indicators": {},
  "timestamp": 1714216800
}

Validation:

  • trend: uptrend | downtrend | sideway | unknown
  • timeframe: 15m | 30m | 1h | 4h | 1d | 1w

GET /api/trend/{symbol}

Gets on-demand single-TF trend for a symbol by requested timeframe.

  • Query: timeframe (default 1h)

Response shape matches TrendResult:

{
  "symbol": "BTC",
  "timeframe": "1h",
  "source": "binance",
  "type": "crypto",
  "trend": "uptrend",
  "regime": "uptrend",
  "no_trade": false,
  "no_trade_reason": "",
  "score": {
    "uptrend": 4,
    "downtrend": 1,
    "sideway": 1
  },
  "indicators": {
    "price_structure": {"vote": "uptrend"},
    "ema": {"vote": "uptrend"},
    "adx": {"vote": "uptrend"},
    "rsi": {"vote": "sideway"},
    "volume": {"vote": "downtrend"},
    "atr": {"vote": "sideway"}
  },
  "timestamp": 1714216800
}

Common errors:

  • 422 invalid timeframe
  • 500 fetch or analysis error

GET /api/trend/all

Gets all cached trend rows (or filter by timeframe).

  • Query: timeframe (optional)

Each cached row includes:

  • symbol, timeframe, source, type
  • trend, score
  • age_sec, stale, timestamp

GET /api/trend/scan

Triggers immediate full watchlist trend scan via subprocess.

  • Query: timeframe (default 1h)
  • Query: agent (optional dashboard card)

Response per symbol now includes regime and no_trade:

{
  "symbol": "BTC",
  "trend": "uptrend",
  "regime": "trend",
  "no_trade": false,
  "score": { "uptrend": 5, "downtrend": 1, "sideway": 0 }
}

Errors:

  • 422 invalid timeframe
  • 504 timeout (>300s)

Market Multi-TF

Multi-timeframe analysis per v3.2 framework. Fetches HTF + MTF data for each symbol, runs Priority System (7 levels), and returns a priority_decision.

HTF map per MTF timeframe:

MTFHTF
15m1h
30m4h
1h4h
4h1d
1d1d
1w1w

GET /api/market/{symbol}

Returns full multi-TF MarketContext for a single symbol.

  • Query: mtf_tf (default 4h) — MTF timeframe
  • Query: htf_tf (default 1d) — override HTF timeframe (optional)

Behavior:

  • Checks the in-memory market cache first
  • Falls back to fresh fetch + analysis on cache miss, stale cache, or timeframe mismatch
  • Fresh result is written back into the cache

Response:

{
  "symbol": "BTC",
  "htf_tf": "1d",
  "mtf_tf": "4h",
  "htf": {
    "macro_phase": "bull",
    "range_position": "low",
    "liquidity_profile": "clean",
    "range_high": 109000.0,
    "range_low": 74000.0,
    "detail": {}
  },
  "mtf": {
    "regime": "trend",
    "trend_direction": "up",
    "bos_mode": "confirmed",
    "trend_age": "fresh",
    "compression_bias": "none",
    "volatile_chop": false,
    "atr_val": 1250.5,
    "detail": {}
  },
  "priority_decision": "trade_long",
  "no_trade_reason": "",
  "timestamp": 1714216800
}

priority_decision values:

ValueMeaning
trade_longAll layers aligned for long entry
trade_shortAll layers aligned for short entry
standbyMarket not clear — wait
no_tradeAbsolute no-trade zone (volatile_chop, compression, etc.)
watch_regime_shiftHTF/MTF conflict — monitor without trading

htf.macro_phase: bull | bear | range

htf.range_position: low | mid | high | just_broken_up | just_broken_down | unknown

htf.liquidity_profile: clean | equal_highs | equal_lows | void

mtf.regime: trend | compression | sideway | volatile_chop | transition | unknown

mtf.bos_mode: confirmed | quick | none

mtf.trend_age: fresh (< 5× ATR from BOS) | extended (≥ 5× ATR) | none

Errors:

  • 422 invalid timeframe
  • 404 symbol not found on exchange
  • 500 fetch or analysis error

GET /api/market/scan

Runs full-watchlist market scan, caches each MarketContext, and returns the scan summary.

  • Query: mtf_tf (default 4h)
  • Query: agent (optional dashboard card)

Response:

{
  "exchange": "binance",
  "htf_tf": "1d",
  "mtf_tf": "4h",
  "scanned": 8,
  "cached": 8,
  "ttl_sec": 3600,
  "results": [...],
  "summary": {
    "trade_long": 2,
    "trade_short": 1,
    "standby": 3,
    "no_trade": 2
  }
}

GET /api/market/all

Returns all cached MarketContext rows.

  • Query: stale (default true)
    • true: include stale rows
    • false: exclude stale rows

Response:

{
  "count": 8,
  "ttl_sec": 3600,
  "data": [
    {
      "symbol": "BTC",
      "htf_tf": "1d",
      "mtf_tf": "4h",
      "priority_decision": "trade_long",
      "age_sec": 42,
      "stale": false,
      "timestamp": 1714216800
    }
  ]
}

CLI equivalent:

python -m app.trend.trend_scan --timeframe 4h --mode market

Python SDK:

from app.trend import analyze_symbol

ctx = analyze_symbol("BTC", mtf_tf="4h", htf_tf="1d")
print(ctx.priority_decision)   # "trade_long" | "no_trade" | ...
print(ctx.htf.macro_phase)     # "bull" | "bear" | "range"
print(ctx.htf.range_position)  # "low" | "mid" | "high" | ...
print(ctx.mtf.regime)          # "trend" | "compression" | ...

Telegram

GET /api/notify

Sends Telegram message.

Query params:

  • message (required)

Common errors:

  • 503 Telegram env not configured
  • 502 Telegram API error

Trade Management

GET /api/trades

Returns open trades from runtime state.

{ "trades": [], "count": 0 }

GET /api/trades/history

Returns closed-trade history from SQLite.

Query params:

  • limit (default 50, min 1, max 200)
  • offset (default 0, min 0)

GET /api/trades/stats

Returns aggregated trade-history stats.

POST /api/trades/sync

Syncs exchange closed trades into history storage.

GET /api/trades/recover

Recovers open trade records from exchange OCO/open orders.

POST /api/trades/restore

Manually restores a trade into runtime state.

Request body:

{
  "symbol": "BNBUSDT",
  "direction": "buy",
  "size": 1.5,
  "entry_px": 598.0,
  "sl_px": 580.0,
  "tp_px": 638.0,
  "sl_oid": 10293001,
  "tp_oid": 10293002,
  "bot": "mr_combined",
  "date": "2026-04-27"
}

Signal Flow

POST /api/signal

Creates/updates pending signal and sends Telegram approval request.

Request body fields:

  • Required: symbol, direction, bot
  • Optional: price, sl_pct, tp_pct, checklist, passed, total
  • Optional indicators: rsi, macd_hist, bb_lower, bb_upper, bb_mid, vol_ratio, buy_signals, sell_signals, adx, stoch_k
  • Mean-reversion extras: tp1_pct, tp2_pct, range_support, range_resistance, range_midline

Possible status in response:

  • pending
  • telegram_failed
  • duplicate_skipped
  • trade_exists_skipped
  • no_position_sell_skipped

GET /api/signal/list

Returns all in-memory signals.

GET /api/signal/pending

Returns text/plain formatted pending signals for COO forwarding.

GET /api/signal/{symbol}

Returns one pending signal detail for finance validation.

Errors:

  • 404 signal not found
  • 409 signal not in pending
  • 410 signal expired

POST /api/signal/{symbol}/confirm

Consumes signal after successful execution.

POST /api/signal/{symbol}/reject

Marks signal as skipped.

POST /api/signal/cleanup

Expires or deletes old signals.

Query params:

  • max_age_minutes (default 5)

Response:

{
  "ok": true,
  "expired": ["ETHUSDT"],
  "deleted": ["SOLUSDT"],
  "count_expired": 1,
  "count_deleted": 1
}

Error codes

CodeMeaning
400Bad request / invalid JSON body
404Resource not found (e.g. signal)
409Signal exists but not executable
410Signal expired
422Validation failed (timeframe / trend / AI confirm thresholds)
500Internal bot/subprocess error
502Telegram provider/API error
503License required or Telegram not configured
504Subprocess timeout (>300s)

Quick curl

BASE=http://opentrader:8000

# Health & license
curl "$BASE/api/health"
curl "$BASE/api/license/status"

# Bot actions
curl -X POST "$BASE/api/trade?symbol=ETHUSDT&direction=buy&bot=mr_combined&sl=2.0&tp=4.0&ev=31.5&confidence=9"
curl -X POST "$BASE/api/trade/manual?symbol=ETHUSDT&direction=buy&entry_px=2450.0&sl_pct=3.0&rr=1:3&agent=coo"
curl "$BASE/api/pending-entries"
curl "$BASE/api/pending-entries/check?agent=finance"
curl "$BASE/api/pending-entries/recover"
curl "$BASE/api/trailing?agent=tech"
curl "$BASE/api/status"
curl -X POST "$BASE/api/close?symbol=BTC"
curl -X POST "$BASE/api/closeall"
curl -X POST "$BASE/api/reset-daily"

# Scan & signal
curl -X POST "$BASE/api/mr-combined/scan-and-signal?agent=scout" \
  -H "Content-Type: application/json" \
  -d '{"symbols":["BTC","ETH"]}'

# Trend
curl "$BASE/api/trend/BTC?timeframe=1h"
curl "$BASE/api/trend/BTC?timeframe=1w"
curl "$BASE/api/trend/all?timeframe=4h"
curl "$BASE/api/trend/scan?timeframe=1d&agent=tech"

# Market Multi-TF (multi-TF, v3.2 Priority System)
curl "$BASE/api/market/BTC?mtf_tf=4h"
curl "$BASE/api/market/scan?mtf_tf=4h&agent=tech"
curl "$BASE/api/market/all"
curl "$BASE/api/market/all?stale=false"

# Trades
curl "$BASE/api/trades"
curl "$BASE/api/trades/history?limit=20&offset=0"
curl "$BASE/api/trades/stats"
curl -X POST "$BASE/api/trades/sync"
curl "$BASE/api/trades/recover"

# Signal
curl -X POST "$BASE/api/signal" -H "Content-Type: application/json" -d '{"symbol":"ETHUSDT","direction":"buy","bot":"mr_combined","price":2450.5,"sl_pct":3.0,"tp_pct":7.0,"passed":5,"total":7}'
curl "$BASE/api/signal/list"
curl "$BASE/api/signal/pending"
curl "$BASE/api/signal/ETHUSDT"
curl -X POST "$BASE/api/signal/ETHUSDT/confirm"
curl -X POST "$BASE/api/signal/ETHUSDT/reject"
curl -X POST "$BASE/api/signal/cleanup?max_age_minutes=5"