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

(키움증권 해외선물 OpenAPI-W) 상승/하락 추세 파악 (2) 단기추세에서 양봉/음봉 구분을 통한 진입하는 방법

봄이오네 2023. 2. 5. 08:03
반응형
목 차
1. 들어가며
2. 사전설명
   1) 상승/하락 판단을 위한 시간 기준
   2) 상승/하락장 판단을 위한 가정
   3) 양봉/음봉 개수에 따른 승률
   4) 나스닥(NQH23) 23년 1월 1분봉 모음
3. 코드설명
4. 전체 코드
5. 마치며

 

1. 들어가며

지난 글에서는 상승/하락 추세 파악을 통해 양봉/음봉 개수 구하는 방법에 대해 알아보았다. 수익의 극대화도 중요하지만, 그보다 더 중요한 것은 리스크 관리를 통해 손실을 최소화하는 방법이 있을 것이다.

 

100% 수익을 얻는 매매방법은 없다고 생각한다. 급변하는 시장상황은 종목의 가격을 계속 바꾸는 요인이 된다. FOMC 회의에서 금리 인상 발언, 인플레이션 증가, 실업률 증가, 유가 상승 등 시장에는 나스닥, 천연가스, 유가 등 해외선물에 영향을 미칠 수 있는 요소들은 얼마든지 존재한다. 생각지 못한 가격 상승/하락 등 가격변동 요인에 대해 지혜롭게 대처해야 수익이 지속적으로 발생할 수 있을 것이다.

 

이번 글에서는 단기추세에서 나타나는 양봉과 음봉 관련 내용을 파이썬으로 구현해보자.

상승장과 하락장에 대한 명확한 정의는 없다. 어떤 사람에게는 급등/급락하는 시장이 상/하락장이 될 수도 있고, 어떤 사람에게는 시가 대비 종가가 상승하면 상승장, 하락하면 하락장이라고 한다.

필자는 특별한 기준을 정하여 상/하락장을 말하지는 않을 것이다. 수익만 생각할 것이다.


2. 사전설명

위에서도 언급하였지만, 시장의 상승/하락에 대한 명확한 기준은 없다. 그래서 투자자별로 각자의 주관적 판단이 필요하다.

 

1) 상승/하락 판단을 위한 시간 기준

여기서는 현재가 기준으로 20분 이전의 시가와 종가를 기준으로 시장을 판단할 것이다. 이유는?

  • 청산 기준 : 진입 후, 20분 내 설정한 수익(200달러)가 나오면 청산
  • 진입 기준 : 20분 이후의 수익/손실에 대한 판단을 20분 전의 양봉/음봉에 따라 판단할 예정이다.

즉, 청산기준을 현재가 기준 20분 이후로 잡았다면, 진입기준을 현재가 기준 20분 전으로 설정하고 양/음봉을 판별한다.

 

2) 상승/하락장 판단을 위한 가정

그래서 상승장/하락장을 판단하는 기준은?

결론부터 말하면 상승장/하락장을 판단하는 기준은 특별히 제시하지 않을 예정이다. -_-+

 

이유는? 현재가 기준, 20분 이전의 20개 봉을 기준으로, 상승/하락을 논할 실익이 없다. 또한 엘리엇 파동이론의 1파 혹은 3파, 5파 등 각 단계에 있는데, 고작 20개 봉으로 상/하락을 말하는 것은 굉장히 곤란해 보인다.

 

핵심은 현재봉을 제외하였다는 것이다. 시가/고가/저가/종가에 진입을 위해 현재가는 영향을 미치지 않는다. 진입을 위한 판단기준은 오롯이 현재부터 20분이내이다. 예를 들어, 현재가 09:15분이면, 08:55분 ~ 09:14분의 "종가-저가"를 활용한다.

 

또한, 20분 내 양봉/음봉이 각각 몇개있을 때 수익/손실이 어느 정도 확률로 발생하고, 승/패가 어떻게 되느냐이다. 다시한번 말씀드리지만, 수익을 내고 손실을 줄이는 것이 우리들의 목표라는 점은 항상 머릿속에 넣어두자.

 

3) 양봉/음봉 개수에 따른 승률

< 그림1 >은 볼린저밴드 상/하단 + RSI 35/65이면서, 양봉/음봉 개수에 따른 수익(profit)에 따른 승률(확률)을 말한다. 수익은 진입은 나스닥(NQH23) 20분 이내 200달러가 발생할 확률을 설명한 것이다.

(실제 발생한 건 아니고, 1월 한달 간의 1분봉 모음을 임의로 시뮬레이션을 돌려보았다)

※ 주의할 것은 200달러의 수익이 나면 수익, 손실이 나면 손실이 아니라... 진입후 수익>손실이면 "승"으로 표기하였고, 수익<손실이면 "패"로 표기한 점은 미리 알고 있도록 할자.

 

  • 현재가 기준, 20분 이전의 봉에 양봉이 8~12개 있을 때, 각각 31.3~52.5%에 해당하고
  • 음봉이 8~12개 있을 때, 각각 38.5%~46.8%의 승률을 보여준다.

 

그림1. 양봉/음봉 개수에 따른 승률

 

4) 나스닥(NQH23) 23년 1월 1분봉 모음

나스닥(NQH23) 23년 1월 1분봉 모음은 아래 링크와 같다. 필자가 23년 1월 한달 내내 모은 나스닥 데이터이다.

※ (주의) 필자의 개인사정으로 1분봉을 받지 못한 날이 존재한다. 일부 데이터(시가, 종가, 볼린저밴드, RSI 등)가 누락되어 있다는 점은 엑셀 분석시 반드시 알고 있어야 한다.

 

minute_bong (230101~230203).xlsx
6.44MB

 

 

 


3. 코드설명

모듈 및 로그인 내용은 생략한다. < 그림2 >에서 짤린 코드는 4번(전체코드)에서 확인가능하다.

 

 

 

1줄~30줄 : 필요 모듈 현황 및 로그인에 대한 설명이며, 여기에서 동 내용은 생략한다.

21줄 : 46줄의 결과(종가-시가)에 대해 양봉이면, 21줄의 self.up_positive_price4의 리스트에 저장한다.

22줄 : 46줄의 결과(종가-시가)에 대해 음봉이면, 22줄의 self.down_negative_price5의 리스트에 저장한다.

 

33줄~38줄 : 72줄에서 실행(NQH23, "1")하면 데이터를 키움서버에 입력(SetInputValue) 및 데이터를 요청(CommRqData)한다.

 

 

40줄~41줄 : 36줄(CommRqData)의 요청을 통해 키움서버에서 이벤트(데이터 요청이 왔으니, 데이터를 반환하라는 이벤트)가 발생하여 12줄의 OnReceiveData(연결)을 통해, rqname == "opc_10002"와 일치하면,

 

42줄~44줄 : for문을 통해 수신받은 데이터(38줄)에서 "현재-1분 ~ 현재-21분"의 시가/종가 데이터를 받아온다.

46줄 : 받아온 시가, 종가의 차이를 계산한다.

 

53줄 : 이 글의 핵심이다. 양봉과 음봉 개수를 각각 구하기 위함이다.

48줄~49줄 : "종가 - 시가"의 차이가 양수이면, 21줄의 양수 리스트에 담아라.

50줄~51줄 : "종가 - 시가"의 차이가 음수이면, 22줄의 음수 리스트에 담아라.

 

53줄~60줄 : 받아온 데이터를 출력하라.

이 글에서 알고 싶은 것은 20분 이내 양봉(54줄)과 음봉 개수(59줄)이다. 48줄~51줄에 의해 21줄~22줄에 리스트에 각각의 요소들을 담았고, 리스트 앞에 len함수를 붙이면 리스트 내 요소 개수를 반환한다.

 

62줄~64줄 : 나중에 지속적으로 데이터를 받을 예정이다. 49줄과 51줄에서 계속 append되어 데이터가 누적되므로, 21줄~22줄의 리스트를 지속적으로 초기화한다. 이유는? 다음 데이터 조회시, 불필요한 데이터가 존재하면, 계산에 영향을 미치기 때문이다. 즉, 20개 데이터만 받아오는데, 초기화를 하지 않으면, 20개 데이터에 신규 20개 데이터가 누적되어 총 40개의 데이터가 리스트에 존재한다. ㅠ_ㅠ

 

72줄 : 나스닥(NQH23) 1분봉의 데이터를 33줄에 전달하여 rq_data_opc10002 함수를 실행한다.

 


4. 전체 코드

print 문은 코드의 결과를 확인하기 위함이이며삭제해도 무관한다.

54줄(양봉의 개수)와 59줄(음봉의 개수)를 구하는 것이 핵심이다.

 

더보기
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.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_open_price = []

        self.up_positive_price4 = []
        self.down_negative_price5 = []

    ########## 로그인 관련 함수 ##########
    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")
        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(1, 21):
                open_price1 = abs(float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002", "opc10002", i, "시가").strip()))
                close_price1 = abs(float(self.kiwoom.dynamicCall("GetCommData(QString,QString,int,QString)", "opc_10002", "opc10002", i, "현재가").strip()))

                self.close_open_price = close_price1 - open_price1

                if self.close_open_price > 0:
                    self.up_positive_price4.append(self.close_open_price)
                elif self.close_open_price < 0:
                    self.down_negative_price5.append(self.close_open_price)

            print(self.up_positive_price4)
            self.up_positive_price6 = len(self.up_positive_price4)
            print(self.up_positive_price6)
            print(type(self.up_positive_price6))

            print(self.down_negative_price5)
            self.down_negative_price7 = len(self.down_negative_price5)
            print(self.down_negative_price7)

            del self.close_open_price[:]        # 초기화
            del self.up_positive_price4[:]      # 초기화
            del self.down_negative_price5[:]    # 초기화

            self.tr_event_loop.exit()

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

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

    app.exec_()
    
    
# (expected result)
# [1.0, 0.5, 0.75, 0.25, 0.25, 0.5, 0.5, 0.25, 0.75]
# 9
# 9
# <class 'int'>
# [-0.5, -1.0, -1.5, -0.5, -2.0, -1.75, -2.75, -0.25, -0.75, -0.75, -0.75]
# 11
# 11

 


5. 마치며

현재 기준, 20분 이내 양봉과 음봉의 개수를 구하는 방법을 알아보았다. 위의 데이터만으로는 시장에 대해 "상승장이다, 하락장이다"고 단언할 수는 없을 것이다. 함부러 시장을 재단해서는 안된다는 걸... 요즘 정말 많이 깨닫고 있다. ㅠㅠ

 

다음 글에서는 < 그림1 >에서 확인한 사항에 대한 투자 전략(진입 방법)에 대해 간략히 알아볼 것이다.

반응형