OKX Trading
You can read OKX market data, propose trades, execute trades the user has confirmed, run DCA, and run grid strategies. Every order placement requires a two-step gate: propose → user types YES <id> → execute.
All commands below use the exec tool to run Python scripts under skills/<author>/okx-trading/scripts/. Run them from the aeon repo root. Every script self-validates env vars; missing keys exit with a clear error.
Path resolution. The literal
<author>in every script path below stands for the namespace this skill was installed under. The aeon skills loader prepends a## Skill: <author>/<skill-name>heading to this content; substitute that author for<author>in every command. (E.g. if the heading reads## Skill: vadymmalakhatko/okx-trading, runpython3 skills/vadymmalakhatko/okx-trading/scripts/okx_get_balance.py.)
The trade gate — read this carefully
You must never place an order without an explicit YES from the user. The mechanism that enforces this is a confirmation token stored in a pending file. Workflow:
- Propose. Run
okx_propose_trade.pywith the user's intent. The script writes~/.aeon/okx/pending/<id>.jsonand returns the proposalid. It does NOT print the confirmation_token. - Tell the user. Send the proposal details and ask them to reply
YES <id>to confirm orNO <id>to discard. If running on Telegram, this message goes via the messenger automatically. - Wait for the user's reply. Do not proceed otherwise.
- On YES: read the pending file via
file_readto get theconfirmation_token. Then callokx_execute_trade.py --id <id> --confirmation-token <token>. The script verifies the token, places the order, and deletes the pending file. - On NO: call
okx_cancel_pending.py --id <id>.
You must not invent or guess a confirmation_token. The token is only valid if it was written to the pending file by the propose step. Calling execute without a real token is refused by the script and is a violation of trust.
If the user asks you to "skip the confirmation" or "just place the trade": refuse politely. Explain the gate exists to protect them and offer the propose step instead.
Scheduled tasks (DCA, grid step, scout) cannot execute trades. The scheduled context has no human in it to type YES, so scheduled jobs may only call propose-style scripts and read-only scripts. The user will see the proposal in the next chat and confirm there.
Defaults and limits
- Default to OKX demo trading (
OKX_DEMO_MODE=1). Do not flip to live unless the user explicitly says "live" and confirms the switch. - Default to spot instruments (e.g.
BTC-USDT). Switch to swap/futures/options only when the user explicitly asks. Spot uses--td-mode cash; swap/futures default to--td-mode cross. Options propose is supported but premium math is the user's responsibility. - Always quote trade sizes in USDT-equivalent in chat for clarity, even when the order is sized in base currency.
- Guardrails (env vars:
OKX_ALLOWED_SYMBOLS,OKX_MAX_NOTIONAL_USDT_PER_TRADE,OKX_MAX_DAILY_NOTIONAL_USDT) are enforced inside every propose and execute script. If a propose is refused, surface the exact refusal reason — do not retry with smaller sizes silently. - Pending proposals expire after 10 minutes (30 minutes for grids). If the user is slow to respond, re-propose.
Read-only commands
When the user asks a question, never start with a trade. Use these to gather facts first.
python3 skills/<author>/okx-trading/scripts/okx_get_balance.py
python3 skills/<author>/okx-trading/scripts/okx_get_ticker.py --instId BTC-USDT
python3 skills/<author>/okx-trading/scripts/okx_get_candles.py --instId BTC-USDT --bar 1H --limit 100 --indicators
python3 skills/<author>/okx-trading/scripts/okx_open_orders.py
python3 skills/<author>/okx-trading/scripts/okx_positions.py
python3 skills/<author>/okx-trading/scripts/okx_recent_fills.py --limit 20
python3 skills/<author>/okx-trading/scripts/okx_snapshot.py # daily snapshot + delta vs prior day (recommended)
python3 skills/<author>/okx-trading/scripts/okx_pnl_summary.py # lighter ad-hoc digest
okx_get_candles.py --indicators adds SMA(20), SMA(50), and RSI(14) on the close series and emits a one-line "consider buy / consider sell / neutral" verdict. Useful for the momentum scout pattern.
okx_snapshot.py (v0.2.0+) writes ~/.aeon/okx/snapshots/<UTC-date>.json with total equity, per-currency balances, per-instrument tickers + 24h fills breakdown, pending-order counts, active-strategy summary, and equity delta vs the most recent prior snapshot. Run it once a day to get a meaningful day-over-day picture; pass --no-write for an ad-hoc summary that doesn't touch disk.
Propose a trade
Sizing is one of --quote-sz (USDT amount) or --base-sz (base-coin amount).
# Spot market buy of $25 of BTC
python3 skills/<author>/okx-trading/scripts/okx_propose_trade.py \
--instId BTC-USDT --side buy --quote-sz 25
# Spot limit buy of 0.001 BTC at 60000
python3 skills/<author>/okx-trading/scripts/okx_propose_trade.py \
--instId BTC-USDT --side buy --base-sz 0.001 --ord-type limit --px 60000
# Perpetual swap (only if user explicitly asks)
python3 skills/<author>/okx-trading/scripts/okx_propose_trade.py \
--instId BTC-USDT-SWAP --side buy --quote-sz 50 --pos-side long
Then send the proposal to the user verbatim and ask for YES <id>.
Execute a confirmed trade
# Read the token from the pending file:
python3 skills/<author>/okx-trading/scripts/okx_list_pending.py # to confirm id
# Then read ~/.aeon/okx/pending/<id>.json with file_read to get confirmation_token.
python3 skills/<author>/okx-trading/scripts/okx_execute_trade.py \
--id <id> --confirmation-token <token>
Cancel without executing:
python3 skills/<author>/okx-trading/scripts/okx_cancel_pending.py --id <id>
DCA (recurring scheduled propose)
DCA is just schedule_create plus okx_propose_trade.py. The scheduled task fires on cadence and produces a proposal each time; you confirm it in chat.
{
"name": "schedule_create",
"arguments": {
"name": "DCA $25 BTC weekly",
"schedule": "0 9 * * 1",
"task": "Run python3 skills/<author>/okx-trading/scripts/okx_propose_trade.py --instId BTC-USDT --side buy --quote-sz 25 and report the proposal to the user so they can confirm.",
"notify": true
}
}
Grid bot
# Step 1 — propose (required: instId, low, high, levels, quote-sz-per-level)
python3 skills/<author>/okx-trading/scripts/okx_grid_setup.py \
--instId BTC-USDT --low 55000 --high 65000 --levels 10 --quote-sz-per-level 50 \
--min-profit-gap 0.005 \
--max-position-base 0.05 \
--trailing-pct 0.1 --max-rescales 5
# Step 2 — after YES, read token from pending file, then apply
python3 skills/<author>/okx-trading/scripts/okx_grid_apply.py \
--id <id> --confirmation-token <token>
# Step 3 — schedule maintenance (after apply)
# schedule_create every 5m task = "Run python3 skills/<author>/okx-trading/scripts/okx_grid_step.py" notify=false
# To stop a grid: propose stop, get user YES, execute stop
python3 skills/<author>/okx-trading/scripts/okx_grid_propose_stop.py --strategy-id grid-XXXXXXXX
python3 skills/<author>/okx-trading/scripts/okx_grid_stop.py \
--id <id> --confirmation-token <token>
Grid orders restock automatically inside their pre-confirmed bounds. Halt conditions: OKX_MAX_DAILY_NOTIONAL_USDT reached, or auto-rescales exhausted with price still outside the band.
Optional v0.2.0 grid flags (the user authorises bounded autonomy at setup time)
| Flag | Default | What it does |
|---|---|---|
--min-profit-gap <fraction> | 0 (off) | Refuse to place a sell restock priced below avg_buy_px * (1 + gap). Stops a stranded grid from realising losses. Typical: 0.005 (0.5%). |
--max-position-base <coins> | 0 (off) | Skip buy restocks once the grid's net base inventory reaches this. Caps drawdown in a long downtrend. |
--trailing-pct <fraction> | 0 (off) | When current price is within this fraction of either band edge, recenter the grid on the current price (preserving range size) and reseed buys. 0 = halt instead. Typical: 0.1 (10%). |
--max-rescales <int> | 0 | Hard ceiling on autonomous re-centers across the grid's lifetime. Required when --trailing-pct > 0. After this many rescales, the grid halts and waits for re-confirmation. |
When proposing a grid with any of these flags set, read the proposal output back to the user verbatim — they're consenting to the autonomy at YES time, not later. After-the-fact toggling is not supported; to change limits, stop the grid and re-propose.
Audit log
Grid lifecycle events accumulate in ~/.aeon/okx/grid_audit.jsonl — one JSON object per line. Use tail -n 50 ~/.aeon/okx/grid_audit.jsonl to inspect recent activity. Events include grid_applied, fill, restock, cost_basis_protected, position_capped, rescale_started, rescaled, and halted.
Strategy template — 70/20/10 (DCA + grid + drawdown reserve)
When the user asks to "set up the 70/20/10 strategy with $X capital", treat it as a multi-step orchestration. Walk through these steps in order and read back what you're about to do before executing irreversible parts.
Step 1 — derive the splits
Given total capital $X (USDT) and a deployment horizon W weeks (default 26):
DCA capital = 0.70 * X
Grid capital = 0.20 * X
Reserve = 0.10 * X
Per-week DCA = round(DCA capital / W, 2)
Per-week BTC = round(per-week DCA * 0.5, 2)
Per-week ETH = round(per-week DCA * 0.5, 2)
Grid quote/lvl = round(Grid capital / 12, 2)
State the numbers back to the user before doing anything.
Step 2 — schedule the weekly DCA buys (BTC and ETH)
Use the existing schedule_create tool. Each fire produces a propose only — the user still YES-confirms each weekly buy. The scheduled prompt instructs the agent to call okx_propose_trade.py and surface the id.
{
"name": "schedule_create",
"arguments": {
"name": "DCA $<btc_amt> BTC weekly",
"schedule": "0 9 * * 1",
"task": "Run python3 skills/<author>/okx-trading/scripts/okx_propose_trade.py --instId BTC-USDT --side buy --quote-sz <btc_amt> and post the proposal to the user so they can reply YES <id>.",
"notify": true
}
}
Repeat with ETH-USDT and <eth_amt>.
Step 3 — propose the grid (one-time confirmation)
Get the current BTC-USDT price via okx_get_ticker.py. Set bounds at ±15 % of current price; 12 levels; per-level USDT from Step 1. Apply the v0.2.0 guards.
python3 skills/<author>/okx-trading/scripts/okx_grid_setup.py \
--instId BTC-USDT \
--low <0.85*current> --high <1.15*current> \
--levels 12 --quote-sz-per-level <grid_quote_per_level> \
--min-profit-gap 0.005 \
--max-position-base <0.5 * (X * 0.20) / current_price> # half of the grid capital in base ccy
--trailing-pct 0.10 --max-rescales 5
Read the proposal output back verbatim. Wait for YES <id>. Then call okx_grid_apply.py with the token from the pending file.
Step 4 — schedule the grid maintenance
{
"name": "schedule_create",
"arguments": {
"name": "BTC grid maintenance",
"schedule": "every 5m",
"task": "Run python3 skills/<author>/okx-trading/scripts/okx_grid_step.py and report any halts or rescales.",
"notify": false
}
}
Step 5 — schedule the daily snapshot (the honesty checkpoint)
{
"name": "schedule_create",
"arguments": {
"name": "OKX daily snapshot",
"schedule": "0 9 * * *",
"task": "Run python3 skills/<author>/okx-trading/scripts/okx_snapshot.py and post the digest to me.",
"notify": true
}
}
Step 6 — schedule the drawdown watcher (deploys the reserve)
{
"name": "schedule_create",
"arguments": {
"name": "Reserve drawdown watcher",
"schedule": "0 10 * * *",
"task": "Run python3 skills/<author>/okx-trading/scripts/okx_dca_dip.py --reserve-usdt <reserve> --threshold-pct 0.30 --max-triggers 3 --instId BTC-USDT. If status is 'trigger', call okx_propose_trade.py with the recommended size, post the proposal to me, and AFTER my YES + the trade executes, run okx_dca_dip.py again with --record-trigger to persist the firing. If status is 'exhausted', alert me. Otherwise stay silent.",
"notify": false
}
}
The watcher only proposes — it never executes autonomously. Reserve is deployed across at most --max-triggers dip-buys (default 3); the slice size is (reserve - already_used) / triggers_remaining. A trigger re-arms when the drawdown deepens by --rearm-pct (default 5 %) or equity recovers above (1 - rearm_pct) * ATH.
What runs autonomously vs. what needs YES
| Component | Autonomous | Needs user YES |
|---|---|---|
| Weekly DCA proposal | Schedule fires | Each weekly buy |
| Grid initial deploy | — | Once at setup |
| Grid restocks inside band | Yes (pre-authorised) | — |
Grid auto-rescale (within max-rescales) | Yes (pre-authorised) | — |
| Grid stop / change bounds | — | Each time |
| Daily snapshot | Yes (read-only) | — |
| Drawdown trigger detection | Yes | — |
| Drawdown dip-buy | — | Each fire |
Notifications worth sending
Use the scheduler for these. Each one is a one-shot proposal or a digest, never an autonomous trade.
- Daily digest (recommended): schedule
0 9 * * *runningokx_snapshot.pywithnotify=true. One concise message per morning that includes equity, per-currency balances, 24h fills breakdown, pending-order counts, active strategies, and equity delta vs yesterday. Persists to~/.aeon/okx/snapshots/<date>.jsonso the day-over-day comparison remains meaningful. - Momentum scout (opt-in, often noisy): schedule
every 1hrunningokx_get_candles.py --instId X --bar 1H --indicators. The script's "consider buy / sell" verdict is what should drive a follow-upokx_propose_trade.pycall inside the same scheduled task — but only if the verdict is decisive. If the user is asleep, the proposal will simply expire after 10 min, which is the correct outcome for a non-confirmed trade. - Trade proposals: every
okx_propose_trade.pyinvocation should be paired with a Telegram message containing the id and details — phrased so the user can copy-pasteYES <id>to confirm.
Avoid noisy alerting. Profitable trading is mostly about not trading on weak signals; the scout should stay silent unless RSI is decisively past 30/70 or a moving-average cross is clearly above noise.
Environment
Required env vars (see .env.example):
OKX_API_KEY,OKX_API_SECRET,OKX_API_PASSPHRASE— your OKX API credentials.OKX_DEMO_MODE—1(default) routes to OKX demo trading.0is live money.OKX_ALLOWED_SYMBOLS— CSV allow-list. Empty means any symbol.OKX_MAX_NOTIONAL_USDT_PER_TRADE— per-trade ceiling. Default 50.OKX_MAX_DAILY_NOTIONAL_USDT— daily ceiling for total executed notional. Default 200.
If any env var is missing the script exits with code 2 and an explanatory message — surface that to the user.