
전략을 정했으니 이제 매일 알아서 도는 봇을 만들 차례입니다. 오라클 클라우드 서버가 있긴 하지만, 결국 서버 없이 GitHub Actions로 돌리기로 했습니다. 하루에 한 번만 돌면 되는 봇이라 그걸로 충분했습니다.
한눈에 보기
- 평일 아침 9시 10분에 한 번 돌고, 매달 첫 거래일에만 사고팝니다.
- 처음에는 계좌 없이 가상 장부 1,000만 원으로 시작했습니다. 설정 하나로 실제 계좌로 바꿀 수 있습니다.
- 백테스트도 봇 규칙 그대로 다시 돌리니 연 +15.8%가 +14.2%로 조금 낮아졌습니다. 이쪽이 현실에 가깝습니다.
봇이 하는 일
- 어제 종가까지 데이터를 받습니다.
- 혼합 7:3 규칙으로 목표 비중을 계산합니다. (KODEX 200 / 단기채권 / 대형주 모멘텀 종목들)
- 매달 첫 거래일이면 목표 비중에 맞게 사고팝니다. 나머지 날은 평가금액만 기록합니다.
- 텔레그램으로 결과를 보냅니다.
이때는 아직 증권사 계좌가 없어서 가상 장부(1,000만 원)로 시작했습니다. 나중에 계좌를 연결하면 설정 하나만 바꾸면 되도록 만들어 뒀습니다.
백테스트도 봇과 똑같이
앞 편의 혼합 7:3 결과(+15.8%)는 "1년에 한 번 비율 맞추기"로 계산한 숫자였습니다. 실제 봇은 매달 비중을 보고, 차이가 2% 안쪽이면 건드리지 않습니다. 그래서 봇 규칙 그대로 다시 돌렸습니다.
| 연평균 | 최대 낙폭 | 최악의 해 | |
|---|---|---|---|
| 혼합 7:3 (봇 방식) | +14.2% | -44.7% | 2018 -6.3% |
조금 낮아졌지만, 실제로 돌아갈 방식으로 계산한 숫자라 이쪽을 기준으로 삼기로 했습니다. 이 시리즈에서 이후에 나오는 "지금 전략 +14.2%"가 이 숫자입니다.
오라클 서버를 두고 왜 GitHub Actions인가
- 이 전략은 하루에 한 번만 돌면 됩니다. GitHub Actions는 정해진 시간에 코드를 공짜로 돌려 줍니다.
- 서버를 관리할 필요가 없고, 실행할 때마다 기록이 남아서 뭐가 잘못됐는지 보기 쉽습니다.
장중 내내 시세를 봐야 하는 전략을 쓰게 되면 그때 오라클 서버로 옮길 생각입니다. 서버용 설정 파일은 미리 만들어 뒀습니다.
GitHub Actions의 schedule(cron)은 UTC 기준입니다. 한국 시간 9시 10분이면 10 0 * * 1-5로 적어야 합니다. 그리고 정각 근처에는 실행이 몇 분씩 밀리기도 하니 장 시작 직후처럼 몇 분이 중요한 일에는 맞지 않습니다.
첫 시험 실행
시험으로 돌려 보니, 마침 KODEX 200이 200일선 아래였습니다. 그래서 봇은 70%를 단기채권에 넣고 30%는 현금으로 뒀습니다. 하락장이라 주식은 한 주도 사지 않은 겁니다. 처음엔 "왜 아무것도 안 사지?" 싶었는데, 규칙대로 정확히 움직인 결과였습니다.
코드로 보면
봇 실행은 GitHub Actions 워크플로 파일 하나가 전부입니다. 키는 전부 Secrets에서 환경 변수로 넘기고, 하루 실행 기록은 별도 브랜치에 저장해서 다음 날 이어 씁니다.
name: kis-bot
on:
schedule:
- cron: "10 0 * * 1-5" # 평일 09:10 (한국시간)
workflow_dispatch: # 버튼으로 수동 실행도 가능
jobs:
run:
runs-on: ubuntu-latest
env:
KIS_ENV: demo # 모의투자
KIS_APP_KEY: ${{ secrets.KIS_APP_KEY }}
KIS_APP_SECRET: ${{ secrets.KIS_APP_SECRET }}
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install -e .
- run: quant-pilot run --broker kis
봇의 판단은 "이번 달에 이미 리밸런싱했나?"부터 봅니다. 했으면 악재로 걸린 종목만 팔고 끝내고, 안 했으면 목표 비중을 계산해서 주문 목록을 만듭니다. 목표와 2% 이상 차이 날 때만 주문하도록 해서 자잘한 매매를 줄였습니다.
BAND = 0.02 # 목표 비중과 2% 이상 차이 날 때만 주문
def decide(panel, universe, positions, cash, last_rebalance, today, strategy, blocked):
"""blocked(악재 종목)는 후보에서 빼고, 갖고 있으면 리밸런싱 날이 아니어도 판다."""
i = len(panel.days) - 1
prices = {s: c[i] for s, c in panel.close.items() if c[i]}
exits = {s: blocked[s] for s, q in positions.items() if q > 0 and s in blocked}
if last_rebalance == today.strftime("%Y-%m"):
orders = [Order(s, -positions[s]) for s in exits if s in prices]
return Decision(rebalanced=False, orders=orders)
allowed = [s for s in universe if s not in blocked]
targets = strategy.target_weights(panel, i, allowed, set(positions) - set(blocked))
orders = plan_orders(targets, positions, prices, cash, BAND)
return Decision(rebalanced=True, orders=orders)
다음 편에서는 공시와 뉴스를 읽어서 악재가 있는 종목을 피하게 만듭니다.
주식 자동매매 봇 만들기
'개발 > 주식 자동매매' 카테고리의 다른 글
| 주식 자동매매 봇 만들기 6편: 텔레그램 알림 봇 만들기와 한국투자증권 모의투자 연결 (0) | 2026.10.15 |
|---|---|
| 주식 자동매매 봇 만들기 5편: 공시와 뉴스로 악재 종목 거르기, 네이버 뉴스 검색 API 발급 (0) | 2026.10.15 |
| 주식 자동매매 봇 만들기 3편: 8가지 전략 비교, 그리고 연 +32%가 가짜였던 이유 (0) | 2026.10.15 |
| 주식 자동매매 봇 만들기 2편: 모멘텀 전략 10년 백테스트, 결과는 지수도 못 이김 (0) | 2026.10.15 |
| 주식 자동매매 봇 만들기 1편: 국내주식·한국투자증권·Python으로 정한 이유 (0) | 2026.10.15 |