촬리의늘솔길

주식자동매매 종합반 3주차 본문

✍~2022/주식자동매매

주식자동매매 종합반 3주차

리촬리 2021. 8. 21. 22:06

KOA Studio란?

키움 API 사용 설명을 위한 실행파일입니다.

 

개발가이드 학습!

각 항목별 '기본설명', '기본동작', '관련함수'가 분류되어있음

 

'기본설명', '기본동작' 클릭 시 각 항목 별 설명이 가운데 창에 나옴

주식기본정보요청 해보기

-[메뉴]-[파일]-[Open API 접속]

-[왼쪽 아래 탭]-[TR 목록]-[opt10001: 주식기본정보요청]

-오른쪽 속성 창의 종목코드에 '005930' 입력 후 '조회' 버튼 클릭

-결과 확인

연결확인 오류 뜸

로그인이 되어있어야 함

로그인하니 잘 됨.

 

02. 키움 API에 대해서 좀 더 자세히 알아봅시다!

3) 일반 함수와 이벤트 함수로 구성 된 키움 API!

1.

  1. 일반 함수란?
    • 호출 시 바로 반환 값을 받을 수 있는 함수
    • 키움서버에 데이터를 전송하지 않는 경우 ( 일반함수와 이벤트함수의 근본적인 차이점)
    • 키움서버쪽에 데이터를 요청하는것이 아니기때문에 일반함수라고..
  2. 이벤트 함수란?(aka 콜백 함수)
  • 호출 시 다른 함수에서 결과를 받게 되는 함수
  • 키움서버에 데이터를 전송하는 경우

데이터 요청할때

SetInputValue (인자값 넣어주고)

CommRqData(데이터 요청)

OnReceiveRealData(이벤트 발생)

결과전달은 요청한곳이 아닌, 함수에 전달됨

4) 모든 이벤트 함수는 비동기 방식!

  • 데이터 요청 순서대로 결과를 전달 받는 것이 아님(주의 필요)

순서는 장담할 수 없다고함.

03. SpartaQuant.py 에 대해 알아봅시다!

더보기

SpartaQuant.py

from PyQt5.QtCore import QObject, pyqtSlot
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QAxContainer import QAxWidget
from PyQt5.QtCore import QThread
from threading import Lock, Thread
from collections import deque
from sys import argv
from time import sleep
from json import dumps
from os import kill, getpid


class SyncRequestDecorator:
    """키움 API 비동기 함수 데코레이터
    """

    @staticmethod
    def kiwoom_sync_request(func):
        def func_wrapper(self, *args, **kwargs):
            self.request_thread_worker.request_queue.append((func, args, kwargs))

        return func_wrapper

    @staticmethod
    def kiwoom_sync_callback(func):
        def func_wrapper(self, *args, **kwargs):
            # print("[%s] 키움 함수 콜백: %s %s" % (func.__name__, args, kwargs))

            func(self, *args, **kwargs)  # 콜백 함수 호출
            if self.request_thread_worker.request_thread_lock.locked():
                self.request_thread_worker.request_thread_lock.release()  # 요청 쓰레드 잠금 해제

            # 재시도 횟수 초기화
            myWindow._kiwoom.request_thread_worker.retry_count = 0

        return func_wrapper


class RequestThreadWorker(QObject):
    def __init__(self):
        """요청 쓰레드
        """
        super().__init__()
        self.request_queue = deque()
        self.request_thread_lock = Lock()

        # 간혹 요청에 대한 결과가 콜백으로 오지 않음
        # 마지막 요청을 저장해 뒀다가 일정 시간이 지나도 결과가 안오면 재요청
        self.retry_timer = None
        self.max_retry = 3
        self.retry_count = 0

    def retry(self, request):
        self.retry_count = self.retry_count + 1

        if self.retry_count == self.max_retry:
            kill(getpid(), 9)

        # print("[%s] 키움 함수 재시도: %s %s" % (request[0].__name__, request[1], request[2]))

        self.request_queue.appendleft(request)

    def run(self):
        last_request = None
        while True:
            # 큐에 요청이 있으면 하나 뺌
            # 없으면 블락상태로 있음

            try:
                request = self.request_queue.popleft()
            except IndexError as e:
                if self.request_thread_lock.locked():
                    if not self.request_thread_lock.acquire(blocking=True, timeout=5):
                        self.request_thread_lock.release()
                        self.retry(last_request)
                    else:
                        self.request_thread_lock.release()

                sleep(0.2)
                continue

            # 요청에대한 결과 대기
            if not self.request_thread_lock.acquire(blocking=True, timeout=5):
                if self.request_thread_lock.locked():
                    self.request_thread_lock.release()
                # 요청 실패
                sleep(0.2)

                self.request_queue.appendleft(request)
                self.retry(last_request)  # 실패한 요청 재시도
            else:
                last_request = request
                # 요청 실행
                # print("[%s] 키움 함수 실행: %s %s" % (request[0].__name__, request[1], request[2]))
                request[0](trader, *request[1], **request[2])

            sleep(1)  # 0.2초 이상 대기 후 마무리


class Kiwoom(QObject):
    # Variables

    def __init__(self):
        super().__init__()
        self.ocx = QAxWidget("KHOPENAPI.KHOpenAPICtrl.1")
        self.ocx.OnEventConnect[int].connect(self.OnEventConnect)
        self.ocx.OnReceiveMsg[str, str, str, str].connect(self.OnReceiveMsg)
        self.ocx.OnReceiveTrData[str, str, str, str, str, int, str, str, str].connect(self.OnReceiveTrData)
        self.ocx.OnReceiveRealData[str, str, str].connect(self.OnReceiveRealData)
        self.ocx.OnReceiveChejanData[str, int, str].connect(self.OnReceiveChejanData)
        self.ocx.OnReceiveConditionVer[int, str].connect(self.OnReceiveConditionVer)
        self.ocx.OnReceiveTrCondition[str, str, str, int, int].connect(self.OnReceiveTrCondition)
        self.ocx.OnReceiveRealCondition[str, str, str, str].connect(self.OnReceiveRealCondition)

        # 요청 쓰레드
        self.request_thread_worker = RequestThreadWorker()
        self.request_thread = QThread()
        self.request_thread_worker.moveToThread(self.request_thread)
        self.request_thread.started.connect(self.request_thread_worker.run)
        self.request_thread.start()

    # 로그인
    # 0 - 성공, 음수값은 실패
    @pyqtSlot(result=int)
    def CommConnect(self):
        return self.ocx.dynamicCall("CommConnect()")

    # 로그인 상태 확인
    # 0:미연결, 1:연결완료, 그외는 에러
    @pyqtSlot(result=int)
    def GetConnectState(self):
        res = self.ocx.dynamicCall("GetConnectState()")
        if res == 1:
            print('Online')
        else:
            print('Offline')
        return res

    # 로그 아웃
    @pyqtSlot()
    def CommTerminate(self):
        self.ocx.dynamicCall("CommTerminate()")

    # 로그인한 사용자 정보를 반환한다.
    # “ACCOUNT_CNT” – 전체 계좌 개수를 반환한다.
    # "ACCNO" – 전체 계좌를 반환한다. 계좌별 구분은 ‘;’이다.
    # “USER_ID” - 사용자 ID를 반환한다.
    # “USER_NAME” – 사용자명을 반환한다.
    # “KEY_BSECGB” – 키보드보안 해지여부. 0:정상, 1:해지
    # “FIREW_SECGB” – 방화벽 설정 여부. 0:미설정, 1:설정, 2:해지
    @pyqtSlot(str, result=str)
    def GetLoginInfo(self, tag):
        return self.ocx.dynamicCall("GetLoginInfo(QString)", [tag])

    # Tran 입력 값을 서버통신 전에 입력값일 저장한다.
    @pyqtSlot(str, str)
    def SetInputValue(self, id, value):
        self.ocx.dynamicCall("SetInputValue(QString, QString)", [id, value])

    # 통신 데이터를 송신한다.
    # 0이면 정상
    # OP_ERR_SISE_OVERFLOW – 과도한 시세조회로 인한 통신불가
    # OP_ERR_RQ_STRUCT_FAIL – 입력 구조체 생성 실패
    # OP_ERR_RQ_STRING_FAIL – 요청전문 작성 실패
    # OP_ERR_NONE – 정상처리
    @pyqtSlot(str, str, int, str, result=int)
    def CommRqData(self, rQName, trCode, prevNext, screenNo):
        return self.ocx.dynamicCall("CommRqData(QString, QString, int, QString)", [rQName, trCode, prevNext, screenNo])

    # 수신 받은 데이터의 반복 개수를 반환한다.
    @pyqtSlot(str, str, result=int)
    def GetRepeatCnt(self, trCode, recordName):
        return self.ocx.dynamicCall("GetRepeatCnt(QString, QString)", [trCode, recordName])

    # Tran 데이터, 실시간 데이터, 체결잔고 데이터를 반환한다.
    # 1. Tran 데이터o
    # 2. 실시간 데이터
    # 3. 체결 데이터
    # 1. Tran 데이터
    # sJongmokCode : Tran명
    # sRealType : 사용안함
    # sFieldName : 레코드명
    # nIndex : 반복인덱스
    # sInnerFieldName: 아이템명
    # 2. 실시간 데이터
    # sJongmokCode : Key Code
    # sRealType : Real Type
    # sFieldName : Item Index (FID)
    # nIndex : 사용안함
    # sInnerFieldName:사용안함
    # 3. 체결 데이터
    # sJongmokCode : 체결구분
    # sRealType : “-1”
    # sFieldName : 사용안함
    # nIndex : ItemIndex
    # sInnerFieldName:사용안함
    @pyqtSlot(str, str, str, int, str, result=str)
    def CommGetData(self, jongmokCode, realType, fieldName, index, innerFieldName):
        return self.ocx.dynamicCall("CommGetData(QString, QString, QString, int, QString)",
                                    [jongmokCode, realType, fieldName, index, innerFieldName]).strip()

    # strRealType – 실시간 구분
    # nFid – 실시간 아이템
    # Ex) 현재가출력 - openApi.GetCommRealData(“주식시세”, 10);
    # 참고)실시간 현재가는 주식시세, 주식체결 등 다른 실시간타입(RealType)으로도 수신가능
    @pyqtSlot(str, int, result=str)
    def GetCommRealData(self, realType, fid):
        return self.ocx.dynamicCall("GetCommRealData(QString, int)", [realType, fid]).strip()

    # 주식 주문을 서버로 전송한다.
    # sRQName - 사용자 구분 요청 명
    # sScreenNo - 화면번호[4]
    # sAccNo - 계좌번호[10]
    # nOrderType - 주문유형 (1:신규매수, 2:신규매도, 3:매수취소, 4:매도취소, 5:매수정정, 6:매도정정)
    # sCode, - 주식종목코드
    # nQty – 주문수량
    # nPrice – 주문단가
    # sHogaGb - 거래구분
    # sHogaGb – 00:지정가, 03:시장가, 05:조건부지정가, 06:최유리지정가, 07:최우선지정가, 10:지정가IOC, 13:시장가IOC, 16:최유리IOC, 20:지정가FOK, 23:시장가FOK, 26:최유리FOK, 61:장전시간외종가, 62:시간외단일가, 81:장후시간외종가
    # ※ 시장가, 최유리지정가, 최우선지정가, 시장가IOC, 최유리IOC, 시장가FOK, 최유리FOK, 장전시간외, 장후시간외 주문시 주문가격을 입력하지 않습니다.
    # ex)
    # 지정가 매수 - openApi.SendOrder("RQ_1", "0101", "5015123410", 1, "000660", 10, 48500, "00", "");
    # 시장가 매수 - openApi.SendOrder("RQ_1", "0101", "5015123410", 1, "000660", 10, 0, "03", "");
    # 매수 정정 - openApi.SendOrder("RQ_1","0101", "5015123410", 5, "000660", 10, 49500, "00", "1");
    # 매수 취소 - openApi.SendOrder("RQ_1", "0101", "5015123410", 3, "000660", 10, "00", "2");
    # sOrgOrderNo – 원주문번호
    @pyqtSlot(str, str, str, int, str, int, int, str, str, result=int)
    def SendOrder(self, rQName, screenNo, accNo, orderType, code, qty, price, hogaGb, orgOrderNo):
        print("sendOrder", rQName, screenNo, accNo, orderType, code, qty, price, hogaGb, orgOrderNo)
        return self.ocx.dynamicCall("SendOrder(QString, QString, QString, int, QString, int, int, QString, QString)",
                                    [rQName, screenNo, accNo, orderType, code, qty, price, hogaGb, orgOrderNo])

    # 체결잔고 데이터를 반환한다.
    @pyqtSlot(int, result=str)
    def GetChejanData(self, fid):
        return self.ocx.dynamicCall("GetChejanData(int)", [fid])

    # 서버에 저장된 사용자 조건식을 가져온다.
    @pyqtSlot(result=int)
    def GetConditionLoad(self):
        res = self.ocx.dynamicCall("GetConditionLoad()")
        if res == 1:
            print('GetConditionLoad() success')
        else:
            print('GetConditionLoad() failed')

    # 조건검색 조건명 리스트를 받아온다.
    # 조건명 리스트(인덱스^조건명)
    # 조건명 리스트를 구분(“;”)하여 받아온다
    @pyqtSlot(result=str)
    def GetConditionNameList(self):
        return self.ocx.dynamicCall("GetConditionNameList()")

    # 조건검색 종목조회TR송신한다.
    # LPCTSTR strScrNo : 화면번호
    # LPCTSTR strConditionName : 조건명
    # int nIndex : 조건명인덱스
    # int nSearch : 조회구분(0:일반조회, 1:실시간조회, 2:연속조회)
    # 1:실시간조회의 화면 개수의 최대는 10개
    @pyqtSlot(str, str, int, int)
    @SyncRequestDecorator.kiwoom_sync_request
    def SendCondition(self, scrNo, conditionName, index, search):
        self.ocx.dynamicCall("SendCondition(QString,QString, int, int)", [scrNo, conditionName, index, search])

    # 실시간 조건검색을 중지합니다.
    # ※ 화면당 실시간 조건검색은 최대 10개로 제한되어 있어서 더 이상 실시간 조건검색을 원하지 않는 조건은 중지해야만 카운트 되지 않습니다.
    @pyqtSlot(str, str, int)
    def SendConditionStop(self, scrNo, conditionName, index):
        self.ocx.dynamicCall("SendConditionStop(QString, QString, int)", [scrNo, conditionName, index])

    # 복수종목조회 Tran을 서버로 송신한다.
    # OP_ERR_RQ_STRING – 요청 전문 작성 실패
    # OP_ERR_NONE - 정상처리
    #
    # sArrCode – 종목간 구분은 ‘;’이다.
    # nTypeFlag – 0:주식관심종목정보, 3:선물옵션관심종목정보
    @pyqtSlot(str, bool, int, int, str, str)
    @SyncRequestDecorator.kiwoom_sync_request
    def CommKwRqData(self, arrCode, next, codeCount, typeFlag, rQName, screenNo):
        self.ocx.dynamicCall("CommKwRqData(QString, QBoolean, int, int, QString, QString)",
                             [arrCode, next, codeCount, typeFlag, rQName, screenNo])

    # 실시간 등록을 한다.
    # strScreenNo : 화면번호
    # strCodeList : 종목코드리스트(ex: 039490;005930;…)
    # strFidList : FID번호(ex:9001;10;13;…)
    #   9001 – 종목코드
    #   10 - 현재가
    #   13 - 누적거래량
    # strOptType : 타입(“0”, “1”)
    # 타입 “0”은 항상 마지막에 등록한 종목들만 실시간등록이 됩니다.
    # 타입 “1”은 이전에 실시간 등록한 종목들과 함께 실시간을 받고 싶은 종목을 추가로 등록할 때 사용합니다.
    # ※ 종목, FID는 각각 한번에 실시간 등록 할 수 있는 개수는 100개 입니다.
    @pyqtSlot(str, str, str, int, result=int)
    def SetRealReg(self, screenNo, codeList, fidList, optType):
        return self.ocx.dynamicCall("SetRealReg(QString, QString, QString, QString)",
                                    [screenNo, codeList, fidList, optType])

    # 종목별 실시간 해제
    # strScrNo : 화면번호
    # strDelCode : 실시간 해제할 종목코드
    # -화면별 실시간해제
    # 여러 화면번호로 걸린 실시간을 해제하려면 파라메터의 화면번호와 종목코드에 “ALL”로 입력하여 호출하시면 됩니다.
    # SetRealRemove(“ALL”, “ALL”);
    # 개별화면별로 실시간 해제 하시려면 파라메터에서 화면번호는 실시간해제할
    # 화면번호와 종목코드에는 “ALL”로 해주시면 됩니다.
    # SetRealRemove(“0001”, “ALL”);
    # -화면의 종목별 실시간해제
    # 화면의 종목별로 실시간 해제하려면 파라메터에 해당화면번호와 해제할
    # 종목코드를 입력하시면 됩니다.
    # SetRealRemove(“0001”, “039490”);
    @pyqtSlot(str, str)
    def SetRealRemove(self, scrNo, delCode):
        self.ocx.dynamicCall("SetRealRemove(QString, QString)", [scrNo, delCode])

    # 차트 조회한 데이터 전부를 배열로 받아온다.
    # LPCTSTR strTrCode : 조회한TR코드
    # LPCTSTR strRecordName: 조회한 TR명
    # ※항목의 위치는 KOA Studio의 TR목록 순서로 데이터를 가져옵니다.
    # 예로 OPT10080을 살펴보면 OUTPUT의 멀티데이터의 항목처럼 현재가, 거래량, 체결시간등 순으로 항목의 위치가 0부터 1씩 증가합니다.
    @pyqtSlot(str, str, result=str)
    def GetCommDataEx(self, trCode, recordName):
        return dumps(self.ocx.dynamicCall("GetCommDataEx(QString, QString)", [trCode, recordName]))

    # 차트의 특정 조회데이터를 받아온다.
    @pyqtSlot(str, str, int, str, result=str)
    def GetCommData(self, trCode, recordName, nIndex, itemName):
        return self.ocx.dynamicCall("GetCommData(QString, QString, int, QString)",
                                    [trCode, recordName, nIndex, itemName])

    # 리얼 시세를 끊는다.
    # 화면 내 모든 리얼데이터 요청을 제거한다.
    # 화면을 종료할 때 반드시 위 함수를 호출해야 한다.
    # Ex) openApi.DisconnectRealData(“0101”);
    @pyqtSlot(str)
    def DisconnectRealData(self, scnNo):
        self.ocx.dynamicCall("DisconnectRealData(QString)", [scnNo])

    # 종목코드의 한글명을 반환한다.
    # strCode – 종목코드
    # 종목한글명
    @pyqtSlot(str, result=str)
    def GetMasterCodeName(self, code):
        return self.ocx.dynamicCall("GetMasterCodeName(QString)", [code])

    # 국내 주식 시장별 종목코드를 ;로 구분하여 전달
    # strMarket – 종목코드
    # 마켓 구분값
    # 0 : 장내
    # 10 : 코스닥
    # 3 : ELW
    # 8 : ETF
    # 50 : KONEX
    # ...
    @pyqtSlot(str, result=str)
    def GetCodeListByMarket(self, strMarket):
        return self.ocx.dynamicCall("GetCodeListByMarket(QString)", [strMarket])

    # 입력한 종목의 전일가를 전달
    # strCode – 종목코드
    def GetMasterLastPrice(self, code):
        return self.ocx.dynamicCall("GetMasterLastPrice(QString)", [code])

    # 통신 연결 상태 변경시 이벤트
    # nErrCode가 0이면 로그인 성공, 음수면 실패
    def OnEventConnect(self, errCode):
        if errCode == 0:
            print('로그인 성공!')
        else:
            print('Error')
            kill(getpid(), 9)

    # 수신 메시지 이벤트
    def OnReceiveMsg(self, scrNo, rQName, trCode, msg):
        print('_OnReceiveMsg()', scrNo, rQName, trCode, msg)

    # 실시간 시세 이벤트
    def OnReceiveRealData(self, jongmokCode, realType, realData):
        # print('_OnReceiveRealData', jongmokCode, realType, realData)
        pass

    # 체결데이터를 받은 시점을 알려준다.
    # sGubun – 0:주문체결통보, 1:잔고통보, 3:특이신호
    # sFidList – 데이터 구분은 ‘;’ 이다.
    def OnReceiveChejanData(self, gubun, itemCnt, fidList):
        # print('_OnReceiveChejanData()', gubun, itemCnt, fidList)
        pass

    # 로컬에 사용자조건식 저장 성공여부 응답 이벤트
    def OnReceiveConditionVer(self, ret, msg):
        print('_OnReceiveConditionVer()', ret, msg)

    # 편입, 이탈 종목이 실시간으로 들어옵니다.
    # strCode : 종목코드
    # strType : 편입(“I”), 이탈(“D”)
    # strConditionName : 조건명
    # strConditionIndex : 조건명 인덱스
    def OnReceiveRealCondition(self, code, strType, conditionName, conditionIndex):
        print('_OnReceiveRealCondition()', code, strType, conditionName, conditionIndex)

    @SyncRequestDecorator.kiwoom_sync_callback
    def OnReceiveTrCondition(self, scrNo, codeList, conditionName, index, next, **kwargs):
        print('_OnReceiveTrCondition()', scrNo, codeList, conditionName, index, next)

        # 조건식 결과 li_code에 리스트로 저장
        self.li_code = codeList.split(';')[:-1]
        self.cnt_stock = len(self.li_code)

    # Tran 수신시 이벤트
    @SyncRequestDecorator.kiwoom_sync_callback
    def OnReceiveTrData(self, scrNo, rQName, trCode, recordName, prevNext, dataLength, errorCode, message, splmMsg,
                        **kwargs):
        print('OnReceiveTrData()', scrNo, rQName, trCode, recordName, prevNext, dataLength, errorCode, message,
              splmMsg)

class SpartaQuant(QMainWindow):
    def __init__(self):
        super().__init__()
        self._kiwoom = Kiwoom()

        t1 = Thread(target=self.main_thread)
        t1.daemon = True
        t1.start()

    def main_thread(self):
        ###############################
        # 1. 로그인                    #
        ###############################

        # 로그인 시도
        self._kiwoom.CommConnect()

        # 로그인 완료 대기
        while True:
            if self._kiwoom.GetLoginInfo("ACCOUNT_CNT") != "":
                break
            print("로그인 대기 중...")
            sleep(5)

if __name__ == "__main__":
    app = QApplication(argv)
    myWindow = SpartaQuant()
    trader = myWindow._kiwoom
    app.exec_()

1. 키움 API 기능이 담긴 코어 클래스(Core Class)

 

키움 API 이벤트 함수 호출 및 응답 처리 시 추가 코딩 필요

2.class SpartaQuant

우리가 코드로 채워나가야 할 메인 클래스

main_thread 내 코드가 자동매매를 위한 메인코드가 될 것임

3.class SyncRequestDecorator

키움 API 비동기 함수 데코레이터

SyncRequestDecorator를 통해 요청-응답이 순서대로 일어나게 함

  • 응답이 오기 전에 다른 요청을 보내지 않게 할 수 있음
  • 함수 단위로 설정해야 함!!!!!!
  • 키움서버에 데이터를 요청하는 모든 함수에 사용!

 

class RequestThreadWorker

  1. 키움 API 비동기 함수 사용 안정화를 위한 클래스
  2. 일정 시간 동안 요청-응답이 이루어지지 않으면 다시 실행

04. 키움 API의 로그인 기능 사용하기

 

KOA Studio에서 관련 함수 확인!

-CommConnect() - 로그인 시도

-GetLoginInfo() - 계정정보 조회함수

수업의 목적이 '주식'알고리즘을 만들어서 구현하는 것이지

구현하는 중간에 class의 상세한 기능을 아는것이 아니라하넹..

- 코드 확인!

 

  1. class SpartaQuant → def main_thread
    • commConnect() 를 사용한 로그인 시도 
    • ############################### # 1. 로그인 # ############################### # 로그인 시도 self._kiwoom.CommConnect()​
    • GetLoginInfo() 를 통한 로그인 성공 시까지 대기
      • 주의: GetConnectState()는 단순 키움 서버 접속을 뜻함! (로그인 여부는 알 수 없음)

      # 로그인 완료 대기
      while True:
          if self._kiwoom.GetLoginInfo("ACCOUNT_CNT") != "":
              break
          print("로그인 대기 중...")
          sleep(5)​

05. 주식 기본 정보 가져오기

먼저 TR에 대해 알아보자!

TR이란?

  • TR은 Transaction의 줄임말
  • 키움서버에 데이터 요청 시 TR을 사용하여야 함

TR 종류

  • 데이터 요청
    • SetInputValue() → CommRqData() → [이벤트] OnReceiveTrData() 
  • 조건식 결과요청
    • GetConditionLoad() → SendCondition() → [이벤트] OnReceiveTrCondition()
       
    •  
    • TR 사용 시 주의사항
      • 비동기 식이므로, TR 함수는 SyncRequestDecorator를 사용하여 동기화 시켜야 함!

KOA Studio에서 관련 함수 확인! (개발가이드 -> 조회와 실시간데이터처리)

SetInputValue() - 조회 요청 시 인자값을 지정해주는 함수

 

CommRqData() - 조회 요청 함수

OnReceiveTrData() - CommRqData 응답이왔을때 호출되는 함수(이벤트 함수)

GetCommData() - OnReceiveTrData 호출 시 결과 값(항목을 하나씩)을 추출하는 함수

그냥 이렇게 사용하는구나~ 느낌

 

싱글데이터 : 하나의 꾸러미가 되어서 오는것

멀티데이터 : 두가지 정보에 대해 한번에 요청하였을때 각각의 꾸러미로 오는것

 

KOA Studio에서 관련 TR 목록 확인!

  • TR 목록에서 활용하고 싶은 요청 선택
  • 주식 기본정보 코드 추가함
더보기
from PyQt5.QtCore import QObject, pyqtSlot
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QAxContainer import QAxWidget
from PyQt5.QtCore import QThread
from threading import Lock, Thread
from collections import deque
from sys import argv
from time import sleep
from json import dumps
from os import kill, getpid


class SyncRequestDecorator:
    """키움 API 비동기 함수 데코레이터
    """

    @staticmethod
    def kiwoom_sync_request(func):
        def func_wrapper(self, *args, **kwargs):
            self.request_thread_worker.request_queue.append((func, args, kwargs))

        return func_wrapper

    @staticmethod
    def kiwoom_sync_callback(func):
        def func_wrapper(self, *args, **kwargs):
            # print("[%s] 키움 함수 콜백: %s %s" % (func.__name__, args, kwargs))

            func(self, *args, **kwargs)  # 콜백 함수 호출
            if self.request_thread_worker.request_thread_lock.locked():
                self.request_thread_worker.request_thread_lock.release()  # 요청 쓰레드 잠금 해제

            # 재시도 횟수 초기화
            myWindow._kiwoom.request_thread_worker.retry_count = 0

        return func_wrapper


class RequestThreadWorker(QObject):
    def __init__(self):
        """요청 쓰레드
        """
        super().__init__()
        self.request_queue = deque()
        self.request_thread_lock = Lock()

        # 간혹 요청에 대한 결과가 콜백으로 오지 않음
        # 마지막 요청을 저장해 뒀다가 일정 시간이 지나도 결과가 안오면 재요청
        self.retry_timer = None
        self.max_retry = 3
        self.retry_count = 0

    def retry(self, request):
        self.retry_count = self.retry_count + 1

        if self.retry_count == self.max_retry:
            kill(getpid(), 9)

        # print("[%s] 키움 함수 재시도: %s %s" % (request[0].__name__, request[1], request[2]))

        self.request_queue.appendleft(request)

    def run(self):
        last_request = None
        while True:
            # 큐에 요청이 있으면 하나 뺌
            # 없으면 블락상태로 있음

            try:
                request = self.request_queue.popleft()
            except IndexError as e:
                if self.request_thread_lock.locked():
                    if not self.request_thread_lock.acquire(blocking=True, timeout=5):
                        self.request_thread_lock.release()
                        self.retry(last_request)
                    else:
                        self.request_thread_lock.release()

                sleep(0.2)
                continue

            # 요청에대한 결과 대기
            if not self.request_thread_lock.acquire(blocking=True, timeout=5):
                if self.request_thread_lock.locked():
                    self.request_thread_lock.release()
                # 요청 실패
                sleep(0.2)

                self.request_queue.appendleft(request)
                self.retry(last_request)  # 실패한 요청 재시도
            else:
                last_request = request
                # 요청 실행
                # print("[%s] 키움 함수 실행: %s %s" % (request[0].__name__, request[1], request[2]))
                request[0](trader, *request[1], **request[2])

            sleep(1)  # 0.2초 이상 대기 후 마무리


class Kiwoom(QObject):
    # Variables

    def __init__(self):
        super().__init__()
        self.ocx = QAxWidget("KHOPENAPI.KHOpenAPICtrl.1")
        self.ocx.OnEventConnect[int].connect(self.OnEventConnect)
        self.ocx.OnReceiveMsg[str, str, str, str].connect(self.OnReceiveMsg)
        self.ocx.OnReceiveTrData[str, str, str, str, str, int, str, str, str].connect(self.OnReceiveTrData)
        self.ocx.OnReceiveRealData[str, str, str].connect(self.OnReceiveRealData)
        self.ocx.OnReceiveChejanData[str, int, str].connect(self.OnReceiveChejanData)
        self.ocx.OnReceiveConditionVer[int, str].connect(self.OnReceiveConditionVer)
        self.ocx.OnReceiveTrCondition[str, str, str, int, int].connect(self.OnReceiveTrCondition)
        self.ocx.OnReceiveRealCondition[str, str, str, str].connect(self.OnReceiveRealCondition)

        # 요청 쓰레드
        self.request_thread_worker = RequestThreadWorker()
        self.request_thread = QThread()
        self.request_thread_worker.moveToThread(self.request_thread)
        self.request_thread.started.connect(self.request_thread_worker.run)
        self.request_thread.start()

    # 로그인
    # 0 - 성공, 음수값은 실패
    @pyqtSlot(result=int)
    def CommConnect(self):
        return self.ocx.dynamicCall("CommConnect()")

    # 로그인 상태 확인
    # 0:미연결, 1:연결완료, 그외는 에러
    @pyqtSlot(result=int)
    def GetConnectState(self):
        res = self.ocx.dynamicCall("GetConnectState()")
        if res == 1:
            print('Online')
        else:
            print('Offline')
        return res

    # 로그 아웃
    @pyqtSlot()
    def CommTerminate(self):
        self.ocx.dynamicCall("CommTerminate()")

    # 로그인한 사용자 정보를 반환한다.
    # “ACCOUNT_CNT” – 전체 계좌 개수를 반환한다.
    # "ACCNO" – 전체 계좌를 반환한다. 계좌별 구분은 ‘;’이다.
    # “USER_ID” - 사용자 ID를 반환한다.
    # “USER_NAME” – 사용자명을 반환한다.
    # “KEY_BSECGB” – 키보드보안 해지여부. 0:정상, 1:해지
    # “FIREW_SECGB” – 방화벽 설정 여부. 0:미설정, 1:설정, 2:해지
    @pyqtSlot(str, result=str)
    def GetLoginInfo(self, tag):
        return self.ocx.dynamicCall("GetLoginInfo(QString)", [tag])

    # Tran 입력 값을 서버통신 전에 입력값일 저장한다.
    @pyqtSlot(str, str)
    def SetInputValue(self, id, value):
        self.ocx.dynamicCall("SetInputValue(QString, QString)", [id, value])

    # 통신 데이터를 송신한다.
    # 0이면 정상
    # OP_ERR_SISE_OVERFLOW – 과도한 시세조회로 인한 통신불가
    # OP_ERR_RQ_STRUCT_FAIL – 입력 구조체 생성 실패
    # OP_ERR_RQ_STRING_FAIL – 요청전문 작성 실패
    # OP_ERR_NONE – 정상처리
    @pyqtSlot(str, str, int, str, result=int)
    def CommRqData(self, rQName, trCode, prevNext, screenNo):
        return self.ocx.dynamicCall("CommRqData(QString, QString, int, QString)", [rQName, trCode, prevNext, screenNo])

    # 수신 받은 데이터의 반복 개수를 반환한다.
    @pyqtSlot(str, str, result=int)
    def GetRepeatCnt(self, trCode, recordName):
        return self.ocx.dynamicCall("GetRepeatCnt(QString, QString)", [trCode, recordName])

    # Tran 데이터, 실시간 데이터, 체결잔고 데이터를 반환한다.
    # 1. Tran 데이터o
    # 2. 실시간 데이터
    # 3. 체결 데이터
    # 1. Tran 데이터
    # sJongmokCode : Tran명
    # sRealType : 사용안함
    # sFieldName : 레코드명
    # nIndex : 반복인덱스
    # sInnerFieldName: 아이템명
    # 2. 실시간 데이터
    # sJongmokCode : Key Code
    # sRealType : Real Type
    # sFieldName : Item Index (FID)
    # nIndex : 사용안함
    # sInnerFieldName:사용안함
    # 3. 체결 데이터
    # sJongmokCode : 체결구분
    # sRealType : “-1”
    # sFieldName : 사용안함
    # nIndex : ItemIndex
    # sInnerFieldName:사용안함
    @pyqtSlot(str, str, str, int, str, result=str)
    def CommGetData(self, jongmokCode, realType, fieldName, index, innerFieldName):
        return self.ocx.dynamicCall("CommGetData(QString, QString, QString, int, QString)",
                                    [jongmokCode, realType, fieldName, index, innerFieldName]).strip()

    # strRealType – 실시간 구분
    # nFid – 실시간 아이템
    # Ex) 현재가출력 - openApi.GetCommRealData(“주식시세”, 10);
    # 참고)실시간 현재가는 주식시세, 주식체결 등 다른 실시간타입(RealType)으로도 수신가능
    @pyqtSlot(str, int, result=str)
    def GetCommRealData(self, realType, fid):
        return self.ocx.dynamicCall("GetCommRealData(QString, int)", [realType, fid]).strip()

    # 주식 주문을 서버로 전송한다.
    # sRQName - 사용자 구분 요청 명
    # sScreenNo - 화면번호[4]
    # sAccNo - 계좌번호[10]
    # nOrderType - 주문유형 (1:신규매수, 2:신규매도, 3:매수취소, 4:매도취소, 5:매수정정, 6:매도정정)
    # sCode, - 주식종목코드
    # nQty – 주문수량
    # nPrice – 주문단가
    # sHogaGb - 거래구분
    # sHogaGb – 00:지정가, 03:시장가, 05:조건부지정가, 06:최유리지정가, 07:최우선지정가, 10:지정가IOC, 13:시장가IOC, 16:최유리IOC, 20:지정가FOK, 23:시장가FOK, 26:최유리FOK, 61:장전시간외종가, 62:시간외단일가, 81:장후시간외종가
    # ※ 시장가, 최유리지정가, 최우선지정가, 시장가IOC, 최유리IOC, 시장가FOK, 최유리FOK, 장전시간외, 장후시간외 주문시 주문가격을 입력하지 않습니다.
    # ex)
    # 지정가 매수 - openApi.SendOrder("RQ_1", "0101", "5015123410", 1, "000660", 10, 48500, "00", "");
    # 시장가 매수 - openApi.SendOrder("RQ_1", "0101", "5015123410", 1, "000660", 10, 0, "03", "");
    # 매수 정정 - openApi.SendOrder("RQ_1","0101", "5015123410", 5, "000660", 10, 49500, "00", "1");
    # 매수 취소 - openApi.SendOrder("RQ_1", "0101", "5015123410", 3, "000660", 10, "00", "2");
    # sOrgOrderNo – 원주문번호
    @pyqtSlot(str, str, str, int, str, int, int, str, str, result=int)
    def SendOrder(self, rQName, screenNo, accNo, orderType, code, qty, price, hogaGb, orgOrderNo):
        print("sendOrder", rQName, screenNo, accNo, orderType, code, qty, price, hogaGb, orgOrderNo)
        return self.ocx.dynamicCall("SendOrder(QString, QString, QString, int, QString, int, int, QString, QString)",
                                    [rQName, screenNo, accNo, orderType, code, qty, price, hogaGb, orgOrderNo])

    # 체결잔고 데이터를 반환한다.
    @pyqtSlot(int, result=str)
    def GetChejanData(self, fid):
        return self.ocx.dynamicCall("GetChejanData(int)", [fid])

    # 서버에 저장된 사용자 조건식을 가져온다.
    @pyqtSlot(result=int)
    def GetConditionLoad(self):
        res = self.ocx.dynamicCall("GetConditionLoad()")
        if res == 1:
            print('GetConditionLoad() success')
        else:
            print('GetConditionLoad() failed')

    # 조건검색 조건명 리스트를 받아온다.
    # 조건명 리스트(인덱스^조건명)
    # 조건명 리스트를 구분(“;”)하여 받아온다
    @pyqtSlot(result=str)
    def GetConditionNameList(self):
        return self.ocx.dynamicCall("GetConditionNameList()")

    # 조건검색 종목조회TR송신한다.
    # LPCTSTR strScrNo : 화면번호
    # LPCTSTR strConditionName : 조건명
    # int nIndex : 조건명인덱스
    # int nSearch : 조회구분(0:일반조회, 1:실시간조회, 2:연속조회)
    # 1:실시간조회의 화면 개수의 최대는 10개
    @pyqtSlot(str, str, int, int)
    @SyncRequestDecorator.kiwoom_sync_request
    def SendCondition(self, scrNo, conditionName, index, search):
        self.ocx.dynamicCall("SendCondition(QString,QString, int, int)", [scrNo, conditionName, index, search])

    # 실시간 조건검색을 중지합니다.
    # ※ 화면당 실시간 조건검색은 최대 10개로 제한되어 있어서 더 이상 실시간 조건검색을 원하지 않는 조건은 중지해야만 카운트 되지 않습니다.
    @pyqtSlot(str, str, int)
    def SendConditionStop(self, scrNo, conditionName, index):
        self.ocx.dynamicCall("SendConditionStop(QString, QString, int)", [scrNo, conditionName, index])

    # 복수종목조회 Tran을 서버로 송신한다.
    # OP_ERR_RQ_STRING – 요청 전문 작성 실패
    # OP_ERR_NONE - 정상처리
    #
    # sArrCode – 종목간 구분은 ‘;’이다.
    # nTypeFlag – 0:주식관심종목정보, 3:선물옵션관심종목정보
    @pyqtSlot(str, bool, int, int, str, str)
    @SyncRequestDecorator.kiwoom_sync_request
    def CommKwRqData(self, arrCode, next, codeCount, typeFlag, rQName, screenNo):
        self.ocx.dynamicCall("CommKwRqData(QString, QBoolean, int, int, QString, QString)",
                             [arrCode, next, codeCount, typeFlag, rQName, screenNo])

    # 실시간 등록을 한다.
    # strScreenNo : 화면번호
    # strCodeList : 종목코드리스트(ex: 039490;005930;…)
    # strFidList : FID번호(ex:9001;10;13;…)
    #   9001 – 종목코드
    #   10 - 현재가
    #   13 - 누적거래량
    # strOptType : 타입(“0”, “1”)
    # 타입 “0”은 항상 마지막에 등록한 종목들만 실시간등록이 됩니다.
    # 타입 “1”은 이전에 실시간 등록한 종목들과 함께 실시간을 받고 싶은 종목을 추가로 등록할 때 사용합니다.
    # ※ 종목, FID는 각각 한번에 실시간 등록 할 수 있는 개수는 100개 입니다.
    @pyqtSlot(str, str, str, int, result=int)
    def SetRealReg(self, screenNo, codeList, fidList, optType):
        return self.ocx.dynamicCall("SetRealReg(QString, QString, QString, QString)",
                                    [screenNo, codeList, fidList, optType])

    # 종목별 실시간 해제
    # strScrNo : 화면번호
    # strDelCode : 실시간 해제할 종목코드
    # -화면별 실시간해제
    # 여러 화면번호로 걸린 실시간을 해제하려면 파라메터의 화면번호와 종목코드에 “ALL”로 입력하여 호출하시면 됩니다.
    # SetRealRemove(“ALL”, “ALL”);
    # 개별화면별로 실시간 해제 하시려면 파라메터에서 화면번호는 실시간해제할
    # 화면번호와 종목코드에는 “ALL”로 해주시면 됩니다.
    # SetRealRemove(“0001”, “ALL”);
    # -화면의 종목별 실시간해제
    # 화면의 종목별로 실시간 해제하려면 파라메터에 해당화면번호와 해제할
    # 종목코드를 입력하시면 됩니다.
    # SetRealRemove(“0001”, “039490”);
    @pyqtSlot(str, str)
    def SetRealRemove(self, scrNo, delCode):
        self.ocx.dynamicCall("SetRealRemove(QString, QString)", [scrNo, delCode])

    # 차트 조회한 데이터 전부를 배열로 받아온다.
    # LPCTSTR strTrCode : 조회한TR코드
    # LPCTSTR strRecordName: 조회한 TR명
    # ※항목의 위치는 KOA Studio의 TR목록 순서로 데이터를 가져옵니다.
    # 예로 OPT10080을 살펴보면 OUTPUT의 멀티데이터의 항목처럼 현재가, 거래량, 체결시간등 순으로 항목의 위치가 0부터 1씩 증가합니다.
    @pyqtSlot(str, str, result=str)
    def GetCommDataEx(self, trCode, recordName):
        return dumps(self.ocx.dynamicCall("GetCommDataEx(QString, QString)", [trCode, recordName]))

    # 차트의 특정 조회데이터를 받아온다.
    @pyqtSlot(str, str, int, str, result=str)
    def GetCommData(self, trCode, recordName, nIndex, itemName):
        return self.ocx.dynamicCall("GetCommData(QString, QString, int, QString)",
                                    [trCode, recordName, nIndex, itemName])

    # 리얼 시세를 끊는다.
    # 화면 내 모든 리얼데이터 요청을 제거한다.
    # 화면을 종료할 때 반드시 위 함수를 호출해야 한다.
    # Ex) openApi.DisconnectRealData(“0101”);
    @pyqtSlot(str)
    def DisconnectRealData(self, scnNo):
        self.ocx.dynamicCall("DisconnectRealData(QString)", [scnNo])

    # 종목코드의 한글명을 반환한다.
    # strCode – 종목코드
    # 종목한글명
    @pyqtSlot(str, result=str)
    def GetMasterCodeName(self, code):
        return self.ocx.dynamicCall("GetMasterCodeName(QString)", [code])

    # 국내 주식 시장별 종목코드를 ;로 구분하여 전달
    # strMarket – 종목코드
    # 마켓 구분값
    # 0 : 장내
    # 10 : 코스닥
    # 3 : ELW
    # 8 : ETF
    # 50 : KONEX
    # ...
    @pyqtSlot(str, result=str)
    def GetCodeListByMarket(self, strMarket):
        return self.ocx.dynamicCall("GetCodeListByMarket(QString)", [strMarket])

    # 입력한 종목의 전일가를 전달
    # strCode – 종목코드
    def GetMasterLastPrice(self, code):
        return self.ocx.dynamicCall("GetMasterLastPrice(QString)", [code])

    # 통신 연결 상태 변경시 이벤트
    # nErrCode가 0이면 로그인 성공, 음수면 실패
    def OnEventConnect(self, errCode):
        if errCode == 0:
            print('로그인 성공!')
        else:
            print('Error')
            kill(getpid(), 9)

    # 수신 메시지 이벤트
    def OnReceiveMsg(self, scrNo, rQName, trCode, msg):
        print('_OnReceiveMsg()', scrNo, rQName, trCode, msg)

    # 실시간 시세 이벤트
    def OnReceiveRealData(self, jongmokCode, realType, realData):
        # print('_OnReceiveRealData', jongmokCode, realType, realData)
        pass

    # 체결데이터를 받은 시점을 알려준다.
    # sGubun – 0:주문체결통보, 1:잔고통보, 3:특이신호
    # sFidList – 데이터 구분은 ‘;’ 이다.
    def OnReceiveChejanData(self, gubun, itemCnt, fidList):
        # print('_OnReceiveChejanData()', gubun, itemCnt, fidList)
        pass

    # 로컬에 사용자조건식 저장 성공여부 응답 이벤트
    def OnReceiveConditionVer(self, ret, msg):
        print('_OnReceiveConditionVer()', ret, msg)

    # 편입, 이탈 종목이 실시간으로 들어옵니다.
    # strCode : 종목코드
    # strType : 편입(“I”), 이탈(“D”)
    # strConditionName : 조건명
    # strConditionIndex : 조건명 인덱스
    def OnReceiveRealCondition(self, code, strType, conditionName, conditionIndex):
        print('_OnReceiveRealCondition()', code, strType, conditionName, conditionIndex)

    @SyncRequestDecorator.kiwoom_sync_callback
    def OnReceiveTrCondition(self, scrNo, codeList, conditionName, index, next, **kwargs):
        print('_OnReceiveTrCondition()', scrNo, codeList, conditionName, index, next)

    # Tran 수신시 이벤트
    @SyncRequestDecorator.kiwoom_sync_callback
    def OnReceiveTrData(self, scrNo, rQName, trCode, recordName, prevNext, dataLength, errorCode, message, splmMsg,
                        **kwargs):
        print('OnReceiveTrData()', scrNo, rQName, trCode, recordName, prevNext, dataLength, errorCode, message,
              splmMsg)

        if rQName == "주식정보":
            # PER, PBR 정보 획득
            name = self.GetCommData(trCode, rQName, 0, "종목명").strip()
            code = self.GetCommData(trCode, rQName, 0, "종목코드").strip().replace("A", "")
            PER = float(self.GetCommData(trCode, rQName, 0, "PER"))
            PBR = float(self.GetCommData(trCode, rQName, 0, "PBR"))
            ROE = float(self.GetCommData(trCode, rQName, 0, "ROE"))

            print('종목명: %s PER: %.2f PBR: %.2f ROE: %.2f' % (name, PER, PBR, ROE))

    @SyncRequestDecorator.kiwoom_sync_request
    def get_opt10001(self, code):
        self.SetInputValue("종목코드", code)
        self.CommRqData("주식정보", "opt10001", 0, "0105")

class SpartaQuant(QMainWindow):
    def __init__(self):
        super().__init__()
        self._kiwoom = Kiwoom()

        t1 = Thread(target=self.main_thread)
        t1.daemon = True
        t1.start()

    def main_thread(self):
        ###############################
        # 1. 로그인                    #
        ###############################

        # 로그인 시도
        self._kiwoom.CommConnect()

        # 로그인 완료 대기
        while True:
            if self._kiwoom.GetLoginInfo("ACCOUNT_CNT") != "":
                break
            print("로그인 대기 중...")
            sleep(5)
        sleep(5)

        ###############################
        # 2. 주식기본정보요청            #
        ###############################
        self._kiwoom.get_opt10001('005930')

if __name__ == "__main__":
    app = QApplication(argv)
    myWindow = SpartaQuant()
    trader = myWindow._kiwoom
    app.exec_()

 

- class Kiwoom

키움 클래스 내 opt10001 조회 요청 함수 제작 및 동기화

@SyncRequestDecorator.kiwoom_sync_request <- 이게 있어야 한다고함
def get_opt10001(self, code):
    self.SetInputValue("종목코드", code)
    self.CommRqData("주식정보", "opt10001", 0, "0105")

-class Kiwoom → OnReceiveTRData

주식기본정보 추출 코드 제작

 @SyncRequestDecorator.kiwoom_sync_callback
    def OnReceiveTrData(self, scrNo, rQName, trCode, recordName, prevNext, dataLength, errorCode, message, splmMsg,
                        **kwargs):
        print('OnReceiveTrData()', scrNo, rQName, trCode, recordName, prevNext, dataLength, errorCode, message,
              splmMsg)

        if rQName == "주식정보":
            # PER, PBR 정보 획득
            name = self.GetCommData(trCode, rQName, 0, "종목명").strip()
            code = self.GetCommData(trCode, rQName, 0, "종목코드").strip().replace("A", "")
            A가있으면 주문 취소가 되므로 없애줘야함
            
            PER = float(self.GetCommData(trCode, rQName, 0, "PER")) 숫자라서 실수로 바꿔준다고
            PBR = float(self.GetCommData(trCode, rQName, 0, "PBR"))
            ROE = float(self.GetCommData(trCode, rQName, 0, "ROE"))

            print('종목명: %s PER: %.2f PBR: %.2f ROE: %.2f' % (name, PER, PBR, ROE))


싱글데이터라서 하나의 꾸러미라 0번만 보면 됨

class SpartaQuant → def main_thread

-주식기본정보 요청

###############################
# 2. 주식기본정보요청          #
###############################
self._kiwoom.get_opt10001('005930')

- 실행

06. 조건식 결과 가져오기

조건식 추가!

  1. 조건식이란?
    • 키움 HTS(Home Traiding System)에서 입력 가능
    • 특정 조건에 맞는 주식 종목을 결과로 받을 수 있음
  2. 조건식을 만들어보자!
  • 영웅문4 실행(주의: 실계좌로 로그인!)
  • [메뉴]-[영웅검색]-[조건검색] 클릭!
  • 조건식 탭에서 PER 검색
  • 최근결산 기준 상위 5개 조건 추가
     
  • 검색결과확인
  • 내 조건식 저장하기(조건식명: TEST)
  • 내 조건식 확인

 

KOA Studio에서 관련 함수 확인!

  1. GetConditionLoad() - 사용자 조건검색 목록을 요청
  2. GetConditionNameList() - 조건식 고유번호와 조건식 이름 리스트 요청
  3. SendCondition() - 조건검색결과 요청
  4. [이벤트] OnReceiveTrCondition() - 조검검색결과 응답
class SpartaQuant → def main_thread
GetConditionLoad()를 통한 조건식 불러오기

        ###############################
        # 2. 조건식 결과 가져오기        #
        ###############################

        # 조건식 불러오기
        self._kiwoom.GetConditionLoad()
        
GetConditionNameList()를 통한 조건식 리스트 가져오기
        # 조건식 리스트 가져오기 (res="000^스파르타;001^조건식1;002^조건식2;...")
        res = self._kiwoom.GetConditionNameList()
        print(res)
        res = res.split(';')
        print(res)
        
        
        
조건식 중 'TEST' 조건식 존재여부 확인
        # 조건식 중 'TEST' 조건식 존재여부 확인
        condition_index = None
        condition_name = None

        for name in res:
            if 'TEST' in name:
                [condition_index, condition_name] = name.split('^')
                break

        if condition_index == None or condition_name == None:
            print("Can't find a condition.")
            exit(0)

        print("조건식:", condition_name, "번호:", condition_index)


SendCondition()를 통한 'TEST' 조건식 결과 요청하기
        # '스파르타' 조건식 결과 요청하기
        condition_index = int(condition_index)
        self._kiwoom.SendCondition("0156", condition_name, condition_index, 0)
        
        
class SpartaQuant → def main_thread
# 조건식 결과 출력
print(codeList)

 

왜 SendCondition()은 새로 함수를 만들어서 동기화를 시켜주지 않나요?

  • A1. SendCondition()은 함수 자체에 동기화를 시켜 놓았기 때문입니다.
  • A2. 반면 CommRqData()을 사용해야 할 경우 SetInputValue()와 같이 동기화를 시켜야 하기 때문에 특별히 하나의 함수로 묶어서 동기화를 시켜줘야 합니다!

 

 

 음... 이게 지금 나에게 의미있는 강의인지 잘 모르겠다

코드를 직접 짜는것도아니고, 

나로서는 그닥.. 처음에는 그냥 주식과 관련된 자동매매 서비스에 대해 직접 코드짜고 구현하는줄알았더니

그냥 키움 KOA서비스에서 어떻게 가져오는지.. 아주 간략하게 알려줘서 집중이 안되는것같다.

4주차는 어떨지 의문이생긴다.

728x90

'✍~2022 > 주식자동매매' 카테고리의 다른 글

주식 자동매매 종합반 2주차 일지  (0) 2021.08.12
주식 자동매매 종합반 1주차  (0) 2021.08.07