Upbit에서 암호화폐 과거 시세를 가져와서 몇 가지 전략을 검증할 계획을 세웠다. Upbit는 현재 공식적으로 API를 지원하지 않는다.
Steemit에서 찾아보니 업비트에서 과거 시세를 가져올 수 있는 방법이 있었다.
Steemit의 보물 중에 한 분이신 @segyepark이 쓰신 글이다.
공식 API 없는 업비트 시세 정보 가져오기사용하기 쉽도록 java script를 적용한 버전. 그런데 현재 시세만 가져온다.
업비트(Upbit) 시세 구글시트에 자동으로 가져오기ImportJSON 함수를 이용한 버전
구글시트로 업비트 시세 조회하기
위의 글을 참고로 하여 과거 steem의 시세를 받아올 수 있는 프로그램을 파이썬으로 만들어보았다. 굳이 업비트 자료가 필요한 것은 steem을 거래하고 싶었기 때문이다.
결론적으로 업비트에서 스팀달러 일간 과거 가격을 볼 수 있는 html 주소는 아래와 같다. 마지막에 있는 count를 늘리면 원하는 기간만큼의 데이터를 가지고 올 수 있다.
https://crix-api-endpoint.upbit.com/v1/crix/candles/days?code=CRIX.UPBIT.BTC-SBD&count=3
그 결과는 json 형태로 넘어온다.
이 데이터를 잘 분석하여 우리가 필요로 하는 아래 형태의 데이터를 뽑아주는 파이썬 프로그램을 짜 보는 것이 오늘의 목표이다.
이 데이터는 아래와 같은 keyword를 이용하면 찾을 수 있다
위와 같은 json 형태의 데이터 중 원하는 항목만 뽑아주는 프로그램을 만들어보자.
파이썬에서 지원하는 urllib라고 하는 패키지를 이용하면 앞에서 소개한 url에서 나오는 결과를 받아올 수 있다. 그후 받은 값을 json이라고 하는 parsing용 패키지를 이용하면 원하는 데이터 값을 뽑아낼 수 있다. 이 과정을 코딩하면 다음과 같다.
coinName = "steem";
url = 'https://crix-api-endpoint.upbit.com/v1/crix/candles/days?code=CRIX.UPBIT.KRW-' + coinName +'&count=10';
text = urllib.request.urlopen(url).read() # url에 있는 데이터 읽기
sdata = bytes.decode(text) # Byte를 텍스트로 만든다.
data = json.loads(sdata) # json 구조로 변환
code = data[0]['code']
print(code)
print("==========================================")
print(" date open high low final vol")
print("==========================================")
for i in range(len(data)) :
date = data[i]['candleDateTimeKst']
onlyDate = date.split('T') # 날짜정보와 시간정보 분리
print(onlyDate[0], "%d"%data[i]['openingPrice'], "%d"%data[i]['highPrice'], "%d"%data[i]['lowPrice'], "%d"%data[i]['tradePrice'], "%d"%(data[i]['candleAccTradeVolume']));
print("==========================================")
전체적인 구조는 간단하다. url에 있는 데이터를 읽은 후 json 형태로 변환하여 원하는 데이터만 출력한 것이다.
이 프로그램의 문제는 업비트에 시세 데이터를 요청하는 함수가 실패하는 경우에 대한 대응 코드가 없다는 것이다. 원인은 정확하게 모르겠으나, 업비트의 서버가 단위 시간 당 처리하는 용량의 제한 때문으로 보인다.
코드를 조금 수정하여 실패하면 5초간 휴식을 한 후 다시 요청하는 코드로 변경해 보자. 사실 프로그래밍이 어려운 것은 이런 예외 처리를 많이 하여야 하기 때문이다. 위에서 보여준 코드는 누구나 만들 수 있지만 발생 가능한 모든 경우에 대하여 모두 대응하는 튼튼한(?) 코드를 만들기에는 상당한 시간과 노하우가 필요하다.
기존에는 url에서 데이터를 가져오는 부분이 한 줄이었지만 다양한 에러에 대한 대응 부분을 넣으면 이렇게 코드가 길어진다. 이 코드가 하는 일은 앞에서 설명한 url에서 원하는 데이타를 받지 못하는 HTTPerror이면 5초 간 쉰 후 다시 시도하는 코드이다.
fail2GetData = False
failCnt = 0
while True:
try :
text = urllib.request.urlopen(url).read()
except urllib.error.HTTPError as e:
print('Error code: ', e.code, ' 5초 대기')
failCnt += 1
if ( failCnt > 10 ) :
fail2GetData = True;
break;
time.sleep(5)
except urllib.error.URLError as e:
print("URL error")
exit()
else:
failCnt += 1
if ( failCnt > 10 ) :
fail2GetData = True;
break;
time.sleep(5)
break;
if ( fail2GetData ) :
print("Fail to access url")
exit()
일단 이렇게 출력된 결과를 copy하여 엑셀로 복사하면 과거 시세 데이터를 활용한 무언가를 할 수 있는 토대가 만들어졌다. 다음 편에서는 검색한 결과를 엑셀로 저장해 주는 부분까지 추가하여 마무리할 예정이다.
소스코드는 아래 사이트에 올라있으니 참고하시기를
https://github.com/multiwhs/steem-project1/blob/master/PythonUpbitDataGathering.py
@tmkor 님이 Upbit 데이터를 가져올 때 headers 정보를 넣으면 실패하는 경우가 거의 없다고 알려주셨다. 아래 댓글 참고. 그리고 urllib보다 reqeusts가 더 좋다고 알려주셨다. 그래서 requests를 이용한 코드로 변경하였다.
headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'}
coinName = "steem";
url = 'https://crix-api-endpoint.upbit.com/v1/crix/candles/days?code=CRIX.UPBIT.KRW-' + coinName +'&count=10';
try:
res = requests.get(url, headers=headers)
except requests.exceptions.HTTPError as err:
print (err)
exit(1)
data = res.json() # json 구조로 변환
코드도 훨씬 간단해졌고, 시세 정보를 가져오는데 오류가 거의 없어졌다.
소스코드 확인하기
https://github.com/multiwhs/steem-project1/blob/master/PythonUpbitDataGathering-2.py
고마워요 @tmkor님.. 사랑합니다.
p.s node.js 보다는 파이썬이 확실히 편하군요. urllib 등 처음보는 패키지의 경우에는 사용법을 익히는데 시간이 많이 걸리기는 마찬가지지만요. 코딩하는 방식이 C와 유사하고, 함수 명도 짧아서 좋습니다. 형 변환도 자동으로 되어서 편하네요.
upbit 페이지의 javascript를 보면 이미 내부적으로 이미 API를 쓰는것을 확인하실 수 있더군요. (저는 실시간 호가를 얻기 위해 노력했었는데요 코드를 뜯어보니 ajax socket으로 ws프레임 이 실시간 전송 되는 형태라서 selenium등을 써야할 것 같아 일단 보류상태입니다.)
그나저나 "과거 시세 데이터를 활용한 무언가를 할 수 있는 토대가 만들어졌다."에 많은 관심이 가는 1인 입니다. ^_^
좋은 정보 잘 보고 갑니다~
시스템 내부 부하가 걸려있기 때문에 호가는 구하기가 쉽지 않을 것 같습니다. 그 뭔가는 다다음 글에서 공개할 예정입니다.
소스까지 수정해가면서 보는 대단한 고수 ++/
저는 그냥 현재 시세만 열심히 따라가보겠습니다. ++;;;;;
잘봤습니다. 확실히 프로그래밍이 어려운 이유는 예외처리를 어떻게 하느냐에 달려 있는거 같습니다.
ㅇ기계가 무서운 뇨자입니다 ㅠ
열심히 읽으면 뇌가 경련을 일으켜요
제 뇌속으로 회로 연결시켜 주세효~~~^^
개발 글에는 추천!
업비트로 request 보낼 때 헤더를 빼고 보내면 서버에서 종종 튕겨냅니다. 아래는 제가 python에서 사용하는 헤더인데, 이걸로 하면 별 문제가 없었습니다.
headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'}
저의 실력을 너무 높게 평가하시는 듯 합니다. headers를 언제 쓰는지 모르는 1인입니다. 사용 예가 있는지요?
아~ python으로 하기 계시는군요. :)
urllib로 바로 사용하시는 것도 좋지만, requests 라이브러리 사용을 추천드립니다. 더 직관적이기 때문입니다.
pip install requests
import requests headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'} res = requests.get('https://crix-api-endpoint.upbit.com/v1/crix/candles/days?code=CRIX.UPBIT.BTC-SBD&count=3', headers=headers) data = res.json()
위 코드 설명을 드리면,
입니다. ^^
헤더는 서버에 요청할 때 부가적인 정보를 담기 위해 사용합니다. 사용한 헤더는 구글 크롬에서 upbit 페이지를 열 때 서버로 보내는 내용과 동일합니다. 이러한 부가정보가 없다면 사용자가 브라우저에서 보낸 것이 아니라, tradingideas님 처럼 봇이 데이터를 긁어가는거라고 간주할 수 있겠지요. 그래서 헤더가 없으면 서버에서 일정 확률로 데이터를 반환하지 않는 것 입니다.
requests 좋은데요? 그리고 headers 정보 감사합니다. 어쩜 @tmkor님은 모르는 것이 없으세요? 회사에서 사랑받으실 듯 합니다.
좋은글 감사합니당~
점점더 급작스레 어려워지는데,, 그래도 일단 @tradingideas 님의 글을 정독과 정주행 중에 있습니다~^^
감사합니다.
이번 것은 조금 어려웠나요? 좀 더 자세하게 쓸려고 했는데, 글 쓰는데 시간이 너무 많이 걸려서 많은 부분을 생략해서 그런 것 같습니다. 다음 번에는 좀 더 자세하게 쓰겠습니다.
한동안 게으름을 피우느라 파이썬을 멀리했는데
다시 접근을 해봐야 겠네요.
감사합니다.
👍👍👍👍👍
오호...봐도 봐도 익숙해지지 않는 파이썬 코드인 것 같습니다 ㅜㅜ
파이썬 공부 더 해야겠네요 ㅋㅋㅋㅋㅋㅋ
보팅 파워는 없지만.. 고마움을 표현할 방법이 있었네요. ㅎㅎ
@홍보해!
격려해 주셔서 감사합니다.