자동매매 봇 구축 프롬프트 가이드

AutoTrade Bot Build Prompt Guide

AI 코딩 어시스턴트에 단계별로 입력하여 모의투자 자동매매 봇을 처음부터 구축
Build a paper-trading bot from scratch by feeding step-by-step prompts to an AI coding assistant
2026-04-20 구축 가이드 Build Guide KIS API Python
개념 Concept 학습 원리 Theory 구축 프롬프트 Build Prompt
💡

사용법: 이 문서의 프롬프트 블록(보라색 박스)을 AI 코딩 어시스턴트(Claude, Cursor 등)에 순서대로 붙여넣으세요. 각 단계를 완료한 후 다음 단계로 넘어갑니다.

How to use: Paste each prompt block (purple box) into your AI coding assistant (Claude, Cursor, etc.) in order. Complete each step before moving to the next.

📚

코딩 원칙 (Step 1~8 공통 적용): 각 프롬프트 실행 전 AI에 다음 규칙을 먼저 선언하면 품질이 높아집니다. "함수는 단일 책임, 레이어 단방향 의존(orchestrator→agent→service→client), async 우선, 외부 호출마다 timeout 명시, 에러는 조용히 삼키지 말고 log+반환."

Coding principles (applied to Steps 1–8): Before running each prompt, declare these rules to your AI for higher quality output. "Single responsibility per function, one-way layer dependency (orchestrator→agent→service→client), async-first, explicit timeout on every external call, never swallow errors silently — log and return."

사전 준비 (프롬프트 실행 전 완료)

Prerequisites (Complete Before Running Prompts)

1. 한국투자증권 계좌 개설 + API 발급 Open KIS Account + Get API Keys

  1. 한국투자증권 계좌 개설 (비대면 가능)
  2. Open an account at Korea Investment & Securities (online)
  3. KIS Developers 접속 → 회원가입
  4. Visit KIS Developers → Sign up
  5. 모의투자 신청 → 모의투자 APP KEY / SECRET 발급
  6. Apply for paper trading → Get paper trading APP KEY / SECRET
  7. 발급된 값 메모: APP_KEY (32자리), APP_SECRET (긴 영숫자+특수문자), ACCOUNT_NO (8자리)
  8. Note down: APP_KEY (32 chars), APP_SECRET (long alphanumeric), ACCOUNT_NO (8 digits)

2. 설치할 것 Things to Install

항목Item 설치 방법Installation
Python 3.11+ python.org
Git git-scm.com
코드 에디터Code Editor VS Code 권장VS Code recommended
PostgreSQL (선택)PostgreSQL (optional) Neon 무료 클라우드 DB 권장 Neon free cloud DB recommended

3. KIS API 기본 정보 KIS API Reference

항목Endpoint 모의투자Paper Trading
REST URL https://openapivts.koreainvestment.com:29443
토큰 발급Token POST /oauth2/tokenP
현재가 조회Current Price GET /uapi/.../inquire-price (FHKST01010100)
잔고 조회Balance GET /uapi/.../inquire-balance (VTTC8434R)
매수Buy POST /uapi/.../order-cash (VTTC0802U)
매도Sell POST /uapi/.../order-cash (VTTC0801U)

Step 1 프로젝트 생성 + KIS 연결Project Setup + KIS Connection

아래 프롬프트를 AI에 입력하세요:

Paste the following prompt into your AI assistant:

Python 자동매매 봇 프로젝트를 만들어줘.

폴더 구조:
autotrade/
├── main.py          # 진입점
├── kis_api.py       # KIS API 클라이언트
├── strategy.py      # 매매 전략
├── config.py        # 설정
├── requirements.txt
└── .env

config.py:
- .env에서 APP_KEY, APP_SECRET, ACCOUNT_NO 로드
- BASE_URL = "https://openapivts.koreainvestment.com:29443" (모의투자)

kis_api.py에 구현할 함수:
1. get_token() → POST /oauth2/tokenP로 토큰 발급, 메모리 캐시
2. get_price(symbol) → 현재가 조회, 숫자 반환
3. get_balance() → {cash: 현금, holdings: [{symbol, qty, avg_price}]}
4. buy(symbol, qty) → 매수 주문 (ord_dvsn="01" 시장가)
5. sell(symbol, qty) → 매도 주문

모든 API 호출에 공통 헤더:
{
  "authorization": "Bearer {token}",
  "appkey": APP_KEY,
  "appsecret": APP_SECRET,
  "tr_id": "{거래ID}",
  "custtype": "P",
  "content-type": "application/json"
}

잔고 조회 파라미터:
CANO={계좌번호}, ACNT_PRDT_CD="01", INQR_DVSN="01"

main.py:
- 토큰 발급 테스트
- 삼성전자(005930) 현재가 조회
- 잔고 조회
- 결과 출력

requirements.txt:
requests
python-dotenv

.env 예시:
APP_KEY=여기에_입력
APP_SECRET=여기에_입력
ACCOUNT_NO=여기에_입력

⚠️ 주의: .env 파일은 절대 git에 커밋하지 마세요. .gitignore에 .env를 반드시 추가하세요.
Create a Python automated trading bot project.

Folder structure:
autotrade/
├── main.py          # entry point
├── kis_api.py       # KIS API client
├── strategy.py      # trading strategy
├── config.py        # configuration
├── requirements.txt
└── .env

config.py:
- Load APP_KEY, APP_SECRET, ACCOUNT_NO from .env
- BASE_URL = "https://openapivts.koreainvestment.com:29443" (paper trading)

Functions to implement in kis_api.py:
1. get_token() → issue token via POST /oauth2/tokenP, cache in memory
2. get_price(symbol) → fetch current price, return as number
3. get_balance() → {cash: available cash, holdings: [{symbol, qty, avg_price}]}
4. buy(symbol, qty) → market buy order (ord_dvsn="01")
5. sell(symbol, qty) → market sell order

Common headers for all API calls:
{
  "authorization": "Bearer {token}",
  "appkey": APP_KEY,
  "appsecret": APP_SECRET,
  "tr_id": "{transaction_id}",
  "custtype": "P",
  "content-type": "application/json"
}

Balance query params:
CANO={account_number}, ACNT_PRDT_CD="01", INQR_DVSN="01"

main.py:
- Test token issuance
- Fetch current price of Samsung Electronics (005930)
- Fetch balance
- Print results

requirements.txt:
requests
python-dotenv

.env example:
APP_KEY=enter_here
APP_SECRET=enter_here
ACCOUNT_NO=enter_here

⚠️ Warning: Never commit .env to git. Add .env to .gitignore.
python main.py 실행 → 삼성전자 현재가 + 잔고 출력되면 성공
Run python main.py → Success if Samsung Electronics price + balance is printed

Step 2 기본 매매 전략Basic Trading Strategy

RSI(14일) 기반 전략을 구현합니다.

Implement an RSI(14-day) based strategy.

strategy.py에 간단한 매매 전략을 만들어줘.

RSI(14일) 기반 전략:
- RSI < 30 → 매수 (과매도)
- RSI > 70 → 매도 (과매수)
- 그 외 → 대기

구현할 함수:
1. calc_rsi(closes: list, period=14) → float
   - Wilder's RSI 계산
   - 데이터 부족 시 50.0 반환

2. decide(symbol, closes, holdings) → "BUY" | "SELL" | "HOLD"
   - RSI 계산
   - 매수: RSI < 30 AND 미보유
   - 매도: RSI > 70 AND 보유 중
   - 그 외: HOLD

일봉 데이터는 kis_api.py에 함수 추가:
get_daily_prices(symbol, days=30):
  → GET /uapi/domestic-stock/v1/quotations/inquire-daily-itemchartprice
  → tr_id: FHKST03010100
  → 종가 리스트 반환
Create a simple trading strategy in strategy.py.

RSI(14-day) based strategy:
- RSI < 30 → BUY (oversold)
- RSI > 70 → SELL (overbought)
- Otherwise → HOLD

Functions to implement:
1. calc_rsi(closes: list, period=14) → float
   - Wilder's RSI calculation
   - Return 50.0 if insufficient data

2. decide(symbol, closes, holdings) → "BUY" | "SELL" | "HOLD"
   - Calculate RSI
   - BUY: RSI < 30 AND not currently holding
   - SELL: RSI > 70 AND currently holding
   - Otherwise: HOLD

Add to kis_api.py:
get_daily_prices(symbol, days=30):
  → GET /uapi/domestic-stock/v1/quotations/inquire-daily-itemchartprice
  → tr_id: FHKST03010100
  → return list of closing prices
💡

RSI 기초: RSI(Relative Strength Index)는 0~100 사이의 값으로, 30 이하는 과매도(매수 신호), 70 이상은 과매수(매도 신호)를 의미합니다.

RSI basics: RSI (Relative Strength Index) ranges 0-100. Below 30 = oversold (buy signal), above 70 = overbought (sell signal).

Step 3 자동 실행 (스케줄러)Auto Execution (Scheduler)

main.py에 스케줄러를 추가해서 자동으로 매매하게 만들어줘.

pip install apscheduler 추가.

동작:
1. 프로그램 시작 시 토큰 발급
2. 평일 09:05~15:25 사이, 5분마다 run_cycle() 실행
3. run_cycle():
   a. 매매 대상 종목 리스트 (예: ["005930", "035720", "000660"])
   b. 각 종목별 일봉 조회 → RSI 계산 → 매매 판단
   c. BUY → 잔고 확인 후 매수 (1주)
   d. SELL → 보유 중이면 매도
   e. 결과 로그 출력
4. 장 외 시간에는 실행 안 함

장 개장 판단:
- 평일(월~금) AND 09:00~15:30 사이
- 공휴일 체크는 생략 (교육용)

로그 형식:
[09:05] 005930 RSI=28.3 → BUY 1주 @ 72,300원
[09:05] 035720 RSI=55.1 → HOLD
Add a scheduler to main.py for automatic trading.

Add: pip install apscheduler

Behavior:
1. Issue token on program start
2. Run run_cycle() every 5 minutes on weekdays between 09:05–15:25 KST
3. run_cycle():
   a. Target symbol list (e.g. ["005930", "035720", "000660"])
   b. Per symbol: fetch daily prices → calculate RSI → make trade decision
   c. BUY → check balance, buy 1 share
   d. SELL → sell if currently holding
   e. Log result
4. Skip execution outside market hours

Market hours check:
- Weekday (Mon–Fri) AND between 09:00–15:30 KST
- Skip holiday check (educational purposes)

Log format:
[09:05] 005930 RSI=28.3 → BUY 1 share @ 72,300
[09:05] 035720 RSI=55.1 → HOLD
모의투자 시간(평일 09:00~15:30) 내 실행 → 로그 출력되면 성공
Run during paper trading hours (weekdays 09:00-15:30) → Success if logs are printed

Step 4 리스크 관리Risk Management

수익보다 생존이 먼저입니다. 리스크 관리 모듈을 추가합니다.

Survival comes before profit. Add a risk management module.

리스크 관리 모듈을 추가해줘. risk.py 파일로.

규칙:
1. 최대 보유 종목 수: 5개
2. 종목당 최대 투자 금액: 총자산의 20%
3. 손절: 매입가 대비 -3% 이하면 즉시 매도
4. 일일 손실 한도: 총자산의 -5% 이하면 당일 매매 중단

구현:
- can_buy(cash, holdings, max_positions=5) → bool
- calc_qty(price, cash, max_pct=0.2) → int (매수 가능 수량)
- check_stop_loss(holding, current_price, stop_pct=-0.03) → bool
- check_daily_limit(daily_pnl, total_assets, limit_pct=-0.05) → bool

run_cycle()에 적용:
- 매수 전: can_buy + calc_qty
- 매 사이클: 보유 종목 손절 체크
- 일일 한도 초과 시: 사이클 스킵
Add a risk management module as risk.py.

Rules:
1. Max positions held at once: 5
2. Max investment per symbol: 20% of total assets
3. Stop-loss: sell immediately if down -3% from entry price
4. Daily loss limit: stop trading for the day if daily P&L reaches -5% of total assets

Implement:
- can_buy(cash, holdings, max_positions=5) → bool
- calc_qty(price, cash, max_pct=0.2) → int (number of shares to buy)
- check_stop_loss(holding, current_price, stop_pct=-0.03) → bool
- check_daily_limit(daily_pnl, total_assets, limit_pct=-0.05) → bool

Apply in run_cycle():
- Before buying: can_buy + calc_qty
- Each cycle: check stop-loss for all holdings
- Skip cycle if daily limit exceeded
⚠️

필수 규칙: "한 번의 거래로 전체 자본의 2% 이상을 잃지 않는다." 이 단순한 규칙 하나만 지켜도 파산 확률이 극적으로 줄어듭니다.

Essential rule: "Never lose more than 2% of total capital on a single trade." This single rule dramatically reduces the probability of ruin.

Step 5 데이터 저장 (DB)Data Storage (DB)

거래 기록을 PostgreSQL에 저장해줘.

Neon DB 사용 (무료):
1. neon.tech 가입 → 프로젝트 생성 → Connection string 복사
2. .env에 DATABASE_URL 추가

pip install psycopg2-binary 추가.

db.py에 구현:
1. init_tables() — 테이블 생성:
   trades: id, ts, symbol, action(BUY/SELL), qty, price, pnl_amt
   balance_snapshot: id(1), cash, holdings(JSON), updated_at

2. log_trade(symbol, action, qty, price, pnl_amt=0)
3. save_balance(cash, holdings_list)
4. get_today_trades() → 오늘 거래 내역
5. get_today_pnl() → 오늘 총 손익

run_cycle()에서:
- 매수/매도 시 log_trade() 호출
- 사이클 끝에 save_balance() 호출
- 시작 시 init_tables() 호출
Save trade records to PostgreSQL.

Using Neon DB (free tier):
1. Sign up at neon.tech → create project → copy Connection string
2. Add DATABASE_URL to .env

Add: pip install psycopg2-binary

Implement in db.py:
1. init_tables() — create tables:
   trades: id, ts, symbol, action(BUY/SELL), qty, price, pnl_amt
   balance_snapshot: id(1), cash, holdings(JSON), updated_at

2. log_trade(symbol, action, qty, price, pnl_amt=0)
3. save_balance(cash, holdings_list)
4. get_today_trades() → today's trade history
5. get_today_pnl() → today's total P&L

In run_cycle():
- Call log_trade() on every buy/sell
- Call save_balance() at end of each cycle
- Call init_tables() on startup

Neon DB: 무료 플랜으로도 충분합니다. 가입 후 Connection string을 .envDATABASE_URL에 추가하세요.

Neon DB: The free plan is sufficient. After signup, add the connection string to DATABASE_URL in your .env.

Step 6 알림Notifications

Telegram 알림을 추가해줘.

준비:
1. @BotFather에서 봇 생성 → BOT_TOKEN
2. 봇에 메시지 보내고 → /getUpdates로 CHAT_ID 확인
3. .env에 추가:
   TELEGRAM_BOT_TOKEN=...
   TELEGRAM_CHAT_ID=...

notify.py:
- send(message) → Telegram으로 메시지 전송

알림 시점:
- 매수 시: "[BUY] 005930 삼성전자 1주 @ 72,300원"
- 매도 시: "[SELL] 005930 삼성전자 1주 @ 74,100원 (+2.5%)"
- 봇 시작: "자동매매 시작 — 3종목"
- 에러 발생: "오류: {메시지}"
Add Telegram notifications.

Setup:
1. Create a bot via @BotFather → get BOT_TOKEN
2. Send a message to the bot → use /getUpdates to find CHAT_ID
3. Add to .env:
   TELEGRAM_BOT_TOKEN=...
   TELEGRAM_CHAT_ID=...

notify.py:
- send(message) → send message to Telegram

Send notifications when:
- Buy: "[BUY] 005930 Samsung 1 share @ 72,300"
- Sell: "[SELL] 005930 Samsung 1 share @ 74,100 (+2.5%)"
- Bot starts: "AutoTrade started — 3 symbols"
- Error: "Error: {message}"
💡

Telegram Bot 만들기: Telegram에서 @BotFather를 검색하고 /newbot 명령어를 입력하면 됩니다. 봇 이름과 username을 지정하면 BOT_TOKEN이 발급됩니다.

Creating a Telegram Bot: Search for @BotFather in Telegram and enter the /newbot command. Specify a name and username to receive your BOT_TOKEN.

Step 7 모니터링 대시보드Monitoring Dashboard

간단한 웹 대시보드를 만들어줘. Python Flask 사용.

pip install flask 추가.

dashboard.py:
- GET / → HTML 페이지 (잔고 + 오늘 거래 + 손익)
- GET /api/status → JSON (잔고, 포지션, 오늘 PnL)

HTML 페이지 내용 (인라인 CSS, 단일 파일):
- 계좌 현황: 예수금, 보유평가, 총자산
- 보유 종목 테이블: 종목, 수량, 매입가, 현재가, 수익률
- 오늘 거래 내역: 시각, 종목, 매수/매도, 수량, 가격
- 30초마다 자동 새로고침

Flask 서버를 별도 스레드로 실행 (스케줄러와 병행):
- main.py에서 threading으로 시작
- 포트: 8080
Create a simple web dashboard using Python Flask.

Add: pip install flask

dashboard.py:
- GET / → HTML page (balance + today's trades + P&L)
- GET /api/status → JSON (balance, positions, today's PnL)

HTML page content (inline CSS, single file):
- Account summary: cash, holdings value, total assets
- Holdings table: symbol, qty, avg price, current price, return %
- Today's trades: time, symbol, buy/sell, qty, price
- Auto-refresh every 30 seconds

Run Flask server in a separate thread (alongside the scheduler):
- Start via threading in main.py
- Port: 8080

Step 8 배포 (선택)Deployment (Optional)

Railway에 봇을 배포해줘.

준비:
1. railway.com 가입
2. GitHub에 프로젝트 push
3. Railway에서 GitHub 레포 연결

필요한 파일:
- Procfile: "web: python main.py"
- runtime.txt: "python-3.11.x"

Railway 환경변수 설정:
- APP_KEY, APP_SECRET, ACCOUNT_NO
- DATABASE_URL (Neon)
- TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID
- PORT=8080

배포 후 확인:
- Railway 로그에서 "자동매매 시작" 확인
- 대시보드 URL 접속
- Telegram 알림 수신
Deploy the bot to Railway.

Setup:
1. Sign up at railway.com
2. Push the project to GitHub
3. Connect the GitHub repo in Railway

Required files:
- Procfile: "web: python main.py"
- runtime.txt: "python-3.11.x"

Set Railway environment variables:
- APP_KEY, APP_SECRET, ACCOUNT_NO
- DATABASE_URL (Neon)
- TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID
- PORT=8080

After deployment, verify:
- Confirm "AutoTrade started" in Railway logs
- Access the dashboard URL
- Receive Telegram notification

배포 체크리스트: (1) 환경변수 전부 설정, (2) Railway 로그에서 정상 시작 확인, (3) 대시보드 접속 확인, (4) Telegram 알림 수신 확인.

Deploy checklist: (1) All env vars configured, (2) Confirm normal startup in Railway logs, (3) Verify dashboard access, (4) Verify Telegram alerts.

Step 9 에이전트 구조로 리팩터링Refactor to Agent Architecture

Step 1~8로 만든 단일 스크립트를 독립적으로 수정·교체 가능한 에이전트 구조로 전환합니다. 이 단계부터 봇이 운영 수준의 견고함을 갖춥니다.

Refactor the single-script bot from Steps 1–8 into an agent structure where each module can be modified and replaced independently. From this step, the bot gains production-level resilience.

현재 main.py / kis_api.py / strategy.py / risk.py / db.py를 에이전트 구조로 리팩터링해줘.

목표: 각 모듈을 독립적으로 수정해도 다른 모듈에 영향을 주지 않는 구조.

## 레이어 규칙
- 호출 방향: orchestrator → agent → service → client → storage
- 역방향 호출 금지. 레이어 건너뛰기 금지.

## 1. FinanceAgent (finance_agent.py)
잔고를 단일 소스로 관리 — KIS API 중복 호출 방지.

class FinanceAgent:
    def __init__(self, kis_client):
        # kis_client 주입 (의존성 주입 — hard import 금지)
        self._kis = kis_client
        self._cache = {"cash": 0, "holdings": [], "_ts": 0.0}
        self._pending = 0  # 매수 예약 중 금액
        self._lock = threading.Lock()
        TTL = 60  # 초

    def get_balance(self) -> dict:
        # TTL 초과 시 KIS 재조회, 캐시 반환

    def get_available_cash(self) -> float:
        # 가용 현금 = 캐시 현금 - pending

    def cache_age(self) -> float:
        # 마지막 갱신 후 경과 시간(초) — 외부에서 _cache 직접 접근 금지

    def commit_buy(self, amount: int):
        # 매수 직후 캐시 즉시 차감

    def commit_sell(self, proceeds: int):
        # 매도 완료 후 캐시 환원

    def health(self) -> dict:
        # {"cash": ..., "holdings": N, "age_s": ..., "pending": ...}

## 2. TradingManager (trading_manager.py)
전략 실행 + circuit breaker + 병렬 매수·매도.

class TradingManager:
    def __init__(self, kis_client, finance_agent):
        # 의존성 주입
        self._kis = kis_client
        self._fa = finance_agent
        self._health = {
            "buy": {"fails": 0, "tripped": False},
            "sell": {"fails": 0, "tripped": False},
        }
        CIRCUIT_THRESHOLD = 3

    def start(self):
        # 스케줄러 시작, DB 초기화

    def run_cycle(self):
        # ThreadPoolExecutor(max_workers=2)로 buy + sell 병렬 실행
        # 각 future에 timeout=60 설정

    def _run_buy_safe(self):
        # circuit tripped이면 skip
        # 성공 시 fails 리셋, 실패 시 _trip() 호출

    def _run_sell_safe(self):
        # sell은 포지션 보호 우선 — tripped이어도 강제 실행 후 리셋

    def _trip(self, module: str, err: Exception):
        # fails 증가, threshold 초과 시 tripped=True + Telegram 알림

    def get_health(self) -> dict:
        # 각 모듈 circuit 상태 반환

    def smoke_test(self) -> dict:
        # {"ok": bool, "checks": [{"name": ..., "ok": ..., "detail": ...}]}
        # KIS 연결, DB 연결, 잔고 조회 가능 여부 2초 안에 확인

    def shutdown(self):
        # 스케줄러 종료, 자원 해제

## 3. main.py 수정
- FinanceAgent 먼저 초기화
- TradingManager에 kis_client + finance_agent 주입
- 시작 시 smoke_test() 실행 → 실패 항목 Telegram 알림
- manager.start() → 스케줄러 진입

## 에러 처리 규칙
- except Exception: pass 금지
- 에러 메시지에 원인 포함: f"balance fetch failed: status={resp.status}"
- 외부 호출마다 try/except + log

## dry_run 모드
환경변수 DRY_RUN=true 시:
- 실제 주문 대신 "DRY_RUN: would BUY 005930 1주" 로그 출력
- 잔고 조회·가격 조회는 실제 실행 (로직 검증용)
- Telegram은 "(dry_run)" 접두사 추가
Refactor the current main.py / kis_api.py / strategy.py / risk.py / db.py into an agent architecture.

Goal: modify any module independently without affecting the others.

## Layer rules
- Call direction: orchestrator → agent → service → client → storage
- No reverse calls. No skipping layers.

## 1. FinanceAgent (finance_agent.py)
Single source of truth for balance — prevents duplicate KIS API calls.

class FinanceAgent:
    def __init__(self, kis_client):
        # inject kis_client (dependency injection — no hard imports)
        self._kis = kis_client
        self._cache = {"cash": 0, "holdings": [], "_ts": 0.0}
        self._pending = 0  # amount reserved for pending buys
        self._lock = threading.Lock()
        TTL = 60  # seconds

    def get_balance(self) -> dict:
        # refresh from KIS if TTL exceeded, return cache

    def get_available_cash(self) -> float:
        # available cash = cached cash - pending

    def cache_age(self) -> float:
        # seconds since last refresh — external code must not access _cache directly

    def commit_buy(self, amount: int):
        # immediately deduct from cache on buy

    def commit_sell(self, proceeds: int):
        # restore cache on sell completion

    def health(self) -> dict:
        # {"cash": ..., "holdings": N, "age_s": ..., "pending": ...}

## 2. TradingManager (trading_manager.py)
Strategy execution + circuit breaker + parallel buy/sell.

class TradingManager:
    def __init__(self, kis_client, finance_agent):
        # dependency injection
        self._kis = kis_client
        self._fa = finance_agent
        self._health = {
            "buy": {"fails": 0, "tripped": False},
            "sell": {"fails": 0, "tripped": False},
        }
        CIRCUIT_THRESHOLD = 3

    def start(self):
        # start scheduler, initialize DB

    def run_cycle(self):
        # run buy + sell in parallel via ThreadPoolExecutor(max_workers=2)
        # set timeout=60 on each future

    def _run_buy_safe(self):
        # skip if circuit tripped
        # reset fails on success, call _trip() on failure

    def _run_sell_safe(self):
        # sell prioritizes position protection — force-run even if tripped, then reset

    def _trip(self, module: str, err: Exception):
        # increment fails, set tripped=True + send Telegram alert at threshold

    def get_health(self) -> dict:
        # return circuit state for each module

    def smoke_test(self) -> dict:
        # {"ok": bool, "checks": [{"name": ..., "ok": ..., "detail": ...}]}
        # verify KIS connection, DB connection, balance fetch within 2 seconds

    def shutdown(self):
        # stop scheduler, release resources

## 3. Update main.py
- Initialize FinanceAgent first
- Inject kis_client + finance_agent into TradingManager
- Run smoke_test() on startup → send failures to Telegram
- manager.start() → enter scheduler loop

## Error handling rules
- No bare except Exception: pass
- Include cause in error messages: f"balance fetch failed: status={resp.status}"
- Wrap every external call with try/except + log

## dry_run mode
When DRY_RUN=true env var is set:
- Log "DRY_RUN: would BUY 005930 1 share" instead of placing real orders
- Balance/price fetches still execute (for logic verification)
- Prefix Telegram messages with "(dry_run)"
⚠️

독립 수정 테스트: 리팩터링 완료 후 "FinanceAgent를 mock으로 교체해도 TradingManager 코드를 수정하지 않아도 되나?"를 확인하세요. 수정이 필요하다면 레이어 경계가 잘못된 것입니다.

Independence test: After refactoring, verify: "Can I replace FinanceAgent with a mock without modifying TradingManager?" If not, the layer boundary is wrong.

방금 만든 TradingManager의 smoke_test()를 실행해서 시스템 상태를 확인해줘.

출력 형식:
[smoke_test]
  ✅ KIS connection — 응답 200
  ✅ DB connection — trades 테이블 존재
  ✅ balance fetch — 예수금 5,000,000원
  ❌ telegram — 연결 실패: timeout

전체 결과: 3/4 통과

실패 항목이 있으면 Telegram으로 요약 전송 (Telegram 자체가 실패면 콘솔에만 출력).
Run the smoke_test() on the TradingManager we just built to verify system health.

Output format:
[smoke_test]
  ✅ KIS connection — status 200
  ✅ DB connection — trades table exists
  ✅ balance fetch — cash 5,000,000
  ❌ telegram — connection failed: timeout

Overall: 3/4 passed

Send a summary to Telegram if any checks fail (if Telegram itself fails, print to console only).
DRY_RUN=true python main.py 실행 → 실제 주문 없이 전체 사이클 로그 출력되면 성공
Run DRY_RUN=true python main.py → Success if full cycle logs print without real orders

완성 후 확장 아이디어

Extension Ideas After Completion

단계Phase 내용Description
전략 추가Add Strategies 이동평균 크로스, MACD, 볼린저 밴드 Moving average crossover, MACD, Bollinger Bands
ML 도입Introduce ML scikit-learn으로 매수/매도 확률 예측 Predict buy/sell probability with scikit-learn
LLM 활용Use LLMs Gemini/Claude API로 뉴스 분석 → 매매 판단 보조 News analysis via Gemini/Claude API → supplemental trading judgment
실시간 시세Real-time Quotes WebSocket으로 체결가 수신 Receive tick data via WebSocket
단타 모드Day Trading Mode 5분봉 기반 변동성 돌파 전략 5-min candle volatility breakout strategy
실전 전환Go Live 모의투자 검증 후 실전 URL/KEY로 교체 After paper trading validation, switch to live URL/KEY
에이전트 아키텍처Agent Architecture Step 9 — 잔고 캐시(FinanceAgent) + circuit breaker + 병렬 실행으로 운영 수준 견고함 확보 Step 9 — Balance cache (FinanceAgent) + circuit breaker + parallel execution for production-grade resilience
모듈 독립화Module Independence smoke_test() + dry_run 모드 추가 → 각 모듈을 mock으로 교체 가능한 구조. 디버깅 시간 대폭 단축 Add smoke_test() + dry_run mode → each module swappable with a mock. Dramatically reduces debugging time

트러블슈팅

Troubleshooting

증상Symptom 원인Cause 해결Solution
EGW00001 일시적 서버 오류Temporary server error 3초 후 재시도Retry after 3 seconds
기간이 만료된 tokenExpired token 토큰 24시간 만료Token expires after 24h 토큰 재발급Re-issue token
잔고 0Balance 0 모의투자 초기 자금 미설정Paper trading initial funds not set KIS 홈페이지에서 모의투자 신청 확인Verify paper trading registration on KIS website
주문 실패Order failed 장 외 시간Outside market hours 09:00~15:30 확인Verify 09:00-15:30 KST
RSI 50 고정RSI stuck at 50 일봉 데이터 부족Insufficient daily candle data 15일 이상 데이터 필요Need 15+ days of data

.env 전체 예시

.env Full Example

# KIS 모의투자
APP_KEY=PSxxxxxxxxxxxxxxxxxx
APP_SECRET=xxxxxxxxxxxxxxxxxxxx
ACCOUNT_NO=50xxxxxx

# PostgreSQL (Neon)
DATABASE_URL=postgresql://user:pass@host/db?sslmode=require

# Telegram
TELEGRAM_BOT_TOKEN=123456:ABCxxxxx
TELEGRAM_CHAT_ID=123456789
# KIS Paper Trading
APP_KEY=PSxxxxxxxxxxxxxxxxxx
APP_SECRET=xxxxxxxxxxxxxxxxxxxx
ACCOUNT_NO=50xxxxxx

# PostgreSQL (Neon)
DATABASE_URL=postgresql://user:pass@host/db?sslmode=require

# Telegram
TELEGRAM_BOT_TOKEN=123456:ABCxxxxx
TELEGRAM_CHAT_ID=123456789
⚠️

보안: .env 파일은 절대 Git에 커밋하지 마세요. .gitignore.env를 반드시 추가하세요.

Security: Never commit your .env file to Git. Always add .env to .gitignore.

이 문서는 교육 목적입니다. 실전 투자 전 충분한 모의투자 검증을 권장합니다.

This document is for educational purposes. Thorough paper trading validation is recommended before live trading.