이번 시간에는 요청이 있었던 업비트API와 파이썬을 이용하여 볼린저밴드 지표값을 구하는 방법에 대해서 살펴 보려고 합니다.
볼린저밴드 외에도 다른 지표들을 계산하는 방법은 아래 링크를 클릭하여 확인하실 수 있습니다.
2021.07.27 - [프로젝트/비트코인 자동매매] - RSI 상대강도지수 구하기 - 파이썬 업비트 비트코인 자동매매
2021.07.29 - [프로젝트/비트코인 자동매매] - MFI 자금흐름지수 구하기 - 파이썬 업비트 비트코인 자동매매
2021.08.03 - [프로젝트/비트코인 자동매매] - MACD 지표 구하기 - 파이썬 업비트 비트코인 자동매매
목차 - 클릭하면 이동합니다.
볼린저밴드 보조지표
볼린저밴드는 이동평균선을 기준선으로 정하고 표준편차로 상단과 하단에 밴드를 만들어 가격의 범위를 정한 후 매매 신호를 포착하는데 활용하는 보조지표 입니다.
일반적으로 볼린저밴드는 20일 이동평균선을 이용하고 표준편차는 2로 사용하기 때문에 업비트에서 볼린저밴드 지표를 추가하면 기본 설정인 ① BB (20, 2)가 표시 됩니다.
기본 설정값은 원래 주식에서 많이 사용하던 값이기 때문에 변동성이 심한 코인이나 원하는 경우에는 이동평균선 및 표준편차를 원하는 값으로 변경하여 사용할 수 있습니다.
볼린저 밴드를 계산하는 방법은 아래와 같습니다.
① 기준선(중심선) : 20일 이동평균선
② 상한선 : 20일 이동평균선 + (20일 표준편차 * 곱)
③ 하한선 : 20일 이동평균선 - (20일 표준편차 * 곱)
볼린저밴드 지표의 일반적인 해석
① 코인 가격이 볼린저밴드 상한선을 넘어서면 하락으로 전환
② 코인 가격이 볼린저밴드 하한선 아래로 내려가면 상승으로 전환
③ 하락장에서 볼린저밴드 기준선을 상승 돌파 후 상한선에 도달하면 상승 추세 전환으로 판단
④ 상승장에서 볼린저밴드 기준선을 하락 돌파 후 하한선에 도달하면 하락 추세 전환으로 판단
⑤ 볼린저밴드의 상/하한선의 폭이 좁아지고 지속되면 횡보구간으로 판단. 횡보 후에 큰 추세 전환이 나올 가능성이 많음.
볼린저밴드를 보면 위에 설명드린대로 거의 대부분 캔들이 밴드 상한 및 하한을 터치하거나 넘어서면서 차트를 그리고 있는 것을 알 수 있습니다.
그래서 볼린저밴드를 상당히 신뢰할 수 있는 지표라고 생각할 수도 있지만 볼린저밴드도 다른 지표들과 마찬가지로 이미 지난 가격을 가지고 계산하는 후행 지표입니다. 즉 미래의 값을 예측하는 지표가 아니며 미래의 값을 예측하는데 조금의 도움을 받을 수 있는 지표라고 생각하시면 좋을 듯 싶습니다.
볼린저밴드 지표를 활용항 매매신호 포착 방법
볼린저밴드의 상/하한선을 터치하는 것만으로 매매를 하는 것은 손실의 위험소지가 많습니다. 항상 다른 보조지표를 추가로 참고하여 매매하는 전략을 세우시는 것이 좋습니다.
① 하락장에서 볼린저밴드 기준선을 상승 돌파 후 상한선에 도달하면 매수(상승추세전환)
② 상승장에서 볼린저밴드 기준선을 하락 돌파 후 하한선에 도달하면 매도(하락추세전환)
③ 볼린저밴드 상/하한선의 폭이 좁게 지속되다가 거래량이 늘어나면 매수(횡보후 급상승)
업비트 API를 이용한 파이썬 코드
공통코드
import logging
import requests
import time
import smtplib
import jwt
import sys
import uuid
import hashlib
import math
import numpy
import pandas as pd
from datetime import datetime, timedelta
from decimal import Decimal
from urllib.parse import urlencode
# Keys
access_key = '업비트에서 발급받은 Access Key'
secret_key = '업비트에서 발급받은 Secret Key'
server_url = 'https://api.upbit.com'
# -----------------------------------------------------------------------------
# - Name : set_loglevel
# - Desc : 로그레벨 설정
# - Input
# 1) level : 로그레벨
# 1. D(d) : DEBUG
# 2. E(e) : ERROR
# 3. 그외(기본) : INFO
# - Output
# -----------------------------------------------------------------------------
def set_loglevel(level):
try:
# ---------------------------------------------------------------------
# 로그레벨 : DEBUG
# ---------------------------------------------------------------------
if level.upper() == "D":
logging.basicConfig(
format='[%(asctime)s][%(levelname)s][%(filename)s:%(lineno)d]:%(message)s',
datefmt='%Y/%m/%d %I:%M:%S %p',
level=logging.DEBUG
)
# ---------------------------------------------------------------------
# 로그레벨 : ERROR
# ---------------------------------------------------------------------
elif level.upper() == "E":
logging.basicConfig(
format='[%(asctime)s][%(levelname)s][%(filename)s:%(lineno)d]:%(message)s',
datefmt='%Y/%m/%d %I:%M:%S %p',
level=logging.ERROR
)
# ---------------------------------------------------------------------
# 로그레벨 : INFO
# ---------------------------------------------------------------------
else:
# -----------------------------------------------------------------------------
# 로깅 설정
# 로그레벨(DEBUG, INFO, WARNING, ERROR, CRITICAL)
# -----------------------------------------------------------------------------
logging.basicConfig(
format='[%(asctime)s][%(levelname)s][%(filename)s:%(lineno)d]:%(message)s',
datefmt='%Y/%m/%d %I:%M:%S %p',
level=logging.INFO
)
# ----------------------------------------
# Exception Raise
# ----------------------------------------
except Exception:
raise
# -----------------------------------------------------------------------------
# - Name : send_request
# - Desc : 리퀘스트 처리
# - Input
# 1) reqType : 요청 타입
# 2) reqUrl : 요청 URL
# 3) reqParam : 요청 파라메타
# 4) reqHeader : 요청 헤더
# - Output
# 4) reponse : 응답 데이터
# -----------------------------------------------------------------------------
def send_request(reqType, reqUrl, reqParam, reqHeader):
try:
# 요청 가능회수 확보를 위해 기다리는 시간(초)
err_sleep_time = 0.3
# 요청에 대한 응답을 받을 때까지 반복 수행
while True:
# 요청 처리
response = requests.request(reqType, reqUrl, params=reqParam, headers=reqHeader)
# 요청 가능회수 추출
if 'Remaining-Req' in response.headers:
hearder_info = response.headers['Remaining-Req']
start_idx = hearder_info.find("sec=")
end_idx = len(hearder_info)
remain_sec = hearder_info[int(start_idx):int(end_idx)].replace('sec=', '')
else:
logging.error("헤더 정보 이상")
logging.error(response.headers)
break
# 요청 가능회수가 3개 미만이면 요청 가능회수 확보를 위해 일정시간 대기
if int(remain_sec) < 3:
logging.debug("요청 가능회수 한도 도달! 남은횟수:" + str(remain_sec))
time.sleep(err_sleep_time)
# 정상 응답
if response.status_code == 200 or response.status_code == 201:
break
# 요청 가능회수 초과인 경우
elif response.status_code == 429:
logging.error("요청 가능회수 초과!:" + str(response.status_code))
time.sleep(err_sleep_time)
# 그 외 오류
else:
logging.error("기타 에러:" + str(response.status_code))
logging.error(response.status_code)
break
# 요청 가능회수 초과 에러 발생시에는 다시 요청
logging.info("[restRequest] 요청 재처리중...")
return response
# ----------------------------------------
# Exception Raise
# ----------------------------------------
except Exception:
raise
# -----------------------------------------------------------------------------
# - Name : get_candle
# - Desc : 캔들 조회
# - Input
# 1) target_item : 대상 종목
# 2) tick_kind : 캔들 종류 (1, 3, 5, 10, 15, 30, 60, 240 - 분, D-일, W-주, M-월)
# 3) inq_range : 조회 범위
# - Output
# 1) 캔들 정보 배열
# -----------------------------------------------------------------------------
def get_candle(target_item, tick_kind, inq_range):
try:
# ----------------------------------------
# Tick 별 호출 URL 설정
# ----------------------------------------
# 분붕
if tick_kind == "1" or tick_kind == "3" or tick_kind == "5" or tick_kind == "10" or tick_kind == "15" or tick_kind == "30" or tick_kind == "60" or tick_kind == "240":
target_url = "minutes/" + tick_kind
# 일봉
elif tick_kind == "D":
target_url = "days"
# 주봉
elif tick_kind == "W":
target_url = "weeks"
# 월봉
elif tick_kind == "M":
target_url = "months"
# 잘못된 입력
else:
raise Exception("잘못된 틱 종류:" + str(tick_kind))
logging.debug(target_url)
# ----------------------------------------
# Tick 조회
# ----------------------------------------
querystring = {"market": target_item, "count": inq_range}
res = send_request("GET", server_url + "/v1/candles/" + target_url, querystring, "")
candle_data = res.json()
logging.debug(candle_data)
return candle_data
# ----------------------------------------
# Exception Raise
# ----------------------------------------
except Exception:
raise
# -----------------------------------------------------------------------------
# - Name : get_bb
# - Desc : 볼린저밴드 조회
# - Input
# 1) target_item : 대상 종목
# 2) tick_kind : 캔들 종류 (1, 3, 5, 10, 15, 30, 60, 240 - 분, D-일, W-주, M-월)
# 3) inq_range : 캔들 조회 범위
# 4) loop_cnt : 지표 반복계산 횟수
# - Output
# 1) 볼린저 밴드 값
# -----------------------------------------------------------------------------
def get_bb(target_item, tick_kind, inq_range, loop_cnt):
try:
# 캔들 데이터 조회용
candle_datas = []
# 볼린저밴드 데이터 리턴용
bb_list = []
# 캔들 추출
candle_data = get_candle(target_item, tick_kind, inq_range)
# 조회 횟수별 candle 데이터 조합
for i in range(0, int(loop_cnt)):
candle_datas.append(candle_data[i:int(len(candle_data))])
# 캔들 데이터만큼 수행
for candle_data_for in candle_datas:
df = pd.DataFrame(candle_data_for)
dfDt = df['candle_date_time_kst'].iloc[::-1]
df = df['trade_price'].iloc[::-1]
# 표준편차(곱)
unit = 2
band1 = unit * numpy.std(df[len(df) - 20:len(df)])
bb_center = numpy.mean(df[len(df) - 20:len(df)])
band_high = bb_center + band1
band_low = bb_center - band1
bb_list.append({"type": "BB", "DT": dfDt[0], "BBH": round(band_high, 4), "BBM": round(bb_center, 4),
"BBL": round(band_low, 4)})
return bb_list
# ----------------------------------------
# 모든 함수의 공통 부분(Exception 처리)
# ----------------------------------------
except Exception:
raise
볼린저밴드 지표도 공통 모듈에 작성하고 필요할 때마다 호출하여 사용하는 것이 쉽고 편리합니다.
위의 예시에서는 upbit.py 라는 공통 모듈을 만들어서 사용하고 있으며 공통 모듈을 만들고 구조를 잡는 방법은 아래 포스트를 참고하시면 도움이 될 것 같습니다.
2021.06.06 - [프로젝트/비트코인 자동매매] - 비트코인 자동매매 - 프로젝트 구조 만들기
로직 호출
import os
import sys
import logging
import math
import traceback
# 공통 모듈 Import
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
from lib import upbit as upbit # noqa
# -----------------------------------------------------------------------------
# - Name : main
# - Desc : 메인
# -----------------------------------------------------------------------------
if __name__ == '__main__':
# noinspection PyBroadException
try:
print("***** USAGE ******")
print("[1] 로그레벨(D:DEBUG, E:ERROR, 그외:INFO)")
# 로그레벨(D:DEBUG, E:ERROR, 그외:INFO)
upbit.set_loglevel('I')
# ---------------------------------------------------------------------
# Logic Start!
# ---------------------------------------------------------------------
# 볼린저밴드 조회
bb_data = upbit.get_bb('KRW-BTC', '30', '200', 10)
for bb_data_for in bb_data:
logging.info(bb_data_for)
except KeyboardInterrupt:
logging.error("KeyboardInterrupt Exception 발생!")
logging.error(traceback.format_exc())
sys.exit(1)
except Exception:
logging.error("Exception 발생!")
logging.error(traceback.format_exc())
sys.exit(1)
bb_data = upbit.get_bb('KRW-BTC', '30', '200', 10)
위와 같이 원하는 곳에서 공통모듈을 호출하면 볼린저밴드 값을 가져올 수 있습니다. 위의 예시는 비트코인(KRW-BTC)의 30분봉을 기준으로 총 10개의 볼린저밴드 데이터를 구하는 예시 입니다.
호출 결과
{'type': 'BB', 'DT': '2021-08-03T17:00:00', 'BBH': 45915342.6301, 'BBM': 45238950.0, 'BBL': 44562557.3699}
{'type': 'BB', 'DT': '2021-08-03T16:30:00', 'BBH': 45996260.3999, 'BBM': 45278100.0, 'BBL': 44559939.6001}
{'type': 'BB', 'DT': '2021-08-03T16:00:00', 'BBH': 46030189.1093, 'BBM': 45305150.0, 'BBL': 44580110.8907}
{'type': 'BB', 'DT': '2021-08-03T15:30:00', 'BBH': 46034272.6533, 'BBM': 45316100.0, 'BBL': 44597927.3467}
{'type': 'BB', 'DT': '2021-08-03T15:00:00', 'BBH': 46027529.1087, 'BBM': 45340650.0, 'BBL': 44653770.8913}
{'type': 'BB', 'DT': '2021-08-03T14:30:00', 'BBH': 46019871.818, 'BBM': 45369250.0, 'BBL': 44718628.182}
{'type': 'BB', 'DT': '2021-08-03T14:00:00', 'BBH': 45989712.0026, 'BBM': 45415350.0, 'BBL': 44840987.9974}
{'type': 'BB', 'DT': '2021-08-03T13:30:00', 'BBH': 45988210.4348, 'BBM': 45473100.0, 'BBL': 44957989.5652}
{'type': 'BB', 'DT': '2021-08-03T13:00:00', 'BBH': 45971348.2542, 'BBM': 45533150.0, 'BBL': 45094951.7458}
{'type': 'BB', 'DT': '2021-08-03T12:30:00', 'BBH': 46038912.3128, 'BBM': 45571100.0, 'BBL': 45103287.6872}
① DT : 캔들 기준시간
② BBH : 상한선
③ BBM : 기준선(중심선)
④ BBL : 하한선
작성한 모듈을 호출하면 위와 같이 볼린저밴드 데이터를 추출할 수 있습니다.
업비트에서 볼린저밴드를 추가한 후에 값을 비교해 볼 수 있습니다. 소수점까지 정확하게 일치함을 확인할 수 있습니다.
다음 시간에는 지표를 한번에 구하는 통합 모듈을 만들어서 지금까지 만들었던 지표를 한 번의 호출로 조회하는 방법을 알아보도록 하겠습니다.
우측 상단 버튼을 눌러 블로그를 구독해 주시면 조금 더 빨리 소식을 받아보실 수 있습니다.
'프로젝트 > 비트코인 자동매매' 카테고리의 다른 글
윌리암스 %R 지표 구하기 - 파이썬 업비트 비트코인 자동매매 (5) | 2021.10.07 |
---|---|
코인 자동매매 프로그램 샘플 예제 - 파이썬 업비트 비트코인 자동매매 (17) | 2021.09.07 |
보조 지표 한번에 구하기 - 파이썬 업비트 비트코인 자동매매 (11) | 2021.08.10 |
MACD 지표 구하기 - 파이썬 업비트 비트코인 자동매매 (4) | 2021.08.03 |
MFI 자금흐름지수 구하기 - 파이썬 업비트 비트코인 자동매매 (7) | 2021.07.29 |
RSI 상대강도지수 구하기 - 파이썬 업비트 비트코인 자동매매 (17) | 2021.07.27 |