Strategy API (ctx)

A strategy is one JavaScript function. Backtest and live use exactly the same interface — nothing exists on only one side.

function onUpdate(ctx) {
  // called per candle (backtest) / per update (live)
  // return null, one order object, or an array of orders
}

What can you build?

  • Markets: crypto spot (Upbit, Binance and other connected exchanges), perpetual futures (Binance USDT-M & COIN-M and others — leverage, long/short, isolated/cross margin), Korean stocks (broker open APIs — Start trading Korean stocks), US & global stocks (IBKR — not yet confirmed on a real device), FX/CFD (MetaTrader 5 — live path in progress). Backtest, paper and live coverage differ per market.
  • Live futures: some derivatives exchanges require the key screen's [Connection test] (a small order round trip) before live trading. Once your account passes it you can trade live and run automatically on that exchange right away (change the key and you test again). Exchanges whose API cannot separate trading from withdrawal need you to read a warning and consent explicitly before the key is stored, and seed phrases are never accepted — see Security.
  • Multi-symbol scanner: one strategy watches many symbols (up to 200) on the same exchange — per-symbol positions, orders and state, separated automatically.
  • Ordering on two exchanges from one strategy (legs — cross-exchange arbitrage/hedging): backtests (incl. jobs), paper AND live real orders are supported. Per-leg wallets, fees and an interlocked kill-switch; live requires API keys for every leg before it starts (no half-executed hedge, no silent downgrade). P&L of legs in different currencies is never aggregated (an approximate USD figure is shown for reference only).
  • Aux-symbol reference: use another symbol/exchange's price as a signal (ctx.ref slots — e.g. watch Binance BTC while trading on Upbit). Works in backtests, paper and live.
  • Multiple exchanges at once: you can also launch one run per exchange — a single agent holds keys for several exchanges and runs them all in parallel (strategy A on Upbit + strategy B on Binance futures).
  • Multi-agent cluster: scanner symbols are distributed across several agents automatically and run in parallel (Elite plan + onboarding required).
  • Data: candles (1m–1d), volume, orderbook, funding rates, open interest/liquidations, base indicators (US Treasury yields, dollar, VIX, oil… — collected by your agent, read with ctx.data('base.name')), your own DB (a DATA declaration in the strategy code — connection details stay on the agent), and external signals (webhooks & polling — ctx.signal, see External signals). Unknown values are null — never faked as 0.
  • FX rates (USD/KRW for the kimchi premium, KRW↔USDT conversion): there is no built-in rate — we no longer collect FX. Fetch it yourself — the simplest is a webhook signal ctx.signal: e.g. a TradingView alert on 1-minute USDKRW (once per bar close) posting to /api/hooks/t/<token>?slot=usdkrw with body {"rate": {{close}}, "t": "{{timenow}}"}, then const s = ctx.signal('usdkrw'); const rate = s && Number(s.payload.rate) and drop stale values by s.age (seconds; server limit 12 per minute per slot). Backtests see only the records from when you started sending, as-of. Otherwise live with ctx.http (declare your FX vendor, approve it on the run) or, for backtests too, with ctx.data (your own rate table in DATA, or the daily base.usdkrw_h10 series, which is published about 10 days late). No rate = null = no premium. The old ctx.fx is kept only so saved strategies don't crash — it always returns null.
  • Orders: market, limit, split entries, live-only smart orders (chasing/trigger).
  • Safety: the wallet (max loss = the wallet), order-rate limit and approval ceiling, dry-run. (Old live runs deployed without a base amount and legs keep the previous loss kill-switch.)
  • Validation: backtests up to 100k bars, walk-forward via fixed end dates, plus a per-tick decision-time estimate in the studio and a live tick monitor (see on the run card whether your server keeps up).
Strategy studio — my strategies and the code editor
Strategy studio — my strategies and the code editor
Strategy list — grouped by engine · return/MDD
Strategy list — grouped by engine · return/MDD

Not supported yet (we don't pretend otherwise):

  • Live trading with legs split across agents — split execution (a compute agent plus per-leg execution agents) works in paper only. Live orders open after a two-device measurement. Live legs runs today use one agent that holds every leg's keys.
  • Automatic cluster rebalancing — distribution is static. If an agent drops, its symbols wait and the UI says so honestly (they are not silently moved).
  • Real-account verification of some Korean brokers — LS Securities, NH Namuh PLUG, NH N2 PLUG, Toss Securities: the order/fill round trip has not yet been confirmed on a real account (the deploy shows an 'unverified' warning with the reason). If something is wrong, the broker's error code is shown as is. Start small.

Rules

  • Pure function — no network, no imports, no async. Only the ctx below.
  • qty is always an amount of coin (not cash).
  • Indicators return null without enough data — always null-check.
  • Keep your variables in ctx.state; they persist across calls.
  • ctx.ref slots use logical names ('hedge' etc.) — real symbols are bound at deploy time.
  • Do not feature-detect ctx (typeof ctx.watch === 'function' and the like). The platform checks at deploy time that the agent supports every ctx feature the strategy uses, and refuses with the required version if not.
  • ctx.watch only displays the prices you are waiting for on the chart and run card — it is not an order. Only the return value of onUpdate trades.
  • If the entry also needs non-price conditions (RSI etc.), add them as conds — with the same values you decided on (same period, same bar): conds: [{ label: 'RSI(14) close', now: rsi, op: '<', ref: 30, closed: true }]. If any is false the run card shows "waiting on conditions" and hides the arrival estimate.

Order objects

  • Market: { side: 'buy'|'sell', qty } — walks the book (slippage, partial fills, taker fee).
  • Limit: { side, qty, type: 'limit', price } — fills at your price with maker fee when touched. postOnly: true rejects a limit that would fill immediately.
  • Smart (work order): { side, qty, type: 'smart', chase?, slices?, trigger?, hardStopMs? } — executed over time: chasing (peg the book → cross at deadline), slicing, stop/trailing triggers, and a hard time budget, in live and paper runs. Backtests approximate it as a market order — validate with a live paper run.
  • Limit ladder: { book: [{ key, side, qty, price | at: 'top' }], tol?, refreshMs? } — return the list of limits that should be resting, every tick. It is reconciled with what is already resting: inside the tolerance nothing is sent, outside it the order is amended, keys you drop are cancelled. Tolerance = max(0.05%, distance from price × 0.2) — tight near the price, loose far away, nearest handled first. Maker-only (postOnly) by default. at: 'top' = best bid + 1 tick (or the best bid if that would cross). refreshMs re-prices inside the tolerance when the order is that old and ≥1 tick off (live: only while the rate limit has spare budget). A filled key is not placed again until you omit it once. Backtests have no tick size, so top-of-book joins the best price (noted in the result).
  • Cancel all: { cancel: 'all' } — cancels resting limits AND all work orders (chasing can't be stopped by canceling a single order).

Sending out (ctx.http) — alerts & external APIs

A strategy can call Telegram/Discord/Slack alerts or an external API (agent 1.348.0+). Keys never go in the code — keep them in your agent's HTTP vault; the code only names a slot.

const HTTP = { origins: ['https://api.telegram.org'], creds: { tg: 'https://api.telegram.org' } };
function onUpdate(ctx) {
  if (ctx.close > 100000) ctx.http.send({ id: 'alert', cred: 'tg', path: '/sendMessage', method: 'POST', json: { chat_id: 1, text: 'BTC > 100k' } });
  return null;
}
  • Nothing leaves for an origin not in the HTTP declaration. Each run needs your explicit approval of slot ↔ vault entry on the agent screen (re-approve when the declaration or code changes).
  • The vault shows the domain large when you add a key; lookalike (te1egram.org…) or unfamiliar domains must be retyped to save. A key is attached only for its origin and scrubbed from responses.
  • 5 sends per tick · 64KB responses · redirects not followed · private IPs blocked. Results arrive next tick via ctx.http.result(id).
  • Backtests never send — the result only counts them in httpNotSent.
  • Key-shaped strings in code: a warning on save, placeholders when an AI reads the code, and the strategy is skipped by the public git publish.

The table below is the entire ctx (generated from the same source as the editor autocompletion). To get strategy code from an AI, use Create an AI Strategy or paste the whole AI reference.

ctx

Ginagawa ang talahanayang ito mula sa parehong pinagmulan (code) ng autocomplete ng editor at ng AI docs (/llms.txt) — walang ibang function maliban sa nakalista rito.

상태

ctx.candle현재 봉 { t, o, h, l, c, v } — t 는 실데이터면 실제 봉 시각(epoch ms), 합성 시세만 봉 번호. ★라이브·페이퍼도 실제 봉 시작 시각(에이전트 1.66.0+ — 그 이전 라이브는 링버퍼 인덱스(499)로 굳어 있었다: 요일·시간대 로직을 쓰면 반드시 1.66.0 이상에서 확인할 것). 시각을 모르는 경로는 null(인덱스를 시각인 척 주지 않는다)
ctx.price현재가(종가)
ctx.closes현재 봉까지의 종가 **배열**(미래 없음) — 숫자가 아니다. 직전 마감 봉 종가 = ctx.closes.at(-2), 지금 봉 = ctx.closes.at(-1). 배열 자체를 비교·계산하면 던진다(2026-10-10)
ctx.alloc지갑 { denom, status, target, current, pendingReturn, reserved, lockedByMargin, lastForced, rev, changes } — 읽기 전용, 숫자는 denom 단위. status≠'ok' 면 줄이기만 된다. legs 는 { ex: 값 }. 옛 실거래 run(기준액 없음)은 null
ctx.highs봉별 고가 배열(closes 와 같은 길이). 모르면 null · 직전 마감 봉 = .at(-2)
ctx.lows봉별 저가 배열(highs 와 같은 규칙)
ctx.opens봉별 시가 배열(highs 와 같은 규칙)
ctx.times봉 시작 시각 배열(epoch ms) · 모르면 null
ctx.i현재 봉 번호
ctx.position보유 수량(코인). 선물(usdm)에선 부호 수량 — +롱/−숏
ctx.entryPx평균 진입가 — 선물: 포지션 평균 진입가 · spot: 보유 중 평균 매수 단가(체결가 가중, 수수료 제외 · 라이브/페이퍼는 이 실행이 직접 산 체결 기준 — 직접 안 산 보유가 섞이면 null · 코인을 들고 시작한 실행은 배포 때 유저가 넣은 매수 단가(기본 현재 시세, 비우면 null)). 포지션 없으면/모르면 null
ctx.liqPx(선물·CFD) 격리 청산가(해석해) — cfd 에선 스탑아웃 가격(마진 레벨 stopOut 도달점). 포지션 없으면/spot 이면 null
ctx.uPnl(선물) 미실현손익(quote) — spot 이면 null
ctx.leverage(선물) 현재 레버리지 — spot 이면 null
ctx.funding(선물) 직전 적용 펀딩 rate — 아직 없으면/spot 이면 null
ctx.marginRatio(선물 P2) cross 유지마진율 = 유지마진÷계정평가(1 이상 = 청산권). cross 포지션 없으면/spot 이면 null
ctx.marginMode(선물 P2) 현재 종목 마진 모드 'isolated'|'cross' — spot 이면 null
ctx.setMarginMode(sym, 'isolated'|'cross')(선물 P2) 마진 모드 전환 — 그 심볼 포지션이 없을 때만(거래소 동일). 반환: 적용된 모드 | null. 기본 isolated
ctx.cash주문 가능 현금
ctx.state호출 간 유지되는 내 변수 저장소 — ★ 실행 전체 공유(다종목이면 전 종목이 같은 객체). 종목별 값은 ctx.symState 에
ctx.fees수수료율 { maker, taker }

지표

ctx.sma(n, ago=0)단순이동평균. 데이터 부족이면 null
ctx.ema(n, ago=0)지수이동평균. 부족이면 null
ctx.rsi(n, ago=0)RSI 0~100. 부족이면 null
ctx.high(n, ago=0)최근 n봉 최고 종가
ctx.low(n, ago=0)최근 n봉 최저 종가
ctx.change(n, ago=0)n봉 전 대비 변화율 — 소수(0.05 = +5%. markets()의 ch* 퍼센트 단위와 다름!)
ctx.bb(n=20, k=2, ago=0)볼린저 밴드 → {upper, mid, lower, width} | null. width=(upper-lower)/mid (스퀴즈 판정용, mid=0이면 null)
ctx.macd(fast=12, slow=26, sig=9, ago=0)MACD → {macd, signal, hist} | null. 구간이 모자라면 signal·hist 만 null(macd 는 준다)
ctx.atr(n=14, ago=0)ATR(평균 진폭) → number | null. 변동성 기반 손절폭·포지션 크기에 쓴다. 구간이 모자라거나 라이브에서 고가·저가를 못 얻으면 null
ctx.stoch(n=14, d=3, ago=0)스토캐스틱 → {k, d} | null. %K=(종가−최저)/(최고−최저)×100, %D=%K의 SMA(d). 구간이 완전 횡보면 k=50(중립). d 구간이 모자라면 d만 null
ctx.wma(n, ago=0)가중이동평균 → number|null
ctx.stdev(n, ago=0)종가 표준편차(모집단) → number|null
ctx.zscore(n, ago=0)(종가−SMA)/표준편차 → number|null. 횡보면 0
ctx.cci(n=20, ago=0)CCI, TP=(고+저+종)/3 → number|null
ctx.willr(n=14, ago=0)윌리엄스 %R(0~−100) → number|null. 횡보면 −50
ctx.adx(n=14, ago=0){adx, plusDI, minusDI}|null. ★구간 합 정의(Wilder 평활 아님 — 타 차트와 값 다를 수 있음). 2n봉 필요
ctx.mfi(n=14, ago=0)MFI 0~100 → number|null. 거래량 필요 · 형성 중 봉은 null → ago=1 권장
ctx.vwap(n=20, ago=0)최근 n봉 이동 VWAP(세션 VWAP 아님) → number|null. 형성 중 봉 null → ago=1
ctx.obv(n=20, ago=0)최근 n봉 Σ부호×거래량(★누적 OBV 아님) → number|null. 형성 중 봉 null → ago=1
ctx.donchian(n=20, ago=0){upper, mid, lower}|null — 고가·저가 기준(ctx.high/low 는 종가 기준)
ctx.keltner(n=20, mult=2, atrN=10, ago=0){upper, mid, lower}|null. mid=EMA(n), ±mult×ATR(atrN)
ctx.ichimoku(t=9, k=26, s=52, ago=0){tenkan, kijun, spanA, spanB, cloudA, cloudB}|null. span=지금 계산값(차트에선 k봉 앞), cloud=지금 봉 위 구름(k봉 전 값). 후행스팬 없음
ctx.emaW(n, ago=0)표준 EMA(차트 도구 정의) — 20n 봉 필요, 모자라면 null. ctx.ema(n봉 창)와 값이 다르다
ctx.rsiW(n=14, ago=0)Wilder RSI — 20n 봉 필요 · 무변동 50
ctx.atrW(n=14, ago=0)Wilder ATR — 20n 봉 필요
ctx.adxW(n=14, ago=0)Wilder ADX {adx,plusDI,minusDI} — 20n 봉 필요

오더북

ctx.book()호가 전체 { bids, asks } (좋은 가격 순)
ctx.bid()최우선 매수호가
ctx.ask()최우선 매도호가
ctx.mid()중간가
ctx.spread()호가 차이(절대값)
ctx.spreadPct()호가 차이(중간가 대비 %)
ctx.fillPrice(side, qty)시장가 예상 체결 { avgPx, filled }. filled<qty면 유동성 부족
ctx.depth(side, px)px 까지 쌓인 누적 물량

주문

ctx.openOrders()대기 중인 지정가 목록(읽기 전용, 행에 ex — 작업주문은 workOrders 로)
ctx.workOrders()활성 작업주문(스마트 — 체이싱·분할·트리거 대기) 목록. 백테스트는 트리거 대기분만(체이싱은 봉 해상도에 개념 없음)

스캐너(여러 종목)

ctx.sym이번 호출의 종목(예: "BTC")
ctx.ex이번 틱 종목이 속한 거래소(예: 'upbit'). legs 백테스트·라이브에선 실값, 호출부가 거래소를 안 알려준 단일 백테스트는 null
ctx.quote이번 틱 다리의 표시통화(예: 'KRW', 'USDT'). 다리별 금액 계산(예: KRW 다리 6,000원 vs USDT 다리 6달러)의 분기 재료. 모르면 null — 지어내지 않는다
ctx.wallets()거래소(다리)별 지갑 맵 { upbit: { quote, cash, reserved? }, … } — 단일 실행도 한 다리짜리 맵(거래소를 모르면 'default' 키). 통화가 달라 합산값은 없다 — 환산이 필요하면 환율을 ctx.http·ctx.data 로 직접 가져온다
ctx.marketType시장 종류 'spot'|'usdm'|'coinm'|'cfd' — spot 이 아니면 position 이 부호 수량(+롱/−숏), liqPx·uPnl·leverage·funding 필드가 채워진다(entryPx 는 spot 에서도 평균 매수 단가). forex 는 기본 cfd(펀딩 없음·liqPx=스탑아웃가)
ctx.syms시세를 받은 종목 이름 목록
ctx.markets()전 종목 요약 [{ sym, ex, price, ch1, ch5, ch60 }] — ch 는 퍼센트(2 = +2%. change()의 소수 단위와 다름!). 봉이 모자라면 null. 단일 종목 실행에선 그 한 종목만 담긴 배열(빈 배열 아님). ex 는 legs 면 그 다리 거래소, 모르면 null
ctx.scan스캔 상태 { hit, active, maxActive, via?, candidates? } — hit = { names:[조건], by:{ 이름:{ barTs, closeAt } }, ex? } | null(마감 뒤 holdBars 봉). 활성 종목(적중·보유·주문)에만 불린다. via(legs)·candidates(경합 봉)는 SCAN 절. SCAN 없으면 null
ctx.market(sym)한 종목 요약 (없으면 null)
ctx.pos(sym, ex?)그 종목 보유 수량(이번 틱 다리 기준). 2번째 인자로 다른 다리(거래소) 지정 조회 — 모르는 다리는 null(0 은 "없음"을 단정하는 거짓)
ctx.positions종목별 보유 수량 스냅샷 { SYM: qty } — 이미 든 종목을 피할 때
ctx.symState현재 종목 전용 저장소(자동 분리) — 종목별 손절가·보유 플래그는 여기에. state.__sym[sym] 과 같은 객체

거래량

ctx.vol현재 봉 거래량 (모르면 null)
ctx.volumes봉별 거래량 배열 (모르면 null)
ctx.volPrev직전 **확정** 봉의 거래량 (봉 중에도 값이 있다 · 모르면 null). 1.86.0 미만 라이브에서는 항상 null
ctx.avgVol(n)최근 n개 **확정** 봉의 평균 거래량 (형성 중 봉은 제외 · 봉이 모자라거나 모르면 null). ★1.86.0 미만 라이브은 형성 중 봉을 평균에 넣었다 — 값이 낮게 나오고 봉 안에서 답이 바뀐다

멀티에셋

ctx.ref(slot)보조 종목 현재가. 슬롯명은 논리명(예: 'hedge')
ctx.refs사용 가능한 슬롯명 목록

파생 심리(OI·청산)

ctx.binanceOi()(라이브·페이퍼) 현재 종목 최신 미결제약정 { ts, oi, oiUsd } — 바이낸스 USDT-M 5분 집계. 백테스트·수집 전·모르면 null
ctx.binanceLiqs(n)(라이브·페이퍼) 최근 n분(기본 5·최대 60) 강제청산 합계 { longUsd, shortUsd, cnt } — longUsd=롱 청산(하방 압력의 해소). 백테스트·모르면 null. ★행이 없는 분은 "청산 0" 과 "수집 공백" 을 구분할 수 없음

외부 신호(웹훅)

ctx.signal(slot)외부 신호(TradingView 웹훅 등) 조회 { payload, at, age } — payload 는 보낸 JSON 그대로(파싱·해석 안 함), at 은 신호 시각(epoch ms), age 는 경과 초(내림). 백테스트는 현재 봉 시각 이하의 가장 최근 신호(as-of — 미래 신호는 안 보인다), 라이브·페이퍼는 60초 캐시 스냅샷. 신호가 아직 없거나 모르는 slot 이면 null(0·빈객체로 지어내지 않음 — 반드시 null 검사)
ctx.signals(slot, { since, limit })봉 사이 신호 **전부** [{ payload, at, age }] 오름차순 — since 초과~현재 봉 이하(없으면 직전 봉부터). limit 기본 50·상한 500(넘으면 truncated·dropped — 라이브에서 보관분보다 과거면 dropped null). 모르는 slot null · 0건 []. 뉴스·공시처럼 여러 건 오는 원천용(ctx.signal 은 최신 하나)
ctx.signalSeries(slot, { field, agg, interval, halfLifeH, label, sym })LLM 신호 슬롯의 칸 하나를 **이 전략 봉 축 배열**로(마지막 = 지금 봉). agg: count·mean·max·min·sum·decay(halfLifeH 기본 6)·labelCount. 귀속 = 신호 at · 닫힌 봉만 · 모르는 slot null. 빈 봉: count·sum 0 · mean·max·min null · decay 감쇠. 정밀도보다 짧은 봉은 null+경고(topic data)
ctx.data(key)외부 데이터 as-of 값. 이름 '<출처>.<이름>': base.<x> = 에이전트 기본 수집(선언 불필요, 예: 'base.vix') · <DB 별칭>.<이름> = 코드의 const DATA 에 적은 SQL(예: 'main.fg'). 옛 이름(base_vix · 계정 데이터셋 키 fg)도 같은 값 + legacy-key 경고. ★const DATA·점 이름은 에이전트 1.436.0 이상 — 그 아래 에이전트는 DATA 잡을 집지 않는다(제출이 AGENT_TOO_OLD·AGENT_NONE_ELIGIBLE 로 이유를 말한다 — 업데이트 안내). 현재 봉 시각 이하 최신 행(look-ahead 금지), 라이브·페이퍼는 주입된 최신값. 값 그대로. null = 모른다 — 이유는 결과 datasets[] 의 why·warn·src·rows(null≠0). ★행의 ts 기준 — DB 에 들어온 시각은 안 본다(dataAxis:'ts' + [PIT] 고지). 반드시 null 검사
ctx.dataKeys주입된 데이터의 정본 이름 목록(예: ['base.vix','main.fg'] — 옛 이름으로 불러도 정본. 빈 배열 = 외부 데이터 없음)

로그

ctx.log(...)디버그. 라이브는 분당 20건 서버 전송 제한 — 초과분은 VPS 로컬 파일(runstate/run-<id>.log)에 전량 보존. 백테스트는 500건 상한
ctx.logOnce(...)디버그 1회 — ctx.log 와 같지만 **같은 문장은 실행당 한 번만** 찍는다(서식 문장 200자가 같으면 같은 문장). 시작 설정·"데이터 부족" 같은 매 틱 반복 안내용. 숫자가 바뀌는 문장은 다른 문장이다(가격을 넣으면 매번 찍힌다). 라이브·페이퍼는 에이전트 재시작·코드 재배포 뒤 다시 한 번 찍는다. 기억 상한(라이브 200·백테스트 1000문장)을 넘은 새 문장은 1회 보장 없이 ctx.log 처럼 찍힌다. 라이브에서 1회 문장은 분당 20건 제한을 받지 않는다(상한이 있어서)
ctx.warn(...)중요. 라이브에서 스로틀 없이 항상 서버 전송 + 직전 로그 맥락 동봉 — 촘촘한 검증·이상 신호는 log 대신 이걸 쓰세요
ctx.watch(list)체결 조건 선언 — [{side:'buy'|'sell', price?:number, trigger?:'above'|'below', at?:epochSec, qty?:number, note?:string, sym?:string}] 를 매 틱 선언하면 run 카드가 '[선언] 매수 61,200 (−1.8%)' 로, 실거래·페이퍼 차트가 그 가격에 **점선**과 도달 추정 범위('약 5m~20m', 최근 14봉 ATR 기준 — 시각 예측 아님)로 그린다. 가격 없는 선언(at·이벤트)은 카드에만. 다종목(syms) 전략은 **종목 틱마다** 그 종목 것만 선언한다 — 종목별로 따로 보관되고(sym 생략 = 이번 틱 종목), 그 틱에 안 부르면 그 종목 선언만 지워진다(에이전트 1.243.0+, 이전엔 마지막 종목 것만 남았다). 틱마다 덮어씀(누적 아님), 최대 8개, note 60자. ★ trigger = 가격이 어느 쪽에서 닿아야 하나 — 'below'(내려와 닿으면: 저점 매수·손절/트레일 스탑 매도) / 'above'(올라와 닿으면: 익절 매도·돌파 매수). 주면 카드가 '조건 충족'을 판정하고 스탑이면 '스탑 매도'로 표시한다. 안 주면 거리만 보이고 충족 판정은 안 한다(방향을 지어내지 않는다, 에이전트 1.273.0+). ★ 가격 외 조건이 더 있으면 conds:[{label, now, op:'<'|'<='|'>'|'>=', ref, closed?}, {label, ok:true|false}] (최대 4) — **판단에 쓴 그 값**을 싣는다(같은 기간·같은 봉). 충족은 플랫폼이 now·op·ref 로 판정하고, 하나라도 거짓이면 카드가 '조건 대기'로 그리고 도달 추정을 안 보인다(에이전트 1.278.0+, 이전엔 조건만 빠진다). closed:true = 마감 봉 값(다음 마감까지 안 바뀜). 백테스트는 무시, 라이브·페이퍼만 보고. 반환값 없음
ctx.http.send(req)HTTP — const HTTP={origins:[...],creds:{slot:origin}} 선언 + run 승인 필요. send({id,url}|{id,cred,path,method,json}) 큐잉 → 다음 평가 ctx.http.result(id): null·{ok:false,code}·{ok:true,status,json|text}. 비밀은 보관소(코드가 못 봄). 백테스트는 안 보냄(code:'backtest')
ctx.alert(msg)조건 근접 알림 — 전략이 "진입 조건에 근접했다"고 스스로 판정해 유저에게 알림을 보낸다. msg 는 문자열로 강제(200자 컷)·반환값 없음. 라이브·페이퍼는 run 당 60초 쿨다운으로 발송(60초 내 재호출은 무시 — 로그에 남음), 백테스트는 발송 없이 로그에 [알림] 으로만 기록(상한 50개, 초과는 개수만 alertsDropped)

Mga key ng order object

side'buy' | 'sell'
qty수량(코인). 현금이 아니다
type'limit'=지정가, 'smart'=작업 주문(시간에 걸쳐 집행 — 체이싱·분할·트리거). 기본은 시장가. smart 는 라이브·페이퍼에서 집행되고 백테스트는 시장가로 근사
price지정가일 때의 가격
postOnlytrue 면 즉시 체결될 지정가는 거부(메이커만)
chasesmart: 호가 추격 { escalateMs?=20000, deadlineMs?=45000, bandTicks?, minRepegMs? } — 호가 조인(메이커)→1틱 전진→마감 시 크로스(테이커). 기본 켜짐, false 면 끔
slicessmart: 분할 { n, everyMs } — 수량을 n조각으로 everyMs 간격 집행
triggersmart: 조건 대기 { type:'stop'|'trail', px?, offset? } — 조건이 닿기 전엔 주문이 안 나간다. 실데이터 백테스트도 봉 h/l 로 발동 판정(2026-08, 체결 라벨 stop/trail)
hardStopMssmart: 이 시간이 지나면 무조건 종료(남은 수량 포기)
reduceOnly정리 표시 — 줄이기만(포지션보다 크면 0 에서 잘림, 포지션이 없으면 거절). 닫는 주문엔 늘 붙인다
cancel'all' 이면 대기 주문 전체 취소
book지정가 사다리: [{ key, side, qty, price | at:'top', postOnly?(기본 true), reduceOnly? }] — 매 틱 같은 목록을 내면 걸린 것과 맞춘다(한도 안이면 호출 0). 상세는 llms 토픽 orders
tol사다리 허용 한도 { minPct?=0.05, ratio?=0.2 } — max(minPct, 거리% × ratio)
refreshMs사다리 주기 갱신 — 한도 안이어도 이만큼 지나고 1틱 이상 다르면 고친다(실거래는 유량 여유 있을 때만)