2. 해외선물/2-3. 해외선물 설명

(키움증권 해외선물 OpenAPI-W) 1분봉 데이터로 3분봉, 60분봉 만들기 (3) 리스트문과 for문 활용 (중급)

봄이오네 2023. 11. 1. 08:09
반응형

 

목 차
1. 들어가기
2. 사전설명
   1) 리스트 내 for문 작성하기
   2) 리스트 내 for문의 실제 활용 형태
3. 코드설명
4. 전체코드
5. 마치며

 

1. 들어가기

지난 글에서는 1분봉 데이터를 활용하여 3분봉 데이터를 구하는 방법을 알아보았다. 각 분(minute)을 3으로 나누어 몫(quotient)과 나머지(remainder)을 구하여 3분봉 데이터를 추출할 수 있는 접근법이었다.
 
이번 글에서는 지난글의 코드를 조금씩 줄여나가는 방법에 대해 알아볼 예정이다. 처음부터 간략한 코드를 설명할까 생각했는데, 장문의 코드가 단문으로 줄어드는 과정을 보여주는 것도 좋아보여서 지난글(총 134줄)이 길어졌던거 같다.
 


2. 사전설명

1) 리스트 내 for문 작성하기

그 동안은 코드가 길어지더라도 for문과 리스트를 각각 작성하였다. "리스트 내 for문 작성"이 낯설었던 이유가 크게 작용한다. 그런데, 이번에는 코드의 간결성도 그렇고, 최대값/최소값을 변수에 담아야 하기 때문에 어쩔 수 없이 "리스트 for문을 작성"하였다.
 

aaa = []
for i in range(0,4):
    aaa.append(i)
print(aaa)


### (expected result) ###
### [0, 1, 2, 3]

코드 1-1. for문과 리스트문을 각각 작성한 경우

 
아래는 for문과 리스트문을 함께 써준 경우이다.

aaa = [i for i in range(0,4)]
print(aaa)


### (expected result) ###
### [0, 1, 2, 3]

코드 1-2. for문과 리스트문을 같이 써준 경우

 
코드1-1 및 코드1-2는 결과물이 같다는 점에서 큰 차이는 없다. 0~3까지 출력하는 코드는 간단하다. 그런데 공부하면서 느끼는 것이지만, for문과 리스트문을 함께 쓰는 것은 숙지하게 되니, 그렇게 코드 작성이 편할 수 밖에 없다.
 
for문 내 리스트 작성의 결론은 아래와 같다.

그림1. 리스트 내 for문을 작성한 내용

 
aaa = [i for in range(0 ,4)]일 때, 결과물은 < 코드1-2 >에서 확인하였듯이 [0,1,2,3,]이다.
 

2) 리스트 내 for문의 실제 활용 형태

아래 코드에서 활용하겠지만, 여기서 그 형태를 보자. 14:36~14:38분, 14:39~14:41분 등 각각 최대값을 구해야한다.

high_price = max(abs(float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002", "opc10002", i, "고가").strip())),
                 abs(float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002", "opc10002", i-1, "고가").strip())),
                 abs(float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002", "opc10002", i-2, "고가").strip())))
low_price = min(abs(float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002", "opc10002", i, "저가").strip())),
                 abs(float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002", "opc10002", i-1, "저가").strip())),
                 abs(float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002", "opc10002", i-1, "저가").strip())))

코드2-1. 3분내 최대값과 최소값을 구한다.

 
< 코드2-1 >의 1줄~3줄 및 4줄~6줄은 3분 사이의 최고값을 구하는 코드이다.
 

high_price = max([abs(float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002", "opc10002", i, "고가").strip())) for i in range(i-2, i+1)])
low_price = min([abs(float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002", "opc10002", i, "고가").strip())) for i in range(i-2, i+1)])

코드2-2. 3분내 최대값과 최소값을 리스트와 for문을 통해 구한다.

 
< 코드2-2 >는 < 코드2-1 >의 6줄을 2줄로 간략히 정리한 for문/리스트 문이다. 위의 < 코드2-2 >코드 형태에 익숙해지자!
 


3. 코드설명

라이브러리 및 로그인에 관한 설명은 생략한다.

그림2-1. 라이브러리 및 로그인에 관한 내용은 생략한다.

 
1줄~25줄 : 라이브러리 및 로그인에 관한 내용이며, 설명을 생략한다.
27줄~33줄 : 116줄에서 함수를 실행하기 위해 함수를 정의한다.
 

그림2-2. 데이터를 수신받고, 1분봉으로 3분봉을 만드는 화면이다.

 
36줄~41줄 : 키움증권에서 데이터를 수신받는다.
42줄 : 58줄~110줄에서 정의된 함수를 실행한다.
45줄~51줄 : 현재 체결시간을 정의하고, 인수화(51줄)한다.
53줄~56줄 : 1분봉 데이터를 담아줄 리스트를 선언한다.
 
58줄~69줄 : 체결시간(51줄)을 3으로 나누어서, 나머지(remainder)에 따라 적용되는 함수를 정의한다.
62줄~64줄 : 3분 내 최대값/최소값을 각각 구한다.
66줄~69줄 : 시가, 고가, 저가, 종가를 리스트에 넣는다.
 

그림2-3. 분(minute)을 3으로 나눈 나머지를 활용한다.

 
76줄~110줄 : 51줄(체결시간)의 분(minute)를 3으로 나눈 나머지(remainder)가 1 혹은 2일 때 적용되는 코드이다.
 

그림2-4. 1분봉 데이터를 요청한다.

 
116줄 : 28줄(rq_data_opc10002)에서 정의된 함수를 실행한다.
 


4. 전체코드

아래 코드는 키움증권에 월 시세 이용료($185)를 지불하여야 실행된다. 변경된 코드는 리스트 및 for문이 쓰인 62줄~63줄, 80줄~81줄, 98줄~99줄이다.
 

더보기
import sys
from PyQt5.QAxContainer import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *

class btl_system():
    def __init__(self):
        self.kiwoom = QAxWidget("KFOPENAPI.KFOpenAPICtrl.1")
        print("로그인 시작!")

        self.kiwoom.dynamicCall("CommConnect(1)")  # CommConnect() : 괄호 안에 자동(1)을 넣는다.
        self.kiwoom.OnEventConnect.connect(self.login_Connect)
        self.kiwoom.OnReceiveTrData.connect(self.trdata_get)

        self.login_event_loop = QEventLoop()
        self.login_event_loop.exec_()

        self.kiwoom.dynamicCall("GetCommonFunc(QString, QString)", "ShowAccountWindow", "")  # 계좌번호 입력창을 띄우는 내부함수

    def login_Connect(self, err_code):
        if err_code == 0:
            print('로그인 성공했습니다!')
        else:
            print('로그인 실패했습니다!')
        self.login_event_loop.exit()

    ########## 키움서버에 TR 요청하는 함수 모음 ##########
    def rq_data_opc10002(self, stock_code_num, time_unit):  # 1분봉 받기
        self.kiwoom.dynamicCall("SetInputValue(QString,QString)", "종목코드", stock_code_num)
        self.kiwoom.dynamicCall("SetInputValue(QString,QString)", "시간단위", time_unit)
        self.kiwoom.dynamicCall("CommRqData(QString, QString, QString, QString)", "opc_10002", "opc10002", "", "1002")
        self.tr_event_loop = QEventLoop()
        self.tr_event_loop.exec_()

    ########## OnReceiveTrData을 통해 수신받은 데이터 함수  ##########
    def trdata_get(self, scrno, rqname, trcode, recordname, prenext):
        if rqname == "opc_10002":
            getrepeatcnt = self.kiwoom.dynamicCall("GetRepeatCnt(QString,QString)", trcode, recordname)
            self.getrepeatcnt = getrepeatcnt

            self.tr_event_loop.exit()
            btl.minute_1_3_bong()

    def minute_1_3_bong(self):
        time_0 = self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002", "opc10002", 0, "체결시간")
        print(time_0)
        print(time_0[8:12])
        print(time_0[10:12])

        aaa = time_0[10:12]
        aaa = int(time_0[10:12])

        self.bbb = []
        self.ccc = []
        self.ddd = []
        self.eee = []

        if aaa % 3 == 0:      ### aaa는 현재시간       ### 0으로 떨어지면 시가, 2로 떨어지면 종가
            for i in range(12, 0, -3):      # 1분전 봉까지 출력 (과거 → 현재)
                if i//3 == i/3:
                    open_price = abs(float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002", "opc10002", i, "시가").strip()))
                    high_price = max([abs(float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002", "opc10002", i, "고가").strip())) for i in range(i-2, i+1)])
                    low_price = min([abs(float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002", "opc10002", i, "고가").strip())) for i in range(i-2, i+1)])
                    close_price = abs(float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002", "opc10002", i-2, "현재가").strip()))

                    self.bbb.append(open_price)
                    self.ccc.append(high_price)
                    self.ddd.append(low_price)
                    self.eee.append(close_price)

            print(self.bbb)
            print(self.ccc)
            print(self.ddd)
            print(self.eee)

        elif aaa % 3 == 1:      ### aaa는 현재시간       ### 0으로 떨어지면 시가, 2로 떨어지면 종가
            for i in range(12+1, 1, -3):      # 1분전 봉까지 출력 (과거 → 현재)
                if i//3 == (i-1)/3:
                    open_price = abs(float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002", "opc10002", i, "시가").strip()))
                    high_price = max([abs(float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002", "opc10002", i, "고가").strip())) for i in range(i-2, i+1)])
                    low_price = min([abs(float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002", "opc10002", i, "고가").strip())) for i in range(i-2, i+1)])
                    close_price = abs(float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002", "opc10002", i-2, "현재가").strip()))

                    self.bbb.append(open_price)
                    self.ccc.append(high_price)
                    self.ddd.append(low_price)
                    self.eee.append(close_price)

            print(self.bbb)
            print(self.ccc)
            print(self.ddd)
            print(self.eee)

        elif aaa % 3 == 2:      ### aaa는 현재시간       ### 0으로 떨어지면 시가, 2로 떨어지면 종가
            for i in range(12+2, 2, -3):      # 1분전 봉까지 출력 (과거 → 현재)
                if i//3 == (i-2)/3:
                    open_price = abs(float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002", "opc10002", i, "시가").strip()))
                    high_price = max([abs(float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002", "opc10002", i, "고가").strip())) for i in range(i-2, i+1)])
                    low_price = min([abs(float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002", "opc10002", i, "고가").strip())) for i in range(i-2, i+1)])
                    close_price = abs(float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002", "opc10002", i-2, "현재가").strip()))

                    self.bbb.append(open_price)
                    self.ccc.append(high_price)
                    self.ddd.append(low_price)
                    self.eee.append(close_price)

            print(self.bbb)
            print(self.ccc)
            print(self.ddd)
            print(self.eee)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    btl = btl_system()

    btl.rq_data_opc10002("NQZ23", "1")

    app.exec_()

 


5. 마치며

이번 글의 핵심은 리스트 내 for문을 작성하여 코드를 간략하게 만드는 것이었다. 지난글이 134줄이며, 이번글은 118줄(△16줄)로 작성이 되었다. 다음 글에서는 코드를 더 줄여보도록 하자.
 
 

반응형