Deterministic market-state engine for trading agents — state, gate, coordinates, with receipts.
Call this server's tools from your browser. Nothing to install.
Decker API key — free tier available (issue at decker-ai.com Settings → API Keys or via Telegram /apikey)
Requests go through mcptry because browsers can't call most MCP servers directly. Header values are sent to the server for that request only and are never stored or logged.
Tool list from our last check. Connect live to refresh it.
Axis③ (Order/Execution) — closes (or partially reduces) an existing position through DECKER'S OWN execution engine (see decker.place_order for what that means — same account-linkage requirement applies here for real positions). Unlike place_order, there is no crypto-6 restriction — this reduces risk, not adds it, so any symbol you actually hold (including HL-synthetic/KRX paper positions) can be closed. Mode is NOT chosen by the caller — this looks up whatever position(s) actually exist for the symbol (real via live exchange query, virtual via the paper ledger) and closes whichever are open; if both a real and a virtual position exist for the same symbol, both are closed and the response reports execution_mode as 'mixed'. No open position for the symbol = a clean not-found response, not an error — safe to call speculatively.
e.g. BTCUSDT, XYZ_GOLDUSD — whatever symbol you hold. Aliases resolve like other tools.
Fraction of the current position to close, 0 < x <= 1. Default 1.0 = full close. E.g. 0.5 closes half.
This tool isn't marked read-only, so it may change something on the server or in an account you connected.
Axis③ (Order/Execution) — closes (or partially reduces) an existing position through DECKER'S OWN execution engine (see decker.place_order for what that means — same account-linkage requirement applies here for real positions). Unlike place_order, there is no crypto-6 restriction — this reduces risk, not adds it, so any symbol you actually hold (including HL-synthetic/KRX paper positions) can be closed. Mode is NOT chosen by the caller — this looks up whatever position(s) actually exist for the symbol (real via live exchange query, virtual via the paper ledger) and closes whichever are open; if both a real and a virtual position exist for the same symbol, both are closed and the response reports execution_mode as 'mixed'. No open position for the symbol = a clean not-found response, not an error — safe to call speculatively.
| Argument | Type | Description |
|---|---|---|
| symbol* | string | e.g. BTCUSDT, XYZ_GOLDUSD — whatever symbol you hold. Aliases resolve like other tools. |
| close_fraction | number | Fraction of the current position to close, 0 < x <= 1. Default 1.0 = full close. E.g. 0.5 closes half. |
{
"type": "object",
"required": [
"symbol"
],
"properties": {
"symbol": {
"type": "string",
"description": "e.g. BTCUSDT, XYZ_GOLDUSD — whatever symbol you hold. Aliases resolve like other tools."
},
"close_fraction": {
"type": "number",
"default": 1,
"maximum": 1,
"description": "Fraction of the current position to close, 0 < x <= 1. Default 1.0 = full close. E.g. 0.5 closes half.",
"exclusiveMinimum": 0
}
}
}THE answer for 'what is the situation and what do I do on this symbol now' — one verdict per symbol, built from the per-timeframe judgment state (CR1); no scores or weights. READ IN THIS ORDER: (1) verdict = state (direction · phase) + action: 진입 enter now (a new signal) | 유지 hold the existing stance, NOT a new entry | 청산 exit | 대기 wait; `reason` says why, `until` names the close that would change it. (2) summary_ko = ready-to-show Korean lines. (3) entry / stop / target / rr = the prices (entry.mode says whether it is a new entry or on hold — when the action is not 진입 it never says 'now'; target may be null with target.reason, e.g. no coordinate at least 2% from the entry price; rr mixes the entry timeframe's stop with the target timeframe's target). (4) as_of = the bar this answer is based on. Default = the answer only (about 3KB per symbol; summary_ko already narrates the next closes and the last signal). Pass detail=true for the structured detail: path (nearby prices and what a close there means), watch (upcoming bar closes in time order and the question each decides), story (this / last signal), ladders, matrix_summary (every timeframe's row incl. gate), tf_states (EVERY timeframe's own state: judgment stage, open reversal test with its confirm price, engine action, next-bar label chain, recently missed targets — none folded by direction), knots (every timeframe's decision prices ordered by distance from the price, up and down, with which timeframes and roles sit on each — the ORDER in which transactions would be reached, not a probability). `reason` may end with '반대 방향 반전 보류 중: …' = other timeframes whose reversal test runs AGAINST the verdict's direction, i.e. is testing the current stance (a fact, not a vote; it never changes the action). Omit `symbol` for all 14 symbols. Roles: frame = highest confirmed timeframe (sets direction and target); entry = lowest aligned timeframe whose signal is alive; 1w supplies direction/target only, never an entry. grade: 정렬 aligned | 되돌림 pullback against the frame | 전환 시험 frame under test | 재장전 no confirmed signal. Caveats: 정렬 does not mean every timeframe's gate is GO (check matrix_summary[].gate); entry.mode '지금' does not mean a new event this bar. Narrate or filter the result — do not re-decide coordinates.
| Argument | Type | Description |
|---|---|---|
| detail |
Price-axis EPISODE history — answers 'where did I buy/sell last time, what was the target value and the event chain then, and where did price move WITHOUT a signal (and which target values it passed = missing points)'. Read-only from the persisted event ledger (no recompute). `episodes` (newest first) = one target selection until the next: target {price, from_price, reason}, chain (primary-unit judgment events), signals {ts, side, entry, targets, resolved{status, close, bar_low, bar_high}}, left (target_left: MISSING = a target value price passed through with no judgment; MOVED), missing, signal_gap (no signal in that episode), move {start_close, end_close, pct}. `signal_gaps` (ascending) = the interval between consecutive signals with its price move and the missing points inside (last one is open). Missing points carry `sign` and `position` (price below/above the target at the pass) — classify on the read side; do not re-decide coordinates. History starts at the first persisted event of this generation.
| Argument | Type | Description |
|---|---|---|
| limit | integer | number of episodes |
| symbol* | string | e.g. BTCUSDT |
| timeframe* | string |
{
"type": "object",
"required": [
"symbol",
"timeframe"
],
"properties": {
"limit": {
"type": "integer",
"default": 5,
"maximum": 20,
"minimum": 1,
"description": "number of episodes"
},
"symbol": {
"type": "string",
"description": "e.g. BTCUSDT"
},
"timeframe": {
"enum": [
"30m",
"1h",
"4h",
"8h",
"1d",
"1w"
],
"type": "string"
}
}
}PER-TIMEFRAME judgment state of a symbol, structured (consumer contract CR1) — the evidence behind decker.get_assembly's single verdict. For 'what is the situation and what do I do' call decker.get_assembly first; use this when you need one timeframe's exact state. Per timeframe (top→short): stage = target_selected (대상선정) | pending (판정보류: the close is still inside the signal bar) | confirmed (확인) | failed (실패) | none; side = buy/sell (a failed signal asserts nothing about the opposite side); signal_time; signal_bar {time, low, high} (its edges decide confirm/fail); entry (= close at the signal); stop; target (take-profit: opposite-side confirmed-signal edge at least 2% from entry, else null + target_reason); unit_target (the price this timeframe is judging now — NOT the take-profit); units (the targets judged together, max 6, primary first); reverse_pending (a reversal on hold: the first reversing key appeared, the next judgment bar has not closed beyond the target); standing (the last confirmed signal's entry/stop/target while a new target is being selected); prev_signal; turn (reverse|same|null); levels (nearby prices). state_text = ready-to-show Korean lines (🟢▲ buy confirmed · 🔴▼ sell confirmed · ⚪ pending · 🟡 target selected / reversal on hold · ✖️ failed). timeframe_summary lists timeframes by (stage, side) — a listing, not a verdict; no scores, weights or probabilities anywhere. events = recent target_selected / signal / signal_resolved / reverse_failed / reverse_tested / conn_arrival, newest first, always including each timeframe's own latest 2 (pass `timeframe` for one timeframe's full history). as_of = the latest closed bar among the returned timeframes (it can be earlier than get_assembly.as_of, which is the 30m bar). Read as persisted (no recompute). A state reading, NOT a trade instruction. Crypto and Hyperliquid synthetics only (KRX: decker.get_reading). Before placing any order, check the intent with decker.validate_intent.
| Argument | Type | Description |
|---|---|---|
| symbol* | string | e.g. BTCUSDT, XYZ_GOLDUSD |
| timeframe | string |
Axis③ (Order/Execution) — this user's actual exposure (each open row carries vs_verdict: does the held side match the current symbol verdict, plus `knots` = the decision prices nearest to the price above and below, with which timeframes sit on each — the ORDER in which transactions would be reached, not a probability; `exit_levels` only when the held side matches): real open futures positions (execution_mode=real, with live sl_price/tp_price), virtual (paper) open positions, and the last 10 closed round-trips per mode. This is what your money actually did, distinct from decker.get_signals (axis②, what the engine recommends) — use this before deciding whether to place another order (avoid duplicate/over-exposure) and to check current protective stop/target on a real position.
No arguments.
{
"type": "object",
"properties": {}
}How ONE timeframe looks right now, in plain language: state (phase, this timeframe's key direction, posture GO/WATCH/HOLD — a stance, not an order), mtf.by_tf (the same for other timeframes), narrative, plus `assembly` = the symbol's single verdict (the same one decker.get_assembly returns: direction, action 진입/유지/청산/대기, entry/stop/target). ⚠ `state.direction` is only this timeframe's direction and can differ from `assembly.direction` — for the decision use `assembly` (direction_basis says which is which). For the full detail call decker.get_assembly with detail=true; for the structured per-timeframe judgment use decker.get_market_state. Engine-native raw fields are not exposed here.
| Argument | Type | Description |
|---|---|---|
| tf | string | |
| symbol* | string | e.g. BTCUSDT |
| include_tfs | string | Comma-separated additional TFs (e.g. '1h,4h,1d'). |
{
"type": "object",
"required": [
"symbol"
],
"properties": {
"tf": {
"enum": [
"15m",
"30m",
"1h",
"4h",
"8h",
"1d",
"1w"
],
"type": "string",
"default": "4h"
},
"symbol": {
"type": "string",
"description": "e.g. BTCUSDT"
},
"include_tfs": {
"type": "string",
"description": "Comma-separated additional TFs (e.g. '1h,4h,1d')."
}
}
}Active trading signals for the current user (with Skill Overlay applied), in customer-facing shape: coordinates (entry/target/stop), decision (ENTER/WAIT/SKIP — is_actual_trigger=true only for ENTER; WAIT/SKIP are standing candidates, not executed triggers), action_gate posture (GO/WATCH/HOLD — a stance, not an order command), progress, MTF verdict, and a plain-language summary_ko line. risk_reward_ratio is computed on the DISPLAYED coordinates (after overlay). Signals are retained rather than cut when they age (turn-retention policy) — read freshness_state (open|aged) / age_bars / freshness_sec before treating an old PENDING row as current. Filtered by symbols / min_progress / action_gate — action_gate here filters the CURRENT-MOMENT representative state, it does not search history (almost always 0 rows unless a gate is GO right now); for past GO events with their entry/target/stop and realized performance use decker.get_trigger_history instead. Before placing any order through any execution tool, check the intent with decker.validate_intent.
| Argument | Type | Description |
|---|---|---|
| limit | integer | |
| symbols | array | Symbol filter (e.g. ['BTCUSDT','ETHUSDT']). Omit for all. |
| timeframe | string | Signal horizon filter (30m=scalp, 1h=swing, 4h/8h/1d=position). The same symbol can hold OPPOSITE directions on different horizons — omit to get the latest active signal regardless of horizon (its timeframe field says which one you got; when a specific symbols[] was requested, a row's other_horizon_conflict field flags it if another horizon is ACTIVE with the opposite direction). Prefer decker.get_assembly for the composed cross-horizon judgment instead of guessing which horizon to pass here. |
| action_gate | string | Engine action gate filter (3-layer grammar: gate = transition posture, not an order command). Rows where the engine emitted no gate for this bar (effective_action_gate null, e.g. KRX daily) are excluded when this filter is set. |
ACTION axis — actual historical GO triggers (not standing WATCH/HOLD candidates) for a symbol, with entry/target/stop coordinates and realized performance (mfe_pct/mae_pct/exit_reason/exit_price from trigger_performance, null = still open). Distinct from decker.get_signals (which reflects only the current-moment state, not a searchable history — its action_gate filter answers 'what does symbol×tf look like right now', not 'when did this last fire'). Use this to answer 'what did the engine actually trigger recently and at what price' — decker.get_signals/get_market_state cannot answer that. direction is judgment_signals-native vocabulary ("long"/"short"), distinct from the "+"/"-" convention used by other tools — read as-is, no translation.
| Argument | Type | Description |
|---|---|---|
| limit | integer | |
| since | string | ISO8601 lower bound on trigger time (exclusive). Optional. |
| symbol* | string | e.g. BTCUSDT |
| timeframe | string | Optional — omit for all timeframes. |
{
"type": "object",
"required": [
"symbol"
],
"properties": {
"limit": {
"type": "integer",
"default": 20,
"maximum": 50,
"minimum": 1
},
"since": {
"type": "string",
"description": "ISO8601 lower bound on trigger time (exclusive). Optional."
},
"symbol": {
"type": "string",
"description": "e.g. BTCUSDT"
},
"timeframe": {
"enum": [
"30m",
"1h",
"4h",
"8h",
"1d"
],
"type": "string",
"description": "Optional — omit for all timeframes."
}
}
}Trading skill catalog + currently active overlay for this user. Returns 8 published skills (conservative_v0/standard_v0/aggressive_v0/default_v0/scalp_v0/tight_v0/wide_v0/swing_v0) and the user's selected one. Also returns symbol_skills: the symbols where this user's paper account turns the symbol verdict's entry event into an order (read-only here; turn on/off via REST /auto-order-rules/symbol-skills).
No arguments.
{
"type": "object",
"properties": {}
}Ready-to-show Korean status card for a symbol — the same card the daily briefing sends, built from the same consumer contract (CR1) as decker.get_market_state, so direction and prices always agree with it. Shows the timeframes grouped 상위/중간/단기 with one line each (direction + stage + the price that decided it), the nearby prices to watch, and a short explanation of the chosen timeframe (tf, default 4h): what closed beyond what, whether the signal flips the previous one, entry/target/stop. Also returns direction (+/- only while that timeframe's signal is pending or confirmed; a failed or absent signal gives no direction — failure does not assert the opposite side), wait_target, invalidation, ref_price and recent self-scoring verdicts. Use this when you want text to show as-is; use decker.get_market_state when you want structured values to reason over. layer=STATE_VIEW: a market-state reading, NOT a trade instruction. Before placing any order through any execution tool, check the intent with decker.validate_intent.
| Argument | Type | Description |
|---|---|---|
| tf | string | Optional view timeframe — the grounded narrative is composed on this TF's bar (e.g. '1h' when the user asks about the 1-hour picture). Omit for the engine's default action TF (usually 4h, same as the daily briefing card). |
| symbol* | string | e.g. BTCUSDT, XYZ_GOLDUSD (crypto + HL TradFi synthetics; KRX daily lineage not yet covered by view v1) |
{
"type": "object",
"required": [
"symbol"
],
"properties": {
"tf": {
"enum": [
"30m",
"1h",
"4h",
"8h",
"1d"
],
"type": "string",
"description": "Optional view timeframe — the grounded narrative is composed on this TF's bar (e.g. '1h' when the user asks about the 1-hour picture). Omit for the engine's default action TF (usually 4h, same as the daily briefing card)."
},
"symbol": {
"type": "string",
"description": "e.g. BTCUSDT, XYZ_GOLDUSD (crypto + HL TradFi synthetics; KRX daily lineage not yet covered by view v1)"
}
}
}Axis③ (Order/Execution) — unlike every other tool here, this one moves money. It places a market order through DECKER'S OWN execution engine (same path as the decker-ai.com chat trading UI, source='mcp') — it does NOT hand off to your own broker connection or exchange account; Decker executes using whatever exchange credentials this user has separately linked to their Decker account on the website. execution_mode (virtual|real) is NOT chosen by the caller — it is resolved server-side from this user's account settings (user_settings.execution_mode) AND the platform's real-trading kill switch; a real-money order requires both an explicit user opt-in AND role/tier eligibility (PRO/ENTERPRISE or admin) AND passing the tier's hard notional/leverage/daily-count caps (checked here before dispatch — violation blocks the order, does not downgrade it to virtual). The response always states which mode actually executed — treat 'virtual' in the response as authoritative even if you expected real. Restricted to the crypto-6 universe (BTCUSDT/ETHUSDT/SOLUSDT/BNBUSDT/XRPUSDT/DOGEUSDT) for this MCP path — HL-synthetic and KRX symbols are read-only via other tools. Call decker.validate_intent first to read the engine's current stance; this tool does not check it for you. Positions are tracked as ONE net row per user+symbol+mode, not per order — if you already hold a position on this symbol, this order nets into it and the response's pre_existing_position field says so. A later close_position call closes the combined total, not just what this call added.
| Argument | Type | Description |
|---|---|---|
| side* | string | Order direction (buy/long or sell/short). |
| symbol* | string | Crypto-6 only for this MCP write path. |
| notional_usd* | number | Order size in USD (quantity = notional_usd / current price). This is the value checked against the account's tier notional cap. |
Change active trading skill overlay for this user. Immediately affects all subsequent get_signals calls and downstream channels.
| Argument | Type | Description |
|---|---|---|
| skill_id* | string | trading_skills.id (e.g. 'aggressive_v0'). |
{
"type": "object",
"required": [
"skill_id"
],
"properties": {
"skill_id": {
"type": "string",
"description": "trading_skills.id (e.g. 'aggressive_v0')."
}
}
}Axis③ (Order/Execution) — modifies the stop-loss and/or take-profit on an EXISTING open position. There is no cancel_order tool because Decker only places market orders (there is no resting order to cancel) — the actual gap this fills is modifying protective stops on a position you already hold. real: cancels the old exchange stop/take-profit order(s) and places new ones at the given price(s) (new order placed first, old one canceled only after — no unprotected window). virtual: updates the paper position's stop_loss/take_profit columns directly (polled by the paper monitor). Provide at least one of sl_price/tp_price — the other side, if omitted, is left at its current value. If both a real and a virtual position are open for this symbol, pass mode explicitly or the call is rejected asking which one.
| Argument | Type | Description |
|---|---|---|
| mode | string | Only required when both a real and a virtual position are open for this symbol — says which one to modify. |
| symbol* | string | e.g. BTCUSDT — must be a symbol you currently hold. |
| sl_price | number | New stop-loss price. Omit to leave the current stop unchanged. |
| tp_price | number | New take-profit price. Omit to leave the current target unchanged. |
{
"type": "object",
"required": [
"symbol"
],
"properties": {
"mode": {
"enum": [
"real",
"virtual"
],
"type": "string",
"description": "Only required when both a real and a virtual position are open for this symbol — says which one to modify."
},
"symbol": {
"type": "string",
"description": "e.g. BTCUSDT — must be a symbol you currently hold."
},
"sl_price": {
"type": "number",
"description": "New stop-loss price. Omit to leave the current stop unchanged.",
"exclusiveMinimum": 0
},
"tp_price": {
"type": "number",
"description": "New take-profit price. Omit to leave the current target unchanged.",
"exclusiveMinimum": 0
}
}
}Pre-trade gate check for a proposed order intent. Call this BEFORE placing any order through any execution tool (e.g. a broker MCP's review→place flow). Checks the intent (symbol + side) against Decker's deterministic market state: engine action_gate (GO/WATCH/HOLD — a transition posture, not an order command), current structural state, and the active signal's direction / invalidation (stop) coordinates. Returns a stance reading, NOT an approval or rejection: the vocabulary is the engine gate as-is plus a mechanical tf_signal_alignment (aligned/opposed vs the active signal's direction). covered=false means the engine does not emit state for this symbol — treat as unknown, not as HOLD. top-level action_gate can be null even when covered=true — this is not a missing field, it means the current bar has no fresh trigger reading; check gate_null_reason ('no_gate_available' = neither the current bar nor the carried-forward signal had a gate at all, vs 'signal_stale' = a value existed but the underlying signal exceeded the staleness threshold and was deliberately suppressed rather than served as a confident-but-old answer). The order decision and responsibility remain with the calling agent/user. Every check is persisted to an auditable decision ledger (check_id). READ verdict_check FIRST: it compares the intent with the symbol's single verdict (the same one decker.get_assembly returns — direction, action 진입/유지/청산/대기, entry/stop/target, what would change it) and says in summary_ko whether a NEW entry exists now (entry_now) or the stance is only being held. The timeframe-level gate/signal fields below describe one timeframe (default: your open position's) and can be stale.
| Argument | Type | Description |
|---|---|---|
| side* | string | Proposed order direction (buy/long = +, sell/short = -). |
| symbol* | string | e.g. BTCUSDT, SILVER, 테슬라 — aliases resolve to the engine symbol (XYZ_SILVERUSD, XYZ_TSLAUSD, …). |
| timeframe |
| boolean |
| Include path / watch / story / ladders / matrix_summary (about 20KB per symbol). Default false: the answer only. |
| symbol | string | Optional symbol or alias (BTC, 비트코인, GOLD, 테슬라...). Omit for all 14 symbols. |
{
"type": "object",
"properties": {
"detail": {
"type": "boolean",
"description": "Include path / watch / story / ladders / matrix_summary (about 20KB per symbol). Default false: the answer only."
},
"symbol": {
"type": "string",
"description": "Optional symbol or alias (BTC, 비트코인, GOLD, 테슬라...). Omit for all 14 symbols."
}
}
}| Alias for a single-element `timeframes` (backward compatible). |
| timeframes | array | Subset to return. Omit for all timeframes. |
| events_limit | integer |
{
"type": "object",
"required": [
"symbol"
],
"properties": {
"symbol": {
"type": "string",
"description": "e.g. BTCUSDT, XYZ_GOLDUSD"
},
"timeframe": {
"enum": [
"30m",
"1h",
"4h",
"8h",
"1d",
"1w"
],
"type": "string",
"description": "Alias for a single-element `timeframes` (backward compatible)."
},
"timeframes": {
"type": "array",
"items": {
"enum": [
"30m",
"1h",
"4h",
"8h",
"1d",
"1w"
],
"type": "string"
},
"description": "Subset to return. Omit for all timeframes."
},
"events_limit": {
"type": "integer",
"default": 10,
"maximum": 50,
"minimum": 0
}
}
}| min_progress | number | Minimum progress_pct (0-100). |
{
"type": "object",
"properties": {
"limit": {
"type": "integer",
"default": 20,
"maximum": 100,
"minimum": 1
},
"symbols": {
"type": "array",
"items": {
"type": "string"
},
"description": "Symbol filter (e.g. ['BTCUSDT','ETHUSDT']). Omit for all."
},
"timeframe": {
"enum": [
"30m",
"1h",
"4h",
"8h",
"1d"
],
"type": "string",
"description": "Signal horizon filter (30m=scalp, 1h=swing, 4h/8h/1d=position). The same symbol can hold OPPOSITE directions on different horizons — omit to get the latest active signal regardless of horizon (its timeframe field says which one you got; when a specific symbols[] was requested, a row's other_horizon_conflict field flags it if another horizon is ACTIVE with the opposite direction). Prefer decker.get_assembly for the composed cross-horizon judgment instead of guessing which horizon to pass here."
},
"action_gate": {
"enum": [
"GO",
"WATCH",
"HOLD"
],
"type": "string",
"description": "Engine action gate filter (3-layer grammar: gate = transition posture, not an order command). Rows where the engine emitted no gate for this bar (effective_action_gate null, e.g. KRX daily) are excluded when this filter is set."
},
"min_progress": {
"type": "number",
"maximum": 100,
"minimum": 0,
"description": "Minimum progress_pct (0-100)."
}
}
}{
"type": "object",
"required": [
"symbol",
"side",
"notional_usd"
],
"properties": {
"side": {
"enum": [
"buy",
"long",
"sell",
"short"
],
"type": "string",
"description": "Order direction (buy/long or sell/short)."
},
"symbol": {
"enum": [
"BTCUSDT",
"ETHUSDT",
"SOLUSDT",
"BNBUSDT",
"XRPUSDT",
"DOGEUSDT"
],
"type": "string",
"description": "Crypto-6 only for this MCP write path."
},
"notional_usd": {
"type": "number",
"description": "Order size in USD (quantity = notional_usd / current price). This is the value checked against the account's tier notional cap.",
"exclusiveMinimum": 0
}
}
}| string |
| Gate horizon. Omit = your open position's entry TF if you hold one on this symbol, else decker.get_assembly's entry TF (the single judgment authority's current best-path TF), else the engine's default action TF (4h). |
| order_type | string | Optional, informational (market/limit/…) — recorded in the ledger, does not change the state verdict. |
{
"type": "object",
"required": [
"symbol",
"side"
],
"properties": {
"side": {
"enum": [
"buy",
"long",
"sell",
"short"
],
"type": "string",
"description": "Proposed order direction (buy/long = +, sell/short = -)."
},
"symbol": {
"type": "string",
"description": "e.g. BTCUSDT, SILVER, 테슬라 — aliases resolve to the engine symbol (XYZ_SILVERUSD, XYZ_TSLAUSD, …)."
},
"timeframe": {
"enum": [
"30m",
"1h",
"4h",
"8h",
"1d"
],
"type": "string",
"description": "Gate horizon. Omit = your open position's entry TF if you hold one on this symbol, else decker.get_assembly's entry TF (the single judgment authority's current best-path TF), else the engine's default action TF (4h)."
},
"order_type": {
"type": "string",
"description": "Optional, informational (market/limit/…) — recorded in the ledger, does not change the state verdict."
}
}
}