🧱 1단계 – 콘솔에서 혼자 거래를 시작합니다

처음에는 파이썬으로 주식 매매 프로그램을 간단히 만들었습니다.
엑셀 파일에 종목, 매수가, 매도가를 기록하고, 매매 조건에 따라 콘솔에 매수/매도 메시지를 출력합니다.

🔹 사용한 코드:

            current_price = get_current_price(kiwoom, code)
            print(f"[{name}] 현재가: {current_price} | 매수: {buy_price} | 매도: {sell_price}")

            qty = 10  # 10주 매매 예시

            if current_price <= buy_price and name not in bought:
                print(f"[매수] {name} @ {current_price}")
                kiwoom.SendOrder("매수", "1001", account, 1, code, qty, 0, "03", "")
                bought.add(name)
                sold.discard(name)

            elif current_price >= sell_price and name not in sold:
                print(f"[매도] {name} @ {current_price}")
                kiwoom.SendOrder("매도", "1002", account, 2, code, qty, 0, "03", "")
                sold.add(name)
                bought.discard(name)

하지만 문제는 매매 내역만 볼 수 있고, 현재 어떤 종목을 가지고 있는지 확인하기가 어렵다는 점입니다.
보유 종목 현황이 없어 불편함을 느낍니다.


💡 2단계 – GUI로 보유 주식을 눈으로 확인하고자 합니다

콘솔 출력만으로는 부족하다는 것을 깨닫고, GUI를 통해 보유 주식을 시각적으로 보여주자는 결심을 합니다.
PyQt를 이용하여 간단한 창과 표를 만드는 것부터 시작합니다.


🪟 3단계 – PyQt5로 테이블 GUI를 구성합니다

PyQt5를 사용하여 종목명, 수량, 평균 매수가를 보여주는 테이블을 생성합니다.

🔹 PyQt 기본 구조:

import sys
import time
import pandas as pd
from datetime import datetime
from PyQt5.QtWidgets import *
from PyQt5.QtCore import QTimer
from pykiwoom.kiwoom import Kiwoom

EXCEL_PATH = "stocks.xlsx"
LOG_FILE = "trade_log.txt"
INTERVAL_MS = 30000  # 30초 주기

class TradingBot(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("\U0001F4C8 Excel 기반 자동 매매 시스템")
        self.setGeometry(300, 100, 900, 700)

        self.kiwoom = Kiwoom()
        self.kiwoom.CommConnect(block=True)
        self.account = self.kiwoom.GetLoginInfo("ACCNO")[0]
        self.account_pw = ""

        self.bought_set = set()
        self.sold_set = set()

        self.init_ui()
        self.get_password()
        self.update_holdings()
        self.log("시스템 트레이딩 시작됨 ✅")

        self.timer = QTimer()
        self.timer.timeout.connect(self.run_trading)
        self.timer.start(INTERVAL_MS)

    def init_ui(self):
        self.text_log = QTextEdit()
        self.text_log.setReadOnly(True)

        self.btn_manual = QPushButton("\U0001F501 수동 실행")
        self.btn_manual.clicked.connect(self.run_trading)

        self.status_label = QLabel("⏱️ 대기 중...")

        self.table = QTableWidget()
        self.table.setColumnCount(4)
        self.table.setHorizontalHeaderLabels(["종목명", "수량", "매입가", "현재가"])
        self.table.horizontalHeader().setStretchLastSection(True)

        layout = QVBoxLayout()
        layout.addWidget(self.text_log)
        layout.addWidget(self.status_label)
        layout.addWidget(self.btn_manual)
        layout.addWidget(QLabel("\n📦 현재 보유 종목"))
        layout.addWidget(self.table)

        central = QWidget()
        central.setLayout(layout)
        self.setCentralWidget(central)

    def get_password(self):
        pw, ok = QInputDialog.getText(self, "계좌 비밀번호 입력", "계좌 비밀번호를 입력하세요:", QLineEdit.Password)
        if ok and pw:
            self.account_pw = pw
            self.log("🔐 계좌 비밀번호가 등록되었습니다.")
        else:
            QMessageBox.critical(self, "오류", "비밀번호가 입력되지 않았습니다. 프로그램을 종료합니다.")
            sys.exit(1)

    def log(self, message):
        now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        log_msg = f"[{now}] {message}"
        self.text_log.append(log_msg)
        with open(LOG_FILE, "a", encoding="utf-8") as f:
            f.write(log_msg + "\n")

    def read_excel(self):
        return pd.read_excel(EXCEL_PATH)

    def name_to_code(self, name):
        codes = self.kiwoom.GetCodeListByMarket('0') + self.kiwoom.GetCodeListByMarket('10')
        for code in codes:
            if self.kiwoom.GetMasterCodeName(code) == name:
                return code
        return None

    def get_current_price(self, code):
        price = self.kiwoom.GetMasterLastPrice(code)
        if isinstance(price, str):
            return int(price.replace(",", ""))
        elif isinstance(price, int):
            return price
        else:
            return 0

    def run_trading(self):
        self.status_label.setText("🔍 주가 확인 중...")
        df = self.read_excel()

        for _, row in df.iterrows():
            name = row['종목명']
            buy_price = int(row['매수가'])
            sell_price = int(row['매도가'])
            code = self.name_to_code(name)

            if not code:
                self.log(f"[오류] 종목명 {name} → 코드 변환 실패")
                continue

            current = self.get_current_price(code)
            self.log(f"{name} 현재가: {current} / 매수: {buy_price} / 매도: {sell_price}")

            qty = 10

            if current <= buy_price and name not in self.bought_set:
                self.log(f"📥 매수 주문: {name} @ {current}")
                self.kiwoom.SendOrder("매수", "1001", self.account, 1, code, qty, 0, "03", "")
                self.bought_set.add(name)
                self.sold_set.discard(name)

            elif current >= sell_price and name not in self.sold_set:
                self.log(f"📤 매도 주문: {name} @ {current}")
                self.kiwoom.SendOrder("매도", "1002", self.account, 2, code, qty, 0, "03", "")
                self.sold_set.add(name)
                self.bought_set.discard(name)

        self.update_holdings()
        self.status_label.setText("⏱️ 대기 중...")

    def update_holdings(self):
        try:
            data = self.kiwoom.block_request("opw00018",
                                             계좌번호=self.account,
                                             비밀번호=self.account_pw,
                                             비밀번호입력매체구분="00",
                                             조회구분=2,
                                             output="계좌평가잔고개별합산",
                                             next=0)

            self.log(f"[디버그] 계좌번호: {self.account}")
            self.log(f"[디버그] 비밀번호: {'입력됨' if self.account_pw else '미입력'}")
            self.log(f"[디버그] opw00018 응답 타입: {type(data)}")
            self.log(f"[디버그] opw00018 응답: {data}")

            if not isinstance(data, dict) or '종목번호' not in data:
                self.log("[경고] 보유 종목 정보가 없습니다.")
                self.table.setRowCount(0)
                return

            codes = data['종목번호']
            names = data['종목명']
            qtys = data['보유수량']
            prices = data['매입가']

            self.table.setRowCount(len(codes))
            for i in range(len(codes)):
                name = names[i].strip()
                qty = int(qtys[i]) if str(qtys[i]).strip().isdigit() else 0
                buy_price = int(prices[i]) if str(prices[i]).strip().isdigit() else 0
                cur_price = self.get_current_price(codes[i].strip())

                self.table.setItem(i, 0, QTableWidgetItem(name))
                self.table.setItem(i, 1, QTableWidgetItem(str(qty)))
                self.table.setItem(i, 2, QTableWidgetItem(str(buy_price)))
                self.table.setItem(i, 3, QTableWidgetItem(str(cur_price)))
        except Exception as e:
            self.log(f"[보유 종목 조회 오류] {str(e)}")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    bot = TradingBot()
    bot.show()
    sys.exit(app.exec_())

이제 콘솔이 아닌 GUI 창을 통해 보유 종목을 확인할 수 있게 됩니다.
한눈에 보기 편하고, 시각적으로도 깔끔해서 훨씬 관리가 쉬워집니다.


🏁 4단계 – GUI를 통해 실전 트레이딩을 관리합니다

PyQt로 보유 현황을 시각화한 후, 프로그램의 실용성이 크게 향상되었습니다.
더 이상 콘솔 메시지를 뒤져볼 필요 없이, 어떤 종목을 몇 주나 가지고 있는지, 평단가는 얼마인지 한눈에 확인할 수 있습니다.
트레이딩 과정이 체계적으로 정리되어 개발자이자 트레이더로서의 만족도가 매우 높아졌습니다.


✅ 정리

구분 개선 전 개선 후
데이터 확인 콘솔 로그 GUI 테이블
시각화 없음 실시간 보유 종목 표시
사용성 불편 직관적이고 명확함
 

GUI를 도입한 이 작은 변화는 프로그램의 품질을 한 단계 끌어올렸습니다.
이제 이 주식 트레이더는 콘솔 속에서 혼잣말을 하던 시절을 뒤로하고,
PyQt라는 창을 통해 매매를 직관적이고 명확하게 관리하고 있습니다.

Posted by 제이브레인
,

📘 AI는 너무 멀고, 엑셀은 가깝다 – 파이썬으로 자동 주식 매매하기


🧠 Chapter 1. 인공지능 시스템 트레이딩은… 멋지지만

"딥러닝으로 시장을 예측하고 싶다…"
"RNN으로 주가를 예측해볼까?"
"Transformer 기반 모델에 재무제표를 넣으면?"

그렇다.
AI 기반 시스템 트레이딩은 정말 멋집니다.
하지만 문제는…
📆 시간이 너무 많이 듭니다.
📚 논문 읽고,
🧮 모델 만들고,
⚙️ 튜닝하고,
💀 수익은 안 나고…

그래서 나는 생각했습니다.

"이걸 진짜 간단하게 시작할 방법은 없을까?"


🧾 Chapter 2. 엑셀로 전략을 세우고, 파이썬으로 실행하자!

하루를 마무리하며 커피 한 잔.
나는 엑셀을 열고 이렇게 적었습니다.

종목명 매수가 매도가
삼성전자 70000 73000
카카오 45000 48000
 
혹시라도 오피스가 없는 분들은 엑셀을 구글 쉬트에서 만들 수 있습니다.
 
구글? AI는 너무 멀고, 엑셀은 가깝다 – 파이썬으로 자동 주식 매매하기
🧠 Chapter 1. 인공지능 시스템 트레이딩은… 멋지지만
"딥러닝으로 시장을 예측하고 싶다…"
"RNN으로 주가를 예측해볼까?"
"Transformer 기반 모델에 재무제표를 넣으면?"
 
그렇다.
AI 기반 시스템 트레이딩은 정말 멋집니다.
하지만 문제는…
📆 시간이 너무 많이 듭니다.
📚 논문 읽고,
🧮 모델 만들고,
⚙️ 튜닝하고,
💀 수익은 안 나고…
 
그래서 나는 생각했습니다.
 
"이걸 진짜 간단하게 시작할 방법은 없을까?"
 
🧾 Chapter 2. 엑셀로 전략을 세우고, 파이썬으로 실행하자!
하루를 마무리하며 커피 한 잔.
나는 엑셀을 열고 이렇게 적었습니다.
 
종목명 매수가 매도가
삼성전자 70000 73000
카카오 45000 48000
 
혹시라도 오피스가 없는 분들은 엑셀을 구글 쉬트에서 만들 수 있습니다.
구글 앱 중에 구글 쉬트가 있습니다.
 

구글 쉬트에서 위와 같이 입력 후 파일 > 다운로드 > Microsoft Exel 을 선택하면 됩니다.

 
 

“내가 원하는 가격에 사서, 원하는 가격에 팔아줘”
이 전략은 단순하다.
하지만 꾸준히, 빠르게, 감정 없이 실행만 해준다면?
사람보다 나을 수도 있지 않을까?

그래서 파이썬에게 시켰습니다.


🔧 Chapter 3. 만드는 방법은 이렇게 간단합니다.

1단계: 필요한 도구 설치

pip install pandas openpyxl pykiwoom

2단계: 엑셀 만들기 (stocks.xlsx)

종목명 매수가 매도가
삼성전자 70000 73000
카카오 45000 48000
 

저장 후 닫아두자. 나머지는 파이썬이 알아서 합니다.


🤖 Chapter 4. 파이썬 시스템 트레이딩 코드

import pandas as pd
from pykiwoom.kiwoom import Kiwoom
import time

EXCEL_PATH = "stocks.xlsx"
CHECK_INTERVAL = 30  # 몇 초마다 주가를 확인할지

def read_excel(path):
    return pd.read_excel(path)

def name_to_code(kiwoom, name):
    market_codes = kiwoom.GetCodeListByMarket('0') + kiwoom.GetCodeListByMarket('10')
    for code in market_codes:
        if kiwoom.GetMasterCodeName(code) == name:
            return code
    return None

def get_current_price(kiwoom, code):
    price = kiwoom.GetMasterLastPrice(code)
    if isinstance(price, str):
        return int(price.replace(",", ""))
    elif isinstance(price, int):
        return price
    else:
        return 0

def auto_trade_loop(kiwoom, account):
    bought = set()
    sold = set()

    while True:
        df = read_excel(EXCEL_PATH)
        for _, row in df.iterrows():
            name = row['종목명']
            buy_price = int(row['매수가'])
            sell_price = int(row['매도가'])
            code = name_to_code(kiwoom, name)

            if not code:
                print(f"[{name}] 종목 코드 못 찾음.")
                continue

            current_price = get_current_price(kiwoom, code)
            print(f"[{name}] 현재가: {current_price} | 매수: {buy_price} | 매도: {sell_price}")

            qty = 10  # 10주 매매 예시

            if current_price <= buy_price and name not in bought:
                print(f"[매수] {name} @ {current_price}")
                kiwoom.SendOrder("매수", "1001", account, 1, code, qty, 0, "03", "")
                bought.add(name)
                sold.discard(name)

            elif current_price >= sell_price and name not in sold:
                print(f"[매도] {name} @ {current_price}")
                kiwoom.SendOrder("매도", "1002", account, 2, code, qty, 0, "03", "")
                sold.add(name)
                bought.discard(name)

        print(f"🕐 {CHECK_INTERVAL}초 후 다시 확인...\n")
        time.sleep(CHECK_INTERVAL)

def main():
    kiwoom = Kiwoom()
    kiwoom.CommConnect(block=True)
    account = kiwoom.GetLoginInfo("ACCNO")[0]
    auto_trade_loop(kiwoom, account)

if __name__ == "__main__":
    main()

 

파일 삭제 실패 오류

실행 시 아래와 같은 에러가 발생할 수 있습니다.

이 경우 권한 부족 때문에 발생할 수 있습니다.

아래 폴더로 이동한 후 opversionup.exe 프로그램을 관리자 권한으로 실행하세요.

C:\OpenAPI

 

비밀번호 오류

계좌 비밀번호 입력창을 통해 계좌 비밀번호를 입력 및 등록하십시오

라는 에러가 발생하는 경우가 있을 수 있습니다.

 

윈도우 우측 하단을 보면 KOAStudio 실행 아이콘이 보입니다.

여기에서 계좌 비밀번호 저장을 선택 후 비밀번호를 등록하고 AUTO 체크 박스를 체크하면 됩니다.

혹시라도 아이콘이 보이지 않으면 C:\OpenAPI 에서 KOAStudioSA.exe 를 관리자 권한으로 실행 시킵니다.

이후 파일 > Open API 접속을 선택하면 됩니다.

✅ Chapter 5. 결과는?

  • 종목명을 엑셀에 적습니다.
  • 매수/매도 가격을 쓰면,
  • 파이썬이 지정가가 되면 매수하고, 매도가 되면 팔아줍니다.

이걸 모의투자부터 돌려보자.
진짜로 "자동 매매"가 눈앞에서 벌어집니다.


🏁 Chapter 6. 결론

AI는 아직 멀어보일 수 있습니다.
하지만 엑셀은 오늘 당장 시작할 수 있습니다.

엑셀은 나의 전략 노트,
파이썬은 나의 트레이딩 비서.

이제 복잡한 모델이 없어도
단순한 전략으로,
꾸준하게 수익을 쌓아갈 수 있습니다.

Posted by 제이브레인
,