Background

I’ve been actively trading Korean stocks on the KOSPI and KOSDAQ for years. After spending too many hours watching charts, I decided to automate part of my trading strategy using the KIS API.

This post shares what I built, what worked, and what I learned — including mistakes.

The Strategy

My bot focuses on momentum scalping on KOSPI stocks:

  • Screen for stocks with high volume breakouts in the first 30 minutes
  • Enter positions with defined risk (1-2% of account per trade)
  • Exit on target profit or stop loss
  • Maximum 3 concurrent positions

System Architecture

GitHub Actions (scheduler)
    ↓
Python Script
    ↓ ↓ ↓
KIS API    pykrx    Telegram Bot
(trading)  (data)   (alerts)

Core Components

1. Market Scanner

from pykrx import stock
import pandas as pd

def scan_breakouts(min_volume_ratio=2.0):
    """Find stocks with unusual volume"""
    today = stock.get_market_ticker_list(market="KOSPI")
    breakouts = []
    
    for ticker in today[:100]:  # Limit for demo
        try:
            df = stock.get_market_ohlcv_by_date(
                prev_date, today_date, ticker
            )
            if len(df) < 20:
                continue
                
            avg_volume = df["거래량"].mean()
            current_volume = df["거래량"].iloc[-1]
            
            if current_volume > avg_volume * min_volume_ratio:
                breakouts.append({
                    "ticker": ticker,
                    "volume_ratio": current_volume / avg_volume
                })
        except:
            continue
    
    return sorted(breakouts, key=lambda x: x["volume_ratio"], reverse=True)

2. Order Execution (KIS API)

def place_market_order(ticker, quantity, order_type="buy", token=None):
    url = "https://openapi.koreainvestment.com:9443/uapi/domestic-stock/v1/trading/order-cash"
    
    tr_id = "TTTC0802U" if order_type == "buy" else "TTTC0801U"
    
    headers = {
        "authorization": f"Bearer {token}",
        "appkey": APP_KEY,
        "appsecret": APP_SECRET,
        "tr_id": tr_id
    }
    
    body = {
        "CANO": ACCOUNT_NO,
        "ACNT_PRDT_CD": "01",
        "PDNO": ticker,
        "ORD_DVSN": "01",  # Market order
        "ORD_QTY": str(quantity),
        "ORD_UNPR": "0"
    }
    
    res = requests.post(url, headers=headers, json=body)
    return res.json()

Key Lessons Learned

What Worked

  • Automating the scanning phase saved significant time
  • Telegram alerts for signals I review manually before execution
  • Strict position sizing rules enforced by code

What Did Not Work

  • Fully automated execution without human review (too many false signals)
  • Running during the first 5 minutes of market open (too volatile)
  • Ignoring market regime (bot designed for trending markets struggled in choppy conditions)

Risk Management Rules

  1. Never risk more than 2% of account per trade
  2. Maximum 3 concurrent positions
  3. Hard daily loss limit of 5% — bot stops automatically
  4. No trading during major news events

Current Status

The bot currently runs in semi-automated mode: it scans and sends signals via Telegram, but I manually approve each trade before execution. This hybrid approach has been more profitable than either full automation or pure manual trading.