강의가 끝났다.
생각보다 빨리 끝나서 좋다.
다만 이해하지 못하고 일단 받아적기만 한 부분도 분명히 있으니, 다시금 정리할 필요가 있을 것 같다.
우선 코드 전문을 보자...!
__init__.py는 다음과 같다.
from ui.ui import *
class Main():
def __init__(self):
print('실행할 메인 클래스')
UI_class()
if __name__=='__main__':
Main()
최초로 실행될 메인 코드다.
이녀석은 형태 그대로 ui.ui를 실행시키는데, 그건 다음과 같다.
ui.py
from kiwoom.kiwoom import *
from PyQt5.QtWidgets import *
import sys
class UI_class():
def __init__(self):
print("UI Class")
self.app = QApplication(sys.argv) # 변수 초기화 시켜주는것
kiwoom = Kiwoom()
self.app.exec()
이녀석은... 나중에 GUI를 삽입할 수 있도록 별도의 공간을 빼둔 것인데, 당연히 나와는 상관이 없다.
이녀석이 불러오는 kiwoom.py가 가장 중요한것인데,
그건 양이 좀 길어서 접어두겠다.
kiwoom.py
from PyQt5.QAxContainer import *
from PyQt5.QtCore import *
from config.errorCode import *
from PyQt5.QtTest import * #이게 되네
import os
from config.kiwoomType import *
class Kiwoom(QAxWidget):
def __init__(self):
super().__init__()
print('Kiwoom class')
self.realType=RealType()
############## event loop 모음
self.login_event_loop=QEventLoop()
self.detail_account_info_event_loop = QEventLoop()
self.calculator_event_loop=QEventLoop()
# self.detail_account_info_event_loop_2 =QEventLoop()
##########################
################종목분석용
self.calcul_data=[]
###################변수모음
self.account_num=None
self.account_stock_dict={}
self.not_account_stock_dict={}
self.portfolio_stock_dict={}
self.jango_dict={}
###########################스크린 번호 모음
self.screen_my_info="2000"
self.screen_calculation_stock="4000"
self.screen_real_stock="5000"#종목별로 할당할 스크린 번호
self.screen_meme_stock="6000"#종목별 할당할 주문용 스크린 번호
self.screen_start_stop_real="1000"
###################계좌관련변수
self.use_money=0
self.use_money_percent=0.5
##########################아래 함수들은 실행됨
self.get_ocx_instance()
self.event_slots()
self.real_event_slots()
self.signal_login_commConnect()
self.get_account_info()
# self.detail_account_mystock()
self.detail_account_info() # 예수금 가져오기
self.detail_account_mystock() # 계좌평가잔고내역 요청
self.not_concluded_account() # 미체결 요청
#잠시 지울예정임 self.calculator_fnc() #종목분석용, 원래는 장 끝나고 실행
self.read_code() # w저장된 종목들 불러온다.
self.screen_number_setting() #스크린번호를 할당
self.dynamicCall("SetRealReg(QString, QString, QString, QString)",
self.screen_start_stop_real,
'',
self.realType.REALTYPE['장시작시간']['장운영구분'],
"0") #이거 앞으로 받을때는 1이어야 충돌 안 남
for code in self.portfolio_stock_dict.keys():
screen_num=self.portfolio_stock_dict[code]['스크린번호']
fids=self.realType.REALTYPE['주식체결']['체결시간']
self.dynamicCall("SetRealReg(QString, QString, QString, QString)",screen_num,code,fids,"1")
print("실시간 등록 코드: %s, 스크린번호: %s, fid번호: %s" %(code,screen_num,fids))
def get_ocx_instance(self):
self.setControl("KHOPENAPI.KHOpenAPICtrl.1")
def event_slots(self):
self.OnEventConnect.connect(self.login_slot)
self.OnReceiveTrData.connect(self.trdata_slot)
def real_event_slots(self):
self.OnReceiveRealData.connect(self.realdata_slot)
self.OnReceiveChejanData.connect(self.chejan_slot)
def signal_login_commConnect(self):
self.dynamicCall("CommConnect()")
# self.login_event_loop = QEventLoop()
self.login_event_loop.exec_()
def login_slot(self, errCode):
print(errors(errCode))
self.login_event_loop.exit()#d이거 뭔지 확인
def get_account_info(self):
self.account_list = self.dynamicCall("GetLoginInfo(String)","ACCNO")
self.account_num = self.account_list.split(';')[0]
print("계좌 번호 : %s" % self.account_num)
def detail_account_info(self):
print("예수금을 요청하는 부분")
self.dynamicCall("SetInputValue(String, String)","계좌번호",self.account_num)
self.dynamicCall("SetInputValue(String, String)","비밀번호","0000")
self.dynamicCall("SetInputValue(String, String)","비밀번호입력매체구분","00")
self.dynamicCall("SetInputValue(String, String)","조회구분","2")
self.dynamicCall("CommRqData(String, String, int, String)","예수금상세현황요청","opw00001","0",self.screen_my_info)
# event loop : 명령을 처리하는 동안 다른 작업을 가능하게 하는 것, 병렬화
self.detail_account_info_event_loop = QEventLoop() # 아랫줄과 함께, 2set는 명령마다 대기를 위해서 들어가야함
self.detail_account_info_event_loop.exec_() #_exec()랑은 최신버전 차이
def detail_account_mystock(self, sPrevNext="0"):
print("계좌평가 잔고내역 요청")
self.dynamicCall("SetInputValue(String, String)","계좌번호",self.account_num)
self.dynamicCall("SetInputValue(String, String)","비밀번호","0000")
self.dynamicCall("SetInputValue(String, String)","비밀번호입력매체구분","00")
self.dynamicCall("SetInputValue(String, String)","조회구분","2")
self.dynamicCall("CommRqData(String, String, int, String)","계좌평가잔고내역요청","opw00018",sPrevNext,self.screen_my_info)
# self.detail_account_info_event_loop_2=QEventLoop()
self.detail_account_info_event_loop.exec_()
def not_concluded_account(self, sPrevNext="0"):
print("미체결요청")
self.dynamicCall("SetInputValue(QString, QString)","계좌번호",self.account_num)
self.dynamicCall("SetInputValue(QString, QString)","체결구분","1")
self.dynamicCall("SetInputValue(QString, QString)", "매매구분", "0")
self.dynamicCall("CommRqData(String, String, int, String)","실시간미체결요청","opt10075",sPrevNext,self.screen_my_info)
self.detail_account_info_event_loop.exec_()
def trdata_slot(self, sScrNo, sRQName, sTrCode, sRecordName,sPrevNext):
'''
tr 요청을 받는 구역. Slot임!
:param sScrNo: 스크린번호
:param sRQName: 내가 요청했을 때 지은 이름
:param sTrCode: 요청 id, tr코드
:param sRecordName: 사용안함
:param sPrevNext: 다음 페이지가 있는지
:return:
'''
if sRQName == "예수금상세현황요청":
deposit=self.dynamicCall("GetCommData(String, String, int, String)", sTrCode, sRQName, 0, "예수금")
print("예수금 %s" % deposit)
print("예수금 형변환%s" % int(deposit))
self.use_money=int(deposit)*self.use_money_percent #일부분만 사용하는 코드
self.use_money=self.use_money/4
ok_deposit=self.dynamicCall("GetCommData(String, String, int, String)", sTrCode, sRQName, 0, "출금가능금액")
print("출금가능금액 %s" % ok_deposit)
print("출금가능금액 형변환 %s" % int(ok_deposit))
self.detail_account_info_event_loop.exit() # 도중에 빠짐
elif sRQName == "계좌평가잔고내역요청":
print("1")
total_buy_money=self.dynamicCall("GetCommData(String, String, int, String)", sTrCode, sRQName, 0, "총매입금액")
total_buy_money_result = int(total_buy_money)
print("총매입금액 %s" % total_buy_money_result)
total_profit_loss_rate = self.dynamicCall("GetCommData(String, String, int, String)", sTrCode, sRQName, 0, "총수익률(%)")
total_profit_loss_rate_result=float(total_profit_loss_rate)
print("총수익률(%%) %s" % total_profit_loss_rate_result)
rows=self.dynamicCall("GetRepeatCnt(QString, QString)",sTrCode, sRQName) #GetRepeatCnt : 멀티데이터 조회 용도
cnt=0
for i in range(rows):
code=self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, i, "종목번호")
code=code.strip()[1:]
code_name=self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, i, "종목명")
stock_quantity=self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, i, "보유수량")
buy_price = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, i, "매입가")
learn_rate = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, i, "수익률(%)")
current_price = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, i, "현재가")
total_chegual_price = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, i, "매입금액")
possible_quantity = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, i, "매매가능수량")
if code in self.account_stock_dict:
pass
else:
self.account_stock_dict.update({code:{}})
code_name=code_name.strip()
stock_quantity=int(stock_quantity.strip())
buy_price = int(buy_price.strip())
learn_rate = float(learn_rate.strip())
current_price = int(current_price.strip())
total_chegual_price = int(total_chegual_price.strip())
possible_quantity = int(possible_quantity.strip())
self.account_stock_dict[code].update({"종목명":code_name})
self.account_stock_dict[code].update({"보유수량":stock_quantity})
self.account_stock_dict[code].update({"매입가":buy_price})
self.account_stock_dict[code].update({"수익률(%)":learn_rate})
self.account_stock_dict[code].update({"현재가":current_price})
self.account_stock_dict[code].update({"매입금액":total_chegual_price})
self.account_stock_dict[code].update({"매매가능수량":possible_quantity})
cnt += 1
print("계좌에 가지고 있는 종목 %s"% self.account_stock_dict)
print("계좌 보유종목 카운트 %s"%cnt)
if sPrevNext=="2":
self.detail_account_mystock(sPrevNext="2")
else:
self.detail_account_info_event_loop.exit()
elif sRQName == "실시간미체결요청":
rows = self.dynamicCall("GetRepeatCnt(QString, QString)", sTrCode, sRQName) # GetRepeatCnt : 멀티데이터 조회 용도
for i in range(rows):
code = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, i, "종목코드")
code_name=self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, i, "종목명")
order_no=self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, i, "주문번호")
order_status=self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, i, "주문상태")
order_quantity = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, i, "주문수량")
order_price = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, i, "주문가격")
order_gubun = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, i, "주문구분")
not_quantity = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, i, "미체결수량")
ok_quantity = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, i, "체결량")
code=code.strip()
code_name=code_name.strip()
order_no=int(order_no.strip())
order_status=int(order_status.strip())
order_quantity = int(order_quantity.strip())
order_price = float(order_price.strip())
order_gubun = order_gubun.strip().lstrip('+').lstrip('-')
not_quantity = int(not_quantity.strip())
ok_quantity = int(ok_quantity.strip())
if order_no in self.not_account_stock_dict:
pass
else:
self.not_account_stock_dict[order_no]={}
self.not_account_stock_dict[order_no].update({"종목코드":code})
self.not_account_stock_dict[order_no].update({"종목명":code_name})
self.not_account_stock_dict[order_no].update({"주문번호":order_no})
self.not_account_stock_dict[order_no].update({"주문상태":order_status})
self.not_account_stock_dict[order_no].update({"주문수량":order_quantity})
self.not_account_stock_dict[order_no].update({"주문가격":order_price})
self.not_account_stock_dict[order_no].update({"주문구분":order_gubun})
self.not_account_stock_dict[order_no].update({"미체결수량":not_quantity})
self.not_account_stock_dict[order_no].update({"체결량":ok_quantity})
print("미체결 종목 :%s"%self.not_account_stock_dict[order_no])
self.detail_account_info_event_loop.exit()
elif sRQName=="주식일봉차트조회":
#print("일봉데이터 요청")
code=self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, 0, "종목코드")
code = code.strip()
print("%s 일봉데이터 요청" % code)
cnt=self.dynamicCall("GetRepeatCnt(QString, QString)",sTrCode,sRQName)
print("데이터 일수 %s" % cnt)
#data=self.dynamicCall("GetCommDataEx(QString, QString)", sTrCode,sRQName)
#[['','현재가','거래량','거래대금','날짜','시가','고가','저가,''],['','현재가','거래량','거래대금','날짜','시가','고가','저가,''],...]]
#600일치 데이터인 것 확인
for i in range(cnt):
data=[]
current_price=self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, i, "현재가")
value = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, i,"거래량")
trading_value = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, i,"거래대금")
date = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, i,"일자")
start_price = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, i,"시가")
high_price = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, i,"고가")
low_price = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, i,"저가")
data.append("")
data.append(current_price.strip())
data.append(value.strip())
data.append(trading_value.strip())
data.append(date.strip())
data.append(start_price.strip())
data.append(high_price.strip())
data.append(low_price.strip())
data.append("")
self.calcul_data.append(data.copy()) #위에 만들어줘야 하는 list
if sPrevNext =="2":
print(sPrevNext)
# self.day_kiwoom_db(code=code, sPrevNext=sPrevNext) #재귀함수에서 오류나는것같음
else:
print("총 일 수%s" %len(self.calcul_data))
#self.logging.logger.debug("총 일수 %s"% len(self.calcul_data))
#120이평선을 그릴 만큼의 data가 있는지 check 필요
pass_success = False
if self.calcul_data == None or len(self.calcul_data)<120:
pass_success=False
print("check1")
else:
#120일 이상이면
total_price=0
for value in self.calcul_data[:120]: #[D,D-1,...,D-120]
total_price += int(value[1])
moving_average_price=total_price/120
#오늘 주가 120일 이평선에 걸쳐있는지 확인
bottom_stock_price=False
check_price=None
if int(self.calcul_data[0][7]) <= moving_average_price and moving_average_price <= int(self.calcul_data[0][6]): #[0] : 오늘 [7] : 저가
print("오늘 주가 120이평선에 걸쳐있는 것 확인")
bottom_stock_price=True
check_price=int(self.calcul_data[0][6])
#과거의 일봉들이 120일 이평선보다 밑에 있는지 확인,
#그렇게 확인을 하다가 일봉이 120일 이평선보다 위에 있으면 계산 진행
prev_price=None #과거의 일봉 저가
if bottom_stock_price == True:
moving_average_price_prev=0
price_top_moving=False
idx=1
while True:
if len(self.calcul_data[idx:])<120:#최근부터 하루씩 거슬러올라가며 120일치 계속 있는지 확인
print("120일치 없음")
break
total_price=0
for value in self.calcul_data[idx:120+idx]:
total_price+=int(value[1])
moving_average_price_prev=total_price/120
if moving_average_price_prev <= int(self.calcul_data[idx][6]) and idx <= 20: # 20일정도는 아래여야되지않겠냐? 강의자가 만든거라함
print("20일동안 주가가 120일 이평선과 같거나 위에 있으면 조건 통과 못 함")
price_top_moving=False
break
elif int(self.calcul_data[idx][7]) > moving_average_price_prev and idx >20:
print("120일 이평선 위에 있는 일봉 확인 됨")
price_top_moving=True
prev_price=int(self.calcul_data[idx][7])
break
idx += 1
#해당부분 이평선이 가장 최근 일자의 이평선 가격보다 낮은지 확인
if price_top_moving == True:
if moving_average_price > moving_average_price_prev and check_price > prev_price:
print("포착된 이평선의 가격이 오늘자(최근일자) 이평선 가격보다 낮은 것 확인됨")
print("포착된 부분의 일봉 저가가 오늘자 일봉의 고가보다 낮은지 확인됨")
pass_success=True
pass_success=True #삭제해야됨
if pass_success==True:
print("조건부 통과됨")
code_nm = self.dynamicCall("GetMasterCodeName(QString)", code)
f=open("files/condition_stock.txt","a",encoding="utf8")
f.write("%s\t%s\t%s\n"%(code, code_nm, self.calcul_data[0][1]))
f.close()
elif pass_success == False:
print("조건부 통과 못함")
self.calcul_data.clear() #함수 초기화
self.calculator_event_loop.exit()
#print("cc")
#rows=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName)
#print(rows) #None 오류 이것
if sPrevNext=="2":
self.day_kiwoom_db(code=code,sPrevNext=sPrevNext)
else:
self.calculator_event_loop.exit()
def get_code_list_by_market(self,market_code): #종목코드들 반환
code_list=self.dynamicCall("GetCodeListByMarket(QString)",market_code)
code_list=code_list.split(";")[:-1]
return code_list
def calculator_fnc(self):
#종목분석 실행용 함수
code_list=self.get_code_list_by_market("10")
print("코스닥 갯수 %s" %len(code_list))
for idx, code in enumerate(code_list):
if idx%300==5:
self.dynamicCall("DisconnectRealData(QString)",self.screen_calculation_stock) #calculation_stock ㅇㄷ? @ 51.lec
print("%s / %s : KOSDAQ Stock Code : %s is updating..." % (idx+1, len(code_list), code))
self.day_kiwoom_db(code=code)
#일봉데이터 계산
def day_kiwoom_db(self, code=None, date=None, sPrevNext="0"):
QTest.qWait(600)#(3.6초)
#print("check3")
self.dynamicCall("SetInputValue(QString, QString)", "종목코드", code)
self.dynamicCall("SetInputValue(QString, QString)", "수정주가구분", "1")
if date != None:
self.dynamicCall("SetInputValue(QString, QString)", "기준일자", date)
self.dynamicCall("CommRqData(QString, QString, int, QString)","주식일봉차트조회","opt10081",sPrevNext,self.screen_calculation_stock)
#screen calculate 찾아야됨
self.calculator_event_loop.exec_()
#매수법칙 계산 들어가면 됨
#가상환경 재설치해야됨(32bit로)
#이평선은 함수화를 하면 좋을것같다
#221113-1 일단 32bit 가상환경으로 다시 만든 뒤 console 돌려서 오류가 뭔지 확인필요하다.
def read_code(self):
if os.path.exists("files/condition_stock.txt"): #있으면 True
f = open("files/condition_stock.txt","r",encoding="utf8")
lines = f.readlines()
for line in lines:
if line != "":
ls=line.split("\t")
stock_code =ls[0]
stock_name=ls[1]
stock_price=ls[2].split("\n")[0]
stock_price=abs(float(stock_price))
self.portfolio_stock_dict.update({stock_code:{"종목명":stock_name, "현재가":stock_price}}) #{"종목코드":{종목명:삼성,가격:얼마}, "...
f.close()
print(self.portfolio_stock_dict)
def screen_number_setting(self):
screen_overwrite=[]
#계좌평가잔고내역에 있는 종목들
for code in self.account_stock_dict.keys():
if code not in screen_overwrite:
screen_overwrite.append(code)
#미체결에 있는 종목들
for order_number in self.not_account_stock_dict.keys():
code = self.not_account_stock_dict[order_number]['종목코드']
if code not in screen_overwrite:
screen_overwrite.append(code)
#포트폴리오에 담겨있는 종목들
for code in self.portfolio_stock_dict.keys():
if code not in screen_overwrite:
screen_overwrite.append(code)
#스크린번호 할당
cnt=0 #스크린 번호 하나에 100개 종목만 가능
for code in screen_overwrite:
temp_screen=int(self.screen_real_stock)
meme_screen=int(self.screen_meme_stock)
if(cnt%50)==0:
temp_screen+=1 #5000->5001
self.screen_real_stock=str(temp_screen)
if(cnt%50)==0:
meme_screen+=1 #6000->6001
self.screen_meme_stock=str(meme_screen)
if code in self.portfolio_stock_dict.keys():
self.portfolio_stock_dict[code].update({"스크린번호":str(self.screen_real_stock)})
self.portfolio_stock_dict[code].update({"주문용스크린번호":str(self.screen_meme_stock)})
elif code not in self.portfolio_stock_dict.keys():
self.portfolio_stock_dict.update({code:{"스크린번호":str(self.screen_real_stock),"주문용스크린번호":str(self.screen_meme_stock)}})
cnt+=1
print(self.portfolio_stock_dict)
def realdata_slot(self, sCode, sRealType, sRealData): #sRealData 안 씀
if sRealType=="장시작시간":
fid=self.realType.REALTYPE[sRealType]['장운영구분']
value=self.dynamicCall("GetCommRealData(QString, int)", sCode, fid)
if value =='0':
print("장 시작 전")
elif value=='3':
print("장 시작")
elif value=='2':
print("장 종료, 동시호가로 넘어감")
elif value=='4':
print("3시 30분 , 장 종료")
elif sRealType == "주식체결":
a = self.dynamicCall("GetCommRealData(QString,int)",sCode,self.realType.REALTYPE[sRealType]['현재시간']) #HHMMSS
b = self.dynamicCall("GetCommRealData(QString,int)",sCode,self.realType.REALTYPE[sRealType]['현재가']) #+-2500
b=abs(int(b))
c = self.dynamicCall("GetCommRealData(QString,int)",sCode,self.realType.REALTYPE[sRealType]['전일대비']) #+-2500
c=abs(int(c))
d=self.dynamicCall("GetCommRealData(QString,int)",sCode,self.realType.REALTYPE[sRealType]['등락율']) #+-2500
d=float(d)
e = self.dynamicCall("GetCommRealData(QString,int)",sCode,self.realType.REALTYPE[sRealType]['(최우선)매도호가']) #+-2500
e=abs(int(e))
f = self.dynamicCall("GetCommRealData(QString,int)", sCode,self.realType.REALTYPE[sRealType]['(최우선)매수호가']) # +-2500
f = abs(int(f))
g = self.dynamicCall("GetCommRealData(QString,int)", sCode,self.realType.REALTYPE[sRealType]['거래량']) # +-2500
g = abs(int(g))
h = self.dynamicCall("GetCommRealData(QString,int)", sCode,self.realType.REALTYPE[sRealType]['누적거래량']) # +-2500
h = abs(int(h))
i= self.dynamicCall("GetCommRealData(QString,int)", sCode,self.realType.REALTYPE[sRealType]['고가']) # +-2500
i = abs(int(i))
j = self.dynamicCall("GetCommRealData(QString,int)", sCode,self.realType.REALTYPE[sRealType]['시가']) # +-2500
j = abs(int(j))
k = self.dynamicCall("GetCommRealData(QString,int)", sCode,self.realType.REALTYPE[sRealType]['저가']) # +-2500
k = abs(int(k))
if sCode not in self.portfolio_stock_dict:
self.portfolio_stock_dict.update({sCode:{}})
self.portfolio_stock_dict[sCode].update({"체결시간":a})
self.portfolio_stock_dict[sCode].update({"현재가":b})
self.portfolio_stock_dict[sCode].update({"전일대비":c})
self.portfolio_stock_dict[sCode].update({"등락율":d})
self.portfolio_stock_dict[sCode].update({"(최우선)매도호가":e})
self.portfolio_stock_dict[sCode].update({"(최우선)매수호가":f})
self.portfolio_stock_dict[sCode].update({"거래량":g})
self.portfolio_stock_dict[sCode].update({"누적거래량":h})
self.portfolio_stock_dict[sCode].update({"고가":i})
self.portfolio_stock_dict[sCode].update({"시가":j})
self.portfolio_stock_dict[sCode].update({"저가":k})
print(self.portfolio_stock_dict[sCode])
## 조건문 lec.61
#계좌 잔고 평가내역에 있고 오늘 산 잔고에는 없을 경우
if sCode in self.account_stock_dict.keys() and sCode not in self.jango_dict.keys():
print("%s %s"%("신규매도를 한다", sCode))
asd=self.account_stock_dict[sCode]
#조건 항
meme_rate=(b-asd['매입가'])/asd['매입가']*100 #등락률
if asd['매매가능수량']>0 and (meme_rate >5 or meme_rate <-5):
order_success = self.dynamicCall("SendOrder(QString,QString,QString,int,QString,int,int,QString,QString)",
["신규매도", self.portfolio_stock_dict[sCode]['주문용스크린번호'],self.account_num,2,
sCode, asd['매매가능수량'],0,self.realType.SENDTYPE['거래구분']['시장가'],""])
if order_success ==0:
print("매도주문 전달 성공")
del self.account_stock_dict[sCode]
else:
print("매도주문 전달 실패")
#오늘 산 잔고에 있을경우
elif sCode in self.jango_dict.keys():
print("%s %s"%("신규매도를 한다2",sCode))
jd=self.jango_dict[sCode]
meme_rate=(b-jd['매입단가'])/jd['매입단가']*100
if jd['주문가능수량']>0 and (meme_rate>5 or meme_rate < -5):
order_success = self.dynamicCall(
"SendOrder(QString,QString,QString,int,QString,int,int,QString,QString)",
["신규매도", self.portfolio_stock_dict[sCode]['주문용스크린번호'],self.account_num,2,
sCode, jd['주문가능수량'],0,self.realType.SENDTYPE['거래구분']['시장가'],""])
if order_success==0:
self.logging.logger.debug("매도주문 전달 성공")
else:
self.logging.logger.debug("매도주문 전달 실패")
#등락률이 2% 이상이고, 오늘 산 잔고에 없을경우
elif d>2.0 and sCode not in self.jango_dict:
print("%s %s"%("신규매수를 한다",sCode))
result=(self.use_money*0.1)/e
quantity=int(result)
order_success=self.dynamicCall(
"SendOrder(QString,QString,QString,int,QString,int,int,QString,QString)",
["신규매수", self.portfolio_stock_dict[sCode]['주문용스크린번호'], self.account_num, 1,
sCode, quantity, 0, self.realType.SENDTYPE['거래구분']['지정가'], ""])
if order_success == 0:
self.logging.logger.debug("매수주문 전달 성공")
else:
self.logging.logger.debug("매수주문 전달 실패")
not_meme_list=list(self.not_account_stock_dict)
for order_num in not_meme_list:
code = self.not_account_stock_dict[order_num]["종목코드"]
meme_price=self.not_account_stock_dict[order_num]["주문가격"]
not_quantity= self.not_account_stock_dict[order_num]["미체결수량"]
order_gubun = self.not_account_stock_dict[order_num]["주문구분"]
if order_gubun=="매수" and not_quantity>0 and e > meme_price:
print("%s %s"%("매수 취소 한다",sCode))
order_success = self.dynamicCall(
"SendOrder(QString, QString, QString, int, QString, int, int, QString, QString)",
["매수취소", self.portfolio_stock_dict[sCode]["주문용스크린번호"], self.account_num, 3, code, 0, 0,
self.realType.SENDTYPE['거래구분']['지정가'], order_num]
)
if order_success == 0:
self.logging.logger.debug("매수취소 전달 성공")
else:
self.logging.logger.debug("매수취소 전달 실패")
elif not_quantity ==0:
del self.not_account_stock_dict[order_num]
def chejan_slot(self, sGubun, nItemCnt, sFidList):
if int(sGubun)==0:
print("주문체결")
account_num = self.dynamicCall("GetChejanData(int)", self.realType.REALTYPE['주문체결']['계좌번호'])
sCode = self.dynamicCall("GetChejanData(int)", self.realType.REALTYPE['주문체결']['종목코드'])[1:]
stock_name = self.dynamicCall("GetChejanData(int)", self.realType.REALTYPE['주문체결']['종목명'])
stock_name = stock_name.strip()
origin_order_number = self.dynamicCall("GetChejanData(int)",
self.realType.REALTYPE['주문체결']['원주문번호']) # 출력 : defaluse : "000000"
order_number = self.dynamicCall("GetChejanData(int)",
self.realType.REALTYPE['주문체결']['주문번호']) # 출럭: 0115061 마지막 주문번호
order_status = self.dynamicCall("GetChejanData(int)",
self.realType.REALTYPE['주문체결']['주문상태']) # 출력: 접수, 확인, 체결
order_quan = self.dynamicCall("GetChejanData(int)", self.realType.REALTYPE['주문체결']['주문수량']) # 출력 : 3
order_quan = int(order_quan)
order_price = self.dynamicCall("GetChejanData(int)", self.realType.REALTYPE['주문체결']['주문가격']) # 출력: 21000
order_price = int(order_price)
not_chegual_quan = self.dynamicCall("GetChejanData(int)",
self.realType.REALTYPE['주문체결']['미체결수량']) # 출력: 15, default: 0
not_chegual_quan = int(not_chegual_quan)
order_gubun = self.dynamicCall("GetChejanData(int)", self.realType.REALTYPE['주문체결']['주문구분']) # 출력: -매도, +매수
order_gubun = order_gubun.strip().lstrip('+').lstrip('-')
chegual_time_str = self.dynamicCall("GetChejanData(int)",
self.realType.REALTYPE['주문체결']['주문/체결시간']) # 출력: '151028'
chegual_price = self.dynamicCall("GetChejanData(int)",
self.realType.REALTYPE['주문체결']['체결가']) # 출력: 2110 default : ''
if chegual_price == '':
chegual_price = 0
else:
chegual_price = int(chegual_price)
chegual_quantity = self.dynamicCall("GetChejanData(int)",
self.realType.REALTYPE['주문체결']['체결량']) # 출력: 5 default : ''
if chegual_quantity == '':
chegual_quantity = 0
else:
chegual_quantity = int(chegual_quantity)
current_price = self.dynamicCall("GetChejanData(int)", self.realType.REALTYPE['주문체결']['현재가']) # 출력: -6000
current_price = abs(int(current_price))
first_sell_price = self.dynamicCall("GetChejanData(int)",
self.realType.REALTYPE['주문체결']['(최우선)매도호가']) # 출력: -6010
first_sell_price = abs(int(first_sell_price))
first_buy_price = self.dynamicCall("GetChejanData(int)",
self.realType.REALTYPE['주문체결']['(최우선)매수호가']) # 출력: -6000
first_buy_price = abs(int(first_buy_price))
######## 새로 들어온 주문이면 주문번호 할당
if order_number not in self.not_account_stock_dict.keys():
self.not_account_stock_dict.update({order_number: {}})
self.not_account_stock_dict[order_number].update({"종목코드": sCode})
self.not_account_stock_dict[order_number].update({"주문번호": order_number})
self.not_account_stock_dict[order_number].update({"종목명": stock_name})
self.not_account_stock_dict[order_number].update({"주문상태": order_status})
self.not_account_stock_dict[order_number].update({"주문수량": order_quan})
self.not_account_stock_dict[order_number].update({"주문가격": order_price})
self.not_account_stock_dict[order_number].update({"미체결수량": not_chegual_quan})
self.not_account_stock_dict[order_number].update({"원주문번호": origin_order_number})
self.not_account_stock_dict[order_number].update({"주문구분": order_gubun})
self.not_account_stock_dict[order_number].update({"주문/체결시간": chegual_time_str})
self.not_account_stock_dict[order_number].update({"체결가": chegual_price})
self.not_account_stock_dict[order_number].update({"체결량": chegual_quantity})
self.not_account_stock_dict[order_number].update({"현재가": current_price})
self.not_account_stock_dict[order_number].update({"(최우선)매도호가": first_sell_price})
self.not_account_stock_dict[order_number].update({"(최우선)매수호가": first_buy_price})
print(self.not_account_stock_dict)
elif int(sGubun)==1:
print("잔고")
account_num = self.dynamicCall("GetChejanData(int)", self.realType.REALTYPE['잔고']['계좌번호'])
sCode = self.dynamicCall("GetChejanData(int)", self.realType.REALTYPE['잔고']['종목코드'])[1:]
stock_name = self.dynamicCall("GetChejanData(int)", self.realType.REALTYPE['잔고']['종목명'])
stock_name = stock_name.strip()
current_price = self.dynamicCall("GetChejanData(int)", self.realType.REALTYPE['잔고']['현재가'])
current_price = abs(int(current_price))
stock_quan = self.dynamicCall("GetChejanData(int)", self.realType.REALTYPE['잔고']['보유수량'])
stock_quan = int(stock_quan)
like_quan = self.dynamicCall("GetChejanData(int)", self.realType.REALTYPE['잔고']['주문가능수량'])
like_quan = int(like_quan)
buy_price = self.dynamicCall("GetChejanData(int)", self.realType.REALTYPE['잔고']['매입단가'])
buy_price = abs(int(buy_price))
total_buy_price = self.dynamicCall("GetChejanData(int)",
self.realType.REALTYPE['잔고']['총매입가']) # 계좌에 있는 종목의 총매입가
total_buy_price = int(total_buy_price)
meme_gubun = self.dynamicCall("GetChejanData(int)", self.realType.REALTYPE['잔고']['매도매수구분'])
meme_gubun = self.realType.REALTYPE['매도수구분'][meme_gubun]
first_sell_price = self.dynamicCall("GetChejanData(int)", self.realType.REALTYPE['잔고']['(최우선)매도호가'])
first_sell_price = abs(int(first_sell_price))
first_buy_price = self.dynamicCall("GetChejanData(int)", self.realType.REALTYPE['잔고']['(최우선)매수호가'])
first_buy_price = abs(int(first_buy_price))
if sCode not in self.jango_dict.keys():
self.jango_dict.update({sCode: {}})
self.jango_dict[sCode].update({"현재가": current_price})
self.jango_dict[sCode].update({"종목코드": sCode})
self.jango_dict[sCode].update({"종목명": stock_name})
self.jango_dict[sCode].update({"보유수량": stock_quan})
self.jango_dict[sCode].update({"주문가능수량": like_quan})
self.jango_dict[sCode].update({"매입단가": buy_price})
self.jango_dict[sCode].update({"총매입가": total_buy_price})
self.jango_dict[sCode].update({"매도매수구분": meme_gubun})
self.jango_dict[sCode].update({"(최우선)매도호가": first_sell_price})
self.jango_dict[sCode].update({"(최우선)매수호가": first_buy_price})
if stock_quan == 0:
del self.jango_dict[sCode]
self.dynamicCall("SetRealRemove(QString, QString)", self.portfolio_stock_dict[sCode]['스크린번호'],sCode)
#송수신메세지 Lec69 #삽입하는건 다시보기
def msg_slot(self,sScrNo, sRQName, sTrCode, msg):
print("스크린: %s, 요청이름: %s, tr코드: %s --- %s"%(sScrNo, sRQName, sTrCode, msg))
#파일삭제
def file_delect(self): #삽입하는건 다시보기 Lec69
if os.path.isfile("files/condition_stock.txt"):
os.remove("files/condition_stock.txt")
#스크린넘버 공부 필요
...으아악, 방대하다...!
방대하지만, 생각보다 단순한 부분들도 많다.
이 코드안에서 꼭 필요한 부분만 필수적으로 이해할 수 있다면 강의를 들은 보람이 있을것만 같다.
내가 책을 처음으로 봤을때, 책의 코드는 거의 완성된 형태를 갖추고있었다.
몇가지 테크닉이 섞인 깔끔한 코드. 그런 책의 코드는 실제로 구동할때는 다양한 이점이 있겠지만
능지이슈인지... 내가 도무지 책만보고 이해할 수 없었다.
유튜브가 정말 좋았던건, 음성과 영상 설명이라는 점보다도
해당 강의가 정말 쉽게, API를 호출을 위한 최소로만 이해하며 초보자를 위한 강의를 진행한다는 점이었다.
혹시라도 파이썬 초보자가 자동매매를 시작해보고싶다면 주저없이 해당 유튜브강의를 추천하고싶다...!
아무튼, 나는 결국 책의 코드를 사용할것이다. 당연히 완성돼있으니까!
책의 코드를 이해하기위한 선행과정으로, 위 코드를 뜯어보기로 하자.
코드를 보면, 결국 다양한 함수들로 엮여있는데, 그중 가장 첫 함수를 뜯어보겠다.
def __init__(self):
super().__init__()
print('Kiwoom class')
self.realType=RealType()
############## event loop 모음
self.login_event_loop=QEventLoop()
self.detail_account_info_event_loop = QEventLoop()
self.calculator_event_loop=QEventLoop()
# self.detail_account_info_event_loop_2 =QEventLoop()
##########################
################종목분석용
self.calcul_data=[]
###################변수모음
self.account_num=None
self.account_stock_dict={}
self.not_account_stock_dict={}
self.portfolio_stock_dict={}
self.jango_dict={}
###########################스크린 번호 모음
self.screen_my_info="2000"
self.screen_calculation_stock="4000"
self.screen_real_stock="5000"#종목별로 할당할 스크린 번호
self.screen_meme_stock="6000"#종목별 할당할 주문용 스크린 번호
self.screen_start_stop_real="1000"
###################계좌관련변수
self.use_money=0
self.use_money_percent=0.5
##########################아래 함수들은 실행됨
self.get_ocx_instance()
self.event_slots()
self.real_event_slots()
self.signal_login_commConnect()
self.get_account_info()
# self.detail_account_mystock()
self.detail_account_info() # 예수금 가져오기
self.detail_account_mystock() # 계좌평가잔고내역 요청
self.not_concluded_account() # 미체결 요청
#잠시 지울예정임 self.calculator_fnc() #종목분석용, 원래는 장 끝나고 실행
self.read_code() # w저장된 종목들 불러온다.
self.screen_number_setting() #스크린번호를 할당
self.dynamicCall("SetRealReg(QString, QString, QString, QString)",
self.screen_start_stop_real,
'',
self.realType.REALTYPE['장시작시간']['장운영구분'],
"0") #이거 앞으로 받을때는 1이어야 충돌 안 남
for code in self.portfolio_stock_dict.keys():
screen_num=self.portfolio_stock_dict[code]['스크린번호']
fids=self.realType.REALTYPE['주식체결']['체결시간']
self.dynamicCall("SetRealReg(QString, QString, QString, QString)",screen_num,code,fids,"1")
print("실시간 등록 코드: %s, 스크린번호: %s, fid번호: %s" %(code,screen_num,fids))
__init__(self) 함수는 일반적으로 함수에서 가장 먼저 실행되는 부분이다.
kiwoom.py의 해당 함수에는 각종 변수들을 먼저 선언한 뒤 다른 필요한 함수들을 하나씩 실행시키는 역할을 한다.
그럼 이 코드에 있는 각종 함수들을 천천히 알아보도록 하자.
(계속)
'글 > 코딩' 카테고리의 다른 글
(교육정리) Pandas와 Data 통계 - 1 (0) | 2023.07.25 |
---|---|
백준 알고리즘 문제 풀이 1764 : 듣보잡 (0) | 2023.07.05 |
[python][자동매매] 강의가 끝나고, 머릿속 정리 3. 계좌정보 불러오기 + Tr Data 불러오기 (0) | 2023.06.01 |
[python][자동매매] 강의가 끝나고, 머릿속 정리 2. Login (0) | 2023.05.31 |
[Python][자동매매] Process finished with exit code -1073740791 (0xC0000409) 오류문 해결과정 (0) | 2023.05.29 |