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

(키움증권 해외선물 OpenAPI-W) 볼린저밴드 상한선/하한선 값 구하기

봄이오네 2023. 1. 30. 08:02
반응형
목 차
1. 들어가며
2. 사전 설명
   1) 볼린저밴드 개념
   2) 볼린저밴드 구하는 방법
   3) 파이참에서 볼린저밴드 구하는 방법
3. 코드 설명
4. 전체 코드
5. 마치며

 

1. 들어가며

해외선물 거래를 하면서 여러가지 지표를 사용하게 된다. 이동평균선을 비롯하여 일목균형표, 볼린저밴드, RSI, MACD 등 각 지표를 영웅문G의 차트에 추가하여 매매할 때 참고를 한다.

 

개인적으로 볼린저밴드와 RSI 지표를 활용한다.

이 글에서는 볼린저밴드의 상한선 값과 하한선 값을 각각 구해보는 방법을 알아볼 것이다.


2. 사전 설명

1) 볼린저밴드 개념

20일 이동평균선에 표준편차를 더하거나 빼서 주가의 위치를 알려주는 지표이다.

이렇게 말하니깐, 상당히 어려워 보인다. @.@

 

표준편차 개념은 인터넷 검색을 통해 알아보시기를 추천한다. 개념이 어렵다. 흑~

  • 표준편차 신뢰구간1(1-α) : 68.3%
  • 표준편차 신뢰구간2(2-α) : 95.5%
  • 표준편차 신뢰구간3(3-α) : 99.7%

이 중 볼린저밴드는 신뢰구간2의 95.5%를 활용한다.

즉, 주가의 흐름이 볼린저밴드의 상한선과 하한선 안에 있을 확률이 95.5%라는 뜻이다.

 

2) 볼린저밴드 구하는 방법

아래에서는 일봉의 개념이 아닌, 1분봉의 개념으로 설명할 예정이다.

< 그림1 >에서 검정색의 곡선이 볼린저밴드의 상한선과 하한선이다.

 

볼린저밴드를 (20, 2)로 설정할 경우, 중신선/상단선/하단선은 아래와 같이 구할 수 있다.

  • 중심선 : 20분 이동평균선
  • 상단선 : 20분 이동평균선 + 표준편차 x 2
  • 하단선 : 20분 이동평균선 - 표준편차 x 2 

우리는 표준편차만 구하면 될 것으로 보인다.

 

3) 파이참에서 볼린저밴드 구하는 방법

중심선을 구하고 나서, 표준편차를 구해서 더하거나 빼면 그 값이 나올 것으로 보인다.

즉, 현재를 기준으로 20분의 1분 데이터 종가의 평균을 구한 후, 표준편차를 구하면 된다.

→ numpy 함수를 이용하여 평균/표준편차를 구한다.

 

  • numpy.mean(리스트) : 리스트 내의 숫자 모음에 대해 평균을 구한다.
  • numpy.std(리스트) : 리스트 내의 숫자 모음에 대해 표준편차를 구한다.

3. 코드 설명

numpy 모듈을 이용하면 의외로 간단하게 구할 수 있다. 먼저 20분의 종가를 리스트에 넣고 평균/표준편차를 구하면 된다. 중복된 설명(모듈 및 로그인 정보 등)은 생략하도록 한다.

 

그림2. 필요모듈 및 로그인하는 화면

 

1줄~29줄 : 모듈 및 로그인 방법에 관한 내용이다.

32줄~35줄 : 종목코드 및 시간단위(1분)을 키움서버에 입력하고, CommRqData 함수로 키움서버에 요청한다.

→ 전체코드는 아래의 4번(전체코드)에서 확인한다.

 

그림3. 수신받은 20개 종가로 평균, 표준편차를 구한다.

 

41줄 : 35줄에서 요청(rqname)한 데이터가 opc_10002이면

42줄 : for문으로 20개 데이터를 순차적으로 가져온다.

           → 주의할 점은 현재가를 기준으로 역순이다.

                현재가, 현재가-1, 현재가-2 ....

43줄 : 42줄에서 가져온 데이터를 close_price1의 변수에 넣는다.

45줄 : 이 글의 핵심이다.

           21줄에서 빈칸으로 만들어놓은 self..close_price2의 빈 리스트에 43줄의 데이터를 추가한다.(append)

 

51줄 : 상한선(평균에 표준편차(x2) 합산 결과)

52줄 : 하한선(평균에서 표준편차(x2)을 차감 결과)

53줄~54줄 : 부동소수점 문제가 발생하므로, 소수 3번째 자리에서 반올림한다.

 

64줄 : 나스닥(23년 3월물)과 1분을 입력하여 32줄의 함수(rq_data_opc10002)를 실행한다.


4. 전체 코드

현재가를 기준으로 볼린저밴드의 상한선/하한선을 구하는 코드는 아래와 같다.

 

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

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

        self.kiwoom.OnEventConnect.connect(self.login_Connect)
        self.kiwoom.OnReceiveTrData.connect(self.trdata_get)

        self.kiwoom.dynamicCall("CommConnect(1)")  # CommConnect() : 괄호 안에 자동(1)을 넣는다.
        self.login_event_loop = QEventLoop()  # from PyQt5.QtCore import * : qtcore가 임포트되어야 함
        self.login_event_loop.exec_()

        ##### 볼린저밴드 상단선/하단선 구하기 #####
        self.close_price2 = []

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

    ########## 키움서버에 TR 요청하는 함수 모음 ##########
    def rq_data_opc10002(self, stock_code_num, time_unit):
        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", "", "1012")
        time.sleep(0.5)
        self.tr_event_loop = QEventLoop()
        self.tr_event_loop.exec_()

    def trdata_get(self, scrno, rqname, trcode, recordname, prenext):
        if rqname == "opc_10002":
            for i in range(0, 20):
                close_price1 = abs(float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002", "opc10002", i, "현재가").strip()))

                self.close_price2.append(close_price1)
                self.tr_event_loop.exit()

            # print(self.close_price2)        # 합계
            # print(len(self.close_price2))   # 개수 : 20개

            self.close_price3 = numpy.mean(self.close_price2) + numpy.std(self.close_price2) * 2
            self.close_price4 = numpy.mean(self.close_price2) - numpy.std(self.close_price2) * 2

            self.close_price3 = round(self.close_price3, 2)
            self.close_price4 = round(self.close_price4, 2)

            print(self.close_price3)
            print(self.close_price4)

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

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

    app.exec_()
    
    
# (expected result)
# 12223.99
# 12220.36

 


5. 마치며

볼린저밴드의 상한선과 하한선 값을 구하는 방법을 알아보았다. numpy 함수를 이용하니 상.당.히 쉬웠다.

볼린저밴드 값을 그대로 활용할지, 혹은 조정계수를 그대로 넣어줄지는 사용자가 결정할 일이다.

 

해외선물 매매를 하다보면 상/하한선을 돌파하는 경우가 생각보다 많다. 그 때마다 진입/청산이 된다면 상당히 많은 거래(수수료)와 수익의 불확실성이 증가할 수도 있다. 투자자 입장에서는 달갑지만은 않은 일이리라...

 

조정계수는 필자가 임의로 지칭한 숫자이다.

예를 들어보자. 나스닥 상한선이 10,000p인데, 현재가가 10,000.25에 들어갔다면, 숏으로 진입하는게 좋을까?

10,000p x 0.1% = 10p, 즉 상한선에 조정계수를 곱하면 10,010p로 변환이 될 것이다.

 

당초 54줄~55줄에서 일정 숫자를 곱해주면 된다.

그림4. 부동소수점 문제 회피를 위해 소수 3번째 자리에서 반올림해준다.

 

아래 코드와 같이 상한선에는 0.1%를 더해주고, 하한선에는 0.1% 빼주면 상/하한선의 폭이 넓어진다.

(진입을 위한 최적점을 고민할 필요가 있다)

self.close_price3 = round(self.close_price3, 2) * 1.001
self.close_price4 = round(self.close_price4, 2) * 0.009

 

또한, 신뢰구간3을 적용(표준편차 x 3)을 적용하여 신뢰구간 99.7%의 통계적 방법을 고민해보는 것도 좋아 보인다. 예외가 발생한 곳에서 진입하여 반대매매로 수익내면 그 또한 좋은 방법이 아닐가?

 

다음 글에는 RSI 구하는 방법을 알아보자.

반응형