주식자동매매 27강. 계좌관리하기 (4), opt10045 주문 전송하기
본문 바로가기

주식 자동매매 강의/기초반(모든 코딩의 뿌리)

주식자동매매 27강. 계좌관리하기 (4), opt10045 주문 전송하기

반응형

여러분들의 이해를 돕기위해 최근 유튜브 강의도 병행한다고 진행이 느려졌습니다. 강의를 들으시면서 블로그 글을 읽으시면 이해하기가 조금 더 편하실 것입니다. 이번 강의에서는 opt100045에 대하여 주문을 넣어 보도록 하겠습니다.

 

[유튜브 강의, 링크]와 같이 보시면 많은 도움이 되실 겁니다.

 

이해가 어려우시면 스크롤을 최하단까지 내리셔서 요약 설명과 코드를 복사 붙여 넣기 하세요. 감사합니다.

 

1. opt100045 주문 전송 전 기본 셋팅

 아래 코드를 하나씩 이해해 보도록 하겠습니다. 우선 opt100045 주문은 종목별/기간별 종목투자자 평단가를 확인하는 것입니다. 아래 코드에서는 C_K_F_class()라는 함수를 구현하였으며, opt100045 명령을 키움 서버에 전송하기 위한 코드입니다.

opt100045 주문 전송 전 기본셋팅
opt100045 주문 전송 전 기본셋팅

- code_list = [] : code_list에 우리 계좌에 있는 종목 번호를 입력할 것입니다. 따라서, 리스트 형태([])로 정의합니다. 리스트는 [112345, 23456, 2313456,... ]의 형태를 뜻합니다.

- for code in self.k.acc_portfolio.keys() : 절대 언어 self.k.acc_portfolio에 들어있는 코드번호들을 가져와 code 에 입력합니다. 이때, self.k.acc_portfolio는 딕셔너리로 구성되어 있으며( 1234 = 금액 : 4000...) 앞에 있는 숫자가 keys를 뜻합니다. 따라서 숫자(종목 코드 번호)를 가져와 code에 입력하는 것입니다. 한번 더 말씀 드리지만 keys()는 딕셔너리의 제일 앞에 있는 내용이라 생각하시면 됩니다.

 - code_list.append(code) :  code객체에 저장된 종목 코드 번호를 code_list에 저장합니다. 이때, 사용되는 함수는 append로써 특정 번호를 copy 하는 역할을 합니다. 리스트에 어떤 값을 추가하고 싶으시면 append를 사용하시면 됩니다.

 - for idx, code in enumerate(code_list) : enumerate 함수를 사용하여 code_list에 있는 코드 번호들에 번호를 부여하여 하니씩 나열시킵니다. ex. [1, 12345], [2, 45678].....라는 형식으로 코드 번호가 들어갑니다. code_list안에 들어있는 계좌잔고 만큼 for문이 돌아가게 됩니다.

 - QTest.qWait(1000) : 1초씩(1000 ms) 지연을 발생시킵니다. 키움 서버에 짧은 시간 안에 너무 많은 명령을 전송하는 순간 계좌가 일시 정지하게 됩니다.(키움 서버 보호 차원), 따라서 일정 지연이 필요합니다. 보통 3.6초(3600 ms)마다 1번씩 명령을 전송하는 것이 안전하나, 적은 횟수 내에서 1초 마다 키움 서버에 명령을 전송하는 것도 괜찮을 것 같습니다.

 - self.k.kiwoom.dynamicCall("DisconnectRealData(Qstring), self.Find_down_Screen) : 기존에 명령된 self.Find_down_Screen에 대한 접속을 끊는 역할을 합니다. 이때 DisconnectRealData가 스크린에 들어 있는 코드에 대하여 접속을 끊는 역할을 합니다. 하나의 스크린에 50개 이상의 주문이 들어가게 되면 에러가 발생할 수 있으므로 한번 주문마다 이전 주문에 대한 기록을 끊는 것도 효율적이라 생각합니다. 물론 10~49개의 주문마다 스크린 번호를 바꿔 주거나 끊어주는 것도 답입니다. 선택은 여러분의 몫이죠.

 - self.code_in_all = code : code에 입력된 종목 코드 번호들을 다시 self.code_in_all에 입력시킵니다. 위험도 판단 시 사용될 예정입니다.

 - date_today = datetime.today().strftime(%Y%m%d) : 오늘은 날짜를 년월일로 나타냅니다.

 - date_prev = datetime.today() - timedelta(10)) : 오늘로부터 10일 전 날짜를 가져옵니다.

 - date_prev = date_prev.strftime(%Y%m%d) : 10일 전 날짜를 년월일로 나타냅니다.

 - 위의 부분이 아주 중요합니다. 기관 평균가를 받아오는 날짜를 정의하는 곳입니다.

 

2. opt100045 서버에 기관매매동향 내역 요청하기

서버에 내역을 요청하는 방법은 17강에 아주 자세히 설명되어 있으니 참고 바랍니다.

opt100045 주문 전송
opt100045 주문 전송

 

 - self.k.kiwoom에 접속해(kiwoom 스크립트) 키움 서버에 명령을 전송할 수 있는 기능을 할당 받습니다.

 - dynamicCall을 사용해 키움서버로 명령을 보냅니다.

 - SetInputValue(QString, QString)", 원하는 값, 입력값)을 사용해 KOA Studio에서 정의한 값들을 넣어 줍니다. 이 값을 이용해 주문을 전송합니다. 값들은 종목코드/시작일자/종료일자/기관추정단가구분/외인추정단가구분/누적기간/기간구분으로 분류됩ㄴ디ㅏ.

 - CommRqData(String, String, int, string)", 고유명사, opt10045, 0, 스크린 번호)를 이용해 주문 후 반환된 값들을 받아오고 저장합니다. 

 - SetInputValue과 CommRqData는 한몸이라 생각하시면 편하실 것입니다.

 - self.detail_account_info_event_loop.exec_() : 주문전송과 받아오기가 마무리될 때까지 다음 코드가 실행되지 않도록 막아주는 역할을 합니다. 앞선 강의에도 배웠지만 exec_()는 무한루프의 기능을 하며, 명령이 완전히 마무리 되면 exit()를 사용하여 다음 강의에서 무한루프를 끊어 줄 것입니다.

 - 아래는 KOA Stuio이니 참고 바랍니다.

KOA Studio 화면
KOA Studio 화면

3. 요약 및 코드 공개

 (1) 계좌잔고 평가내역에 저장된 종목 코드번호를 가져옵니다.

 (2) 각 종목 코드마다의 기관 평균가를 검색할 기간을 설정합니다.

 (3) 각 종목 코드마다의 기관 평균가에 대한 정보를 키움 서버에 요청하고 관련 정보를 저장합니다.

 (4) 받아오는 부분은 다음 강에서 공개하도록 하겠습니다.

반응형
from PyQt5.QtCore import *           # eventloop/스레드를 사용 할 수 있는 함수 가져옴.
from kiwoom import Kiwoom            # 로그인을 위한 클래스
from PyQt5.QtWidgets import *        # PyQt import
from PyQt5.QtTest import *           # 시간관련 함수
from datetime import datetime, timedelta    # 특정 일자를 조회


class Thread2(QThread):
    def __init__(self, parent):     # 부모의 윈도우 창을 가져올 수 있다.
        super().__init__(parent)    # 부모의 윈도우 창을 초기화 한다.
        self.parent = parent        # 부모의 윈도우를 사용하기 위한 조건

        ################## 키움서버 함수를 사용하기 위해서 kiwoom의 능력을 상속 받는다.
        self.k = Kiwoom()
        ################## 사용되는 변수
        self.Find_down_Screen = "1200"  # 계좌평가잔고내역을 받기위한 스크린
        self.code_in_all = None  # 1600개 코드 중 1개 코드, 쌓이지 않고 계속 갱신

        ###### 슬롯
        self.k.kiwoom.OnReceiveTrData.connect(self.trdata_slot)  # 내가 알고 있는 Tr 슬롯에다 특정 값을 던져 준다.

        ###### EventLoop
        self.detail_account_info_event_loop = QEventLoop()  # 계좌 이벤트루프

        ###### 기관외국인 평균가 가져오기
        self.C_K_F_class()



    def C_K_F_class(self):

        code_list = []

        for code in self.k.acc_portfolio.keys():
            code_list.append(code)

        print("계좌 종목 개수 %s" % (code_list))

        #self.parent.progressBar5.setMaximum(len(code_list) - 1) /차 후 설명 드리겠습니다.

        for idx, code in enumerate(code_list):
            #self.parent.progressBar5.setValue(idx) / 차 후 설명드리겠습니다.

            QTest.qWait(1000)

            self.k.kiwoom.dynamicCall("DisconnectRealData(QString)", self.Find_down_Screen)  # 해당 스크린을 끊고 다시 시작

            self.code_in_all = code  # 종목코드 선언 (중간에 코드 정보 받아오기 위해서)
            print("%s / %s : 종목 검사 중 코드이름 : %s." % (idx + 1, len(code_list), self.code_in_all))

            date_today = datetime.today().strftime("%Y%m%d")
            date_prev = datetime.today() - timedelta(10)  # 넉넉히 10일전의 데이터를 받아온다. 또는 20일이상 데이터도 필요
            date_prev = date_prev.strftime("%Y%m%d")

            self.k.kiwoom.dynamicCall("SetInputValue(QString, QString)", "종목코드", code)
            self.k.kiwoom.dynamicCall("SetInputValue(QString, QString)", "시작일자", date_prev)
            self.k.kiwoom.dynamicCall("SetInputValue(QString, QString)", "종료일자", date_today)
            self.k.kiwoom.dynamicCall("SetInputValue(QString, QString)", "기관추정단가구분", "1")
            self.k.kiwoom.dynamicCall("SetInputValue(QString, QString)", "외인추정단가구분", "1")
            self.k.kiwoom.dynamicCall("CommRqData(String, String, int, String)", "종목별기관매매추이요청2", "opt10045", "0", self.Find_down_Screen)
            self.detail_account_info_event_loop.exec_()
반응형

.link_tit