시스템 트레이딩/JbTrader 1호
5.10편-매수/매 가격에 현재 가격 및 상하 호가 선택 기능 추가
제이브레인
2025. 3. 19. 20:37
시장가로 매수하거나 지정가로 매수하려고 할 때 현재 가격이 표시되어 있지 않고 입력만 하도록 되어 있어서 잘못 입력할 수도 있고 가격을 다른 곳에서 확인 후에 입력해야 해서 많이 불편하네요.
그래서 매수/매도 가격에 현재가와 상하호가를 입력하여 직관적으로 선택하도록 기능추가하려고 합니다.
1) 코드 생성
ChatGPT 에 사용한 프롬프트입니다.
매수/매도 가격에 현재가를 기준으로 상하호가를 계산하여 입력하도록 했습니다.
그리고 5초에 한번 자동 업데이트하게 했고 만약 사용자 매수/매도 가격을 선택하면 자동 업데이트를 멈추게했습니다.
python pyqt5 designer 로 guruma_one.ui 를 만들었습니다.
guruma_one.ui 에는 다음과 같은 콤포넌트들이 존재합니다.
name : accountComboBox, type : QComboBox
name : logTextEdit, type : QTextEdit
name : stockCode , type : QLineEdit
name : orderType, type : QComboBox
name : buyPrice, type : QComboBox
name : buyAmount, type : QLineEdit
name : sellPrice, type : QComboBox
name : sellAmount, type : QLineEdit
name : buyButton, type : QPushButton
다음과 같은 기능을 제공합니다.
1. 키움증권 API 를 연결하고 연결 후 API를 초기화하고 실 서버 연결 시 "실 서버 연결 성공" 이라고 메시지를 logTextEdit 에 출력하고
모의 투자 서버 연결 시 "모의 투자 서버 연결 성공"이라고 메시지를 logTextEdit 에 출력
2. Kiwoom 계좌 정보를 accountComboBox 에 출력
3. stockCode 에 종목 코드를 입력하면 호가 단위를 현재 종목코드의 현재가를 기준으로 계산
4. buyPrice 에 -20호가 부터 +20호가까지 추가합니다.
QSpinBox 의 각 item 은 -+호가 : 가격을 표시하고 현재가격은 현재가 : 가격을 표시합니다.
기본값은 현재가를 선택합니다.
5. sellPrice 에도 buyPrice와 동일한 값을 선택합니다.
기본값은 +1호가를 선택합니다.
6. 5초에 한번씩 가격을 업데이트하고 만약 buyPrice 또는 sellPrice 의 가격을 선택하면 업데이트를 멈춥니다.
파이썬 코드로 작성해주세요. 주석도 자세히 달아주세요. 코드는 오류 테스트 후에 오류가 있으면 수정해주세요.
Qt Designer 에서 buyPrice, sellPrice 를 QLineEdit -> QComboBox 로 변경합니다.
2) 생성된 코드
생성된 코드이며 일부 버그는 수정하였습니다.
아래 코드는 다음 위치에서 다운 받는 것이 가능합니다.
git clone https://github.com/jbpark/JbTraderExample.git
cd JbTraderExample/jbtrader/ch5.10
price_update.py
import sys
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.uic import loadUi
from pykiwoom.kiwoom import Kiwoom
def get_cell_value_or_error(df, row_idx, col_name):
try:
return df.at[row_idx, col_name] # 특정 위치의 값 반환
except KeyError:
raise ValueError(f"Invalid index '{row_idx}' or column '{col_name}'")
class GurumaApp(QMainWindow):
def __init__(self):
super().__init__()
loadUi("guruma_one.ui", self) # UI 파일 로드
self.kiwoom = Kiwoom() # Kiwoom 인스턴스 생성
self.kiwoom.CommConnect() # API 연결
self.kiwoom.OnEventConnect = self.event_connect # 연결 이벤트 핸들러
# 버튼 클릭 이벤트 연결
self.buyButton.clicked.connect(self.update_stock_price)
# 주기적으로 가격 업데이트를 위한 타이머 설정
self.price_update_timer = QTimer(self)
self.price_update_timer.timeout.connect(self.update_stock_price)
self.price_update_timer.start(5000) # 5초마다 실행
self.buyPrice.installEventFilter(self)
self.sellPrice.installEventFilter(self)
def eventFilter(self, obj, event):
if (obj == self.buyPrice or obj == self.sellPrice) and event.type() == event.MouseButtonPress:
self.on_combobox_clicked()
return super().eventFilter(obj, event)
def on_combobox_clicked(self):
print("QComboBox가 클릭되었습니다!") # 특정 함수 실행
self.stop_price_update()
def event_connect(self, err_code):
"""
키움증권 API 연결 이벤트 처리
"""
if err_code == 0:
server_type = self.kiwoom.GetLoginInfo("GetServerGubun")
if server_type == "1":
self.logTextEdit.append("모의 투자 서버 연결 성공")
else:
self.logTextEdit.append("실 서버 연결 성공")
self.load_accounts()
else:
self.logTextEdit.append("API 연결 실패")
def load_accounts(self):
"""
계좌 정보를 가져와서 accountComboBox에 출력
"""
accounts = self.kiwoom.GetLoginInfo("ACCNO").split(';')
self.accountComboBox.addItems([acc for acc in accounts if acc])
def update_stock_price(self):
"""
stockCode 입력 시 현재 가격 및 상하 몇 호가 가격을 buyPrice, sellPrice에 반영
"""
stock_code = self.stockCode.text().strip()
if not stock_code:
self.logTextEdit.append("종목 코드를 입력하세요.")
return
self.kiwoom.SetInputValue("종목코드", stock_code)
self.kiwoom.CommRqData("주식기본정보", "opt10001", 0, "0101")
QTimer.singleShot(5000, self.process_stock_price)
def get_price_tick(self, price):
"""
현재가를 기반으로 호가 단위를 계산하는 함수
"""
if price < 1000:
return 1
elif price < 5000:
return 5
elif price < 10000:
return 10
elif price < 50000:
return 50
elif price < 100000:
return 100
elif price < 500000:
return 500
else:
return 1000
def process_stock_price(self):
"""
조회한 주식 정보를 buyPrice와 sellPrice에 반영
"""
"""
조회한 주식 정보를 buyPrice와 sellPrice에 반영
"""
stock_code = self.stockCode.text().strip()
if not stock_code:
self.logTextEdit.append("종목 코드를 입력하세요.")
return
df = self.kiwoom.block_request("opt10001",
종목코드=stock_code,
output="주식기본정보",
next=0)
current_price_str = get_cell_value_or_error(df, 0, '현재가')
if not current_price_str:
self.logTextEdit.append("현재가 정보를 가져올 수 없습니다.")
return
current_price_str = str(int(current_price_str))
try:
current_price = abs(int(current_price_str))
except ValueError:
self.logTextEdit.append("현재가 변환 오류: {current_price_str}")
return
price_tick = self.get_price_tick(current_price)
self.buyPrice.clear()
self.sellPrice.clear()
for i in range(-20, 21): # -20호가부터 +20호가까지 추가
price = current_price + i * price_tick
label = f"{i}호가: {price}" if i != 0 else f"현재가: {price}"
self.buyPrice.addItem(label, price)
self.sellPrice.addItem(label, price)
# 기본값 설정: buyPrice는 현재가, sellPrice는 +1호가
self.buyPrice.setCurrentIndex(20)
self.sellPrice.setCurrentIndex(21)
self.logTextEdit.append(f"현재가 {current_price}원부터 ±20호가까지 설정 완료")
def stop_price_update(self):
"""
사용자가 가격을 선택하면 타이머 정지
"""
print("stop_price_update")
self.price_update_timer.stop()
self.logTextEdit.append("가격 선택 완료. 자동 업데이트 중지")
if __name__ == "__main__":
app = QApplication(sys.argv)
window = GurumaApp()
window.show()
sys.exit(app.exec_())
3) 코드 실행
종목 코드에 삼성전자를 입력하고 실행 시 아래와 같이 매수 금액이 표시되는 것을 확인 할 수 있습니다.
매도 금액은 현재가 보다 1호가 위를 기본 선택하도록 했습니다.
4) JbTrader 에 가격 정보 업데이트 통합
위 가격 정보를 표시하는 기능을 JbTrader 에 통합시켰습니다.
통합된 코드는 다음 위치에 있습니다.
git clone https://github.com/jbpark/JbTraderExample.git
cat JbTraderExample/jbtrader/ch5.10/guruma_one.py
통합 후에 실행한 모습니다.
뭔가~ 그럴 듯한 모습을 갖춰가는 것 같지 않나요?
모의 계좌이긴한데 삼성전자를 사두었었는데 수익률이 1.32%네요.
실계좌도 빨리 수익이 났으면 좋겠어요.