AlgoLab Blog · 자동매매 실무 · 2026

ChatGPT 자동매매 코드 — 실제로 막히는 6곳

실무 · 트러블슈팅 2026-09-25 · 약 8분 읽기 · 알고랩 AlgoLab
한 줄 요약

생성형 모델이 짠 국내 증권사 자동매매 코드는 파이썬 문법이 아니라 증권사 스펙에서 막힙니다. 실제로 걸리는 곳은 여섯 군데입니다 — ⑴ 모의투자에서 tr_id 접두어가 바뀌는 것(KIS 공식 샘플은 T·J·C를 V로 치환합니다), ⑵ HTTP 200 인데 실패인 응답(KIS 는 rt_cd, 키움은 본문 return_code로만 알려 줍니다), ⑶ 호출 간격(공식 샘플은 실전 0.05초·모의 0.5초를 쉽니다), ⑷ 토큰을 매 호출 재발급, ⑸ 연속조회 미처리(tr_cont / cont-yn·next-key), ⑹ 없는 엔드포인트·필드를 지어내는 것. 이 여섯은 전부 실행은 되는데 결과가 틀리는 종류라, 오류 메시지를 기다리면 영영 안 보입니다.

이 글에서 다루는 것

  1. 문법은 맞는데 왜 안 도는가
  2. ① 모의투자에서 거래 코드가 바뀐다
  3. ② HTTP 200 이 성공이 아니다
  4. ③ 공식 샘플이 매 호출마다 쉬는 이유
  5. ④ 토큰을 매번 새로 받는 코드
  6. ⑤ 첫 페이지만 받고 끝내는 조회
  7. ⑥ 지어낸 필드 위에 쌓은 로직
  8. 그래서 어떤 순서로 쓰나

1. 문법은 맞는데 왜 안 도는가

“ChatGPT 에게 자동매매 프로그램을 만들어 달라고 했더니 코드가 나왔는데, 돌려도 주문이 안 나간다”는 상담이 꾸준히 들어옵니다. 코드를 받아 보면 대개 파이썬 자체는 멀쩡합니다. requests.post 로 보내고, json.loads 로 받고, 전략 부분도 그럴듯하게 짜여 있습니다.

문제는 다른 층에 있습니다. 생성형 모델이 잘하는 것은 일반적인 코드 모양이고, 국내 증권사 API 가 요구하는 것은 그 증권사만의 규약입니다. 한국투자증권 KIS 와 키움증권 REST API 는 둘 다 REST 지만 성공을 알리는 방식도, 모의투자로 넘어가는 방식도, 페이지를 넘기는 방식도 서로 다릅니다. 이 규약은 코드 모양으로는 드러나지 않습니다.

그래서 증상이 고약합니다. 프로그램은 예외 없이 끝까지 돌고, 로그에도 빨간 글씨가 없고, 다만 주문이 안 나가 있거나 데이터가 절반만 들어와 있습니다. 아래 여섯 곳은 알고랩이 “AI가 짜 준 코드인데 안 돕니다”로 들어온 코드를 열어 봤을 때 반복해서 나온 자리이며, 모두 양 증권사의 공식 저장소 원문과 대조해 정리했습니다.

잘 맞추는 층 파이썬 문법 · requests/json 사용법 · 전략 의사코드 · 디렉터리 구조 자주 틀리는 층 tr_id 접두어 · rt_cd/return_code 판정 · 호출 간격 · 토큰 캐시 · 연속조회 지어내는 층 존재하지 않는 엔드포인트 · 없는 응답 필드 · 폐기된 TR 코드
같은 코드 안에서 층마다 신뢰도가 다르다 — 아래로 갈수록 사람이 원문으로 확인해야 한다

2. ① 모의투자에서 거래 코드가 바뀐다

가장 자주, 가장 조용히 막히는 곳입니다. 국내 증권사는 실전과 모의투자가 별개 환경이라 앱키도 따로 발급받고 주소도 다릅니다. 여기까지는 대부분의 글에 적혀 있어서 모델도 맞춥니다. 문제는 거래 코드 자체가 바뀐다는 점입니다.

한국투자증권 공식 저장소(koreainvestment/open-trading-api)의 examples_llm/kis_auth.py 에는 이 치환이 함수로 들어 있습니다.

# examples_llm/kis_auth.py — 공식 샘플 원문
    tr_id = ptr_id
    if ptr_id[0] in ("T", "J", "C"):   # 실전투자용 TR id 체크
        if isPaperTrading():           # 모의투자용 TR id 식별
            tr_id = "V" + ptr_id[1:]

    headers["tr_id"]    = tr_id
    headers["custtype"] = "P"          # 개인·법인 "P", 제휴사 "B"
    headers["tr_cont"]  = tr_cont

즉 국내주식 현금매수 TTTC0802U 는 모의투자에서 VTTC0802U 가 됩니다. 블로그에서 실전 코드를 그대로 베껴 온 답변을 받으면, 모의 계좌에서는 존재하지 않는 TR 을 부르는 셈이 됩니다. 한 발 더 나아가 custtype 헤더를 빠뜨리는 경우도 흔합니다. 실전과 모의를 오가는 전환 절차는 별도 글에서 따로 정리했습니다.

키움 REST 는 아예 도메인이 갈립니다. 공식 클라이언트(Kiwoom-Securities/Kiwoom-REST-API)의 kiwoom/core/auth.py 가 기본값으로 들고 있는 주소는 다음과 같습니다.

# kiwoom/core/auth.py — 공식 클라이언트 원문
DEFAULT_BASE_URLS = {
    "real": "https://api.kiwoom.com",
    "demo": "https://mockapi.kiwoom.com",
}
DEFAULT_WS_BASE_URLS = {
    "real": "wss://api.kiwoom.com:10000",
    "demo": "wss://mockapi.kiwoom.com:10000",
}
TOKEN_PATH  = "/oauth2/token"
REVOKE_PATH = "/oauth2/revoke"

증상이 “인증 실패”로 오는 함정. 키움 공식 문서의 설치 안내는 인증에 실패했습니다[8001...] 이 뜨면 “앱 키·시크릿이 틀렸거나, 모의투자 키를 real로(또는 그 반대로) 쓰고 있는 것”이라고 못 박습니다. 키를 몇 번씩 재발급해도 안 고쳐지는 이유가 여기 있습니다 — 틀린 건 키가 아니라 환경입니다. 이 계열 코드는 키움 REST API 오류코드 — return_code 3의 함정에서 전부 분류해 두었습니다.

3. ② HTTP 200 이 성공이 아니다

두 번째는 성공 판정입니다. 생성된 코드는 거의 예외 없이 이렇게 씁니다.

# AI가 자주 내놓는 형태 — 국내 증권사 API 에서는 틀린 판정
res = requests.post(url, headers=headers, data=json.dumps(body))
if res.status_code == 200:
    print("주문 성공")          # ← 여기가 함정
    order_id = res.json()["output"]["ODNO"]

국내 증권사 API 는 업무 오류를 HTTP 상태코드로 알려 주지 않는 경우가 많습니다. 키움증권 공식 클라이언트의 kiwoom/core/client.py 에는 이 설계가 주석으로 명시돼 있습니다.

# kiwoom/core/client.py — 공식 클라이언트 원문 주석
# 키움은 업무 오류도 HTTP 200으로 내려보내고 실패 여부는 본문 return_code에만 담는다.
if return_code not in (None, 0):
    raise_for_error_code(
        return_code,
        str(data.get("return_msg") or "API 요청에 실패했습니다."),
    )

KIS 쪽도 구조는 같습니다. 공식 샘플의 응답 래퍼 APIResp 는 본문의 rt_cd 가 문자열 "0" 일 때만 성공으로 보고, 실패 사유는 msg_cd 와 msg1 에서 꺼냅니다.

# examples_llm/kis_auth.py — 공식 샘플 원문
self._err_code    = self._body.msg_cd
self._err_message = self._body.msg1
...
if self.getBody().rt_cd == "0":
    # 여기부터가 진짜 성공
증권사성공 조건실패 사유가 들어오는 자리HTTP 상태
한국투자증권 KISrt_cd == "0" (문자열)msg_cd · msg1업무 오류도 200 이 흔함
키움증권 RESTreturn_code == 0 (정수)return_msg공식 주석이 200 이라 명시

여기서 한 겹 더 있습니다. 키움은 같은 return_code 를 정수 0 으로도, 문자열 "0" 이나 "0000" 으로도 내려보냅니다. 공식 클라이언트가 normalize_return_code 라는 정규화 함수를 따로 둔 이유입니다 — if data["return_code"] == 0 한 줄로 끝내면 엔드포인트에 따라 성공을 실패로 읽습니다. KIS 쪽 오류 코드 체계는 아래 관련 글에 정리돼 있습니다.

4. ③ 공식 샘플이 매 호출마다 쉬는 이유

세 번째는 유량입니다. 생성된 코드에는 대기가 아예 없는 경우가 대부분입니다. 종목 50개를 for 문으로 훑는 코드가 나오면, 그 루프는 첫 실행에서 바로 한도에 걸립니다.

KIS 공식 샘플은 이 대기를 인증 환경에 따라 다르게 잡아 둡니다.

# examples_llm/kis_auth.py — 공식 샘플 원문
_smartSleep = 0.1                      # 기본값

def changeTREnv(token_key, svr="prod", product=_cfg["my_prod"]):
    if svr == "prod":                  # 실전투자
        _isPaper = False
        _smartSleep = 0.05
    elif svr == "vps":                 # 모의투자
        _isPaper = True
        _smartSleep = 0.5              # ← 실전의 10배

def smart_sleep():
    time.sleep(_smartSleep)

주목할 점은 모의투자 쪽이 열 배 느슨하다는 것입니다. “모의로 먼저 테스트하고 실전에 올린다”는 순서를 따를 때, 모의에서 통과한 호출 밀도가 실전 기준보다 오히려 여유로운 설정 위에서 검증된 셈이라 방심하기 쉽습니다. 반대 방향의 사고도 흔합니다 — 실전 코드를 모의에 그대로 올려 한도 초과가 나는 경우입니다.

수치는 외우지 말고 확인하십시오. 위 값은 공식 샘플 코드가 실제로 설정한 대기 시간이지 증권사가 보증한 한도 수치가 아닙니다. 초당 허용 건수와 계정별 정책은 바뀝니다 — KIS Developers 와 키움 REST API 공식 문서에서 현재 내용을 확인하십시오. 유량을 코드 구조로 푸는 방법은 자동매매 API 유량 설계에 따로 있습니다.

5. ④ 토큰을 매번 새로 받는 코드

네 번째는 접근 토큰입니다. 생성된 코드에서 가장 흔한 형태는 이렇습니다.

# AI가 자주 내놓는 형태 — 호출마다 토큰 발급
def get_price(code):
    token = get_access_token()        # ← 매번 발급
    headers = {"authorization": f"Bearer {token}", ...}
    return requests.get(url, headers=headers, params={"FID_INPUT_ISCD": code})

토큰 발급 자체가 제한이 걸린 API 입니다. KIS 공식 샘플은 마지막 인증 시각에서 하루가 지났을 때만 다시 인증하도록 짜여 있습니다.

# examples_llm/kis_auth.py — 공식 샘플 원문
if (n2 - _last_auth_time).seconds >= 86400:   # 유효시간 1일
    auth(svr, product)

키움은 한 겹 더 있습니다. 토큰 응답이 expires_in 같은 상대 초가 아니라 expires_dt 라는 절대 시각 문자열로 옵니다.

{
    "expires_dt": "20241107083713",
    "token_type": "bearer",
    "token": "WQJCwyqInphKnR3bSRtB9NE1lv...",
    "return_code": 0,
    "return_msg": "정상적으로 처리되었습니다"
}

모델은 OAuth 일반 상식대로 expires_in 을 읽으려 들기 때문에, 이 응답에서는 KeyError 가 나거나 기본값으로 넘어가 만료 관리가 사실상 꺼집니다. 각 증권사의 토큰 수명과 재발급 규칙은 KIS API 토큰 만료와 재발급과 키움 REST 토큰 유효기간과 폐기에 각각 정리돼 있습니다.

6. ⑤ 첫 페이지만 받고 끝내는 조회

다섯 번째는 연속조회입니다. 국내 증권사 API 는 한 번에 내려주는 행 수가 제한돼 있고, 나머지는 커서를 들고 다시 요청해야 받습니다. 그런데 이 커서가 본문이 아니라 헤더에 있습니다.

증권사커서를 주고받는 자리계속 여부 판단
한국투자증권 KIS요청·응답 헤더 tr_cont응답 tr_cont 값으로 다음 호출을 결정
키움증권 REST헤더 cont-yn · next-keycont-yn 이 Y 인 동안 반복

생성된 코드는 보통 응답 본문만 읽고 끝냅니다. 그러면 프로그램은 오류 없이 돌지만, 잔고 조회에서 종목이 일부만 잡히고 일봉 조회에서 기간이 잘립니다. 백테스트를 그 데이터로 돌리면 결과 자체가 조용히 틀립니다. 키움 쪽 커서 처리는 키움 REST 연속조회 — cont-yn·next-key에 실물로 정리했습니다.

7. ⑥ 지어낸 필드 위에 쌓은 로직

여섯 번째가 가장 위험합니다. 모델은 모르면 그럴듯한 이름을 만듭니다. 실제로 받아 본 코드에서 반복된 유형은 세 가지입니다.

검증 방법은 하나뿐입니다 — 공식 원문에서 문자열로 찾아보는 것. 코드에 등장하는 모든 TR 코드·엔드포인트·응답 필드명을 공식 저장소나 API 포털에서 그대로 검색해 존재를 확인하십시오. 하나라도 안 나오면 그 줄은 지어낸 것입니다. “아마 맞겠지”로 넘긴 필드 하나가 잔고를 잘못 읽고, 그 위에 주문 수량 계산이 얹힙니다.

8. 그래서 어떤 순서로 쓰나

결론은 “쓰지 말라”가 아니라 순서를 뒤집으라는 것입니다. 스펙을 모델에게 물어보는 순서로 시작하면 틀린 필드 위에 코드를 쌓게 됩니다. 반대로 스펙을 사람이 확정한 뒤 붙여 넣고 코드를 맡기면 지어내는 범위가 크게 줄어듭니다.

  1. 공식 저장소를 먼저 연다. 한국투자증권은 koreainvestment/open-trading-api 에 examples_llm 폴더와 MCP 서버를 따로 두었고, 키움은 Kiwoom-Securities/Kiwoom-REST-API 에 예제와 Postman 컬렉션을 공개합니다. 생성형 모델에게 문맥으로 넘기라고 만들어 둔 자료입니다.
  2. 쓸 TR 하나를 확정한다. 기능명으로 api_id(키움) 또는 tr_id(KIS)를 먼저 고정하고, 요청·응답 필드명을 원문에서 복사합니다.
  3. 그 원문을 붙여 넣고 코드를 맡긴다. 이 단계에서 모델은 잘하는 층(문법·구조)만 담당하게 됩니다.
  4. 모의투자에서 응답 전문을 눈으로 본다. rt_cd / return_code 를 직접 출력해 성공 판정이 맞는지 확인합니다.
  5. 주문은 최소 수량으로 한 번. 자동 반복을 켜기 전에 단건으로 체결까지 확인합니다.

체크리스트 — 받은 코드를 열었을 때 6줄만 확인하십시오.

덧붙여, “AI 자동매매”라는 말이 코드를 AI가 짰다는 뜻인지 매매 판단을 AI가 한다는 뜻인지는 전혀 다른 이야기입니다. 후자는 아래 관련 글에서 따로 다뤘습니다. 이 글은 전자 — 생성형 모델이 짠 코드가 증권사 스펙과 어긋나는 지점만 다룹니다.

API 키 발급부터 순서대로 밟고 싶다면 KIS API 키 발급 30분 가이드와 키움 REST API 자동매매 완전 가이드가 출발점입니다. 주문 헤더의 hashkey 를 넣을지 말지는 별도 글에 정리돼 있습니다.

※ 본 글의 코드 인용은 각 증권사가 공개한 공식 저장소 원문이며, 작성 시점 기준입니다. API 스펙·한도·정책은 변경될 수 있으니 반드시 증권사 공식 문서에서 현재 내용을 확인하십시오. 알고랩(퀀트웍스)은 투자자문업·투자일임업을 영위하지 않으며, 본 글은 도구 제작에 관한 기술 정보 제공입니다. 투자 판단과 그 결과의 책임은 투자자 본인에게 있습니다.

받은 코드를 대신 열어 봐 드립니다

AI로 짠 코드가 어디서 막히는지, 살릴 부분과 다시 짜야 할 부분을 먼저 구분해 드립니다. 코드를 보여 주시면 범위부터 잡아 드립니다.
24시간 빠른 답변 가능합니다.

제작 상담하기