AlgoLab Blog · KIS 국내 선물옵션 실무 · 2026

KIS API 선물옵션 주문 — TTTO1101U 함정 6가지

한국투자증권 · 국내 선물옵션 2026-08-30 · 약 8분 읽기 · 알고랩 AlgoLab
한 줄 요약 KIS API 국내 선물옵션 주문의 tr_idTTTO1101U(주간 실전)이고, 엔드포인트는 /uapi/domestic-futureoption/v1/trading/order입니다. 주식 주문 코드를 복사해 종목코드만 바꾸면 반드시 실패합니다. 계좌번호 뒤 두 자리 ACNT_PRDT_CD가 주식 계좌 값이 아니고, 종목코드는 PDNO가 아니라 SHTN_PDNO, 가격은 ORD_UNPR이 아니라 UNIT_PRICE이며, 주문 유형은 NMPR_TYPE_CD·ORD_DVSN_CD·KRX_NMPR_CNDT_CD 세 개를 함께 채워야 합니다.

한국투자증권 KIS API로 주식 자동매매를 만들어 본 사람이 코스피200 선물이나 옵션으로 넘어올 때 거의 같은 곳에서 막힙니다. "주식 주문이 잘 되니까 종목코드만 101W09로 바꾸면 되겠지"라고 생각하기 때문입니다. 그런데 KIS API에서 국내 선물옵션은 주식과 완전히 다른 API 묶음입니다. 경로도, 필드명도, 계좌 지정 방식도 겹치지 않습니다.

이 글은 한국투자증권 공식 저장소 koreainvestment/open-trading-apiexamples_user/domestic_futureoption/ 예제 코드를 기준으로, 실제로 사람이 걸려 넘어지는 지점 6개만 골라 정리한 것입니다. KIS API 앱키 발급과 첫 주문까지는 끝냈다고 가정합니다.

이 글에서 다루는 것

  1. 주식 API와 선물옵션 API는 무엇이 다른가
  2. 함정 1 — 계좌상품코드가 주식과 다르다
  3. 함정 2 — PDNO가 아니라 SHTN_PDNO
  4. 함정 3 — 주문 유형 필드가 하나가 아니라 셋이다
  5. 함정 4 — 야간 주문과 야간 정정취소의 접두어가 다르다
  6. 함정 5 — 모의투자에는 야간이 없다
  7. 함정 6 — 시세는 FO로 갈리고 output이 셋이다
  8. 주문부터 잔고까지 실행 코드
  9. 선물옵션 TR ID 전체 표 · 실시간 항목

주식 API와 선물옵션 API는 무엇이 다른가

가장 먼저 머릿속에서 지워야 할 것은 "같은 KIS API니까 비슷하겠지"라는 가정입니다. 아래 표가 두 묶음의 실제 차이입니다.

국내주식국내 선물옵션
주문 경로/uapi/domestic-stock/v1/trading/order-cash/uapi/domestic-futureoption/v1/trading/order
주문 tr_id매수·매도가 다른 TRTTTO1101U 하나 (매수·매도는 바디로 구분)
종목코드 필드PDNOSHTN_PDNO
가격 필드ORD_UNPRUNIT_PRICE
주문 유형ORD_DVSN 하나NMPR_TYPE_CD + ORD_DVSN_CD + KRX_NMPR_CNDT_CD
매수·매도TR ID로 구분SLL_BUY_DVSN_CD (01 매도 / 02 매수)
계좌 뒤 2자리주식 계좌 값선물옵션 계좌 값 (예제 설명은 03)

겹치는 게 CANO(종합계좌번호)와 ORD_QTY(주문수량) 정도입니다. 그래서 "복사 후 수정"이 아니라 처음부터 새로 쓰는 게 빠릅니다.

국내주식 order-cash 국내선물옵션 order CANO CANO 그대로 ACNT_PRDT_CD ACNT_PRDT_CD 값이 다름 PDNO SHTN_PDNO 이름이 다름 ORD_UNPR UNIT_PRICE ORD_DVSN NMPR_TYPE_CD ORD_DVSN_CD KRX_NMPR_CNDT_CD 1 → 3개 (TR ID로 매수·매도) SLL_BUY_DVSN_CD
주식 주문 바디를 그대로 복사하면 살아남는 필드는 CANO·ORD_QTY뿐이다

함정 1 — 계좌상품코드가 주식과 다르다

KIS API는 계좌를 8자리 + 2자리로 쪼개서 받습니다. 앞 8자리가 CANO, 뒤 2자리가 ACNT_PRDT_CD입니다. 자세한 구조는 KIS API 계좌번호 CANO와 ACNT_PRDT_CD에 정리해 두었습니다.

문제는 이 뒤 2자리가 상품 종류를 가리키는 코드라는 점입니다. 주식 계좌에서 쓰던 값을 그대로 선물옵션 주문에 넣으면 계좌를 찾지 못합니다. 한국투자증권 공식 예제의 선물옵션 잔고 조회 함수는 acnt_prdt_cd 설명에 예시로 03을 적고 있습니다.

증상이 헷갈립니다. 계좌 코드가 틀리면 "계좌가 없다"는 명확한 문구가 아니라 권한·조회 실패 계열 메시지로 돌아오는 경우가 있어서, 앱키나 토큰을 의심하며 몇 시간을 태우기 쉽습니다. 선물옵션 API가 처음부터 안 될 때는 토큰보다 ACNT_PRDT_CD를 먼저 의심하십시오. 메시지별 대응은 KIS API 에러코드 대응에 모아 두었습니다.

내 계좌의 실제 뒤 2자리는 추측하지 말고 확인해야 합니다. 선물옵션 계좌를 따로 개설하지 않았다면 API 이전에 계좌 개설과 선물옵션 거래 자격 요건부터 필요합니다. 이 부분은 증권사 정책이라 코드로 우회되지 않습니다.

함정 2 — PDNO가 아니라 SHTN_PDNO

선물옵션 주문 바디에서 종목코드가 들어가는 자리는 SHTN_PDNO(단축상품번호)입니다. 주식에서 쓰던 PDNO를 넣으면 그 필드는 그냥 무시되고 종목이 비어 있는 주문이 됩니다.

자릿수도 상품마다 다릅니다. 공식 예제의 파라미터 설명은 이렇게 적고 있습니다.

상품자릿수예시시세 FID_COND_MRKT_DIV_CODE
지수선물6자리101W09F
지수옵션9자리201S03370O

앞자리 101이 코스피200 지수선물 계열, 201이 지수옵션 계열입니다. 실시간 예제에서는 선물이 101S12, 옵션이 201S11305 같은 형태로 등장합니다.

월물은 만기마다 바뀝니다. 종목코드를 소스에 상수로 박아 두면 만기가 지난 뒤 봇이 에러 없이 아무 일도 안 하는 상태가 됩니다. 이게 가장 위험한 실패입니다. 종목코드 마스터 파일을 매일 내려받아 최근월물을 계산하는 구조로 만드십시오.

함정 3 — 주문 유형 필드가 하나가 아니라 셋이다

주식은 ORD_DVSN 하나에 지정가나 시장가 코드를 넣으면 끝났습니다. 선물옵션은 세 개를 함께 채워야 합니다. 공식 예제의 코드값은 다음과 같습니다.

필드
NMPR_TYPE_CD호가유형코드01 지정가 · 02 시장가 · 03 조건부 · 04 최유리
KRX_NMPR_CNDT_CD거래소 호가조건0 없음 · 3 IOC · 4 FOK
ORD_DVSN_CD주문구분코드01~04 기본 · 10 지정가IOC · 11 지정가FOK · 12 시장가IOC · 13 시장가FOK · 14 최유리IOC · 15 최유리FOK
ORD_PRCS_DVSN_CD주문처리구분02 주문전송

ORD_DVSN_CD10~15에서 IOC·FOK를 다시 표현한다는 점에 주의하십시오. 같은 조건을 두 필드에 쓰게 되어 있어서 둘이 어긋나면 거절되거나 의도와 다른 주문이 나갑니다. 안전한 습관은 지정가·시장가·최유리 중 하나를 고르고, IOC·FOK를 쓸 때만 KRX_NMPR_CNDT_CDORD_DVSN_CD를 짝으로 맞추는 것입니다.

그리고 시장가나 최유리 지정가일 때 UNIT_PRICE0으로 넣습니다. 공식 예제도 시장가나 최유리 지정가인 경우 0으로 입력하라고 적고 있습니다.

함정 4 — 야간 주문과 야간 정정취소의 접두어가 다르다

국내 선물옵션은 정규장 외에 야간 세션이 있습니다. KIS API는 이걸 별도 TR ID로 구분하는데, 여기서 사람들이 규칙을 잘못 외웁니다.

동작주간 실전야간 실전모의투자
주문TTTO1101USTTN1101UVTTO1101U
정정취소TTTO1103UTTTN1103UVTTO1103U
체결내역TTTO5201RSTTN5201RVTTO5201R
주문가능TTTO5105RSTTN5105RVTTO5105R
잔고CTFO6118RCTFN6118RVTFO6118R

야간 주문은 STTN인데 야간 정정취소는 TTTN입니다. "야간이면 앞을 S로 바꾸면 된다"는 규칙으로 코드를 짜면 정정취소만 조용히 실패합니다. 그리고 이 실패는 주문이 이미 나간 뒤에 드러나기 때문에 손실로 직결됩니다. TR ID는 규칙으로 생성하지 말고 매핑 표에 하드코딩하십시오.

정정취소 자체의 동작 원리는 주식과 같습니다. RVSE_CNCL_DVSN_CD01(정정) 또는 02(취소)를 넣고 ORGN_ODNO에 원주문번호를 넣습니다. 전량이면 RMN_QTY_YNY로, ORD_QTY0으로 둡니다. KIS API 주문 취소·정정에서 주식 쪽 흐름을 먼저 봐 두면 이해가 빠릅니다.

함정 5 — 모의투자에는 야간이 없다

한국투자증권 공식 예제 코드는 모의투자 환경일 때 주간만 허용하고 야간을 요청하면 오류를 냅니다. 즉 모의투자 선물옵션 주문 TR은 VTTO1101U 하나뿐이고 야간에 대응하는 모의 TR이 없습니다. 야간 잔고 CTFN6118R이나 야간 증거금 상세 CTFN7107R 같은 야간 전용 조회도 실전 계열입니다.

실무적으로 이게 의미하는 것: 야간 로직은 모의투자로 끝까지 검증할 수 없습니다. 모의에서 주간 경로를 다 검증한 다음, 야간은 최소 계약 수로 실계좌에서 한 번 통과시키는 단계를 반드시 넣으십시오. 모의에서 실전으로 넘어갈 때 무엇이 바뀌는지는 KIS API 모의투자에서 실전 전환에 정리해 두었습니다.

함정 6 — 시세는 FO로 갈리고 output이 셋이다

선물옵션 시세는 /uapi/domestic-futureoption/v1/quotations/inquire-price, tr_idFHMIF10000000입니다. 파라미터는 두 개뿐입니다.

params = {
    "FID_COND_MRKT_DIV_CODE": "F",   # F: 지수선물, O: 지수옵션
    "FID_INPUT_ISCD": "101W09",
}

주의할 점은 응답이 output 하나가 아니라 output1·output2·output3 세 덩어리로 온다는 것입니다. 주식 현재가 조회처럼 res["output"]으로 접근하면 KeyError가 납니다. 주식 쪽 KIS API 현재가 조회 함정과 응답 구조 자체가 다릅니다.

호가는 FHMIF10010000, 일봉은 FHKIF03020100, 분봉은 FHKIF03020200입니다. 시세 TR은 실전과 모의가 같은 값을 쓰지만 호출하는 도메인이 다르므로 base_url은 환경에 맞게 바꿔야 합니다.

주문부터 잔고까지 실행 코드

아래는 access_token이 이미 있다고 가정하고 시장가 1계약 매수 → 잔고 확인까지 가는 최소 코드입니다. 구조를 보여주기 위한 예시이며 그대로 실전에 쓰라는 뜻이 아닙니다.

import requests

BASE = "https://openapi.koreainvestment.com:9443"   # 실전
CANO, ACNT = "12345678", "03"    # 뒤 2자리는 반드시 본인 선물옵션 계좌 값

H = {
    "content-type": "application/json; charset=utf-8",
    "authorization": f"Bearer {ACCESS_TOKEN}",
    "appkey":  APP_KEY,
    "appsecret": APP_SECRET,
    "custtype": "P",
}

def order_futopt(code, qty, side="02"):
    """side 01=매도, 02=매수 / 시장가"""
    url = BASE + "/uapi/domestic-futureoption/v1/trading/order"
    body = {
        "ORD_PRCS_DVSN_CD": "02",     # 주문전송
        "CANO": CANO,
        "ACNT_PRDT_CD": ACNT,
        "SLL_BUY_DVSN_CD": side,
        "SHTN_PDNO": code,            # PDNO 아님
        "ORD_QTY": str(qty),
        "UNIT_PRICE": "0",            # 시장가는 0
        "NMPR_TYPE_CD": "02",         # 02 시장가
        "KRX_NMPR_CNDT_CD": "0",      # 0 없음
        "ORD_DVSN_CD": "02",          # 02 시장가
        "CTAC_TLNO": "",
        "FUOP_ITEM_DVSN_CD": "",
    }
    h = dict(H, tr_id="TTTO1101U")    # 주간 실전
    r = requests.post(url, headers=h, json=body, timeout=10).json()
    if r.get("rt_cd") != "0":
        raise RuntimeError(f'{r.get("msg_cd")} {r.get("msg1")}')
    return r["output"]["ODNO"]        # 주문번호

POST 바디의 키는 전부 대문자여야 합니다. 공식 예제도 이 점을 따로 명시하고 있습니다. 소문자로 보내면 필드가 통째로 무시되어 "수량 없음" 같은 엉뚱한 메시지를 받습니다.

잔고는 20건에서 끊기므로 연속조회를 처음부터 넣습니다.

def balance_futopt():
    url = BASE + "/uapi/domestic-futureoption/v1/trading/inquire-balance"
    p = {
        "CANO": CANO, "ACNT_PRDT_CD": ACNT,
        "MGNA_DVSN": "01",        # 01 게시, 02 유지
        "EXCC_STAT_CD": "1",      # 1 정산, 2 본정산
        "CTX_AREA_FK200": "", "CTX_AREA_NK200": "",
    }
    rows, cont = [], ""
    while True:
        h = dict(H, tr_id="CTFO6118R", tr_cont=cont)
        r = requests.get(url, headers=h, params=p, timeout=10)
        j = r.json()
        rows += j.get("output1", [])
        cont = r.headers.get("tr_cont", "")
        if cont not in ("F", "M"):        # 더 없음
            break
        cont = "N"                        # 다음 페이지 요청
        p["CTX_AREA_FK200"] = j.get("ctx_area_fk200", "")
        p["CTX_AREA_NK200"] = j.get("ctx_area_nk200", "")
    return rows

실패했을 때 돌아오는 응답은 이런 모양입니다. rt_cd0이 아니면 msg_cdmsg1을 그대로 로그에 남기십시오.

{
  "rt_cd": "1",
  "msg_cd": "40580000",
  "msg1": "모의투자 장운영시간이 아닙니다."
}

운영시간 밖 호출은 에러입니다. 공식 예제도 선물옵션 운영시간 외 API 호출 시 에러가 발생하니 운영시간을 확인하라고 명시하고 있습니다. 봇이 24시간 돌면서 장 시작 전에 주문을 던지면 이 에러가 반복되고, 재시도 로직이 붙어 있으면 호출 제한까지 같이 맞습니다. 장 시작 전에는 주문 루프 자체를 돌리지 않는 스케줄러가 필요합니다.

선물옵션 TR ID 전체 표

공식 예제 domestic_futureoption_functions.py에 정의된 조회 계열입니다. 실전 기준이며 모의 지원 여부는 항목마다 다릅니다.

tr_id용도
FHMIF10000000선물옵션 시세(현재가)
FHMIF10010000선물옵션 호가
FHKIF03020100선물옵션 기간별 시세(일봉)
FHKIF03020200선물옵션 분봉
FHPIF05030100선물옵션 콜풋 전광판
FHPIF05030200선물 전광판
FHPIF05110100선물옵션 일중 예상체결 추이
CTFO6118R / VTFO6118R선물옵션 잔고현황 (실전 / 모의)
CTFO6117R선물옵션 잔고 정산손익내역
CTFO6159R선물옵션 잔고평가손익내역
CTFO6119R선물옵션 기간약정수수료 일별
CTRP6550R선물옵션 총자산현황(예수금)
CTFO5139R선물옵션 기준일별 체결내역
CTFN7107R(야간) 선물옵션 증거금 상세

실시간 WebSocket 항목

실시간은 domestic_futureoption_functions_ws.py에 정의돼 있고, tr_key에 종목코드를 넣어 구독합니다. 접속·등록 절차 자체는 KIS API 웹소켓 실시간 시세와 같습니다.

tr_id내용tr_key 예시
H0IFCNT0지수선물 실시간 체결101S12
H0IFASP0지수선물 실시간 호가101S12
H0IOCNT0지수옵션 실시간 체결201S11305
H0IOASP0지수옵션 실시간 호가201S11305
H0IFCNI0선물옵션 실시간 체결통보(내 주문)HTS ID
H0ZFANC0선물 실시간 예상체결종목코드
H0CFCNT0 / H0CFASP0상품선물 실시간 체결 / 호가종목코드

H0IFCNI0만 성격이 다릅니다. 종목이 아니라 내 계좌에서 벌어진 주문 사건을 받는 통로라 tr_key에 종목코드가 아니라 HTS ID를 넣습니다. 체결 확인을 폴링으로 돌리는 대신 이걸 구독하면 호출 수를 크게 줄일 수 있습니다.

선물옵션은 코드 문제가 아니라 리스크 문제다

여기까지가 API 문제입니다. 그런데 국내 선물옵션 자동매매에서 실제로 계좌를 위험하게 만드는 건 필드명이 아니라 레버리지입니다. 코스피200 선물은 계약당 명목 금액이 크고, 옵션 매도는 손실이 한쪽으로 열려 있습니다. 주식 봇에서 쓰던 수량 계산을 그대로 옮기면 같은 "1"이라는 숫자가 전혀 다른 크기의 베팅이 됩니다.

자주 묻는 것

선물옵션 API도 앱키를 새로 발급받아야 하나요?

appkey·appsecret은 계정 단위라서 새로 발급받을 필요가 없습니다. 같은 access_token으로 주식과 선물옵션을 모두 호출합니다. 바뀌는 것은 tr_id와 엔드포인트, 그리고 ACNT_PRDT_CD입니다. 다만 선물옵션 거래가 가능한 계좌가 있어야 하고, 이건 API가 아니라 증권사 계좌 요건입니다.

주문 결과에서 주문번호는 어디에 있나요?

응답 outputODNO입니다. 정정취소할 때 이 값을 ORGN_ODNO에 넣습니다. 주문번호를 저장하지 않으면 취소할 방법이 없으므로, 주문을 던지는 즉시 파일이나 DB에 남기십시오.

증권사 중 어디가 선물옵션 API를 지원하나요?

증권사마다 지원 범위와 방식이 다릅니다. 선물옵션 지원 여부와 조건은 공지로 바뀌므로 반드시 각 사 개발자 포털에서 현재 상태를 확인하십시오.

2026-08-30 기준으로 한국투자증권 공식 저장소 koreainvestment/open-trading-apiexamples_user/domestic_futureoption/domestic_futureoption_functions.pydomestic_futureoption_functions_ws.py에 정의된 엔드포인트·tr_id·파라미터 설명을 기준으로 작성했습니다. 본문의 코드는 구조를 보여주기 위한 예시이며 그대로 실전에 쓰라는 뜻이 아닙니다. TR ID·필드 구성·모의투자 지원 범위·운영시간은 증권사 공지로 예고 없이 바뀝니다. 운영 전 KIS Developers 포털의 현재 명세로 대조하십시오. 이 글은 수익이나 시장 방향을 예측하지 않으며 투자 권유가 아닙니다.

선물옵션 봇, 안전장치까지 같이 만들어 드립니다

코스피200 선물·옵션 주문 연동부터 증거금 감시, 반대매매 방지 킬 스위치, 체결통보 알림까지 묶어서 실제로 돌아가는 자동매매 프로그램으로 만들어 드립니다. 24시간 빠른 답변 가능합니다.

무료 상담 시작하기