ํ‹ฐ์Šคํ† ๋ฆฌ ๋ทฐ

๋ฐ˜์‘ํ˜•

๐Ÿค– ํ€€ํŠธ ํˆฌ์ž ์‹ค์ „ – ์ฆ๊ถŒ์‚ฌ API๋ฅผ ํ™œ์šฉํ•œ ์ž๋™ ๋งค๋งค ์‹œ์Šคํ…œ ๊ตฌ์ถ•ํ•˜๊ธฐ

์ง€๊ธˆ๊นŒ์ง€๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ง‘ํ•˜๊ณ , ๋ฐฑํ…Œ์ŠคํŠธํ•˜๊ณ , ๋ฆฌ๋ฐธ๋Ÿฐ์‹ฑํ•˜๋ฉฐ
AI๋กœ ๊ฐ€์ค‘์น˜๋ฅผ ์กฐ์ •ํ•˜๋Š” ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ๋‹จ๊ณ„๊นŒ์ง€ ์™„์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด์ œ ๋‚จ์€ ๊ฑด ๋‹จ ํ•˜๋‚˜ —
**“์‹ค์ œ ๋งค๋งค๋ฅผ ์ž๋™์œผ๋กœ ์‹คํ–‰ํ•˜๋Š” ์‹œ์Šคํ…œ”**์ž…๋‹ˆ๋‹ค.

์˜ค๋Š˜์€ ๊ตญ๋‚ด ๊ฐœ์ธ ํˆฌ์ž์ž๋“ค์ด ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š”
**ํ‚ค์›€์ฆ๊ถŒ OpenAPI (Python ์—ฐ๋™)**์„ ๊ธฐ๋ฐ˜์œผ๋กœ,
์‹ค์ „ ๋งค๋งค ์ž๋™ํ™”๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ณผ์ •์„ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค.


1๏ธโƒฃ ์‹œ์Šคํ…œ ๊ฐœ์š”

์ž๋™ ๋งค๋งค ์‹œ์Šคํ…œ์€ ํฌ๊ฒŒ ๋‹ค์Œ ๋‹ค์„ฏ ๋‹จ๊ณ„๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค.

[1] ์ข…๋ชฉ ์„ ์ • (ํŒฉํ„ฐ ์ „๋žต ๊ธฐ๋ฐ˜)
→ [2] ๋งค์ˆ˜ ์กฐ๊ฑด ํŒ๋‹จ (์‹œ๊ทธ๋„)
→ [3] ์ฃผ๋ฌธ ์‹คํ–‰ (API)
→ [4] ํฌํŠธํด๋ฆฌ์˜ค ๊ธฐ๋ก
→ [5] ์ˆ˜์ต๋ฅ  ๋ฐ ๋กœ๊ทธ ๊ด€๋ฆฌ

์ด ๊ตฌ์กฐ๋Š” ์‹ค์ œ ํ€€ํŠธ ํŽ€๋“œ ๋งค๋งค ์‹œ์Šคํ…œ์˜ ์ถ•์†ŒํŒ์ž…๋‹ˆ๋‹ค.
๋‹จ, ๊ฐœ์ธ ํˆฌ์ž์ž๋Š” API ํ˜ธ์ถœ ์ œํ•œ๊ณผ ์‹ค์‹œ๊ฐ„ ์ฒ˜๋ฆฌ ํ•œ๊ณ„๋ฅผ ๋ฐ˜๋“œ์‹œ ๊ณ ๋ คํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.


2๏ธโƒฃ ํ‚ค์›€ OpenAPI ์„ค์น˜

  1. ํ‚ค์›€์ฆ๊ถŒ ์˜์›…๋ฌธ HTS ์„ค์น˜
  2. ํ‚ค์›€์ฆ๊ถŒ OpenAPI+ ์„ค์น˜
  3. ActiveX ๊ธฐ๋ฐ˜์ด๋ฏ€๋กœ ๋ฐ˜๋“œ์‹œ Windows ํ™˜๊ฒฝ ํ•„์š”
  4. ๋กœ๊ทธ์ธ ํ›„ KOA Studio ์‹คํ–‰ → API ๋™์ž‘ ํ…Œ์ŠคํŠธ

3๏ธโƒฃ Python ์—ฐ๋™

ํ‚ค์›€ OpenAPI๋Š” COM(Component Object Model) ๋ฐฉ์‹์œผ๋กœ ํ†ต์‹ ํ•ฉ๋‹ˆ๋‹ค.
์ด๋ฅผ Python์—์„œ ๋‹ค๋ฃจ๋ ค๋ฉด PyQt5 + pythoncom + win32com.client ์กฐํ•ฉ์„ ์”๋‹ˆ๋‹ค.

pip install pyqt5 pywin32

4๏ธโƒฃ ๊ธฐ๋ณธ ๋งค์ˆ˜/๋งค๋„ ์ฝ”๋“œ ์˜ˆ์‹œ

๋ฐ˜์‘ํ˜•
import sys
from PyQt5.QtWidgets import QApplication
from PyQt5.QAxContainer import QAxWidget
import time

app = QApplication(sys.argv)
kiwoom = QAxWidget("KHOPENAPI.KHOpenAPICtrl.1")

# ๋กœ๊ทธ์ธ
kiwoom.dynamicCall("CommConnect()")
time.sleep(5)

# ๊ณ„์ขŒ๋ฒˆํ˜ธ ํ™•์ธ
account = kiwoom.dynamicCall("GetLoginInfo(QString)", ["ACCNO"]).strip(';')
print("๊ณ„์ขŒ๋ฒˆํ˜ธ:", account)

# ๋งค์ˆ˜ ์ฃผ๋ฌธ ์˜ˆ์‹œ
kiwoom.dynamicCall(
    "SendOrder(QString, QString, QString, int, QString, int, int, QString, QString)",
    [
        "๋งค์ˆ˜์ฃผ๋ฌธ",
        "0101",
        account,
        1,  # 1: ๋งค์ˆ˜ / 2: ๋งค๋„
        "005930",  # ์‚ผ์„ฑ์ „์ž
        10,  # ์ˆ˜๋Ÿ‰
        0,  # ๊ฐ€๊ฒฉ (์‹œ์žฅ๊ฐ€)
        "03",  # ์ฃผ๋ฌธ์œ ํ˜•: ์‹œ์žฅ๊ฐ€
        ""
    ]
)

print("โœ… ๋งค์ˆ˜ ์ฃผ๋ฌธ ์ „์†ก ์™„๋ฃŒ")

์ด ์ฝ”๋“œ๋Š” ์‚ผ์„ฑ์ „์ž 10์ฃผ๋ฅผ ์‹œ์žฅ๊ฐ€๋กœ ๋งค์ˆ˜ํ•˜๋Š” ๊ฐ„๋‹จํ•œ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค.
SendOrder์˜ ํŒŒ๋ผ๋ฏธํ„ฐ ๊ตฌ์กฐ๋ฅผ ์ดํ•ดํ•˜๋ฉด, ๋ชจ๋“  ์ฃผ๋ฌธ์„ ์ž๋™ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


5๏ธโƒฃ ์ž๋™ ๋งค๋งค ๋กœ์ง (ํŒฉํ„ฐ ์ „๋žต ๊ฒฐํ•ฉ)

์ด์ œ ์•ž์„œ ๋งŒ๋“  ํŒฉํ„ฐ ๊ธฐ๋ฐ˜ ํฌํŠธํด๋ฆฌ์˜ค ๊ฒฐ๊ณผ๋ฅผ ํ™œ์šฉํ•ด๋ด…์‹œ๋‹ค.
์˜ˆ๋ฅผ ๋“ค์–ด, ๋งค์ผ ์˜ค์ „ 9์‹œ ์ž๋™์œผ๋กœ ๋‹ค์Œ ์กฐ๊ฑด์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

from datetime import datetime
import schedule, pandas as pd

def auto_trade():
    portfolio = pd.read_csv("factor_rank.csv")  # ๋ฆฌ๋ฐธ๋Ÿฐ์‹ฑ ๊ฒฐ๊ณผ ํŒŒ์ผ
    top_picks = portfolio.head(3)["Ticker"].tolist()

    for code in top_picks:
        print(f"{datetime.now()} โ–ถ {code} ๋งค์ˆ˜ ์‹œ๋„ ์ค‘…")
        kiwoom.dynamicCall(
            "SendOrder(QString, QString, QString, int, QString, int, int, QString, QString)",
            ["์ž๋™๋งค์ˆ˜", "0101", account, 1, code, 5, 0, "03", ""]
        )

schedule.every().day.at("09:01").do(auto_trade)

while True:
    schedule.run_pending()
    time.sleep(60)

๐Ÿ‘‰ ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋งค์ผ 9์‹œ 1๋ถ„,
๊ฐ€์žฅ ์ƒ์œ„ ํŒฉํ„ฐ ์ ์ˆ˜๋ฅผ ๊ฐ€์ง„ ์ข…๋ชฉ 3๊ฐœ๋ฅผ ์ž๋™์œผ๋กœ ๋งค์ˆ˜ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.


6๏ธโƒฃ ํฌํŠธํด๋ฆฌ์˜ค ๊ธฐ๋ก & ๋ฆฌํฌํŠธ ์—ฐ๋™

์ž๋™ ๋งค๋งค๊ฐ€ ์‹คํ–‰๋  ๋•Œ๋งˆ๋‹ค

  • ๋งค์ˆ˜ ์ข…๋ชฉ, ์ˆ˜๋Ÿ‰, ์ฒด๊ฒฐ ๊ฐ€๊ฒฉ, ์ž”๊ณ , ์ˆ˜์ต๋ฅ ์„ CSV๋กœ ์ €์žฅํ•˜๊ณ ,
  • ๊ทธ ๋ฐ์ดํ„ฐ๋ฅผ PDF ๋ฆฌํฌํŠธ ์ƒ์„ฑ ์ฝ”๋“œ์™€ ์—ฐ๋™ํ•˜๋ฉด
    ๋งค์ผ ์ž๋™์œผ๋กœ ์—…๋ฐ์ดํŠธ๋œ ์‹ค์ „ ์„ฑ๊ณผ ๋ฆฌํฌํŠธ๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.
with open("trade_log.csv", "a", encoding="utf-8") as f:
    f.write(f"{datetime.now()},{code},๋งค์ˆ˜,5\n")

7๏ธโƒฃ ๋ฆฌ์Šคํฌ ๊ด€๋ฆฌ: Stop Loss & Take Profit

์ž๋™ ๋งค๋งค์˜ ํ•ต์‹ฌ์€ “์†์ต ์ž๋™ ์ œ์–ด”์ž…๋‹ˆ๋‹ค.

def risk_control(current_price, avg_price):
    profit_rate = (current_price - avg_price) / avg_price
    if profit_rate <= -0.05:
        print("๐Ÿ“‰ -5% ์†์ ˆ ์‹คํ–‰")
    elif profit_rate >= 0.10:
        print("๐Ÿš€ +10% ์ต์ ˆ ์‹คํ–‰")

์ด ๋กœ์ง์„ ์‹ค์‹œ๊ฐ„ ๊ฐ€๊ฒฉ ๋ฐ์ดํ„ฐ์™€ ์—ฐ๊ฒฐํ•˜๋ฉด,
์ •ํ•ด๋‘” ์†์ ˆ/์ต์ ˆ ๊ตฌ๊ฐ„์—์„œ ์ž๋™ ๋งค๋„๊นŒ์ง€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.


โš ๏ธ ์ฃผ์˜ํ•  ์ 

์ฃผ์˜ ํ•ญ๋ชฉ ์„ค๋ช…

๋กœ๊ทธ์ธ ์„ธ์…˜ 24์‹œ๊ฐ„ ์œ ์ง€ ๋ถˆ๊ฐ€ → ์žฌ๋กœ๊ทธ์ธ ์ž๋™ํ™” ํ•„์š”
API ํ˜ธ์ถœ ์ œํ•œ ์ดˆ๋‹น 5ํšŒ ๋ฏธ๋งŒ ๊ถŒ์žฅ
์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ HTS์™€ ๋™์ผํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Œ
๋ณด์•ˆ ํ‚ค ํŒŒ์ผ, ๊ณ„์ขŒ๋ฒˆํ˜ธ, ๋น„๋ฐ€๋ฒˆํ˜ธ ์ ˆ๋Œ€ ํ•˜๋“œ์ฝ”๋”ฉ ๊ธˆ์ง€

๐Ÿ“Œ ์ •๋ฆฌ

๋‹จ๊ณ„ ์„ค๋ช…

1 ํ‚ค์›€ OpenAPI ์„ค์น˜ ๋ฐ ๋กœ๊ทธ์ธ
2 PyQt5๋กœ API ํ˜ธ์ถœ ๊ตฌ์กฐ ์ดํ•ด
3 ๋งค์ˆ˜/๋งค๋„ ์ฃผ๋ฌธ ์ž๋™ํ™”
4 ํŒฉํ„ฐ ์ „๋žต ๊ฒฐํ•ฉ์œผ๋กœ ์ž๋™ ์ข…๋ชฉ ์„ ์ •
5 ์ˆ˜์ต๋ฅ  ๊ธฐ๋ก + ๋ฆฌํฌํŠธ ์—ฐ๋™
6 ์†์ ˆ·์ต์ ˆ ์กฐ๊ฑด์œผ๋กœ ๋ฆฌ์Šคํฌ ์ œ์–ด

๐Ÿ‘‰ ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด “๋งค์ผ ์ž๋™์œผ๋กœ ํ€€ํŠธ ์ „๋žต์ด ์‹คํ–‰๋˜๋Š” ์™„์ „ ์ž๋™ ๋งค๋งค ์‹œ์Šคํ…œ”์ด ์™„์„ฑ๋ฉ๋‹ˆ๋‹ค.


๐Ÿ“˜ ๋‹ค์Œ ๊ธ€ ์˜ˆ๊ณ 

๋‹ค์Œ ํŽธ์—์„œ๋Š” **“๋ฐฑ์—”๋“œ ์„œ๋ฒ„์—์„œ ์ž๋™ ๋งค๋งค ์‹คํ–‰ํ•˜๊ธฐ – Flask + ์Šค์ผ€์ค„๋Ÿฌ + DB ์—ฐ๋™ ๊ตฌ์กฐ”**๋ฅผ ๋‹ค๋ฃน๋‹ˆ๋‹ค.
์ฆ‰, Python ์ฝ”๋“œ๊ฐ€ ์•„๋‹Œ API ์„œ๋ฒ„ ํ˜•ํƒœ์˜ ํ€€ํŠธ ๋งค๋งค ์—”์ง„์œผ๋กœ ์—…๊ทธ๋ ˆ์ด๋“œํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ๋“œ๋ฆด๊ฒŒ์š”.


 

ํ€€ํŠธ์ž๋™๋งค๋งค,ํ‚ค์›€์ฆ๊ถŒAPI,ํŒŒ์ด์ฌ์ฃผ์‹,์ž๋™๋งค๋งค์‹œ์Šคํ…œ,ํŒฉํ„ฐํˆฌ์ž,๋ฆฌ๋ฐธ๋Ÿฐ์‹ฑ,์ฃผ์‹๋ด‡,๋ฐ์ดํ„ฐํˆฌ์ž,PyQt5,์ž๋™ํˆฌ์ž


 

โ€ป ์ด ํฌ์ŠคํŒ…์€ ์ฟ ํŒก ํŒŒํŠธ๋„ˆ์Šค ํ™œ๋™์˜ ์ผํ™˜์œผ๋กœ, ์ด์— ๋”ฐ๋ฅธ ์ผ์ •์•ก์˜ ์ˆ˜์ˆ˜๋ฃŒ๋ฅผ ์ œ๊ณต๋ฐ›์Šต๋‹ˆ๋‹ค.
๊ณต์ง€์‚ฌํ•ญ
์ตœ๊ทผ์— ์˜ฌ๋ผ์˜จ ๊ธ€
์ตœ๊ทผ์— ๋‹ฌ๋ฆฐ ๋Œ“๊ธ€
Total
Today
Yesterday
๋งํฌ
ยซ   2026/02   ยป
์ผ ์›” ํ™” ์ˆ˜ ๋ชฉ ๊ธˆ ํ† 
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
๊ธ€ ๋ณด๊ด€ํ•จ
๋ฐ˜์‘ํ˜•