Pure Python wrapper for Korea Investment Securities OpenAPI
Project description
๐ Korea Investment Stock
Pure Python wrapper for Korea Investment Securities OpenAPI
โจ NEW in v0.9.0: Environment variable auto-detection like boto3/Twilio!
โจ v0.8.0: Built-in rate limiting to prevent API errors
๐ฏ Purpose
A simple, transparent wrapper around the Korea Investment Securities OpenAPI. This library handles API authentication and request formatting, giving you direct access to the API responses without abstraction layers.
Philosophy: Keep It Simple
- Pure wrapper: Direct API access without magic
- Minimal dependencies: Only
requestsandpandas - No abstraction: You get exactly what the API returns
- Implement your way: Add rate limiting, caching, retries as you need them
๐ฆ Package Structure
korea_investment_stock/
โโโ korea_investment_stock.py # Core API wrapper
โโโ cache/ # Memory caching (opt-in)
โ โโโ cache_manager.py
โ โโโ cached_korea_investment.py
โโโ rate_limit/ # Rate limiting (opt-in)
โ โโโ rate_limiter.py
โ โโโ rate_limited_korea_investment.py
โโโ token_storage/ # Token persistence (opt-in)
โโโ token_storage.py # FileTokenStorage
โโโ token_storage.py # RedisTokenStorage
Design Pattern: Wrapper-based architecture
- Core
KoreaInvestmentclass unchanged - Optional features as opt-in wrappers
- Stack wrappers for combined functionality
- Zero dependencies for advanced features (except Redis)
๐ Features
Core API Support
- โ Stock Price Queries: Domestic (KR) and US stocks
- โ Stock Information: Company details and market data
- โ IPO Schedule: Public offering information and schedules
- โ
Unified Interface: Query KR/US stocks with
fetch_price(symbol, market) - โ Search Functions: Stock search and lookup
Advanced Features (Opt-in)
- ๐ง Env Auto-Detection (v0.9.0): boto3/Twilio-style automatic credential loading
- ๐ Rate Limiting (v0.8.0): Automatic throttling with logging and monitoring
- ๐พ Memory Caching (v0.7.0): Reduce API calls with TTL-based caching
- ๐ Token Storage (v0.7.0): File or Redis-based token persistence
- ๐ Wrapper Architecture: Stack features as needed
Technical Features
- ๐ง Context Manager: Automatic resource cleanup
- ๐ง Thread-Safe: All wrappers support concurrent access
- ๐ง Environment Variables: API credentials via env vars
- ๐ง Zero Dependencies: Core wrapper needs only
requestsandpandas
๐ฆ Installation
pip install korea-investment-stock
Requirements
- Python 3.11 or higher
- Korea Investment Securities API account
๐ Quick Start
1. Set Up Credentials
# Add to your ~/.zshrc or ~/.bashrc
export KOREA_INVESTMENT_API_KEY="your-api-key"
export KOREA_INVESTMENT_API_SECRET="your-api-secret"
export KOREA_INVESTMENT_ACCOUNT_NO="12345678-01"
2. Basic Usage
from korea_investment_stock import KoreaInvestment
# NEW in v0.9.0: Auto-detect from environment variables!
with KoreaInvestment() as broker:
result = broker.fetch_price("005930", "KR")
if result['rt_cd'] == '0':
price = result['output1']['stck_prpr']
print(f"Price: {price}์")
Alternative: Explicit parameters (still supported)
from korea_investment_stock import KoreaInvestment
import os
# Explicit parameter passing (backward compatible)
with KoreaInvestment(
api_key=os.getenv('KOREA_INVESTMENT_API_KEY'),
api_secret=os.getenv('KOREA_INVESTMENT_API_SECRET'),
acc_no=os.getenv('KOREA_INVESTMENT_ACCOUNT_NO')
) as broker:
result = broker.fetch_price("005930", "KR")
Priority: Constructor parameters > Environment variables
# Mixed usage: override specific values
broker = KoreaInvestment(api_key="override-key") # Others from env vars
๐ API Methods
Stock Price Queries
# Domestic stock price
result = broker.fetch_price("005930", "KR") # Samsung Electronics
# US stock price (requires real account)
result = broker.fetch_price("AAPL", "US") # Apple
# Direct methods
result = broker.fetch_domestic_price("J", "005930")
result = broker.fetch_etf_domestic_price("J", "069500") # KODEX 200
result = broker.fetch_price_detail_oversea("AAPL", "US")
Stock Information
# Stock info
result = broker.fetch_stock_info("005930", "KR")
# Search stock
result = broker.fetch_search_stock_info("005930", "KR")
IPO Schedule
# All IPOs (today + 30 days)
result = broker.fetch_ipo_schedule()
# Specific period
result = broker.fetch_ipo_schedule(
from_date="20250101",
to_date="20250131"
)
# Specific symbol
result = broker.fetch_ipo_schedule(symbol="123456")
# Helper methods
status = broker.get_ipo_status(ipo['subscr_dt']) # "์์ ", "์งํ์ค", "๋ง๊ฐ"
d_day = broker.calculate_ipo_d_day(ipo['subscr_dt']) # Days until subscription
Symbol Lists
# KOSPI symbols
result = broker.fetch_kospi_symbols()
# KOSDAQ symbols
result = broker.fetch_kosdaq_symbols()
๐ง Advanced Usage
Multiple Stock Queries
# Query multiple stocks
stocks = [
("005930", "KR"), # Samsung
("000660", "KR"), # SK Hynix
("035720", "KR"), # Kakao
]
results = []
for symbol, market in stocks:
result = broker.fetch_price(symbol, market)
results.append(result)
# Add your own rate limiting here if needed
Mixed KR/US Portfolio
portfolio = [
("005930", "KR"), # Samsung Electronics
("AAPL", "US"), # Apple
("035720", "KR"), # Kakao
("MSFT", "US"), # Microsoft
]
for symbol, market in portfolio:
result = broker.fetch_price(symbol, market)
if result['rt_cd'] == '0':
if market == "KR":
output = result['output1']
price = output['stck_prpr']
print(f"{symbol}: โฉ{int(price):,}")
else:
output = result['output']
price = output['last']
print(f"{symbol}: ${price}")
Error Handling
try:
result = broker.fetch_price("INVALID", "US")
if result['rt_cd'] != '0':
# API returned error
print(f"API Error: {result['msg1']}")
except ValueError as e:
# Invalid parameters
print(f"Invalid request: {e}")
except Exception as e:
# Network or other errors
print(f"Error: {e}")
API Rate Limiting (NEW in v0.8.0) ๐
Problem: Korea Investment API has a 20 calls/second limit. Exceeding this causes errors.
Solution: Automatic rate limiting wrapper that throttles API calls safely.
from korea_investment_stock import KoreaInvestment, RateLimitedKoreaInvestment
# Create base broker
broker = KoreaInvestment(api_key, api_secret, acc_no)
# Wrap with rate limiting (15 calls/second - conservative)
rate_limited = RateLimitedKoreaInvestment(broker, calls_per_second=15)
# Use as normal - rate limiting applied automatically
result = rate_limited.fetch_price("005930", "KR")
# Batch processing is now safe!
for symbol, market in stock_list:
result = rate_limited.fetch_price(symbol, market) # Auto-throttled
Configuration Options:
# Production - ultra safe
conservative = RateLimitedKoreaInvestment(broker, calls_per_second=12)
# Testing - closer to limit
aggressive = RateLimitedKoreaInvestment(broker, calls_per_second=18)
# Dynamic adjustment at runtime
rate_limited.adjust_rate_limit(calls_per_second=10)
# Check statistics (NEW: Extended statistics)
stats = rate_limited.get_rate_limit_stats()
print(f"Rate: {stats['calls_per_second']}/sec")
print(f"Total calls: {stats['total_calls']}")
print(f"Throttled: {stats['throttled_calls']} ({stats['throttle_rate']*100:.1f}%)")
print(f"Total wait time: {stats['total_wait_time']:.2f}s")
print(f"Avg wait time: {stats['avg_wait_time']:.3f}s")
Logging & Monitoring (NEW):
Built-in logging and extended statistics for production monitoring:
import logging
from korea_investment_stock import KoreaInvestment, RateLimitedKoreaInvestment
# Enable DEBUG logging to see throttle events
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
broker = KoreaInvestment(api_key, api_secret, acc_no)
rate_limited = RateLimitedKoreaInvestment(broker, calls_per_second=15)
# Each throttle event will be logged:
# "Rate limit: waiting 0.067s (call #2)"
result = rate_limited.fetch_price("005930", "KR")
# Get detailed statistics after batch processing
stats = rate_limited.get_rate_limit_stats()
print(f"Performance Metrics:")
print(f" Total calls: {stats['total_calls']}")
print(f" Throttled: {stats['throttled_calls']} ({stats['throttle_rate']*100:.1f}%)")
print(f" Total wait time: {stats['total_wait_time']:.2f}s")
print(f" Avg wait time: {stats['avg_wait_time']:.3f}s")
Extended Statistics Fields:
{
'calls_per_second': 15.0, # Configured rate limit
'total_calls': 500, # Total API calls made
'throttled_calls': 450, # Calls that were throttled (NEW)
'throttle_rate': 0.90, # Throttle percentage (NEW)
'total_wait_time': 28.5, # Total time spent waiting (NEW)
'avg_wait_time': 0.0633 # Average wait per throttle (NEW)
}
Combined with Cache (Recommended!):
from korea_investment_stock import (
KoreaInvestment,
CachedKoreaInvestment,
RateLimitedKoreaInvestment
)
# Stack both wrappers for maximum efficiency
broker = KoreaInvestment(api_key, api_secret, acc_no)
cached = CachedKoreaInvestment(broker, price_ttl=5)
safe_broker = RateLimitedKoreaInvestment(cached, calls_per_second=15)
# Benefits:
# โ
Cache reduces API calls (performance)
# โ
Rate limit protects cache misses (safety)
# โ
No API errors, maximum efficiency
Performance Impact:
| Scenario | Without Rate Limit | With Rate Limit (15/sec) |
|---|---|---|
| 10 API calls | ~1-3 sec | ~0.67 sec |
| 100 API calls | ~10-30 sec | ~6.7 sec |
| 500 API calls | FAILS (rate limit errors) | ~33 sec (100% success) |
Features:
- ๐ Thread-safe with
threading.Lock - โก Token bucket algorithm
- ๐ Real-time statistics with extended metrics (NEW)
- ๐ Built-in logging for throttle events (NEW)
- ๐๏ธ Dynamic rate adjustment
- ๐ง Context manager support
See: examples/stress_test.py for 500+ API calls example with logging and statistics
Memory Caching (Optional)
Reduce API calls and improve response times with built-in memory caching:
from korea_investment_stock import KoreaInvestment, CachedKoreaInvestment
# Create base broker
broker = KoreaInvestment(api_key, api_secret, acc_no)
# Wrap with caching (opt-in)
cached_broker = CachedKoreaInvestment(broker, price_ttl=5)
# First call: API request (cache miss)
result1 = cached_broker.fetch_price("005930", "KR") # ~200ms
# Second call: from cache (cache hit)
result2 = cached_broker.fetch_price("005930", "KR") # <1ms
# Cache statistics
stats = cached_broker.get_cache_stats()
print(f"Hit rate: {stats['hit_rate']}") # "50.00%"
TTL Configuration (in seconds):
cached_broker = CachedKoreaInvestment(
broker,
price_ttl=5, # Real-time price: 5 seconds
stock_info_ttl=300, # Stock info: 5 minutes
symbols_ttl=3600, # Symbol lists: 1 hour
ipo_ttl=1800 # IPO schedule: 30 minutes
)
Performance Benefits:
- ๐ API calls reduced by 30-50%
- โก Response time improved by 90%+ (cached queries)
- ๐ Thread-safe with automatic expiration
- ๐พ No external dependencies (memory-only)
See: examples/cached_basic_example.py for comprehensive examples
Token Storage (Advanced)
Persist API tokens across sessions for faster initialization:
File-based Storage (Default):
from korea_investment_stock import KoreaInvestment, FileTokenStorage
from pathlib import Path
# Default: ~/.cache/kis/token.key
broker = KoreaInvestment(
api_key=api_key,
api_secret=api_secret,
acc_no=acc_no,
token_storage=FileTokenStorage()
)
# Custom path
custom_storage = FileTokenStorage(Path("/custom/path/token.key"))
broker = KoreaInvestment(api_key, api_secret, acc_no, token_storage=custom_storage)
Redis-based Storage (Distributed):
from korea_investment_stock import KoreaInvestment, RedisTokenStorage
# Install redis support: pip install korea-investment-stock[redis]
redis_storage = RedisTokenStorage(
host='localhost',
port=6379,
db=0,
password=None # Optional
)
broker = KoreaInvestment(
api_key=api_key,
api_secret=api_secret,
acc_no=acc_no,
token_storage=redis_storage
)
Benefits:
- โก Skip token generation on subsequent runs (24h TTL)
- ๐ Share tokens across multiple processes (Redis)
- ๐พ Automatic token expiration and renewal
- ๐ Thread-safe token management
See: examples/redis_token_example.py for Redis setup
๐ Response Format
Domestic Stock (KR)
{
'rt_cd': '0', # Return code ('0' = success)
'msg1': '์ ์์ฒ๋ฆฌ๋์์ต๋๋ค', # Message
'output1': {
'stck_prpr': '62600', # Current price
'prdy_vrss': '1600', # Change from previous day
'prdy_ctrt': '2.62', # Change rate (%)
'stck_oprc': '61000', # Opening price
'stck_hgpr': '63000', # High price
'stck_lwpr': '60500', # Low price
'acml_vol': '15234567' # Volume
# ... more fields
}
}
US Stock (US)
{
'rt_cd': '0',
'msg1': '์ ์์ฒ๋ฆฌ๋์์ต๋๋ค',
'output': {
'rsym': 'DNASAAPL', # Exchange + Symbol
'last': '211.16', # Current price
'open': '210.56', # Opening price
'high': '212.13', # High price
'low': '209.86', # Low price
'tvol': '39765812', # Volume
't_xdif': '1.72', # Change
't_xrat': '-0.59', # Change rate (%)
'perx': '32.95', # PER
'pbrx': '47.23', # PBR
'epsx': '6.41', # EPS
'bpsx': '4.47' # BPS
# ... more fields
}
}
โ ๏ธ Important Notes
API Rate Limits
- Korea Investment API: 20 requests/second limit
- NEW in v0.8.0: Built-in
RateLimitedKoreaInvestmentwrapper (recommended!) - Without rate limiting: API errors when limit exceeded
- Best practice: Use
RateLimitedKoreaInvestmentor implement your own
US Stocks
- Auto-detects exchange (NASDAQ โ NYSE โ AMEX)
- Includes financial ratios (PER, PBR, EPS, BPS)
- Requires real account (paper trading not supported)
Context Manager
Always use context manager for proper resource cleanup:
# โ
Good: Automatic cleanup
with KoreaInvestment(api_key, api_secret, acc_no) as broker:
result = broker.fetch_price("005930", "KR")
# โ Bad: Manual cleanup required
broker = KoreaInvestment(api_key, api_secret, acc_no)
result = broker.fetch_price("005930", "KR")
broker.shutdown() # Must call manually
Optional Features
All advanced features are opt-in wrappers:
CachedKoreaInvestment: Memory caching (v0.7.0)RateLimitedKoreaInvestment: Rate limiting (v0.8.0)FileTokenStorage/RedisTokenStorage: Token persistence (v0.7.0)
You control what features to use based on your needs.
๐จ Best Practices & Recommended Setup
Production Setup (Recommended)
Combine all features for optimal performance and safety:
from korea_investment_stock import (
KoreaInvestment,
CachedKoreaInvestment,
RateLimitedKoreaInvestment,
FileTokenStorage # or RedisTokenStorage for distributed
)
# 1. Core broker with token persistence
broker = KoreaInvestment(
api_key=api_key,
api_secret=api_secret,
acc_no=acc_no,
token_storage=FileTokenStorage() # Skip token generation on restart
)
# 2. Add caching layer (reduce API calls)
cached = CachedKoreaInvestment(broker, price_ttl=5)
# 3. Add rate limiting (prevent API errors)
safe_broker = RateLimitedKoreaInvestment(cached, calls_per_second=15)
# 4. Use with context manager
with safe_broker:
for symbol, market in portfolio:
result = safe_broker.fetch_price(symbol, market)
# โ
Cached: <1ms response
# โ
Rate limited: No API errors
# โ
Token persisted: Fast restarts
Custom Implementation Examples
If you prefer to implement your own features:
Custom Rate Limiter:
import time
class CustomRateLimiter:
def __init__(self, calls_per_second=15):
self.min_interval = 1.0 / calls_per_second
self.last_call = 0
def wait(self):
elapsed = time.time() - self.last_call
if elapsed < self.min_interval:
time.sleep(self.min_interval - elapsed)
self.last_call = time.time()
limiter = CustomRateLimiter(calls_per_second=15)
for symbol, market in stocks:
limiter.wait()
result = broker.fetch_price(symbol, market)
Custom Caching:
from datetime import datetime, timedelta
class CustomCache:
def __init__(self, broker, ttl_minutes=5):
self.broker = broker
self.cache = {}
self.ttl = timedelta(minutes=ttl_minutes)
def fetch_price_cached(self, symbol, market):
key = f"{symbol}:{market}"
now = datetime.now()
if key in self.cache:
cached_time, result = self.cache[key]
if now - cached_time < self.ttl:
return result
result = self.broker.fetch_price(symbol, market)
self.cache[key] = (now, result)
return result
Custom Retry Logic:
import time
def fetch_with_retry(broker, symbol, market, max_retries=3):
for attempt in range(max_retries):
try:
result = broker.fetch_price(symbol, market)
if result['rt_cd'] == '0':
return result
time.sleep(2 ** attempt) # Exponential backoff
except Exception as e:
if attempt == max_retries - 1:
raise
time.sleep(2 ** attempt)
๐ Examples
See the examples/ directory for complete working examples:
| Example | Description | Features |
|---|---|---|
basic_example.py |
Simple usage patterns | Basic API calls, error handling |
cached_basic_example.py |
Caching examples | Memory cache, TTL configuration, statistics |
stress_test.py |
Batch processing (500 calls) | Rate limiting, batch operations, performance |
us_stock_price_example.py |
US stock queries | Exchange detection, financial ratios |
redis_token_example.py |
Token persistence | Redis setup, distributed tokens |
Quick Start:
# Set environment variables first
export KOREA_INVESTMENT_API_KEY="your-key"
export KOREA_INVESTMENT_API_SECRET="your-secret"
export KOREA_INVESTMENT_ACCOUNT_NO="12345678-01"
# Run examples
python examples/basic_example.py
python examples/stress_test.py
๐ Migration Guide
v0.9.0 Changes (Non-breaking)
Environment variable auto-detection (like boto3/Twilio):
# Before v0.9.0 - Required explicit env var reading
import os
broker = KoreaInvestment(
api_key=os.getenv('KOREA_INVESTMENT_API_KEY'),
api_secret=os.getenv('KOREA_INVESTMENT_API_SECRET'),
acc_no=os.getenv('KOREA_INVESTMENT_ACCOUNT_NO')
)
# v0.9.0+ - Auto-detect from environment variables
broker = KoreaInvestment() # Reads KOREA_INVESTMENT_* env vars automatically
# Mixed usage - override specific values
broker = KoreaInvestment(api_key="override-key") # Others from env vars
Priority: Constructor parameters > Environment variables
No breaking changes - all existing code continues to work
v0.8.0 Changes (Breaking)
Mock mode removed (See CHANGELOG.md for details):
# v0.7.x (Old)
broker = KoreaInvestment(api_key, api_secret, acc_no, mock=True)
# v0.8.0 (New)
broker = KoreaInvestment(api_key, api_secret, acc_no)
Rate limiting added (Opt-in):
# NEW in v0.8.0: Built-in rate limiting
from korea_investment_stock import RateLimitedKoreaInvestment
rate_limited = RateLimitedKoreaInvestment(broker, calls_per_second=15)
v0.7.0 Changes (Non-breaking)
Added features (all opt-in):
- Memory caching:
CachedKoreaInvestment - Token storage:
FileTokenStorage,RedisTokenStorage - Package restructure: Feature-based modules
No breaking changes - all v0.6.0 code works in v0.7.0
v0.6.0 Changes (Breaking)
Removed features (~6,000 lines of code):
- Built-in rate limiting (now available as opt-in in v0.8.0)
- Built-in caching (now available as opt-in in v0.7.0)
- Batch processing methods
- Monitoring and statistics
- Visualization tools
- Automatic retry decorators
API changes:
# v0.5.0 (Old)
results = broker.fetch_price_list([("005930", "KR"), ("AAPL", "US")])
# v0.6.0+ (New)
results = []
for symbol, market in [("005930", "KR"), ("AAPL", "US")]:
result = broker.fetch_price(symbol, market)
results.append(result)
See CHANGELOG.md for complete migration guide.
๐ Documentation
๐ค Contributing
Contributions welcome! Please:
- Fork the repository
- Create a feature branch
- Submit a pull request
๐ License
MIT License - see LICENSE file
โก Performance Tips
Quick Wins
- Use
RateLimitedKoreaInvestment: Prevent API errors (v0.8.0) - Use
CachedKoreaInvestment: Reduce redundant API calls (v0.7.0) - Use
FileTokenStorage: Skip token generation on restart (v0.7.0) - Stack all wrappers: Maximum performance and safety
- Use context manager: Ensure proper cleanup
Performance Comparison
| Setup | 100 stocks (same) | 100 stocks (unique) | Token generation |
|---|---|---|---|
| Basic | ~33 sec | ~33 sec | Every run (~1 sec) |
| + Rate Limit | ~6.7 sec | ~6.7 sec | Every run (~1 sec) |
| + Cache | ~0.1 sec | ~6.7 sec | Every run (~1 sec) |
| + Token Storage | ~0.1 sec | ~6.7 sec | First run only |
| All combined | ~0.1 sec | ~6.7 sec | First run only |
Optimization Strategy
# Choose based on your use case:
# Real-time trading (fast changing prices)
cached = CachedKoreaInvestment(broker, price_ttl=1)
# Analysis/backtesting (less frequent updates)
cached = CachedKoreaInvestment(broker, price_ttl=60)
# Production batch processing
safe_broker = RateLimitedKoreaInvestment(
CachedKoreaInvestment(broker, price_ttl=5),
calls_per_second=15
)
# Distributed systems
broker = KoreaInvestment(
api_key, api_secret, acc_no,
token_storage=RedisTokenStorage() # Share tokens across instances
)
๐ Credits
- Korea Investment Securities for providing the OpenAPI
- Original contributors: Jonghun Yoo, Brayden Jo, Frank Oh
Remember: This is a pure wrapper. You control rate limiting, caching, error handling, and monitoring according to your needs.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file korea_investment_stock-0.6.2.tar.gz.
File metadata
- Download URL: korea_investment_stock-0.6.2.tar.gz
- Upload date:
- Size: 142.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a097713b7967e264aae27cf6ee26b3ebdde7a3e8ddf80f20dee58f821482eab4
|
|
| MD5 |
bbdce608b4007cc023674289b96d40ea
|
|
| BLAKE2b-256 |
8ddcdc02bdcd465c10db853742d827250787e302e079376c50c7398a50cd1338
|
File details
Details for the file korea_investment_stock-0.6.2-py3-none-any.whl.
File metadata
- Download URL: korea_investment_stock-0.6.2-py3-none-any.whl
- Upload date:
- Size: 57.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1c64c7c5b07d93d9c9f5fa48086db29d2e0f8f0180913014c62a039072455e09
|
|
| MD5 |
731b5133065c37e9a232ba6579cb12a5
|
|
| BLAKE2b-256 |
0e95d78bf49e3885461b78ef251c33d130ba0ffc18517cc2e85a8bb4efa1b1dc
|