Introduction

pykrx is the easiest way to get Korean stock market data in Python. No API key needed, no account required — just install and start pulling data.

This guide covers 10 real-world examples from basic price data to advanced screening.

Setup

pip install pykrx pandas
from pykrx import stock
import pandas as pd
from datetime import datetime, timedelta

# Helper: today's date
TODAY = datetime.now().strftime("%Y%m%d")
MONTH_AGO = (datetime.now() - timedelta(days=30)).strftime("%Y%m%d")
YEAR_AGO = (datetime.now() - timedelta(days=365)).strftime("%Y%m%d")

Example 1: Get Stock Price History

from pykrx import stock

# Samsung Electronics (005930)
df = stock.get_market_ohlcv_by_date("20240101", "20241231", "005930")
df.columns = ["Open", "High", "Low", "Close", "Volume"]
print(df.tail())

Example 2: Get All KOSPI Tickers

from pykrx import stock

kospi = stock.get_market_ticker_list(market="KOSPI")
kosdaq = stock.get_market_ticker_list(market="KOSDAQ")

print(f"KOSPI stocks: {len(kospi)}")
print(f"KOSDAQ stocks: {len(kosdaq)}")

# Get company name
name = stock.get_market_ticker_name("005930")
print(f"005930 = {name}")  # 삼성전자

Example 3: Market Cap Rankings

from pykrx import stock

# Top 10 KOSPI by market cap
df = stock.get_market_cap_by_ticker(TODAY, market="KOSPI")
top10 = df.sort_values("시가총액", ascending=False).head(10)

for ticker, row in top10.iterrows():
    name = stock.get_market_ticker_name(ticker)
    market_cap_trillion = row["시가총액"] / 1e12
    print(f"{name}: {market_cap_trillion:.1f}T KRW")

Example 4: Fundamental Data (PER, PBR, Dividend)

from pykrx import stock

# Get fundamentals for all KOSPI stocks
df = stock.get_market_fundamental_by_ticker(TODAY, market="KOSPI")

# Find undervalued stocks (low PER, low PBR)
undervalued = df[
    (df["PER"] > 0) & (df["PER"] < 10) &
    (df["PBR"] > 0) & (df["PBR"] < 1.0)
].copy()

undervalued["name"] = [stock.get_market_ticker_name(t) for t in undervalued.index]
print(undervalued[["name", "PER", "PBR", "DIV"]].sort_values("PER").head(10))

Example 5: KOSPI Index Data

from pykrx import stock

# Get KOSPI index history
# "1001" = KOSPI, "2001" = KOSDAQ
kospi_index = stock.get_index_ohlcv_by_date("20240101", "20241231", "1001")
print(kospi_index.tail())

# Year high/low
print(f"2024 KOSPI High: {kospi_index['고가'].max():,}")
print(f"2024 KOSPI Low: {kospi_index['저가'].min():,}")

Example 6: Foreign Investor Trading Data

from pykrx import stock

# Foreign net buying/selling for Samsung
df = stock.get_market_trading_volume_by_date(
    "20240101", "20241231", "005930"
)
print(df.tail())

# Days with heavy foreign buying
foreign_buying = df[df["외국인"] > 1000000]
print(f"Heavy foreign buying days: {len(foreign_buying)}")

Example 7: Sector Performance

from pykrx import stock

# Get sector (theme) data
# Major KOSPI sector tickers
sectors = {
    "1001": "KOSPI",
    "1028": "KOSPI200",
    "2001": "KOSDAQ",
    "1163": "KOSPI IT",
    "1150": "KOSPI Finance"
}

for code, name in sectors.items():
    df = stock.get_index_ohlcv_by_date(MONTH_AGO, TODAY, code)
    if not df.empty:
        start = df["종가"].iloc[0]
        end = df["종가"].iloc[-1]
        change = (end - start) / start * 100
        print(f"{name}: {change:+.2f}%")

Example 8: Volume Surge Detector

from pykrx import stock
import pandas as pd

def find_volume_surges(market="KOSPI", multiplier=3.0):
    """Find stocks with volume 3x above their 20-day average"""
    
    tickers = stock.get_market_ticker_list(market=market)
    surges = []
    
    for ticker in tickers[:50]:  # Limit for demo
        try:
            df = stock.get_market_ohlcv_by_date(MONTH_AGO, TODAY, ticker)
            if len(df) < 20:
                continue
            
            avg_vol = df["거래량"].iloc[:-1].mean()
            today_vol = df["거래량"].iloc[-1]
            
            if today_vol > avg_vol * multiplier:
                name = stock.get_market_ticker_name(ticker)
                surges.append({
                    "ticker": ticker,
                    "name": name,
                    "volume_ratio": today_vol / avg_vol,
                    "close": df["종가"].iloc[-1]
                })
        except:
            continue
    
    return sorted(surges, key=lambda x: x["volume_ratio"], reverse=True)

surges = find_volume_surges()
for s in surges[:5]:
    print(f"{s['name']}: {s['volume_ratio']:.1f}x average volume")

Example 9: Simple Backtesting

from pykrx import stock
import pandas as pd

def backtest_moving_average(ticker, short=5, long=20):
    """Simple moving average crossover backtest"""
    
    df = stock.get_market_ohlcv_by_date("20230101", "20241231", ticker)
    df.columns = ["Open", "High", "Low", "Close", "Volume"]
    
    df[f"MA{short}"] = df["Close"].rolling(short).mean()
    df[f"MA{long}"] = df["Close"].rolling(long).mean()
    
    # Signal: 1 = buy, -1 = sell
    df["signal"] = 0
    df.loc[df[f"MA{short}"] > df[f"MA{long}"], "signal"] = 1
    df.loc[df[f"MA{short}"] < df[f"MA{long}"], "signal"] = -1
    
    # Returns
    df["returns"] = df["Close"].pct_change()
    df["strategy"] = df["signal"].shift(1) * df["returns"]
    
    total_return = (1 + df["strategy"].dropna()).prod() - 1
    buy_hold = (df["Close"].iloc[-1] - df["Close"].iloc[0]) / df["Close"].iloc[0]
    
    print(f"Strategy return: {total_return:.2%}")
    print(f"Buy & hold return: {buy_hold:.2%}")
    
    return df

result = backtest_moving_average("005930")

Example 10: Export to Excel

from pykrx import stock
import pandas as pd

# Get data for multiple stocks
watchlist = {
    "005930": "Samsung Electronics",
    "000660": "SK Hynix", 
    "373220": "LG Energy Solution",
    "005380": "Hyundai Motor",
    "035420": "NAVER"
}

with pd.ExcelWriter("korean_stocks.xlsx") as writer:
    for ticker, name in watchlist.items():
        df = stock.get_market_ohlcv_by_date("20240101", "20241231", ticker)
        df.columns = ["Open", "High", "Low", "Close", "Volume"]
        df.to_excel(writer, sheet_name=name[:30])
        print(f"Saved {name}")

print("Excel file created: korean_stocks.xlsx")

Common Mistakes

MistakeFix
Date with dashes "2024-01-01"Remove dashes: "20240101"
Using company name as tickerUse 6-digit number: "005930"
Calling too frequentlyAdd time.sleep(0.5) between calls
Empty result on weekendsKRX is closed Sat/Sun

Key Takeaways

  • pykrx is free and requires no API key
  • Date format is always YYYYMMDD
  • Column names default to Korean — rename for easier use
  • Add delays between multiple API calls to avoid rate limiting
  • For real-time data and trading, use KIS API instead