Bot API Reference
opentrader exposes HTTP API on port 8000 (internal Docker network).
- Base URL (inside
openclawcontainer):http://opentrader:8000 - Prefix for all routes:
/api
Summary
| Section | Endpoints |
|---|---|
| Health | GET /api/health |
| License | GET /api/license/status, POST /api/license/register, POST /api/license/activate |
| Dashboard | GET /api/dashboard, POST /api/agent/{name}, GET /api/state |
| News | GET /api/news |
| Portfolio | GET /api/portfolio |
| Bot Actions | POST /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 & Signal | POST /api/mr-combined/scan-and-signal |
| Trend Single-TF | POST /api/trend/{symbol}, GET /api/trend/{symbol}, GET /api/trend/all, GET /api/trend/scan |
| Market Multi-TF | GET /api/market/{symbol}, GET /api/market/scan, GET /api/market/all |
| Telegram | GET /api/notify |
| Trade Management | GET /api/trades, GET /api/trades/history, GET /api/trades/stats, POST /api/trades/sync, GET /api/trades/recover, POST /api/trades/restore |
| Signal Flow | POST /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:activeorrequiredcommit: from envGIT_COMMIT(fallbackdev)
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:
| Param | Required | Type | Notes |
|---|---|---|---|
symbol | yes | string | e.g. ETHUSDT |
direction | yes | string | buy/sell |
bot | yes | string | Bot name |
sl | yes | float | Stop-loss % |
tp | yes | float | Take-profit % |
ev | yes | float | Must be > 25 |
confidence | yes | int | Must be >= 8 |
agent | no | string | Default ops |
Validation errors:
422ifev <= 25422ifconfidence < 8
GET /api/trailing
Updates trailing stops.
- Query:
agent(defaulttech)
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(defaultops)
POST /api/closeall
Cancels all SL/TP OCO orders and closes all open positions at market price.
- Query:
agent(defaultops)
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:
| Param | Required | Type | Notes |
|---|---|---|---|
symbol | yes | string | e.g. ETHUSDT |
direction | yes | string | buy or sell |
entry_px | yes | float | Fixed entry price |
sl_pct | yes | float | Stop-loss % |
rr | no | string | Risk:Reward e.g. 1:3 (use with sl_pct) |
tp_pct | no | float | Take-profit % (required if no rr) |
ttl_hours | no | float | Max wait time (default 24h) |
agent | no | string | Default 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(defaultfinance)
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(defaultscout) - 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|unknowntimeframe:15m|30m|1h|4h|1d|1w
GET /api/trend/{symbol}
Gets on-demand single-TF trend for a symbol by requested timeframe.
- Query:
timeframe(default1h)
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:
422invalid timeframe500fetch 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,typetrend,scoreage_sec,stale,timestamp
GET /api/trend/scan
Triggers immediate full watchlist trend scan via subprocess.
- Query:
timeframe(default1h) - 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:
422invalid timeframe504timeout (>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:
| MTF | HTF |
|---|---|
15m | 1h |
30m | 4h |
1h | 4h |
4h | 1d |
1d | 1d |
1w | 1w |
GET /api/market/{symbol}
Returns full multi-TF MarketContext for a single symbol.
- Query:
mtf_tf(default4h) — MTF timeframe - Query:
htf_tf(default1d) — 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:
| Value | Meaning |
|---|---|
trade_long | All layers aligned for long entry |
trade_short | All layers aligned for short entry |
standby | Market not clear — wait |
no_trade | Absolute no-trade zone (volatile_chop, compression, etc.) |
watch_regime_shift | HTF/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:
422invalid timeframe404symbol not found on exchange500fetch or analysis error
GET /api/market/scan
Runs full-watchlist market scan, caches each MarketContext, and returns the scan summary.
- Query:
mtf_tf(default4h) - 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(defaulttrue)true: include stale rowsfalse: 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:
503Telegram env not configured502Telegram 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(default50, min1, max200)offset(default0, min0)
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:
pendingtelegram_failedduplicate_skippedtrade_exists_skippedno_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:
404signal not found409signal not inpending410signal 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(default5)
Response:
{
"ok": true,
"expired": ["ETHUSDT"],
"deleted": ["SOLUSDT"],
"count_expired": 1,
"count_deleted": 1
}
Error codes
| Code | Meaning |
|---|---|
400 | Bad request / invalid JSON body |
404 | Resource not found (e.g. signal) |
409 | Signal exists but not executable |
410 | Signal expired |
422 | Validation failed (timeframe / trend / AI confirm thresholds) |
500 | Internal bot/subprocess error |
502 | Telegram provider/API error |
503 | License required or Telegram not configured |
504 | Subprocess 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"