Files
openclaw/agents/stock/workspace/memory/2026-05-07-kiwoom-api-audit.md
T
hyowons 549545bde6 Initial commit: OpenClaw 워크스페이스 버전관리 시작
설정·스크립트·스킬·문서·큐레이션 메모리 추적.
시크릿(credentials/identity)·런타임 상태(state/logs/sessions/sqlite)·
백업(clobbered/bak)·dream 캐시는 .gitignore로 제외.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 15:10:57 +09:00

7.1 KiB

2026-05-07 키움 REST API 전수 감사 + budget 기능 도입

코디 작업. 관리자님이 "삼성전자 100만원 매수" 같은 금액 기반 자연어 매매를 요청한 게 발단. 작업 중 발견된 키움 API 호출부의 사전 이슈들을 PDF 공식 명세 기반으로 전수 정정.

1차 작업: budget(예산) 기반 매매 도입

자연어 "100만원어치 매수" 처리. 키움 API에는 금액 입력 필드 자체가 없어서 우리가 환산해야 함.

결정 사양:

  • 환산 가격: 매도1호가 (매수), 매수1호가 (매도)
  • 슬리피지 마진: 0% (예산 100% 사용)
  • 소수점 매매: 안 함 (정수 주식, 버림)
  • 예산 < 1주 가격: 거부 (BUDGET_TOO_SMALL)

구현:

  • orders/guards.py convert_budget_to_qty() 신규
  • orders/handler.py propose_trade() budget 파라미터 추가, CLI --budget 플래그
  • orders/card.py 환산 정보 노출 ("예산 1,000,000원 → 매도1호가 75,100원 기준 환산, 잔액 23,700원")
  • skills/order-trading/SKILL.md 페이로드 추출 규칙·예시 4개 추가
  • 회귀 테스트 17건 추가 (총 111건 통과)

키움 발주는 그대로: 환산된 정수 qty + trde_tp=3 시장가로 진행. 환산용 가격은 키움에 안 감, 우리 내부 계산용.

2차 작업: ka10004 호가 API 사전 이슈 발견·수정

dry-run 검증하다 발견. ka10004(주식호가요청)이 처음부터 잘못된 endpoint(/api/dostk/acnt)로 가서 1504 에러로 항상 실패하고 있었음. ledger 36건 어디에도 호가 데이터 들어간 카드 없었음. budget 도입 전부터 있던 이슈.

원인: kiwoom_client._call() 이 모든 TR을 ENDPOINT_ACNT 로 보냄. ka10004는 시세 카테고리 → /api/dostk/mrkcond 사용해야 함.

부수 효과: ka10004 정상화로 시장가 카드의 호가창·평균체결가·슬리피지 표시도 처음으로 작동.

3차 작업: 키움 PDF 공식 명세 전수 대조

관리자님이 키움 PDF 제공 (~/.openclaw/docs/키움 REST API 문서.pdf — 528쪽). 코드의 모든 키움 호출(10개 TR)을 명세와 1:1 대조.

PDF 페이지 매핑:

  • ka10001(주식기본정보) p.15, ka10004(호가) p.24, ka10099(종목리스트) p.229, ka10170(당일매매일지) p.241
  • kt00001(예수금) p.377, kt00004(계좌평가) p.385, kt00007(체결내역) p.391, kt00018(잔고내역) p.420
  • kt10000(매수) p.423, kt10001(매도) p.425

발견 사항

항목 코드 추측 명세 정답 영향
ka10001 상한가 uplmtprice/uplmt_pric/upper_limit upl_pric ±30% 가격 가드가 0/0 으로 무의미하게 통과하고 있었음
ka10001 하한가 lwlmtprice/lwlmt_pric/lower_limit lst_pric 동일
ka10001 거래정지 trde_susp_yn/halt_yn 시도 명세에 없음 별도 TR 확인 필요 (지금은 보수적 False)
ka10004 1호가 sel_fpr_bid_1 같은 suffix 패턴 sel_fpr_bid (suffix 없음) (ka10004 endpoint 수정과 함께 정정)
ka10004 2~10호가 같은 suffix 패턴 sel_{N}th_pre_bid (정정)
ka10099 nxtEnable 미사용 (_nxt_eligible 하드코딩 True) 명세에 존재 향후 NXT 가드 정확화에 활용
kt10000/kt10001 rt_cd rt_cd 변수 시도 후 fallback 명세는 return_code 동작은 함, 정리 잔여
그 외 6개 TR 모두 일치

코드 수정

orders/datasource.py _safe_quote:

out = {
    'cur_price': abs(_to_int(raw.get('cur_prc'))),
    'prev_close': abs(_to_int(raw.get('base_pric'))),  # 기준가 = 전일종가
    'upper_limit': abs(_to_int(raw.get('upl_pric'))),  # 상한가
    'lower_limit': abs(_to_int(raw.get('lst_pric'))),  # 하한가
    'halt': False,  # ka10001 명세에 없음 — 별도 TR 보강 예정
}

검증:

  • 삼성전자(005930): cur=273,500 / upper=345,500 / lower=186,500 — 정상
  • 상한가 위 매수 시도(400,000) → PRICE_ABOVE_UPPER 거부 (이전엔 통과했을 것)

잔여 보강 항목

  • #11 kt10000/kt10001 응답 ord_seq_no fallback 제거, rt_cdreturn_code 변수명 통일 2026-05-07 완료. kiwoom_order.py:111-125 정리 + 회귀 테스트 6건(tests/test_kiwoom_order.py) 신규. ledger response_summary 키도 return_code+return_msg 명시.
  • #13b list 응답 페이지네이션 안전판 2026-05-07 완료. 명세상 list 반환 TR 응답 헤더에 cont-yn/next-key 정의돼 있는데 _call이 헤더 무시하고 1페이지만 받던 문제. kiwoom_client._call_paginated(label, tr_id, body, list_field, ...) 헬퍼 신규. 적용: get_positions(kt00018) / get_trade_journal(ka10170) / get_order_executions(kt00007). cont-yn=Y + next-key 자동 후속 호출, list_field 누적, max_pages=50 safety cap. 첫 페이지 8005 토큰 만료 자동 재발급. 현재 4계좌 종목 30개 환경에선 cont-yn=N으로 누락 사고 없지만 미래 종목 100+ 도달 시 안전판. 회귀 테스트 8건(tests/test_pagination.py) 신규. 총 144건 통과.
  • #12 ka10099 nxtEnable 활용 + 거래정지 별도 TR 보강 2026-05-07 완료. 결과:
    • _nxt_eligible 캐시 lookup 도입. kiwoom_client.pylookup_stock_meta(code) 신규, refresh_stock_codesnxt_enable/state/order_warning 같이 저장하는 v2 스키마. datasource._nxt_eligible(symbol, quote)가 캐시 lookup 후 보수적 fallback. 캐시 갱신 후 4264 종목 중 3634이 NXT 미상장으로 판명 — 이전엔 모든 종목 True 통과로 NXT 시간대 매수 시도 시 키움이 사후 거부했을 것. 이제 가드 단계 사전 거부.
    • 거래정지(halt) 단독 TR 부재 확인. ka10001/ka10099 등 REST 응답에 거래정지 boolean 플래그 없음. 실시간 websocket 1h (VI 발동/해제) 만 존재 — OpenClaw 범위 밖. 사후 broker reject가 최종 안전판으로 결정. ka10099의 state/order_warning 활용한 위험 종목 경고 카드는 후속 안건.
    • 회귀 테스트 5건(tests/test_nxt_eligible.py) 신규. 총 122건 통과.
    • 캐시 갱신 명령 (스키마 v2 적용 위해 1회 필요): python3 ~/.openclaw/agents/stock/workspace/scripts/kiwoom_client.py refresh-codes
  • #13 위험 종목 카드 경고 2026-05-07 완료. 등급별 차등 정책(거부+경고). guards.evaluate_stock_state(stock_meta) 신규. handler가 propose_trade에서 호출. card.format_card에 state_warning 줄 추가. 회귀 테스트 14건(tests/test_stock_state.py). 거부 정책: orderWarning ∈ {2 정리매매, 4 투자위험} OR state에 '거래정지'·'정리매매'. 경고 정책: orderWarning ∈ {1 ETF주의, 3 단기과열, 5 투자경과} OR state에 '관리종목'. 거부 우선. 실 캐시 기준 거부 ~110+ 종목 (대부분 관리종목+거래정지 결합), 경고 ~175 종목.

코디 자동 메모리에도 등록

  • ~/.claude/projects/-Users-snowoyh--openclaw/memory/reference_kiwoom_endpoints.md 신규 — TR_ID 별 endpoint 도메인 매핑(acnt/stkinfo/mrkcond/ordr), ka10004 호가 필드 함정, 공식 PDF 위치