Created
November 17, 2017 07:25
-
-
Save esstory/6c1c0cdc37fa2eaa612ce61dafa28c99 to your computer and use it in GitHub Desktop.
CYBOS/CREON PLUS - 해외선물 잔고 실시간 잔고(잔고 및 현재가 실시간 반영) 예제
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sys | |
from PyQt5.QtWidgets import * | |
import win32com.client | |
from pandas import Series, DataFrame | |
import pandas as pd | |
import locale | |
import os | |
locale.setlocale(locale.LC_ALL, '') | |
# cp object | |
g_objCpStatus = win32com.client.Dispatch('CpUtil.CpCybos') | |
g_objCpTrade = win32com.client.Dispatch('CpTrade.CpTdUtil') | |
g_objCodeMgr = win32com.client.Dispatch("CpUtil.CpCodeMgr") | |
gExcelFile = 'ovfuturejango.xlsx' | |
# 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 == "ovfjango": | |
pbdata = {} | |
pbdata["처리구분"] = self.client.GetHeaderValue(2) # “00”-주문, “01”-정정, “02”-취소, “03”-체결 | |
pbdata["code"] = self.client.GetHeaderValue(6) # 종목코드 | |
pbdata["종목명"] = self.client.GetHeaderValue(7) # 종목코드 | |
pbdata["매매구분"] = self.client.GetHeaderValue(8) # 매매구분 | |
pbdata["잔고수량"] = self.client.GetHeaderValue(9) # 잔고수량 | |
pbdata["단가"] = self.client.GetHeaderValue(10) # 단가 | |
pbdata["청산가능"] = self.client.GetHeaderValue(11) # 청산가능수량 | |
pbdata["미체결수량"] = self.client.GetHeaderValue(12) # 미체결수량 | |
pbdata["현재가"] = self.client.GetHeaderValue(13) # 현재가 | |
pbdata["대비부호"] = self.client.GetHeaderValue(14) # 전일대비부호 | |
pbdata["전일대비"] = self.client.GetHeaderValue(15) # 전일대비 | |
pbdata["전일대비율"] = self.client.GetHeaderValue(16) # 전일대비율 | |
pbdata["평가금액"] = self.client.GetHeaderValue(17) # 평가금액 | |
pbdata["평가손익"] = self.client.GetHeaderValue(18) # 평가손익 | |
pbdata["손익률"] = self.client.GetHeaderValue(19) # 손익률 | |
pbdata["매입금액"] = self.client.GetHeaderValue(20) # 매입금액 | |
pbdata["승수"] = self.client.GetHeaderValue(21) # 승수 | |
pbdata["통화코드"] = self.client.GetHeaderValue(23) # 통화코드 | |
print(pbdata) | |
self.caller.updateContract(pbdata) | |
if self.name == "ovfucur": | |
# 현재가 체결 데이터 실시간 업데이트 | |
code = self.client.GetHeaderValue(0) | |
self.caller.mstData[code]['cur'] = self.client.GetHeaderValue(7) | |
self.caller.mstData[code]['open'] = self.client.GetHeaderValue(14) | |
self.caller.mstData[code]['high'] = self.client.GetHeaderValue(15) | |
self.caller.mstData[code]['low'] = self.client.GetHeaderValue(16) | |
self.caller.mstData[code]['offer'] = self.client.GetHeaderValue(22) # 매도호가 | |
self.caller.mstData[code]['bid'] = self.client.GetHeaderValue(23) # 매수호가 | |
self.caller.mstData[code]['diff'] = self.client.GetHeaderValue(9) # 대비 | |
self.caller.mstData[code]['vol'] = self.client.GetHeaderValue(11) # 거래량 | |
print('체결실시간', self.caller.mstData[code]) | |
self.caller.updatePrice(code) | |
elif self.name == "ovfubid": | |
code = self.client.GetHeaderValue(0) | |
sindx = 5 | |
for i in range(1, 6): | |
offerkey = 'offer' + str(i) | |
bidkey = 'bid' + str(i) | |
self.caller.mstData[code][offerkey] = self.client.GetHeaderValue(sindx) | |
self.caller.mstData[code][bidkey] = self.client.GetHeaderValue(sindx + 3) | |
sindx += 6 | |
#print('5차호가실시간', self.caller.mstData[code]) | |
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 | |
class CpPBOvfJango(CpPublish): | |
def __init__(self): | |
super().__init__('ovfjango', 'CpForeDib.OvFutBalance') | |
class CpPBOvFuCur(CpPublish): | |
def __init__(self): | |
super().__init__("ovfucur", "CpForeDib.OvFutCur") | |
class CpPBOvFuOBid(CpPublish): | |
def __init__(self): | |
super().__init__("ovfubid", "CpForeDib.OvFutBid") | |
# 해외선물 현재가 조회 | |
class CpRPOvForMst: | |
def __init__(self): | |
if (g_objCpStatus.IsConnect == 0): | |
print("PLUS가 정상적으로 연결되지 않음. ") | |
return | |
self.objMst = win32com.client.Dispatch("CpForeDib.OvFutMst") | |
self.objCur = CpPBOvFuCur() | |
self.objBid = CpPBOvFuOBid() | |
return | |
def Request(self, code, caller): | |
# 현재가 통신 | |
self.objCur.Unsubscribe() | |
self.objBid.Unsubscribe() | |
self.objMst.SetInputValue(0, code) | |
ret = self.objMst.BlockRequest() | |
if self.objMst.GetDibStatus() != 0: | |
print("통신상태", self.objMst.GetDibStatus(), self.objMst.GetDibMsg1()) | |
return False | |
self.objCur.Subscribe(code, caller) | |
self.objBid.Subscribe(code, caller) | |
# 수신 받은 현재가 정보를 rtMst 에 저장 | |
caller.mstData[code] = {} | |
caller.mstData[code]['cur'] = self.objMst.GetHeaderValue(29) | |
caller.mstData[code]['open'] = self.objMst.GetHeaderValue(35) | |
caller.mstData[code]['high'] = self.objMst.GetHeaderValue(36) | |
caller.mstData[code]['low'] = self.objMst.GetHeaderValue(37) | |
caller.mstData[code]['tick'] = self.objMst.GetHeaderValue(6) # 호가 단위 | |
caller.mstData[code]['consize'] = self.objMst.GetHeaderValue(9) # 계약크기 | |
caller.mstData[code]['float'] = self.objMst.GetHeaderValue(4) # 가격 소수점 | |
caller.mstData[code]['jinb'] = self.objMst.GetHeaderValue(5) # 진법 | |
caller.mstData[code]['offer'] = self.objMst.GetHeaderValue(33) # 매도호가 | |
caller.mstData[code]['bid'] = self.objMst.GetHeaderValue(34) # 매수호가 | |
caller.mstData[code]['diff'] = self.objMst.GetHeaderValue(31) # 대비 | |
caller.mstData[code]['vol'] = self.objMst.GetHeaderValue(32) # 거래량 | |
sindx = 62 | |
for i in range(1, 6): | |
offerkey = 'offer' + str(i) | |
bidkey = 'bid' + str(i) | |
caller.mstData[code][offerkey] = self.objMst.GetHeaderValue(sindx) | |
caller.mstData[code][bidkey] = self.objMst.GetHeaderValue(sindx + 3) | |
sindx += 6 | |
print(caller.mstData[code]) | |
def UnSubscribe(self): | |
self.objCur.Unsubscribe() | |
self.objBid.Unsubscribe() | |
# 해외선물 잔고(미결제) 통신 | |
class CpOvfJango: | |
def __init__(self): | |
self.acc = g_objCpTrade.AccountNumber[0] # 계좌번호 | |
def Request(self, caller): | |
if (g_objCpStatus.IsConnect == 0): | |
print('PLUS가 정상적으로 연결되지 않음. ') | |
return False | |
# 해외선물 미결제 잔고 | |
objRq = win32com.client.Dispatch('CpForeTrade.OvfNotPaymentInq') | |
objRq.SetInputValue(1, self.acc) # 계좌번호 | |
while True : | |
objRq.BlockRequest() | |
# 현재가 통신 및 통신 에러 처리 | |
rqStatus = objRq.GetDibStatus() | |
rqRet = objRq.GetDibMsg1() | |
print('통신상태', rqStatus, rqRet) | |
if rqStatus != 0: | |
return False | |
# 조회 건수 | |
cnt = objRq.GetHeaderValue(0) | |
print(cnt) | |
if cnt == 0 : | |
break | |
for i in range(cnt): | |
item = {} | |
item['code'] = 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(10, i) | |
item['전일대비'] = objRq.GetDataValue(11, i) | |
item['전일대비율'] = objRq.GetDataValue(12, i) | |
item['평가금액'] = objRq.GetDataValue(13, i) | |
item['평가손익'] = objRq.GetDataValue(14, i) | |
item['손익률'] = objRq.GetDataValue(15, i) | |
item['매입금액'] = objRq.GetDataValue(16, i) | |
item['승수'] = objRq.GetDataValue(17, i) | |
item['통화코드'] = objRq.GetDataValue(18, i) | |
key = (item['code'], item['매매구분']) | |
caller.ovfJangadata[key] = item | |
print(item) | |
if objRq.Continue == False : | |
print("연속 조회 여부: 다음 데이터가 없음") | |
break | |
# print(self.data) | |
class MyWindow(QMainWindow): | |
def __init__(self): | |
super().__init__() | |
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.pbContract = CpPBOvfJango() | |
self.setWindowTitle('PLUS API TEST') | |
self.setGeometry(300, 300, 300, 240) | |
# 해외선물 잔고 | |
self.ovfJangadata = {} | |
# 해외선물 종목 데이터 | |
# [종목] = {key:value} 식으로 저장 | |
self.mstData = {} | |
self.mstObj = {} | |
nH = 20 | |
btnPrint = QPushButton('DF 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) | |
self.btnStart_clicked() | |
def btnStart_clicked(self): | |
self.pbContract.Unsubscribe() | |
obj = CpOvfJango() | |
obj.Request(self); | |
# 잔고에서 중복을 제거 하기 위해 set 으로 임시 저장 | |
codelist = set() | |
for key,value in self.ovfJangadata.items(): | |
codelist.add(key[0]) | |
# 종목의 현재가 정보 요청 | |
for code in codelist: | |
self.mstObj[code] = CpRPOvForMst() | |
self.mstObj[code].Request(code, self) | |
self.pbContract.Subscribe("",self) | |
def btnPrint_clicked(self): | |
for key,value in self.ovfJangadata.items(): | |
print(key, value) | |
def btnExit_clicked(self): | |
self.pbContract.Unsubscribe() | |
exit() | |
def btnExcel_clicked(self): | |
if (len(self.ovfJangadata) == 0): | |
print('잔고 없음') | |
return | |
# df= pd.DataFrame(columns=self.ovfJangadata.keys()) | |
isFirst = True | |
for k, v in self.ovfJangadata.items(): | |
# 데이터 프레임의 컬럼은 데이터의 key 값으로 생성 | |
if isFirst: | |
df = pd.DataFrame(columns = v.keys()) | |
isFirst = False | |
df.loc[len(df)] = v | |
# Create a Pandas Excel writer using XlsxWriter as the engine. | |
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 updateContract(self, pbdata): | |
key = (pbdata['code'], pbdata['매매구분']) | |
code = key[0] | |
print(key) | |
# 새로운 잔고 추가 | |
if key not in self.ovfJangadata.keys() : | |
print(key, '찾기 실패') | |
if pbdata["잔고수량"] == 0: | |
return | |
item = {} | |
item['code'] = pbdata['code'] | |
item['종목명'] = pbdata['종목명'] | |
item['매매구분'] = pbdata['매매구분'] | |
item['잔고수량'] = pbdata['잔고수량'] | |
item['단가'] = pbdata['단가'] | |
item['청산가능'] = pbdata['청산가능'] | |
item['미체결수량'] = pbdata['미체결수량'] | |
item['현재가'] = pbdata['현재가'] | |
item['전일대비'] = pbdata['전일대비'] | |
item['전일대비율'] = pbdata['전일대비율'] | |
item['평가금액'] = pbdata['평가금액'] | |
item['평가손익'] = pbdata['평가손익'] | |
item['손익률'] = pbdata['손익률'] | |
item['매입금액'] = pbdata['매입금액'] | |
item['승수'] = pbdata['승수'] | |
item['통화코드'] = pbdata['통화코드'] | |
self.ovfJangadata[key] = item | |
print('새로운 잔고 추가 -', key) | |
return | |
# 기존 잔고에 대한 처리 | |
item = self.ovfJangadata[key] | |
item['잔고수량'] = pbdata['잔고수량'] | |
item['청산가능'] = pbdata['청산가능'] | |
if (pbdata["처리구분"] == '00'): # 주문 접수 | |
print('주문 접수 -', key) | |
self.ovfJangadata[key] = item | |
if code not in self.mstObj: | |
self.mstObj[code] = CpRPOvForMst() | |
self.mstObj[code].Request(code, self) | |
return | |
if item['잔고수량'] == 0: # 잔고 삭제 | |
del self.ovfJangadata[key] | |
print('잔고 삭제 -', key) | |
# 현재가 실시간 요청 정보 제거 | |
self.mstObj[code].UnSubscribe() | |
del self.mstObj[code] | |
del self.mstData[code] | |
return | |
print('체결 업데이트-', key) | |
item['단가'] = pbdata['단가'] | |
item['미체결수량'] = pbdata['미체결수량'] | |
item['현재가'] = pbdata['현재가'] | |
item['전일대비'] = pbdata['전일대비'] | |
item['전일대비율'] = pbdata['전일대비율'] | |
item['평가금액'] = pbdata['평가금액'] | |
item['평가손익'] = pbdata['평가손익'] | |
item['손익률'] = pbdata['손익률'] | |
item['매입금액'] = pbdata['매입금액'] | |
self.ovfJangadata[key] = item | |
return | |
def updatePrice(self, code): | |
# 해외선물 잔고 평가손익 업데이트 | |
# 평가손익 = (평균가 - 현재가) / tick size * tick value * 수량 | |
for key,value in self.ovfJangadata.items(): | |
if (key[0] != code): | |
continue | |
print('평가손익 업데이트') | |
cur = self.mstData[code]['cur'] | |
value['현재가'] = cur | |
value['전일대비'] = self.mstData[code]['diff'] | |
value['전일대비율'] = (self.mstData[code]['diff'] / (cur - self.mstData[code]['diff']) * 100) | |
tickunit = g_objCodeMgr.GetTickUnit(code) | |
tickvalue = g_objCodeMgr.GetTickValue(code) | |
avg = value['단가'] / tickunit * tickvalue | |
cpr = cur / tickunit * tickvalue | |
value['평가금액'] = cpr | |
print(code, 'avg', avg, 'cpr', cpr, 'tickunit', tickunit, 'tickunit',tickvalue) | |
if (key[1] == 'S'): # 매도 잔고 | |
value['평가손익'] = (avg - cpr) * value["잔고수량"] | |
print(code, value['평가손익']) | |
# avg = 잔고 평균가 / tick size * tickvalue | |
# cpr = 현재가 / ticsize * tick value | |
# 매도 평가 손익 = avg - cpr * 수량 | |
elif (key[1] == 'B'): # 매수잔고 | |
value['평가손익'] = (cpr - avg) * value["잔고수량"] | |
print('평가손익 변경:', code, value['평가손익']) | |
# avg = 잔고 평균가 / tick size * tickvalue | |
# cpr = 현재가 / ticsize * tick value | |
# 매수 평가 손익 = cpr - avg * 수량 | |
return | |
if __name__ == '__main__': | |
app = QApplication(sys.argv) | |
myWindow = MyWindow() | |
myWindow.show() | |
app.exec_() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment