해외선물 HTS 개발하며 정리한 계약 단위·틱가치·증거금 처리 구조

베일드 솔루션·2026년 9월 25일


해외선물 HTS를 만들기 전에는 "코인 거래소 엔진에 종목만 추가하면 되겠지"라고 생각했습니다. 막상 붙여 보니 수량 단위, 가격이 움직일 때의 가치, 거래 시간이 전부 달랐습니다. 이 글은 해외선물·CFD 거래 화면을 개발하면서 정리한 계약 단위, 틱가치, 증거금, 세션 시간 처리 구조입니다.

코인 거래소와 뭐가 다른가

코인 거래소에서 수량은 보통 "0.015 BTC"처럼 소수로 표현하고, 손익은 가격 차이 × 수량으로 끝납니다. 해외선물은 세 가지가 다릅니다.

  • 수량 단위가 계약입니다. 1계약, 2계약처럼 정수로 거래합니다.
  • 가격이 움직이는 최소 단위(틱)와 그 한 칸의 가치(틱가치)가 종목마다 정해져 있습니다.
  • 거래 시간(세션)이 있고, 매일 짧은 휴장 시간이 있습니다.

그래서 손익 계산을 "가격 차이 × 수량"으로 두면 종목마다 틀린 값이 나옵니다.

종목 명세부터 데이터로 만든다

가장 먼저 한 일은 종목마다 다른 규칙을 코드에 흩어 두지 않고 종목 명세 테이블 하나에 모은 것입니다.

type ContractSpec = {
  symbol: string        // ES, NQ, GC, CL ...
  tickSize: number      // 가격 최소 변동 단위
  tickValue: number     // 1틱 움직일 때 1계약 손익 (USD)
  multiplier: number    // 포인트당 가치 = tickValue / tickSize
  currency: 'USD'
  session: string       // 거래 세션 ID (거래소 시간대 기준)
  marginPerContract: number   // 계약당 필요 증거금 (운영사 설정)
  maintenanceRatio: number    // 유지 증거금 비율
  maxLeverage: number
}

대표 종목의 명세는 이렇습니다.

종목틱 크기틱가치포인트당 가치
E-mini S&P 500 (ES)0.25$12.50$50
E-mini 나스닥 100 (NQ)0.25$5.00$20
금 (GC)0.10$10.00$100
WTI 원유 (CL)0.01$10.00$1,000

같은 "10포인트 상승"이라도 ES 1계약은 $500, NQ 1계약은 $200입니다. 이 차이를 명세 테이블이 전부 흡수하게 만들면, 새 종목을 추가할 때 코드를 고칠 필요가 없습니다.

손익은 틱 단위 정수로 계산한다

손익 공식 자체는 단순합니다.

function pnl(spec: ContractSpec, side: 1 | -1, entry: number, exit: number, contracts: number) {
  const ticks = Math.round((exit - entry) / spec.tickSize)  // 몇 틱 움직였나
  return side * ticks * spec.tickValue * contracts
}

핵심은 Math.round로 틱 수를 정수로 만든 뒤 곱한다는 점입니다. (4521.75 - 4500.25) * 50처럼 소수끼리 바로 곱하면 부동소수점 오차가 누적돼서 원장 합계가 몇 센트씩 어긋납니다. 금액은 센트 단위 정수로 저장하고, 가격은 틱 수로 바꿔서 계산하는 규칙을 처음부터 두는 것이 좋습니다.

주문 가격은 틱 격자에 맞춘다

사용자가 입력한 지정가가 틱 격자에 안 맞으면(예: 틱 크기 0.25인데 4500.30) 거래소 규칙상 존재할 수 없는 가격입니다. 서버에서 반드시 정렬해야 합니다.

function alignPrice(spec: ContractSpec, price: number, side: 1 | -1) {
  const t = price / spec.tickSize
  // 매수는 내림, 매도는 올림: 사용자에게 불리하지 않은 쪽으로
  const ticks = side === 1 ? Math.floor(t + 1e-9) : Math.ceil(t - 1e-9)
  return ticks * spec.tickSize
}

클라이언트 입력창에서도 틱 단위로만 올라가고 내려가게 막아 두면 문의가 크게 줄어듭니다.

증거금과 강제청산

해외선물은 계약 금액 전체가 아니라 증거금만 걸고 거래합니다.

  • 필요 증거금 = 계약당 증거금 × 계약 수
  • 평가 자산 = 잔고 + 미실현 손익
  • 평가 자산이 필요 증거금 × 유지 비율 아래로 내려가면 강제청산

강제청산 가격은 미리 계산해서 화면에 보여주는 것이 좋습니다. 사용자가 가장 자주 묻는 질문이 "어디서 청산되나요?"이기 때문입니다.

function liquidationPrice(spec: ContractSpec, side: 1 | -1, entry: number, contracts: number, equity: number) {
  const maintenance = spec.marginPerContract * contracts * spec.maintenanceRatio
  const lossRoom = equity - maintenance                      // 버틸 수 있는 손실액
  const ticks = Math.floor(lossRoom / (spec.tickValue * contracts))
  return entry - side * ticks * spec.tickSize
}

증거금 값은 거래소나 브로커 기준이 수시로 바뀌기 때문에 코드에 박지 않고 관리자 화면에서 종목별로 수정할 수 있게 했습니다.

거래 시간은 거래소 시간대로 저장한다

미국 선물 대부분은 일요일 저녁부터 금요일 오후까지 거의 24시간 열리고, 매일 미국 동부 시간 기준 오후 5시부터 1시간 휴장합니다. 한국 시간으로 바꾸면 서머타임 기간에는 오전 6시~7시, 아닐 때는 오전 7시~8시입니다.

여기서 흔한 실수가 휴장 시간을 한국 시간으로 하드코딩하는 것입니다. 서머타임이 바뀌는 날 한 시간씩 어긋나서, 휴장 중에 주문이 들어가거나 열려 있는데 막히는 일이 생깁니다. 세션은 거래소 시간대(America/New_York, America/Chicago)로 저장하고, 화면에 보여줄 때만 한국 시간으로 변환해야 합니다.

정리

  • 종목마다 다른 규칙은 명세 테이블 하나로 모은다
  • 손익은 틱 수 정수 × 틱가치로 계산하고, 금액은 센트 정수로 저장한다
  • 주문 가격은 서버에서 틱 격자에 정렬한다
  • 증거금·유지 비율은 관리자에서 수정 가능하게 두고, 청산 가격은 미리 보여준다
  • 세션은 거래소 시간대로 저장하고 표시할 때만 변환한다

코인 거래소 경험이 있어도 이 다섯 가지는 처음부터 다시 설계하는 편이 빨랐습니다. 다음 글에서는 실시간 시세를 받아 호가와 체결을 만드는 구조를 정리해 보겠습니다.

profile
거래소 및 AI 자동화 시스템 개발 노트

0개의 댓글