Server Programming/Python

대신증권 Python API 활용하기

Dev.BeryL 2022. 1. 28. 10:41
728x90

대신증권에서 거래, 정보 등을 하기 위한 API를 제공해준다.

 

몇가지 코드의 예시가 있다.

순서

1. 대신증권 CYBOS 5 HTS 설치

2. 대신증권 CYBOS Plus 설치 

 

대신증권은 Python 32비트를 지원하기에 64비트에서는 오류가 날 수 있다.

 

CYBOS 5 연결 코드

requierments : pypiwin32

#CYBOS Plus connection 확인

import win32com.client

objCpCybos = win32com.client.Dispatch("CpUtil.CpCybos")
bConnect = objCpCybos.isConnect
if(bConnect == 0):
	print("연결 실패")
	exit()

 

기본 현금 매수 및 매도 요청 코드

import win32com.client
 
 
# 연결 여부 체크
objCpCybos = win32com.client.Dispatch("CpUtil.CpCybos")
bConnect = objCpCybos.IsConnect
if (bConnect == 0):
    print("PLUS가 정상적으로 연결되지 않음. ")
    exit()
 
# 주문 초기화
objTrade =  win32com.client.Dispatch("CpTrade.CpTdUtil")
initCheck = objTrade.TradeInit(0)
if (initCheck != 0):
    print("주문 초기화 실패")
    exit()
 
 
# 주식 매수 주문
acc = objTrade.AccountNumber[0] #계좌번호
accFlag = objTrade.GoodsList(acc, 1)  # 주식상품 구분
print(acc, accFlag[0])
objStockOrder = win32com.client.Dispatch("CpTrade.CpTd0311")
objStockOrder.SetInputValue(0, "2")   # 2: 매수
objStockOrder.SetInputValue(1, acc )   #  계좌번호
objStockOrder.SetInputValue(2, accFlag[0])   # 상품구분 - 주식 상품 중 첫번째
objStockOrder.SetInputValue(3, "A003540")   # 종목코드 - A003540 - 대신증권 종목
objStockOrder.SetInputValue(4, 10)   # 매수수량 10주
objStockOrder.SetInputValue(5, 14100)   # 주문단가  - 14,100원
objStockOrder.SetInputValue(7, "0")   # 주문 조건 구분 코드, 0: 기본 1: IOC 2:FOK
objStockOrder.SetInputValue(8, "01")   # 주문호가 구분코드 - 01: 보통
 
# 매수 주문 요청
objStockOrder.BlockRequest()
 
rqStatus = objStockOrder.GetDibStatus()
rqRet = objStockOrder.GetDibMsg1()
print("통신상태", rqStatus, rqRet)
if rqStatus != 0:
    exit()


--------------------------------------

import win32com.client
 
 
# 연결 여부 체크
objCpCybos = win32com.client.Dispatch("CpUtil.CpCybos")
bConnect = objCpCybos.IsConnect
if (bConnect == 0):
    print("PLUS가 정상적으로 연결되지 않음. ")
    exit()
 
# 주문 초기화
objTrade =  win32com.client.Dispatch("CpTrade.CpTdUtil")
initCheck = objTrade.TradeInit(0)
if (initCheck != 0):
    print("주문 초기화 실패")
    exit()
 
 
# 주식 매도 주문
acc = objTrade.AccountNumber[0] #계좌번호
accFlag = objTrade.GoodsList(acc, 1)  # 주식상품 구분
print(acc, accFlag[0])
objStockOrder = win32com.client.Dispatch("CpTrade.CpTd0311")
objStockOrder.SetInputValue(0, "1")   #  1: 매도
objStockOrder.SetInputValue(1, acc )   #  계좌번호
objStockOrder.SetInputValue(2, accFlag[0])   #  상품구분 - 주식 상품 중 첫번째
objStockOrder.SetInputValue(3, "A003540")   #  종목코드 - A003540 - 대신증권 종목
objStockOrder.SetInputValue(4, 10)   #  매도수량 10주
objStockOrder.SetInputValue(5, 14100)   #  주문단가  - 14,100원
objStockOrder.SetInputValue(7, "0")   #  주문 조건 구분 코드, 0: 기본 1: IOC 2:FOK
objStockOrder.SetInputValue(8, "01")   # 주문호가 구분코드 - 01: 보통
 
# 매도 주문 요청
objStockOrder.BlockRequest()
 
rqStatus = objStockOrder.GetDibStatus()
rqRet = objStockOrder.GetDibMsg1()
print("통신상태", rqStatus, rqRet)
if rqStatus != 0:
    exit()

 

주문 체결 실시간 처리

import sys
from PyQt5.QtWidgets import *
from enum import Enum
import win32com.client
 
# 설명: 주식 한 종목의 매수/정정/취소 주문 처리 및 실시간 시세와 주문 체결 처리 예제
#   매수주문 - 현재가/10차 호가를 구해 10차 호가로 매수 주문 냄
#   정정주문 - 누를 때 마다 호가를 9차 > 8차 > 7차 식으로 올려 정정주문 (가격은 실시간으로 업데이트 된 가격임)
#   취소주문 - 취소 주문
 
# CpEvent: 실시간 현재가 수신 클래스 - 아래 3가지 실시간 시세 수신
#       실시간 체결 현재가
#       실시간 10차 호가
#       실시간 주문 체결
# CpPBStockCur : (실시간)현재가 체결 요청 클래스
# CpPBStockBid : (실시간)현재가 10차 호가 요청 클래스
# CpPBConclusion : (실시간)주문체결 데이터 요청 클래스
# CpRPOrder : (RQ/RP)주식 매수/매도/정정 통신 클래스
# CpRPCurrentPrice : (RQ/RP)주식 현재가 통신 클래스
# OrderMain : 주문/체결에 대한 핵심 처리 클래스
#       매수/정정/취소 주문 버튼 클릭에 대한 이벤트 처리
#       실시간 주문 체결 업데이트에 따른 주문 상태 업데이트
 
 
# enum 주문 상태 세팅용
class orderStatus(Enum):
    nothing = 1          # 별 일 없는 상태
    newOrder = 2          # 신규 주문 낸 상태
    orderConfirm = 3      # 신규 주문 처리 확인
    modifyOrder = 4     # 정정 주문 낸 상태
    cancelOrder = 5      # 취소 주문 낸 상태
 
# 현재가와 10차 호가를 저장하기 위한 단순 저장소
class stockPricedData:
    def __init__(self):
        self.cur = 0        # 현재가
        self.offer = []     # 매도호가
        self.bid = []       # 매수호가
 
# 주문 체결 pb 기록용(종료 시 받은 데이터 print)
class orderHistoryData:
    def __init__(self):
        self.flag = ""
        self.code = ""
        self.price = 0
        self.orderamount = 0
        self.contamount = 0
        self.etc = ""
 
    def sethistory(self, flag, code, price, amount, contamount, ordernum, etc):
        self.flag = flag
        self.code = code
        self.price = price
        self.orderamount = amount
        self.contamount = contamount
        self.ordernum = ordernum
        self.etc = etc
 
    def printhistory(self):
        print(self.flag, self.code, "가격:", self.price, "수량:", self.orderamount, "체결수량:", self.contamount, "주문번호:", self.ordernum, self.etc)
 
 
 
# 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" : "접수"}
        self.buyselldic = {"1" : "매도", "2" : "매수"}
        print(self.concdic)
        print(self.buyselldic)
 
    # PLUS 로 부터 실제로 시세를 수신 받는 이벤트 핸들러 
    def OnReceived(self):
        print(self.name)
        if self.name == "stockcur" :
            # 현재가 체결 데이터 실시간 업데이트
            exFlag = self.client.GetHeaderValue(19)  # 예상체결 플래그
            cprice = self.client.GetHeaderValue(13)  # 현재가
            # 장중이 아니면 처리 안함.
            if exFlag != ord('2'):
                return
 
            # 현재가 업데이트
            self.parent.sprice.cur = cprice
            print("PB > 현재가 업데이트 : ", cprice)
 
            # 현재가 변경  call back 함수 호출
            self.parent.monitorPriceChange()
 
            return
 
        elif self.name == "stockbid" :
            # 현재가 10차 호가 데이터 실시간 업데이트
            dataindex = [3,4,7,8,11,12, 15,16, 19, 20, 27,28, 31,32,35,36,39,40,43,44]
            obi = 0
            for i in range(10):
                self.parent.sprice.offer[i] = self.client.GetHeaderValue(dataindex[obi])
                self.parent.sprice.bid[i] = self.client.GetHeaderValue(dataindex[obi + 1])
                obi += 2
 
            # for debug
            for i in range(10):
                print("PB > 10차 호가 : ",i + 1, "차 매도/매수 호가: ", self.parent.sprice.offer[i], self.parent.sprice.bid[i])
            return True
 
            # 10차 호가 변경 call back 함수 호출
            self.parent.monitorPriceChange()
 
            return
 
        elif self.name == "conclution" :
            # 주문 체결 실시간 업데이트
            conflag = self.client.GetHeaderValue(14)    # 체결 플래그
            ordernum = self.client.GetHeaderValue(5)    # 주문번호
            amount = self.client.GetHeaderValue(3)      # 체결 수량
            price = self.client.GetHeaderValue(4)       # 가격
            code = self.client.GetHeaderValue(9)        # 종목코드
            bs = self.client.GetHeaderValue(12)         # 매수/매도 구분
            balace = self.client.GetHeaderValue(23)  # 체결 후 잔고 수량
 
            conflags = ""
            if conflag in self.concdic :
                conflags = self.concdic.get(conflag)
                print(conflags)
 
            bss = ""
            if (bs in self.buyselldic):
                bss = self.buyselldic.get(bs)
 
            print(conflags, bss, code, "주문번호:", ordernum)
            # call back 함수 호출해서 orderMain 에서 후속 처리 하게 한다.
            self.parent.monitorOrderStatus(code, ordernum, conflags, price, amount, balace)
            return
 
 
# CpPBStockCur: 실시간 현재가 요청 클래스
class CpPBStockCur:
    def __init__(self):
        self.name = "stockcur"
        self.obj = win32com.client.Dispatch("DsCbo1.StockCur")
 
    def Subscribe(self, code, sprice, parent):
        self.obj.SetInputValue(0, code)
        handler = win32com.client.WithEvents(self.obj, CpEvent)
        handler.set_params(self.obj, self.name, parent)
        self.obj.Subscribe()
        self.sprice = sprice
 
    def Unsubscribe(self):
        self.obj.Unsubscribe()
 
# CpPBStockBid: 실시간 10차 호가 요청 클래스
class CpPBStockBid:
    def __init__(self):
        self.name = "stockbid"
        self.obj = win32com.client.Dispatch("Dscbo1.StockJpBid")
 
    def Subscribe(self, code, sprice, parent):
        self.obj.SetInputValue(0, code)
        handler = win32com.client.WithEvents(self.obj, CpEvent)
        handler.set_params(self.obj, self.name, parent)
        self.obj.Subscribe()
        self.sprice = sprice
 
 
    def Unsubscribe(self):
        self.obj.Unsubscribe()
 
# 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()
 
class CpRPOrder:
    def __init__(self):
        # 연결 여부 체크
        self.objCpCybos = win32com.client.Dispatch("CpUtil.CpCybos")
        bConnect = self.objCpCybos.IsConnect
        if (bConnect == 0):
            print("PLUS가 정상적으로 연결되지 않음. ")
            return
 
        # 주문 초기화
        self.objTrade = win32com.client.Dispatch("CpTrade.CpTdUtil")
        initCheck = self.objTrade.TradeInit(0)
        if (initCheck != 0):
            print("주문 초기화 실패")
            return
        
        self.acc = self.objTrade.AccountNumber[0]  # 계좌번호
        self.accFlag = self.objTrade.GoodsList(self.acc, 1)  # 주식상품 구분
        print(self.acc, self.accFlag[0])
 
        # 매수/정정/취소 주문 object 생성
        self.objBuyOrder = win32com.client.Dispatch("CpTrade.CpTd0311")     # 매수
        self.objModifyOrder = win32com.client.Dispatch("CpTrade.CpTd0313")  # 정정
        self.objCancelOrder = win32com.client.Dispatch("CpTrade.CpTd0314")  # 취소
        self.orderNum = 0 # 주문 번호
 
    def buyOrder(self, code, price, amount):
        # 주식 매수 주문
        print("신규 매수", code, price, amount)
 
        self.objBuyOrder.SetInputValue(0, "2")  # 2: 매수
        self.objBuyOrder.SetInputValue(1, self.acc)  # 계좌번호
        self.objBuyOrder.SetInputValue(2, self.accFlag[0])  # 상품구분 - 주식 상품 중 첫번째
        self.objBuyOrder.SetInputValue(3, code)  # 종목코드
        self.objBuyOrder.SetInputValue(4, amount)  # 매수수량
        self.objBuyOrder.SetInputValue(5, price)  # 주문단가 
        self.objBuyOrder.SetInputValue(7, "0")  # 주문 조건 구분 코드, 0: 기본 1: IOC 2:FOK
        self.objBuyOrder.SetInputValue(8, "01")  # 주문호가 구분코드 - 01: 보통
 
        # 매수 주문 요청
        self.objBuyOrder.BlockRequest()
 
        rqStatus = self.objBuyOrder.GetDibStatus()
        rqRet = self.objBuyOrder.GetDibMsg1()
        print("통신상태", rqStatus, rqRet)
        if rqStatus != 0:
            return False
 
        # 주의: 매수 주문에  대한 구체적인 처리는 cpconclution 으로 파악해야 한다.
        return True
 
    def modifyOrder(self, ordernum, code, price):
        # 주식 정정 주문
        print("정정주문", ordernum, code, price)
        self.objModifyOrder.SetInputValue(1, ordernum)  #  원주문 번호 - 정정을 하려는 주문 번호
        self.objModifyOrder.SetInputValue(2, self.acc)  # 상품구분 - 주식 상품 중 첫번째
        self.objModifyOrder.SetInputValue(3, self.accFlag[0])  # 상품구분 - 주식 상품 중 첫번째
        self.objModifyOrder.SetInputValue(4, code)  # 종목코드
        self.objModifyOrder.SetInputValue(5, 0)  # 정정 수량, 0 이면 잔량 정정임
        self.objModifyOrder.SetInputValue(6, price)  #  정정주문단가
 
        # 정정주문 요청
        self.objModifyOrder.BlockRequest()
 
        rqStatus = self.objModifyOrder.GetDibStatus()
        rqRet = self.objModifyOrder.GetDibMsg1()
        print("통신상태", rqStatus, rqRet)
        if rqStatus != 0:
            return False
 
        # 새로운 주문 번호 구한다.
        self.orderNum = self.objModifyOrder.GetHeaderValue(7)
 
    def cancelOrder(self, ordernum, code):
        # 주식 취소 주문
        print("취소주문", ordernum, code)
        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, 0)  # 정정 수량, 0 이면 잔량 취소임
 
        # 취소주문 요청
        self.objCancelOrder.BlockRequest()
 
        rqStatus = self.objCancelOrder.GetDibStatus()
        rqRet = self.objCancelOrder.GetDibMsg1()
        print("통신상태", rqStatus, rqRet)
        if rqStatus != 0:
            return False
 
# CpRPCurrentPrice : 주식 현재가 및 10차 호가 조회
class CpRPCurrentPrice:
    def __init__(self):
        self.objCpCybos = win32com.client.Dispatch("CpUtil.CpCybos")
        bConnect = self.objCpCybos.IsConnect
        if (bConnect == 0):
            print("PLUS가 정상적으로 연결되지 않음. ")
            return
        self.objStockMst = win32com.client.Dispatch("DsCbo1.StockMst")
        self.objStockjpbid = win32com.client.Dispatch("DsCbo1.StockJpBid2")
 
        return
 
 
    def Request(self, code, rtMst):
        # 현재가 통신
        self.objStockMst.SetInputValue(0, code)
        self.objStockMst.BlockRequest()
 
        # 10차 호가 통신
        self.objStockjpbid.SetInputValue(0, code)
        self.objStockjpbid.BlockRequest()
 
        print("통신상태", self.objStockMst.GetDibStatus(), self.objStockMst.GetDibMsg1())
        if self.objStockMst.GetDibStatus() != 0:
            return False
        print("통신상태", self.objStockjpbid.GetDibStatus(), self.objStockjpbid.GetDibMsg1())
        if self.objStockjpbid.GetDibStatus() != 0:
            return False
 
        # 수신 받은 현재가 정보를 rtMst 에 저장
        rtMst.cur =  self.objStockMst.GetHeaderValue(11)  # 종가
        # 10차호가
        for i in range(10):
            rtMst.offer.append(self.objStockjpbid.GetDataValue(0, i))  # 매도호가
            rtMst.bid.append(self.objStockjpbid.GetDataValue(1, i) ) # 매수호가
 
        # for debug
        for i in range(10):
            print(i+1, "차 매도/매수 호가: ", rtMst.offer[i], rtMst.bid[i])
        return True
 
 
# 주문 테스트용 클래스
class OrderMain() :
    def __init__(self):
        self.isSB = False   # 실시간 처리        
        self.initOrder()    # 주문 상태 - 초기화
 
        self.sprice = stockPricedData()  # 주문 현재가/10차 호가 저장 (실시간 업데이트)
        self.cporder = CpRPOrder()    # 주문 통신 object
 
        # 실시간 통신 object
        self.cur = CpPBStockCur()
        self.bid = CpPBStockBid()
 
        # 주문체결은 미리 실시간 요청
        self.conclution = CpPBConclusion()
        self.conclution.Subscribe(self)
 
        self.history = []
 
    def stopSubscribe(self):
        if self.isSB:
            self.cur.Unsubscribe()
            self.bid.Unsubscribe()
 
        self.isSB = False
 
    # 매수 주문
    def BuyOrder(self):
        self.stopSubscribe()
        self.code = "A003540"  # 테스트용 종목코드
        self.buyamount = 1     # 주문 수량
 
        # 1. 현재가 구하기
        price = CpRPCurrentPrice()
        if price.Request(self.code, self.sprice) == False:
            print("현재가 통신 실패")
            self.initOrder()
            return
 
 
        print("신규 매수주문 - ", self.orderNonce + 1, "차 매수호가 ", + self.sprice.bid[self.orderNonce])
        bResult = self.cporder.buyOrder(self.code,self.sprice.bid[self.orderNonce], self.buyamount)
        if bResult == False:
            print("주문 실패")
            self.initOrder()
            return
        self.orderStatus = orderStatus.newOrder  # 주문상태 업데이트
 
        # 실시간 통신 요청
        self.cur.Subscribe(self.code, self.sprice, self)
        self.bid.Subscribe(self.code, self.sprice, self)
        self.isSB = True
 
    # 정정주문
    def ModifyOrder(self):
        if not(self.orderStatus == orderStatus.orderConfirm):
            print("정정주문 확인 불가 상태 ")
            return
 
        if self.ordernum == 0:
            print("주문 번호가 없습니다")
            return
 
        # 정정주문 할 때 마다 1 호가씩 올린다.
        self.orderNonce -= 1
        if self.orderNonce <= 0:
            self.orderNonce = 0
        print("정정 주문 - ", self.orderNonce + 1, "차 매수호가 ", + self.sprice.bid[self.orderNonce])
        bResult = self.cporder.modifyOrder(self.ordernum, self.code, self.sprice.bid[self.orderNonce])
        if  bResult== False:
            print("정정 주문 실패")
            return
 
        # 주문상태 업데이트
        self.orderStatus = orderStatus.modifyOrder
 
        # 정정주문은 거래소에서 거부 당할 수 있어 확인/거부 여부를 반드시 확인 해야 함.
 
        return
 
 
    # 취소주문
    def CancelOrder(self):
        if not(self.orderStatus == orderStatus.orderConfirm):
            print("취소주문 확인 불가 상태 ")
            return
 
        if self.ordernum == 0:
            print("주문 번호가 없습니다")
            return
        # 취소 주문
        bResult = self.cporder.cancelOrder(self.ordernum, self.code)
        if bResult == False:
            print("취소 주문 실패")
            return
 
        self.orderStatus = orderStatus.cancelOrder  # 주문상태 업데이트
        # 취소주문은 거래소에서 거부 당할 수 있어 확인/거부 여부를 반드시 확인 해야 함.
 
        return
 
    # 전체 클리어
    def clearAll(self):
        self.initOrder()
        self.stopSubscribe()
        self.conclution.Unsubscribe()
 
        # debug
        if (len(self.history)) :
            print("주문 내역 정리 ============================")
            for i in range(0, len(self.history)) :
                self.history[i].printhistory()
 
        self.history = []
 
        return
 
    def initOrder(self):
        # 주문 정보 초기화
        self.orderStatus = orderStatus.nothing
        self.ordernum = 0        # 주문번호
        self.remainAmount = 0 # 주문 후 미체결 수량
        self.orderNonce = 9     # 매수 주문 호가 조정 변수 ( 9 > 8 > 7 .. 순으로 호가 조정)
 
    def monitorPriceChange(self):
        # 이곳에서 시세 변경에 대한 감시 등의 로직 추가고려
 
        return
 
 
    # 실시간 주문 체결 업데이트
    def monitorOrderStatus(self, code, ordernum, conflags, price, amount, balance) :
        print("주문체결: ", code, ordernum, conflags, price, amount, balance)
        if self.orderStatus == orderStatus.nothing :
            return
        # 체결: 체결 시 체결 수량/미체결 수량 계산
        if conflags == "체결":
            self.remainAmount -= amount   # 미체결 수량 계산
            if self.orderStatus == orderStatus.orderConfirm :
                print("주문 체결 됨 ", "수량", amount, "잔고수량:", balance, "미체결수량:", self.remainAmount)
 
            if self.remainAmount <= 0 :   # 전량 체결 됨
                self.initOrder()
 
            # for debug
            history = orderHistoryData()
            history.sethistory(conflags, code, price, self.remainAmount, amount, ordernum, "")
            self.history.append(history)
 
 
        #  접수: 신규 주문 > 접수 ;--> 주문번호, 주문 정상 처리
        elif conflags == "접수":
            if self.orderStatus == orderStatus.newOrder:
                self.ordernum = ordernum  # 주문번호 업데이트
                self.remainAmount = amount  # 주문 후 미체결 수량
                self.orderStatus = orderStatus.orderConfirm
 
                # for debug
                history = orderHistoryData()
                history.sethistory(conflags, code, price, amount, 0, ordernum, "신규 매수")
                self.history.append(history)
                history.printhistory()
 
        #  확인: 정정/취소 주문 > 확인 ;--> 정정/취소 주문 정상 처리 확인
        elif conflags == "확인":
            etc = ""
            if self.orderStatus == orderStatus.modifyOrder:     # 정정 확인
                self.ordernum = ordernum  # 주문번호 업데이트
                self.orderStatus = orderStatus.orderConfirm
                etc  = "정정확인"
            elif self.orderStatus == orderStatus.cancelOrder:    # 취소 확인
                self.initOrder()
                etc  = "취소확인"
 
            # for debug
            history = orderHistoryData()
            print(code, price)
            print(self.remainAmount, ordernum)
            history.sethistory(conflags, code, price, self.remainAmount, 0,  ordernum, etc)
            self.history.append(history)
            history.printhistory()
 
 
        # 거부: 정정/취소 주문 > 거부 ;--> 정정/취소 주문 거부, 정정/취소 불가
        elif conflags == "거부":
            if self.orderStatus == orderStatus.modifyOrder or self.orderStatus == orderStatus.cancelOrder:
                print("주문거부 발생, 반드시 확인 필요")
                self.orderStatus = orderStatus.newOrder  # 주문 상태를 이전으로 돌림
 
            # for debug
            history = orderHistoryData()
            history.sethistory(conflags, code, price, amount, 0,  ordernum, "")
            self.history.append(history)
            history.printhistory()
 
 
        return
 
 
class MyWindow(QMainWindow):
 
    def __init__(self):
        self.orerMain = OrderMain()
        super().__init__()
        self.setWindowTitle("PLUS API TEST")
        self.setGeometry(300, 300, 300, 230)
 
        btnBuy = QPushButton("매수주문", self)
        btnBuy.move(20, 20)
        btnBuy.resize(200,30)
        btnBuy.clicked.connect(self.btnBuy_clicked)
 
        btnModify = QPushButton("정정주문(1호가씩 올림)", self)
        btnModify.move(20, 70)
        btnModify.resize(200,30)
        btnModify.clicked.connect(self.btnModify_clicked)
 
        btnCancel = QPushButton("취소주문", self)
        btnCancel.move(20, 120)
        btnCancel.resize(200,30)
        btnCancel.clicked.connect(self.btnCancel_clicked)
 
        btnExit = QPushButton("종료", self)
        btnExit.move(20, 170)
        btnExit.resize(200,30)
        btnExit.clicked.connect(self.btnExit_clicked)
 
 
    # 매수 주문
    def btnBuy_clicked(self):
        self.orerMain.BuyOrder()
 
    # 정정주문
    def btnModify_clicked(self):
        self.orerMain.ModifyOrder()
        return
 
 
    # 취소주문
    def btnCancel_clicked(self):
        self.orerMain.CancelOrder()
 
    # 종료
    def btnExit_clicked(self):
        self.orerMain.clearAll()
        exit()
 
 
if __name__ == "__main__":
    app = QApplication(sys.argv)
    myWindow = MyWindow()
    myWindow.show()
    app.exec_()

 

보유 주식 잔고 전량 매도주문

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_()
반응형