파이썬으로 업비트(Upbit) 과거 시세 가져오기(1)

in #kr6 years ago (edited)

Upbit에서 암호화폐 과거 시세를 가져와서 몇 가지 전략을 검증할 계획을 세웠다. Upbit는 현재 공식적으로 API를 지원하지 않는다.

Steemit에서 찾아보니 업비트에서 과거 시세를 가져올 수 있는 방법이 있었다.


위의 글을 참고로 하여 과거 steem의 시세를 받아올 수 있는 프로그램을 파이썬으로 만들어보았다. 굳이 업비트 자료가 필요한 것은 steem을 거래하고 싶었기 때문이다.

결론적으로 업비트에서 스팀달러 일간 과거 가격을 볼 수 있는 html 주소는 아래와 같다. 마지막에 있는 count를 늘리면 원하는 기간만큼의 데이터를 가지고 올 수 있다.
https://crix-api-endpoint.upbit.com/v1/crix/candles/days?code=CRIX.UPBIT.BTC-SBD&count=3

그 결과는 json 형태로 넘어온다.
image.png

이 데이터를 잘 분석하여 우리가 필요로 하는 아래 형태의 데이터를 뽑아주는 파이썬 프로그램을 짜 보는 것이 오늘의 목표이다.
image.png

이 데이터는 아래와 같은 keyword를 이용하면 찾을 수 있다
image.png


위와 같은 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와 유사하고, 함수 명도 짧아서 좋습니다. 형 변환도 자동으로 되어서 편하네요.

Sort:  

Chicken chow mein is my fav. Yours?

점점더 급작스레 어려워지는데,, 그래도 일단 @tradingideas 님의 글을 정독과 정주행 중에 있습니다~^^

감사합니다.

이번 것은 조금 어려웠나요? 좀 더 자세하게 쓸려고 했는데, 글 쓰는데 시간이 너무 많이 걸려서 많은 부분을 생략해서 그런 것 같습니다. 다음 번에는 좀 더 자세하게 쓰겠습니다.

소스까지 수정해가면서 보는 대단한 고수 ++/
저는 그냥 현재 시세만 열심히 따라가보겠습니다. +
+;;;;;

잘봤습니다. 확실히 프로그래밍이 어려운 이유는 예외처리를 어떻게 하느냐에 달려 있는거 같습니다.

ㅇ기계가 무서운 뇨자입니다 ㅠ
열심히 읽으면 뇌가 경련을 일으켜요
제 뇌속으로 회로 연결시켜 주세효~~~^^

개발 글에는 추천!
업비트로 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()

위 코드 설명을 드리면,

  • 1라인 : 모듈 import
  • 2라인 : 헤더 설정
  • 3라인 : requests 모듈의 get 함수 사용. get함수는 get방식으로 서버에 데이터를 요구하는 것임. 첫번째 인자로 URL을 설정, headers 인자로 2라인에 설정한 header 변수 사용
  • 4라인 : 서버의 반환 데이터를 json으로 변환하여 data 변수에 적재
    입니다. ^^

헤더는 서버에 요청할 때 부가적인 정보를 담기 위해 사용합니다. 사용한 헤더는 구글 크롬에서 upbit 페이지를 열 때 서버로 보내는 내용과 동일합니다. 이러한 부가정보가 없다면 사용자가 브라우저에서 보낸 것이 아니라, tradingideas님 처럼 봇이 데이터를 긁어가는거라고 간주할 수 있겠지요. 그래서 헤더가 없으면 서버에서 일정 확률로 데이터를 반환하지 않는 것 입니다.

requests 좋은데요? 그리고 headers 정보 감사합니다. 어쩜 @tmkor님은 모르는 것이 없으세요? 회사에서 사랑받으실 듯 합니다.

upbit 페이지의 javascript를 보면 이미 내부적으로 이미 API를 쓰는것을 확인하실 수 있더군요. (저는 실시간 호가를 얻기 위해 노력했었는데요 코드를 뜯어보니 ajax socket으로 ws프레임 이 실시간 전송 되는 형태라서 selenium등을 써야할 것 같아 일단 보류상태입니다.)

그나저나 "과거 시세 데이터를 활용한 무언가를 할 수 있는 토대가 만들어졌다."에 많은 관심이 가는 1인 입니다. ^_^

좋은 정보 잘 보고 갑니다~

시스템 내부 부하가 걸려있기 때문에 호가는 구하기가 쉽지 않을 것 같습니다. 그 뭔가는 다다음 글에서 공개할 예정입니다.

한동안 게으름을 피우느라 파이썬을 멀리했는데
다시 접근을 해봐야 겠네요.
감사합니다.

👍👍👍👍👍

오호...봐도 봐도 익숙해지지 않는 파이썬 코드인 것 같습니다 ㅜㅜ

파이썬 공부 더 해야겠네요 ㅋㅋㅋㅋㅋㅋ

Coin Marketplace

STEEM 0.19
TRX 0.14
JST 0.030
BTC 59908.77
ETH 3191.82
USDT 1.00
SBD 2.43