Strategy API (ctx)
กลยุทธ์คือฟังก์ชัน JavaScript เดียว backtest และ live ใช้อินเทอร์เฟซเดียวกันทุกประการ
function onUpdate(ctx) {
// ถูกเรียกทุกแท่ง (backtest) / ทุกการอัปเดต (live)
// คืน null, ออบเจกต์คำสั่งเดียว หรืออาเรย์ของคำสั่ง
}
สร้างอะไรได้บ้าง
- ตลาด: Upbit·Binance สปอต, Binance USDT-M·COIN-M ฟิวเจอร์ส — เลเวอเรจ ลอง/ชอร์ต มาร์จิ้นแยก/รวม
- สแกนเนอร์หลายเหรียญ: กลยุทธ์เดียวดูหลายเหรียญ (สูงสุด 200) บนตลาดเดียวกัน — สถานะ·คำสั่ง·ตัวแปรแยกรายเหรียญอัตโนมัติ
- สั่งซื้อขายสองตลาดจากกลยุทธ์เดียว (legs — arbitrage/hedge ข้ามตลาด): รองรับ backtest (รวม job)·paper และคำสั่งจริง (LIVE) กระเป๋า·ค่าธรรมเนียม·kill-switch แยกและเชื่อมโยงทุกขา — live ต้องมี API key ครบทุกขาก่อนเริ่ม (กัน hedge ที่รันแค่ครึ่งเดียว ไม่มีการลดระดับเงียบ ๆ) ไม่รวมยอดกำไรขาดทุนข้ามสกุลเงิน (แสดง USD โดยประมาณเพื่ออ้างอิงเท่านั้น)
- อ้างอิงเหรียญเสริม: ใช้ราคาของเหรียญ/ตลาดอื่นเป็นสัญญาณ (สล็อต
ctx.ref — เช่น ดู BTC บน Binance ขณะเทรดบน Upbit) ใช้ได้ทั้งแบ็กเทสต์ จำลอง และเทรดจริง - หลายตลาดพร้อมกัน: เปิดการรันแยกต่อตลาดก็ได้ — เอเจนต์เดียวถือคีย์หลายตลาดและรันขนานทั้งหมด (กลยุทธ์ A บน Upbit + B บน Binance ฟิวเจอร์ส)
- คลัสเตอร์หลายเอเจนต์: กระจายเหรียญของสแกนเนอร์ให้หลายเอเจนต์รันขนานอัตโนมัติ (แผน Elite + ผ่านการ onboarding)
- ข้อมูล: แท่งเทียน (1m–1d)·ปริมาณ·orderbook·funding·open interest/การบังคับปิด + ตัวชี้วัดพื้นฐาน (ดอกเบี้ยพันธบัตรสหรัฐ·ดอลลาร์·VIX·น้ำมัน… — เอเจนต์เก็บจาก FRED อ่านด้วย
ctx.data('key')) + อัตราแลกเปลี่ยนจริง (USD/KRW — ctx.fx) + สัญญาณภายนอก (webhook) ค่าที่ไม่รู้คือ null — ไม่ปลอมเป็น 0 - คำสั่ง: market·limit·ทยอยเข้า, smart order เฉพาะ live (ไล่ราคา/ทริกเกอร์)
- ความปลอดภัย: ลิมิตคำสั่ง (guardrail), kill-switch ขาดทุนรวมค่าธรรมเนียม, dry-run
- การตรวจสอบ: backtest สูงสุด 100k แท่ง, walk-forward ด้วยวันสิ้นสุดคงที่ + เวลาตัดสินใจต่อ tick ในสตูดิโอ และมอนิเตอร์ tick จริง (ดูบนการ์ดการรันว่าเซิร์ฟเวอร์ตามทันไหม)
ยังไม่รองรับ (เราไม่แสร้งว่าทำได้):
- เทรดจริงแบบแยกขา legs ให้หลายเอเจนต์ — การแยกรัน (เอเจนต์คำนวณ + เอเจนต์สั่งต่อขา) ใช้ได้เฉพาะเปเปอร์ คำสั่งจริงจะเปิดหลังวัดผลบนสองเครื่อง ตอนนี้ legs เทรดจริงใช้เอเจนต์เดียวที่ถือคีย์ทุกขา
- จัดสรรคลัสเตอร์ใหม่อัตโนมัติ — การกระจายเป็นแบบคงที่ ถ้าเอเจนต์หลุด เหรียญของมันจะรอและ UI บอกตรง ๆ (ไม่ย้ายเงียบ ๆ)
- การตรวจสอบกับบัญชีจริงของการเทรดหุ้น (KIS) — deploy KRX เปเปอร์ และคำสั่งจริง (KIS order TR) เชื่อมแล้ว แต่ยังไม่เคยตรวจ TR คำสั่งและฟิลด์ตอบกลับกับบัญชีจริง ถ้าผิด รหัสข้อผิดพลาดของโบรกเกอร์ (msg_cd) จะแสดงตามจริง เริ่มด้วยจำนวนเล็กน้อย
กติกา
- ฟังก์ชันบริสุทธิ์ — ห้ามเครือข่าย ห้าม import ห้าม async ใช้ได้เฉพาะ ctx ด้านล่าง
qty คือจำนวนเหรียญเสมอ (ไม่ใช่เงินสด)- ตัวชี้วัดคืน
null เมื่อข้อมูลไม่พอ — ตรวจ null เสมอ - เก็บตัวแปรใน
ctx.state จะคงอยู่ข้ามการเรียก - สล็อต
ctx.ref ใช้ชื่อเชิงตรรกะ ('hedge') — สัญลักษณ์จริงผูกตอน deploy - อย่าเขียนโค้ดตรวจว่ามีฟีเจอร์ ctx หรือไม่ (
typeof ctx.watch === 'function' ฯลฯ) แพลตฟอร์มตรวจตอนดีพลอยว่าเอเจนต์รองรับฟีเจอร์ ctx ที่กลยุทธ์ใช้ ถ้าไม่รองรับจะบอกเวอร์ชันที่ต้องใช้และไม่ดีพลอย ctx.watch แค่แสดงราคาที่รออยู่บนกราฟและการ์ดรัน — ไม่ใช่คำสั่ง คำสั่งมีแค่ค่าที่ onUpdate คืนเท่านั้น- ถ้าการเข้าต้องมีเงื่อนไขอื่นนอกจากราคา (RSI ฯลฯ) ให้ใส่เป็น
conds ด้วยค่าเดียวกับที่ใช้ตัดสิน (ช่วงเดียวกัน แท่งเดียวกัน): conds: [{ label: 'RSI(14) close', now: rsi, op: '<', ref: 30, closed: true }] ถ้ามีข้อใดเป็นเท็จ การ์ดรันจะแสดง "รอเงื่อนไข" และซ่อนเวลาประมาณ
ออบเจกต์คำสั่ง
- ตลาด:
{ side: 'buy'|'sell', qty } — ไล่ orderbook (slippage, partial fill, ค่าธรรมเนียม taker) - ลิมิต:
{ side, qty, type: 'limit', price } — เต็มที่ราคาของคุณด้วยค่าธรรมเนียม maker; postOnly: true ปฏิเสธลิมิตที่จะเต็มทันที - สมาร์ต (work order):
{ side, qty, type: 'smart', chase?, slices?, trigger?, hardStopMs? } — ดำเนินการตามเวลา: chasing (ตาม orderbook → cross เมื่อครบกำหนด), แบ่งย่อย, trigger แบบ stop/trail และเวลาจำกัด ใช้ได้ทั้ง live และ paper backtest จะประมาณเป็นคำสั่งตลาด — ตรวจสอบด้วย paper run - บันไดลิมิต:
{ book: [{ key, side, qty, price | at: 'top' }], tol?, refreshMs? } — ส่งรายการลิมิตที่ ควร วางไว้ทุกติ๊ก ระบบจะเทียบกับคำสั่งที่วางอยู่: อยู่ในช่วงยอมรับก็ไม่ส่งอะไร นอกช่วงจะแก้ไข key ที่หายไปจะถูกยกเลิก ช่วงยอมรับ = max(0.05%, ระยะห่างจากราคา × 0.2) — ใกล้ราคาแคบ ไกลกว้าง จัดการใกล้ก่อน ค่าเริ่มต้นเป็นเมกเกอร์เท่านั้น (postOnly) at: 'top' = bid สูงสุด + 1 ติ๊ก refreshMs แก้ราคาตามรอบแม้อยู่ในช่วง (เทรดจริง: เฉพาะเมื่อโควตาเหลือ) key ที่เต็มแล้วจะไม่วางซ้ำจนกว่าจะเอาออกหนึ่งครั้ง แบ็กเทสต์ไม่มีขนาดติ๊ก จึงเข้าคิวที่ราคาดีที่สุด (แสดงในผลลัพธ์) - ยกเลิกทั้งหมด:
{ cancel: 'all' } — ยกเลิกลิมิตที่ค้างและ work order ทั้งหมด (chasing หยุดด้วยการยกเลิกคำสั่งเดียวไม่ได้)
ส่งออกภายนอก (ctx.http) — แจ้งเตือนและ API ภายนอก
กลยุทธ์เรียกแจ้งเตือน Telegram/Discord/Slack หรือ API ภายนอกได้ (เอเจนต์ 1.348.0 ขึ้นไป) ห้ามใส่คีย์ในโค้ด — เก็บไว้ในคลัง HTTPของเอเจนต์ โค้ดอ้างแค่ชื่อช่อง
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;
}
- ส่งได้เฉพาะ origin ที่ประกาศใน
HTTP และแต่ละ run ต้องอนุมัติการจับคู่ช่อง ↔ รายการในคลังเองบนหน้าเอเจนต์ (อนุมัติใหม่เมื่อประกาศหรือโค้ดเปลี่ยน) - ตอนเพิ่มคีย์ คลังแสดงโดเมนตัวใหญ่ โดเมนที่คล้ายกัน (
te1egram.org…) หรือไม่คุ้นเคยต้องพิมพ์โดเมนซ้ำจึงบันทึกได้ คีย์แนบเฉพาะ origin ของมันและถูกลบออกจากคำตอบ - 5 ครั้งต่อ tick · คำตอบ 64KB · ไม่ตาม redirect · บล็อก IP ส่วนตัว ผลลัพธ์ได้ใน tick ถัดไปด้วย
ctx.http.result(id) - แบ็กเทสต์ไม่ส่งจริง — นับไว้ใน
httpNotSent เท่านั้น - สตริงที่หน้าตาเหมือนคีย์ในโค้ด: เตือนตอนบันทึก, AI เห็นเป็นตัวแทน, และกลยุทธ์นั้นถูกข้ามในการเผยแพร่ git สาธารณะ
ตารางด้านล่างคือ ctx ทั้งหมด (สร้างจากแหล่งเดียวกับ autocomplete) ต้องการโค้ดจาก AI ใช้สร้างกลยุทธ์ด้วย AI หรือวางเอกสาร AIทั้งฉบับ
ctx
ตารางนี้สร้างจากแหล่งเดียวกับ autocomplete และเอกสาร AI (/llms.txt) — ฟังก์ชันที่ไม่อยู่ในนี้ไม่มีอยู่จริง
상태
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.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.fx 로 직접
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: { names:[조건 이름…], by:{ 이름:{ barTs, closeAt } } } | null, active, maxActive } — hit 은 받아들여진 적중(걸린 봉 **마감 뒤** holdBars 봉 동안, 조건별로 센다). onUpdate 는 활성 종목(적중·보유·주문 중)에만 불린다. 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사용 가능한 슬롯명 목록
ctx.fx(quote)1 USD 당 해당 통화 값(예: ctx.fx('KRW')). USD 환산은 직접
ctx.fxQuotes사용 가능한 통화 목록
파생 심리(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.data(key)외부 데이터셋 as-of 값(예: ctx.data('fg')) — **기본 지표**도 같은 이름으로 읽는다(예: ctx.data('base_ust10y')·ctx.data('base_vix')·ctx.data('base_dxy_h10') — 에이전트가 FRED 에서 모으는 일별 값, 선언 불필요. 목록은 에이전트가 보고한 것만, AI 는 list_datasets 의 baseIndicators. 시각은 **알 수 있게 된 때**(관측일 + 발표 지연 lagDays, 받은 시각이 더 이르면 그 시각)라 미래를 안 본다). 값을 그대로 돌려준다(봉투 없음). null = 모른다 — 데이터셋 미등록·해당 시점 행 없음·조회 실패 전부 null 이고, 구별은 결과의 dataMeta 로. 백테스트는 현재 봉 시각 이하 최신 행(look-ahead 금지), 라이브·페이퍼는 러너가 주입한 최신값. ★행의 **시각(ts) 기준**이다 — 그 행이 DB 에 언제 들어왔는지는 안 본다(event-only, 결과 dataAxis:'ts' + [PIT] 고지). 늦게 적재되거나 나중에 고쳐지는 데이터면 백테스트가 낙관될 수 있다. 반드시 null 검사
ctx.dataKeys주입된 데이터셋 키 목록(빈 배열 = 이 실행에 외부 데이터 없음)
로그
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)
คีย์ของออบเจกต์คำสั่ง
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: 이 시간이 지나면 무조건 종료(남은 수량 포기)
cancel'all' 이면 대기 주문 전체 취소
book지정가 사다리: [{ key, side, qty, price | at:'top', postOnly?(기본 true), reduceOnly? }] — 매 틱 같은 목록을 내면 걸린 것과 맞춘다(한도 안이면 호출 0). 상세는 llms '## Limit ladder'
tol사다리 허용 한도 { minPct?=0.05, ratio?=0.2 } — max(minPct, 거리% × ratio)
refreshMs사다리 주기 갱신 — 한도 안이어도 이만큼 지나고 1틱 이상 다르면 고친다(실거래는 유량 여유 있을 때만)