시세 조회, 차트 대시보드, 급등락 감시, 모의매매에 LLM을 붙이는 방법
API로 만드는 주식 프로그램
증권사 API를 사용하면 HTS 화면을 사람이 직접 누르지 않아도 프로그램이 시세를 조회하고, 차트를 그리고, 조건을 감시하고, 모의 또는 실전 주문 흐름을 만들 수 있다. 한국투자증권의 KIS Developers는 이런 기능을 Open API 형태로 제공한다. 공식 포털은 API 설명, 예제 코드, 개발자 지원 자료를 제공하며, 프로젝트의 코드는 이 흐름을 Python 프로그램으로 옮긴 것이다.
여기서 중요한 점은 자동매매 자체가 목적이 아니라 데이터 흐름을 이해하는 것이다. 토큰을 발급받고, 종목코드로 시세를 조회하고, 캔들 데이터를 표준 형태로 바꾸고, 조건을 만족하는 이벤트를 탐지하고, 그 결과를 사람이 이해할 수 있는 화면과 로그로 보여 주는 과정이 핵심이다.
공식 API 흐름
| 단계 | 설명 | 프로젝트 위치 |
|---|---|---|
| 1. 앱키 준비 | KIS Developers에서 앱키와 앱시크릿을 발급받고 .env.local에 저장한다. | chart/kis_client.py |
| 2. 접근 토큰 발급 | POST /oauth2/tokenP로 access_token을 받아 이후 요청의 Authorization 헤더에 넣는다. | KISClient.issue_access_token |
| 3. TR ID 지정 | API마다 정해진 tr_id를 헤더에 넣는다. 모의투자에서는 일부 TR 접두사가 바뀐다. | _tr_id_for_domestic |
| 4. 시세 조회 | 국내주식 현재가, 일/주/월봉, 1분봉 등을 REST GET으로 조회한다. | inquire_daily_itemchart, fetch_minute_candles |
| 5. 결과 정규화 | KIS 응답 필드를 Open, High, Low, Close, Volume 같은 표준 컬럼으로 바꾼다. | candles_to_dataframe |
client = KISClient()
client.issue_access_token()
body = client.inquire_daily_itemchart(
fid_input_iscd="005930",
period="D",
)
df = candles_to_dataframe(body)프로젝트 구성
| 파일 | 역할 |
|---|---|
| moei/main.py | 가장 작은 KIS API 예제다. 토큰 발급 후 삼성전자 현재가를 조회한다. |
| chart/kis_client.py | REST 인증, 토큰 재발급, 일봉 조회, KIS 응답을 DataFrame으로 바꾸는 공통 클라이언트다. |
| chart/main.py | PyQt5와 matplotlib로 캔들 차트, 이동평균, RSI, 거래량, LLM 판단 패널을 배치한 대시보드다. |
| chart/build_stock_list.py | KRX 종목 목록을 만들고 관리종목, 투자주의, 거래정지 등 위험 표시 종목을 제외한다. |
| chart/fetch_positive_news.py | 네이버 금융 뉴스를 수집하고 호재/악재 키워드로 종목별 후보를 만든다. |
| chart/paper_trade.py | 호재 종목을 대상으로 익절/손절 규칙을 적용하는 모의매매 스케줄러다. |
| chart/surge_monitor.py | KIS 1분봉 데이터를 반복 조회해 급등과 급락 이벤트를 감시한다. |
| chart/surge_paper_trade.py | 급등 뒤 저점과 호가를 보고 가상 매수한다. 실주문은 넣지 않는다. |
| chart/surge_minute_redis_writer.py | 모의투자 1분봉만 조회해 Redis에 넣는다. |
| chart/surge_paper_train_loop.py | 장중 가상매매가 끝나면 손익 데이터를 만들고 LoRA 학습을 반복한다. |
| chart/training/train_surge_lora.py | 끝난 가상매매의 손익 원인을 설명하도록 작은 텍스트 모델을 미세조정한다. |
응용 프로그램 1: 현재가 조회
가장 작은 프로그램은 종목코드 하나를 넣고 현재가를 조회하는 것이다. 이 단계에서 독자는 API 키, 토큰, 헤더, 파라미터, 응답 JSON의 의미를 익힌다. 아래 예제는 흐름을 쉽게 확인할 수 있도록 삼성전자 종목코드 005930을 사용한다.
python moei/main.py
# 흐름
# 1) .env.local에서 APP_KEY / APP_SECRET 읽기
# 2) /oauth2/tokenP로 접근 토큰 발급
# 3) /uapi/domestic-stock/v1/quotations/inquire-price 호출
# 4) output JSON 출력응용 프로그램 2: 차트 대시보드
차트 프로그램은 일봉, 주봉, 월봉 데이터를 가져와 캔들, 이동평균, RSI, 거래량을 한 화면에 보여 준다. 오른쪽에는 LLM 판단, 판단 이유, 이력 영역이 이미 준비되어 있다. 현재 코드에서는 placeholder에 가깝지만, 이 빈자리가 바로 LLM을 붙이기 좋은 자리다.
| 화면 영역 | 기능 | LLM 적용 아이디어 |
|---|---|---|
| 캔들 차트 | 가격 흐름, 이동평균, RSI, 거래량 표시 | 최근 20봉의 변화와 기술적 조건을 자연어로 요약한다. |
| LLM 판단 | 매수/관망/주의 같은 사람이 읽는 판단 표시 | 규칙 기반 신호를 받아 이유를 설명한다. 직접 주문 판단을 맡기지는 않는다. |
| 판단 이유 | 근거 문장 표시 | 가격, 거래량, 뉴스, 리스크를 한 문단으로 정리한다. |
| 이력 | 자동 판단의 누적 기록 | 이전 판단과 현재 판단이 왜 달라졌는지 비교 설명한다. |
응용 프로그램 3: 뉴스 기반 후보 선정
fetch_positive_news.py는 네이버 금융 뉴스를 수집하고, 호재와 악재 키워드 사전으로 점수를 계산한 뒤 종목명과 매칭한다. 현재 positive_news.csv에는 호재성 기사에서 추출한 종목별 행이 저장되어 있다. 이 구조는 초보자에게 매우 좋다. 왜냐하면 처음부터 LLM에게 모든 판단을 맡기지 않고, 키워드 규칙으로 작동하는 기준선을 먼저 만들기 때문이다.
| 현재 방식 | 장점 | LLM으로 개선할 부분 |
|---|---|---|
| 키워드 점수 | 동작이 빠르고 이유가 명확하다. | 문맥을 보지 못해 '급등 후 우려' 같은 문장을 오해할 수 있다. |
| 종목명 문자열 매칭 | 구현이 단순하다. | 짧은 종목명, 동명이슈, 지주사/자회사 관계를 구분하기 어렵다. |
| CSV 저장 | 검증과 재현이 쉽다. | LLM 요약과 근거 문장을 함께 저장하면 리뷰가 쉬워진다. |
뉴스 제목 + 요약
-> 종목명 매칭
-> 호재/악재 키워드 점수
-> 후보 종목 CSV
-> LLM 요약과 리스크 설명 추가응용 프로그램 4: 급등락 실시간 감시
surge_monitor.py는 KIS 1분봉을 일정 주기로 조회하고, 최근 N분 수익률, 거래량 급증, 캔들 폭 조건을 동시에 만족할 때 이벤트를 출력한다. watchlist를 직접 지정할 수도 있고, 거래대금 상위 종목이나 호재 후보 종목으로 자동 구성할 수도 있다.
| 탐지 조건 | 의미 |
|---|---|
| 최근 N분 누적 수익률 | 짧은 시간 안에 가격이 얼마나 움직였는지 본다. |
| 거래량 평균 대비 배수 | 평소보다 시장 참여가 갑자기 늘었는지 본다. |
| 마지막 1분봉 캔들 폭 | 순간 변동성이 충분히 큰지 본다. |
| 중복 알림 방지 | 같은 분봉 이벤트를 반복해서 출력하지 않는다. |
LLM은 이 이벤트의 원인을 추정하는 보조자로 쓸 수 있다. 예를 들어 방금 발생한 급등 이벤트에 대해 최근 뉴스, 공시, 업종 흐름, 과거 이벤트 이력을 모아 '왜 알림이 발생했는지' 설명하게 할 수 있다. 단, LLM의 설명은 사실 확인 전의 가설로 표시해야 한다.
응용 프로그램 5: 모의매매 스케줄러
paper_trade.py는 실거래가 아니라 모의 실험이다. 매일 아침 호재 후보를 고르고, 장 마감 후 당일 OHLC 데이터로 익절 +5%, 손절 -3%, 미도달 시 종가 청산 규칙을 적용한다. 결과는 날짜별 CSV로 저장된다. 프로젝트에는 2026년 4월 29일, 4월 30일, 5월 4일, 5월 6일의 모의매매 결과 파일이 있다.
| 규칙 | 설명 |
|---|---|
| 매수 | 후보 종목을 당일 시초가에 매수했다고 가정한다. |
| 익절 | 당일 고가가 매수가의 +5% 이상이면 익절 체결로 본다. |
| 손절 | 당일 저가가 매수가의 -3% 이하이면 손절 체결로 본다. |
| 동시 도달 | 일봉만으로 순서를 알 수 없으므로 보수적으로 손절을 먼저 적용한다. |
| 마감 청산 | 익절/손절 모두 없으면 종가에 청산해 다음 날로 넘기지 않는다. |
4일치 결과 파일을 모아 보면 체결된 33건 중 이익으로 끝난 거래는 15건이고, 4일 순손익은 −11,558원이다. 손절 14건은 모두 −3%에, 익절 6건은 +5%에 몰려 있다. 나머지 13건은 어느 쪽에도 닿지 않아 종가에 청산되었는데, 하루 손익을 가른 것은 오히려 이 거래들이었다. 종가 청산이 대부분 플러스였던 5월 4일은 이익이, 손절이 많았던 4월 30일과 5월 6일은 손실이 났다.
LLM을 넣기 좋은 부분
LLM을 주식 프로그램에 넣을 때 가장 좋은 위치는 '사람이 읽고 판단해야 하는 정보의 압축'이다. 숫자 계산, 주문 전송, 손절 조건 같은 부분은 결정적 코드와 규칙으로 처리하고, LLM은 설명, 요약, 분류, 질의응답, 리포트 작성에 배치하는 것이 안전하다.
| 적용 위치 | LLM 역할 | 주의점 |
|---|---|---|
| 뉴스 분석 | 기사 여러 개를 읽고 호재, 악재, 중립으로 분류하며 핵심 근거를 요약한다. | 기사 원문 출처와 시간, 불확실성을 함께 남긴다. |
| 차트 설명 | 이동평균, RSI, 거래량 변화가 어떤 상태인지 자연어로 설명한다. | 기술적 지표를 투자 권유처럼 단정하지 않는다. |
| 급등락 알림 | 왜 알림이 떴는지 가격/거래량/뉴스 관점으로 설명한다. | 원인 추정과 확인된 사실을 구분한다. |
| 모의매매 회고 | 오늘의 결과 CSV를 읽고 승률, 손익, 실패 원인을 요약한다. | 과최적화나 사후 해석을 경계한다. |
| 사용자 질의응답 | 사용자가 '왜 이 종목이 후보인가'라고 물으면 근거를 찾아 답한다. | 개인 맞춤 투자 조언으로 넘어가지 않게 제한한다. |
| 전략 문서화 | 규칙, 파라미터, 변경 이력, 리스크를 자동 문서로 남긴다. | LLM이 만든 문서는 사람이 최종 검토한다. |
LLM에게 맡기면 위험한 부분
- 실전 주문 버튼을 LLM 판단만으로 누르게 하지 않는다.
- 손절, 익절, 포지션 크기 같은 위험 관리 규칙을 자연어 출력에 의존하지 않는다.
- API 키, 앱시크릿, 계좌번호 같은 민감 정보를 프롬프트에 넣지 않는다.
- 뉴스 요약만으로 사실 여부가 확인된 것처럼 표시하지 않는다.
- 백테스트 결과가 좋다는 이유로 미래 수익이 보장된다고 쓰지 않는다.
추천 아키텍처
KIS Open API
-> KISClient: 인증, 토큰 재발급, REST 호출
-> DataFrame: OHLCV, 1분봉, 현재가 정규화
-> Rule Engine: 급등락, 손절/익절, 후보 필터
-> Storage: CSV 로그, 이벤트 기록, 모의매매 결과
-> LLM Layer: 뉴스 요약, 차트 설명, 이벤트 해설, 회고 리포트
-> UI: PyQt 대시보드, 알림, 사용자 질문이 구조에서 LLM은 마지막 판단자가 아니라 해설자와 분석 보조자다. 수치 계산은 코드가 하고, LLM은 그 숫자가 무엇을 의미하는지 독자와 사용자에게 설명한다. 이 역할 분담을 지키면 프로그램은 훨씬 안정적이고 책의 메시지도 명확해진다.
실습 순서
- 1단계: KIS Developers에서 API 키를 발급받고 .env.local에 APP_KEY와 APP_SECRET을 설정한다.
- 2단계: moei/main.py로 현재가 조회가 되는지 확인한다.
- 3단계: chart/main.py로 일봉 차트를 그리고 종목코드를 바꿔 본다.
- 4단계: build_stock_list.py로 투자 유의 종목을 제외한 종목 목록을 만든다.
- 5단계: fetch_positive_news.py로 뉴스 기반 후보 CSV를 만든다.
- 6단계: paper_trade.py --runonce로 모의매매 결과를 저장한다.
- 7단계: surge_monitor.py로 1분봉 급등락 감시 조건을 실험한다.
- 8단계: surge_paper_trade.py로 같은 신호를 가상 매매까지 이어 본다. 실주문은 켜지 않는다.
- 9단계: 장마감 CSV를 training/build_surge_dataset.py로 바꾸고 train_surge_lora.py로 회고 문장을 학습한다.
안전 장치
주식 API 프로그램은 재미있는 장난감처럼 보이지만, 실전 계좌와 연결되면 곧바로 돈이 움직인다. 따라서 이 예제는 모의투자와 페이퍼 트레이딩을 기본값으로 둔다. 실전 주문을 연결하려면 위험 고지, 권한 분리, 주문 전 확인, 최대 손실 제한과 로그 보존을 먼저 구현해야 한다.
분봉 가상매매와 장후 회고
앞의 paper_trade.py와 surge_monitor.py는 초안에 가깝다. 하나는 일봉으로 하루를 통째로 가정하고, 다른 하나는 1분봉 급등락을 알림으로만 남긴다. 그 다음 코드는 신호를 받자마자 사지 않는다. surge_paper_trade.py는 급등이 뜬 뒤 몇 분 동안 저점, 고점, 호가 스프레드, 매수 잔량을 본다. 스프레드가 벌어졌다가 다시 좁혀지고, 매수 잔량이 우세하고, 가격이 저점 근처에 있을 때만 가상으로 들어간다. 이 진입을 dip_peak이라고 부른다. 신호 봉 종가에 바로 사는 surge-close는 비교용으로만 남아 있다.
체결은 한국투자증권 모의투자 시세와 메모리, CSV로만 계산한다. 주문 API는 호출하지 않는다. 종목당 한도는 100만 원이고, 동시에 보유하거나 관찰하는 종목은 기본적으로 셋이다. 목표 수익은 최소 +5%이고 손절은 매수가 기준이다. 장이 끝나면 남은 포지션은 마지막 분봉 종가로 정리하고, 들어가지 못한 관찰은 취소한다.
실제로 남은 기록(2026년 5월 13일~6월 4일, 청산 81건)을 모으면 이 설계의 과제가 숫자로 보인다. 처음에는 신호 봉 종가에 바로 사는 방식이었고, 곧 dip_peak 저점 매수로 바꾼 뒤 6월에는 돌파와 관찰 종료 후 추세 진입을 시험했다. 승률은 25.9%(21건)였고 누적 실현손익은 약 −109만 원이다. 손실 거래는 −3%에서 정확히 끊겼지만 이익 거래도 평균 +3.3%에 그쳐, 본전을 맞추려면 승률이 47.6%는 되어야 했다. 목표 수익을 손절 폭보다 충분히 크게 잡아야 하는 이유가 여기에 있다.
분봉 조회와 매매 판단을 한 프로세스에 두면 시세가 늦을 때 판단까지 멈춘다. surge_minute_redis_writer.py가 모의 분봉만 Redis에 넣고, surge_paper_trade.py는 그 값을 읽어 진입 여부만 정한다. 키는 hankook:surge:minute:{종목코드}다.
| chart/surge_paper_trade.py | 급등 뒤 저점과 호가를 보고 가상 매수·청산한다. |
| chart/surge_minute_redis_writer.py | 모의 1분봉만 조회해 Redis에 적재한다. |
| chart/surge_redis_common.py | 분봉 JSON 키 형식을 맞춘다. |
| chart/surge_paper_train_loop.py | 장중 가상매매 뒤 데이터셋 생성과 LoRA 학습을 반복한다. |
| chart/training/build_surge_dataset.py | 당일 CSV의 매수·매도 쌍을 손익 원인 설명으로 바꾼다. |
| chart/training/train_surge_lora.py | Qwen2.5-0.5B 같은 작은 텍스트 모델에 LoRA를 얹는다. |
surge_minute_redis_writer.py
-> Redis hankook:surge:minute:{code}
-> surge_paper_trade.py
-> surge_paper_trades_YYYY-MM-DD.csv
-> training/build_surge_dataset.py
-> training/train_surge_lora.py이 학습에서 LLM은 주문을 내지 않는다. 데이터는 이미 끝난 가상매매의 매수가, 매도가, 실현손익이다. 모델이 배우는 일은 그 급등 뒤에 왜 익절이었거나 손절이었는지를 문장으로 말하는 것이다. 차트 화면의 LLM 패널은 아직 자리만 있고, 사고 파는 조건은 규칙이 맡는다.
남아 있는 학습 기록은 이 단계가 아직 시작점이라는 것도 보여 준다. Qwen2.5-0.5B-Instruct에 붙인 LoRA는 17개 예제로 4 epoch, 28 step을 학습했고, loss는 2.86에서 0.009로, 토큰 정확도는 0.43에서 1.00으로 올라갔다. 그러나 17개 중 15개는 거래 4건을 질문만 바꿔 늘린 것이라, 이 숫자는 원인을 이해했다기보다 문장을 외웠다는 뜻에 가깝다. 끝난 가상매매가 매일 쌓여야 회고 모델도 의미를 갖는다.
마무리
한국투자증권 API를 사용하면 개인 개발자도 시세 조회, 차트 대시보드, 이벤트 감시, 모의매매 같은 실전적인 프로그램을 만들 수 있다. 여기에 LLM을 붙이면 숫자와 로그가 사람의 언어로 바뀐다. 좋은 구조는 명확하다. API는 데이터를 가져오고, 규칙 엔진은 조건을 판정하고, LLM은 그 결과를 설명한다. 이 분업이 지켜질 때 주식 프로그램은 더 똑똑해지면서도 더 안전해진다.
참고 자료
- KIS Developers 공식 포털: https://apiportal.koreainvestment.com/
- 한국투자증권 공식 GitHub: https://github.com/koreainvestment/open-trading-api
- KIS Developers 공식 GitHub 조직: https://github.com/koreainvestment
댓글
0개아직 댓글이 없습니다.