행위

대신증권 파이썬 api

DB CAFE

Dbcafe (토론 | 기여)님의 2021년 1월 5일 (화) 15:32 판
(차이) ← 이전 판 | 최신판 (차이) | 다음 판 → (차이)
thumb_up 추천메뉴 바로가기


목차

1 대신 증권 자동매매 프로그램 설치[편집]

1.파이썬 x86 32bit https://www.python.org/ftp/python/3.7.6/python-3.7.6.exe

2.대신증권 api https://money2.daishin.com/download/CYBOS5.exe

3.윈도우용 crontab http://www.nncron.ru/download/nncronlt117.exe

2 조회/실시간 이해하기[편집]

대신증권 플러스의 조회(다음 조회 포함)와 실시간 방식을 이해하기


대신증권에서 통용되는 통신관련 용어( RQ, RP, SB, SBC) 입니다.

- 조회 요청 : RQ(Request )

- 조회 응답 : RP(Response)

- 실시간 등록 : SB(Subscribe)

- 실시간 해지 : SBC(Subscribe Cancel)


- "조회 / 실시간" 처리 절차 (RQ / RP, SB / SBC)

2(4).png

- "조회 / 실시간" 예제 (주식 현재가 조회/실시간 - Visual Basic .Net) 5(1).png

- "다음 조회" 처리 절차 9(1).png

- "다음 조회" 예제 (매매입체분석 투자자 매매동향 - Visual Basic .Net) 7(1).png 8(2).png

3 데이터 요청 방법 2가지 BlockRequest 와 Request 방식 비교[편집]

플러스 API 에서 데이터를 요청하는 방법은 크게 2가지가 있습니다

BlockRequest 방식 - 가장 간단하게 데이터 요청해서 수신 가능 Request 호출 후 Received 이벤트로 수신 받기


아래는 위 2가지를 비교할 수 있도록 만든 예제 코드입니다

import pythoncom
from PyQt5.QtWidgets import *
import win32com.client
 
import win32event
 
g_objCodeMgr = win32com.client.Dispatch('CpUtil.CpCodeMgr')
 
StopEvent = win32event.createEvent(None, 0, 0, None)
 
class CpEvent:
    def set_params(self, client, name, caller):
        self.client = client  # CP 실시간 통신 object
        self.name = name  # 서비스가 다른 이벤트를 구분하기 위한 이름
        self.caller = caller  # callback 을 위해 보관
 
    def OnReceived(self):
        # 실시간 처리 - 현재가 주문 체결
        if self.name == 'stockmst':
            print('recieved')
            win32event.SetEvent(StopEvent)
            return
 
 
class CpCurReply:
    def __init__(self, objEvent):
        self.name = "stockmst"
        self.obj = objEvent
 
    def Subscribe(self):
        handler = win32com.client.WithEvents(self.obj, CpEvent)
        handler.set_params(self.obj, self.name, None)
 
 
def MessagePump(timeout):
    waitables = [StopEvent]
    while 1:
        rc = win32event.MsgWaitForMultipleObjects(
            waitables,
            0,  # Wait for all = false, so it waits for anyone
            timeout, #(or win32event.INFINITE)
            win32event.QS_ALLEVENTS)  # Accepts all input
 
        if rc == win32event.WAIT_OBJECT_0:
            # Our first event listed, the StopEvent, was triggered, so we must exit
            print('stop event')
            break
 
        elif rc == win32event.WAIT_OBJECT_0 + len(waitables):
            # A windows message is waiting - take care of it. (Don't ask me
            # why a WAIT_OBJECT_MSG isn't defined < WAIT_OBJECT_0...!).
            # This message-serving MUST be done for COM, DDE, and other
            # Windowsy things to work properly!
            print('pump')
            if pythoncom.PumpWaitingMessages():
                break  # we received a wm_quit message
        elif rc == win32event.WAIT_TIMEOUT:
            print('timeout')
            return
            pass
        else:
            print('exception')
            raise RuntimeError("unexpected win32wait return value")
 
 
code = 'A005930'
 
##############################################################
#1. BlockRequest
print('#####################################')
objStockMst = win32com.client.Dispatch("DsCbo1.StockMst")
objStockMst.SetInputValue(0, code)
objStockMst.BlockRequest()
print('BlockRequest 로 수신 받은 데이터')
item = {}
item['종목명']= g_objCodeMgr.CodeToName(code)
item['현재가'] = objStockMst.GetHeaderValue(11)  # 종가
item['대비'] =  objStockMst.GetHeaderValue(12)  # 전일대비
print(item)
 
print('')
##############################################################
# 2. Request ==> 메시지 펌프 ==>  OnReceived 이벤트 수신
print('#####################################')
objReply = CpCurReply(objStockMst)
objReply.Subscribe()
 
code = 'A005930'
objStockMst.SetInputValue(0, code)
objStockMst.Request()
MessagePump(10000)
item = {}
item['종목명']= g_objCodeMgr.CodeToName(code)
item['현재가'] = objStockMst.GetHeaderValue(11)  # 종가
item['대비'] =  objStockMst.GetHeaderValue(12)  # 전일대비
print(item)

일반적인 데이터 요청에는 BlockRequest 방식이 가장 간단합니다

다만, BlockRequest 함수 내에서도 동일 하게 메시지펌핑을 하고 있어 해당 통신이 마치기 전에 실시간 시세를 수신 받거나 다른 이벤트에 의해 재귀 호출 되는 문제가 있을 경우 함수 호출이 실패할 수 있습니다

복잡한 실시간 시세 수신 중에 통신을 해야 하는 경우에는 Request 방식을 이용해야 합니다.

4 주식 현재가 조회/실시간[편집]

5 주식 일자별 조회(다음)[편집]

6 주식 현재가 조회[편집]

대신정보 현재가 정보

7 종목정보 구하는 예제[편집]

8 주식차트 조회(일간/주간/월간/분간/틱) 예제[편집]

주식 차트 정보를 구하는 파이썬 예제입니다


사용된 PLUS OBJECT

■ CpSysDib.StockChart - 차트 조회 OBJECT


주요 기능

■ 기간(일간) - 일간 차트를 특정 기간을 주어 조회

■ 개수(일간) - 최근일 부터 개수만큼 조회

■ 분차트 조회

■ 틱차트 조회

■ 주간 차트 조회

■ 월간 차트 조회

■ 엑셀로 저장 - 수신 받은 데이터를 엑셀로 내보내기 하여 데이터 확인

import sys
from PyQt5.QtWidgets import *
import win32com.client
import pandas as pd
import os
 
g_objCodeMgr = win32com.client.Dispatch('CpUtil.CpCodeMgr')
g_objCpStatus = win32com.client.Dispatch('CpUtil.CpCybos')
 
 
class CpStockChart:
    def __init__(self):
        self.objStockChart = win32com.client.Dispatch("CpSysDib.StockChart")
 
    # 차트 요청 - 기간 기준으로
    def RequestFromTo(self, code, fromDate, toDate, caller):
        print(code, fromDate, toDate)
        # 연결 여부 체크
        bConnect = g_objCpStatus.IsConnect
        if (bConnect == 0):
            print("PLUS가 정상적으로 연결되지 않음. ")
            return False
 
        self.objStockChart.SetInputValue(0, code)  # 종목코드
        self.objStockChart.SetInputValue(1, ord('1'))  # 기간으로 받기
        self.objStockChart.SetInputValue(2, toDate)  # To 날짜
        self.objStockChart.SetInputValue(3, fromDate)  # From 날짜
        #self.objStockChart.SetInputValue(4, 500)  # 최근 500일치
        self.objStockChart.SetInputValue(5, [0, 2, 3, 4, 5, 8])  # 날짜,시가,고가,저가,종가,거래량
        self.objStockChart.SetInputValue(6, ord('D'))  # '차트 주기 - 일간 차트 요청
        self.objStockChart.SetInputValue(9, ord('1'))  # 수정주가 사용
        self.objStockChart.BlockRequest()
 
        rqStatus = self.objStockChart.GetDibStatus()
        rqRet = self.objStockChart.GetDibMsg1()
        print("통신상태", rqStatus, rqRet)
        if rqStatus != 0:
            exit()
 
        len = self.objStockChart.GetHeaderValue(3)
 
        caller.dates = []
        caller.opens = []
        caller.highs = []
        caller.lows = []
        caller.closes = []
        caller.vols = []
        for i in range(len):
            caller.dates.append(self.objStockChart.GetDataValue(0,i))
            caller.opens.append(self.objStockChart.GetDataValue(1, i))
            caller.highs.append(self.objStockChart.GetDataValue(2, i))
            caller.lows.append(self.objStockChart.GetDataValue(3, i))
            caller.closes.append(self.objStockChart.GetDataValue(4, i))
            caller.vols.append(self.objStockChart.GetDataValue(5, i))
 
        print(len)
 
    # 차트 요청 - 최근일 부터 개수 기준
    def RequestDWM(self, code, dwm, count, caller):
        # 연결 여부 체크
        bConnect = g_objCpStatus.IsConnect
        if (bConnect == 0):
            print("PLUS가 정상적으로 연결되지 않음. ")
            return False
 
        self.objStockChart.SetInputValue(0, code)  # 종목코드
        self.objStockChart.SetInputValue(1, ord('2'))  # 개수로 받기
        self.objStockChart.SetInputValue(4, count)  # 최근 500일치
        self.objStockChart.SetInputValue(5, [0, 2, 3, 4, 5, 8])  # 요청항목 - 날짜,시가,고가,저가,종가,거래량
        self.objStockChart.SetInputValue(6, dwm)  # '차트 주기 - 일/주/월
        self.objStockChart.SetInputValue(9, ord('1'))  # 수정주가 사용
        self.objStockChart.BlockRequest()
 
        rqStatus = self.objStockChart.GetDibStatus()
        rqRet = self.objStockChart.GetDibMsg1()
        print("통신상태", rqStatus, rqRet)
        if rqStatus != 0:
            exit()
 
        len = self.objStockChart.GetHeaderValue(3)
 
        caller.dates = []
        caller.opens = []
        caller.highs = []
        caller.lows = []
        caller.closes = []
        caller.vols = []
        caller.times = []
        for i in range(len):
            caller.dates.append(self.objStockChart.GetDataValue(0, i))
            caller.opens.append(self.objStockChart.GetDataValue(1, i))
            caller.highs.append(self.objStockChart.GetDataValue(2, i))
            caller.lows.append(self.objStockChart.GetDataValue(3, i))
            caller.closes.append(self.objStockChart.GetDataValue(4, i))
            caller.vols.append(self.objStockChart.GetDataValue(5, i))
 
        print(len)
 
        return
 
    # 차트 요청 - 분간, 틱 차트
    def RequestMT(self, code, dwm, count, caller):
        # 연결 여부 체크
        bConnect = g_objCpStatus.IsConnect
        if (bConnect == 0):
            print("PLUS가 정상적으로 연결되지 않음. ")
            return False
 
        self.objStockChart.SetInputValue(0, code)  # 종목코드
        self.objStockChart.SetInputValue(1, ord('2'))  # 개수로 받기
        self.objStockChart.SetInputValue(4, count)  # 조회 개수
        self.objStockChart.SetInputValue(5, [0, 1, 2, 3, 4, 5, 8])  # 요청항목 - 날짜, 시간,시가,고가,저가,종가,거래량
        self.objStockChart.SetInputValue(6, dwm)  # '차트 주기 - 분/틱
        self.objStockChart.SetInputValue(7, 1)  # 분틱차트 주기
        self.objStockChart.SetInputValue(9, ord('1'))  # 수정주가 사용
        self.objStockChart.BlockRequest()
 
        rqStatus = self.objStockChart.GetDibStatus()
        rqRet = self.objStockChart.GetDibMsg1()
        print("통신상태", rqStatus, rqRet)
        if rqStatus != 0:
            exit()
 
        len = self.objStockChart.GetHeaderValue(3)
 
        caller.dates = []
        caller.opens = []
        caller.highs = []
        caller.lows = []
        caller.closes = []
        caller.vols = []
        caller.times = []
        for i in range(len):
            caller.dates.append(self.objStockChart.GetDataValue(0, i))
            caller.times.append(self.objStockChart.GetDataValue(1, i))
            caller.opens.append(self.objStockChart.GetDataValue(2, i))
            caller.highs.append(self.objStockChart.GetDataValue(3, i))
            caller.lows.append(self.objStockChart.GetDataValue(4, i))
            caller.closes.append(self.objStockChart.GetDataValue(5, i))
            caller.vols.append(self.objStockChart.GetDataValue(6, i))
 
        print(len)
 
        return
 
 
 
class MyWindow(QMainWindow):
    def __init__(self):
        super().__init__()
 
        # 기본 변수들
        self.dates = []
        self.opens = []
        self.highs = []
        self.lows = []
        self.closes = []
        self.vols = []
        self.times = []
 
        self.objChart = CpStockChart()
 
 
        # 윈도우 버튼 배치
        self.setWindowTitle("PLUS API TEST")
        nH = 20
 
        self.codeEdit = QLineEdit("", self)
        self.codeEdit.move(20, nH)
        self.codeEdit.textChanged.connect(self.codeEditChanged)
        self.codeEdit.setText('00660')
        self.label = QLabel('종목코드', self)
        self.label.move(140, nH)
        nH += 50
 
        btchart1= QPushButton("기간(일간) 요청", self)
        btchart1.move(20, nH)
        btchart1.clicked.connect(self.btchart1_clicked)
        nH += 50
 
        btchart2 = QPushButton("개수(일간) 요청", self)
        btchart2.move(20, nH)
        btchart2.clicked.connect(self.btchart2_clicked)
        nH += 50
 
        btchart3 = QPushButton("분차트 요청", self)
        btchart3.move(20, nH)
        btchart3.clicked.connect(self.btchart3_clicked)
        nH += 50
 
        btchart4 = QPushButton("틱차트 요청", self)
        btchart4.move(20, nH)
        btchart4.clicked.connect(self.btchart4_clicked)
        nH += 50
 
        btchart5 = QPushButton("주간차트 요청", self)
        btchart5.move(20, nH)
        btchart5.clicked.connect(self.btchart5_clicked)
        nH += 50
 
        btchart6 = QPushButton("월간차트 요청", self)
        btchart6.move(20, nH)
        btchart6.clicked.connect(self.btchart6_clicked)
        nH += 50
 
        btchart7 = QPushButton("엑셀로 저장", self)
        btchart7.move(20, nH)
        btchart7.clicked.connect(self.btchart7_clicked)
        nH += 50
 
        btnExit = QPushButton("종료", self)
        btnExit.move(20, nH)
        btnExit.clicked.connect(self.btnExit_clicked)
        nH += 50
 
        self.setGeometry(300, 300, 300, nH)
        self.setCode('A000660')
 
    # 기간(일간) 으로 받기
    def btchart1_clicked(self):
        if self.objChart.RequestFromTo(self.code, 20160102, 20171025, self) == False:
            exit()
 
    # 개수(일간) 으로 받기
    def btchart2_clicked(self):
        if self.objChart.RequestDWM(self.code, ord('D'), 500, self) == False:
            exit()
 
    # 분차트 받기
    def btchart3_clicked(self):
        if self.objChart.RequestMT(self.code, ord('m'), 500, self) == False:
            exit()
 
 
    # 틱차트 받기
    def btchart4_clicked(self):
        if self.objChart.RequestMT(self.code, ord('T'), 500, self) == False:
            exit()
 
    # 주간차트
    def btchart5_clicked(self):
        if self.objChart.RequestDWM(self.code, ord('W'), 100, self) == False:
            exit()
 
    # 월간차트
    def btchart6_clicked(self):
        if self.objChart.RequestDWM(self.code, ord('M'), 100, self) == False:
            exit()
 
 
    def btchart7_clicked(self):
        charfile = 'chart.xlsx'
        if (len(self.times) == 0):
            chartData = {'일자' : self.dates,
                       '시가' : self.opens,
                       '고가' : self.highs,
                       '저가' : self.lows,
                       '종가' : self.closes,
                       '거래량' : self.vols,
                       }
            df =pd.DataFrame(chartData, columns=['일자','시가','고가','저가','종가','거래량'])
        else:
            chartData = {'일자' : self.dates,
                       '시간' : self.times,
                       '시가' : self.opens,
                       '고가' : self.highs,
                       '저가' : self.lows,
                       '종가' : self.closes,
                       '거래량' : self.vols,
                       }
            df =pd.DataFrame(chartData, columns=['일자','시간','시가','고가','저가','종가','거래량'])
 
        df = df.set_index('일자')
 
        # create a Pandas Excel writer using XlsxWriter as the engine.
        writer = pd.ExcelWriter(charfile, engine='xlsxwriter')
        # Convert the dataframe to an XlsxWriter Excel object.
        df.to_excel(writer, sheet_name='Sheet1')
        # Close the Pandas Excel writer and output the Excel file.
        writer.save()
        os.startfile(charfile)
        return
 
    def codeEditChanged(self):
        code = self.codeEdit.text()
        self.setCode(code)
 
    def setCode(self, code):
        if len(code) < 6:
            return
 
        print(code)
        if not (code[0] == "A"):
            code = "A" + code
 
        name = g_objCodeMgr.CodeToName(code)
        if len(name) == 0:
            print("종목코드 확인")
            return
 
        self.label.setText(name)
        self.code = code
 
 
    def btnExit_clicked(self):
        exit()
 
 
 
if __name__ == "__main__":
    app = QApplication(sys.argv)
    myWindow = MyWindow()
    myWindow.show()
    app.exec_()

9 지수옵션 최근월물 시세 조회(실시간 포함)[편집]

10 매매입체분석(투자주체별현황) 예제[편집]

매매입체분석(CpSysDib.CpSvr7254) 서비스를 이용하여 데이터를 조회하는 파이썬 예제입니다 조회 후 엑셀 내보내기를 통해 엑셀 파일로 데이터 확인 가능합니다.

import sys
from PyQt5.QtWidgets import *
import win32com.client
from pandas import Series, DataFrame
import pandas as pd
import locale
import os
import time
 
locale.setlocale(locale.LC_ALL, '')
# cp object
g_objCodeMgr = win32com.client.Dispatch('CpUtil.CpCodeMgr')
g_objCpStatus = win32com.client.Dispatch('CpUtil.CpCybos')
g_objCpTrade = win32com.client.Dispatch('CpTrade.CpTdUtil')
 
gExcelFile = '7254.xlsx'
 
 
class CpRp7354:
    def Request(self, code, caller):
        # 연결 여부 체크
        objCpCybos = win32com.client.Dispatch('CpUtil.CpCybos')
        bConnect = objCpCybos.IsConnect
        if (bConnect == 0):
            print('PLUS가 정상적으로 연결되지 않음. ')
            return False
 
        # 관심종목 객체 구하기
        objRq = win32com.client.Dispatch('CpSysDib.CpSvr7254')
        objRq.SetInputValue(0, code)
        objRq.SetInputValue(1, 6)  # 일자별
        objRq.SetInputValue(4, ord('0'))  # '0' 순매수 '1' 매매비중
        objRq.SetInputValue(5, 0)  # '전체
        objRq.SetInputValue(6, ord('1'))  # '1' 순매수량 '2' 추정금액(백만)
 
        sumcnt = 0
        caller.data7254 = None
        caller.data7254 = pd.DataFrame(columns=('date', 'close', '개인', '외국인', '기관계',
                                                '금융투자', '보험', '투신', '은행', '기타금융', '연기금', '국가,지자체',
                                                '기타법인', '기타외인'))
 
        while True:
            remainCount = g_objCpStatus.GetLimitRemainCount(1)  # 1 시세 제한
            if remainCount <= 0:
                print('시세 연속 조회 제한 회피를 위해 sleep', g_objCpStatus.LimitRequestRemainTime)
                time.sleep(g_objCpStatus.LimitRequestRemainTime / 1000)
 
            objRq.BlockRequest()
 
            # 현재가 통신 및 통신 에러 처리
            rqStatus = objRq.GetDibStatus()
            print('통신상태', rqStatus, objRq.GetDibMsg1())
            if rqStatus != 0:
                return False
 
            cnt = objRq.GetHeaderValue(1)
            sumcnt += cnt
 
            for i in range(cnt):
                item = {}
                item['date'] = objRq.GetDataValue(0, i)
                item['close'] = objRq.GetDataValue(14, i)
                item['개인'] = objRq.GetDataValue(1, i)
                item['외국인'] = objRq.GetDataValue(2, i)
                item['기관계'] = objRq.GetDataValue(3, i)
                item['금융투자'] = objRq.GetDataValue(4, i)
                item['보험'] = objRq.GetDataValue(5, i)
                item['투신'] = objRq.GetDataValue(6, i)
                item['은행'] = objRq.GetDataValue(7, i)
                item['기타금융'] = objRq.GetDataValue(8, i)
                item['연기금'] = objRq.GetDataValue(9, i)
                item['국가,지자체'] = objRq.GetDataValue(13, i)
                item['기타법인'] = objRq.GetDataValue(10, i)
                item['기타외인'] = objRq.GetDataValue(11, i)
 
                caller.data7254.loc[len(caller.data7254)] = item
 
            # 1000 개 정도만 처리
            if sumcnt > 1000:
                break;
            # 연속 처리
            if objRq.Continue != True:
                break
 
        caller.data7254 = caller.data7254.set_index('date')
        # 인덱스 이름 제거
        caller.data7254.index.name = None
        print(caller.data7254)
        return True
 
 
class MyWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('PLUS API TEST')
        self.setGeometry(300, 300, 300, 240)
        self.isSB = False
        self.objCur = []
 
        self.data7254 = DataFrame()
 
        btnStart = QPushButton('요청 시작', self)
        btnStart.move(20, 20)
        btnStart.clicked.connect(self.btnStart_clicked)
 
        btnExcel = QPushButton('Excel 내보내기', self)
        btnExcel.move(20, 70)
        btnExcel.clicked.connect(self.btnExcel_clicked)
 
        btnPrint = QPushButton('DF Print', self)
        btnPrint.move(20, 120)
        btnPrint.clicked.connect(self.btnPrint_clicked)
 
        btnExit = QPushButton('종료', self)
        btnExit.move(20, 190)
        btnExit.clicked.connect(self.btnExit_clicked)
 
 
 
    def btnStart_clicked(self):
        # 요청 필드 배열 - 종목코드, 시간, 대비부호 대비, 현재가, 거래량, 종목명
        obj7254 = CpRp7354()
        obj7254.Request('A000660',self)
 
 
    def btnExcel_clicked(self):
        print(len(self.data7254.index))
        # create a Pandas Excel writer using XlsxWriter as the engine.
        writer = pd.ExcelWriter(gExcelFile, engine='xlsxwriter')
        # Convert the dataframe to an XlsxWriter Excel object.
        self.data7254.to_excel(writer, sheet_name='Sheet1')
        # Close the Pandas Excel writer and output the Excel file.
        writer.save()
        os.startfile(gExcelFile)
        return
 
    def btnPrint_clicked(self):
        print(self.data7254)
 
    def btnExit_clicked(self):
        exit()
 
 
if __name__ == '__main__':
    app = QApplication(sys.argv)
    myWindow = MyWindow()
    myWindow.show()
    app.exec_()

11 해외선물 현재가/5차 호가 조회(실시간 업데이트 포함)[편집]

12 주식 현재가(10차호가/시간대별/일자별) 구현하기 예제[편집]

주식 현재가 화면을 구성하는 10차 호가, 시간대별, 일자별 데이터를 구현한 파이썬 예제입니다

화면 UI 는 PYQT 를 이용하였고 첨부된 파일에서 소스와 UI 를 받아 확인 가능합니다.

import sys
 
import pandas
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import *
from PyQt5 import uic
from PyQt5.QtCore import *
import win32com.client
from pandas import Series, DataFrame
import locale
 
 
# cp object
g_objCodeMgr = win32com.client.Dispatch("CpUtil.CpCodeMgr")
g_objCpStatus = win32com.client.Dispatch("CpUtil.CpCybos")
g_objCpTrade = win32com.client.Dispatch("CpTrade.CpTdUtil")
locale.setlocale(locale.LC_ALL, '')
 
 
# 현재가 정보 저장 구조체
class stockPricedData:
    def __init__(self):
        self.dicEx = {ord('0'): "동시호가/장중 아님", ord('1'): "동시호가", ord('2'): "장중"}
        self.code = ""
        self.name = ""
        self.cur = 0        # 현재가
        self.diff = 0       # 대비
        self.diffp = 0      # 대비율
        self.offer = [0 for _ in range(10)]     # 매도호가
        self.bid = [0 for _ in range(10)]       # 매수호가
        self.offervol = [0 for _ in range(10)]     # 매도호가 잔량
        self.bidvol = [0 for _ in range(10)]       # 매수호가 잔량
        self.totOffer = 0       # 총매도잔량
        self.totBid = 0         # 총매수 잔량
        self.vol = 0            # 거래량
        self.tvol = 0           # 순간 체결량
        self.baseprice = 0      # 기준가
        self.high = 0
        self.low = 0
        self.open = 0
        self.volFlag = ord('0')  # 체결매도/체결 매수 여부
        self.time = 0
        self.sum_buyvol = 0
        self.sum_sellvol = 0
        self.vol_str = 0
 
        # 예상체결가 정보
        self.exFlag= ord('2')
        self.expcur = 0         # 예상체결가
        self.expdiff = 0        # 예상 대비
        self.expdiffp = 0       # 예상 대비율
        self.expvol = 0         # 예상 거래량
        self.objCur = CpPBStockCur()
        self.objOfferbid = CpPBStockBid()
 
    def __del__(self):
        self.objCur.Unsubscribe()
        self.objOfferbid.Unsubscribe()
 
 
    # 전일 대비 계산
    def makediffp(self):
        lastday = 0
        if (self.exFlag == ord('1')):  # 동시호가 시간 (예상체결)
            if self.baseprice > 0  :
                lastday = self.baseprice
            else:
                lastday = self.expcur - self.expdiff
            if lastday:
                self.expdiffp = (self.expdiff / lastday) * 100
            else:
                self.expdiffp = 0
        else:
            if self.baseprice > 0  :
                lastday = self.baseprice
            else:
                lastday = self.cur - self.diff
            if lastday:
                self.diffp = (self.diff / lastday) * 100
            else:
                self.diffp = 0
 
    def getCurColor(self):
        diff = self.diff
        if (self.exFlag == ord('1')):  # 동시호가 시간 (예상체결)
            diff = self.expdiff
        if (diff > 0):
            return 'color: red'
        elif (diff == 0):
            return  'color: black'
        elif (diff < 0):
            return 'color: blue'
 
 
# CpEvent: 실시간 이벤트 수신 클래스
class CpEvent:
    def set_params(self, client, name, rpMst, parent):
        self.client = client  # CP 실시간 통신 object
        self.name = name  # 서비스가 다른 이벤트를 구분하기 위한 이름
        self.parent = parent  # callback 을 위해 보관
        self.rpMst = rpMst
 
 
 
    # PLUS 로 부터 실제로 시세를 수신 받는 이벤트 핸들러
    def OnReceived(self):
        if self.name == "stockcur":
            # 현재가 체결 데이터 실시간 업데이트
            self.rpMst.exFlag = self.client.GetHeaderValue(19)  # 예상체결 플래그
            code = self.client.GetHeaderValue(0)
            diff = self.client.GetHeaderValue(2)
            cur= self.client.GetHeaderValue(13)  # 현재가
            vol = self.client.GetHeaderValue(9)  # 거래량
 
            # 예제는 장중만 처리 함.
            if (self.rpMst.exFlag == ord('1')):  # 동시호가 시간 (예상체결)
                # 예상체결가 정보
                self.rpMst.expcur = cur
                self.rpMst.expdiff = diff
                self.rpMst.expvol = vol
            else:
                self.rpMst.cur = cur
                self.rpMst.diff = diff
                self.rpMst.makediffp()
                self.rpMst.vol = vol
                self.rpMst.open = self.client.GetHeaderValue(4)
                self.rpMst.high = self.client.GetHeaderValue(5)
                self.rpMst.low = self.client.GetHeaderValue(6)
                self.rpMst.tvol = self.client.GetHeaderValue(17)
                self.rpMst.volFlag = self.client.GetHeaderValue(14)  # '1'  매수 '2' 매도
                self.rpMst.time = self.client.GetHeaderValue(18)
                self.rpMst.sum_buyvol = self.client.GetHeaderValue(16)  #누적매수체결수량 (체결가방식)
                self.rpMst.sum_sellvol = self.client.GetHeaderValue(15) #누적매도체결수량 (체결가방식)
                if (self.rpMst.sum_sellvol) :
                    self.rpMst.volstr = self.rpMst.sum_buyvol / self.rpMst.sum_sellvol * 100
                else :
                    self.rpMst.volstr = 0
 
            self.rpMst.makediffp()
            # 현재가 업데이트
            self.parent.monitorPriceChange()
 
            return
 
        elif self.name == "stockbid":
            # 현재가 10차 호가 데이터 실시간 업데이c
            code = self.client.GetHeaderValue(0)
            dataindex = [3, 7, 11, 15, 19, 27, 31, 35, 39, 43]
            obi = 0
            for i in range(10):
                self.rpMst.offer[i] = self.client.GetHeaderValue(dataindex[i])
                self.rpMst.bid[i] = self.client.GetHeaderValue(dataindex[i] + 1)
                self.rpMst.offervol[i] = self.client.GetHeaderValue(dataindex[i] + 2)
                self.rpMst.bidvol[i] = self.client.GetHeaderValue(dataindex[i] + 3)
 
            self.rpMst.totOffer = self.client.GetHeaderValue(23)
            self.rpMst.totBid = self.client.GetHeaderValue(24)
            # 10차 호가 변경 call back 함수 호출
            self.parent.monitorOfferbidChange()
            return
 
# SB/PB 요청 ROOT 클래스
class CpPublish:
    def __init__(self, name, serviceID):
        self.name = name
        self.obj = win32com.client.Dispatch(serviceID)
        self.bIsSB = False
 
    def Subscribe(self, var, rpMst, parent):
        if self.bIsSB:
            self.Unsubscribe()
 
        if (len(var) > 0):
            self.obj.SetInputValue(0, var)
 
        handler = win32com.client.WithEvents(self.obj, CpEvent)
        handler.set_params(self.obj, self.name, rpMst, parent)
        self.obj.Subscribe()
        self.bIsSB = True
 
    def Unsubscribe(self):
        if self.bIsSB:
            self.obj.Unsubscribe()
        self.bIsSB = False
 
# CpPBStockCur: 실시간 현재가 요청 클래스
class CpPBStockCur(CpPublish):
    def __init__(self):
        super().__init__("stockcur", "DsCbo1.StockCur")
 
# CpPBStockBid: 실시간 10차 호가 요청 클래스
class CpPBStockBid(CpPublish):
    def __init__(self):
        super().__init__("stockbid", "Dscbo1.StockJpBid")
 
 
# SB/PB 요청 ROOT 클래스
class CpPBConnection:
    def __init__(self):
        self.obj = win32com.client.Dispatch("CpUtil.CpCybos")
        handler = win32com.client.WithEvents(self.obj, CpEvent)
        handler.set_params(self.obj, "connection", None)
 
 
# CpRPCurrentPrice:  현재가 기본 정보 조회 클래스
class CpRPCurrentPrice:
    def __init__(self):
        if (g_objCpStatus.IsConnect == 0):
            print("PLUS가 정상적으로 연결되지 않음. ")
            return
        self.objStockMst = win32com.client.Dispatch("DsCbo1.StockMst")
        return
 
 
    def Request(self, code, rtMst, callbackobj):
        # 현재가 통신
        rtMst.objCur.Unsubscribe()
        rtMst.objOfferbid.Unsubscribe()
 
        self.objStockMst.SetInputValue(0, code)
        ret = self.objStockMst.BlockRequest()
        if self.objStockMst.GetDibStatus() != 0:
            print("통신상태", self.objStockMst.GetDibStatus(), self.objStockMst.GetDibMsg1())
            return False
 
 
        # 수신 받은 현재가 정보를 rtMst 에 저장
        rtMst.code = code
        rtMst.name = g_objCodeMgr.CodeToName(code)
        rtMst.cur =  self.objStockMst.GetHeaderValue(11)  # 종가
        rtMst.diff =  self.objStockMst.GetHeaderValue(12)  # 전일대비
        rtMst.baseprice  =  self.objStockMst.GetHeaderValue(27)  # 기준가
        rtMst.vol = self.objStockMst.GetHeaderValue(18)  # 거래량
        rtMst.exFlag = self.objStockMst.GetHeaderValue(58)  # 예상플래그
        rtMst.expcur = self.objStockMst.GetHeaderValue(55)  # 예상체결가
        rtMst.expdiff = self.objStockMst.GetHeaderValue(56)  # 예상체결대비
        rtMst.makediffp()
 
        rtMst.totOffer = self.objStockMst.GetHeaderValue(71)  # 총매도잔량
        rtMst.totBid = self.objStockMst.GetHeaderValue(73)  # 총매수잔량
 
 
        # 10차호가
        for i in range(10):
            rtMst.offer[i] = (self.objStockMst.GetDataValue(0, i))  # 매도호가
            rtMst.bid[i] = (self.objStockMst.GetDataValue(1, i) ) # 매수호가
            rtMst.offervol[i] = (self.objStockMst.GetDataValue(2, i))  # 매도호가 잔량
            rtMst.bidvol[i] = (self.objStockMst.GetDataValue(3, i) ) # 매수호가 잔량
 
 
        rtMst.objCur.Subscribe(code,rtMst, callbackobj)
        rtMst.objOfferbid.Subscribe(code,rtMst, callbackobj)
 
 
# CpWeekList:  일자별 리스트 구하기
class CpWeekList:
    def __init__(self):
        self.objWeek = win32com.client.Dispatch("Dscbo1.StockWeek")
        return
 
 
    def Request(self, code, caller):
        # 현재가 통신
        self.objWeek.SetInputValue(0, code)
        # 데이터들
        dates = []
        opens = []
        highs = []
        lows = []
        closes = []
        diffs = []
        vols = []
        diffps = []
        foreign_vols = []
        foreign_diff = []
        foreign_p = []
 
        # 누적 개수 - 100 개까지만 하자
        sumCnt = 0
        while True:
            ret = self.objWeek.BlockRequest()
            if self.objWeek.GetDibStatus() != 0:
                print("통신상태", self.objWeek.GetDibStatus(), self.objWeek.GetDibMsg1())
                return False
 
            cnt = self.objWeek.GetHeaderValue(1)
            sumCnt += cnt
            if cnt == 0:
                break
 
            for i in range(cnt):
                dates.append(self.objWeek.GetDataValue(0, i))
                opens.append(self.objWeek.GetDataValue(1, i))
                highs.append(self.objWeek.GetDataValue(2, i))
                lows.append(self.objWeek.GetDataValue(3, i))
                closes.append(self.objWeek.GetDataValue(4, i))
 
                temp = self.objWeek.GetDataValue(5, i)
                diffs.append(temp)
                vols.append(self.objWeek.GetDataValue(6, i))
 
                temp2 = self.objWeek.GetDataValue(10, i)
                if (temp < 0):
                    temp2 *= -1
                diffps.append(temp2)
 
                foreign_vols.append(self.objWeek.GetDataValue(7, i)) # 외인보유
                foreign_diff.append(self.objWeek.GetDataValue(8, i)) # 외인보유 전일대비
                foreign_p.append(self.objWeek.GetDataValue(9, i)) # 외인비중
 
            if (sumCnt > 100):
                break
 
            if self.objWeek.Continue == False:
                break
 
        if len(dates) == 0:
            return False
 
        caller.rpWeek = None
        weekCol = {'close': closes,
                   'diff':  diffs,
                   'diffp': diffps,
                    'vol': vols,
                    'open':opens,
                    'high': highs,
                    'low': lows,
                    'for_v' : foreign_vols,
                    'for_d': foreign_diff,
                    'for_p': foreign_p,
                   }
        caller.rpWeek = DataFrame(weekCol, index=dates)
        return True
 
 
# CpStockBid:  시간대별 조회
class CpStockBid:
    def __init__(self):
        self.objSBid = win32com.client.Dispatch("Dscbo1.StockBid")
        return
 
 
    def Request(self, code, caller):
        # 현재가 통신
        self.objSBid.SetInputValue(0, code)
        self.objSBid.SetInputValue(2, 80)  # 요청개수 (최대 80)
        self.objSBid.SetInputValue(3, ord('C'))  # C 체결가 비교 방식 H 호가 비교방식
 
        times = []
        curs = []
        diffs = []
        tvols = []
        offers = []
        bids = []
        vols = []
        offerbidFlags = [] # 체결 상태 '1' 매수 '2' 매도
        volstrs = [] # 체결강도
        marketFlags = [] # 장구분 '1' 동시호가 예상체결' '2' 장중
 
        # 누적 개수 - 100 개까지만 하자
        sumCnt = 0
        while True:
            ret = self.objSBid.BlockRequest()
            if self.objSBid.GetDibStatus() != 0:
                print("통신상태", self.objSBid.GetDibStatus(), self.objSBid.GetDibMsg1())
                return False
 
            cnt = self.objSBid.GetHeaderValue(2)
            sumCnt += cnt
            if cnt == 0:
                break
 
            strcur = ""
            strflag = ""
            strflag2 = ""
            for i in range(cnt):
                cur = self.objSBid.GetDataValue(4, i)
                times.append(self.objSBid.GetDataValue(9, i))
                diffs.append(self.objSBid.GetDataValue(1, i))
                vols.append(self.objSBid.GetDataValue(5, i))
                tvols.append(self.objSBid.GetDataValue(6, i))
                offers.append(self.objSBid.GetDataValue(2, i))
                bids.append(self.objSBid.GetDataValue(3, i))
                flag = self.objSBid.GetDataValue(7, i)
                if (flag == ord('1')):
                    strflag = "체결매수"
                else:
                    strflag = "체결매도"
                offerbidFlags.append(strflag)
                volstrs.append(self.objSBid.GetDataValue(8, i))
                flag = self.objSBid.GetDataValue(10, i)
                if (flag == ord('1')):
                    strflag2 = "예상체결"
                    #strcur = '*' + str(cur)
                else:
                    strflag2 = "장중"
                    #strcur = str(cur)
                marketFlags.append(strflag2)
                curs.append(cur)
 
 
            if (sumCnt > 100):
                break
 
            if self.objSBid.Continue == False:
                break
 
        if len(times) == 0:
            return False
 
        caller.rpStockBid = None
        sBidCol = {'time': times,
                   'cur':  curs,
                   'diff': diffs,
                    'vol': vols,
                    'tvol':tvols,
                    'offer': offers,
                    'bid': bids,
                    'flag': offerbidFlags,
                    'market': marketFlags,
                    'volstr': volstrs}
        caller.rpStockBid = DataFrame(sBidCol)
        print(caller.rpStockBid)
        return True
 
class Form(QtWidgets.QDialog):
    def __init__(self, parent=None):
        QtWidgets.QDialog.__init__(self, parent)
        self.ui = uic.loadUi("hoga.ui", self)
        self.ui.show()
        self.objMst = CpRPCurrentPrice()
        self.item = stockPricedData()
 
        # 일자별
        self.objWeek = CpWeekList()
        self.rpWeek = DataFrame()   # 일자별 데이터프레임
 
 
        # 시간대별
        self.rpStockBid = DataFrame()
        self.objStockBid = CpStockBid()
        self.todayIndex = 0
 
        self.setCode("000660")
 
 
    @pyqtSlot()
    def slot_codeupdate(self):
        code = self.ui.editCode.toPlainText()
        self.setCode(code)
 
    def slot_codechanged(self):
        code = self.ui.editCode.toPlainText()
        self.setCode(code)
 
 
    def monitorPriceChange(self):
        self.displyHoga()
        self.updateWeek()
        self.updateStockBid()
 
    def monitorOfferbidChange(self):
        self.displyHoga()
 
    def setCode(self, code):
        if len(code) < 6 :
            return
 
        print(code)
        if not (code[0] == "A"):
            code = "A" + code
 
        name = g_objCodeMgr.CodeToName(code)
        if len(name) == 0:
            print("종목코드 확인")
            return
 
        self.ui.label_name.setText(name)
 
        if (self.objMst.Request(code, self.item, self) == False):
            return
        self.displyHoga()
 
 
        # 일자별
        self.ui.tableWeek.clearContents()
        if (self.objWeek.Request(code, self) == True):
            print(self.rpWeek)
            self.displyWeek()
 
        # 시간대별
        self.ui.tableStockBid.clearContents()
        if (self.objStockBid.Request(code, self) == True):
            self.displyStockBid()
 
 
    # 10차 호가 UI 채우기
    def displyHoga(self):
        self.ui.label_offer10.setText(format(self.item.offer[9],','))
        self.ui.label_offer9.setText(format(self.item.offer[8],','))
        self.ui.label_offer8.setText(format(self.item.offer[7],','))
        self.ui.label_offer7.setText(format(self.item.offer[6],','))
        self.ui.label_offer6.setText(format(self.item.offer[5],','))
        self.ui.label_offer5.setText(format(self.item.offer[4],','))
        self.ui.label_offer4.setText(format(self.item.offer[3],','))
        self.ui.label_offer3.setText(format(self.item.offer[2],','))
        self.ui.label_offer2.setText(format(self.item.offer[1],','))
        self.ui.label_offer1.setText(format(self.item.offer[0],','))
 
        self.ui.label_offer_v10.setText(format(self.item.offervol[9],','))
        self.ui.label_offer_v9.setText(format(self.item.offervol[8],','))
        self.ui.label_offer_v8.setText(format(self.item.offervol[7],','))
        self.ui.label_offer_v7.setText(format(self.item.offervol[6],','))
        self.ui.label_offer_v6.setText(format(self.item.offervol[5],','))
        self.ui.label_offer_v5.setText(format(self.item.offervol[4],','))
        self.ui.label_offer_v4.setText(format(self.item.offervol[3],','))
        self.ui.label_offer_v3.setText(format(self.item.offervol[2],','))
        self.ui.label_offer_v2.setText(format(self.item.offervol[1],','))
        self.ui.label_offer_v1.setText(format(self.item.offervol[0],','))
 
        self.ui.label_bid10.setText(format(self.item.bid[9],','))
        self.ui.label_bid9.setText(format(self.item.bid[8],','))
        self.ui.label_bid8.setText(format(self.item.bid[7],','))
        self.ui.label_bid7.setText(format(self.item.bid[6],','))
        self.ui.label_bid6.setText(format(self.item.bid[5],','))
        self.ui.label_bid5.setText(format(self.item.bid[4],','))
        self.ui.label_bid4.setText(format(self.item.bid[3],','))
        self.ui.label_bid3.setText(format(self.item.bid[2],','))
        self.ui.label_bid2.setText(format(self.item.bid[1],','))
        self.ui.label_bid1.setText(format(self.item.bid[0],','))
 
        self.ui.label_bid_v10.setText(format(self.item.bidvol[9],','))
        self.ui.label_bid_v9.setText(format(self.item.bidvol[8],','))
        self.ui.label_bid_v8.setText(format(self.item.bidvol[7],','))
        self.ui.label_bid_v7.setText(format(self.item.bidvol[6],','))
        self.ui.label_bid_v6.setText(format(self.item.bidvol[5],','))
        self.ui.label_bid_v5.setText(format(self.item.bidvol[4],','))
        self.ui.label_bid_v4.setText(format(self.item.bidvol[3],','))
        self.ui.label_bid_v3.setText(format(self.item.bidvol[2],','))
        self.ui.label_bid_v2.setText(format(self.item.bidvol[1],','))
        self.ui.label_bid_v1.setText(format(self.item.bidvol[0],','))
 
        cur = self.item.cur
        diff = self.item.diff
        diffp = self.item.diffp
        if (self.item.exFlag == ord('1')):  # 동시호가 시간 (예상체결)
            cur = self.item.expcur
            diff = self.item.expdiff
            diffp = self.item.expdiffp
 
 
        strcur = format(cur, ',')
        if (self.item.exFlag == ord('1')):  # 동시호가 시간 (예상체결)
            strcur = "*" + strcur
 
        curcolor = self.item.getCurColor()
        self.ui.label_cur.setStyleSheet(curcolor)
        self.ui.label_cur.setText(strcur)
        strdiff = str(diff) + "  " + format(diffp, '.2f')
        strdiff += "%"
        self.ui.label_diff.setText(strdiff)
        self.ui.label_diff.setStyleSheet(curcolor)
 
        self.ui.label_totoffer.setText(format(self.item.totOffer,','))
        self.ui.label_totbid.setText(format(self.item.totBid,','))
 
    # 일자별 리스트 UI 채우기
    def displyWeek(self):
        rowcnt = len(self.rpWeek.index)
        if rowcnt == 0:
            return
        self.ui.tableWeek.setRowCount(rowcnt)
 
        nRow = 0
 
        for index, row in self.rpWeek.iterrows():
            datas = [index, row['close'],row['diff'],row['diffp'],row['vol'],row['open'],row['high'],row['low'],
                     row['for_v'], row['for_d'], row['for_p']]
            for col in range(len(datas)) :
                val = ''
                if (col == 0):  # 일자
                    # 20170929 ==> 2017/09/29
                    yyyy = int(datas[col] / 10000)
                    mm = int(datas[col] - (yyyy * 10000))
                    dd = mm % 100
                    mm = mm / 100
                    val = '%04d/%02d/%02d' %(yyyy, mm, dd)
                elif (col == 3 or col == 10): # 대비율
                    val = locale.format('%.2f', datas[col], 1)
                    val += "%"
 
                else:
                    val = locale.format('%d', datas[col], 1)
 
                item = QTableWidgetItem(val)
                item.setTextAlignment(Qt.AlignVCenter | Qt.AlignRight)
                self.ui.tableWeek.setItem(nRow, col, item)
 
            if (nRow == 0) :
                self.todayIndex = index
            nRow += 1
 
            self.tableWeek.resizeColumnsToContents()
        return
 
    # 일자별 리스트 UI 채우기 - 오늘 날짜 업데이트
    def updateWeek(self):
        rowcnt = len(self.rpWeek.index)
        if rowcnt == 0:
            return
 
        # 오늘 날짜 데이터 업데이트
        self.rpWeek.set_value(self.todayIndex, 'close', self.item.cur)
        self.rpWeek.set_value(self.todayIndex, 'open', self.item.open)
        self.rpWeek.set_value(self.todayIndex, 'high', self.item.high)
        self.rpWeek.set_value(self.todayIndex, 'low', self.item.low)
        self.rpWeek.set_value(self.todayIndex, 'vol', self.item.vol)
        self.rpWeek.set_value(self.todayIndex, 'diff', self.item.diff)
        self.rpWeek.set_value(self.todayIndex, 'diffp', self.item.diffp)
 
        datas = [self.todayIndex, self.item.cur,self.item.diff, self.item.diffp, self.item.vol,
                 self.item.open, self.item.high, self.item.low]
        for col in range(len(datas)) :
            val = ''
            if (col == 0):  # 일자
                # 20170929 ==> 2017/09/29
                yyyy = int(datas[col] / 10000)
                mm = int(datas[col] - (yyyy * 10000))
                dd = mm % 100
                mm = mm / 100
                val = '%04d/%02d/%02d' %(yyyy, mm, dd)
            elif (col == 3): # 대비율
                val = locale.format('%.2f', datas[col], 1)
                val += "%"
 
            else:
                val = locale.format('%d', datas[col], 1)
 
            item = QTableWidgetItem(val)
            item.setTextAlignment(Qt.AlignVCenter | Qt.AlignRight)
            self.ui.tableWeek.setItem(0, col, item)
 
        return
 
 
 
    # 시간대별 리스트 UI  채우기
    def displyStockBid(self):
        rowcnt = len(self.rpStockBid.index)
        if rowcnt == 0:
            return
        self.ui.tableStockBid.setRowCount(rowcnt)
 
        nRow = 0
 
        for index, row in self.rpStockBid.iterrows():
            # 행 내에 표시할 데이터 - 컬럼 순
            datas = [row['time'], row['cur'], row['diff'], row['offer'], row['bid'], row['vol'], row['tvol'],
                     row['tvol'], row['volstr']]
            market = row['market']
            for col in range(len(datas)):
                val = ''
                if col == 0: # 시각
                    # 155925 ==> 15:59:25
                    hh = int(datas[col] / 10000)
                    mm = int(datas[col] - (hh * 10000))
                    ss = mm % 100
                    mm = mm / 100
                    val = '%02d:%02d:%02d' %(hh, mm, ss)
                elif col == 6: # 체결매도
                    market = row['flag']
                    if (market == "체결매도") :
                        val = locale.format('%d', datas[col], 1)
                elif col == 7: # 체결매수
                    market = row['flag']
                    if (market == "체결매수"):
                        val = locale.format('%d', datas[col], 1)
                elif col == 8:  # 체결강도
                    val = locale.format('%.2f', datas[col], 1)
                elif col == 1: # 현재가
                    val = locale.format('%d', datas[col], 1)
                    if (market == "예상체결"):
                        val = '*' + val
                else:          # 기타
                    val = locale.format('%d', datas[col], 1)
                item = QTableWidgetItem(val)
                item.setTextAlignment(Qt.AlignVCenter | Qt.AlignRight)
                self.ui.tableStockBid.setItem(nRow, col, item)
            nRow += 1
 
        self.tableStockBid.resizeColumnsToContents()
        return
 
 
    def updateStockBid(self):
        rowcnt = len(self.rpStockBid.index)
        if rowcnt == 0:
            return
        if (self.item.exFlag == ord('1')):  # 동시호가 시간 (예상체결)
            return
 
        buyvol = sellvol = 0
        if self.item.volFlag == ord('1') :
                buyvol = self.item.tvol
        if self.item.volFlag == ord('2') :
                sellvol = self.item.tvol
        line = DataFrame({"time": self.item.time,
                          "cur": self.item.cur,
                          "diff": self.item.diff,
                          "offer": self.item.offer[0],
                          "bid": self.item.bid[0],
                          "vol": self.item.vol,
                          "tvol": buyvol,
                          "tvol": sellvol,
                          "volstr": self.item.volstr},
                         index=[0])
 
        self.rpStockBid = pandas.concat([line, self.rpStockBid.ix[:]]).reset_index(drop=True)
 
        # 행 내에 표시할 데이터 - 컬럼 순
        datas = [self.item.time, self.item.cur, self.item.diff, self.item.offer[0], self.item.bid[0],
                 self.item.vol, sellvol, buyvol, self.item.volstr]
        self.ui.tableStockBid.insertRow(0)
        for col in range(len(datas)):
            val = ''
            if col == 0: # 시각
                # 155925 ==> 15:59:25
                hh = int(datas[col] / 10000)
                mm = int(datas[col] - (hh * 10000))
                ss = mm % 100
                mm = mm / 100
                val = '%02d:%02d:%02d' %(hh, mm, ss)
            elif col == 6: # 체결매도
                val = locale.format('%d', datas[col], 1)
            elif col == 7: # 체결매수
                val = locale.format('%d', datas[col], 1)
            elif col == 8: # 체결강도
                val = locale.format('%.2f', datas[col], 1)
            else:          # 기타
                val = locale.format('%d', datas[col], 1)
 
            item = QTableWidgetItem(val)
            item.setTextAlignment(Qt.AlignVCenter | Qt.AlignRight)
            self.ui.tableStockBid.setItem(0, col, item)
 
 
        return
 
 
if __name__ == '__main__':
        app = QtWidgets.QApplication(sys.argv)
        w = Form()
        sys.exit(app.exec())

13 주식 미체결 조회 및 실시간 미체결 업데이트/취소주문/일괄 취소 예제...[편집]

주식 미체결을 실시간으로 처리하는 파이썬 예제 입니다

※ 제공된 예제는 PLUS API 학습을 위해 제공되는 예제로, 모든 예외 케이스와 상세 처리가 포함되어 있지 않습니다. 참고로만 이용하시기 바랍니다.

1. 미체결 (CpTrade.CpTd5339)

   예제에서는 Cp5339 클래스에서 미체결을 조회 합니다 
   연속 조회를 통해 당일 발생한 모든 미체결을 조회 합니다. 

2. 실시간 주문 체결 (DsCbo1.CpConclusion) 미체결을 실시간으로 감시하기 위해서는 "DsCbo1.CpConclusion" 에 이벤트를 등록하고 이벤트를 수신 받아 처리 해야 합니다 DsCbo1.CpConclusion 이벤트는 CpEvent::OnReceived 에서 수신 받아 처리 합니다.

기본적으로 실시간 주문 체결의 경우 아래 4가지 type 이 있습니다 ▶ 접수 - 주문에 대한 1차 수신 ▶ 확인(정정 또는 취소) - 정정이나 취소 주문에 대해 거래소로 부터 확인 응답을 받음 ▶ 체결 - 주문 내역 중 일부 또는 전체가 체결됨을 통보 받음 ▶ 거부 - 정정 또는 취소 주문 등이 거래소로 부터 거부 됨(이미 체결 된 경우)


3. 취소 주문 (CpTrade.CpTd0314) 클래스 CpRPOrder 에서는 CpTrade.CpTd0314 를 이용하여 취소주문을 냅니다 CpRPOrder 에서는 2가지 취소 방식을 제공하는데

RequestCancel 는 Request API 를 BlockRequestCancel 는 BlockRequest API 를 각각 이용하여 취소 주문을 냅니다

Request API 의 경우 수신이벤트를 등록하고 처리해야 함으로 이에 대한 예제로 참고 바랍니다.

4. 일괄 취소 일괄 취소는 미체결 된 전체 주문 리스트에 대해 BlockRequest 를 이용하여 취소 주문을 냅니다

5. 연속 주문에 대한 처리 PLUS 는 연속적으로 주문/계좌 조회가 발생 할 경우 서비스 보호를 위해 오류 코드 4를 리턴합니다 이 경우에는 CpUtil.CpCybos > LimitRequestRemainTime 를 이용하여 남은 대기 시간동안 대기 후 재 시도 또는 다른 방법을 찾아야 합니다 (신규 주문인 경우 대기 시간 동안 주문 가격이 달라질 수 있으니 이런 점에 유의가 필요 합니다) 예제에 취소 주문과 미체결 통신에 연속 조회 오류 코드를 참고 하시기 바랍니다.

import sys
from PyQt5.QtWidgets import *
import win32com.client
import time
 
 
g_objCodeMgr = win32com.client.Dispatch("CpUtil.CpCodeMgr")
g_objCpStatus = win32com.client.Dispatch("CpUtil.CpCybos")
g_objCpTrade = win32com.client.Dispatch("CpTrade.CpTdUtil")
 
# 미체결 주문 정보 저장 구조체
class orderData:
    def __init__(self):
        self.code = ""          # 종목코드
        self.name = ""          # 종목명
        self.orderNum = 0       # 주문번호
        self.orderPrev = 0      # 원주문번호
        self.orderDesc = ""     # 주문구분내용
        self.amount = 0     # 주문수량
        self.price = 0      # 주문 단가
        self.ContAmount = 0  # 체결수량
        self.credit = ""     # 신용 구분 "현금" "유통융자" "자기융자" "유통대주" "자기대주"
        self.modAvali = 0  # 정정/취소 가능 수량
        self.buysell = ""  # 매매구분 코드  1 매도 2 매수
        self.creditdate = ""    # 대출일
        self.orderFlag = ""     # 주문호가 구분코드
        self.orderFlagDesc = "" # 주문호가 구분 코드 내용
 
        # 데이터 변환용
        self.concdic = {"1": "체결", "2": "확인", "3": "거부", "4": "접수"}
        self.buyselldic = {"1": "매도", "2": "매수"}
 
    def debugPrint(self):
        print("%s, %s, 주문번호 %d, 원주문 %d, %s, 주문수량 %d, 주문단가 %d, 체결수량 %d, %s, "
              "정정가능수량 %d, 매수매도: %s, 대출일 %s, 주문호가구분 %s %s"
              %(self.code, self.name, self.orderNum, self.orderPrev, self.orderDesc, self.amount, self.price,
                self.ContAmount,self.credit,self.modAvali, self.buyselldic.get(self.buysell),
                self.creditdate,self.orderFlag, self.orderFlagDesc))
 
 
 
# CpEvent: 실시간 이벤트 수신 클래스
class CpEvent:
    def set_params(self, client, name, parent):
        self.client = client  # CP 실시간 통신 object
        self.name = name  # 서비스가 다른 이벤트를 구분하기 위한 이름
        self.parent = parent  # callback 을 위해 보관
 
        self.concdic = {"1" : "체결", "2" : "확인", "3" : "거부", "4" : "접수"}
 
 
    # PLUS 로 부터 실제로 이벤트(체결/주문 응답/시세 이벤트 등)를 수신 받아 처리하는 함수.
    # 여러가지 이벤트가 이 클래스로 들어 오기 때문에 구분은  이벤트 등록 시 사용한 self.name 을 통해 구분한다.
    def OnReceived(self):
        # 주문 Request 에 대한 응답 처리
        if self.name == "td0314" :
            print("[CpEvent]주문응답")
            self.parent.OrderReply()
            return
 
        # 주문 체결 PB 에 대한 처리
        elif self.name == "conclusion" :
            # 주문 체결 실시간 업데이트
            i3 = self.client.GetHeaderValue(3)     # 체결 수량
            i4 = self.client.GetHeaderValue(4)     # 가격
            i5 = self.client.GetHeaderValue(5)     # 주문번호
            i6 = self.client.GetHeaderValue(6)     # 원주문번호
            i9 = self.client.GetHeaderValue(9)     # 종목코드
            i12 = self.client.GetHeaderValue(12)   # 매수/매도 구분 1 매도 2매수
            i14 = self.client.GetHeaderValue(14)   # 체결 플래그 1 체결 2 확인...
            i15 = self.client.GetHeaderValue(15)   # 신용대출구분
            i16 = self.client.GetHeaderValue(16)  # 정정/취소 구분코드 (1 정상, 2 정정 3 취소)
            i17  = self.client.GetHeaderValue(17)  # 현금신용대용 구분
            i18 = self.client.GetHeaderValue(18)  # 주문호가구분코드
            i19 = self.client.GetHeaderValue(19)  # 주문조건구분코드
            i20 = self.client.GetHeaderValue(20)  # 대출일
            i21 = self.client.GetHeaderValue(21)  # 장부가
            i22 = self.client.GetHeaderValue(22)  # 매도가능수량
            i23 = self.client.GetHeaderValue(23)  # 체결기준잔고수량
 
            # for debug
            print("[CpEvent]%s, 수량 %d, 가격 %d, 주문번호 %d, 원주문 %d, 코드 %s, 매도매수 %s, 신용대출 %s 정정취소 %s,"
                  "현금신용대용 %s, 주문호가구분 %s, 주문조건구분 %s, 대출일 %s, 장부가 %d, 매도가능 %d, 체결기준잔고%d"
                  %(self.concdic.get(i14), i3, i4, i5, i6, i9, i12, i15, i16, i17, i18, i19, i20, i21, i22, i23) )
 
 
            # 체결 에 대한 처리
            #   미체결에서 체결이 발생한 주문번호를 찾아 주문 수량과 체결 수량을 비교한다
            #   전부 체결이면 미체결을 지우고, 부분 체결일 경우 체결 된 수량만큼 주문 수량에서 제한다.
            if (i14 == "1") : # 체결
                if not (i5 in self.parent.diOrderList) :
                    print("[CpEvent]주문번호 찾기 실패", i5)
                    return
                item = self.parent.diOrderList[i5]
                if (item.amount - i3 > 0):  # 일부 체결인경우
                    # 기존 데이터 업데이트
                    item.amount -= i3
                    item.modAvali = item.amount
                    item.ContAmount += i3
                else:   # 전체 체결인 경우
                    self.parent.deleteOrderNum(i5)
 
                # for debug
                #for i in range(len(self.parent.orderList)):
                #    self.parent.orderList[i].debugPrint()
                print("[CpEvent]미체결 개수 ", len(self.parent.orderList))
 
 
 
            # 확인 에 대한 처리
            #   정정확인 - 정정주문이 발생한 원주문을 찾아
            #       부분 정정인 경우 - 기존 주문은 수량을 업데이트, 새로운 정정에 의한 미체결 주문번호는 신규 추가
            #       전체 정정인 경우 - 주문 리스트의 원주문/주문번호만 업데이트
            #   취소 확인 - 취소주문이 발생한 원주문을 찾아 미체결 리스트에서 제거 한다.
            elif (i14 == "2") : # 확인
                # 원주문 번호로 찾는다.
                if not (i6 in self.parent.diOrderList) :
                    print("[CpEvent]원주문번호 찾기 실패", i6)
                    # IOC/FOK 의 경우 취소 주문을 낸적이 없어도 자동으로 취소 확인이 들어 온다.
                    if i5 in self.parent.diOrderList and (i16 == "3") :
                        self.parent.deleteOrderNum(i5)
                        self.parent.ForwardPB("cancelpb", i5)
 
                    return
                item = self.parent.diOrderList[i6]
                if (i16 == "2") : # 정정 확인 ==> 미체결 업데이트 해야 함.
                    print("[CpEvent]정정확인", item.amount, i3)
                    if (item.amount - i3 > 0):  # 일부 정정인 경우
                        # 기존 데이터 업데이트
                        item.amount -= i3
                        item.modAvali = item.amount
                        # 새로운  미체결 추가
                        item2 = orderData()
                        item2.code = i9
                        item2.name = g_objCodeMgr.CodeToName(i9)
                        item2.orderNum = i5
                        item2.orderPrev = i6
                        item2.buysell = i12
                        item2.modAvali = item2.amount = i3
                        item2.price = i4
                        item2.orderFlag = i18
                        item2.debugPrint()
                        self.parent.diOrderList[i5] = item2
                        self.parent.orderList.append(item2)
 
                    else:   # 잔량 정정 인 경우 ==> 업데이트
                        item.orderNum = i5  # 주문번호 변경
                        item.orderPrev = i6 # 원주문번호 변경
                        item.modAvali = item.amount = i3
                        item.price = i4
                        item.orderFlag = i18
                        item.debugPrint()
                        # 주문번호가  변경 되어 기존 key 는 제거
                        self.parent.diOrderList[i5] = item
                        del self.parent.diOrderList[i6]
 
 
                elif (i16 == "3") : # 취소 확인 ==> 미체결 찾아 지운다.
                    self.parent.deleteOrderNum(i6)
                    self.parent.ForwardPB("cancelpb", i6)
                # for debug
                #for i in range(len(self.parent.orderList)):
                #    self.parent.orderList[i].debugPrint()
                print("[CpEvent]미체결 개수 ", len(self.parent.orderList))
 
            elif (i14 == "3"):  # 거부
                print("[CpEvent]거부")
 
            # 접수 - 신규 접수만 처리. 새로운 주문에 대한 접수는 미체결 리스트에 추가한다.
            elif (i14 == "4"):  # 접수
                if not (i16 == "1") :
                    print("[CpEvent]정정이나 취소 접수는 일단 무시한다.")
                    return
                item = orderData()
                item.code = i9
                item.name = g_objCodeMgr.CodeToName(i9)
                item.orderNum = i5
                item.buysell = i12
                item.modAvali = item.amount = i3
                item.price = i4
                item.orderFlag = i18
                item.debugPrint()
                self.parent.diOrderList[i5] = item
                self.parent.orderList.append(item)
 
                print("[CpEvent]미체결 개수 ", len(self.parent.orderList))
 
            return
 
 
# SB/PB 요청 ROOT 클래스
class CpPublish:
    def __init__(self, name, serviceID):
        self.name = name
        self.obj = win32com.client.Dispatch(serviceID)
        self.bIsSB = False
 
    def __del__(self):
        self.Unsubscribe()
 
 
    def Subscribe(self, var, parent):
        if self.bIsSB:
            self.Unsubscribe()
 
        if (len(var) > 0):
            self.obj.SetInputValue(0, var)
 
        handler = win32com.client.WithEvents(self.obj, CpEvent)
        handler.set_params(self.obj, self.name, parent)
        self.obj.Subscribe()
        self.bIsSB = True
 
    def Unsubscribe(self):
        if self.bIsSB:
            self.obj.Unsubscribe()
        self.bIsSB = False
 
# CpPBStockCur: 실시간 현재가 요청 클래스
class CpConclution(CpPublish):
    def __init__(self):
        super().__init__("conclusion", "DsCbo1.CpConclusion")
 
# 취소 주문 요청에 대한 응답 이벤트 처리 클래스
class CpPB0314:
    def __init__(self, obj) :
        self.name = "td0314"
        self.obj = obj
 
    def Subscribe(self, parent):
        handler = win32com.client.WithEvents(self.obj, CpEvent)
        handler.set_params(self.obj, self.name, parent)
 
# 주식 주문 취소 클래스
class CpRPOrder:
    def __init__(self):
        self.acc = g_objCpTrade.AccountNumber[0]  # 계좌번호
        self.accFlag = g_objCpTrade.GoodsList(self.acc, 1)  # 주식상품 구분
        self.objCancelOrder = win32com.client.Dispatch("CpTrade.CpTd0314")  # 취소
        self.callback = None
        self.bIsRq = False
        self.RqOrderNum = 0     # 취소 주문 중인 주문 번호
 
    # 주문 취소 통신 - Request 를 이용하여 취소 주문
    # callback 은 취소 주문의 reply 이벤트를 전달하기 위해 필요
    def RequestCancel(self, ordernum, code, amount, callback):
        # 주식 취소 주문
        if self.bIsRq:
            print("RequestCancel - 통신 중이라 주문 불가 ")
            return False
        self.callback = callback
        print("[CpRPOrder/RequestCancel]취소주문", ordernum, code,amount)
        self.objCancelOrder.SetInputValue(1, ordernum)  # 원주문 번호 - 정정을 하려는 주문 번호
        self.objCancelOrder.SetInputValue(2, self.acc)  # 상품구분 - 주식 상품 중 첫번째
        self.objCancelOrder.SetInputValue(3, self.accFlag[0])  # 상품구분 - 주식 상품 중 첫번째
        self.objCancelOrder.SetInputValue(4, code)  # 종목코드
        self.objCancelOrder.SetInputValue(5, amount)  # 정정 수량, 0 이면 잔량 취소임
 
        # 취소주문 요청
        ret = 0
        while True:
            ret = self.objCancelOrder.Request()
            if ret == 0:
                break
 
            print("[CpRPOrder/RequestCancel] 주문 요청 실패 ret : ", ret)
            if ret == 4:
                remainTime = g_objCpStatus.LimitRequestRemainTime
                print("연속 통신 초과에 의해 재 통신처리 : ", remainTime / 1000, "초 대기")
                time.sleep(remainTime / 1000)
                continue
            else:   # 1 통신 요청 실패 3 그 외의 오류 4: 주문요청제한 개수 초과
                return False;
 
 
        self.bIsRq = True
        self.RqOrderNum = ordernum
 
        # 주문 응답(이벤트로 수신
        self.objReply = CpPB0314(self.objCancelOrder)
        self.objReply.Subscribe(self)
        return True
 
    # 취소 주문 - BloockReqeust 를 이용해서 취소 주문
    def BlockRequestCancel(self, ordernum, code, amount, callback):
        # 주식 취소 주문
        self.callback = callback
        print("[CpRPOrder/BlockRequestCancel]취소주문2", ordernum, code,amount)
        self.objCancelOrder.SetInputValue(1, ordernum)  # 원주문 번호 - 정정을 하려는 주문 번호
        self.objCancelOrder.SetInputValue(2, self.acc)  # 상품구분 - 주식 상품 중 첫번째
        self.objCancelOrder.SetInputValue(3, self.accFlag[0])  # 상품구분 - 주식 상품 중 첫번째
        self.objCancelOrder.SetInputValue(4, code)  # 종목코드
        self.objCancelOrder.SetInputValue(5, amount)  # 정정 수량, 0 이면 잔량 취소임
 
        # 취소주문 요청
        ret = 0
        while True:
            ret = self.objCancelOrder.BlockRequest()
            if ret == 0:
                break;
            print("[CpRPOrder/RequestCancel] 주문 요청 실패 ret : ", ret)
            if ret == 4:
                remainTime = g_objCpStatus.LimitRequestRemainTime
                print("연속 통신 초과에 의해 재 통신처리 : ", remainTime / 1000, "초 대기")
                time.sleep(remainTime / 1000)
                continue
            else:   # 1 통신 요청 실패 3 그 외의 오류 4: 주문요청제한 개수 초과
                return False;
 
        print("[CpRPOrder/BlockRequestCancel] 주문결과", self.objCancelOrder.GetDibStatus(), self.objCancelOrder.GetDibMsg1())
        if self.objCancelOrder.GetDibStatus() != 0:
            return False
        return True
 
    # 주문 취소 Request 에 대한 응답 처리
    def OrderReply(self):
        self.bIsRq = False
 
        if self.objCancelOrder.GetDibStatus() != 0:
            print("[CpRPOrder/OrderReply]통신상태",
                  self.objCancelOrder.GetDibStatus(), self.objCancelOrder.GetDibMsg1())
            self.callback.ForwardReply(-1, 0)
            return False
 
        orderPrev = self.objCancelOrder.GetHeaderValue(1)
        code = self.objCancelOrder.GetHeaderValue(4)
        orderNum = self.objCancelOrder.GetHeaderValue(6)
        amount = self.objCancelOrder.GetHeaderValue(5)
 
        print("[CpRPOrder/OrderReply] 주문 취소 reply, 취소한 주문:",orderPrev, code, orderNum, amount)
 
# 주문 취소를 요청한 클래스로 포워딩 한다.
        if (self.callback != None) :
            self.callback.ForwardReply(0, orderPrev)
 
 
 
# 미체결 조회 서비스
class Cp5339:
    def __init__(self):
        self.objRq = win32com.client.Dispatch("CpTrade.CpTd5339")
        self.acc = g_objCpTrade.AccountNumber[0]  # 계좌번호
        self.accFlag = g_objCpTrade.GoodsList(self.acc, 1)  # 주식상품 구분
 
 
    def Request5339(self, dicOrderList, orderList):
        self.objRq.SetInputValue(0, self.acc)
        self.objRq.SetInputValue(1, self.accFlag[0])
        self.objRq.SetInputValue(4, "0") # 전체
        self.objRq.SetInputValue(5, "1") # 정렬 기준 - 역순
        self.objRq.SetInputValue(6, "0") # 전체
        self.objRq.SetInputValue(7, 20) # 요청 개수 - 최대 20개
 
        print("[Cp5339] 미체결 데이터 조회 시작")
        # 미체결 연속 조회를 위해 while 문 사용
        while True :
            ret = self.objRq.BlockRequest()
            if self.objRq.GetDibStatus() != 0:
                print("통신상태", self.objRq.GetDibStatus(), self.objRq.GetDibMsg1())
                return False
 
            if (ret == 2 or ret == 3):
                print("통신 오류", ret)
                return False;
 
            # 통신 초과 요청 방지에 의한 요류 인 경우
            while (ret == 4) : # 연속 주문 오류 임. 이 경우는 남은 시간동안 반드시 대기해야 함.
                remainTime = g_objCpStatus.LimitRequestRemainTime
                print("연속 통신 초과에 의해 재 통신처리 : ",remainTime/1000, "초 대기" )
                time.sleep(remainTime / 1000)
                ret = self.objRq.BlockRequest()
 
 
            # 수신 개수
            cnt = self.objRq.GetHeaderValue(5)
            print("[Cp5339] 수신 개수 ", cnt)
            if cnt == 0 :
                break
 
            for i in range(cnt):
                item = orderData()
                item.orderNum = self.objRq.GetDataValue(1, i)
                item.orderPrev  = self.objRq.GetDataValue(2, i)
                item.code  = self.objRq.GetDataValue(3, i)  # 종목코드
                item.name  = self.objRq.GetDataValue(4, i)  # 종목명
                item.orderDesc  = self.objRq.GetDataValue(5, i)  # 주문구분내용
                item.amount  = self.objRq.GetDataValue(6, i)  # 주문수량
                item.price  = self.objRq.GetDataValue(7, i)  # 주문단가
                item.ContAmount = self.objRq.GetDataValue(8, i)  # 체결수량
                item.credit  = self.objRq.GetDataValue(9, i)  # 신용구분
                item.modAvali  = self.objRq.GetDataValue(11, i)  # 정정취소 가능수량
                item.buysell  = self.objRq.GetDataValue(13, i)  # 매매구분코드
                item.creditdate  = self.objRq.GetDataValue(17, i)  # 대출일
                item.orderFlagDesc  = self.objRq.GetDataValue(19, i)  # 주문호가구분코드내용
                item.orderFlag  = self.objRq.GetDataValue(21, i)  # 주문호가구분코드
 
                # 사전과 배열에 미체결 item 을 추가
                dicOrderList[item.orderNum] = item
                orderList.append(item)
 
            # 연속 처리 체크 - 다음 데이터가 없으면 중지
            if self.objRq.Continue == False :
                print("[Cp5339] 연속 조회 여부: 다음 데이터가 없음")
                break
 
        return True
 
 
# 샘플 코드  메인 클래스
class testMain():
    def __init__(self):
        self.bTradeInit = False
        # 연결 여부 체크
        if (g_objCpStatus.IsConnect == 0):
            print("PLUS가 정상적으로 연결되지 않음. ")
            return False
        if (g_objCpTrade.TradeInit(0) != 0):
            print("주문 초기화 실패")
            return False
        self.bTradeInit = True
 
        # 미체결 리스트를 보관한 자료 구조체
        self.diOrderList= dict()  # 미체결 내역 딕셔너리 - key: 주문번호, value - 미체결 레코드
        self.orderList = []       # 미체결 내역 리스트 - 순차 조회 등을 위한 미체결 리스트
 
        # 미체결 통신 object
        self.obj = Cp5339()
        # 주문 취소 통신 object
        self.objOrder = CpRPOrder()
 
        # 실시간 주문 체결
        self.contsb = CpConclution()
        self.contsb.Subscribe("", self)
 
 
        return
 
    # 더 이상 미체결이 아닌 주문번호를 찾아 지운다.
    def deleteOrderNum(self, orderNum):
        print("미체결 주문 번호 삭제: ", orderNum)
        del self.diOrderList[orderNum]
        for i in range(len(self.orderList)):
            if (self.orderList[i].orderNum == orderNum):
                del self.orderList[i]
                break
 
    # 미체결 주문 조회
    def Reqeust5339(self):
        if self.bTradeInit == False :
            print("TradeInit 실패")
            return False
 
        self.diOrderList = {}
        self.orderList = []
        self.obj.Request5339(self.diOrderList, self.orderList)
 
        for item in self.orderList:
            item.debugPrint()
        print("[Reqeust5339]미체결 개수 ", len(self.orderList))
 
    # 첫번째 미체결을 취소 한다.
    # Request 함수 이용 - OnRecieved 이벤트를 통해 응답을 받는다.
    def RequestCancel(self):
        if len(self.orderList) > 0 :
            item = self.orderList[0]
            self.objOrder.RequestCancel(item.orderNum, item.code, item.amount, self)
 
    # 첫번째 미체결을 취소 한다. -  BlockReqest 이용
    def BlockRequestCancel(self):
        print(2)
        if len(self.orderList) > 0:
            item = self.orderList[0]
            self.objOrder.BlockRequestCancel(item.orderNum, item.code, item.amount, self)
 
 
    # 일괄 취소
    def RequestCancelAll(self):
        onums = []
        codes = []
        amounts = []
        for item in self.orderList :
            onums.append(item.orderNum)
            codes.append(item.code)
            amounts.append(item.amount)
 
        for i in range(len(onums)):
            self.objOrder.BlockRequestCancel(onums[i], codes[i], amounts[i],self)
 
    # 주문 응답 받음.
    def ForwardReply(self, ret, orderNum):
        print("[testMain/ForwardReply] reply ret %d, 주문번호 %d" %(ret, orderNum))
 
    # 주문 체결에 대한 실시간 업데이트
    def ForwardPB(self,name, orderNum):
        # 취소 확인을 받은 후 , 다음 취소 할 게 있음 취소 주문 전송
        if (name == "cancelpb") :
            print("[testMain/ForwardPB] 취소 확인 받음, 주문번호", orderNum)
 
 
class MyWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.main = testMain()
        self.setWindowTitle("PLUS API TEST")
 
        nHeight = 20
        btnNoContract = QPushButton("미체결 조회", self)
        btnNoContract.move(20, nHeight)
        btnNoContract.clicked.connect(self.btnNoContract_clicked)
 
        nHeight += 50
        btnCancel = QPushButton("취소 주문(Request)", self)
        btnCancel.move(20, nHeight)
        btnCancel.resize(200,30)
        btnCancel.clicked.connect(self.btnCancel_clicked)
 
        nHeight += 50
        btnCancel2 = QPushButton("취소 주문(BlockRequest)", self)
        btnCancel2.move(20, nHeight)
        btnCancel2.resize(200, 30)
        btnCancel2.clicked.connect(self.btnCancel2_clicked)
 
        nHeight += 50
        btnAllCancel = QPushButton("일괄 취소", self)
        btnAllCancel.move(20, nHeight)
        btnAllCancel.clicked.connect(self.btnAllCancel_clicked)
 
        nHeight += 50
        btnExit = QPushButton("종료", self)
        btnExit.move(20, nHeight)
        btnExit.clicked.connect(self.btnExit_clicked)
 
        nHeight += 50
        self.setGeometry(300, 500, 300, nHeight)
 
        # 시작 부터 미체결 미리 조회 한다.
        self.main.Reqeust5339()
 
    # 미체결 조회
    def btnNoContract_clicked(self):
        self.main.Reqeust5339()
        return
 
    # 취소 주문- 주문 리스트에 최근 거 부터
    def btnCancel_clicked(self):
        self.main.RequestCancel()
        return
 
    # 취소 주문- 주문 리스트에 최근 거 부터
    def btnCancel2_clicked(self):
        self.main.BlockRequestCancel()
        return
 
    # 일괄 취소 - 미체결 전체 취소
    def btnAllCancel_clicked(self):
        self.main.RequestCancelAll()
        return
 
 
    def btnExit_clicked(self):
        exit()
        return
 
 
 
if __name__ == "__main__":
    app = QApplication(sys.argv)
    myWindow = MyWindow()
    myWindow.show()
    app.exec_()

14 주식 현재가 10차 호가 화면 구현하기[편집]

import sys
from PyQt5 import QtWidgets
from PyQt5 import uic
from PyQt5.QtCore import pyqtSlot
import win32com.client
 
# cp object
g_objCodeMgr = win32com.client.Dispatch("CpUtil.CpCodeMgr")
g_objCpStatus = win32com.client.Dispatch("CpUtil.CpCybos")
g_objCpTrade = win32com.client.Dispatch("CpTrade.CpTdUtil")
 
 
# 현재가 정보 저장 구조체
class stockPricedData:
    def __init__(self):
        self.dicEx = {ord('0'): "동시호가/장중 아님", ord('1'): "동시호가", ord('2'): "장중"}
        self.code = ""
        self.name = ""
        self.cur = 0        # 현재가
        self.diff = 0       # 대비
        self.diffp = 0      # 대비율
        self.offer = [0 for _ in range(10)]     # 매도호가
        self.bid = [0 for _ in range(10)]       # 매수호가
        self.offervol = [0 for _ in range(10)]     # 매도호가 잔량
        self.bidvol = [0 for _ in range(10)]       # 매수호가 잔량
        self.totOffer = 0       # 총매도잔량
        self.totBid = 0         # 총매수 잔량
        self.vol = 0            # 거래량
        self.baseprice = 0      # 기준가
 
        # 예상체결가 정보
        self.exFlag= ord('2')
        self.expcur = 0         # 예상체결가
        self.expdiff = 0        # 예상 대비
        self.expdiffp = 0       # 예상 대비율
        self.expvol = 0         # 예상 거래량
        self.objCur = CpPBStockCur()
        self.objOfferbid = CpPBStockBid()
 
    def __del__(self):
        self.objCur.Unsubscribe()
        self.objOfferbid.Unsubscribe()
 
 
    # 전일 대비 계산
    def makediffp(self):
        lastday = 0
        if (self.exFlag == ord('1')):  # 동시호가 시간 (예상체결)
            if self.baseprice > 0  :
                lastday = self.baseprice
            else:
                lastday = self.expcur - self.expdiff
            if lastday:
                self.expdiffp = (self.expdiff / lastday) * 100
            else:
                self.expdiffp = 0
        else:
            if self.baseprice > 0  :
                lastday = self.baseprice
            else:
                lastday = self.cur - self.diff
            if lastday:
                self.diffp = (self.diff / lastday) * 100
            else:
                self.diffp = 0
 
    def getCurColor(self):
        diff = self.diff
        if (self.exFlag == ord('1')):  # 동시호가 시간 (예상체결)
            diff = self.expdiff
        if (diff > 0):
            return 'color: red'
        elif (diff == 0):
            return  'color: black'
        elif (diff < 0):
            return 'color: blue'
 
 
# CpEvent: 실시간 이벤트 수신 클래스
class CpEvent:
    def set_params(self, client, name, rpMst, parent):
        self.client = client  # CP 실시간 통신 object
        self.name = name  # 서비스가 다른 이벤트를 구분하기 위한 이름
        self.parent = parent  # callback 을 위해 보관
        self.rpMst = rpMst
 
 
 
    # PLUS 로 부터 실제로 시세를 수신 받는 이벤트 핸들러
    def OnReceived(self):
        if self.name == "stockcur":
            # 현재가 체결 데이터 실시간 업데이트
            self.rpMst.exFlag = self.client.GetHeaderValue(19)  # 예상체결 플래그
            code = self.client.GetHeaderValue(0)
            diff = self.client.GetHeaderValue(2)
            cur= self.client.GetHeaderValue(13)  # 현재가
            vol = self.client.GetHeaderValue(9)  # 거래량
 
            # 예제는 장중만 처리 함.
            if (self.rpMst.exFlag == ord('1')):  # 동시호가 시간 (예상체결)
                # 예상체결가 정보
                self.rpMst.expcur = cur
                self.rpMst.expdiff = diff
                self.rpMst.expvol = vol
            else:
                self.rpMst.cur = cur
                self.rpMst.diff = diff
                self.rpMst.makediffp()
                self.vol = vol
 
            self.rpMst.makediffp()
            # 현재가 업데이트
            self.parent.monitorPriceChange()
 
            return
 
        elif self.name == "stockbid":
            # 현재가 10차 호가 데이터 실시간 업데이c
            code = self.client.GetHeaderValue(0)
            dataindex = [3, 7, 11, 15, 19, 27, 31, 35, 39, 43]
            obi = 0
            for i in range(10):
                self.rpMst.offer[i] = self.client.GetHeaderValue(dataindex[i])
                self.rpMst.bid[i] = self.client.GetHeaderValue(dataindex[i] + 1)
                self.rpMst.offervol[i] = self.client.GetHeaderValue(dataindex[i] + 2)
                self.rpMst.bidvol[i] = self.client.GetHeaderValue(dataindex[i] + 3)
 
            self.rpMst.totOffer = self.client.GetHeaderValue(23)
            self.rpMst.totBid = self.client.GetHeaderValue(24)
            # 10차 호가 변경 call back 함수 호출
            self.parent.monitorOfferbidChange()
            return
 
# SB/PB 요청 ROOT 클래스
class CpPublish:
    def __init__(self, name, serviceID):
        self.name = name
        self.obj = win32com.client.Dispatch(serviceID)
        self.bIsSB = False
 
    def Subscribe(self, var, rpMst, parent):
        if self.bIsSB:
            self.Unsubscribe()
 
        if (len(var) > 0):
            self.obj.SetInputValue(0, var)
 
        handler = win32com.client.WithEvents(self.obj, CpEvent)
        handler.set_params(self.obj, self.name, rpMst, parent)
        self.obj.Subscribe()
        self.bIsSB = True
 
    def Unsubscribe(self):
        if self.bIsSB:
            self.obj.Unsubscribe()
        self.bIsSB = False
 
# CpPBStockCur: 실시간 현재가 요청 클래스
class CpPBStockCur(CpPublish):
    def __init__(self):
        super().__init__("stockcur", "DsCbo1.StockCur")
 
# CpPBStockBid: 실시간 10차 호가 요청 클래스
class CpPBStockBid(CpPublish):
    def __init__(self):
        super().__init__("stockbid", "Dscbo1.StockJpBid")
 
 
# SB/PB 요청 ROOT 클래스
class CpPBConnection:
    def __init__(self):
        self.obj = win32com.client.Dispatch("CpUtil.CpCybos")
        handler = win32com.client.WithEvents(self.obj, CpEvent)
        handler.set_params(self.obj, "connection", None)
 
 
# CpRPCurrentPrice:  현재가 기본 정보 조회 클래스
class CpRPCurrentPrice:
    def __init__(self):
        if (g_objCpStatus.IsConnect == 0):
            print("PLUS가 정상적으로 연결되지 않음. ")
            return
        self.objStockMst = win32com.client.Dispatch("DsCbo1.StockMst")
        return
 
 
    def Request(self, code, rtMst, callbackobj):
        # 현재가 통신
        rtMst.objCur.Unsubscribe()
        rtMst.objOfferbid.Unsubscribe()
 
        self.objStockMst.SetInputValue(0, code)
        ret = self.objStockMst.BlockRequest()
        if self.objStockMst.GetDibStatus() != 0:
            print("통신상태", self.objStockMst.GetDibStatus(), self.objStockMst.GetDibMsg1())
            return False
 
 
        # 수신 받은 현재가 정보를 rtMst 에 저장
        rtMst.code = code
        rtMst.name = g_objCodeMgr.CodeToName(code)
        rtMst.cur =  self.objStockMst.GetHeaderValue(11)  # 종가
        rtMst.diff =  self.objStockMst.GetHeaderValue(12)  # 전일대비
        rtMst.baseprice  =  self.objStockMst.GetHeaderValue(27)  # 기준가
        rtMst.vol = self.objStockMst.GetHeaderValue(18)  # 거래량
        rtMst.exFlag = self.objStockMst.GetHeaderValue(58)  # 예상플래그
        rtMst.expcur = self.objStockMst.GetHeaderValue(55)  # 예상체결가
        rtMst.expdiff = self.objStockMst.GetHeaderValue(56)  # 예상체결대비
        rtMst.makediffp()
 
        rtMst.totOffer = self.objStockMst.GetHeaderValue(71)  # 총매도잔량
        rtMst.totBid = self.objStockMst.GetHeaderValue(73)  # 총매수잔량
 
 
        # 10차호가
        for i in range(10):
            rtMst.offer[i] = (self.objStockMst.GetDataValue(0, i))  # 매도호가
            rtMst.bid[i] = (self.objStockMst.GetDataValue(1, i) ) # 매수호가
            rtMst.offervol[i] = (self.objStockMst.GetDataValue(2, i))  # 매도호가 잔량
            rtMst.bidvol[i] = (self.objStockMst.GetDataValue(3, i) ) # 매수호가 잔량
 
 
        rtMst.objCur.Subscribe(code,rtMst, callbackobj)
        rtMst.objOfferbid.Subscribe(code,rtMst, callbackobj)
 
 
 
 
class Form(QtWidgets.QDialog):
    def __init__(self, parent=None):
        QtWidgets.QDialog.__init__(self, parent)
        self.ui = uic.loadUi("hoga.ui", self)
        self.ui.show()
        self.objMst = CpRPCurrentPrice()
        self.item = stockPricedData()
 
        self.setCode("000660")
 
    @pyqtSlot()
    def slot_codeupdate(self):
        code = self.ui.editCode.toPlainText()
        self.setCode(code)
 
    def slot_codechanged(self):
        code = self.ui.editCode.toPlainText()
        self.setCode(code)
 
 
    def monitorPriceChange(self):
        self.displyHoga()
 
    def monitorOfferbidChange(self):
        self.displyHoga()
 
    def setCode(self, code):
        if len(code) < 6 :
            return
 
        print(code)
        if not (code[0] == "A"):
            code = "A" + code
 
        name = g_objCodeMgr.CodeToName(code)
        if len(name) == 0:
            print("종목코드 확인")
            return
 
        self.ui.label_name.setText(name)
 
        if (self.objMst.Request(code, self.item, self) == False):
            return
        self.displyHoga()
 
    def displyHoga(self):
        self.ui.label_offer10.setText(format(self.item.offer[9],','))
        self.ui.label_offer9.setText(format(self.item.offer[8],','))
        self.ui.label_offer8.setText(format(self.item.offer[7],','))
        self.ui.label_offer7.setText(format(self.item.offer[6],','))
        self.ui.label_offer6.setText(format(self.item.offer[5],','))
        self.ui.label_offer5.setText(format(self.item.offer[4],','))
        self.ui.label_offer4.setText(format(self.item.offer[3],','))
        self.ui.label_offer3.setText(format(self.item.offer[2],','))
        self.ui.label_offer2.setText(format(self.item.offer[1],','))
        self.ui.label_offer1.setText(format(self.item.offer[0],','))
 
        self.ui.label_offer_v10.setText(format(self.item.offervol[9],','))
        self.ui.label_offer_v9.setText(format(self.item.offervol[8],','))
        self.ui.label_offer_v8.setText(format(self.item.offervol[7],','))
        self.ui.label_offer_v7.setText(format(self.item.offervol[6],','))
        self.ui.label_offer_v6.setText(format(self.item.offervol[5],','))
        self.ui.label_offer_v5.setText(format(self.item.offervol[4],','))
        self.ui.label_offer_v4.setText(format(self.item.offervol[3],','))
        self.ui.label_offer_v3.setText(format(self.item.offervol[2],','))
        self.ui.label_offer_v2.setText(format(self.item.offervol[1],','))
        self.ui.label_offer_v1.setText(format(self.item.offervol[0],','))
 
        self.ui.label_bid10.setText(format(self.item.bid[9],','))
        self.ui.label_bid9.setText(format(self.item.bid[8],','))
        self.ui.label_bid8.setText(format(self.item.bid[7],','))
        self.ui.label_bid7.setText(format(self.item.bid[6],','))
        self.ui.label_bid6.setText(format(self.item.bid[5],','))
        self.ui.label_bid5.setText(format(self.item.bid[4],','))
        self.ui.label_bid4.setText(format(self.item.bid[3],','))
        self.ui.label_bid3.setText(format(self.item.bid[2],','))
        self.ui.label_bid2.setText(format(self.item.bid[1],','))
        self.ui.label_bid1.setText(format(self.item.bid[0],','))
 
        self.ui.label_bid_v10.setText(format(self.item.bidvol[9],','))
        self.ui.label_bid_v9.setText(format(self.item.bidvol[8],','))
        self.ui.label_bid_v8.setText(format(self.item.bidvol[7],','))
        self.ui.label_bid_v7.setText(format(self.item.bidvol[6],','))
        self.ui.label_bid_v6.setText(format(self.item.bidvol[5],','))
        self.ui.label_bid_v5.setText(format(self.item.bidvol[4],','))
        self.ui.label_bid_v4.setText(format(self.item.bidvol[3],','))
        self.ui.label_bid_v3.setText(format(self.item.bidvol[2],','))
        self.ui.label_bid_v2.setText(format(self.item.bidvol[1],','))
        self.ui.label_bid_v1.setText(format(self.item.bidvol[0],','))
 
        cur = self.item.cur
        diff = self.item.diff
        diffp = self.item.diffp
        if (self.item.exFlag == ord('1')):  # 동시호가 시간 (예상체결)
            cur = self.item.expcur
            diff = self.item.expdiff
            diffp = self.item.expdiffp
 
 
        strcur = format(cur, ',')
        if (self.item.exFlag == ord('1')):  # 동시호가 시간 (예상체결)
            strcur = "*" + strcur
 
        curcolor = self.item.getCurColor()
        self.ui.label_cur.setStyleSheet(curcolor)
        self.ui.label_cur.setText(strcur)
        strdiff = str(diff) + "  " + format(diffp, '.2f')
        strdiff += "%"
        self.ui.label_diff.setText(strdiff)
        self.ui.label_diff.setStyleSheet(curcolor)
 
        self.ui.label_totoffer.setText(format(self.item.totOffer,','))
        self.ui.label_totbid.setText(format(self.item.totBid,','))
 
 
 
if __name__ == '__main__':
        app = QtWidgets.QApplication(sys.argv)
        w = Form()
        sys.exit(app.exec())

15 주식 예약 매수/매도/취소/조회 예제[편집]

 - 예약매수 전송 - 예약 매수(오늘 종가 기준)
 - 예약매도 전송 - 예약 매수(오늘 종가 기준)
 - 예약주문 취소 - 미체결 예약주문 리스트에서 첫번째 예약 주문 취소
 - 예약내역 가져오기 - 예약주문 리스트 가져와 미체결 건만 리스트에 저장(취소 주문 할 수 있도록)
# 예약매수/매도/예약주문 내역 조회 예제
# 화면 설명
#   예약매수 전송 - 예약 매수(오늘 종가 기준)
#   예약매도 전송 - 예약 매수(오늘 종가 기준)
#   예약주문 취소 - 미체결 예약주문 리스트에서 첫번째 예약 주문 취소
#   예약내역 가져오기 - 예약주문 리스트 가져와 미체결 건만 리스트에 저장(취소 주문 할 수 있도록)

import sys
from PyQt5.QtWidgets import *
from enum import Enum
import win32com.client
import time
import pythoncom
 
 
g_objCodeMgr = win32com.client.Dispatch("CpUtil.CpCodeMgr")
g_objCpStatus = win32com.client.Dispatch("CpUtil.CpCybos")
g_objCpTrade = win32com.client.Dispatch("CpTrade.CpTdUtil")
 
# enum 주문 상태 세팅용
class EorderBS(Enum):
    buy = 1          # 매수
    sell= 2          # 매도
    none =3
 
# 현재가 정보 저장 구조체
class stockPricedData:
    def __init__(self):
        self.dicEx = {ord('0'): "동시호가/장중 아님", ord('1'): "동시호가", ord('2'): "장중"}
        self.code = ""
        self.name = ""
        self.cur = 0        # 현재가
        self.open = self.high = self.low = 0 # 시/고/저
        self.diff = 0
        self.diffp = 0
        self.objCur = None
        self.objBid = None
        self.vol = 0 # 거래량
        self.offer = [0 for _ in range(10)]     # 매도호가
        self.bid = [0 for _ in range(10)]       # 매수호가
        self.offervol = [0 for _ in range(10)]     # 매도호가 잔량
        self.bidvol = [0 for _ in range(10)]       # 매수호가 잔량
 
    # 전일 대비 계산
    def makediffp(self, baseprice):
        lastday = 0
        if baseprice :
            lastday =baseprice
        else:
            lastday = self.cur - self.diff
        if lastday:
            self.diffp = (self.diff / lastday) * 100
        else:
            self.diffp = 0
 
    def debugPrint(self, type):
        if type == 0  :
            print("%s, %s %s, 현재가 %d 대비 %d, (%.2f), 1차매도 %d(%d) 1차매수 %d(%d)"
                  %(self.dicEx.get(self.exFlag), self.code,
                    self.name, self.cur, self.diff, self.diffp,
                    self.offer[0], self.offervol[0],self.bid[0], self.bidvol[0] ) )
        else :
            print("%s %s, 현재가 %.2f 대비 %.2f, (%.2f), 1차매도 %.2f(%d) 1차매수 %.2f(%d)"
                  % (self.code,
                     self.name, self.cur, self.diff, self.diffp,
                     self.offer[0], self.offervol[0], self.bid[0], self.bidvol[0]))
 
# 주문 데이터
class orderData:
    def __init__(self):
        self.dicEx = {EorderBS.buy: "매수", EorderBS.sell: "매도", EorderBS.none: "없음"}
        self.orderNum = 0
        self.bs = EorderBS.none  # 0 : buy 1: sell
        self.code = ""
        self.amount = 0
        self.price = 0
 
    def debugPrint(self):
        print(self.dicEx.get(self.bs), self.code, self.orderNum, self.amount, self.price)
 
 
# CpRPCurrentPrice:  현재가 기본 정보 조회 클래스
class CpRPCurrentPrice:
    def __init__(self):
        if (g_objCpStatus.IsConnect == 0):
            print("PLUS가 정상적으로 연결되지 않음. ")
            return
        self.objStockMst = win32com.client.Dispatch("DsCbo1.StockMst")
        return
 
    def Request(self, code, rtMst):
        # 현재가 통신
        rqtime = time.time()
 
        self.objStockMst.SetInputValue(0, code)
        ret = self.objStockMst.BlockRequest()
        if self.objStockMst.GetDibStatus() != 0:
            print("통신상태", self.objStockMst.GetDibStatus(), self.objStockMst.GetDibMsg1())
            return False
 
 
        # 수신 받은 현재가 정보를 rtMst 에 저장
        rtMst.code = code
        rtMst.name = g_objCodeMgr.CodeToName(code)
        rtMst.cur =  self.objStockMst.GetHeaderValue(11)  # 종가
        rtMst.diff =  self.objStockMst.GetHeaderValue(12)  # 전일대비
        rtMst.baseprice  =  self.objStockMst.GetHeaderValue(27)  # 기준가
        rtMst.exFlag = self.objStockMst.GetHeaderValue(58)  # 예상플래그
        if rtMst.baseprice :
            rtMst.diffp = (rtMst.diff / rtMst.baseprice) * 100
 
 
        # 10차호가
        for i in range(10):
            rtMst.offer[i] = (self.objStockMst.GetDataValue(0, i))  # 매도호가
            rtMst.bid[i] = (self.objStockMst.GetDataValue(1, i) ) # 매수호가
            rtMst.offervol[i] = (self.objStockMst.GetDataValue(2, i))  # 매도호가 잔량
            rtMst.bidvol[i] = (self.objStockMst.GetDataValue(3, i) ) # 매수호가 잔량
        return True
 
# CpRPPreOrder - 예약 주문 매수/매도/취소/내역 조회 클래스
class CpRPPreOrder:
    def __init__(self):
        if (g_objCpStatus.IsConnect == 0):
            print("PLUS가 정상적으로 연결되지 않음. ")
            return
        if (g_objCpTrade.TradeInit(0) != 0):
            print("주문 초기화 실패")
            exit
        self.objOrder = win32com.client.Dispatch("CpTrade.CpTdNew9061")
        self.objCancel = win32com.client.Dispatch("CpTrade.CpTdNew9064")
        self.objResult = win32com.client.Dispatch("CpTrade.CpTd9065")
 
        self.acc = g_objCpTrade.AccountNumber[0]  # 계좌번호
        self.accFlag = g_objCpTrade.GoodsList(self.acc, 1)  # 주식상품 구분
 
        return
 
    # 예약 매수 또는 매도
    def RequestOrder(self, bs, code, price, amount, data):
        data.code = code
        data.amount = amount
        data.price = price;
        rqBS = "2"
        if bs == EorderBS.buy:  # 매수
            rqBS = "2"
            data.bs = EorderBS.buy
        elif bs == EorderBS.sell: # 매도
            data.bs = EorderBS.sell
            rqBS = "1"
 
        # 예약 주문
        print(self.acc, self.accFlag[0])
        self.objOrder.SetInputValue(0, self.acc)
        self.objOrder.SetInputValue(1, self.accFlag[0])
        self.objOrder.SetInputValue(2, rqBS)
        self.objOrder.SetInputValue(3, code)
        self.objOrder.SetInputValue(4, amount)
        self.objOrder.SetInputValue(5, "01") # 주문호가 구분 01: 보통
        self.objOrder.SetInputValue(6, price)
 
        # 예약 주문 요청
        self.objOrder.BlockRequest()
 
        if self.objOrder.GetDibStatus() != 0:
            print("통신상태", self.objOrder.GetDibStatus(), self.objOrder.GetDibMsg1())
            return False
 
        data.orderNum = self.objOrder.GetHeaderValue(0)  # 예약번호
        print("예약주문 성공, 예약번호 #", data.orderNum)
 
 
    # 예약 취소 주문
    def RequestCancel(self, ordernum, code):
        # 예약주문 취소
        print(self.acc, self.accFlag[0])
        self.objCancel.SetInputValue(0, ordernum)
        self.objCancel.SetInputValue(1, self.acc)
        self.objCancel.SetInputValue(2, self.accFlag[0])
        self.objCancel.SetInputValue(3, code)
 
        # 예약 취소  주문 요청
        self.objCancel.BlockRequest()
 
        if self.objCancel.GetDibStatus() != 0:
            print("통신상태", self.objCancel.GetDibStatus(), self.objCancel.GetDibMsg1())
            return False
 
        print("예약주문 취소 ", ordernum, self.objCancel.GetDibMsg1() )
 
    # 예약 주문 내역 조회 및 미체결 리스트 구하기
    def RequestOrderList(self, orderList):
        print(self.acc, self.accFlag[0])
        self.objResult.SetInputValue(0, self.acc)
        self.objResult.SetInputValue(1, self.accFlag[0])
        self.objResult.SetInputValue(2, 20)
 
        while True: # 연속 조회로 전체 예약 주문 가져온다.
            self.objResult.BlockRequest()
            if self.objResult.GetDibStatus() != 0:
                print("통신상태", self.objResult.GetDibStatus(), self.objResult.GetDibMsg1())
                return False
 
            cnt = self.objResult.GetHeaderValue(4)
            if cnt == 0 :
                break
 
            for i in range(cnt):
                i1 = self.objResult.GetDataValue(1, i)  # 주문구분(매수 또는 매도)
                i2 =  self.objResult.GetDataValue(2, i)  # 코드
                i3 = self.objResult.GetDataValue(3, i)  # 주문 수량
                i4 = self.objResult.GetDataValue(4, i)  # 주문호가구분
                i5 = self.objResult.GetDataValue(6, i)  # 예약번호
                i6 = self.objResult.GetDataValue(12, i)  # 처리구분내용 - 주문취소 또는 주문예정
                i7 = self.objResult.GetDataValue(9, i)  # 주문단가
                i8= self.objResult.GetDataValue(11, i)  # 주문번호
                i9 = self.objResult.GetDataValue(12, i)  # 처리구분코드
                i10= self.objResult.GetDataValue(13, i)  # 거부코드
                i11 = self.objResult.GetDataValue(14, i)  # 거부내용
                print(i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11)
 
                # 미체결
                if (i6 == "주문예정") :
                    item = orderData()
                    item.orderNum = i5
                    if (i1 == "매수") :
                        item.bs = EorderBS.buy
                    else:
                        item.bs = EorderBS.sell
                    item.code = i2
                    item.amount = i3
                    item.price = i7
 
                    orderList.append(item)
 
            # 연속 처리 체크 - 다음 데이터가 없으면 중지
            if self.objResult.Continue == False :
                break
 
# 샘플 코드  메인 클래스
class testMain():
    def __init__(self):
        self.orderList = []
        self.objMst = CpRPCurrentPrice()
        self.obj9061 = CpRPPreOrder()
 
        # 미체결 된 예약 주문 처음부터 받아옴.
        self.resultOrder()
        return
 
    def newBuyOrder(self, code, amount):
        mstData = stockPricedData()
        if self.objMst.Request(code, mstData) == False :
            print("현재가 요청 실패")
            return
 
        item = orderData()
        ret = self.obj9061.RequestOrder(EorderBS.buy, code, mstData.cur, amount, item )
        if (ret == False):
            return False
        self.orderList.append(item)
        item.debugPrint()
 
    def newSellOrder(self, code, amount):
        mstData = stockPricedData()
        if self.objMst.Request(code, mstData) == False :
            print("현재가 요청 실패")
            return
 
        item = orderData()
        ret = self.obj9061.RequestOrder(EorderBS.sell, code, mstData.cur, amount, item )
        if (ret == False):
            return False
        self.orderList.append(item)
        item.debugPrint()
 
    def cancelOrder(self):
        if len(self.orderList) == 0:
            print("취소 할 주문 확인하세요")
            return
        item = self.orderList[0]
        ret = self.obj9061.RequestCancel(item.orderNum, item.code)
        if (ret == False):
            return False
 
        del (self.orderList[0])
 
    def resultOrder(self):
        self.orderList = []
        # 미체결 주문만 받아 보자
        ret = self.obj9061.RequestOrderList(self.orderList)
        if (ret == False):
            return False
 
 
 
 
class MyWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.main = testMain()
        self.setWindowTitle("PLUS API TEST")
        w = 200
        h = 30
 
        nHeight = 20
        btnBuy = QPushButton("예약매수 전송", self)
        btnBuy.move(20, nHeight)
        btnBuy.resize(w, h)
        btnBuy.clicked.connect(self.btnBuy_clicked)
 
        nHeight += 50
        btnSell = QPushButton("예약 매도 전송", self)
        btnSell.move(20, nHeight)
        btnSell.resize(w, h)
        btnSell.clicked.connect(self.btnSell_clicked)
 
        nHeight += 50
        btnCancel = QPushButton("예약 주문 취소", self)
        btnCancel.move(20, nHeight)
        btnCancel.resize(w, h)
        btnCancel.clicked.connect(self.btnCancel_clicked)
 
        nHeight += 50
        btnResult= QPushButton("예약 내역 가져오기", self)
        btnResult.move(20, nHeight)
        btnResult.resize(w, h)
        btnResult.clicked.connect(self.btnResult_clicked)
 
        nHeight += 50
        btnExit = QPushButton("종료", self)
        btnExit.move(20, nHeight)
        btnExit.resize(w, h)
        btnExit.clicked.connect(self.btnExit_clicked)
 
        nHeight += 50
        self.setGeometry(300, 500, 300, nHeight)
 
    def btnBuy_clicked(self):
        self.main.newBuyOrder("A003540", 10)
        return
 
    def btnSell_clicked(self):
        self.main.newSellOrder("A003540", 10)
        return
 
    def btnCancel_clicked(self):
        self.main.cancelOrder()
        return
 
 
    def btnResult_clicked(self):
        self.main.resultOrder()
        return
 
 
    def btnExit_clicked(self):
        exit()
        return
 
 
 
if __name__ == "__main__":
    app = QApplication(sys.argv)
    myWindow = MyWindow()
    myWindow.show()
    app.exec_()

16 주식/ELW/선물/옵션/업종 전 종목 시세 조회 예제[편집]

import sys
from PyQt5.QtWidgets import *
from enum import Enum
import win32com.client
import time
import pythoncom
 
 
g_objCodeMgr = win32com.client.Dispatch("CpUtil.CpCodeMgr")
g_objCpStatus = win32com.client.Dispatch("CpUtil.CpCybos")
g_objElwMgr = win32com.client.Dispatch("CpUtil.CpElwCode")
g_objFutureMgr = win32com.client.Dispatch("CpUtil.CpFutureCode")
g_objOptionMgr = win32com.client.Dispatch("CpUtil.CpOptionCode")
 
 
=== 감시 중인 현재가 정보 저장 구조체===
class stockPricedData:
    def __init__(self):
        self.dicEx = {ord('0'): "동시호가/장중 아님", ord('1'): "동시호가", ord('2'): "장중"}
        self.code = ""
        self.name = ""
        ===elf.cur = 0        # 현재가===
        ===elf.open = self.high = self.low = 0 # 시/고/저===
        self.diff = 0
        self.diffp = 0
        self.objCur = None
        self.objBid = None
        #self.Zoffer = 0
        #self.ZodfferDate = 0
        ===elf.vol = 0 # 거래량===
        ===elf.offer = [0 for _ in range(10)]     # 매도호가===
        ===elf.bid = [0 for _ in range(10)]       # 매수호가===
        ===elf.offervol = [0 for _ in range(10)]     # 매도호가 잔량===
        ===elf.bidvol = [0 for _ in range(10)]       # 매수호가 잔량===
 
    === 전일 대비 계산===
    def makediffp(self, baseprice):
        lastday = 0
        if baseprice :
            lastday =baseprice
        else:
            lastday = self.cur - self.diff
        if lastday:
            self.diffp = (self.diff / lastday) * 100
        else:
            self.diffp = 0
 
    def debugPrint(self, type):
        if type == 0  :
            print("%s, %s %s, 현재가 %d 대비 %d, (%.2f), 1차매도 %d(%d) 1차매수 %d(%d)"
                  %(self.dicEx.get(self.exFlag), self.code,
                    self.name, self.cur, self.diff, self.diffp,
                    self.offer[0], self.offervol[0],self.bid[0], self.bidvol[0] ) )
        else :
            print("%s %s, 현재가 %.2f 대비 %.2f, (%.2f), 1차매도 %.2f(%d) 1차매수 %.2f(%d)"
                  % (self.code,
                     self.name, self.cur, self.diff, self.diffp,
                     self.offer[0], self.offervol[0], self.bid[0], self.bidvol[0]))
 
 
class CpMarketEye:
    def __init__(self):
        if (g_objCpStatus.IsConnect == 0):
            print("PLUS가 정상적으로 연결되지 않음. ")
            return False
        self.objRq = win32com.client.Dispatch("CpSysDib.MarketEye")
        self.RpFiledIndex = 0
 
    def Request(self, codes, dicCodes):
        #rqField = [코드, 대비부호, 대비, 현재가, 시가, 고가, 저가, 매도호가, 매수호가, 거래량, 장구분, 매도잔량,매수잔량,
        === 공매도수량, 공매도날짜]===
===        rqField = [0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 15, 16, 127, 128]  # 요청 필드===
        ===qField = [0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 15, 16]  # 요청 필드===
 
        ===elf.objRq.SetInputValue(0, rqField)  # 요청 필드===
        ===elf.objRq.SetInputValue(1, codes)  # 종목코드 or 종목코드 리스트===
        self.objRq.BlockRequest()
 
        === 현재가 통신 및 통신 에러 처리===
        rqStatus = self.objRq.GetDibStatus()
        print("통신상태", rqStatus, self.objRq.GetDibMsg1())
        if rqStatus != 0:
            return False
 
        cnt = self.objRq.GetHeaderValue(2)
 
        for i in range(cnt):
            ===ode = self.objRq.GetDataValue(0, i)  # 코드===
            record = None
            if code in dicCodes:
                record = dicCodes.get(code)
            else:
                record = stockPricedData()
 
            record.code = code
            record.name = g_objCodeMgr.CodeToName(code)
 
            ===ecord.diff = self.objRq.GetDataValue(2, i)  # 전일대비===
            ===ecord.cur = self.objRq.GetDataValue(3, i)  # 현재가===
            ===ecord.open = self.objRq.GetDataValue(4, i)  # 시가===
            ===ecord.high = self.objRq.GetDataValue(5, i)  # 고가===
            ===ecord.low = self.objRq.GetDataValue(6, i)  # 저가===
            ===ecord.offer[0] =  self.objRq.GetDataValue(7, i)  # 매도호가===
            ===ecord.bid[0] = self.objRq.GetDataValue(8, i)  # 매수호가===
            ===ecord.vol = self.objRq.GetDataValue(9, i)  # 거래량===
            ===ecord.exFlag = self.objRq.GetDataValue(10, i)  # 장구분===
            ===ecord.offervol[0] = self.objRq.GetDataValue(11, i)  # 매도잔량===
            ===ecord.bidvol[0] = self.objRq.GetDataValue(12, i)  # 매수잔량===
            ===record.Zoffer = self.objRq.GetDataValue(13, i)  # 공매도수량===
            ===record.ZofferDate = self.objRq.GetDataValue(14, i)  # 공매도날짜===
 
            record.makediffp(0)
            dicCodes[code] = record
 
        return True
 
 
 
 
=== 샘플 코드  메인 클래스===
class testMain():
    def __init__(self):
        ===elf.dicStockCodes = dict()  # 주식 전 종목 시세 ===
        ===elf.dicElwCodes = dict()  # ELW 전종목 시세 ===
        ===elf.dicFutreCodes = dict()  # 선물 전종목 시세 ===
        ===elf.dicOptionCodes = dict()  # 옵션 전종목 시세 ===
        ===elf.dicUpjongCodes = dict()  # 업종 전종목 시세 ===
        self.obj = CpMarketEye()
        return
 
    def ReqeustStockMst(self):
        ===odeList = g_objCodeMgr.GetStockListByMarket(1)  # 거래소===
        ===odeList2 = g_objCodeMgr.GetStockListByMarket(2)  # 코스닥===
 
        allcodelist = codeList + codeList2
        print("전체 종목 코드 #", len(allcodelist))
 
 
        rqCodeList = []
        for i, code in enumerate(allcodelist):
            rqCodeList.append(code)
            if len(rqCodeList) == 200:
                self.obj.Request(rqCodeList, self.dicStockCodes)
                rqCodeList = []
                continue
 
        if len(rqCodeList) > 0 :
            self.obj.Request(rqCodeList, self.dicStockCodes)
 
        print("거래소 + 코스닥 전 종목 ", len(self.dicStockCodes))
        for key in self.dicStockCodes :
            self.dicStockCodes[key].debugPrint(0)
 
 
    def ReqeustElwMst(self):
 
        allcodelist = []
        for i in range(g_objElwMgr.GetCount()) :
            allcodelist.append(g_objElwMgr.GetData(0,i))
 
        print("전체 종목 코드 #", len(allcodelist))
 
 
        rqCodeList = []
        for i, code in enumerate(allcodelist):
            rqCodeList.append(code)
            if len(rqCodeList) == 200:
                self.obj.Request(rqCodeList, self.dicElwCodes)
                rqCodeList = []
                continue
 
        if len(rqCodeList) > 0 :
            self.obj.Request(rqCodeList, self.dicElwCodes)
 
        print("ELW 전종목", len(self.dicElwCodes))
        for key in self.dicElwCodes :
            self.dicElwCodes[key].debugPrint(0)
 
    def ReqeustFutreMst(self):
        allcodelist = []
        for i in range(g_objFutureMgr.GetCount()) :
            allcodelist.append(g_objFutureMgr.GetData(0,i))
 
        print("전체 종목 코드 #", len(allcodelist))
 
 
        rqCodeList = []
        for i, code in enumerate(allcodelist):
            rqCodeList.append(code)
            if len(rqCodeList) == 200:
                self.obj.Request(rqCodeList, self.dicFutreCodes)
                rqCodeList = []
                continue
 
        if len(rqCodeList) > 0 :
            self.obj.Request(rqCodeList, self.dicFutreCodes)
 
        print("선물 전종목 ", len(self.dicFutreCodes))
        for key in self.dicFutreCodes :
            self.dicFutreCodes[key].debugPrint(1)
 
    def ReqeustOptionMst(self):
        allcodelist = []
        for i in range(g_objOptionMgr.GetCount()) :
            allcodelist.append(g_objOptionMgr.GetData(0,i))
 
        print("전체 종목 코드 #", len(allcodelist))
 
 
        rqCodeList = []
        for i, code in enumerate(allcodelist):
            rqCodeList.append(code)
            if len(rqCodeList) == 200:
                self.obj.Request(rqCodeList, self.dicOptionCodes)
                rqCodeList = []
                continue
 
        if len(rqCodeList) > 0 :
            self.obj.Request(rqCodeList, self.dicOptionCodes)
 
        print("옵션 전종목 ", len(self.dicOptionCodes))
        for key in self.dicOptionCodes :
            self.dicOptionCodes[key].debugPrint(1)
 
    def ReqeustUpjongMst(self):
        ===odeList = g_objCodeMgr.GetIndustryList() # 증권 산업 업종 리스트===
 
        allcodelist = codeList
        print("전체 종목 코드 #", len(allcodelist))
 
 
        rqCodeList = []
        for i, code in enumerate(allcodelist):
            code2 = "U" + code
            rqCodeList.append(code2)
            if len(rqCodeList) == 200:
                self.obj.Request(rqCodeList, self.dicUpjongCodes)
                rqCodeList = []
                continue
 
        if len(rqCodeList) > 0 :
            self.obj.Request(rqCodeList, self.dicUpjongCodes)
 
        print("증권산업업종 리스트", len(self.dicUpjongCodes))
        for key in self.dicUpjongCodes :
            self.dicUpjongCodes[key].debugPrint(1)
 
class MyWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.main = testMain()
        self.setWindowTitle("PLUS API TEST")
        self.setGeometry(300, 500,  300, 500)
 
        nHeight = 20
        btnStock = QPushButton("주식 전 종목", self)
        btnStock.move(20, nHeight)
        btnStock.clicked.connect(self.btnStock_clicked)
 
        nHeight += 50
        btnElw = QPushButton("ELW 전 종목", self)
        btnElw.move(20, nHeight)
        btnElw.clicked.connect(self.btnElw_clicked)
 
        nHeight += 50
        btnFuture = QPushButton("선물 전 종목", self)
        btnFuture.move(20, nHeight)
        btnFuture.clicked.connect(self.btnFuture_clicked)
 
        nHeight += 50
        btnOption= QPushButton("옵션 전 종목", self)
        btnOption.move(20, nHeight)
        btnOption.clicked.connect(self.btnOption_clicked)
 
        nHeight += 50
        btnUpjong= QPushButton("업종 전 종목", self)
        btnUpjong.move(20, nHeight)
        btnUpjong.clicked.connect(self.btnUpjong_clicked)
 
        nHeight += 50
        btnExit = QPushButton("종료", self)
        btnExit.move(20, nHeight)
        btnExit.clicked.connect(self.btnExit_clicked)
 
    def btnStock_clicked(self):
        self.main.ReqeustStockMst()
        return
 
    def btnElw_clicked(self):
        self.main.ReqeustElwMst()
        return
 
    def btnFuture_clicked(self):
        self.main.ReqeustFutreMst()
        return
 
    def btnOption_clicked(self):
        self.main.ReqeustOptionMst()
        return
 
    def btnUpjong_clicked(self):
        self.main.ReqeustUpjongMst()
        return
 
 
    def btnExit_clicked(self):
        exit()
        return
 
 
 
if __name__ == "__main__":
    app = QApplication(sys.argv)
    myWindow = MyWindow()
    myWindow.show()
    app.exec_()

17 시세 연속 조회 제한 확인용 예제[편집]

18 VI 발동 종목에 대한 현재가 변화 추이 감시 예제[편집]

19 주식 주문 체결 실시간 처리 예제[편집]

20 주식 잔고 종목 조회 및 실시간 현재가 업데이트[편집]

21 당일 상승률 상위 200 종목 실시간 통신 예제[편집]

22 MACD 차트지표 계산(실시간)[편집]

23 차트 데이터 구하는 예제[편집]

24 주식 현금 매도주문[편집]

25 주식 현금 매수주문[편집]

26 주식 복수종목 조회/실시간[편집]

27 주식 복수종목 실시간 등록/해지[편집]

28 ETF NAV , ETN IIV 실시간 수신 예제[편집]

29 데이터 요청 방법 2가지 BlockRequest 와 Request 방식 비교 예제[편집]

30 주식 5분 차트 MACD 신호[편집]

이번 예제는

- 5분 차트를 조회 하고 실시간으로 업데이트 합니다 (분차트 생성 로직 참고)
- MACD 를 실시간으로 계산합니다 
- MACD OSCILLATOR 가 0 보다 커지면 그 다음 봉에 매수 신호 발생을, 반대의 경우 매도 신호 발생 처리 합니다 

(신호는 분차트가 완성 된 이후에 발생하도록 임의로 처리 해 두었는데 이 부분은 참고만 해 주시기 바랍니다)


테스트 종목 코드는 A069500 코덱스200 종목의 5분 차트로 임의로 지정 했습니다

import sys
from PyQt5.QtWidgets import *
import win32com.client
import ctypes
import time
 
################################################
# PLUS 공통 OBJECT
g_objCodeMgr = win32com.client.Dispatch('CpUtil.CpCodeMgr')
g_objCpStatus = win32com.client.Dispatch('CpUtil.CpCybos')
g_objCpTrade = win32com.client.Dispatch('CpTrade.CpTdUtil')
 
 
################################################
# PLUS 실행 기본 체크 함수
def InitPlusCheck():
    # 프로세스가 관리자 권한으로 실행 여부
    if ctypes.windll.shell32.IsUserAnAdmin():
        print('정상: 관리자권한으로 실행된 프로세스입니다.')
    else:
        print('오류: 일반권한으로 실행됨. 관리자 권한으로 실행해 주세요')
        return False
 
    # 연결 여부 체크
    if (g_objCpStatus.IsConnect == 0):
        print("PLUS가 정상적으로 연결되지 않음. ")
        return False
 
    # 주문 관련 초기화
    if (g_objCpTrade.TradeInit(0) != 0):
        print("주문 초기화 실패")
        return False
 
    return True
 
 
################################################
# CpEvent: 실시간 이벤트 수신 클래스
class CpEvent:
    def set_params(self, client, name, caller):
        self.client = client  # CP 실시간 통신 object
        self.name = name  # 서비스가 다른 이벤트를 구분하기 위한 이름
        self.caller = caller  # callback 을 위해 보관
 
    def OnReceived(self):
        # 실시간 처리 - 현재가 체결 데이터
        if self.name == 'stockcur':
            code = self.client.GetHeaderValue(0)  # 초
            name = self.client.GetHeaderValue(1)  # 초
            timess = self.client.GetHeaderValue(18)  # 초
            exFlag = self.client.GetHeaderValue(19)  # 예상체결 플래그
            cprice = self.client.GetHeaderValue(13)  # 현재가
            diff = self.client.GetHeaderValue(2)  # 대비
            cVol = self.client.GetHeaderValue(17)  # 순간체결수량
            vol = self.client.GetHeaderValue(9)  # 거래량
 
            if exFlag != ord('2'):
                return
 
            item = {}
            item['code'] = code
            item['time'] = timess
            item['diff'] = diff
            item['cur'] = cprice
            item['vol'] = cVol
 
            # 현재가 업데이트
            self.caller.updateCurData(item)
 
            return
 
 
################################################
# plus 실시간 수신 base 클래스
class CpPublish:
    def __init__(self, name, serviceID):
        self.name = name
        self.obj = win32com.client.Dispatch(serviceID)
        self.bIsSB = False
 
    def Subscribe(self, var, caller):
        if self.bIsSB:
            self.Unsubscribe()
 
        if (len(var) > 0):
            self.obj.SetInputValue(0, var)
 
        handler = win32com.client.WithEvents(self.obj, CpEvent)
        handler.set_params(self.obj, self.name, caller)
        self.obj.Subscribe()
        self.bIsSB = True
 
    def Unsubscribe(self):
        if self.bIsSB:
            self.obj.Unsubscribe()
        self.bIsSB = False
 
 
################################################
# CpPBStockCur: 실시간 현재가 요청 클래스
class CpPBStockCur(CpPublish):
    def __init__(self):
        super().__init__('stockcur', 'DsCbo1.StockCur')
 
 
# MACD 지표 계산
class CMACD:
    def __init__(self):
        self.objSeries = win32com.client.Dispatch("CpIndexes.CpSeries")
        self.objIndex = win32com.client.Dispatch("CpIndexes.CpIndex")
 
    # 차트 데이터 세팅 하기
    def setChartData(self, chartData):
        nLen = len(chartData['T'])
        for i in range(nLen):
            self.objSeries.Add(chartData['C'][i], chartData['O'][i], chartData['H'][i], chartData['L'][i],
                               chartData['V'][i])
 
        return
 
    # 기존 차트 데이터에 새로 들어온 신규 데이터 추가
    def addLastData(self, chartData):
        self.objSeries.Add(chartData['C'][-1], chartData['O'][-1], chartData['H'][-1], chartData['L'][-1],
                           chartData['V'][-1])
 
    # MACD 계산
    def makeMACD(self):
        result = {}
        # 지표 계산 object
        self.objIndex.series = self.objSeries
        self.objIndex.put_IndexKind("MACD")  # 계산할 지표: MACD
        self.objIndex.put_IndexDefault("MACD")  # MACD 지표 기본 변수 자동 세팅
 
        print("MACD 변수", self.objIndex.get_Term1(), self.objIndex.get_Term2(), self.objIndex.get_Signal())
 
        # 지표 데이터 계산 하기
        self.objIndex.Calculate()
 
        cntofIndex = self.objIndex.ItemCount
        print("지표 개수:  ", cntofIndex)
        indexName = ["MACD", "SIGNAL", "OSC"]
 
        result['MACD'] = []
        result['SIGNAL'] = []
        result['OSC'] = []
        for index in range(cntofIndex):
            cnt = self.objIndex.GetCount(index)
            for j in range(cnt):
                value = self.objIndex.GetResult(index, j)
                result[indexName[index]].append(value)
            # for j in range(cnt) :
            #    value = self.objIndex.GetResult(index,j)
            # print(indexName[index], value)  # 지표의 최근 값 표시
 
        print('MACD %.2f SIGNLA %.2f OSC %.2f' % (result['MACD'][-1], result['SIGNAL'][-1], result['OSC'][-1]))
        return (True, result)
 
    # MACD 업데이트(차트 데이터 개수에 변화가 없을 경우에만 사용)
    def updateMACD(self, chartData):
        result = {}
        # 지표 데이터 update
        self.objSeries.update(chartData['C'][-1], chartData['O'][-1], chartData['H'][-1], chartData['L'][-1],
                              chartData['V'][-1])
        self.objIndex.update()
        cntofIndex = self.objIndex.ItemCount
        print("지표 개수:  ", cntofIndex)
 
        indexName = ["MACD", "SIGNAL", "OSC"]
 
        result['MACD'] = []
        result['SIGNAL'] = []
        result['OSC'] = []
 
        for index in range(cntofIndex):
            cnt = self.objIndex.GetCount(index)
            for j in range(cnt):
                value = self.objIndex.GetResult(index, j)
                result[indexName[index]].append(value)
 
        print('MACD %.2f SIGNLA %.2f OSC %.2f' % (result['MACD'][-1], result['SIGNAL'][-1], result['OSC'][-1]))
        return (True, result)
 
 
# 분차트 관리 클래스
#   주어진 주기로 분차트 조회 , 실시간 분차트 데이터 생성, MACD 계산 호출
class CMinchartData:
    def __init__(self, interval):
        # interval : 분차트 주기
        self.interval = interval
        self.objCur = {}
        self.data = {}
        self.code = ''
        self.objMACD = CMACD()
        self.LASTTIME = 1530
 
        # 오늘 날짜
        now = time.localtime()
        self.todayDate = now.tm_year * 10000 + now.tm_mon * 100 + now.tm_mday
        print(self.todayDate)
 
    def MonCode(self, code):
        self.data = {}
        self.code = code
 
        self.data['O'] = []
        self.data['H'] = []
        self.data['L'] = []
        self.data['C'] = []
        self.data['V'] = []
        self.data['D'] = []
        self.data['T'] = []
        self.data['MACD'] = []
        self.data['SIGNAL'] = []
        self.data['OSC'] = []
 
        # 차트 기본 통신
        self.rqChartMinData(code, self.interval)
 
        # MACD 클래스에 수신 받은 차트 데이터 세팅
        self.objMACD.setChartData(self.data)
        # MACD 계산 하기
        ret, result = self.objMACD.makeMACD()
 
        self.data['MACD'] = result['MACD']
        self.data['SIGNAL'] = result['SIGNAL']
        self.data['OSC'] = result['OSC']
 
        # 실시간 시세 요청
        if (code not in self.objCur):
            self.objCur[code] = CpPBStockCur()
            self.objCur[code].Subscribe(code, self)
 
    def stop(self):
        for k, v in self.objCur.items():
            v.Unsubscribe()
        self.objCur = {}
 
    # 분차트 - 코드, 주기, 개수
    def rqChartMinData(self, code, interval):
        objRq = win32com.client.Dispatch("CpSysDib.StockChart")
 
        objRq.SetInputValue(0, code)  # 종목 코드
        objRq.SetInputValue(1, ord('2'))  # 개수로 조회
        objRq.SetInputValue(4, 500)  # 통신 개수 - 500 개로 고정
        objRq.SetInputValue(5, [0, 1, 2, 3, 4, 5, 8])  # 날짜,시간, 시가,고가,저가,종가,거래량
        objRq.SetInputValue(6, ord('m'))  # '차트 주가 - 분 데이터
        objRq.SetInputValue(7, interval)  # 차트 주기
        objRq.SetInputValue(9, ord('1'))  # 9 - 수정주가(char)
 
        totlen = 0
        objRq.BlockRequest()
        rqStatus = objRq.GetDibStatus()
        rqRet = objRq.GetDibMsg1()
        print("통신상태", rqStatus, rqRet)
        if rqStatus != 0:
            exit()
 
        len = objRq.GetHeaderValue(3)
        print(totlen)
        totlen += len
 
        print("날짜", "시가", "고가", "저가", "종가", "거래량")
        print("==============================================-")
 
        for i in range(len):
            day = objRq.GetDataValue(0, i)
            time = objRq.GetDataValue(1, i)
            open = objRq.GetDataValue(2, i)
            high = objRq.GetDataValue(3, i)
            low = objRq.GetDataValue(4, i)
            close = objRq.GetDataValue(5, i)
            vol = objRq.GetDataValue(6, i)
 
            self.data['D'].append(day)
            self.data['T'].append(time)
            self.data['O'].append(open)
            self.data['H'].append(high)
            self.data['L'].append(low)
            self.data['C'].append(close)
            self.data['V'].append(vol)
 
        # 수신된 역순으로 넣는다 -> 최근 날짜가 맨 뒤로 가도록
        self.data['D'].reverse()
        self.data['T'].reverse()
        self.data['O'].reverse()
        self.data['H'].reverse()
        self.data['L'].reverse()
        self.data['C'].reverse()
        self.data['V'].reverse()
 
    # 가격 실시간 변경 시 분차트 데이터 재 계산
    def updateCurData(self, item):
        time = item['time']
        self.cur = cur = item['cur']
        vol = item['vol']
        self.makeMinchart(time, cur, vol)
 
    def getHMTFromTime(self, time):
        hh, mm = divmod(time, 10000)
        mm, tt = divmod(mm, 100)
        return (hh, mm, tt)
 
    def getChartTime(self, time):
        # time 600 (10:00 인 경우 600)
        lChartTime = time + self.interval
 
        # 630 ==> 1030 분으로 변경
        hour, min  = divmod(lChartTime, 60)
        lCurTime = hour * 100 + min
 
        if (lCurTime > self.LASTTIME):
            lCurTime = self.LASTTIME
 
        return lCurTime
 
    # 실시간 데이터를 통해 분차트 업데이트
    def makeMinchart(self, time, cur, vol):
        # time 분해 ==> 시, 분, 초
        hh, mm, tt = self.getHMTFromTime(time)
        #hhmm = hh * 100 + mm
        # 1000 ==> 600 분
        converedMintime = hh * 60 + mm
 
        bFind = False
        nLen = len(self.data['T'])
 
        # 분차트 주기 기준으로 나눠서 시간 차트 시간 계산
        #   1분 봉의 경우 14:10분 봉: 14:09:00~14:09:59
        #   5분 봉의 경우 14:10분 봉 : 14:05:00~ 14:09:59
        a, b = divmod(converedMintime, self.interval)
        intervaltime = a * self.interval
        lCurTime = self.getChartTime(intervaltime)
        print('차트 시간 계산 : 들어온 시간 %d, 차트 시간 %d' % (time, lCurTime))
 
        if (nLen > 0):
            lLastTime = self.data['T'][-1]
            if (lLastTime == lCurTime):
                bFind = True
 
                self.data['C'][-1] = cur
                if (self.data['H'][-1] < cur):
                    self.data['H'][-1] = cur
                if (self.data['L'][-1] > cur):
                    self.data['L'][-1] = cur
                self.data['V'][-1] += vol
                print('들어온 시간 %d ==> 마지막 분차트 시간 %d 에 업데이트' % (time, lLastTime))
 
                ret, result = self.objMACD.updateMACD(self.data)
                self.data['MACD'] = result['MACD']
                self.data['SIGNAL'] = result['SIGNAL']
                self.data['OSC'] = result['OSC']
 
        # 신규 봉이 추가
        if bFind == False:
            print('들어온 시간 %d ==> 새로운 분차트 시간 %d 에 업데이트' % (time, lCurTime))
            self.data['D'].append(self.todayDate)
            self.data['T'].append(lCurTime)
            self.data['O'].append(cur)
            self.data['H'].append(cur)
            self.data['L'].append(cur)
            self.data['C'].append(cur)
            self.data['V'].append(vol)
 
            # 데이터 추가 - MACD 계산 모듈에 차트 데이터 추가
            self.objMACD.addLastData(self.data)
            # MACD 계산
            ret, result = self.objMACD.makeMACD()
 
            self.data['MACD'] = result['MACD']
            self.data['SIGNAL'] = result['SIGNAL']
            self.data['OSC'] = result['OSC']
 
            # MACD 의 신호가 교차됐는지 체크
            self.checkMACD()
 
        return
 
    def checkMACD(self):
        if (len(self.data['OSC']) < 5):
            return
        # 현재 시점에서 이전 봉(-2) 가 매수신호/매도 신호 발생했는 지 체크
        # -1 : 현재 시점 -2: 바로 직전 봉 -3 그 전 봉
        print('osc', self.data['OSC'][-3], self.data['OSC'][-2], self.data['OSC'][-1])
        if self.data['OSC'][-3] < 0:
            if self.data['OSC'][-2] > 0:
                print('MACD 매수, 시간 %d, 가격 %d' % (self.data['T'][-1], self.data['C'][-1]))
        elif self.data['OSC'][-3] > 0:
            if self.data['OSC'][-2] < 0:
                print('MACD 매도, 시간 %d, 가격 %d' % (self.data['T'][-1], self.data['C'][-1]))
 
    def printdata(self):
        nLen = len(self.data['T'])
        for i in range(nLen):
            print(self.data['D'][i],
                  self.data['T'][i],
                  self.data['O'][i],
                  self.data['H'][i],
                  self.data['L'][i],
                  self.data['C'][i],
                  self.data['V'][i],
                  self.data['MACD'][i],
                  self.data['SIGNAL'][i],
                  self.data['OSC'][i])
 
        # print(code, self.minDatas[code])
 
 
################################################
# 테스트를 위한 메인 화면
class MyWindow(QMainWindow):
    def __init__(self):
        super().__init__()
 
        # plus 상태 체크
        if InitPlusCheck() == False:
            exit()
 
        self.minData = CMinchartData(5)
        self.minData.MonCode('A069500')
 
        self.setWindowTitle("주식 분 차트 생성")
        self.setGeometry(300, 300, 300, 180)
 
        nH = 20
 
        btnPrint = QPushButton('print', self)
        btnPrint.move(20, nH)
        btnPrint.clicked.connect(self.btnPrint_clicked)
        nH += 50
 
        btnExit = QPushButton('종료', self)
        btnExit.move(20, nH)
        btnExit.clicked.connect(self.btnExit_clicked)
        nH += 50
 
    def btnPrint_clicked(self):
        self.minData.printdata()
        return
 
    def btnExit_clicked(self):
        self.minData.stop()
        exit()
        return
 
 
if __name__ == "__main__":
    app = QApplication(sys.argv)
    myWindow = MyWindow()
    myWindow.show()
    app.exec_()

31 종목검색 조회 및 실시간 감시 예제[편집]

32 선물 현재가/시간대별/일자별/주문/잔고/미체결 예제[편집]

33 투자자별 매매 종합 예제[편집]

34 전종목 시가총액 구하기 예제[편집]

35 차트 지표 계산(MACD/RSI 등)[편집]

36 선물 분 차트 그리기(matplotlib 이용)[편집]

37 체결기준 주식 당일 매매 손익 예제[편집]

체결가를 기준으로 당일 매매의 평가손익 및 실현손익을 조회할 수 있는 서비스 예제 입니다.

■ 사용 서비스

 - CpTrade.CpTd6032 : 체결기준 주식 당일 매매 손익 


※ 주의사항: 본 예제는 PLUS 활용을 돕기 위해 예제로만 제공됩니다

import sys
from PyQt5.QtWidgets import *
import win32com.client
import ctypes
 
g_objCodeMgr = win32com.client.Dispatch('CpUtil.CpCodeMgr')
g_objCpStatus = win32com.client.Dispatch('CpUtil.CpCybos')
g_objCpTrade = win32com.client.Dispatch('CpTrade.CpTdUtil')
 
def InitPlusCheck():
    # 프로세스가 관리자 권한으로 실행 여부
    if ctypes.windll.shell32.IsUserAnAdmin():
        print('정상: 관리자권한으로 실행된 프로세스입니다.')
    else:
        print('오류: 일반권한으로 실행됨. 관리자 권한으로 실행해 주세요')
        return False
 
    # 연결 여부 체크
    if (g_objCpStatus.IsConnect == 0):
        print("PLUS가 정상적으로 연결되지 않음. ")
        return False
 
    # 주문 관련 초기화
    if (g_objCpTrade.TradeInit(0) != 0):
        print("주문 초기화 실패")
        return False
 
    return True
 
 
 
# Cp6032 : 주식 잔고 조회
class Cp6032:
    def __init__(self):
        acc = g_objCpTrade.AccountNumber[0]  # 계좌번호
        accFlag = g_objCpTrade.GoodsList(acc, 1)  # 주식상품 구분
        print(acc, accFlag[0])
 
        self.objRq = win32com.client.Dispatch("CpTrade.CpTd6032")
        self.objRq.SetInputValue(0, acc)  # 계좌번호
        self.objRq.SetInputValue(1, accFlag[0])  # 상품구분 - 주식 상품 중 첫번째
 
    # 실제적인 6032 통신 처리
    def request6032(self, caller):
        sumJango = 0
        sumSellM = 0
        sumRate = 0.0
 
        bIsFist = True
        while True:
            self.objRq.BlockRequest()
            # 통신 및 통신 에러 처리
            rqStatus = self.objRq.GetDibStatus()
            rqRet = self.objRq.GetDibMsg1()
            print("통신상태", rqStatus, rqRet)
            if rqStatus != 0:
                return False
 
            cnt = self.objRq.GetHeaderValue(0)
            print('데이터 조회 개수', cnt)
 
            # 헤더 정보는 한번만 처리
            if bIsFist == True:
                sumJango = self.objRq.GetHeaderValue(1)
                sumSellM = self.objRq.GetHeaderValue(2)
                sumRate = self.objRq.GetHeaderValue(3)
                print('잔량평가손익', sumJango, '매도실현손익',sumSellM, '수익률',sumRate)
                bIsFist = False
 
            for i in range(cnt):
                item = {}
                item['종목코드'] = self.objRq.GetDataValue(12, i)  # 종목코드
                item['종목명'] = self.objRq.GetDataValue(0, i)  # 종목명
                item['신용일자'] = self.objRq.GetDataValue(1, i)
                item['전일잔고'] = self.objRq.GetDataValue(2, i)
                item['금일매수수량'] = self.objRq.GetDataValue(3, i)
                item['금일매도수량'] = self.objRq.GetDataValue(4, i)
                item['금일잔고'] = self.objRq.GetDataValue(5, i)
                item['평균매입단가'] = self.objRq.GetDataValue(6, i)
                item['평균매도단가'] = self.objRq.GetDataValue(7, i)
                item['현재가'] = self.objRq.GetDataValue(8, i)
                item['잔량평가손익'] = self.objRq.GetDataValue(9, i)
                item['매도실현손익'] = self.objRq.GetDataValue(10, i)
                item['수익률'] = self.objRq.GetDataValue(11, i)
 
                print(item)
                caller.data6032.append(item)
            if (self.objRq.Continue == False):
                break
        return True
 
 
 
class MyWindow(QMainWindow):
    def __init__(self):
        super().__init__()
 
        # plus 상태 체크
        if InitPlusCheck() == False:
            exit()
 
        self.setWindowTitle("체결기준 주식당일매매손익 예제")
        self.setGeometry(300, 300, 300, 180)
 
        # 6033 잔고 object
        self.obj6032 = Cp6032()
        self.data6032 = []
 
 
        nH = 20
 
        btnPrint = QPushButton('Print', self)
        btnPrint.move(20, nH)
        btnPrint.clicked.connect(self.btnPrint_clicked)
        nH += 50
 
        btnExit = QPushButton('종료', self)
        btnExit.move(20, nH)
        btnExit.clicked.connect(self.btnExit_clicked)
        nH += 50
 
        # 잔고 요청
        self.request6032()
        
        
    def request6032(self):
        if self.obj6032.request6032(self) == False:
            return
 
    def btnPrint_clicked(self):
        print('체결기준 당일매매손익')
        for item in self.data6032 :
            print(item)
        return
 
    def btnExit_clicked(self):
        exit()
        return
 
 
 
 
if __name__ == "__main__":
    app = QApplication(sys.argv)
    myWindow = MyWindow()
    myWindow.show()
    app.exec_()

38 실시간 분차트 데이터 만들기[편집]

실시간 시세를 받아, 1분 차트 데이터를 만드는 예제입니다. 예제에서는 코스피 200 에 속하는 200 종목을 실시간으로 요청한 후 받은 시세를 기준으로 1분 차트 데이터를 만듭니다. (200 종목 분 차트 데이터 생성)

■ 분 차트 기준 : 14:10분 봉의 겨우 14:09:00~14:09:59초 사이에 들어온 데이터를 하나의 봉으로 만듭니다 ■ 예제에서는 시세 수신이 있을 경우에만 데이터를 추가하여 분데이터를 만듭니다. (해당 시간 체결이 없을 경우에는 분 데이터를 채우지 않음) ■ 과거 데이터 조회 없이 예제 실행 이후 들어온 데이터로만 분 차트를 만드는 예제입니다.


※ 주의사항: 본 예제는 PLUS 활용을 돕기 위해 예제로만 제공됩니다. 또한 제공된 코드에는 장 운영 시간 변경 등의 예외 코드가 반영 되어 있지 않습니다.

import sys
from PyQt5.QtWidgets import *
import win32com.client
import ctypes
 
################################################
# PLUS 공통 OBJECT
g_objCodeMgr = win32com.client.Dispatch('CpUtil.CpCodeMgr')
g_objCpStatus = win32com.client.Dispatch('CpUtil.CpCybos')
g_objCpTrade = win32com.client.Dispatch('CpTrade.CpTdUtil')
 
 
################################################
# PLUS 실행 기본 체크 함수
def InitPlusCheck():
    # 프로세스가 관리자 권한으로 실행 여부
    if ctypes.windll.shell32.IsUserAnAdmin():
        print('정상: 관리자권한으로 실행된 프로세스입니다.')
    else:
        print('오류: 일반권한으로 실행됨. 관리자 권한으로 실행해 주세요')
        return False
 
    # 연결 여부 체크
    if (g_objCpStatus.IsConnect == 0):
        print("PLUS가 정상적으로 연결되지 않음. ")
        return False
 
    # # 주문 관련 초기화
    # if (g_objCpTrade.TradeInit(0) != 0):
    #     print("주문 초기화 실패")
    #     return False
 
    return True
 
 
################################################
# CpEvent: 실시간 이벤트 수신 클래스
class CpEvent:
    def set_params(self, client, name, caller):
        self.client = client  # CP 실시간 통신 object
        self.name = name  # 서비스가 다른 이벤트를 구분하기 위한 이름
        self.caller = caller  # callback 을 위해 보관
 
    def OnReceived(self):
        # 실시간 처리 - 현재가 주문 체결
        if self.name == 'stockcur':
            code = self.client.GetHeaderValue(0)  # 초
            name = self.client.GetHeaderValue(1)  # 초
            timess = self.client.GetHeaderValue(18)  # 초
            exFlag = self.client.GetHeaderValue(19)  # 예상체결 플래그
            cprice = self.client.GetHeaderValue(13)  # 현재가
            diff = self.client.GetHeaderValue(2)  # 대비
            cVol = self.client.GetHeaderValue(17)  # 순간체결수량
            vol = self.client.GetHeaderValue(9)  # 거래량
 
            if exFlag != ord('2'):
                return
 
            item = {}
            item['code'] = code
            item['time'] = timess
            item['diff'] = diff
            item['cur'] = cprice
            item['vol'] = vol
 
            # 현재가 업데이트
            self.caller.updateCurData(item)
 
 
            return
 
 
################################################
# plus 실시간 수신 base 클래스
class CpPublish:
    def __init__(self, name, serviceID):
        self.name = name
        self.obj = win32com.client.Dispatch(serviceID)
        self.bIsSB = False
 
    def Subscribe(self, var, caller):
        if self.bIsSB:
            self.Unsubscribe()
 
        if (len(var) > 0):
            self.obj.SetInputValue(0, var)
 
        handler = win32com.client.WithEvents(self.obj, CpEvent)
        handler.set_params(self.obj, self.name, caller)
        self.obj.Subscribe()
        self.bIsSB = True
 
    def Unsubscribe(self):
        if self.bIsSB:
            self.obj.Unsubscribe()
        self.bIsSB = False
 
 
################################################
# CpPBStockCur: 실시간 현재가 요청 클래스
class CpPBStockCur(CpPublish):
    def __init__(self):
        super().__init__('stockcur', 'DsCbo1.StockCur')
 
 
class CMinchartData:
    def __init__(self):
        self.minDatas = {}
        self.objCur = {}
 
    def stop(self):
        for k,v in self.objCur.items() :
            v.Unsubscribe()
 
 
    def addCode(self, code):
        if (code in self.minDatas) :
            return
 
        self.minDatas[code] = []
        self.objCur[code] = CpPBStockCur()
        self.objCur[code].Subscribe(code, self)
 
 
 
    def updateCurData(self, item):
        code = item['code']
        time  = item['time']
        cur = item['cur']
        self.makeMinchart(code, time, cur)
 
 
    def makeMinchart(self, code, time, cur) :
        hh, mm = divmod(time, 10000)
        mm, tt = divmod(mm, 100)
        mm += 1
        if (mm == 60) :
            hh += 1
            mm = 0
 
        hhmm = hh * 100 + mm
        if hhmm > 1530 :
            hhmm = 1530
        bFind = False
        minlen =len(self.minDatas[code])
        if (minlen > 0) :
            # 0 : 시간 1 : 시가 2: 고가 3: 저가 4: 종가
            if (self.minDatas[code][-1][0] == hhmm) :
                item = self.minDatas[code][-1]
                bFind = True
                item[4] = cur
                if (item[2] < cur):
                    item[2] = cur
                if (item[3] > cur):
                    item[3] = cur
 
        if bFind ==  False :
            self.minDatas[code].append([hhmm, cur, cur, cur, cur])
 
#        print(code, self.minDatas[code])
        return
 
    def print(self, code):
        print('====================================================-')
        print('분데이터 print', code, g_objCodeMgr.CodeToName(code))
        print('시간,시가,고가,저가,종가')
        for item in self.minDatas[code] :
            hh, mm = divmod(item[0], 100)
            print("%02d:%02d,%d,%d,%d,%d" %(hh, mm, item[1], item[2], item[3], item[4]))
 
 
 
# print(code, self.minDatas[code])
 
 
 
################################################
# 테스트를 위한 메인 화면
class MyWindow(QMainWindow):
    def __init__(self):
        super().__init__()
 
        # plus 상태 체크
        if InitPlusCheck() == False:
            exit()
 
        self.minData = CMinchartData()
 
        # 코스피 200 종목 가져와 추가
        self.codelist = g_objCodeMgr.GetGroupCodeList(180)
        for code in self.codelist :
            print(code, g_objCodeMgr.CodeToName(code))
            self.minData.addCode(code)
 
 
        self.setWindowTitle("주식 분 차트 생성")
        self.setGeometry(300, 300, 300, 180)
 
        nH  = 20
 
        btnPrint = QPushButton('print', self)
        btnPrint.move(20, nH)
        btnPrint.clicked.connect(self.btnPrint_clicked)
        nH += 50
 
 
        btnExit = QPushButton('종료', self)
        btnExit.move(20, nH)
        btnExit.clicked.connect(self.btnExit_clicked)
        nH += 50
 
 
    def btnPrint_clicked(self):
        for i in range(len(self.codelist)):
            self.minData.print(self.codelist[i])
            if i > 10: break
        return
 
 
    def btnExit_clicked(self):
        self.minData.stop()
        exit()
        return
 
 
 
if __name__ == "__main__":
    app = QApplication(sys.argv)
    myWindow = MyWindow()
    myWindow.show()
    app.exec_()

39 주식 잔고 실시간 조회(현재가 및 주문 체결 실시간 반영)[편집]

40 해외선물 주문 예제[편집]

41 특징주 포착(뉴스/공시/차트/외국인 신호) 예제[편집]

HTS #8092 특징주 포착(뉴스/차트 신호) 화면 예제 입니다 . 특징주 포착 서비스를 통해 뉴스/공시/차트/외국인 관련 주요 신호를 조회 및 실시간으로 받아 볼 수 있습니다 자세한 내용은 HTS 화면과 함께 참고 하시기 바랍니다

■ 사용된 PLUS 객체

- CpSysDib.CpMarketWatch - #8092 특징주 포착 조회 서비스 
- Dscbo1.CpSvr8092S - 실시간 뉴스/공시 수신
- CpSysDib.CpMarketWatchS - 실시간 차트/외국인 신호 수신

제공된 예제는 UI 부분은 없이 수신 된 신호를 기본 print() 로 표출하고 있습니다 
엑셀 내보내기를 제공하고 있어 수신 받은 내용을 #8092 와 비교 할 수 있습니다.
import sys
from PyQt5.QtWidgets import *
import win32com.client
import ctypes
import pandas as pd
import os
 
g_objCodeMgr = win32com.client.Dispatch('CpUtil.CpCodeMgr')
g_objCpStatus = win32com.client.Dispatch('CpUtil.CpCybos')
g_objCpTrade = win32com.client.Dispatch('CpTrade.CpTdUtil')
 
gExcelFile = '8092.xlsx'
 
 
def InitPlusCheck():
    # 프로세스가 관리자 권한으로 실행 여부
    if ctypes.windll.shell32.IsUserAnAdmin():
        print('정상: 관리자권한으로 실행된 프로세스입니다.')
    else:
        print('오류: 일반권한으로 실행됨. 관리자 권한으로 실행해 주세요')
        return False
 
    # 연결 여부 체크
    if (g_objCpStatus.IsConnect == 0):
        print("PLUS가 정상적으로 연결되지 않음. ")
        return False
 
    '''
    # 주문 관련 초기화
    if (g_objCpTrade.TradeInit(0) != 0):
        print("주문 초기화 실패")
        return False
    '''
    return True
 
 
# CpEvent: 실시간 이벤트 수신 클래스
class CpEvent:
    def set_params(self, client, name, caller):
        self.client = client  # CP 실시간 통신 object
        self.name = name  # 서비스가 다른 이벤트를 구분하기 위한 이름
        self.caller = caller  # callback 을 위해 보관
        self.diccode = {
            10: '외국계증권사창구첫매수',
            11: '외국계증권사창구첫매도',
            12: '외국인순매수',
            13: '외국인순매도',
            21: '전일거래량갱신',
            22: '최근5일거래량최고갱신',
            23: '최근5일매물대돌파',
            24: '최근60일매물대돌파',
            28: '최근5일첫상한가',
            29: '최근5일신고가갱신',
            30: '최근5일신저가갱신',
            31: '상한가직전',
            32: '하한가직전',
            41: '주가 5MA 상향돌파',
            42: '주가 5MA 하향돌파',
            43: '거래량 5MA 상향돌파',
            44: '주가데드크로스(5MA < 20MA)',
            45: '주가골든크로스(5MA > 20MA)',
            46: 'MACD 매수-Signal(9) 상향돌파',
            47: 'MACD 매도-Signal(9) 하향돌파',
            48: 'CCI 매수-기준선(-100) 상향돌파',
            49: 'CCI 매도-기준선(100) 하향돌파',
            50: 'Stochastic(10,5,5)매수- 기준선상향돌파',
            51: 'Stochastic(10,5,5)매도- 기준선하향돌파',
            52: 'Stochastic(10,5,5)매수- %K%D 교차',
            53: 'Stochastic(10,5,5)매도- %K%D 교차',
            54: 'Sonar 매수-Signal(9) 상향돌파',
            55: 'Sonar 매도-Signal(9) 하향돌파',
            56: 'Momentum 매수-기준선(100) 상향돌파',
            57: 'Momentum 매도-기준선(100) 하향돌파',
            58: 'RSI(14) 매수-Signal(9) 상향돌파',
            59: 'RSI(14) 매도-Signal(9) 하향돌파',
            60: 'Volume Oscillator 매수-Signal(9) 상향돌파',
            61: 'Volume Oscillator 매도-Signal(9) 하향돌파',
            62: 'Price roc 매수-Signal(9) 상향돌파',
            63: 'Price roc 매도-Signal(9) 하향돌파',
            64: '일목균형표매수-전환선 > 기준선상향교차',
            65: '일목균형표매도-전환선 < 기준선하향교차',
            66: '일목균형표매수-주가가선행스팬상향돌파',
            67: '일목균형표매도-주가가선행스팬하향돌파',
            68: '삼선전환도-양전환',
            69: '삼선전환도-음전환',
            70: '캔들패턴-상승반전형',
            71: '캔들패턴-하락반전형',
            81: '단기급락후 5MA 상향돌파',
            82: '주가이동평균밀집-5%이내',
            83: '눌림목재상승-20MA 지지'
        }
 
    def OnReceived(self):
        print(self.name)
        # 실시간 처리 - marketwatch : 특이 신호(차트, 외국인 순매수 등)
        if self.name == 'marketwatch':
            code = self.client.GetHeaderValue(0)
            name = g_objCodeMgr.CodeToName(code)
            cnt = self.client.GetHeaderValue(2)
 
            for i in range(cnt):
                item = {}
                newcancel = ''
                time = self.client.GetDataValue(0, i)
                h,m = divmod(time, 100)
                item['시간'] = '%02d:%02d' % (h,m)
                update = self.client.GetDataValue(1, i)
                item['코드'] = code
                item['종목명'] = name
                cate = self.client.GetDataValue(2, i)
                if (update == ord('c')):
                    newcancel =  '[취소]'
                if cate in self.diccode:
                    item['특이사항'] = newcancel + self.diccode[cate]
                else:
                    item['특이사항'] = newcancel + ''
 
                self.caller.listWatchData.insert(0,item)
                print(item)
 
        # 실시간 처리 - marketnews : 뉴스 및 공시 정보
        elif self.name == 'marketnews':
            item = {}
            update = self.client.GetHeaderValue(0)
            cont = ''
            if update == ord('D') :
                cont = '[삭제]'
            code = item['코드'] = self.client.GetHeaderValue(1)
            time = self.client.GetHeaderValue(2)
            h, m = divmod(time, 100)
            item['시간'] = '%02d:%02d' % (h, m)
            item['종목명'] = name = g_objCodeMgr.CodeToName(code)
            cate = self.client.GetHeaderValue(4)
            item['특이사항'] = cont + self.client.GetHeaderValue(5)
            print(item)
            self.caller.listWatchData.insert(0, item)
 
class CpPublish:
    def __init__(self, name, serviceID):
        self.name = name
        self.obj = win32com.client.Dispatch(serviceID)
        self.bIsSB = False
 
    def Subscribe(self, var, caller):
        if self.bIsSB:
            self.Unsubscribe()
 
        if (len(var) > 0):
            self.obj.SetInputValue(0, var)
 
        handler = win32com.client.WithEvents(self.obj, CpEvent)
        handler.set_params(self.obj, self.name, caller)
        self.obj.Subscribe()
        self.bIsSB = True
 
    def Unsubscribe(self):
        if self.bIsSB:
            self.obj.Unsubscribe()
        self.bIsSB = False
 
# CpPBMarkeWatch:
class CpPBMarkeWatch(CpPublish):
    def __init__(self):
        super().__init__('marketwatch', 'CpSysDib.CpMarketWatchS')
 
# CpPBMarkeWatch:
class CpPB8092news(CpPublish):
    def __init__(self):
        super().__init__('marketnews', 'Dscbo1.CpSvr8092S')
 
# CpRpMarketWatch : 특징주 포착 통신
class CpRpMarketWatch:
    def __init__(self):
        self.objStockMst = win32com.client.Dispatch('CpSysDib.CpMarketWatch')
        self.objpbMarket = CpPBMarkeWatch()
        self.objpbNews = CpPB8092news()
        return
 
    def Request(self, code, caller):
        self.objpbMarket.Unsubscribe()
        self.objpbNews.Unsubscribe()
 
        self.objStockMst.SetInputValue(0, code)
        # 1: 종목 뉴스 2: 공시정보 10: 외국계 창구첫매수, 11:첫매도 12 외국인 순매수 13 순매도
        rqField = '1,2,10,11,12,13'
        self.objStockMst.SetInputValue(1, rqField)
        self.objStockMst.SetInputValue(2, 0) # 시작 시간: 0 처음부터
 
        ret = self.objStockMst.BlockRequest()
        if self.objStockMst.GetDibStatus() != 0:
            print('통신상태', self.objStockMst.GetDibStatus(), self.objStockMst.GetDibMsg1())
            return False
 
        cnt = self.objStockMst.GetHeaderValue(2)  # 수신 개수
        for i in range(cnt):
            item = {}
 
            time  = self.objStockMst.GetDataValue(0, i)
            h, m = divmod(time, 100)
            item['시간'] = '%02d:%02d' % (h, m)
            item['코드'] = self.objStockMst.GetDataValue(1, i)
            item['종목명'] = g_objCodeMgr.CodeToName(item['코드'])
            cate = self.objStockMst.GetDataValue(3, i)
            item['특이사항'] = self.objStockMst.GetDataValue(4, i)
            print(item)
            caller.listWatchData.append(item)
 
        self.objpbMarket.Subscribe(code, caller)
        self.objpbNews.Subscribe(code, caller)
 
        return True
 
 
 
class MyWindow(QMainWindow):
    def __init__(self):
        super().__init__()
 
        # plus 상태 체크
        if InitPlusCheck() == False:
            exit()
 
        self.listWatchData = []
        self.objMarketWatch = CpRpMarketWatch()
 
        self.setWindowTitle("특징주 포착(#8092 Market-Watch")
        self.setGeometry(300, 300, 300, 180)
 
        nH = 20
 
        btnPrint = QPushButton('Print', self)
        btnPrint.move(20, nH)
        btnPrint.clicked.connect(self.btnPrint_clicked)
        nH += 50
 
        btnExcel = QPushButton('Excel 내보내기', self)
        btnExcel.move(20, nH)
        btnExcel.clicked.connect(self.btnExcel_clicked)
        nH += 50
 
 
        btnExit = QPushButton('종료', self)
        btnExit.move(20, nH)
        btnExit.clicked.connect(self.btnExit_clicked)
        nH += 50
 
        self.objMarketWatch.Request('*', self)
 
 
    def btnPrint_clicked(self):
        for item in self.listWatchData:
            print(item)
        return
 
    def btnExcel_clicked(self):
 
        if (len(self.listWatchData) == 0):
            print('데이터 없음')
            return
 
        df = pd.DataFrame(columns=['시간', '코드', '종목명', '특이사항'])
 
        for item in self.listWatchData:
            df.loc[len(df)] = item
 
        writer = pd.ExcelWriter(gExcelFile, engine='xlsxwriter')
        # Convert the dataframe to an XlsxWriter Excel object.
        df.to_excel(writer, sheet_name='Sheet1')
        # Close the Pandas Excel writer and output the Excel file.
        writer.save()
        os.startfile(gExcelFile)
        return
 
 
    def btnExit_clicked(self):
        exit()
        return
 
 
 
 
 
if __name__ == "__main__":
    app = QApplication(sys.argv)
    myWindow = MyWindow()
    myWindow.show()
    app.exec_()

42 보유 주식 잔고 일괄 매도 예제[편집]

[예제 목적] 주식 잔고를 조회 하여 특정 호가(매수 5차 호가)로 일괄 매도 하는 예제

[예제에 사용된 플러스 객체] CpTrade.CpTd6033 - 주식 잔고 조회 Object DsCbo1.StockMst - 현재가 조회 Object CpTrade.CpTd0311 - 주식 매수/매도 주문 Object DsCbo1.CpConclusion - [실시간]주문 체결 처리 Object

[주의 사항] - 매수 5차로 매도 하는 예제로 실제 매도가 체결 될 수 있으니 주의가 필요 - 해당 예제의 테스트는 모의투자에서 먼저 검증해 보시길 권장합니다. - 예제는 현금 잔고에 대해서만 처리 했음 (신용, 담보 등의 잔고는 처리 안됨) - PLUS 연속 조회 오류(15초간 20건) 발생 시에는 매도가 안될 수 있음 : 해당 오류에 대한 별도 처리가 필요 - 주문 수량은 매도 가능 수량으로 했음(주문 중인 다른 미체결이 있을 경우 남은 매도 가능 수량으로만 매도)

import sys
from PyQt5.QtWidgets import *
import win32com.client
import ctypes
 
g_objCodeMgr = win32com.client.Dispatch('CpUtil.CpCodeMgr')
g_objCpStatus = win32com.client.Dispatch('CpUtil.CpCybos')
g_objCpTrade = win32com.client.Dispatch('CpTrade.CpTdUtil')
 
def InitPlusCheck():
    # 프로세스가 관리자 권한으로 실행 여부
    if ctypes.windll.shell32.IsUserAnAdmin():
        print('정상: 관리자권한으로 실행된 프로세스입니다.')
    else:
        print('오류: 일반권한으로 실행됨. 관리자 권한으로 실행해 주세요')
        return False
 
    # 연결 여부 체크
    if (g_objCpStatus.IsConnect == 0):
        print("PLUS가 정상적으로 연결되지 않음. ")
        return False
 
    # 주문 관련 초기화
    if (g_objCpTrade.TradeInit(0) != 0):
        print("주문 초기화 실패")
        return False
 
    return True
 
 
# CpEvent: 실시간 이벤트 수신 클래스
class CpEvent:
    def set_params(self, client, name, caller):
        self.client = client  # CP 실시간 통신 object
        self.name = name  # 서비스가 다른 이벤트를 구분하기 위한 이름
        self.caller = caller  # callback 을 위해 보관
 
        # 구분값 : 텍스트로 변경하기 위해 딕셔너리 이용
        self.dicflag12 = {'1': '매도', '2': '매수'}
        self.dicflag14 = {'1': '체결', '2': '확인', '3': '거부', '4':'접수'}
        self.dicflag15 = {'00': '현금', '01': '유통융자', '02': '자기융자', '03': '유통대주',
                          '04':'자기대주', '05':'주식담보대출', '07':'채권담보대출',
                          '06':'매입담보대출', '08':'플러스론',
                          '13':'자기대용융자', '15':'유통대용융자'}
        self.dicflag16 = {'1': '정상주문', '2': '정정주문', '3': '취소주문'}
        self.dicflag17 = {'1': '현금', '2': '신용', '3': '선물대용', '4': '공매도'}
        self.dicflag18 = {'01': '보통', '02': '임의', '03':'시장가', '05': '조건부지정가'}
        self.dicflag19 = {'0': '없음', '1': 'IOC', '2': 'FOK'}
 
 
    def OnReceived(self):
#        print(self.name)
        # 실시간 처리 - 주문체결
        if self.name == 'conclution':
            # 주문 체결 실시간 업데이트
            conc = {}
 
            # 체결 플래그
            conc['체결플래그'] = self.dicflag14[self.client.GetHeaderValue(14)]
 
            conc['주문번호'] = self.client.GetHeaderValue(5)  # 주문번호
            conc['주문수량'] = self.client.GetHeaderValue(3)  # 주문/체결 수량
            conc['주문가격'] = self.client.GetHeaderValue(4)  # 주문/체결 가격
            conc['원주문'] = self.client.GetHeaderValue(6)
            conc['종목코드'] = self.client.GetHeaderValue(9)  # 종목코드
            conc['종목명'] = g_objCodeMgr.CodeToName(conc['종목코드'])
 
            conc['매수매도'] = self.dicflag12[self.client.GetHeaderValue(12)]
 
            flag15  = self.client.GetHeaderValue(15) # 신용대출구분코드
            if (flag15 in self.dicflag15):
                conc['신용대출'] = self.dicflag15[flag15]
            else:
                conc['신용대출'] = '기타'
 
            conc['정정취소'] = self.dicflag16[self.client.GetHeaderValue(16)]
            conc['현금신용'] = self.dicflag17[self.client.GetHeaderValue(17)]
            conc['주문조건'] = self.dicflag19[self.client.GetHeaderValue(19)]
 
            conc['체결기준잔고수량'] = self.client.GetHeaderValue(23)
            loandate = self.client.GetHeaderValue(20)
            if (loandate == 0):
                conc['대출일'] = ''
            else:
                conc['대출일'] = str(loandate)
            flag18 = self.client.GetHeaderValue(18)
            if (flag18 in self.dicflag18):
                conc['주문호가구분'] = self.dicflag18[flag18]
            else:
                conc['주문호가구분'] = '기타'
 
            conc['장부가'] = self.client.GetHeaderValue(21)
            conc['매도가능'] = self.client.GetHeaderValue(22)
 
            print(conc)
            self.caller.updateJangoCont(conc)
 
            return
 
 
class CpPublish:
    def __init__(self, name, serviceID):
        self.name = name
        self.obj = win32com.client.Dispatch(serviceID)
        self.bIsSB = False
 
    def Subscribe(self, var, caller):
        if self.bIsSB:
            self.Unsubscribe()
 
        if (len(var) > 0):
            self.obj.SetInputValue(0, var)
 
        handler = win32com.client.WithEvents(self.obj, CpEvent)
        handler.set_params(self.obj, self.name, caller)
        self.obj.Subscribe()
        self.bIsSB = True
 
    def Unsubscribe(self):
        if self.bIsSB:
            self.obj.Unsubscribe()
        self.bIsSB = False
 
# CpPBConclusion: 실시간 주문 체결 수신 클래그
class CpPBConclusion(CpPublish):
    def __init__(self):
        super().__init__('conclution', 'DsCbo1.CpConclusion')
 
# Cp6033 : 주식 잔고 조회
class Cp6033:
    def __init__(self):
        acc = g_objCpTrade.AccountNumber[0]  # 계좌번호
        accFlag = g_objCpTrade.GoodsList(acc, 1)  # 주식상품 구분
        print(acc, accFlag[0])
 
        self.objRq = win32com.client.Dispatch("CpTrade.CpTd6033")
        self.objRq.SetInputValue(0, acc)  # 계좌번호
        self.objRq.SetInputValue(1, accFlag[0])  # 상품구분 - 주식 상품 중 첫번째
        self.objRq.SetInputValue(2, 50)  # 요청 건수(최대 50)
 
    # 실제적인 6033 통신 처리
    def requestJango(self, caller):
        while True:
            ret = self.objRq.BlockRequest()
            if ret == 4 :
                remainTime = g_objCpStatus.LimitRequestRemainTime
                print('연속조회 제한 오류, 남은 시간', remainTime)
                return False
            # 통신 및 통신 에러 처리
            rqStatus = self.objRq.GetDibStatus()
            rqRet = self.objRq.GetDibMsg1()
            print("통신상태", rqStatus, rqRet)
            if rqStatus != 0:
                return False
 
            cnt = self.objRq.GetHeaderValue(7)
            print(cnt)
 
            for i in range(cnt):
                item = {}
                code = self.objRq.GetDataValue(12, i)  # 종목코드
                item['종목코드'] = code
                item['종목명'] = self.objRq.GetDataValue(0, i)  # 종목명
                item['현금신용'] = self.objRq.GetDataValue(1, i)  # 신용구분
                print(code, '현금신용', item['현금신용'])
                item['대출일'] = self.objRq.GetDataValue(2, i)  # 대출일
                item['잔고수량'] = self.objRq.GetDataValue(7, i)  # 체결잔고수량
                item['매도가능'] = self.objRq.GetDataValue(15, i)
                item['장부가'] = self.objRq.GetDataValue(17, i)  # 체결장부단가
                # 매입금액 = 장부가 * 잔고수량
                item['매입금액'] = item['장부가'] * item['잔고수량']
 
                # 잔고 추가
                caller.jangoData[code] = item
 
                if len(caller.jangoData) >= 200:  # 최대 200 종목만,
                    break
 
            if len(caller.jangoData) >= 200:
                break
            if (self.objRq.Continue == False):
                break
        return True
 
 
# 현재가 - 한종목 통신
class CpRPCurrentPrice:
    def __init__(self, caller):
        self.caller = caller
        self.objStockMst = win32com.client.Dispatch('DsCbo1.StockMst')
        return
 
    def Request(self, code):
        self.caller.curData[code] = {}
        self.objStockMst.SetInputValue(0, code)
        ret = self.objStockMst.BlockRequest()
        if self.objStockMst.GetDibStatus() != 0:
            print('통신상태', self.objStockMst.GetDibStatus(), self.objStockMst.GetDibMsg1())
            return False
 
        item = {}
        item['code'] = code
        #caller.curData['종목명'] = g_objCodeMgr.CodeToName(code)
        item['cur'] = self.objStockMst.GetHeaderValue(11)  # 종가
        item['diff'] = self.objStockMst.GetHeaderValue(12)  # 전일대비
        item['vol'] = self.objStockMst.GetHeaderValue(18)  # 거래량
 
        # 10차호가
        for i in range(10):
            key1 = 'offer%d' % (i + 1)
            key2 = 'bid%d' % (i + 1)
            item[key1] = (self.objStockMst.GetDataValue(0, i))  # 매도호가
            item[key2] = (self.objStockMst.GetDataValue(1, i))  # 매수호가
 
        self.caller.curData[code] = item
        return True
 
 
 
 
# 주식 주문 처리
class CpRPOrder:
    def __init__(self, caller):
        self.caller = caller
        self.acc = g_objCpTrade.AccountNumber[0]  # 계좌번호
        self.accFlag = g_objCpTrade.GoodsList(self.acc, 1)  # 주식상품 구분
        print(self.acc, self.accFlag[0])
        self.objOrder = win32com.client.Dispatch("CpTrade.CpTd0311")  # 매수
 
    def buyOrder(self, code, price, amount):
        # 주식 매수 주문
        print("신규 매수", code, price, amount)
 
        self.objOrder.SetInputValue(0, "2")  # 2: 매수
        self.objOrder.SetInputValue(1, self.acc)  # 계좌번호
        self.objOrder.SetInputValue(2, self.accFlag[0])  # 상품구분 - 주식 상품 중 첫번째
        self.objOrder.SetInputValue(3, code)  # 종목코드
        self.objOrder.SetInputValue(4, amount)  # 매수수량
        self.objOrder.SetInputValue(5, price)  # 주문단가
        self.objOrder.SetInputValue(7, "0")  # 주문 조건 구분 코드, 0: 기본 1: IOC 2:FOK
        self.objOrder.SetInputValue(8, "01")  # 주문호가 구분코드 - 01: 보통
 
        # 매수 주문 요청
        ret = self.objOrder.BlockRequest()
        if ret == 4:
            remainTime = g_objCpStatus.LimitRequestRemainTime
            print('주의: 주문 연속 통신 제한에 걸렸음. 대기해서 주문할 지 여부 판단이 필요 남은 시간', remainTime)
            return False
 
        rqStatus = self.objOrder.GetDibStatus()
        rqRet = self.objOrder.GetDibMsg1()
        print("통신상태", rqStatus, rqRet)
        if rqStatus != 0:
            return False
 
        return True
 
 
    def sellOrder(self, code, price, amount):
        # 주식 매도 주문
        print("신규 매도", code, price, amount)
 
        self.objOrder.SetInputValue(0, "1")  # 1: 매도
        self.objOrder.SetInputValue(1, self.acc)  # 계좌번호
        self.objOrder.SetInputValue(2, self.accFlag[0])  # 상품구분 - 주식 상품 중 첫번째
        self.objOrder.SetInputValue(3, code)  # 종목코드
        self.objOrder.SetInputValue(4, amount)  # 매수수량
        self.objOrder.SetInputValue(5, price)  # 주문단가
        self.objOrder.SetInputValue(7, "0")  # 주문 조건 구분 코드, 0: 기본 1: IOC 2:FOK
        self.objOrder.SetInputValue(8, "01")  # 주문호가 구분코드 - 01: 보통
 
        # 매도 주문 요청
        ret = self.objOrder.BlockRequest()
        if ret == 4:
            remainTime = g_objCpStatus.LimitRequestRemainTime
            print('주의: 주문 연속 통신 제한에 걸렸음. 대기해서 주문할 지 여부 판단이 필요 남은 시간', remainTime)
            return False
 
        rqStatus = self.objOrder.GetDibStatus()
        rqRet = self.objOrder.GetDibMsg1()
        print("통신상태", rqStatus, rqRet)
        if rqStatus != 0:
            return False
 
        return True
 
 
 
class MyWindow(QMainWindow):
    def __init__(self):
        super().__init__()
 
        # plus 상태 체크
        if InitPlusCheck() == False:
            exit()
 
        self.setWindowTitle("주식 잔고 일괄매도 예제")
 
        # 6033 잔고 object
        self.obj6033 = Cp6033()
        self.jangoData = {}
        self.objConclusion = CpPBConclusion()
 
        self.objRpCur = CpRPCurrentPrice(self)
        self.curData = {}
 
        self.objRpOrder = CpRPOrder(self)
 
        nH = 20
        btnSellAll = QPushButton('전체매도', self)
        btnSellAll.move(20, nH)
        btnSellAll.clicked.connect(self.btnSellAll_clicked)
        nH += 50
 
 
        btnExit = QPushButton('종료', self)
        btnExit.move(20, nH)
        btnExit.clicked.connect(self.btnExit_clicked)
        nH += 50
 
        self.setGeometry(300, 300, 300, nH)
 
    def btnSellAll_clicked(self):
        # 1. 잔고 요청
        self.objConclusion.Unsubscribe()
        if self.obj6033.requestJango(self) == False:
            return
        self.objConclusion.Subscribe('', self)
 
        for code, value in self.jangoData.items():
            print(code, value)
            # 2. 현재가 통신
            self.objRpCur.Request(code)
 
            # 3. 매수 5호가로 매도 주문
            if (self.curData[code]['bid5'] > 0) :
                jangoNum = value['잔고수량']
                amount = value['매도가능']
                if (jangoNum != amount):
                    print("경고: 미체결 수량이 있어 잔고와 매도 가능 수량이 다름", code, jangoNum, amount)
 
                price = self.curData[code]['bid5']
                self.objRpOrder.sellOrder(code, price, amount)
 
 
        return
 
    def btnExit_clicked(self):
        exit()
        return
 
 
    # 매도 후 실시간 주문 체결 받는 로직
    def updateJangoCont(self, pbCont):
        # 주문 체결에서 들어온 신용 구분 값 ==> 잔고 구분값으로 치환
        dicBorrow = {
               '현금': ord(' '),
               '유통융자': ord('Y'),
               '자기융자': ord('Y'),
               '주식담보대출': ord('B'),
               '채권담보대출': ord('B'),
               '매입담보대출': ord('M'),
               '플러스론': ord('P'),
               '자기대용융자': ord('I'),
               '유통대용융자': ord('I'),
               '기타' : ord('Z')
               }
 
        # 잔고 리스트 map 의 key 값
        code = pbCont['종목코드']
 
        # 접수, 거부, 확인 등은 매도 가능 수량만 업데이트 한다.
        if pbCont['체결플래그'] == '접수' or pbCont['체결플래그'] == '거부' or pbCont['체결플래그'] == '확인' :
            if (code not in self.jangoData) :
                return
            self.jangoData[code]['매도가능'] = pbCont['매도가능']
            return
 
        if (pbCont['체결플래그'] == '체결'):
            if (code not in self.jangoData) : # 신규 잔고 추가
                print('신규 잔고 추가', code)
                # 신규 잔고 추가
                item = {}
                item['종목코드'] = pbCont['종목코드']
                item['종목명'] = pbCont['종목명']
                item['현금신용'] = dicBorrow[pbCont['현금신용']]
                item['대출일'] = pbCont['대출일']
                item['잔고수량'] = pbCont['체결기준잔고수량']
                item['매도가능'] = pbCont['매도가능']
                item['장부가'] = pbCont['장부가']
                # 매입금액 = 장부가 * 잔고수량
                item['매입금액'] = item['장부가'] * item['잔고수량']
 
                self.jangoData[code] = item
 
            else:
                # 기존 잔고 업데이트
                item =self.jangoData[code]
                item['종목코드'] = pbCont['종목코드']
                item['종목명'] = pbCont['종목명']
                item['현금신용'] = dicBorrow[pbCont['현금신용']]
                item['대출일'] = pbCont['대출일']
                item['잔고수량'] = pbCont['체결기준잔고수량']
                item['매도가능'] = pbCont['매도가능']
                item['장부가'] = pbCont['장부가']
                # 매입금액 = 장부가 * 잔고수량
                item['매입금액'] = item['장부가'] * item['잔고수량']
 
                # 잔고 수량이 0 이면 잔고 제거
                if item['잔고수량'] == 0:
                    del self.jangoData[code]
                    print('매도 전부 체결', item['종목명'])
 
        return
 
 
 
if __name__ == "__main__":
    app = QApplication(sys.argv)
    myWindow = MyWindow()
    myWindow.show()
    app.exec_()

43 주식 IOC/FOK 주문 테스트 예제[편집]

주식 IOC/FOK 주문 및 실시간 주문 체결에 대한 간단한 예제입니다.


%EC%9D%B4%EB%AF%B8%EC%A7%80%20009.png


※ IOC 및 FOK 주문

 >> IOC (Immediate - Or-Cancel Order) : 주문즉시 체결 그리고 잔량 자동취소


   호가접수시점에서 호가한 수량 중 매매계약을 체결할 수 있는 수량에 대하여는 매매거래를 성립시키고,
   매매계약이 체결되지 아니한 수량은 취소하는 조건
>> FOK ( Fill- Or- kill Order) : 주문즉시 전부체결 또는 전부자동취소 
   호가의 접수시점에서 호가한 수량의 전부에 대햐여 매매계약을 체결할 수 있는 경우에는 매매거래를 
   성립시키고 그러하지 아니한 경우에는 당해수량의 전부를 취소하는 조건 
☞ 보통(지정가), 시장가, 최유리지정가에 한해서만 주문조건 부여가 가능합니다.


IOC 및 FOK 주문의 경우 주문 즉시 체결 또는 취소가 되는 주문으로 아래 예제 코드에서는 실시간 주문 체결에서 이를 확인할 수 있도록 로그를 남기고 있습니다.

신규 IOC 매수 주문을 낸 경우, 실시간 주문 체결은 접수 -> 취소 확인 순(체결이 되지 않을 경우) 으로 실시간 주문 체결이 오는 것을 아래 로그에서 확인할 수 있습니다.


신규 매수 주문조건구분: IOC 종목코드: A008800 가격: 474 수량: 1 통신상태 0 10766 매수주문이 접수되었습니다.(ordss.cststkord)

conclution {'체결플래그': '접수', '주문번호': 24337, '주문수량': 1, '주문가격': 474, '원주문': 0, '종목코드': 'A008800', '종목명': '행남생활건강', '매수매도': '매수', '신용대출': '해당없음', '정정취소': '정상주문', '현금신용': '현금', '주문조건': 'IOC', '체결기준잔고수량': 0, '대출일': 0, '주문호가구분': '보통', '매도가능수량': 0} conclution {'체결플래그': '확인', '주문번호': 24337, '주문수량': 1, '주문가격': 474, '원주문': 0, '종목코드': 'A008800', '종목명': '행남생활건강', '매수매도': '매수', '신용대출': '해당없음', '정정취소': '취소주문', '현금신용': '현금', '주문조건': 'IOC', '체결기준잔고수량': 0, '대출일': 0, '주문호가구분': '보통', '매도가능수량': 0}


※ 주의: 현재 모의투자 시스템에서는 IOC/FOK 주문을 지원하지 않습니다

※ 주의: 제공된 코드는 개발자의 코딩을 돕기 위한 단순 예제로만 제공 됩니다. 주문의 경우 반드시 사용자가 코드를 먼저 확인 후 주의 해서 테스트 하시기 바랍니다.

import sys
from PyQt5.QtWidgets import *
from enum import Enum
import win32com.client
 
g_objCpStatus = win32com.client.Dispatch('CpUtil.CpCybos')
g_objCpTrade = win32com.client.Dispatch('CpTrade.CpTdUtil')
g_objCodeMgr = win32com.client.Dispatch('CpUtil.CpCodeMgr')
 
# ioc 주문 테스트
 
 
# CpEvent: 실시간 이벤트 수신 클래스
class CpEvent:
    def set_params(self, client, name, parent):
        self.client = client  # CP 실시간 통신 object
        self.name = name  # 서비스가 다른 이벤트를 구분하기 위한 이름
        self.parent = parent  # callback 을 위해 보관
 
        # 구분값 : 텍스트로 변경하기 위해 딕셔너리 이용
        self.dicflag12 = {'1': '매도', '2': '매수'}
        self.dicflag14 = {'1': '체결', '2': '확인', '3': '거부', '4':'접수'}
        self.dicflag15 = {'00': '해당없음', '01': '유통융자', '02': '자기융자', '03': '유통대주',
                          '04':'자기대주', '05':'주식담보대출'}
        self.dicflag16 = {'1': '정상주문', '2': '정정주문', '3': '취소주문'}
        self.dicflag17 = {'1': '현금', '2': '신용', '3': '선물대용', '4': '공매도'}
        self.dicflag18 = {'01': '보통', '02': '임의', '03':'시장가', '05': '조건부지정가'}
        self.dicflag19 = {'0': '없음', '1': 'IOC', '2': 'FOK'}
 
 
    # PLUS 로 부터 실제로 시세를 수신 받는 이벤트 핸들러
    def OnReceived(self):
        print(self.name)
        if self.name == 'conclution':
            # 주문 체결 실시간 업데이트
            conc = {}
 
            # 체결 플래그
            conc['체결플래그'] = self.dicflag14[self.client.GetHeaderValue(14)]
 
            conc['주문번호'] = self.client.GetHeaderValue(5)  # 주문번호
            conc['주문수량'] = self.client.GetHeaderValue(3)  # 주문/체결 수량
            conc['주문가격'] = self.client.GetHeaderValue(4)  # 주문/체결 가격
            conc['원주문'] = self.client.GetHeaderValue(6)
            conc['종목코드'] = self.client.GetHeaderValue(9)  # 종목코드
            conc['종목명'] = g_objCodeMgr.CodeToName(conc['종목코드'])
 
            conc['매수매도'] = self.dicflag12[self.client.GetHeaderValue(12)]
 
            flag15  = self.client.GetHeaderValue(15) # 신용대출구분코드
            if (flag15 in self.dicflag15):
                conc['신용대출'] = self.dicflag15[flag15]
            else:
                conc['신용대출'] = '기타'
 
            conc['정정취소'] = self.dicflag16[self.client.GetHeaderValue(16)]
            conc['현금신용'] = self.dicflag17[self.client.GetHeaderValue(17)]
            conc['주문조건'] = self.dicflag19[self.client.GetHeaderValue(19)]
 
            conc['체결기준잔고수량'] = self.client.GetHeaderValue(23)
            conc['대출일'] = self.client.GetHeaderValue(20)
            flag18 = self.client.GetHeaderValue(18)
            if (flag18 in self.dicflag18):
                conc['주문호가구분'] = self.dicflag18[flag18]
            else:
                conc['주문호가구분'] = '기타'
 
            conc['매도가능수량'] = self.client.GetHeaderValue(22)
 
            print(conc)
 
            return
 
 
# CpPBConclusion: 실시간 주문 체결 수신 클래그
class CpPBConclusion:
    def __init__(self):
        self.name = 'conclution'
        self.obj = win32com.client.Dispatch('DsCbo1.CpConclusion')
 
    def Subscribe(self, parent):
        self.parent = parent
        handler = win32com.client.WithEvents(self.obj, CpEvent)
        handler.set_params(self.obj, self.name, parent)
        self.obj.Subscribe()
 
    def Unsubscribe(self):
        self.obj.Unsubscribe()
 
# CpRPCurrentPrice:  현재가 기본 정보 조회 클래스
class CpRPCurrentPrice:
    def __init__(self):
        if (g_objCpStatus.IsConnect == 0):
            print('PLUS가 정상적으로 연결되지 않음. ')
            return
        self.objStockMst = win32com.client.Dispatch('DsCbo1.StockMst')
        return
 
    def Request(self, code, caller):
        self.objStockMst.SetInputValue(0, code)
        ret = self.objStockMst.BlockRequest()
        if self.objStockMst.GetDibStatus() != 0:
            print('통신상태', self.objStockMst.GetDibStatus(), self.objStockMst.GetDibMsg1())
            return False
 
        caller.curData = {}
 
        caller.curData['code'] = code
        caller.curData['종목명'] = g_objCodeMgr.CodeToName(code)
        caller.curData['현재가'] =  self.objStockMst.GetHeaderValue(11)  # 종가
        caller.curData['대비'] =  self.objStockMst.GetHeaderValue(12)  # 전일대비
        caller.curData['기준가']  =  self.objStockMst.GetHeaderValue(27)  # 기준가
        caller.curData['거래량'] = self.objStockMst.GetHeaderValue(18)  # 거래량
        caller.curData['예상플래그'] = self.objStockMst.GetHeaderValue(58)  # 예상플래그
        caller.curData['예상체결가'] = self.objStockMst.GetHeaderValue(55)  # 예상체결가
        caller.curData['예상대비'] = self.objStockMst.GetHeaderValue(56)  # 예상체결대비
 
        # 10차호가
        for i in range(10):
            key1 = '매도호가%d' %(i+1)
            key2 = '매수호가%d' %(i+1)
            caller.curData[key1] = (self.objStockMst.GetDataValue(0, i))  # 매도호가
            caller.curData[key2] = (self.objStockMst.GetDataValue(1, i) ) # 매수호가
 
        #print(caller.curData)
 
        return True
 
class CpRPOrder:
    def __init__(self):
        # 연결 여부 체크
        if (g_objCpStatus.IsConnect == 0):
            print('PLUS가 정상적으로 연결되지 않음. ')
            return
 
        # 주문 초기화
        if (g_objCpTrade.TradeInit(0) != 0):
            print('주문 초기화 실패')
            return
 
        self.acc = g_objCpTrade.AccountNumber[0]  # 계좌번호
        self.accFlag = g_objCpTrade.GoodsList(self.acc, 1)  # 주식상품 구분
        print(self.acc, self.accFlag[0])
 
        # 주문 object 생성
        self.objBuySell = win32com.client.Dispatch('CpTrade.CpTd0311')  # 매수
 
        # 주문 조건 구분 '0': 없음 1: IOC 2: FOK
        self.dicFlag = {'IOC': '1', 'FOK': '2', '없음': '0'}
 
    def sellOrder(self, code, price, amount, flag):
        print('신규 매도', '주문조건구분:', flag, '종목코드:',code, '가격:',price, '수량:',amount)
 
        self.objBuySell.SetInputValue(0, '1')  # 1 매도 2 매수
        self.objBuySell.SetInputValue(1, self.acc)  # 계좌번호
        self.objBuySell.SetInputValue(2, self.accFlag[0])  # 상품구분 - 주식 상품 중 첫번째
        self.objBuySell.SetInputValue(3, code)  # 종목코드
        self.objBuySell.SetInputValue(4, amount)  # 수량
        self.objBuySell.SetInputValue(5, price)  # 주문단가
        orderflag = self.dicFlag[flag]          # 주문 조건 구분 '0': 없음 1: IOC 2: FOK
        self.objBuySell.SetInputValue(7, orderflag)  # 주문 조건 구분 코드, 0: 기본 1: IOC 2:FOK
        self.objBuySell.SetInputValue(8, '01')  # 주문호가 구분코드 - 01: 보통 03 시장가 05 조건부지정가
 
        # 주문 요청
        self.objBuySell.BlockRequest()
 
        rqStatus = self.objBuySell.GetDibStatus()
        rqRet = self.objBuySell.GetDibMsg1()
        print('통신상태', rqStatus, rqRet)
        if rqStatus != 0:
            return False
 
        return True
 
    def buyOrder(self, code, price, amount, flag):
        print('신규 매수', '주문조건구분:', flag, '종목코드:', code, '가격:', price, '수량:', amount)
 
        self.objBuySell.SetInputValue(0, '2')  # 1 매도 2 매수
        self.objBuySell.SetInputValue(1, self.acc)  # 계좌번호
        self.objBuySell.SetInputValue(2, self.accFlag[0])  # 상품구분 - 주식 상품 중 첫번째
        self.objBuySell.SetInputValue(3, code)  # 종목코드
        self.objBuySell.SetInputValue(4, amount)  # 수량
        self.objBuySell.SetInputValue(5, price)  # 주문단가
        orderflag = self.dicFlag[flag]          # 주문 조건 구분 '0': 없음 1: IOC 2: FOK
        self.objBuySell.SetInputValue(7, orderflag)  # 주문 조건 구분 코드, 0: 기본 1: IOC 2:FOK
        self.objBuySell.SetInputValue(8, '01')  # 주문호가 구분코드 - 01: 보통 03 시장가 05 조건부지정가
 
        # 주문 요청
        self.objBuySell.BlockRequest()
 
        rqStatus = self.objBuySell.GetDibStatus()
        rqRet = self.objBuySell.GetDibMsg1()
        print('통신상태', rqStatus, rqRet)
        if rqStatus != 0:
            return False
        return True
 
 
 
class MyWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('IOC/FOK 주문 테스트')
        self.setGeometry(300, 300, 300, 230)
 
        # 주문 통신 object
        self.objRpOrder = CpRPOrder()
        # 현재가 통신 object
        self.curData = {}
        self.objCur = CpRPCurrentPrice()
 
        # 주문체결은 미리 실시간 요청
        self.conclution = CpPBConclusion()
        self.conclution.Subscribe(self)
 
 
        nH = 20
        # 코드 입력기
        self.codeEdit = QLineEdit('', self)
        self.codeEdit.move(20, nH)
        self.codeEdit.textChanged.connect(self.codeEditChanged)
        self.codeEdit.setText('')
        self.label = QLabel('종목코드', self)
        self.label.move(140, nH)
        self.code = ''
        nH += 50
 
        btnIOCSell = QPushButton('매도IOC', self)
        btnIOCSell.move(20, nH)
        btnIOCSell.resize(200, 30)
        btnIOCSell.clicked.connect(self.btnIOCSell_clicked)
        nH += 50
 
        btnIOCBuy = QPushButton('매수IOC', self)
        btnIOCBuy.move(20, nH)
        btnIOCBuy.resize(200, 30)
        btnIOCBuy.clicked.connect(self.btnIOCBuy_clicked)
        nH += 50
 
        btnFOKSell = QPushButton('매도FOK', self)
        btnFOKSell.move(20, nH)
        btnFOKSell.resize(200, 30)
        btnFOKSell.clicked.connect(self.btnFOKSell_clicked)
        nH += 50
 
        btnFOKBuy = QPushButton('매수FOK', self)
        btnFOKBuy.move(20, nH)
        btnFOKBuy.resize(200, 30)
        btnFOKBuy.clicked.connect(self.btnFOKBuy_clicked)
        nH += 50
 
 
        btnExit = QPushButton('종료', self)
        btnExit.move(20, nH)
        btnExit.resize(200, 30)
        btnExit.clicked.connect(self.btnExit_clicked)
        nH += 50
 
        self.setGeometry(300, 300, 300, nH)
 
 
    def btnIOCSell_clicked(self):
        return self.sellOrder('IOC')
 
    def btnIOCBuy_clicked(self):
        return self.buyOrder('IOC')
 
 
    def btnFOKSell_clicked(self):
        return self.sellOrder('FOK')
 
    def btnFOKBuy_clicked(self):
        return self.buyOrder('FOK')
 
    # 종료
    def btnExit_clicked(self):
        exit()
 
 
    def codeEditChanged(self):
        code = self.codeEdit.text()
        self.setCode(code)
 
    def setCode(self, code):
        if len(code) < 6:
            return
 
        print(code)
        if not (code[0] == 'A'):
            code = 'A' + code
 
        name = g_objCodeMgr.CodeToName(code)
        if len(name) == 0:
            print('종목코드 확인')
            return
 
        self.label.setText(name)
        self.code = code
 
    # 매수는 매수3호가에 IOC/FOK 주문을 낸다. 
    def buyOrder(self, flag):
        # 현재가 통신
        if (self.objCur.Request(self.code, self) == False):
            w = QWidget()
            QMessageBox.warning(w, '오류', '현재가 통신 오류 발생/주문 중단')
            return
 
        self.objRpOrder.buyOrder(self.code, self.curData['매수호가3'], 1, flag)
 
    # 매도는 매도3호가에 IOC/FOK 주문을 낸다.
    def sellOrder(self, flag):
        # 현재가 통신
        if (self.objCur.Request(self.code, self) == False):
            w = QWidget()
            QMessageBox.warning(w, '오류', '현재가 통신 오류 발생/주문 중단')
            return
 
        self.objRpOrder.sellOrder(self.code, self.curData['매도호가3'], 1, flag)
 
 
if __name__ == '__main__':
    app = QApplication(sys.argv)
    myWindow = MyWindow()
    myWindow.show()
    app.exec_()

</python>

=== 종목검색 서비스를 이용하여 예제 전략 조건 조회하기 예제	===
HTS 의 종목검색(#8537) 서비스를 이용하면 원하는 조건에 해당하는 종목을 바로 바로 찾아 볼 수 있습니다.

이번 예제는 PLUS 에서 #8537 의 예제 전략을 가져와 조회 하는 예제입니다 

■ 사용된 PLUS OBJECT:
  - CpSysDib.CssStgList : 전략 리스트 조회 (예제 또는 사용자 전략 선택 가능)
  - CpSysDib.CssStgFind : 특정 전략조건에 해당하는 종목 리스트 조회 
https://money2.daishin.com/E5_Data/MBoard/PType_Basic/Basic/2017/11/%EC%9D%B4%EB%AF%B8%EC%A7%80%202.png

<source lang=python>
import sys
from PyQt5.QtWidgets import *
import win32com.client
import pandas as pd
import os
 
g_objCodeMgr = win32com.client.Dispatch('CpUtil.CpStockCode')
g_objCpStatus = win32com.client.Dispatch('CpUtil.CpCybos')
g_objCpTrade = win32com.client.Dispatch('CpTrade.CpTdUtil')
 
 
# Cp8537 : 종목검색 전략 조회
class Cp8537:
    def __init__(self):
        self.objRq = None
        return
 
    def requestList(self, caller):
        caller.data8537 = {}
        self.objRq = None
        self.objRq = win32com.client.Dispatch("CpSysDib.CssStgList")
 
        # 예제 전략에서 전략 리스트를 가져옵니다.
        self.objRq.SetInputValue(0, ord('0'))   # '0' : 예제전략, '1': 나의전략
        self.objRq.BlockRequest()
 
        # 통신 및 통신 에러 처리
        rqStatus = self.objRq.GetDibStatus()
        if rqStatus != 0:
            rqRet = self.objRq.GetDibMsg1()
            print("통신상태", rqStatus, rqRet)
            return False
 
        cnt = self.objRq.GetHeaderValue(0) # 0 - (long) 전략 목록 수
        flag = self.objRq.GetHeaderValue(1) # 1 - (char) 요청구분
        print('종목검색 전략수:', cnt)
 
 
        for i in range(cnt):
            item = {}
            item['전략명'] = self.objRq.GetDataValue(0, i)
            item['ID'] = self.objRq.GetDataValue(1, i)
            item['전략등록일시'] = self.objRq.GetDataValue(2, i)
            item['작성자필명'] = self.objRq.GetDataValue(3, i)
            item['평균종목수'] = self.objRq.GetDataValue(4, i)
            item['평균승률'] = self.objRq.GetDataValue(5, i)
            item['평균수익'] = self.objRq.GetDataValue(6, i)
            caller.data8537[item['전략명']] = item
            print(item)
            
        return True
 
    def requestStgID(self, id, caller):
        caller.dataStg = []
        self.objRq = None
        self.objRq = win32com.client.Dispatch("CpSysDib.CssStgFind")
        self.objRq.SetInputValue(0, id) # 전략 id 요청
        self.objRq.BlockRequest()
        # 통신 및 통신 에러 처리
        rqStatus = self.objRq.GetDibStatus()
        if rqStatus != 0:
            rqRet = self.objRq.GetDibMsg1()
            print("통신상태", rqStatus, rqRet)
            return False
 
        cnt = self.objRq.GetHeaderValue(0)  # 0 - (long) 검색된 결과 종목 수
        totcnt = self.objRq.GetHeaderValue(1)  # 1 - (long) 총 검색 종목 수
        stime = self.objRq.GetHeaderValue(2)  # 2 - (string) 검색시간
        print('검색된 종목수:', cnt, '전체종목수:', totcnt, '검색시간:', stime)
 
        for i in range(cnt):
            item = {}
            item['code'] = self.objRq.GetDataValue(0, i)
            item['종목명'] = g_objCodeMgr.CodeToName(item['code'])
            caller.dataStg.append(item)
 
        return True
 
class MyWindow(QMainWindow):
    def __init__(self):
        super().__init__()
 
        self.setWindowTitle("종목검색 예제")
        self.setGeometry(300, 300, 500, 180)
 
        self.obj8537 = Cp8537()
        self.data8537 = {}
        self.dataStg = []
 
        nH = 20
        btnOpt1 = QPushButton('전략리스트 조회', self)
        btnOpt1.move(20, nH)
        btnOpt1.clicked.connect(self.btnOpt1_clicked)
        nH += 50
 
        self.comboStg = QComboBox(self)
        self.comboStg.move(20, nH)
        self.comboStg.currentIndexChanged.connect(self.comboChanged)
        self.comboStg.resize(400, 30)
        nH += 50
 
        btnExit = QPushButton('종료', self)
        btnExit.move(20, nH)
        btnExit.clicked.connect(self.btnExit_clicked)
        nH += 50
        self.setGeometry(300, 300, 500, nH)
 
        self.btnOpt1_clicked()
 
 
    # 전략리스트 조회
    def btnOpt1_clicked(self):
        self.obj8537.requestList(self)
 
        for k, v in self.data8537.items():
            self.comboStg.addItem(k)
        return
 
 
 
    def comboChanged(self):
        cur = self.comboStg.currentText()
        print(cur)
        self.requestStgID(cur)
 
 
 
    def requestStgID(self, stgName):
        item = self.data8537[stgName]
        id = item['ID']
        name = item['전략명']
 
        self.obj8537.requestStgID(id, self)
 
        print('검색전략:', id, '전략명:', name, '검색종목수:', len(self.dataStg))
        for item in self.dataStg:
            print(item)
        return
 
    def btnExit_clicked(self):
        exit()
        return
 
 
 
if __name__ == "__main__":
    app = QApplication(sys.argv)
    myWindow = MyWindow()
    myWindow.show()
    app.exec_()

44 종목별 투자자 매매동향 (잠정)데이터[편집]

CpSysDib.CpSvr7210d 를 통해 "종목별 투자자 매매동향(잠정)데이터" 를 조회 합니다.

■ 사용된 PLUS OBJECT

  - CpSysDib.CpSvr7210d - 투자자 매매 동향(잠정) 데이터 조회 

■ 화면 설명

- 기관계 상위 : 기간계 상위 순으로 조회

- 외국인상위 : 외국인상위 순으로 조회

- print : 조회 내용 print

- 엑셀 내보내기 : 조회 내용 엑셀로 내보내기

import sys
from PyQt5.QtWidgets import *
import win32com.client
import pandas as pd
import os
 
g_objCodeMgr = win32com.client.Dispatch('CpUtil.CpStockCode')
g_objCpStatus = win32com.client.Dispatch('CpUtil.CpCybos')
g_objCpTrade = win32com.client.Dispatch('CpTrade.CpTdUtil')
 
 
# Cp7210 : 종목별 투자자 매매동향(잠정)데이터
class Cp7210:
    def __init__(self):
        self.objRq = None
        return
 
    def request(self, investFlag, caller):
        maxRqCont = 1
        rqCnt = 0
        caller.data7210 = []
        self.objRq = None
        self.objRq = win32com.client.Dispatch("CpSysDib.CpSvr7210d")
 
        while True:
            self.objRq.SetInputValue(0, '0')  # 0 전체 1 거래소 2 코스닥 3 업종 4 관심종목
            self.objRq.SetInputValue(1, ord('0')) # 0 수량 1 금액
            self.objRq.SetInputValue(2, investFlag) # 0 종목 1 외국인 2 기관계 3 보험기타 4 투신..
            self.objRq.SetInputValue(3, ord('0')) # 0 상위순 1 하위순
 
            self.objRq.BlockRequest()
            rqCnt += 1
 
            # 통신 및 통신 에러 처리
            rqStatus = self.objRq.GetDibStatus()
            rqRet = self.objRq.GetDibMsg1()
            print("통신상태", rqStatus, rqRet)
            if rqStatus != 0:
                return False
 
            cnt = self.objRq.GetHeaderValue(0)
            date = self.objRq.GetHeaderValue(1) # 집계날짜
            time = self.objRq.GetHeaderValue(2)  # 집계시간
            print(cnt)
 
            for i in range(cnt):
                item = {}
                item['code'] = self.objRq.GetDataValue(0, i)
                item['종목명'] = self.objRq.GetDataValue(1, i)
                item['현재가'] = self.objRq.GetDataValue(2, i)
                item['대비'] = self.objRq.GetDataValue(3, i)
                item['대비율'] = self.objRq.GetDataValue(4, i)
                item['거래량'] = self.objRq.GetDataValue(5, i)
                item['외국인'] = self.objRq.GetDataValue(6, i)
                item['기관계'] = self.objRq.GetDataValue(7, i)
                item['보험기타금융'] = self.objRq.GetDataValue(8, i)
                item['투신'] = self.objRq.GetDataValue(9, i)
                item['은행'] = self.objRq.GetDataValue(10, i)
                item['연기금'] = self.objRq.GetDataValue(11, i)
                item['국가지자체'] = self.objRq.GetDataValue(12, i)
                item['기타법인'] = self.objRq.GetDataValue(13, i)
 
                caller.data7210.append(item)
 
            if rqCnt >= maxRqCont:
                break
 
            if self.objRq.Continue == False:
                break
        return True
 
 
 
 
 
 
class MyWindow(QMainWindow):
    def __init__(self):
        super().__init__()
 
        self.setWindowTitle("종목별 투자자 매매동향(잠정)")
        self.setGeometry(300, 300, 300, 180)
 
        self.obj7210 = Cp7210()
        self.data7210 = []
 
        nH = 20
        btnOpt1 = QPushButton('기관계 상위', self)
        btnOpt1.move(20, nH)
        btnOpt1.clicked.connect(self.btnOpt1_clicked)
        nH += 50
 
        btnOpt2 = QPushButton('외국인 상위', self)
        btnOpt2.move(20, nH)
        btnOpt2.clicked.connect(self.btnOpt2_clicked)
        nH += 50
 
        btnPrint = QPushButton('print', self)
        btnPrint.move(20, nH)
        btnPrint.clicked.connect(self.btnPrint_clicked)
        nH += 50
 
        btnExcel = QPushButton('엑셀 내보내기', self)
        btnExcel.move(20, nH)
        btnExcel.clicked.connect(self.btnExcel_clicked)
        nH += 50
 
 
        btnExit = QPushButton('종료', self)
        btnExit.move(20, nH)
        btnExit.clicked.connect(self.btnExit_clicked)
        nH += 50
        self.setGeometry(300, 300, 300, nH)
 
 
    # 기관계 상위
    def btnOpt1_clicked(self):
        self.obj7210.request(2, self)
        return
 
    # 외국인 상위
    def btnOpt2_clicked(self):
        self.obj7210.request(1, self)
        return
 
 
    def btnPrint_clicked(self):
        for item in self.data7210:
            print(item)
        return
 
    # 엑셀 내보내기
    def btnExcel_clicked(self):
        excelfile = '7210.xlsx'
        df = pd.DataFrame(columns=['code','종목명', '현재가', '대비', '대비율', '거래량', '외국인', '기관계',
                                              '보험기타금융', '투신','은행','연기금','국가지자체','기타법인'])
 
        for item in self.data7210:
            df.loc[(len(df))] = item
 
        # create a Pandas Excel writer using XlsxWriter as the engine.
        writer = pd.ExcelWriter(excelfile, engine='xlsxwriter')
        # Convert the dataframe to an XlsxWriter Excel object.
        df.to_excel(writer, sheet_name='Sheet1')
        # Close the Pandas Excel writer and output the Excel file.
        writer.save()
        os.startfile(excelfile)
        return
 
    def btnExit_clicked(self):
        exit()
        return
 
 
 
if __name__ == "__main__":
    app = QApplication(sys.argv)
    myWindow = MyWindow()
    myWindow.show()
    app.exec_()

45 주식 분할 주문 예제[편집]

46 해외선물 잔고(미결제약정)과 실시간 주문체결 처리 예제[편집]