한국투자증권(KIS) Open API와 LLM(로컬 Hermes / OpenAI GPT / Anthropic Claude)을 결합한 자동매매 에이전트입니다. 장중에 기술적 지표·시장 흐름·수급·공시를 역할별 AI 에이전트들에게 나눠 분석시키고, 포트폴리오 매니저가 종합한 판단이 리스크 규칙을 통과한 경우에만 주문을 냅니다. 모의투자(paper)로 검증한 뒤 설정 한 줄만 바꿔 실전으로 전환하는 구조입니다.
⚠️ 면책: 이 프로젝트는 학습·연구용입니다. LLM 판단은 손실을 낼 수 있는 참고 신호일 뿐이며, 투자 손실의 책임은 전적으로 사용자 본인에게 있습니다. 반드시 모의투자로 충분히 검증하고, 실전은 잃어도 되는 소액으로 시작하세요.
- 역할 에이전트 팀 — 차트·수급·공시 분석가, 악마의 변호인, 리스크 관리자, PM 등 12개 역할이 단계별로 협업
- 다중 LLM 지원 — 로컬 Ollama(Hermes, 무료) / OpenAI GPT / Anthropic Claude를 설정으로 교체
- 기술적 분석 — 이동평균·RSI·MACD·볼린저밴드·거래량·지지/저항 자동 계산
- 공시 연동 — DART 전자공시를 코드로 수집해 LLM에 사실만 전달 (환각 방지)
- 리스크 관리 — 단계별 트레일링 분할매도, 하드 손절, 리스크 관리자 거부권, 종목당 비중 한도, 일일 손실 한도, 매매 시간대 제한
- 신뢰도 기반 분할 매수 — 확신이 높을수록 크게 매수
- 그림자 비교 — 실매매는 한 모델이 하고, 다른 모델들은 같은 질문에 판단만 기록해 성과를 나란히 비교
- 분석 도구 — 웹 대시보드 / 엑셀·그래프 리포트 / Word 주간 보고서
- 알림 — 디스코드 / 슬랙 / 텔레그램 (선택)
stock-ai-agent/
├── main.py # 진입점: 장중 주기적 매매 루프
├── config.yaml # 전략·모델·역할·워치리스트 설정
├── .env # API 키 (커밋 안 됨 — .env.example 참고)
├── app/
│ ├── config.py # 설정 로더
│ ├── kis/ # 한국투자증권 API (인증·시세·주문)
│ ├── llm/ # LLM 클라이언트 (ollama / openai / claude) + JSON 파서
│ ├── data/ # 외부 데이터 수집 (DART 전자공시)
│ ├── agents/ # 역할 정의(roles.py)와 단계별 파이프라인(team.py)
│ ├── strategy/ # 지표 계산·프롬프트·시장 맥락·트레일링 분할매도
│ ├── trader.py # 주문 실행·리스크 규칙·자동 청산
│ ├── shadow.py # 그림자 모델 판단 기록
│ └── notifier.py # 디스코드/슬랙/텔레그램 알림
├── performance.py # 실현손익·승률·buy&hold 비교 (CLI)
├── simulation.py # 그림자 판단으로 '그 모델대로 매매했다면' 시뮬레이션
├── dashboard.py # 웹 대시보드(HTML) 생성
├── report.py # 엑셀(.xlsx) + 그래프(.png) 리포트
├── weekly_report.py # Word(.docx) 주간 성과 보고서
├── tests/ # 청산 로직·에이전트 파이프라인 테스트 (API 호출 없음)
└── deploy/ # 오라클 클라우드 배포 스크립트(systemd)
python -m venv .venv
.venv\Scripts\pip install -r requirements.txt # Windows
# source .venv/bin/activate && pip install -r requirements.txt # Linux/Mac로컬 Hermes를 쓰려면 Ollama 설치 후:
ollama pull hermes3:8b1. .env — .env.example을 복사해 키 입력
KIS_APP_KEY=... # KIS 개발자센터(모의투자용) 앱키
KIS_APP_SECRET=...
KIS_ACCOUNT_NO=12345678-01
DART_API_KEY=... # 공시 수집 (선택, opendart.fss.or.kr 무료 발급)
OPENAI_API_KEY=... # provider가 openai / 그림자에 openai일 때
ANTHROPIC_API_KEY=... # provider가 claude / 그림자에 claude일 때2. config.yaml — 매매 모델·역할·전략·워치리스트
mode: paper # paper(모의) | live(실전)
llm:
provider: ollama # ollama | openai | claude
model: hermes3:8b
agents:
enabled: true # false면 기존처럼 단일 LLM 1콜로 동작
roles: [technical, flow, analyst, bear, pm, risk]
shadow: # 판단만 기록하는 비교용 모델 (선택)
- provider: openai
model: gpt-5-mini# Windows는 한글 로그가 깨지지 않게 UTF-8 모드 권장
$env:PYTHONUTF8 = '1'
.venv\Scripts\python main.py # 장중 상시 실행
.venv\Scripts\python main.py --once # 장 시간 무관 1회 실행(테스트)
.venv\Scripts\python main.py --review # 회고 분석만 실행 (장 마감 후 / cron용)장중(평일 09:00~15:20 KST) interval_minutes마다 사이클이 돌고, 장외에는 대기합니다.
한 모델에게 "사라/팔아라"를 통째로 묻는 대신, 역할을 나눠 각자 좁은 질문에만 답하게 하고 마지막에 PM이 종합합니다. 반대론자와 거부권을 넣어 LLM이 근거 없이 매수로 기우는 것을 막습니다.
| 단계 | 역할 | 하는 일 | 호출 빈도 |
|---|---|---|---|
| 1 | market_regime 시장국면 |
지수 → 강세/약세/횡보 | 사이클당 1회 |
| 1 | sector_risk 섹터상관 |
워치리스트 집중 리스크 | 사이클당 1회 |
| 2 | researcher 정보수집 |
DART 공시 요약 | 공시 있을 때만 |
| 2 | technical 차트분석 |
MA·RSI·MACD·볼린저·지지저항 | 종목마다 |
| 2 | flow 수급분석 |
외국인·기관 순매수 | 종목마다 |
| 3 | analyst 종합분석 |
위 셋을 종합 | 종목마다 |
| 4 | bear 악마의변호인 |
반대 논거만 제시 | 종목마다 |
| 5 | entry 매수담당 |
지금 살 타이밍인가 | 미보유일 때만 |
| 5 | exit 매도심사 |
규칙 외 매도 사유가 있나 | 보유일 때만 |
| 6 | pm PM |
최종 buy/sell/hold + 신뢰도 | 종목마다 |
| 7 | risk 리스크관리 |
거부권 (기본값=거부) | 매매를 낼 때만 |
| — | reviewer 회고분석 |
과거 판단 vs 결과 리뷰 | --review 실행 시 |
config.yaml의 agents.roles에서 역할을 빼면 그만큼 빨라집니다. pm을 빼면 analyst 의견이 그대로 행동이 됩니다.
로컬 hermes3:8b는 GPU가 없으면 호출 1건이 30~60초입니다. 워치리스트 3종목 기준:
| 역할 수 | 사이클당 호출 | 예상 소요 |
|---|---|---|
| 6개 | 약 12콜 | 6~12분 |
| 12개 (전체) | 약 20콜 | 10~20분 |
interval_minutes: 30을 넘기지 않도록 두 가지 장치가 있습니다.
- 조건부 실행 — 보유 중이면 매수담당을, 공시가 없으면 정보수집가를 아예 호출하지 않습니다.
- 조기 종료 — 미보유 종목인데 종합분석이 '중립·저확신'이면(
early_exit_confidence) 뒤 단계를 전부 건너뜁니다.
역할을 늘린다고 정확도가 오르지는 않습니다. 8B 모델은 잘게 쪼갤수록 각 역할의 판단 품질이 떨어져 합의가 '노이즈의 평균'이 되기 쉽습니다. 먼저 소수 정예(
technical,bear,pm,risk)로 성과를 확인한 뒤 늘리세요. 로그의[역할명] 12.3s표기로 역할별 소요 시간을 볼 수 있습니다.
공시 수집은 코드가 하고 LLM은 해석만 합니다. 데이터 소스 없이 LLM에게 "이 종목 뉴스"를 물으면
학습 기억으로 없는 실적 발표를 지어냅니다. researcher 역할은 DART에서 실제로 받아온 공시 목록이
있을 때만 호출되고, 프롬프트에서 목록 밖의 내용을 만들지 말라고 명시합니다.
DART_API_KEY가 없으면 이 역할은 그냥 건너뜁니다.
고점을 따라 기준가를 5% 단위로 끌어올리고(trailing_raise_pct), 기준가 대비 하락 구간마다
정해진 비중만 판다. 전량 손절과 달리 일부만 덜어내 반등 여지를 남긴다.
x=100,000 qty=100
103,000 → HOLD | +3.00%
106,000 → HOLD | 기준가 갱신 x=106,000 # +5% 이상 → 래칫 + 하락 단계 재무장
112,000 → HOLD | 기준가 갱신 x=112,000
106,400 → SELL -5% 10주 | 잔여 90주 # 기준가 대비 -5% → 잔여의 10%
100,800 → SELL -10% 18주 | 잔여 72주 # 기준가 대비 -10% → 잔여의 20%
- 매도 비중은 그 시점의 잔여 수량 기준이다(-10%에서 원 보유 100주가 아니라 잔여 90주의 20%).
- 갭하락으로 여러 단계를 한 번에 지나치면 통과한 단계를 모두 같은 사이클에 발동한다.
- 기준가가 평단 +
trailing_arm_pct% 를 넘기 전까지는 발동하지 않는다. 그 구간은 하드 손절이 맡는다. - 단계는
config.yaml의trailing_steps로 자유롭게 바꿀 수 있다.
계산만 확인하려면: python -m app.strategy.trailing
.venv\Scripts\python performance.py # 실현손익·승률·buy&hold 비교(텍스트)
.venv\Scripts\python dashboard.py # 웹 대시보드 생성 후 브라우저 오픈
.venv\Scripts\python report.py # report.xlsx + equity_curve.png
.venv\Scripts\python weekly_report.py # 주간보고서_날짜.docx (--days N 지정 가능)config.yaml의 shadow:에 모델을 추가하면, 실매매 모델과 같은 질문을 받아 판단만 shadow.csv에 기록합니다(주문은 안 냄).
simulation.py가 각 모델이 그 판단대로 매매했다면의 자산 추이를 동일 조건으로 계산해, 대시보드·리포트에서 buy&hold 벤치마크와 나란히 비교합니다.
해석 원칙: "누가 이겼나"보다 **"각 모델이 buy&hold를 이겼나"**를 보세요. 표본이 적으면 결과는 실력이 아니라 시장 방향·운에 좌우됩니다.
지표·청산 로직과 에이전트 파이프라인은 API·Ollama 없이 검증됩니다.
.venv\Scripts\python -m pytest tests/ -q24시간 무인 가동은 deploy/ 참고 (Ubuntu + systemd):
scp -r stock-ai-agent ubuntu@<서버IP>:~/
ssh ubuntu@<서버IP>
cd ~/stock-ai-agent && ./deploy/setup_oracle.sh
sudo systemctl enable --now stock-agent
journalctl -u stock-agent -f모의투자로 최소 2~4주 검증 후:
- KIS에서 실전용 앱키 발급 →
.env교체 config.yaml의mode: livedaily_loss_limit_pct,max_position_pct를 보수적으로 재조정- 반드시 소액으로 시작
- KIS 주문 tr_id는 개편될 수 있음 — 주문 오류 시 개발자센터의 "주식주문(현금)" 최신 tr_id 확인
.env,trades.csv,shadow.csv등 키·개인 데이터는 커밋되지 않습니다(.gitignore)- 네트워크 타임아웃 시 주문 POST가 재전송되어 중복 체결될 수 있습니다 — 실전 전환 전 확인 필요