TypeScript SDK
@spectre-trade/terminal 패키지의 클라이언트, 스트림, 터미널 탐색, 오류.
설치
npm install @spectre-trade/terminalTerminal
실행 중인 터미널 하나의 클라이언트입니다.
토큰으로 생성하거나, Terminal.discover가 포트와 토큰을 찾고
API 버전을 확인하도록 합니다. 모든 메서드는 async이며,
2xx가 아닌 응답에는 ApiError를, 응답이 없으면
TerminalUnreachableError를 던집니다.
| 필드 | 타입 | 설명 |
|---|---|---|
host | string | |
port | number | |
timeoutMs | number | |
baseUrl | string | http://127.0.0.1:48180/v1/ — 토큰은 여기에 포함되지 않습니다. |
Terminal()
new Terminal(options: TerminalOptions)discover()
static discover(options: DiscoverOptions = {}): Promise<Terminal>터미널을 찾아 확인합니다: 토큰은 options.token 또는
SPECTRE_LOCAL_API_TOKEN에서(없으면 TokenMissingError), 포트는
options.port 또는 instance.json에서(없으면 48180; 파일에 API가 꺼져
있다고 적혀 있으면 LocalApiDisabledError) 가져온 뒤, GET /v1/meta를 한 번
호출합니다. api_version이 "1"이 아니면 IncompatibleTerminalError이고,
unauthorized 응답은 토큰이 오래되었거나 API가 꺼졌을 수 있다는 메시지를
담은 ApiError입니다.
| 옵션 | 타입 | 설명 |
|---|---|---|
token선택 | string | SPECTRE_LOCAL_API_TOKEN보다 우선합니다. |
port선택 | number | instance.json보다 우선합니다. |
host선택 | string | |
timeoutMs선택 | number | |
instancePath선택 | string | OS별 instance.json 경로 대신 이 파일을 읽습니다(테스트용). |
meta()
meta(): Promise<Meta>GET /v1/meta: API 버전, 빌드, 토큰 권한, 제한, 스트림 토픽입니다.
HTTP 작업: GET /v1/meta
health()
health(): Promise<Health>GET /v1/health.
HTTP 작업: GET /v1/health
connections()
connections(): Promise<List<Connection>>GET /v1/connections (connections:read).
HTTP 작업: GET /v1/connections
connection()
connection(id: number): Promise<Connection>GET /v1/connections/{id} (connections:read); 알 수 없는 id는 not_found입니다.
HTTP 작업: GET /v1/connections/{id}
instruments()
instruments(options: InstrumentsOptions): Promise<InstrumentsPage>GET /v1/instruments (market:read): 거래소와 마켓의 카탈로그 한 페이지이며,
connectionId 또는 exchange + market으로 지정합니다. next_cursor를
직접 따라가거나 iterInstruments를 사용합니다.
HTTP 작업: GET /v1/instruments
| 옵션 | 타입 | 설명 |
|---|---|---|
connectionId선택 | number | |
exchange선택 | string | |
market선택 | string | |
q선택 | string | 심볼 또는 기준 코인의 부분 문자열이며, 대소문자를 구분하지 않습니다. |
limit선택 | number | 1–2000, 기본값은 500입니다. |
cursor선택 | string | 이전 페이지의 next_cursor입니다. |
iterInstruments()
iterInstruments(options: InstrumentsOptions): AsyncGenerator<Instrument, void, undefined>옵션에 맞는 모든 종목을 페이지 단위로 차례로 반환합니다.
| 옵션 | 타입 | 설명 |
|---|---|---|
connectionId선택 | number | |
exchange선택 | string | |
market선택 | string | |
q선택 | string | 심볼 또는 기준 코인의 부분 문자열이며, 대소문자를 구분하지 않습니다. |
limit선택 | number | 1–2000, 기본값은 500입니다. |
cursor선택 | string | 이전 페이지의 next_cursor입니다. |
intervals()
intervals(): Promise<Intervals>GET /v1/market/intervals (market:read): 캔들과 클러스터의 인터벌입니다.
HTTP 작업: GET /v1/market/intervals
candles()
candles(instrument: InstrumentAddress, options: CandlesOptions): Promise<Candles>GET /v1/market/candles (market:read): 캔들 히스토리이며, 오래된 것부터 나열됩니다.
HTTP 작업: GET /v1/market/candles
| 옵션 | 타입 | 설명 |
|---|---|---|
interval | string | /v1/market/intervals의 코드입니다: 1m, 5m, … |
limit선택 | number | 최대 2000, 기본값은 500입니다. |
end선택 | number | UTC 밀리초로 나타낸 오른쪽 경계이며, 기본값은 현재 시각입니다. |
connectionId선택 | number |
trades()
trades(instrument: InstrumentAddress, options: TradesOptions = {}): Promise<Trades>GET /v1/market/trades (market:read): 페어의 최근 체결이며, 오래된 것부터
최신 순입니다. 구독이 없는 페어는 요청 시 열리므로 호출에 최대 3초가
걸릴 수 있습니다.
HTTP 작업: GET /v1/market/trades
| 옵션 | 타입 | 설명 |
|---|---|---|
limit선택 | number | 최근 1–1000건, 기본값은 100입니다. |
sinceTs선택 | number | 이 수신 시각 이후만 해당하며, UTC 밀리초입니다. |
connectionId선택 | number |
snapshot()
snapshot(instrument: InstrumentAddress, options: ConnectionOptions = {}): Promise<Snapshot>GET /v1/market/snapshot (market:read): 최우선 매수/매도 호가, 마지막 체결, 마크 가격, 티커입니다.
HTTP 작업: GET /v1/market/snapshot
| 옵션 | 타입 | 설명 |
|---|---|---|
connectionId선택 | number |
orderbook()
orderbook(instrument: InstrumentAddress, options: OrderbookOptions = {}): Promise<Orderbook>GET /v1/market/orderbook (market:read); 3초 안에 전체 호가창이 없으면 unavailable입니다.
HTTP 작업: GET /v1/market/orderbook
| 옵션 | 타입 | 설명 |
|---|---|---|
depth선택 | number | 1–200, 기본값은 50입니다. |
connectionId선택 | number |
clusters()
clusters(instrument: InstrumentAddress, options: ConnectionOptions = {}): Promise<Clusters>GET /v1/market/clusters (market:read): 터미널에서 선택된 인터벌의 버킷
최대 10개입니다. 창에 열려 있지 않은 종목에 대한 첫 호출은 not_active(409)로
응답할 수 있으며, 몇 초 뒤 다시 시도하면 클러스터를 찾습니다.
HTTP 작업: GET /v1/market/clusters
| 옵션 | 타입 | 설명 |
|---|---|---|
connectionId선택 | number |
accountOrders()
accountOrders(connectionId: number): Promise<AccountOrders>GET /v1/account/orders (account:read): 한 연결의 미체결 주문과 트리거
주문이며, 터미널 창에 보이는 것과 같은 상태입니다. 알 수 없는 연결은
not_found입니다.
HTTP 작업: GET /v1/account/orders
accountPositions()
accountPositions(connectionId: number): Promise<AccountPositions>GET /v1/account/positions (account:read): 한 연결의 열린 포지션입니다.
HTTP 작업: GET /v1/account/positions
accountBalances()
accountBalances(connectionId: number): Promise<AccountBalances>GET /v1/account/balances (account:read): 통화별로, API를 켠 뒤 거래소가
보낸 최신 값입니다. 그 뒤로 변동이 없는 통화는 포함되지 않습니다.
HTTP 작업: GET /v1/account/balances
accountFills()
accountFills(connectionId: number, options: AccountFillsOptions = {}): Promise<AccountFills>GET /v1/account/fills (account:read): API를 켠 뒤 연결의 체결 내역
(최근 1000건)이며, 오래된 것부터 최신 순입니다.
HTTP 작업: GET /v1/account/fills
| 옵션 | 타입 | 설명 |
|---|---|---|
limit선택 | number | 최근 1–1000건, 기본값은 100입니다. |
sinceTs선택 | number | 이 거래소 시각 이후만 해당하며, UTC 밀리초입니다. |
placeOrder()
placeOrder(options: PlaceOrderOptions): Promise<Accepted>POST /v1/orders (trading:write): 창이 쓰는 것과 같은 명령으로 지정가 또는
시장가 주문을 넣습니다. 응답은 거래소가 아니라 터미널이 접수했다는 뜻입니다:
스트림의 account.orders나 accountOrders에서 key로 주문을 지켜봅니다.
비활성화되었거나 읽기 전용이거나 자격 증명이 없는 연결은 not_tradable(409),
다른 거래소나 마켓의 종목이거나 수량 또는 가격이 거부되면
invalid_request(400), limits.commands_per_second를 넘으면
rate_limited(429)입니다.
HTTP 작업: POST /v1/orders
| 옵션 | 타입 | 설명 |
|---|---|---|
connectionId | number | 거래 연결이며, 기본값은 없습니다. |
instrument | InstrumentAddress | |
side | Side | |
type | OrderType | limit에는 price가 필요하고, market은 받지 않습니다. |
price선택 | Decimal | |
size선택 | Decimal | 기준 코인 단위의 수량이며, size와 notional 중 정확히 하나만 전달합니다. |
notional선택 | Decimal | 호가 통화 금액이며, size와 notional 중 정확히 하나만 전달합니다. |
reduceOnly선택 | boolean | 기본값은 false입니다. |
postOnly선택 | boolean | 기본값은 false입니다. |
timeInForce선택 | TimeInForce | 없으면 거래소의 기본값입니다. |
clientKey선택 | string | 멱등성 키입니다: 같은 키로 반복하면 이전 응답을 반환하고 주문을 중복해서 넣지 않습니다(터미널은 최근 1000개를 기억합니다). |
cancelOrder()
cancelOrder(options: CancelOrderOptions): Promise<Accepted>DELETE /v1/orders/{id} (trading:write): 터미널 키(t-…) 또는 거래소의
id로 주문 하나를 취소합니다. 취소는 거래소로 직접 전달되며, 확인은
스트림으로 도착합니다.
HTTP 작업: DELETE /v1/orders/{id}
| 옵션 | 타입 | 설명 |
|---|---|---|
connectionId | number | |
instrument | InstrumentAddress | |
orderId | string | 터미널 키(t-…) 또는 거래소의 주문 id입니다. |
cancelAll()
cancelAll(options: InstrumentCommandOptions): Promise<Accepted>POST /v1/orders/cancel-all (trading:write): 스페이스 바를 누른 것처럼 종목의 모든 주문을 취소합니다.
HTTP 작업: POST /v1/orders/cancel-all
| 옵션 | 타입 | 설명 |
|---|---|---|
connectionId | number | |
instrument | InstrumentAddress |
closePosition()
closePosition(options: InstrumentCommandOptions): Promise<Accepted>POST /v1/positions/close (trading:write): 창이 하는 방식 그대로 종목의
포지션을 청산합니다 — 선물에서는 포지션 크기만큼의 시장가 주문, 현물에서는
"모두 취소하고 청산"을 하나의 원자적 명령으로 처리합니다. 열린 포지션이
없으면 invalid_request입니다.
HTTP 작업: POST /v1/positions/close
| 옵션 | 타입 | 설명 |
|---|---|---|
connectionId | number | |
instrument | InstrumentAddress |
placeTrigger()
placeTrigger(options: PlaceTriggerOptions): Promise<Accepted>POST /v1/orders/trigger (trading:write): 창이 쓰는 것과 같은 서비스로
넣는 스톱로스, 테이크프로핏 또는 스톱입니다. stop_loss와 take_profit은
열린 포지션에 걸립니다(방향, 수량, 규칙을 포지션에서 가져오며, 포지션이
없으면 invalid_request). stop에는 side와 notional이 필요합니다.
응답의 key는 트리거의 키(sl_…, tp_…, stop_…)이며,
accountOrders에서 트리거의 client_id로 나타납니다. 취소와 수정에는
그곳의 숫자 id를 사용합니다. 터미널에 트리거 주문이 없는 거래소에서는
unsupported(501)입니다.
HTTP 작업: POST /v1/orders/trigger
| 옵션 | 타입 | 설명 |
|---|---|---|
connectionId | number | |
instrument | InstrumentAddress | |
kind | TriggerType | stop_loss와 take_profit은 열린 포지션에 걸리며, stop은 자체 주문입니다. |
triggerPrice | Decimal | |
side선택 | Side | stop 전용입니다. |
notional선택 | Decimal | stop 전용입니다: 호가 통화 금액입니다. |
rule선택 | TriggerRule | stop 전용입니다. 없으면 매수는 시장 가격 위, 매도는 시장 가격 아래를 뜻합니다. |
clientKey선택 | string | 멱등성 키이며, Terminal.placeOrder에서와 같습니다. |
cancelTrigger()
cancelTrigger(options: CancelTriggerOptions): Promise<Accepted>DELETE /v1/orders/trigger/{id} (trading:write): 숫자 id로 트리거 하나를 취소합니다.
HTTP 작업: DELETE /v1/orders/trigger/{id}
| 옵션 | 타입 | 설명 |
|---|---|---|
connectionId | number | |
instrument | InstrumentAddress | |
triggerId | string | number | accountOrders().triggers에 있는 트리거의 숫자 id입니다. |
cancelAllTriggers()
cancelAllTriggers(options: InstrumentCommandOptions): Promise<Accepted>POST /v1/orders/trigger/cancel-all (trading:write): 종목의 모든 트리거를 취소합니다. 일반 주문은 그대로 남습니다.
HTTP 작업: POST /v1/orders/trigger/cancel-all
| 옵션 | 타입 | 설명 |
|---|---|---|
connectionId | number | |
instrument | InstrumentAddress |
amendTrigger()
amendTrigger(options: AmendTriggerOptions): Promise<Accepted>PATCH /v1/orders/trigger/{id} (trading:write): 트리거를 새 가격으로
옮깁니다 — 창과 같은 순차 교체로, 취소한 뒤 다시 넣습니다. kind는
필수이며, stop에는 side도 필요합니다. key는 null입니다.
HTTP 작업: PATCH /v1/orders/trigger/{id}
| 옵션 | 타입 | 설명 |
|---|---|---|
connectionId | number | |
instrument | InstrumentAddress | |
triggerId | string | number | accountOrders().triggers에 있는 트리거의 숫자 id입니다. |
kind | TriggerType | |
triggerPrice | Decimal | |
side선택 | Side | stop 전용입니다. |
rule선택 | TriggerRule | stop 전용입니다. |
leverage()
leverage(options: InstrumentCommandOptions): Promise<Leverage>GET /v1/account/leverage (account:read): 선물 종목의 레버리지와 마진
모드이며, 터미널의 캐시에서 가져옵니다. 터미널이 아직 다루지 않은 종목은
not_active(409)입니다: 창에서 종목을 열거나 setLeverage를 호출하면
캐시에 들어갑니다.
HTTP 작업: GET /v1/account/leverage
| 옵션 | 타입 | 설명 |
|---|---|---|
connectionId | number | |
instrument | InstrumentAddress |
setLeverage()
setLeverage(options: SetLeverageOptions): Promise<Accepted>POST /v1/account/leverage (trading:write): 창이 하는 방식 그대로
레버리지를 설정합니다 — leverage(1 이상의 정수)와 mode(max, smart)
중 정확히 하나만 전달합니다. 적용된 값은 거래소가 응답한 뒤 leverage에
나타납니다.
HTTP 작업: POST /v1/account/leverage
| 옵션 | 타입 | 설명 |
|---|---|---|
connectionId | number | |
instrument | InstrumentAddress | |
leverage선택 | number | 1 이상의 정수입니다. |
mode선택 | "max" | "smart" |
setMarginMode()
setMarginMode(options: SetMarginModeOptions): Promise<Accepted>POST /v1/account/margin-mode (trading:write): cross 또는 isolated입니다.
포지션이 열려 있는 동안의 변경은 창에서와 같이 거래소가 거부하며,
accepted 응답에는 아직 이것이 나타나지 않습니다.
HTTP 작업: POST /v1/account/margin-mode
| 옵션 | 타입 | 설명 |
|---|---|---|
connectionId | number | |
instrument | InstrumentAddress | |
marginMode | MarginMode |
workspace()
workspace(): Promise<Workspace>GET /v1/workspace (workspace:read): 모든 창과 그 탭, 스택, 차트이며,
창이 구조가 바뀔 때마다 게시하는 스냅샷에서 가져옵니다. id는 터미널의
이번 실행에서만 쓰이는 내부 번호입니다.
HTTP 작업: GET /v1/workspace
workspaceWindow()
workspaceWindow(options: WindowOptions): Promise<WorkspaceWindow>GET /v1/workspace/windows/{id} (workspace:read): 창 하나이며, 알 수 없는 id는 not_found입니다.
HTTP 작업: GET /v1/workspace/windows/{id}
| 옵션 | 타입 | 설명 |
|---|---|---|
windowId | number |
openStack()
openStack(options: OpenStackOptions): Promise<WorkspaceOutcome>POST /v1/workspace/stacks (workspace:write): 종목의 스택을 새 탭 또는
새 창에 열며, chart가 설정되어 있으면 그 위에 차트를 함께 엽니다. 응답은
창, 탭, 새 위젯을 알려 줍니다. 터미널의 카탈로그에 없는 종목은
invalid_request입니다.
HTTP 작업: POST /v1/workspace/stacks
| 옵션 | 타입 | 설명 |
|---|---|---|
instrument | InstrumentAddress | |
connectionId선택 | number | 생략하면 공통 규칙에 따라 선택됩니다. |
chart선택 | boolean | 스택 위에 여는 같은 종목의 차트입니다. |
target선택 | OpenTarget | new_tab(기본값) 또는 new_window입니다. |
windowId선택 | number | 새 탭을 열 창이며, 생략하면 포커스된 창입니다. |
activate선택 | boolean | false는 탭을 백그라운드에서 엽니다. 새 창은 항상 포커스를 가져갑니다. |
openTab()
openTab(options: OpenTabOptions): Promise<WorkspaceOutcome>POST /v1/workspace/tabs (workspace:write): 항목마다 열 하나 — 스택과,
chart가 있으면 그 위의 차트 — 를 배치한 탭을 새 탭 또는 새 창에 엽니다.
name을 생략하면 유일한 종목, 또는 첫 종목에 개수를 붙인 이름이 됩니다.
거부 시에는 해당 항목(items[1]: …)을 알려 주며, 창에는 아무것도 전달되지
않습니다.
HTTP 작업: POST /v1/workspace/tabs
| 옵션 | 타입 | 설명 |
|---|---|---|
items | Array<TabItemOptions | InstrumentAddress> | 순서대로 나열한 열이며, 1개에서 50개까지입니다. 주소만 쓰면 차트 없는 스택입니다. |
name선택 | string | 최대 64자이며, 생략하면 터미널이 종목 이름을 따라 탭 이름을 짓습니다. |
target선택 | OpenTarget | new_tab(기본값) 또는 new_window입니다. |
windowId선택 | number | 새 탭을 열 창이며, 생략하면 포커스된 창입니다. |
activate선택 | boolean | false는 탭을 백그라운드에서 엽니다. 새 창은 항상 포커스를 가져갑니다. |
addStacks()
addStacks(options: AddStacksOptions): Promise<WorkspaceOutcome>POST /v1/workspace/windows/{id}/tabs/{tab}/stacks (workspace:write):
같은 열을 탭의 마지막 열 뒤에 추가합니다. 탭의 이름과 열 너비는 그대로
유지됩니다. 응답에는 추가된 위젯만 나열됩니다.
HTTP 작업: POST /v1/workspace/windows/{id}/tabs/{tab}/stacks
| 옵션 | 타입 | 설명 |
|---|---|---|
windowId | number | |
tabId | number | |
items | Array<TabItemOptions | InstrumentAddress> | 순서대로 나열한 열이며, 1개에서 50개까지입니다. 주소만 쓰면 차트 없는 스택입니다. |
openMultiview()
openMultiview(options: OpenMultiviewOptions): Promise<WorkspaceOutcome>POST /v1/workspace/multiview (workspace:write): «거래소 배치»의 모든
거래소에 코인을 열며, 창의 멀티뷰 버튼과 같은 계획을 따릅니다. 코인에
해당하는 셀이 없으면 not_found입니다.
HTTP 작업: POST /v1/workspace/multiview
| 옵션 | 타입 | 설명 |
|---|---|---|
base | string | 코인(BTC) 또는 페어(BTC/USDT)입니다. |
target선택 | OpenTarget | 생략하면 멀티뷰의 «열기» 설정이 결정합니다. |
windowId선택 | number | |
activate선택 | boolean |
setStackInstrument()
setStackInstrument(options: StackInstrumentOptions): Promise<WorkspaceOutcome>PATCH /v1/workspace/stacks/{widget_id} (workspace:write): 스택과 그 차트의 종목을 다른 종목으로 바꿉니다.
HTTP 작업: PATCH /v1/workspace/stacks/{widget_id}
| 옵션 | 타입 | 설명 |
|---|---|---|
widgetId | number | |
instrument | InstrumentAddress | |
connectionId선택 | number |
closeStack()
closeStack(options: StackOptions): Promise<WorkspaceOutcome>DELETE /v1/workspace/stacks/{widget_id} (workspace:write): 스택의 닫기 버튼처럼 스택 하나를 닫습니다.
HTTP 작업: DELETE /v1/workspace/stacks/{widget_id}
| 옵션 | 타입 | 설명 |
|---|---|---|
widgetId | number |
closeTab()
closeTab(options: TabOptions): Promise<WorkspaceOutcome>DELETE /v1/workspace/windows/{id}/tabs/{tab} (workspace:write): 창의 유일한 탭은 invalid_request입니다.
HTTP 작업: DELETE /v1/workspace/windows/{id}/tabs/{tab}
| 옵션 | 타입 | 설명 |
|---|---|---|
windowId | number | |
tabId | number |
activateTab()
activateTab(options: TabOptions): Promise<WorkspaceOutcome>POST /v1/workspace/windows/{id}/tabs/{tab}/activate (workspace:write): 탭을 선택합니다. 창의 포커스는 바뀌지 않습니다.
HTTP 작업: POST /v1/workspace/windows/{id}/tabs/{tab}/activate
| 옵션 | 타입 | 설명 |
|---|---|---|
windowId | number | |
tabId | number |
closeWindow()
closeWindow(options: WindowOptions): Promise<WorkspaceOutcome>DELETE /v1/workspace/windows/{id} (workspace:write): 메인 창은 invalid_request입니다.
HTTP 작업: DELETE /v1/workspace/windows/{id}
| 옵션 | 타입 | 설명 |
|---|---|---|
windowId | number |
activateWindow()
activateWindow(options: WindowOptions): Promise<WorkspaceOutcome>POST /v1/workspace/windows/{id}/activate (workspace:write): 최소화되어 있으면 복원하고 맨 앞으로 가져옵니다.
HTTP 작업: POST /v1/workspace/windows/{id}/activate
| 옵션 | 타입 | 설명 |
|---|---|---|
windowId | number |
settings()
settings(options: SettingsOptions = {}): Promise<Settings>GET /v1/settings (settings:read): 저장된 그대로의 행이며, 기본값 단계는
채우지 않습니다. 옵션이 없으면 연결이 없는 전역 행입니다. 각 행은
updateSettings로 쓸 수 있는지를 writable로 알려 줍니다.
HTTP 작업: GET /v1/settings
| 옵션 | 타입 | 설명 |
|---|---|---|
scope선택 | "global" | "order_book" | global(기본값) 또는 order_book입니다. |
category선택 | string | |
key선택 | string | |
connectionId선택 | number | 연결의 행이며, 없으면 연결이 없는 행입니다. |
instrument선택 | InstrumentAddress | 페어의 행입니다(scope order_book). |
orderbookSettings()
orderbookSettings(options: InstrumentCommandOptions): Promise<OrderbookSettings>GET /v1/settings/orderbook (settings:read): 연결의 스택이 보는 그대로의
종목 스택 설정 — 페어의 행, 연결의 기본값, 연결이 없는 페어의 행, 전역
기본값 — 이며, 모든 키에 resolved_from이 붙습니다.
HTTP 작업: GET /v1/settings/orderbook
| 옵션 | 타입 | 설명 |
|---|---|---|
connectionId | number | |
instrument | InstrumentAddress |
updateSettings()
updateSettings(changes: SettingChange[]): Promise<SettingsApplied>PATCH /v1/settings (settings:write): Settings App이 하는 방식 그대로
설정을 씁니다. 모든 변경은 먼저 터미널의 카탈로그와 대조됩니다(changes[i]를
알려 주는 invalid_request, 아무것도 쓰지 않음; 레버리지는 여기서
unsupported이므로 setLeverage를 사용합니다). 그 뒤 순서대로 적용됩니다:
Settings App과 같은 행, 창 이벤트, 서비스 캐시입니다. 처음 실패한 변경이
호출을 거부하며, 그 앞의 변경은 적용된 채로 남습니다.
HTTP 작업: PATCH /v1/settings
stream()
stream(options: StreamOptions = {}): Promise<Stream>/v1/stream을 엽니다. Node 전용입니다: 토큰은 핸드셰이크 헤더로 보냅니다.
| 옵션 | 타입 | 설명 |
|---|---|---|
timeoutMs선택 | number | 핸드셰이크 타임아웃이며, 기본값은 클라이언트의 timeoutMs입니다. |
StreamProtocol
스트림 프로토콜에서 소켓과 무관한 절반입니다: 요청 id를 발급하고, 응답을
기다리는 요청을 보관하며, subscribed / unsubscribed / error 프레임으로
요청을 완료하고, 그 밖의 모든 것을 소비자가 순회하는 프레임으로 바꿉니다.
순수하므로 서버 없이 단위 테스트할 수 있습니다.
nextRequestId()
nextRequestId(): string이 스트림 안에서 고유한 새 request_id입니다.
expect()
expect(requestId: string): Promise<ReplyFrame>요청을 등록하고, 그 응답으로 완료될 프로미스를 받습니다.
pendingCount
pendingCount: number아직 응답을 기다리는 요청입니다.
route()
route(raw: unknown): StreamFrame | undefined디코딩된 프레임을 분류합니다. 우리 요청에 대한 응답은 해당 프로미스를
완료하고 undefined를 반환합니다. event, gap, 구독 오류는 소비자에게
반환됩니다. 알 수 없거나 형식이 잘못된 것은 버려집니다.
abortAll()
abortAll(error: Error): void기다리는 모든 요청을 거부합니다. 예를 들어 소켓이 닫힐 때입니다.
Stream
class Stream extends AsyncIterable<StreamFrame>살아 있는 /v1/stream 연결입니다.
terminal.stream()으로 얻습니다. subscribe로 구독한 뒤 순회합니다:
for await (const frame of stream)은 이벤트, gap, 구독 오류를 도착 순서대로
내보내며, 소비자가 바쁜 동안 제한 없이 버퍼링합니다(서버는 스스로를
보호합니다: 5초 넘게 뒤처진 클라이언트는 4008로 닫힙니다). 한 번에 한
소비자만 순회하며, 동시에 호출된 두 번째 next()는 자기 차례를 기다립니다.
서버가 연결을 닫으면 순회가 끝납니다. 1001, 4008, 4029와 끊어진
연결(1006)의 경우 StreamClosedError를 던지며 끝납니다. closeCode와
closeReason은 어느 경우든 설정됩니다. 클라이언트 쪽에서 닫으면(close,
또는 루프에서 break) 순회가 정상적으로 끝납니다.
connect()
static connect(options: StreamConnectOptions): Promise<Stream>WebSocket을 열고 핸드셰이크를 완료합니다. 터미널이 받아 주지 않으면
TerminalUnreachableError로 거부됩니다: Node의 WebSocket은 거부된
핸드셰이크(터미널 꺼짐, API 꺼짐, 토큰 거부)를 하나의 일반 실패로
보고하므로 세 경우를 구분할 수 없습니다.
| 옵션 | 타입 | 설명 |
|---|---|---|
url | string | ws://127.0.0.1:48180/v1/stream. |
token | string | |
timeoutMs선택 | number | 핸드셰이크 타임아웃이며, 기본값은 10 000입니다. |
url
url: string스트림 URL입니다(토큰은 절대 포함되지 않습니다).
closed
closed: boolean소켓이 닫히면 누가 닫았든 true입니다.
closeCode
closeCode: number | undefined닫힌 뒤의 WebSocket 종료 코드입니다.
closeReason
closeReason: string | undefined닫힌 뒤의 WebSocket 종료 사유입니다.
subscribe()
subscribe(options: SubscribeOptions): Promise<Subscribed>종목의 토픽 또는 연결의 계정 토픽을 구독합니다. 서버의 subscribed
프레임(요청한 topic과, 시장 토픽의 경우 instrument를 더한 것)으로
완료되고, 서버의 error 응답에는 StreamRequestError로 거부됩니다. 그동안
다른 구독의 이벤트는 계속 흐릅니다. 초당 신규 거래소 구독 수에 대한
터미널의 제한(/v1/meta)에 유의합니다. 계정 토픽은 거래소를 건드리지
않으며 스냅샷 프레임을 보내지 않으므로, 현재 상태는 먼저 /v1/account/*
작업으로 읽습니다.
| 옵션 | 타입 | 설명 |
|---|---|---|
topic | Topic | |
instrument선택 | InstrumentAddress | 시장 토픽에는 필수이며, account.*에서는 무시됩니다. |
connectionId선택 | number | account.*에는 필수이며, 시장 토픽에서는 생략하면 공통 규칙에 따라 선택됩니다. |
params선택 | BookTopParams | book.top만 읽습니다. 와이어 이름(depth, interval_ms)을 씁니다. |
unsubscribe()
unsubscribe(subscription: Subscribed | SubscribedFrame | string): Promise<void>구독을 즉시 제거합니다. subscribe가 완료하며 반환한 값 또는 sub id만
받습니다. 서버의 unsubscribed로 완료됩니다.
close()
close(code: number = CLOSE_NORMAL, reason?: string): Promise<void>스트림을 닫습니다. 서버는 클라이언트의 코드로 종료 핸드셰이크를 완료하며, 프로미스는 그때 완료되거나, 서버가 끝내 완료하지 않으면 짧은 유예 시간 뒤에 완료됩니다. 여러 번 호출해도 안전합니다.
터미널 탐색
| 상수 | 값 | 설명 |
|---|---|---|
DEFAULT_PORT | 48180 | SPECTRE_LOCAL_API_PORT로 바꾸지 않는 한 터미널이 수신하는 포트입니다. |
TOKEN_ENV | "SPECTRE_LOCAL_API_TOKEN" | 토큰을 읽어 오는 환경 변수입니다. |
instancePath()
function instancePath(options: DiscoveryEnvironment = {}): stringOS별 터미널 instance.json의 경로입니다:
- Linux:
$XDG_DATA_HOME/spectre-terminal/instance.json, 없으면~/.local/share/spectre-terminal/instance.json; - macOS:
~/Library/Application Support/spectre-terminal/instance.json; - Windows:
%LOCALAPPDATA%\spectre-terminal\instance.json, 없으면%USERPROFILE%\AppData\Local\....
터미널 자체의 데이터 디렉터리(dirs::data_local_dir)와 같습니다.
readPort()
function readPort(options: ReadPortOptions = {}): Promise<number | undefined>instance.json에 게시된 Local API 포트입니다.
파일이 없거나 읽을 수 없으면(실행 중인 터미널이 없거나, 이 필드를 게시하지
않는 터미널) undefined로 완료되며, 호출자는 DEFAULT_PORT로 대체합니다.
파일은 있지만 local_api_port가 null이거나 없으면
LocalApiDisabledError를 던집니다 — 터미널은 실행 중이고 API는 꺼져 있는
것입니다. 이 필드 하나만 읽습니다.
tokenFromEnv()
function tokenFromEnv(env: Record<string, string | undefined> = process.env): string | undefinedSPECTRE_LOCAL_API_TOKEN의 토큰이며, 설정되지 않았거나 비어 있으면 undefined입니다.
오류
SpectreTerminalError
class SpectreTerminalError extends Error이 패키지가 던지는 모든 오류의 기본 클래스입니다.
ApiError
class ApiError extends SpectreTerminalError터미널이 2xx가 아닌 상태로 응답했습니다.
code는 계약의 오류 엔벨로프에 있는 안정적인 코드(unauthorized,
not_found, rate_limited, …)이고, status는 HTTP 상태입니다. 엔벨로프가
아닌 2xx 외 본문은 코드 "unknown"이 됩니다.
| 필드 | 타입 | 설명 |
|---|---|---|
code | string | 계약의 안정적인 오류 코드 또는 "unknown"입니다. |
status | number | 응답의 HTTP 상태입니다. |
detail | string | 서버가 보낸, 접두사 없는 사람이 읽기 위한 메시지입니다. |
TerminalUnreachableError
class TerminalUnreachableError extends SpectreTerminalErrorHTTP 응답이 전혀 없습니다: 연결 거부, 요청 타임아웃, 예상치 못한
리디렉션, 또는 완료되지 않은 WebSocket 핸드셰이크입니다. 원래의 실패는
cause에 있습니다.
LocalApiDisabledError
class LocalApiDisabledError extends SpectreTerminalErrorinstance.json은 있지만 Local API 포트가 게시되어 있지 않습니다: 터미널이
API를 끈 채 실행 중이거나, 정상 종료되지 않은 터미널이 파일을 남긴 것입니다.
어느 경우든 Settings App(일반 → Local API)에서 API를 켤 때까지 연결할 포트가
없습니다.
TokenMissingError
class TokenMissingError extends SpectreTerminalErrortoken 옵션도 SPECTRE_LOCAL_API_TOKEN도 토큰을 제공하지 않았습니다.
IncompatibleTerminalError
class IncompatibleTerminalError extends SpectreTerminalError/v1/meta가 이 클라이언트가 사용하지 않는 API 버전을 보고했습니다.
| 필드 | 타입 | 설명 |
|---|---|---|
apiVersion | string | 터미널이 보고한 api_version입니다. |
appVersion | string | 터미널이 보고한 app_version입니다. |
StreamRequestError
class StreamRequestError extends SpectreTerminalError스트림이 subscribe 또는 unsubscribe 요청에 error 프레임으로
응답했습니다. code는 계약의 오류 코드(not_found, rate_limited,
invalid_request, …)입니다. 다른 구독은 계속 흐릅니다.
| 필드 | 타입 | 설명 |
|---|---|---|
code | string | 계약의 안정적인 오류 코드입니다. |
detail | string | 서버가 보낸, 접두사 없는 사람이 읽기 위한 메시지입니다. |
StreamClosedError
class StreamClosedError extends SpectreTerminalError소비자가 대응해야 하는 이유로 서버가 스트림을 닫았습니다: 1001 — API가
꺼졌거나 토큰이 재발급됨; 4008 — 이 클라이언트가 너무 느리게 읽음; 4029
— 토큰당 연결 수 제한. 연결이 close 프레임 없이 끊어진 1006과, 이미 닫힌
스트림에서의 subscribe / unsubscribe에서도 발생합니다.
| 필드 | 타입 | 설명 |
|---|---|---|
code | number | WebSocket 종료 코드입니다. |
reason | string | 서버가 보낸 종료 사유이며, 비어 있을 수 있습니다. |
@spectre-trade/terminal 코드의 커밋 2d1c324에서 생성되었습니다.