처음 Tech&Fin 블로그를 시작했을 때보다 비교적 많은 분들이 블로그를 찾아주시고 올려드린 비트코인 자동매매 프로그램을 설치하여 사용중인 것 같습니다.
사용하시는 분이 많아질 수록 예상치 못한 오류가 발생하는 케이스도 많아지고 있는데요. 이번 시간에는 지금까지 댓글로 문의를 받았던 오류들을 해결하면서 알게된 내용을 공유드리는 시간을 가져 보려고 합니다.
목차 - 클릭하면 이동합니다.
일반적인 오류 해결 방법
어떤 프로그램이나 마찬가지겠지만 오류가 발생하는 원인은 한 가지가 아니라 매우 다양합니다. 또한 같은 메세지를 내 뱉는 오류라 할 지라도 오류의 원인은 다를 수 있습니다.
그렇기 때문에 오류를 해결하는 방법이 한 가지일 수는 없으며 매우 다양한 케이스가 존재할 수 있습니다. 이럴 때 오류를 해결할 수 있는 가장 좋은 방법은 의심이 가는 곳에서 로그를 발생시켜 원인을 차근차근 분석 해 찾아내는 방법입니다.
[2022/03/12 02:00:31 PM][ERROR][test_module.py:31]:Traceback (most recent call last):
File "C:\Python-Project\trade_bot\test_module.py", line 21, in <module>
accounts = upbit.get_accounts('Y', 'KRW')
File "C:\Python-Project\trade_bot\module\upbit.py", line 785, in get_accounts
if market_code + '-' + account_data_for['currency'] == market_item_list_for['market']:
TypeError: string indices must be integers
예를 들어 위와 같은 오류가 발생한 경우 실제 에러가 발생한 내용은 [TypeError: string indices must be integers] 입니다. 스트링의 인덱스가 정수가 아닌 경우에 발생하는 에러인데요. 그럼 왜 이런 에러가 발생했을까요?
앞서 말씀 드렸듯이 원인은 한가지가 아닐 수 있습니다. 그렇기 때문에 가능한 의심이 가는 곳에 로그를 발생 시켜 원인을 차근차근 찾아 보아야 합니다.
File "C:\Python-Project\trade_bot\module\upbit.py", line 785, in get_accounts
if market_code + '-' + account_data_for['currency'] == market_item_list_for['market']:
먼저 바로 위에 줄에 trace가 찍힌 곳부터 차근차근 살펴보면 되는데요. upbit.py의 785 번째 라인에서 에러가 발생했음을 알수 있습니다.
res = send_request("GET", server_url + "/v1/accounts", "", headers)
account_data = res.json()
for account_data_for in account_data:
for market_item_list_for in market_item_list:
# 해당 마켓에 있는 종목만 조합
if market_code + '-' + account_data_for['currency'] == market_item_list_for['market']:
# KRW 및 소액 제외
if except_yn == "Y" or except_yn == "y":
해당 위치로 가서 살펴보니 해당 마켓에 있는 종목만 걸러내기 위한 로직에서 에러가 발생했음을 알 수 있으며 해당 if문 내의 어디선가 오류가 발생했음을 짐작할 수 있습니다.
① market_code
② account_data_for
③ market_item_list_for
우선 세 가지 오브젝트의 내용이 정상적으로 들어오지 않았는지 로그를 발생시켜 확인해 보는 것이 좋습니다.
res = send_request("GET", server_url + "/v1/accounts", "", headers)
account_data = res.json()
for account_data_for in account_data:
for market_item_list_for in market_item_list:
logging.info(market_code)
logging.info(account_data_for)
logging.info(market_item_list_for)
# 해당 마켓에 있는 종목만 조합
if market_code + '-' + account_data_for['currency'] == market_item_list_for['market']:
# KRW 및 소액 제외
if except_yn == "Y" or except_yn == "y":
7~9번 라인과 같이 에러가 발생한 곳 직전에 의심이 가는 세 가지 오브젝트를 로그로 출력해 보았습니다.
[2022/03/12 02:32:18 PM][INFO][upbit.py:784]:KRW
[2022/03/12 02:32:18 PM][INFO][upbit.py:785]:error
[2022/03/12 02:32:18 PM][INFO][upbit.py:786]:{'market': 'KRW-BTC', 'korean_name': '비트코인', 'english_name': 'Bitcoin'}
[2022/03/12 02:32:18 PM][ERROR][test_module.py:30]:Exception 발생!
[2022/03/12 02:32:18 PM][ERROR][test_module.py:31]:Traceback (most recent call last):
File "C:\Python-Project\trade_bot\test_module.py", line 21, in <module>
accounts = upbit.get_accounts('Y', 'KRW')
File "C:\Python-Project\trade_bot\module\upbit.py", line 789, in get_accounts
if market_code + '-' + account_data_for['currency'] == market_item_list_for['market']:
TypeError: string indices must be integers
로그 내용을 살펴보니 2번째 줄에서 account_data_for의 내용이 정상적이지 않고 'error'라는 텍스트 스트링이 들어 있음을 확인할 수 있습니다. 그로 인해 딕셔너리 타입으로 예상하여 account_data_for['currency']라는 키를 가져오려고 한 부분에서 에러가 발생하는 것이 었습니다.
그럼 실질적인 에러의 원인은 다른 곳에 있다는 건데요. 왜 account_data_for에 잔고 데이터가 아닌 error가 입력되어 있는지를 찾아야 합니다.
res = send_request("GET", server_url + "/v1/accounts", "", headers)
account_data = res.json()
account_data_for는 잔고를 조회하는 API를 호출한 결과에서 받아오는 내용인데 결국 업비트 API를 호출한 후 정상 결과가 아닌 error를 받았음을 짐작할 수 있습니다.
# -----------------------------------------------------------------------------
# - 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("에러 코드:" + str(response.status_code))
logging.error("에러 내용:" + str(response.text))
break
# 요청 가능회수 초과 에러 발생시에는 다시 요청
logging.info("[restRequest] 요청 재처리중...")
return response
# ----------------------------------------
# Exception Raise
# ----------------------------------------
except Exception:
raise
업비트 API를 호출하는 공통 모듈 부분입니다. 여기서 에러가 발생한건데요. 에러의 원인을 찾기 위해 50~52 라인에 오류의 내용을 로그로 출력하도록 했습니다.
기존에는 에러코드까지만 로그를 출력해서 원인 파악이 조금 힘들었는데 에러의 상세 내역(response.text)까지 출력하도록 하면 에러의 내용을 파악하는데 훨씬 도움이 됩니다.
C:\Python-Project\trade_bot\venv\Scripts\python.exe C:/Python-Project/trade_bot/test_module.py
[2022/03/12 02:32:18 PM][ERROR][upbit.py:172]:기타 에러:401
[2022/03/12 02:32:18 PM][ERROR][upbit.py:173]:에러 코드:401
[2022/03/12 02:32:18 PM][ERROR][upbit.py:174]:에러 내용:{"error":{"name":"invalid_access_key","message":"잘못된 엑세스 키입니다."}}
[2022/03/12 02:32:18 PM][INFO][upbit.py:784]:KRW
[2022/03/12 02:32:18 PM][INFO][upbit.py:785]:error
[2022/03/12 02:32:18 PM][INFO][upbit.py:786]:{'market': 'KRW-BTC', 'korean_name': '비트코인', 'english_name': 'Bitcoin'}
[2022/03/12 02:32:18 PM][ERROR][test_module.py:30]:Exception 발생!
[2022/03/12 02:32:18 PM][ERROR][test_module.py:31]:Traceback (most recent call last):
File "C:\Python-Project\trade_bot\test_module.py", line 21, in <module>
accounts = upbit.get_accounts('Y', 'KRW')
File "C:\Python-Project\trade_bot\module\upbit.py", line 789, in get_accounts
if market_code + '-' + account_data_for['currency'] == market_item_list_for['market']:
TypeError: string indices must be integers
다시 오류를 재현해보니 오류의 내용을 명확하게 알 수 있게 되었습니다. 바로 액세스키가 잘못되어서 발생한 오류였는데요. 참고로 이 오류는 제가 일부러 발생시킨 오류이며 업비트에서 오류로 넘어올 수 있는 내용은 아래와 같으니 참고하시면 좋을 것 같습니다.
에러 코드 관련한 업비트 API 공식 문서는 아래 링크를 통해 확인하실 수 있습니다.
자주 발생하는 오류 해결 방법
그럼 지금부터 그 동안 댓글로 문의 주신 내용들 중 빈번하게 발생하는 내용들에 대한 해결 방법을 알아보도록 하겠습니다. 파이썬의 버전 및 사용하는 환경이 달라 오류의 해결 방법이 다소 다를 수 있는 점은 미리 양해 부탁 드리겠습니다.
JWT 모듈 관련 오류
C:\Python-Project\trade_bot\venv\Scripts\python.exe C:/Python-Project/trade_bot/test_module.py
[2022/03/12 02:50:27 PM][ERROR][test_module.py:30]:Exception 발생!
[2022/03/12 02:50:27 PM][ERROR][test_module.py:31]:Traceback (most recent call last):
File "C:\Python-Project\trade_bot\test_module.py", line 21, in <module>
accounts = upbit.get_accounts('Y', 'KRW')
File "C:\Python-Project\trade_bot\module\upbit.py", line 774, in get_accounts
jwt_token = jwt.encode(payload, get_env_keyvalue('secret_key'))
AttributeError: module 'jwt' has no attribute 'encode'
JWT는 Json Web Token의 약자로 인증을 위한 모듈입니다. 파이썬에서 설치할 수 있는 JWT 모듈은 총 2가지로 그냥 JWT와 PyJWT가 있습니다. 이 중에 PyJWT를 설치하는 것이 가장 좋으며 JWT를 설치하셨다면 공통모듈의 encode와 decode 부분을 조금 수정해 주어야 합니다.
그렇기 때문에 현재 설치되어 있는 JWT 모듈을 확인하고 JWT가 설치되어 있다면 삭제 후 PyJWT를 설치하여 오류를 해결 할 수 있습니다.
python -m pip list
먼저 파이참 하단의 터미널을 클릭하여 명령어 창을 연 후에 설치되어 있는 모듈을 검색하는 명령어를 수행합니다.
현재 설치되어 있는 모듈을 확인할 수 있으며 현재 pyJWT가 아닌 jwt 1.3.1이 설치되어 있음을 알 수 있습니다.
python -m pip uninstall jwt
먼저 jwt 모듈을 삭제합니다.
python -m pip install pyjwt
pyjwt 모듈을 설치해 줍니다.
인증키 관련 오류
그 외에 자주 발생하는 오류는 인증키 관련 오류인데요. 원인을 알기 힘든 오류가 발생 한다면 아래 인증키 관련 내용을 살펴 보시는 것이 도움이 될 수 있을것 같습니다.
① 허용된 IP 확인 : 업비트에서 API Key를 생성할 때 허용된 IP를 등록했다면 해당 IP에서 프로그램을 수행해야만 정상적으로 작동합니다. 업비트에 등록한 IP와 현재 프로그램을 수행하는 PC 또는 서버의 공인 IP를 확인합니다.
② 인증키 만료 확인 : 업비트에서 발급받은 API Key는 무기한 인증키가 아닙니다. 만료기간이 있기 때문에 이 부분을 잘 확인할 필요가 있습니다. 특히 잘 되던 프로그램이 갑자기 안되는 것은 인증키 만료의 경우일 가능성이 높습니다.
파이썬 버전 및 PIP 버전 오류
파이썬이 현재 시점으로 3.9 버전보다 낮거나 PIP의 버전이 최신이 아닌경우 모듈 설치 및 구동 시 런타임 에러가 발생할 수 있으니 가능한 최신 버전으로 유지하는 것이 좋습니다.
python --version
설치된 파이썬 버전은 위의 명령어로 확인할 수 있습니다. 참고로 리눅스에서 파이썬 3.9를 설치하는 방법은 아래 포스팅을 참고 부탁 드립니다.
2022.01.13 - [코딩스토리/리눅스] - 리눅스 서버에 파이썬 3.9 설치하기
python -m pip --version
PIP 버전은 위의 명령어로 확인할 수 있습니다.
python.exe -m pip install --upgrade pip
PIP는 위의 명령어로 업그레이드 할 수 있습니다.
마치며
이번 포스팅에서는 로그를 이용하여 에러를 찾는 기본적인 방법과 자주 발생하는 문제 해결 방법에 대해서 살펴 보았습니다.
하지만 앞서 말씀 드린바와 같이 에러가 발생하는 경우는 매우 다양할 수 있기 때문에 단순히 에러 내용으로 판단하기 보다는 앞뒤로 로그를 출력해가며 실제 원인을 파악하여 해결하는 방법에 익숙해 지시는 것을 추천 드립니다.
블로그를 구독하면 소식을 조금 더 빨리 받아 보실 수 있습니다. 감사합니다.
'프로젝트 > 비트코인 자동매매' 카테고리의 다른 글
[비트코인 자동매매 시즌2] #1 - 개발 환경 설명 및 설정 (0) | 2024.05.10 |
---|---|
비트코인 자동매매 프로그램 만들기 시즌2 안내 (1) | 2024.04.30 |
급등주 찾기 쿼리 - PostgreSQL 버전 (4) | 2022.05.03 |
업비트 웹소켓 데이터 PostgreSQL DB에 저장하기 - 파이썬 비트코인 자동매매 (12) | 2022.02.23 |
업비트 공지사항 크롤링하여 텔레그램으로 메세지 알림받기 (21) | 2022.01.29 |
급등주 찾기 - 업비트 파이썬 비트코인 자동매매 프로그램 (19) | 2022.01.26 |