Install
openclaw skills install @mbordash/dradis-tactical-commandReal-time supervisor and control interface for the DRADIS prediction-market trading engine (Polymarket International, Polymarket US, and Kalshi). Full support for DRADIS_API_KEY authentication.
openclaw skills install @mbordash/dradis-tactical-commandFull-featured autonomous supervisor for the DRADIS high-frequency prediction market execution engine.
What's new in v1.5.1
- Corrected PATCH contract. Config PATCH bodies are a flat partial object, not a wrapped
updatesobject. A wrapped body is silently ignored and still returns200 OK— see Applying config changes for the mandatory verification step.- Corrected squadron ids. Ids are
{asset}-{cadence}(e.g.btc-hourly), never a bare asset name. Always read them fromlist_squadrons.patch_dynamic_configno longer affects trading. Strategy execution reads squadron-scoped config exclusively; the global endpoint now only governs the LLM advisor. Usepatch_squadron_configfor anything that changes trading behaviour.- New read tools:
get_viper_status(why isn't it trading?),get_llm_actions+ approve/reject (the AI action queue),get_telemetry,get_telemetry_history,get_telemetry_assets,get_logs,get_latency,get_taxonomy_raptors,get_taxonomy_vipers,get_available_markets,get_deployments,get_deployment_region.- Documented
DRADIS_READ_ONLY(403 on writes), public vs. protected routes, query-param defaults and clamps, and per-venue endpoint availability.
DRADIS is an open-source, low-latency Rust trading engine for crypto prediction markets. It features a Viper strategy engine, real-time equity curve, dynamic config hot-reloading, and a built-in LLM advisor.
Project repository: https://github.com/mbordash/DRADIS
DRADIS builds against exactly one venue at a time (mutually exclusive Cargo features):
| Build | Venue | Notes |
|---|---|---|
intl_clob (default) | Polymarket International | Self-custody; on-chain wallet balance available |
us_retail | Polymarket US | Custodial; no on-chain wallet probe |
kalshi | Kalshi | Custodial; no on-chain wallet probe |
The agent cannot tell which build is running from the venue name alone — call get_deployment_region to find out. This matters because a few endpoints are build-specific (noted per tool below).
DynamicConfig./api/config object is a separate, legacy-scope record that now only feeds the LLM advisor.ClawScan has flagged two medium-risk items (as expected for any live-trading integration):
DRADIS_API_KEYThese are intentional and documented. The skill never applies config changes without explicit human confirmation.
This skill is monitoring and configuration only, by design. It deliberately exposes no tool that opens, closes, or settles a position, and none that deploys a squadron. The engine's API does offer such endpoints (DELETE /api/positions/{token_id}, POST /api/positions/manual-exit, POST /api/positions/sync, POST /api/squadrons/deploy) — they are omitted here so that no agent turn can spend money or flatten a position. Perform those actions in the Control Tower UI.
I strongly recommend using a dedicated, least-privilege API key and only running this skill against your own trusted DRADIS instance.
DRADIS supports optional API key authentication via the X-API-Key header.
DRADIS_API_KEY in your OpenClaw configuration.Public vs. protected routes. check_engine_status (/api/health) and list_assets (/api/assets) are intentionally unauthenticated so that container health checks and load balancers can probe them. Every other tool requires the key when DRADIS_API_KEY is set. A healthy /api/health alongside 401s elsewhere means the key is wrong, not that the engine is down — report it that way.
| Code | Meaning | What to tell the user |
|---|---|---|
401 | Missing/incorrect X-API-Key | Configure DRADIS_API_KEY to match the engine's env var |
403 | DRADIS_READ_ONLY=true — the engine rejects every non-GET request | This is a read-only/demo instance; config changes are disabled at the engine, not by the skill |
404 (squadron routes) | Unknown squadron id | Re-read ids from list_squadrons; do not retry with a guessed id |
409 (LLM actions) | Action is not in proposed state, or failed apply-time revalidation | Re-read get_llm_actions; the proposal was already handled or is stale |
400 (config PATCH) | Body was not valid JSON, or a value failed type coercion | Show the engine's error text verbatim |
This skill controls a live trading system with real money at risk. The agent must follow these guardrails at all times:
get_config_schema before proposing a patch, to validate the field name, type, unit, and min/max bounds.list_squadrons and use them verbatim.prices_live: false in get_portfolio_value means positions are marked with stale prices, and get_viper_status rows older than 120 seconds mean the evaluation loop is not reporting.The PATCH body is a flat JSON object containing only the fields to change. It is not wrapped in an updates key.
// CORRECT
{"time_decay_position_size_usdc": "8.0", "enable_basis": false}
// WRONG — silently ignored, still returns 200 OK with the UNCHANGED config
{"updates": {"time_decay_position_size_usdc": "8.0"}}
The engine merges the body over the current config and ignores keys it does not recognise. A misspelled field, or a wrapped body, therefore produces a successful-looking response in which nothing changed.
Mandatory verification step: a 200 OK does not mean the change was applied. Both PATCH endpoints return the full resulting config. After every patch, read back the specific field you changed from the response body and confirm it holds the new value. Report success only if it does; if it does not, tell the user the patch was rejected as an unknown field and did not take effect.
| Goal | Tool |
|---|---|
| Change how a strategy trades (size, thresholds, enable/disable a viper) | patch_squadron_config |
| Change LLM advisor behaviour or autonomy settings | patch_dynamic_config |
patch_dynamic_config writes the global config record, which no squadron reads. Strategy execution loops read a per-squadron config handle, and newly deployed squadrons seed from compile-time defaults rather than the global record. Patching global config to change trading behaviour will appear to succeed and will change nothing. If the user asks to change a strategy parameter without naming a squadron, list the squadrons and ask which one — do not fall back to the global endpoint.
DRADIS_API_URL: Base URL for the engine API (Default: http://localhost:9000/api)DRADIS_API_KEY: API key for authentication (optional locally, recommended for remote/production)All tools send X-API-Key: {{DRADIS_API_KEY}}.
GET{{DRADIS_API_URL}}/healthokGET{{DRADIS_API_URL}}/assets["btc", "eth", "sol"]"kalshi") that carry no raptor signal data; get_telemetry_assets returns the narrower set that does.GET{{DRADIS_API_URL}}/deployment/region{"region": "intl" | "us" | "kalshi", "available_types": ["politics", "sports", "crypto"]}GET{{DRADIS_API_URL}}/status{"strategy_markets": {…}, "session_started_at": "…", "raptors": {…}} — the strategy→market mapping, the current session id, and per-asset raptor connection health.GET{{DRADIS_API_URL}}/vipers/statusasset (optional, e.g. btc; omit for all squadrons){asset, strategy, last_eval_at, last_eval_secs_ago, last_outcome, last_reason, last_reason_secs_ago, last_signal_at, last_signal_secs_ago}last_outcome is one of signal | no_signal | error | timeout. last_reason is the named gate that vetoed the most recent entry attempt ("edge below required", "cooldown active"); only instrumented vipers populate it, so null means "reports liveness only", not "no veto". Interpretation rules:
last_eval_secs_ago > 120 → the evaluation loop is not reporting. Flag as a probable fault.last_outcome of error or timeout → flag as a fault regardless of age.GET{{DRADIS_API_URL}}/positionsasset (optional, e.g. btc)GET{{DRADIS_API_URL}}/positions/pendingasset (optional)GET{{DRADIS_API_URL}}/positions/confirmedasset (optional)GET{{DRADIS_API_URL}}/portfolio{"collateral", "positions_value", "total_value", "unrealized_pnl", "position_count", "prices_live"} (decimal values as strings)intl builds, collateral is probed live on-chain. On us and kalshi builds there is no wallet probe, so it falls back to the database-tracked snapshot. If prices_live is false, positions are marked at stale prices — say so when quoting total_value or unrealized_pnl.GET{{DRADIS_API_URL}}/pnl/historyasset (optional), limit (optional, default 200, clamped 1–1000)asset returns the aggregated curve across all assets, not the primary asset's. Pass asset explicitly whenever the user asks about one asset.GET{{DRADIS_API_URL}}/tradesasset (optional), limit (optional, default 100, clamped 1–500)limit above 500 is silently clamped; never claim more trades were returned than the array contains.GET{{DRADIS_API_URL}}/squadrons{asset}-{cadence}, e.g. btc-hourly, btc-open. Call this before any other squadron tool.GET{{DRADIS_API_URL}}/squadrons/{id}id — a squadron id from list_squadrons, e.g. btc-hourlyGET{{DRADIS_API_URL}}/squadrons/{id}/configid, e.g. btc-hourlyDynamicConfig, or 404 if no config row exists for that idPATCH{{DRADIS_API_URL}}/squadrons/{id}/configid, e.g. btc-hourlyapplication/json{"time_decay_position_size_usdc": "8.0"}get_config_schema first. Unknown keys are ignored and still return 200 — read the changed field back out of the response and confirm it before reporting success. Applied changes reach the patrol loop on its next tick.GET{{DRADIS_API_URL}}/configDynamicConfigget_squadron_config.PATCH{{DRADIS_API_URL}}/configapplication/json{"llm_autonomy_tier": 1}patch_squadron_config.GET{{DRADIS_API_URL}}/config/schema{key, group, enable_key, label, type, unit, min, max, step, advanced}key is exactly what a PATCH body expects. type is one of usd | price | pct | decimal | secs | bool. Use before any PATCH to validate the field name and bounds.GET{{DRADIS_API_URL}}/llm/recommendationsasset (optional), limit (optional, default 10, clamped 1–50)GET{{DRADIS_API_URL}}/llm/actionslimit (optional, default 100, clamped 1–500){id, batch_id, ts, expires_at, model, tier, ghost_mode, field, from_value, to_value, clamped, delta_pct, reason, status, status_detail, …}status is one of proposed | approved | applied | rejected | expired | reverted | failed. Rows with status: "proposed" are awaiting human approval. Depending on the configured autonomy tier the engine may have already applied changes on its own (status: "applied") — surface those when the user asks what changed. Reads the primary database only; the asset param is ignored here.POST{{DRADIS_API_URL}}/llm/actions/{id}/approveid — the numeric action id from get_llm_actions409 if it is no longer valid — report that as "the proposal went stale", not as an error to retry.patch_dynamic_config.POST{{DRADIS_API_URL}}/llm/actions/{id}/rejectidproposed actions can be rejected (409 otherwise). Rejections feed the model's negative-example corpus.GET{{DRADIS_API_URL}}/telemetryoracle_price, velocity_5s, velocity_1s, acceleration, drift_60m, drift_10m, funding_rateGET{{DRADIS_API_URL}}/telemetry/historyasset (optional, defaults to the primary asset), limit (optional, defaults to the full retained window ≈ 1 hour)GET{{DRADIS_API_URL}}/telemetry/assetslist_assetsGET{{DRADIS_API_URL}}/logstail (optional, default 500, clamped 1–2000){"count": n, "lines": [...]} from the in-memory ring buffer, oldest firstGET{{DRADIS_API_URL}}/latencyGET{{DRADIS_API_URL}}/taxonomy/raptorsmarket_class (required, e.g. crypto, sports, politics){id, display, implemented}GET{{DRADIS_API_URL}}/taxonomy/vipersmarket_class (required){id, display, venue_agnostic}GET{{DRADIS_API_URL}}/markets/availablemarket_type (required: crypto | sports | politics), expiry_window (optional: 1h | 4h | 24h | 7d; default varies by type — 24h sports, 30d crypto, 90d politics), min_liquidity (optional, default 500){"markets": [...]} — candidate markets for deploymentGET{{DRADIS_API_URL}}/deployments{id, market_id, market_type, raptors, vipers, status, squadron_id, error, created_at}These exist on the engine API and are omitted on purpose. If a user asks for them, explain that the skill is monitoring-and-configuration only and direct them to the Control Tower UI.
| Endpoint | Why omitted |
|---|---|
DELETE /api/positions/{token_id} | Removes a tracked position |
POST /api/positions/manual-exit | Places a real exit order (intl builds only) |
POST /api/positions/sync | Mutates position records from chain state (intl builds only) |
POST /api/squadrons/deploy | Commits capital to a new market |
/api/setup/* (GET/PUT/POST), POST /api/auth/login | Reads and writes venue credentials and admin tokens |
GET /api/trades/export | Bulk CSV of the full tradelog; download from the UI instead |