Skip to content

gejyn14/pyheroapi

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

32 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

PyHero API - AI์™€ ๋งŒ๋“ค์–ด๋ณธ ํ‚ค์›€์ฆ๊ถŒ REST API Python ํด๋ผ์ด์–ธํŠธ v0.3.3

PyPI version Python versions License: MIT Documentation

ํ‚ค์›€์ฆ๊ถŒ REST API๋ฅผ ํŒŒ์ด์ฌ์œผ๋กœ ์ง๊ด€์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ํด๋ผ์ด์–ธํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.

๐Ÿš€ v0.3.3 ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ: ํฌ๊ด„์ ์ธ ์˜ˆ์ œ ์‹œ์Šคํ…œ, ์‹ค์‹œ๊ฐ„ WebSocket ์ง€์›, 163๊ฐœ API ๋ฉ”์†Œ๋“œ ์™„์ „ ๊ตฌํ˜„


๐ŸŒŸ ์ฃผ์š” ํŠน์ง•

๐Ÿ’ก ์ง๊ด€์ ์ด๊ณ  ๊ฐ•๋ ฅํ•œ API

  • ๐ŸŽฏ ํ•œ ์ค„ ์—ฐ๊ฒฐ: pyheroapi.connect() ํ•œ ๋ฒˆ์œผ๋กœ ๋ชจ๋“  ๊ธฐ๋Šฅ ์‚ฌ์šฉ
  • ๐Ÿ”„ ์ž๋™ ํ† ํฐ ๊ด€๋ฆฌ: ํ† ํฐ ๋ฐœ๊ธ‰, ๊ฐฑ์‹ , ํ๊ธฐ ์™„์ „ ์ž๋™ํ™”
  • ๐Ÿ›ก๏ธ ์šฐ์•„ํ•œ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ: ์˜ˆ์™ธ ๋Œ€์‹  ์•ˆ์ „ํ•œ ๊ธฐ๋ณธ๊ฐ’ ๋ฐ˜ํ™˜
  • โšก ์Šค๋งˆํŠธ ์บ์‹ฑ: ์ค‘๋ณต API ํ˜ธ์ถœ ์ž๋™ ์ตœ์ ํ™”

๐Ÿ“Š ์™„์ „ํ•œ ๊ฑฐ๋ž˜ ์‹œ์Šคํ…œ

  • โœ… ํฌ๊ด„์ ์ธ ์ฃผ๋ฌธ ๊ด€๋ฆฌ: ๋งค์ˆ˜/๋งค๋„, ์ •์ •/์ทจ์†Œ, ์กฐ๊ฑด๋ถ€ ์ฃผ๋ฌธ
  • โœ… ์‹ ์šฉ๊ฑฐ๋ž˜ ์ง€์›: ์‹ ์šฉ ๋งค์ˆ˜/๋งค๋„, ๋Œ€์ถœ ๊ด€๋ฆฌ
  • โœ… ๋‹ค์–‘ํ•œ ์ฃผ๋ฌธ ์œ ํ˜•: ์ง€์ •๊ฐ€, ์‹œ์žฅ๊ฐ€, ์ตœ์œ ๋ฆฌ, IOC, FOK ๋“ฑ
  • โœ… ์‹ค์‹œ๊ฐ„ ์ฃผ๋ฌธ ์ถ”์ : ๋ฏธ์ฒด๊ฒฐ/์ฒด๊ฒฐ ๋‚ด์—ญ, ์‹ค์‹œ๊ฐ„ ์ƒํƒœ ์—…๋ฐ์ดํŠธ

๐Ÿ”ฅ ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆฌ๋ฐ

  • ๐ŸŒ WebSocket ์‹ค์‹œ๊ฐ„ ์‹œ์„ธ: ์ฃผ๊ฐ€, ํ˜ธ๊ฐ€, ์ฒด๊ฒฐ ๋ฐ์ดํ„ฐ
  • ๐Ÿ“ˆ ์‹ค์‹œ๊ฐ„ ๊ณ„์ขŒ ๋ชจ๋‹ˆํ„ฐ๋ง: ์ž”๊ณ , ํฌ์ง€์…˜ ๋ณ€ํ™” ์ถ”์ 
  • โšก ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ: async/await ํŒจํ„ด ์™„์ „ ์ง€์›
  • ๐Ÿ”” ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜ ์ฝœ๋ฐฑ: ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ๋ณ€ํ™” ์•Œ๋ฆผ

๐Ÿ’ฐ ๊ณ ๊ธ‰ ๊ธˆ์œต ๋ฐ์ดํ„ฐ ๋ถ„์„

  • ๐Ÿ“Š ETF ์‹ฌํ™” ๋ถ„์„: NAV, ์ถ”์ ์˜ค์ฐจ, ์ˆ˜์ต๋ฅ , ๊ตฌ์„ฑ์ข…๋ชฉ
  • โšก ELW ์ „๋ฌธ ๋„๊ตฌ: ๊ทธ๋ฆญ์Šค(Delta, Gamma, Theta, Vega), ๋ฏผ๊ฐ๋„ ์ง€ํ‘œ
  • ๐Ÿ›๏ธ ๊ธฐ๊ด€/์™ธ๊ตญ์ธ ๋งค๋งค: ์‹ค์‹œ๊ฐ„ ๋งค๋งค ๋™ํ–ฅ, ์ˆœ๋งค์ˆ˜/์ˆœ๋งค๋„
  • ๐Ÿ“ˆ ์‹œ์žฅ ์ˆœ์œ„ ๋ถ„์„: ๊ฑฐ๋ž˜๋Ÿ‰, ๋“ฑ๋ฝ๋ฅ , ์‹œ๊ฐ€์ด์•ก ์ˆœ์œ„

๐ŸŽ“ ๊ต์œก์  ์˜ˆ์ œ ์‹œ์Šคํ…œ

  • ๐Ÿ“š 7๊ฐœ ํฌ๊ด„์  ๋ชจ๋“ˆ: ๊ธฐ์ดˆ๋ถ€ํ„ฐ ๊ณ ๊ธ‰๊นŒ์ง€ ๋‹จ๊ณ„๋ณ„ ํ•™์Šต
  • ๐Ÿ›ก๏ธ ์•ˆ์ „ํ•œ ์ƒŒ๋“œ๋ฐ•์Šค: ๋ชจ์˜๊ฑฐ๋ž˜ ํ™˜๊ฒฝ ์ œ๊ณต๊ณต
  • ๐Ÿ“– ์ƒ์„ธํ•œ ๋ฌธ์„œํ™”: ๋ชจ๋“  ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ์‹ค์šฉ์  ์˜ˆ์ œ
  • ๐Ÿ”ง ํ”„๋กœ๋•์…˜ ์ค€๋น„: ์‹ค์ œ ๊ฑฐ๋ž˜๋ฅผ ์œ„ํ•œ ์•ˆ์ „ ๊ฐ€์ด๋“œ

๐ŸŽฏ ์™„์ „ํ•œ API ์ปค๋ฒ„๋ฆฌ์ง€

โœ… 163๊ฐœ API ๋ฉ”์†Œ๋“œ ๊ตฌํ˜„ ์™„๋ฃŒ

์นดํ…Œ๊ณ ๋ฆฌ ๊ตฌํ˜„๋œ ๊ธฐ๋Šฅ ์ปค๋ฒ„๋ฆฌ์ง€
๐Ÿ” ์ธ์ฆ & ํ† ํฐ ํ† ํฐ ๋ฐœ๊ธ‰/ํ๊ธฐ, ์ž๋™ ๊ด€๋ฆฌ 100%
๐Ÿ’ฐ ์ฃผ๋ฌธ & ๊ฑฐ๋ž˜ ๋ชจ๋“  ์ฃผ๋ฌธ ์œ ํ˜•, ์‹ ์šฉ๊ฑฐ๋ž˜ 100%
๐Ÿ›๏ธ ๊ณ„์ขŒ ๊ด€๋ฆฌ ์ž”๊ณ , ํฌ์ง€์…˜, ์†์ต๋ถ„์„ 100%
๐Ÿ“Š ์‹œ์„ธ ๋ฐ์ดํ„ฐ ์‹ค์‹œ๊ฐ„/๊ณผ๊ฑฐ ์‹œ์„ธ, ํ˜ธ๊ฐ€ 100%
๐Ÿ“ˆ ์ฐจํŠธ ๋ฐ์ดํ„ฐ ์ผ/์ฃผ/์›”/๋ถ„๋ด‰, ๊ธฐ์ˆ ์  ์ง€ํ‘œ 100%
๐Ÿ” ์ข…๋ชฉ ์ •๋ณด ์ข…๋ชฉ ์ƒ์„ธ์ •๋ณด, ์žฌ๋ฌด๋ฐ์ดํ„ฐ 95%
๐Ÿ’น ETF ๋ถ„์„ NAV, ์ถ”์ ์˜ค์ฐจ, ๊ตฌ์„ฑ์ข…๋ชฉ 100%
โšก ELW ๋„๊ตฌ ๊ทธ๋ฆญ์Šค, ๋ฏผ๊ฐ๋„, ์กฐ๊ฑด๊ฒ€์ƒ‰ 100%
๐Ÿ“Š ์ˆœ์œ„ ์ •๋ณด ๊ฑฐ๋ž˜๋Ÿ‰/๋“ฑ๋ฝ๋ฅ /์‹œ์ด ์ˆœ์œ„ 100%
๐Ÿข ๊ธฐ๊ด€/์™ธ๊ตญ์ธ ๋งค๋งค ๋™ํ–ฅ, ๋ณด์œ  ํ˜„ํ™ฉ 100%
๐Ÿ”„ ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ WebSocket ์ŠคํŠธ๋ฆฌ๋ฐ 100%
๐ŸŽฏ ์กฐ๊ฑด ๊ฒ€์ƒ‰ ์‹ค์‹œ๊ฐ„ ์กฐ๊ฑด๊ฒ€์ƒ‰, ์กฐ๊ฑด๋ชฉ๋ก ๊ด€๋ฆฌ 100%

๐Ÿš€ ๋น ๋ฅธ ์‹œ์ž‘

์„ค์น˜

# ๊ธฐ๋ณธ ์„ค์น˜
pip install pyheroapi

# ์‹ค์‹œ๊ฐ„ ๊ธฐ๋Šฅ ํฌํ•จ
pip install pyheroapi[realtime]

# ๋ชจ๋“  ๊ธฐ๋Šฅ ํฌํ•จ
pip install pyheroapi[all]

ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์„ค์ •

# ~/.bashrc ๋˜๋Š” ~/.zshrc์— ์ถ”๊ฐ€
export KIWOOM_APPKEY="your_app_key_here"
export KIWOOM_SECRETKEY="your_secret_key_here"  
export KIWOOM_ACCOUNT_NUMBER="your_account_number_here"

๐ŸŽฏ 30์ดˆ ์‹œ์ž‘ํ•˜๊ธฐ

import pyheroapi

# 1. ๊ฐ„๋‹จํ•œ ์—ฐ๊ฒฐ (์ƒŒ๋“œ๋ฐ•์Šค ๋ชจ๋“œ)
with pyheroapi.connect(is_production=False) as api:  # SANDBOX MODE: set is_production=False explicitly
    # 2. ์ฃผ์‹ ๊ฐ€๊ฒฉ ์กฐํšŒ
    samsung = api.stock("005930")
    price = samsung.current_price
    print(f"์‚ผ์„ฑ์ „์ž: โ‚ฉ{price:,.0f}")
    
    # 3. ๊ณ„์ขŒ ์ •๋ณด
    account = api.account()
    balance = account.balance["available_balance"] 
    print(f"์ฃผ๋ฌธ๊ฐ€๋Šฅ๊ธˆ์•ก: โ‚ฉ{balance:,.0f}")
    
    # 4. ์‹œ์žฅ ์ˆœ์œ„ (์ƒ์œ„ 10๊ฐœ)
    rankings = api.rankings.volume_leaders(limit=10)
    for rank, stock in enumerate(rankings, 1):
        print(f"{rank}. {stock['name']}: {stock['volume']:,}์ฃผ")

๐Ÿ“š ํฌ๊ด„์ ์ธ ์˜ˆ์ œ ์‹œ์Šคํ…œ

๐ŸŽ“ 7๊ฐœ ๊ต์œก ๋ชจ๋“ˆ - ์ดˆ๊ธ‰๋ถ€ํ„ฐ ์ „๋ฌธ๊ฐ€๊นŒ์ง€

01_authentication.py - ๐Ÿ” ์ธ์ฆ ๊ธฐ์ดˆ

# ํ† ํฐ ๊ด€๋ฆฌ์˜ ๋ชจ๋“  ๊ฒƒ
from pyheroapi import KiwoomClient

# ํ™˜๊ฒฝ๋ณ€์ˆ˜์—์„œ ์ž๋™ ๋กœ๋“œ
client = KiwoomClient.create_with_credentials()

# ํ† ํฐ ์ˆ˜๋™ ๊ด€๋ฆฌ
token = client.issue_token()
print(f"๋ฐœ๊ธ‰๋œ ํ† ํฐ: {token.token[:20]}...")

# ํ† ํฐ ๊ฒ€์ฆ ๋ฐ ํ๊ธฐ
is_valid = client.validate_token()
client.revoke_token()

02_market_data.py - ๐Ÿ“Š ์‹œ์„ธ ๋ฐ์ดํ„ฐ ๋งˆ์Šคํ„ฐ

# ์‹ค์‹œ๊ฐ„ ์‹œ์„ธ๋ถ€ํ„ฐ ๊ณผ๊ฑฐ ๋ฐ์ดํ„ฐ๊นŒ์ง€
with pyheroapi.connect(is_production=False) as api:  # SANDBOX MODE: set is_production=False explicitly
    # ์‹ค์‹œ๊ฐ„ ์‹œ์„ธ
    samsung = api.stock("005930")
    quote = samsung.quote
    print(f"ํ˜„์žฌ๊ฐ€: {quote['current_price']}")
    print(f"๋งค์ˆ˜ํ˜ธ๊ฐ€: {quote['best_bid']}")
    print(f"๋งค๋„ํ˜ธ๊ฐ€: {quote['best_ask']}")
    
    # OHLCV ๊ณผ๊ฑฐ ๋ฐ์ดํ„ฐ
    history = samsung.get_daily_data(period="1Y")
    
    # ๋ถ„๋ด‰ ๋ฐ์ดํ„ฐ 
    minute_data = samsung.get_minute_data(interval="1", period="1D")
    
    # ์‹œ์žฅ ์„ฑ๊ณผ ์ง€ํ‘œ
    performance = samsung.market_performance()
    print(f"๊ฑฐ๋ž˜๊ฐ•๋„: {performance['trading_intensity']}")

03_trading_orders.py - ๐Ÿ’ฐ ๊ฑฐ๋ž˜ ์‹คํ–‰ ์™„์ „ ๊ฐ€์ด๋“œ

# ๋ชจ๋“  ์ข…๋ฅ˜์˜ ์ฃผ๋ฌธ๊ณผ ๊ด€๋ฆฌ
with pyheroapi.connect(is_production=False) as api:  # SANDBOX MODE: set is_production=False explicitly
    # ๊ณ„์ขŒ ์ƒํƒœ ํ™•์ธ
    account = api.account()
    balance = account.balance
    print(f"์ฃผ๋ฌธ๊ฐ€๋Šฅ๊ธˆ์•ก: โ‚ฉ{balance['available']:,.0f}")
    
    # ๋‹ค์–‘ํ•œ ์ฃผ๋ฌธ ์œ ํ˜•
    # ์ง€์ •๊ฐ€ ๋งค์ˆ˜
    result = api.trading.buy("005930", quantity=10, price=75000, order_type="limit")
    
    # ์‹œ์žฅ๊ฐ€ ๋งค๋„  
    result = api.trading.sell("005930", quantity=5, order_type="market")
    
    # ์กฐ๊ฑด๋ถ€ ์ง€์ •๊ฐ€
    result = api.trading.buy("005930", quantity=10, price=75000, 
                           order_type="conditional_limit", condition_price=74000)
    
    # ์ฃผ๋ฌธ ์ˆ˜์ •/์ทจ์†Œ
    if result["success"]:
        order_no = result["order_number"]
        
        # ์ฃผ๋ฌธ ์ˆ˜์ •
        api.trading.modify_order(order_no, "005930", new_quantity=8, new_price=74500)
        
        # ์ฃผ๋ฌธ ์ทจ์†Œ
        api.trading.cancel_order(order_no, "005930", cancel_quantity=8)
    
    # ์‹ ์šฉ๊ฑฐ๋ž˜
    credit_result = api.trading.credit_buy("005930", quantity=100, price=75000)

04_etf_elw.py - ๐Ÿ’น ETF/ELW ์ „๋ฌธ ๋ถ„์„

# ETF์™€ ELW์˜ ๋ชจ๋“  ๊ฒƒ
with pyheroapi.connect(is_production=False) as api:  # SANDBOX MODE: set is_production=False explicitly
    # ETF ์‹ฌํ™” ๋ถ„์„
    kodex = api.etf("069500")
    
    # NAV ๋ถ„์„
    nav_data = kodex.get_nav_analysis()
    print(f"NAV: โ‚ฉ{nav_data['nav']:,.2f}")
    print(f"๊ดด๋ฆฌ์œจ: {nav_data['premium_discount']:.2f}%")
    print(f"์ถ”์ ์˜ค์ฐจ: {nav_data['tracking_error']:.4f}")
    
    # ETF ๊ตฌ์„ฑ์ข…๋ชฉ
    holdings = kodex.get_holdings()
    for holding in holdings[:10]:
        print(f"{holding['symbol']}: {holding['weight']:.2f}%")
    
    # ELW ๊ทธ๋ฆญ์Šค ๋ถ„์„
    elw = api.elw("5XXXXX")  # ELW ์ข…๋ชฉ์ฝ”๋“œ
    
    # ์‹ค์‹œ๊ฐ„ ๊ทธ๋ฆญ์Šค
    greeks = elw.get_greeks()
    print(f"Delta: {greeks['delta']:.4f}")
    print(f"Gamma: {greeks['gamma']:.4f}")
    print(f"Theta: {greeks['theta']:.4f}")
    print(f"Vega: {greeks['vega']:.4f}")
    
    # ๋ฏผ๊ฐ๋„ ์ง€ํ‘œ
    sensitivity = elw.get_sensitivity_indicators()
    
    # ELW ์กฐ๊ฑด ๊ฒ€์ƒ‰ (REST API)
    elw_search = client.get_elw_condition_search(
        underlying_asset_code="201",  # KOSPI200
        right_type="1",  # ์ฝœ์˜ต์…˜
        sort_type="1"    # ์ƒ์Šน์œจ์ˆœ
    )
    
    # ์‹ค์‹œ๊ฐ„ ์กฐ๊ฑด๊ฒ€์ƒ‰ (WebSocket)
    async def on_condition_result(data):
        print(f"์กฐ๊ฑด๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ: {data['symbol']} - {data['name']}")
    
    # ์กฐ๊ฑด๊ฒ€์ƒ‰ ๋ชฉ๋ก ์กฐํšŒ
    await client.realtime.get_conditional_search_list()
    
    # ์กฐ๊ฑด๊ฒ€์ƒ‰ ์‹คํ–‰ (์ผ๋ฐ˜)
    await client.realtime.execute_conditional_search("1", "0")
    
    # ์กฐ๊ฑด๊ฒ€์ƒ‰ ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง
    await client.realtime.execute_conditional_search_realtime("1")
    
    # ์‹ค์‹œ๊ฐ„ ์กฐ๊ฑด๊ฒ€์ƒ‰ ํ•ด์ œ
    await client.realtime.cancel_conditional_search_realtime("1")

05_rankings_analysis.py - ๐Ÿ“ˆ ์‹œ์žฅ ์ˆœ์œ„ ๋ถ„์„

# ์‹œ์žฅ์˜ ๋ชจ๋“  ์ˆœ์œ„ ์ •๋ณด
with pyheroapi.connect(is_production=False) as api:  # SANDBOX MODE: set is_production=False explicitly
    # ๊ฑฐ๋ž˜๋Ÿ‰ ์ˆœ์œ„
    volume_leaders = api.rankings.volume_leaders(limit=20)
    print("๐Ÿ”ฅ ๊ฑฐ๋ž˜๋Ÿ‰ TOP 20")
    for i, stock in enumerate(volume_leaders, 1):
        print(f"{i:2d}. {stock['name']:10s} {stock['volume']:>10,}์ฃผ")
    
    # ์ƒ์Šน๋ฅ  ์ˆœ์œ„
    gainers = api.rankings.price_gainers(limit=10)
    print("\n๐Ÿ“ˆ ์ƒ์Šน๋ฅ  TOP 10")
    for stock in gainers:
        print(f"{stock['name']}: +{stock['change_rate']:.2f}%")
    
    # ์™ธ๊ตญ์ธ ์ˆœ๋งค์ˆ˜ ์ˆœ์œ„
    foreign_net_buy = api.rankings.foreign_net_buying(limit=15)
    print("\n๐ŸŒ ์™ธ๊ตญ์ธ ์ˆœ๋งค์ˆ˜ TOP 15")
    
    # ๊ธฐ๊ด€ ์ˆœ๋งค์ˆ˜ ์ˆœ์œ„  
    institutional_buy = api.rankings.institutional_net_buying(limit=15)
    
    # ํ”„๋กœ๊ทธ๋žจ ๋งค๋งค ๋ถ„์„
    program_trading = api.rankings.program_trading_activity()
    
    # ์„นํ„ฐ๋ณ„ ๋ถ„์„
    sector_performance = api.rankings.sector_performance()
    print("\n๐Ÿญ ์„นํ„ฐ ์„ฑ๊ณผ")
    for sector in sector_performance:
        print(f"{sector['name']}: {sector['change_rate']:+.2f}%")

06_charts_technical.py - ๐Ÿ“Š ์ฐจํŠธ & ๊ธฐ์ˆ ์  ๋ถ„์„

# ๋ชจ๋“  ์ฐจํŠธ ๋ฐ์ดํ„ฐ์™€ ๊ธฐ์ˆ ์  ๋ถ„์„
with pyheroapi.connect(is_production=False) as api:  # SANDBOX MODE: set is_production=False explicitly
    samsung = api.stock("005930")
    
    # ๋‹ค์–‘ํ•œ ์‹œ๊ฐ„ํ”„๋ ˆ์ž„ ์ฐจํŠธ
    daily_chart = samsung.get_chart_data("daily", period="6M")
    weekly_chart = samsung.get_chart_data("weekly", period="2Y") 
    monthly_chart = samsung.get_chart_data("monthly", period="5Y")
    
    # ๋ถ„๋ด‰ ์ฐจํŠธ (1๋ถ„, 5๋ถ„, 15๋ถ„, 30๋ถ„, 60๋ถ„)
    minute_1 = samsung.get_chart_data("1min", period="1D")
    minute_5 = samsung.get_chart_data("5min", period="5D")
    minute_30 = samsung.get_chart_data("30min", period="1M")
    
    # ๊ธฐ์ˆ ์  ์ง€ํ‘œ ๊ณ„์‚ฐ
    def calculate_sma(data, period=20):
        """๋‹จ์ˆœ์ด๋™ํ‰๊ท  ๊ณ„์‚ฐ"""
        prices = [float(d['close']) for d in data]
        sma = []
        for i in range(len(prices)):
            if i >= period - 1:
                avg = sum(prices[i-period+1:i+1]) / period
                sma.append(avg)
            else:
                sma.append(None)
        return sma
    
    # 20์ผ ์ด๋™ํ‰๊ท 
    sma_20 = calculate_sma(daily_chart, 20)
    
    # ๊ฑฐ๋ž˜๋Ÿ‰ ๋ถ„์„
    volume_analysis = samsung.volume_analysis(period="3M")
    
    # ์—…์ข… ์ฐจํŠธ ๋ถ„์„
    sector_chart = api.get_sector_chart("์ „๊ธฐ์ „์ž", period="1Y")

07_realtime_websocket.py - ๐ŸŒ ์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ

# WebSocket์„ ํ†ตํ•œ ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆฌ๋ฐ
import asyncio
from pyheroapi import RealtimeClient

async def main():
    # ์‹ค์‹œ๊ฐ„ ํด๋ผ์ด์–ธํŠธ ์ƒ์„ฑ
    realtime = RealtimeClient()
    await realtime.connect()
    
    # ์‹ค์‹œ๊ฐ„ ์‹œ์„ธ ์ฝœ๋ฐฑ
    def on_price_update(data):
        print(f"[{data['symbol']}] โ‚ฉ{data['price']:,} ({data['change']:+.2f}%)")
    
    # ์‹ค์‹œ๊ฐ„ ํ˜ธ๊ฐ€ ์ฝœ๋ฐฑ  
    def on_orderbook_update(data):
        print(f"๋งค์ˆ˜ํ˜ธ๊ฐ€: โ‚ฉ{data['best_bid']:,} / ๋งค๋„ํ˜ธ๊ฐ€: โ‚ฉ{data['best_ask']:,}")
    
    # ์‹ค์‹œ๊ฐ„ ์ฒด๊ฒฐ ์ฝœ๋ฐฑ
    def on_trade_update(data):
        print(f"์ฒด๊ฒฐ: {data['quantity']:,}์ฃผ @ โ‚ฉ{data['price']:,}")
    
    # ์‹ค์‹œ๊ฐ„ ๊ณ„์ขŒ ์ฝœ๋ฐฑ
    def on_account_update(data):
        print(f"๊ณ„์ขŒ๋ณ€ํ™”: ์ž”๊ณ  โ‚ฉ{data['balance']:,}")
    
    # ์‹ค์‹œ๊ฐ„ ์กฐ๊ฑด๊ฒ€์ƒ‰ ์ฝœ๋ฐฑ
    def on_conditional_search_result(data):
        print(f"๐ŸŽฏ ์กฐ๊ฑด๊ฒ€์ƒ‰ ํŽธ์ž…: {data['symbol']} - ์‹œ๊ฐ„: {data['time']}")
    
    def on_conditional_search_list(data):
        print(f"๐Ÿ“‹ ์กฐ๊ฑด๊ฒ€์ƒ‰ ๋ชฉ๋ก: {len(data)}๊ฐœ ์กฐ๊ฑด์‹ ํ™•์ธ")
    
    # ๊ตฌ๋… ์„ค์ •
    await realtime.subscribe_price("005930", on_price_update)
    await realtime.subscribe_orderbook("005930", on_orderbook_update)  
    await realtime.subscribe_trades("005930", on_trade_update)
    await realtime.subscribe_account("your_account", on_account_update)
    
    # ์กฐ๊ฑด๊ฒ€์ƒ‰ ์ฝœ๋ฐฑ ๋“ฑ๋ก
    realtime.register_callback('conditional_search_realtime', on_conditional_search_result)
    realtime.register_callback('conditional_search_list', on_conditional_search_list)
    
    # ์กฐ๊ฑด๊ฒ€์ƒ‰ ๋ชฉ๋ก ์กฐํšŒ
    await realtime.get_conditional_search_list()
    
    # ์‹ค์‹œ๊ฐ„ ์กฐ๊ฑด๊ฒ€์ƒ‰ ์‹œ์ž‘ (์กฐ๊ฑด์‹ ๋ฒˆํ˜ธ "1")
    await realtime.execute_conditional_search_realtime("1")
    
    # ๋‹ค์ค‘ ์ข…๋ชฉ ๊ตฌ๋…
    symbols = ["005930", "000660", "035420", "005380", "068270"]
    for symbol in symbols:
        await realtime.subscribe_price(symbol, on_price_update)
    
    # ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ˆ˜์‹  ๋Œ€๊ธฐ
    print("๐Ÿ”ด ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ˆ˜์‹  ์ค‘... (Ctrl+C๋กœ ์ค‘๋‹จ)")
    try:
        await realtime.listen()
    except KeyboardInterrupt:
        print("\nโน๏ธ ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ˆ˜์‹  ์ค‘๋‹จ")
        await realtime.disconnect()

# ์‹คํ–‰
if __name__ == "__main__":
    asyncio.run(main())

๐Ÿ”ฅ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ

๐ŸŽฏ ์Šค๋งˆํŠธ ํฌํŠธํด๋ฆฌ์˜ค ๊ด€๋ฆฌ

with pyheroapi.connect(is_production=False) as api:  # SANDBOX MODE: set is_production=False explicitly
    account = api.account()
    
    # ์ „์ฒด ํฌํŠธํด๋ฆฌ์˜ค ๋ถ„์„
    portfolio = account.get_portfolio_analysis()
    print(f"์ด ํ‰๊ฐ€๊ธˆ์•ก: โ‚ฉ{portfolio['total_value']:,.0f}")
    print(f"์‹คํ˜„์†์ต: โ‚ฉ{portfolio['realized_pnl']:,.0f}")
    print(f"ํ‰๊ฐ€์†์ต: โ‚ฉ{portfolio['unrealized_pnl']:,.0f}")
    print(f"์ด ์ˆ˜์ต๋ฅ : {portfolio['total_return']:.2f}%")
    
    # ์ข…๋ชฉ๋ณ„ ํฌ์ง€์…˜
    positions = account.positions
    for pos in positions:
        print(f"{pos['name']}: {pos['quantity']:,}์ฃผ")
        print(f"  ํ‰๊ท ๋‹จ๊ฐ€: โ‚ฉ{pos['avg_price']:,}")
        print(f"  ํ˜„์žฌ๊ฐ€: โ‚ฉ{pos['current_price']:,}")
        print(f"  ์ˆ˜์ต๋ฅ : {pos['return_rate']:+.2f}%")
        print(f"  ํ‰๊ฐ€์†์ต: โ‚ฉ{pos['unrealized_pnl']:+,.0f}")
    
    # ์ผ๋ณ„ ์†์ต ๋‚ด์—ญ
    daily_pnl = account.get_daily_pnl(days=30)
    total_trades = sum(day['trade_count'] for day in daily_pnl)
    total_pnl = sum(day['realized_pnl'] for day in daily_pnl)
    
    print(f"\n๐Ÿ“Š 30์ผ ๊ฑฐ๋ž˜ ํ†ต๊ณ„")
    print(f"์ด ๊ฑฐ๋ž˜ ํšŸ์ˆ˜: {total_trades:,}ํšŒ")
    print(f"์‹คํ˜„์†์ต ํ•ฉ๊ณ„: โ‚ฉ{total_pnl:+,.0f}")

๐Ÿ“Š ๊ณ ๊ธ‰ ์‹œ์žฅ ๋ถ„์„

with pyheroapi.connect(is_production=False) as api:  # SANDBOX MODE: set is_production=False explicitly
    # ์‹œ์žฅ ์ „์ฒด ํ˜„ํ™ฉ
    market_summary = api.get_market_summary()
    print(f"KOSPI: {market_summary['kospi']['value']:.2f} ({market_summary['kospi']['change']:+.2f}%)")
    print(f"KOSDAQ: {market_summary['kosdaq']['value']:.2f} ({market_summary['kosdaq']['change']:+.2f}%)")
    
    # ์™ธ๊ตญ์ธ/๊ธฐ๊ด€ ๋งค๋งค ๋™ํ–ฅ
    institutional_flow = api.get_institutional_trading()
    print(f"\n๐Ÿ’ฐ ์™ธ๊ตญ์ธ ์ˆœ๋งค๋งค: โ‚ฉ{institutional_flow['foreign_net']:+,.0f}์–ต")
    print(f"๐Ÿ’ฐ ๊ธฐ๊ด€ ์ˆœ๋งค๋งค: โ‚ฉ{institutional_flow['institution_net']:+,.0f}์–ต")
    
    # ํ”„๋กœ๊ทธ๋žจ ๋งค๋งค ํ˜„ํ™ฉ
    program_trading = api.get_program_trading()
    print(f"๐Ÿ“ˆ ํ”„๋กœ๊ทธ๋žจ ๋งค์ˆ˜: โ‚ฉ{program_trading['buy_amount']:,.0f}์–ต")
    print(f"๐Ÿ“‰ ํ”„๋กœ๊ทธ๋žจ ๋งค๋„: โ‚ฉ{program_trading['sell_amount']:,.0f}์–ต")
    
    # ์‹ ์šฉ๊ฑฐ๋ž˜ ํ˜„ํ™ฉ
    credit_info = api.get_credit_balance()
    print(f"๐Ÿฆ ์‹ ์šฉ์ž”๊ณ : โ‚ฉ{credit_info['credit_balance']:,.0f}์–ต")
    print(f"๐Ÿฆ ๋Œ€์ฃผ์ž”๊ณ : โ‚ฉ{credit_info['lending_balance']:,.0f}์–ต")

โšก ๊ณ ์„ฑ๋Šฅ ์ผ๊ด„ ์ฒ˜๋ฆฌ

import asyncio
from concurrent.futures import ThreadPoolExecutor

async def analyze_multiple_stocks(symbols):
    """์—ฌ๋Ÿฌ ์ข…๋ชฉ ๋™์‹œ ๋ถ„์„"""
    with pyheroapi.connect(is_production=False) as api:  # SANDBOX MODE: set is_production=False explicitly
        with ThreadPoolExecutor(max_workers=10) as executor:
            # ๋™์‹œ์— ์—ฌ๋Ÿฌ ์ข…๋ชฉ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘
            futures = []
            for symbol in symbols:
                future = executor.submit(api.stock(symbol).get_complete_data)
                futures.append((symbol, future))
            
            results = {}
            for symbol, future in futures:
                try:
                    data = future.result(timeout=30)
                    results[symbol] = data
                    print(f"โœ… {symbol}: ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘ ์™„๋ฃŒ")
                except Exception as e:
                    print(f"โŒ {symbol}: ์˜ค๋ฅ˜ - {e}")
            
            return results

# ๋Œ€ํ˜•์ฃผ TOP 20 ๋™์‹œ ๋ถ„์„
large_caps = ["005930", "000660", "035420", "005380", "068270", 
              "207940", "005490", "035720", "000270", "006400"]

results = asyncio.run(analyze_multiple_stocks(large_caps))

๐Ÿ›ก๏ธ ์•ˆ์ „ ๊ฐ€์ด๋“œ

๐Ÿ”’ ๋ณด์•ˆ ๋ชจ๋ฒ” ์‚ฌ๋ก€

import os
from pyheroapi import KiwoomClient

# โœ… ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์‚ฌ์šฉ (๊ถŒ์žฅ)
appkey = os.getenv("KIWOOM_APPKEY")
secretkey = os.getenv("KIWOOM_SECRETKEY")

# โŒ ํ•˜๋“œ์ฝ”๋”ฉ ๊ธˆ์ง€
# appkey = "PAKXXXXXXXX"  # ์ ˆ๋Œ€ ๊ธˆ์ง€!

# ์•ˆ์ „ํ•œ ํด๋ผ์ด์–ธํŠธ ์ƒ์„ฑ
client = KiwoomClient.create_with_credentials(
    appkey=appkey,
    secretkey=secretkey,
    is_production=False,  # SANDBOX MODE: set is_production=False explicitly
    timeout=30,
    retry_attempts=3
)

๐ŸŽฏ ๊ฑฐ๋ž˜ ์•ˆ์ „ ์ ๊ฒ€

def safe_trading_example():
    """์•ˆ์ „ํ•œ ๊ฑฐ๋ž˜ ํ”„๋กœ์„ธ์Šค"""
    with pyheroapi.connect(is_production=False) as api:  # SANDBOX MODE: set is_production=False explicitly
        account = api.account()
        
        # 1. ๊ณ„์ขŒ ์ƒํƒœ ํ™•์ธ
        balance = account.balance
        available = balance["available_balance"]
        print(f"์ฃผ๋ฌธ๊ฐ€๋Šฅ๊ธˆ์•ก: โ‚ฉ{available:,.0f}")
        
        # 2. ์ข…๋ชฉ ๋ถ„์„
        samsung = api.stock("005930")
        current_price = samsung.current_price
        quote = samsung.quote
        
        # 3. ์•ˆ์ „ ์ ๊ฒ€
        order_amount = current_price * 100  # 100์ฃผ ์ฃผ๋ฌธ๊ธˆ์•ก
        
        if available < order_amount:
            print("โŒ ์ฃผ๋ฌธ๊ฐ€๋Šฅ๊ธˆ์•ก ๋ถ€์กฑ")
            return
        
        if quote["volume"] < 1000:  # ์ตœ์†Œ ๊ฑฐ๋ž˜๋Ÿ‰ ์ ๊ฒ€
            print("โŒ ๊ฑฐ๋ž˜๋Ÿ‰ ๋ถ€์กฑ - ์ฃผ๋ฌธ ์ทจ์†Œ")
            return
        
        # 4. ์ฃผ๋ฌธ ์‹คํ–‰ (์ƒŒ๋“œ๋ฐ•์Šค์—์„œ๋งŒ!)
        result = api.trading.buy("005930", quantity=100, price=current_price)
        
        if result["success"]:
            print(f"โœ… ์ฃผ๋ฌธ ์„ฑ๊ณต: {result['order_number']}")
            
            # 5. ์ฃผ๋ฌธ ์ƒํƒœ ๋ชจ๋‹ˆํ„ฐ๋ง
            order_status = api.trading.get_order_status(result["order_number"])
            print(f"์ฃผ๋ฌธ ์ƒํƒœ: {order_status['status']}")
        else:
            print(f"โŒ ์ฃผ๋ฌธ ์‹คํŒจ: {result['message']}")

# ์‹คํ–‰
safe_trading_example()

๐ŸŽ“ ๋‹จ๊ณ„๋ณ„ ํ•™์Šต ๊ฐ€์ด๋“œ

1๋‹จ๊ณ„: ๊ธฐ์ดˆ (1-2์ผ)

2๋‹จ๊ณ„: ๊ฑฐ๋ž˜ (3-5์ผ)

  • 03_trading_orders.py - ์ฃผ๋ฌธ ์‹คํ–‰๊ณผ ๊ด€๋ฆฌ
  • ์ƒŒ๋“œ๋ฐ•์Šค์—์„œ ์ถฉ๋ถ„ํ•œ ์—ฐ์Šต

3๋‹จ๊ณ„: ๋ถ„์„ (1์ฃผ)

4๋‹จ๊ณ„: ๊ณ ๊ธ‰ (2์ฃผ)

  • 07_realtime_websocket.py - ์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ
  • ํฌํŠธํด๋ฆฌ์˜ค ๊ด€๋ฆฌ ์‹œ์Šคํ…œ ๊ตฌ์ถ•
  • ์ž๋™ ๋งค๋งค ์‹œ์Šคํ…œ ๊ฐœ๋ฐœ

๐Ÿ”ง ํ™˜๊ฒฝ ์„ค์ •

API ํ‚ค ๋ฐœ๊ธ‰

  1. ํ‚ค์›€ OpenAPI ํ™ˆํŽ˜์ด์ง€์ง€ ๋ฐฉ๋ฌธ
  2. ๊ณ„์ • ์ƒ์„ฑ ๋ฐ ๋กœ๊ทธ์ธ
  3. REST API ์„œ๋น„์Šค ์‹ ์ฒญ
  4. APP KEY์™€ SECRET KEY ๋ฐœ๊ธ‰

๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์„ค์ •

# 1. ํ”„๋กœ์ ํŠธ ํด๋” ์ƒ์„ฑ
mkdir my_trading_bot
cd my_trading_bot

# 2. ๊ฐ€์ƒํ™˜๊ฒฝ ์ƒ์„ฑ
python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate

# 3. PyHeroAPI ์„ค์น˜
pip install pyheroapi[all]

# 4. ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์„ค์ •
echo 'export KIWOOM_APPKEY="ํ‚ค์›€ REST API APPKEY' >> ~/.bashrc
echo 'export KIWOOM_SECRETKEY="ํ‚ค์›€ REST API SECRETKEY"' >> ~/.bashrc
source ~/.bashrc

# 5. ์ฒซ ๋ฒˆ์งธ ํ…Œ์ŠคํŠธ
python -c "
import pyheroapi
with pyheroapi.connect(is_production=False) as api:
    print('โœ… ์—ฐ๊ฒฐ ์„ฑ๊ณต!')
    print(f'์‚ผ์„ฑ์ „์ž: โ‚ฉ{api.stock(\"005930\").current_price:,.0f}')
"

๐Ÿ“‹ ์‹œ์Šคํ…œ ์š”๊ตฌ์‚ฌํ•ญ

  • Python: 3.8 ์ด์ƒ
  • ์šด์˜์ฒด์ œ: Windows, macOS, Linux
  • ๋ฉ”๋ชจ๋ฆฌ: ์ตœ์†Œ 512MB (์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ ์‹œ 1GB ๊ถŒ์žฅ)
  • ๋„คํŠธ์›Œํฌ: ์•ˆ์ •์ ์ธ ์ธํ„ฐ๋„ท ์—ฐ๊ฒฐ

์˜์กด์„ฑ ํŒจํ‚ค์ง€

requests>=2.25.0        # HTTP ํด๋ผ์ด์–ธํŠธ
pydantic>=2.0.0         # ๋ฐ์ดํ„ฐ ๊ฒ€์ฆ
typing-extensions>=4.0.0 # ํƒ€์ž… ํžŒํŠธ
websockets>=11.0.0      # ์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ (์„ ํƒ์‚ฌํ•ญ)

โš ๏ธ ์ค‘์š” ๊ณต์ง€์‚ฌํ•ญ

๋ฉด์ฑ…์กฐํ•ญ

  • ๐Ÿ“– ๊ต์œก ๋ชฉ์ : ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๊ต์œก ๋ฐ ๊ฐœ๋ฐœ ๋ชฉ์ ์œผ๋กœ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค
  • ๐Ÿ’ฐ ํˆฌ์ž ์ฑ…์ž„: ์‹ค์ œ ๊ฑฐ๋ž˜๋กœ ์ธํ•œ ์†์‹ค์— ๋Œ€ํ•ด ์ฑ…์ž„์ง€์ง€ ์•Š์Šต๋‹ˆ๋‹ค
  • ๐Ÿงช ์ถฉ๋ถ„ํ•œ ํ…Œ์ŠคํŠธ: ์‹ค๊ฑฐ๋ž˜ ์ „ ๋ฐ˜๋“œ์‹œ ์ƒŒ๋“œ๋ฐ•์Šค์—์„œ ์ถฉ๋ถ„ํžˆ ํ…Œ์ŠคํŠธํ•˜์„ธ์š”
  • ๐Ÿ“Š ์‹œ์žฅ ๋ฆฌ์Šคํฌ: ์ฃผ์‹ ํˆฌ์ž์—๋Š” ์›๊ธˆ ์†์‹ค ์œ„ํ—˜์ด ์žˆ์Šต๋‹ˆ๋‹ค

์‚ฌ์šฉ ์ œํ•œ์‚ฌํ•ญ

  • ๐Ÿ• API ํ˜ธ์ถœ ์ œํ•œ: ์ดˆ๋‹น ์š”์ฒญ ์ˆ˜ ์ œํ•œ (์ž๋™ ๊ด€๋ฆฌ๋จ)
  • ๐Ÿ›๏ธ ์žฅ ์‹œ๊ฐ„ ์ œํ•œ: ์ผ๋ถ€ ๊ธฐ๋Šฅ์€ ๊ฐœ์žฅ ์‹œ๊ฐ„์—๋งŒ ์ž‘๋™
  • ๐Ÿ“ฑ ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ: ์•ฝ๊ฐ„์˜ ์ง€์—ฐ์ด ์žˆ์„ ์ˆ˜ ์žˆ์Œ
  • ๐Ÿ”’ ๋ณด์•ˆ: API ํ‚ค๋Š” ์ ˆ๋Œ€ ๊ณต๊ฐœํ•˜์ง€ ๋งˆ์„ธ์š”

๐Ÿค ์ปค๋ฎค๋‹ˆํ‹ฐ

๊ธฐ์—ฌํ•˜๊ธฐ

  1. ๐Ÿด ์ €์žฅ์†Œ ํฌํฌ
  2. ๐ŸŒฟ ๊ธฐ๋Šฅ ๋ธŒ๋žœ์น˜ ์ƒ์„ฑ: git checkout -b feature/amazing-feature
  3. ๐Ÿ“ ๋ณ€๊ฒฝ์‚ฌํ•ญ ์ปค๋ฐ‹: git commit -m 'Add amazing feature'
  4. ๐Ÿ“ค ๋ธŒ๋žœ์น˜ ํ‘ธ์‹œ: git push origin feature/amazing-feature
  5. ๐Ÿ”„ Pull Request ์ƒ์„ฑ

์ง€์› ์ฑ„๋„

๋ผ์ด์„ ์Šค

MIT License - ์ž์„ธํ•œ ๋‚ด์šฉ์€ LICENSE ํŒŒ์ผ ์ฐธ์กฐ


โญ ์ด ํ”„๋กœ์ ํŠธ๊ฐ€ ๋„์›€์ด ๋˜์…จ๋‹ค๋ฉด ๋ณ„ํ‘œ๋ฅผ ๋ˆŒ๋Ÿฌ์ฃผ์„ธ์š”!

GitHub stars GitHub forks

Happy Trading! ๐Ÿ“ˆ๐Ÿš€

โš ๏ธ ๋ฉด์ฑ…์กฐํ•ญ: ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๊ต์œก ๋ฐ ๊ฐœ๋ฐœ ๋ชฉ์ ์œผ๋กœ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค. ์‹ค์ œ ๊ฑฐ๋ž˜์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์†์‹ค์— ๋Œ€ํ•ด์„œ๋Š” ์ฑ…์ž„์ง€์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ฑฐ๋ž˜ ์‹œ ์ฃผ์˜ํ•˜์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages