2. 해외선물/2-4. 해외선물 API (사용)

(키움증권 해외선물 OpenAPI-W) 스토캐스틱(Stochastic) 값 구하기 (3) 스토캐스틱 슬로우 %K, %D 구하기

봄이오네 2023. 10. 3. 08:07
반응형
목 차
1. 들어가며
2. 사전 준비
   1) Slow %K 및 %D 산출식
   2) Slow %K 및 %D 값
   3) Slow %K 및 %D 계산하기(엑셀)
3. 코드 설명
4. 전체코드
5. 마치며

 

1. 들어가며

지난 글에서는 스토캐스틱 패스트 %K, %D 구하는 방법을 알아보았다. 지수이동평균을 구하는 부분이 약간 어렵게 느껴지겠지만, 몇 번 연습하다보면 그 개념과 코드를 이해할 수 있으리라 생각된다.
 
이번 글에서는 스토캐스틱 슬로우 %K, %D 구하는 방법을 알아보자. 지난 글에서 for문, list 항목 추가 등 충분한 설명을 했으므로, 이번 글에서는 중복되는 내용 등은 생략한다.
 ※ 코드의 연속성을 위해 스토캐스틱 패스트 %K, %D 코드에 스토캐스틱 슬로우 %K, %D 코드를 추가할 것이다. 지난 글과 중복되는 내용(패스트 %K, %D 구하기)은 설명을 생략한다.


2. 사전 준비

1) Slow %K 및 %D 산출식

스토캐스틱 보조지표에 대해 처음 설명할 때 한번 언급한 적이 있지만, 키움증권에서의 슬로우 %K는 타 증권사와 그 결과값이 다르다. 이유는? Slow %K를 구할 때 "Fast %D의 이동평균값"을 취하지 않고, "키움증권에서 자체 계산한 값"을 취하기 때문이다. (https://springcoming.tistory.com/210)
 

  • 스토캐스틱 패스트 %K : (현재가격 - n분(일) 중 최저가) / ( n분(일) 중 최고가 - n분(일) 중 최저가) * 100
  • 스토캐스틱 패스트 %D : 패스트 %K를 m분(일) 로 지수이동평균한 값
  • 스토캐스틱 슬로우 %K : 패스트 %D를 m분(일)로 이동평균한값 → 키움증권식 계산한 값
  • 스토캐스틱 슬로우 %D : 슬로우 %K를 m분(일)로 지수이동평균한 값
  • 스토캐스틱 오실레이터 : 패스트 %D - 슬로우 %D

키움증권을 이용하고 있는 필자로서는 울며겨자먹기 식으로 Slow %K는 "키움 자체 계산 값"을 구해야 할 것 같다. 여기서는 슬로우 %K와 %D를 구할 것이다.

  • 키움자체 Slow %K = sum(현재가 - n분중 최저가) / sum( n분중 최고가 - n분중 최저가) * 100

키움증권 1분봉 데이터의 스토캐스틱 슬로우 설정기간은 12분, 5분, 5분이다.

그림1-1. Slow %K 및 %D의 설정 기간(12, 5, 5)

 

2) Slow %K 및 %D 값

우리가 파이썬 코딩을 통해 구하고자 하는 값은 2023년 9월 30일(토)의 05:59분에 해당하는 Slow %K인 20.29와 Slow %D인 27.16이다.
 

그림1-2. Slow %K 및 %D값은 각각 20.29와 27.16이다.

 
3) Slow %K 및 %D 계산하기(엑셀)
슬로우 %K 및 %D를 구하는 방법을 생각해 보았다. 슬로우 %K의 기간은 < 그림1-2 >에서 확인하였듯이 12분, 5분이다.
이 말은 12분 내 최고값 및 최저값을 구한다는 말이다.

  • 키움자체 Slow %K = sum(현재가 - n분중 최저가) / sum( n분중 최고가 - n분중 최저가) * 100
  • 스토캐스틱 슬로우 %D : 슬로우 %K를 m분(일)로 지수이동평균한 값

키움증권에서는 역순으로 자료를 제공하므로 12분 동안의 최고/최저가이며, 5분 간격으로 분자/분모를 구한다.

  • 05:59분 종가 14891.5(F열601줄)에 따른 최고가는 D590~601(12개), 최저가는 E590~601(12개)에서 산출된다.
  • 05:58분 종가 14894.25(F열600줄)에 따른 최고가는 D589~600(12개), 최저가는 E589~600(12개)에서 산출된다.
  • ...
  • 05:55분 종가 14899(F열597줄)에 따른 최고가는 D586~597(12개), 최저가는 E586~597(12개)에서 산출된다.

 
5분 동안(05:59~05:55) 각각 종가, 최고가, 최저가 등이 결정되면 그 값들을 더하고 나서 100을 곱해주면 Slow %K가 된다.
즉, Slow %K의 분자는 SUM[(05:59분종가 - 05:59분최저가) + (05:58분종가 - 05:58분최저가) + ... +  (05:55분종가 - 05:55분최저가)]이고, Slow %K의 분모는 SUM[(05:59분최고가 - 05:59분최저가) + (05:58분최고가 - 05:58분최저가) + ... + (05:55분최고가 - 05:55분최저가)]이다.
 
정리하면 Slow %K = (분자의 합) / (분모의 합) *100 = 20.29가 나온다.
 
 

그림1-3. 엑셀로 슬로우 %F 및 %D를 구하는 내용


3. 코드 설명

전체본 캡쳐를 해놓고보니, 중복된 내용이 너무 많다. ㅠㅠ. 해당 내용만 발췌해서 설명하면 코드 흐름이 끊길거 같고... 이래저래 고민이다. 일단 풀 버전으로 설명한다.
 ※ 라이브러리, 로그인, opc10002(1분봉 받기), Stochastic Fast %K, %D에 관한 내용은 중복이므로 여기서는 설명하지 않을 예정이다.
 

그림2-1. 라이브러리 및 로그인 관련 내용

 
1줄~18줄 : 활용할 라이브러리 및 로그인에 관련 내용으로 설명을 생략한다.
20줄~31줄 : %K, %D를 구하기 위해 미리 리스트형 변수를 선언한다.
 

그림2-2. 지수이동평균을 구하기 위한 가중치 및 리스트형 변수를 선언

33줄~41줄 : 스토캐스틱 패스트/슬로우/오실레이터를 산출을 위해 지수이동평균시 활용되는 가중치 및 리스트형 변수를 미리 선언한다.
50줄~64줄 : 1분봉 데이터 입력/요청/조회하는 함수이다.
 

그림2-3. Stochastic Fast %K 및 %D를 구하는 함수

68줄~107줄 : 스토캐스틱 패스트 %K 및 %D를 구하는 내용이다. (설명 생략)
 

그림2-4. Stochastic Slow %K 및 %D를 구하는 함수

 
여기서부터 자세히 설명한다. 짤린 부분은 4번(전체코드)에서 확인가능하다. Slow %K의 기간은 12분, 5분이다.

  • 키움자체 Slow %K = sum(현재가 - n분중 최저가) / sum( n분중 최고가 - n분중 최저가) * 100로 계산%K
  • 스토캐스틱 슬로우 %D : 슬로우 %K를 m분(일)로 지수이동평균한 값

 
110줄~112줄 : 3중 for문을 활용하였다. 이유는? 2중 for문으로 1개의 %K는 구할 수 있다. 하지만 %D까지 구하려면, 2중 for문으로 구한 %K를 지수이동평균하여야 하기 때문에 for문을 한번 더 돌려서, 3중 for문이 되는 것이다.
 
110줄 : 슬로우 %D를 구하기 위해 30회 for문을 돌려준다. 아래에서 설명할 것이다.
111줄~112줄 : 이 글의 핵심이다. %K의 기간이 12분, 5분인 것을 반드시 기억하자. k=0일 때 (현재가-최저가) 및 (최고가-최저가)가 나올 것이다. 이것을 2번째 for문을 통해 5번 돌린다(j)는 뜻이다. 5번 돌려야 하는 이유? %K의 기간이 12분, 5분인 것을 기억하자! 즉 k=0일 때 분자0와 분모0, k=1일 때 분자1와 분모1, ... , k=4일 때 분자4와 분모4로 나올 것이다.
키움측 자체 Slow %K = (분자0+분자1+...+분자4) / (분모0+분모1+...+분모4) * 100을 하라는 뜻이다.
    * 여기서 분자는 (현재가-5분중 최저가)의 각각의 합계이고, 분모는 (5분중 최고가-5분중 최저가)의 각각의 합계이다.
 
113줄~115줄 : 현재가, 최고/최저값을 구한다.
116줄~117줄 : 12줄 이내 최고값과 최저값을 20줄에서 선언한 self.nq_max_prcie_list 리스트에 넣는다.
121줄~122줄 : 리스트 내 최고값/최저값을 찾는다.
124줄~125줄 : 이미 사용한 리스트를 각각 초기화 한다.
127줄~128줄 : 키움자체 Slow %K의 분자와 분모를 각각 구하기 위해 (현재가-최저가) 및 (최고가-최저가)를 계산한다.
130줄~131줄 : 각각의 분자 및 분모를 리스트에 넣는다.
133줄~134줄 : 리스트 내에 있는 (현재가-최저가)모음 및 (최고가-최저가)모음을 각각 합산한다.
 

그림2-5. Stochastic Slow %D를 구한다.

 
136줄~137줄 : 이미 사용한 리스트를 초기화한다.
139줄~146줄 : Stochastic %K 값을 구한다.
 
148줄 : 이 글의 두번째 핵심이다. 슬로우 %D를 구하기 위해 리스트(최근→과거)를 역산(과거→최근)한다. 이유는? 지수이동평균은 시계열 순서(과거→최근)으로 되어 있어야, 앞에서 계산한 값을 뒤에서 "가중치"를 둘 수 있기 때문이다.
151줄~156줄 : %F가 모여있는 리스트(148줄, self.stc_slow_d_list)에서 %F 값을 하나씩 가지고 와서, 지수이동평균 계산을 한다.
158줄 : 슬로우 %D를 소수 셋째자리에서 반올림한다.
159줄 : 이미 사용한 self.stc_slow_d_list를 초기화한다.
 
위의 값을 계산하면, 아래와 같이 나온다.

  • 패스트 %K는 3.12(엑셀에는 3.13), 패스트 %D는 14.26(엑셀에는 14.27)
  • 슬로우 %K는 20.29(엑셀에는 20.29), 슬로우 %D는 27.16(엑셀에는 27.16)
3.12
14.26
20.29
27.16

4. 전체코드

전체코드는 아래와 같다. 아래의 코드를 실행하려면, 키움증권에 시세 이용료(월 $185)를 지불하여야 한다.
 

더보기
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", "")  # 계좌번호 입력창을 띄우는 내부함수

        self.nq_max_price_list = []
        self.nq_min_price_list = []

        self.stc_fast_d_list = []

        self.stc_slow_k_list_close_min = []
        self.stc_slow_k_list_max_min = []

        self.stc_slow_d_list_row = []
        self.stc_slow_d_list = []

        self.stc_slow_k_forth_list = []

        self.stc_fast_k_weighting = 2 / (3 + 1)
        self.stc_slow_d_weighting = 2 / (5 + 1)
        self.stc_oscillator_d_weighting = 2 / (26+1)
        self.stc_oscillator_d_9_weighting = 2 / (9+1)

        self.stc_slow_oscillator_k_26_close_min_list = []
        self.stc_slow_oscillator_k_26_max_min_list = []

        self.stc_slow_oscillator_final = []

    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":

            self.tr_event_loop.exit()

            btl.stochastic_searching()

    def stochastic_searching(self):
        ########## 스토캐스틱 패스트 %K, %D ##########
        for i in range(0, 30):  # 현재 분의 STC_패스트 K, D 구하기
            for j in range(i, i + 5):
                self.close_price_row_data = abs(float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002", "opc10002", i, "현재가").strip()))
                max_price_row_data = abs(float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002", "opc10002", j, "고가").strip()))
                min_price_row_data = abs(float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002", "opc10002", j, "저가").strip()))

                self.nq_max_price_list.append(max_price_row_data)
                self.nq_min_price_list.append(min_price_row_data)

            self.close_price_first = self.close_price_row_data
            self.max_price_first = max(self.nq_max_price_list)
            self.min_price_first = min(self.nq_min_price_list)

            self.nq_max_price_list = []
            self.nq_min_price_list = []

            self.stc_fast_k_first = (self.close_price_first - self.min_price_first) / (self.max_price_first - self.min_price_first) * 100
            self.stc_fast_k_first = round(self.stc_fast_k_first, 2)  # 소수 셋째자리에서 반올림

            self.stc_fast_d_list.append(self.stc_fast_k_first)

        self.stc_fast_d_list_final = self.stc_fast_d_list[::-1]
        self.stc_fast_k = self.stc_fast_d_list_final[29]

        self.stc_fast_d_list = []

        for i in range(len(self.stc_fast_d_list_final)):
            if i == 0:
                self.stc_fast_d_first = self.stc_fast_d_list_final[i]
            elif i >= 1:
                self.stc_fast_d_second = (self.stc_fast_d_list_final[i] * self.stc_fast_k_weighting) + (self.stc_fast_d_first * (1 - self.stc_fast_k_weighting))
                self.stc_fast_d_first = self.stc_fast_d_second

        self.stc_fast_d_list_final = []

        self.stc_fast_k = round(self.stc_fast_k, 2)
        self.stc_fast_d = round(self.stc_fast_d_second, 2)

        print(self.stc_fast_k)
        print(self.stc_fast_d)

        ########## 스토캐스틱 슬로우 %K, %D ##########
        for i in range(0, 30):
            for j in range(i, i+5):  # 현재 분의 STC_슬로우 K, D 구하기 ///       stc_slow_k = ∑(현재가 - 최대값) / ∑(최대값 - 최소값)
                for k in range(j, j + 12):
                    self.close_price_row_data = abs(float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002", "opc10002", j, "현재가").strip()))
                    max_price_row_data = abs(float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002", "opc10002", k, "고가").strip()))
                    min_price_row_data = abs(float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002", "opc10002", k, "저가").strip()))

                    self.nq_max_price_list.append(max_price_row_data)
                    self.nq_min_price_list.append(min_price_row_data)

                self.close_price_first = self.close_price_row_data
                self.max_price_first = max(self.nq_max_price_list)
                self.min_price_first = min(self.nq_min_price_list)

                self.nq_max_price_list = []
                self.nq_min_price_list = []

                self.price_first_close_min = self.close_price_first - self.min_price_first
                self.price_first_max_min = self.max_price_first - self.min_price_first

                self.stc_slow_k_list_close_min.append(self.price_first_close_min)
                self.stc_slow_k_list_max_min.append(self.price_first_max_min)

            self.stc_slow_k_first = sum(self.stc_slow_k_list_close_min)
            self.stc_slow_k_second = sum(self.stc_slow_k_list_max_min)

            self.stc_slow_k_list_close_min = []
            self.stc_slow_k_list_max_min = []

            self.stc_slow_k_third = self.stc_slow_k_first / self.stc_slow_k_second * 100
            self.stc_slow_k_third = round(self.stc_slow_k_third, 2)

            self.stc_slow_k_forth_list.append(self.stc_slow_k_third)
            if i == 0:
                self.stc_slow_k = self.stc_slow_k_forth_list[0]
            elif i == 1:
                self.stc_slow_k_1_ago = self.stc_slow_k_forth_list[1]

        self.stc_slow_d_list = self.stc_slow_k_forth_list[::-1]
        self.stc_slow_k_forth_list = []

        for i in range(0, 30):
            if i == 0:
                self.stc_slow_d_first = self.stc_slow_d_list[i]
            elif i >= 1:
                self.stc_slow_d_second = (self.stc_slow_d_list[i] * self.stc_slow_d_weighting) + (self.stc_slow_d_first * (1 - self.stc_slow_d_weighting))
                self.stc_slow_d_first = self.stc_slow_d_second

        self.stc_slow_d = round(self.stc_slow_d_first, 2)
        self.stc_slow_d_list = []

        print(self.stc_slow_k)
        print(self.stc_slow_d)

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

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

    app.exec_()

 


5. 마치며

지금까지 Stochastic Slow %K 및 %D 구하는 방법을 알아보았다. 사실 Slow %K 때문에 3시간 정도 헤맸던 것 같다. 이유는? 인터넷 검색을 해보니 "Slow %K = Fast %D 이동평균"이라고 해서 엑셀로 구해보니, 값이 다른 것이었다. 어떻게 된거지??? 한창을 검색 및 고민하다가 "키움자체 Slow %K 값으로 계산한다"는 검색 결과에 약간 허무했다. 그래도 이번 기회에 증권사별로 다를 수 있다는 내용을 이렇게 알게 되어서 다행이다.! ^^
 
다음 글에서는 Stochastic 관련 마지막 내용으로 Stochastic Oscillator에 대해 알아보도록 하자.
 

반응형