ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 가상화폐 거래소 API를 활용한 봇 만들기 준비 '코인원'
    programing/봇만들기 2017. 11. 9. 15:30

    가상화폐 거래소에서는 대부분 API를 제공하고 있습니다. API는 운영체제와 응용프로그램 사이의 통신에 사용되는 언어나 메시지 형식을 말합니다. 이 방식을 통하면, 원하는 서비스를 만들 수도 있고, 나만의 프로그램을 제할 수도 있습니다.


    이번 시간에는 코인원에서 제공하고 있는 API에 대해서 간략하게 알아보겠습니다. 자동 거래봇을 만드는데 초점을 둬서 포스팅 하도록 하겠습니다.


    참고


      코인원 API Documentation


    제가 주로 사용하는 거래소이기도 하며 국내에서 두 번째로 많은 우량한 거래소입니다. 수수료도 합리적이고, 깔끔한 UI로 구성돼 있어서 홈페이지에서도 쉽게 사용할 수 있습니다. 무엇보다 국내에서 생긴 가상화폐 초창기 거래소이기 때문에 코인원을 이용하는 것 같습니다.


    코인원 API

    http://doc.coinone.co.kr/


    위 링크는 코인원 API 사용방법이 담겨있는 페이지입니다. 나중에 구체적으로 다루겠지만, 저처럼 프로그래밍 지식이 전무하거나 이제 막 프로그래밍을 시작한 사람들은 대충 눈으로 익히고, 저와 함께 천천히 봇을 만들어 보도록 하겠습니다.


      코인원의 Public API호출 방식


    코인원의 Public API호출 방식

    코인원 API documentation에 들어가면 가장 먼저 볼 수 있는 화면 입니다. 영문으로 돼 있어서 알아보기 쉽지 않아서 어려움이 있었습니다. 정작 우리나라 회사인데도 불구하고 한글로 된 설명이 없어서 아쉬운 점이 있습니다.


    위 캡처 화면에 적혀있는 내용은 코인원의 API호출 방식을 말합니다. 간단하게 정리하면 아래와 같습니다.


      공개적인 API 호출 방식


    - 기본 베이스 URL: https://api.coinone.co.kr

    - HTTP Method는 GET 방식으로 호출할 수 있다.

    - 1분에 90번의 호출을 할 수 있다.

    - 요청 횟수가 90번을 초과할 경우 10분 동안 요청할 수 없으며, 아래와 같은 메시지가 출력.

    1
    2
    3
    4
    5
    {
        "result": "error",
        "errorCode": "4",
        "errorMsg": "Blocked user access."
    }
    cs


    갑자기 API호출 방식에서 HTTP는 무엇이며 Method는 뭔지 모르겠고, 이해 안 가는 부분이 많을 겁니다. 작은 것 하나도 놓치지 않고, 열심히 가르쳐 드리겠습니다.


      HTTP Method GET, POST 방식


    HTTP Method에서 이야기하는 GET방식을 간단하게 설명해드리도록 하겠습니다. 보통 서버와 클라이언트가 정보를 주고 받을 때 대표적으로 POST와 GET방식을 사용하게 되는데요. GET방식은 데이터가 암호화되지 않은 채로 서버 측으로 요청이 가기 때문에 보안이 취약하게 됩니다. 


    따라서 노출돼도 괜찮은 정보는 GET방식인 Method를 서버 측에 요청합니다. 반대로 개인정보와 같은 보안이 필요한 데이터는 POST방식으로 서버 측에 요청합니다.


      코인원의 Private API호출 방식


    Private API

    개인정보가 담긴 내용을 서버 측에 호출할 때 POST방식을 이용해서 호출하게 됩니다. Private API를 호출하는 방식은 내용은 아래와 같습니다.


    비공개적인 API 호출 방식

    - 계정(Account)

    - 주문(Order)

    - 거래내역(Transaction)


    Account, Order, Transaction과 같은 개인정보가 담긴 내용이기 때문에 노출되면 안되겠죠. 따라서 POST방식을 이용하게 되고 Private API는 1초에 6번의 호출이 가능하며, Public API처럼 호출 횟수를 초과하게 되면 10분 동안 서버에 호출할 수 없게 됩니다.


    요청 횟수가 6번을 초과할 경우 10분 동안 요청할 수 없으며, 아래와 같은 메시지가 출력 됩니다.

    1
    2
    3
    4
    5
    {
        "result": "error",
        "errorCode": "4",
        "errorMsg": "Blocked user access."
    }
    cs


      헤더에 포함돼야 할 정보


    헤더에 포함돼야 할 정보

    V1 버전은 보안이 상대적으로 취약하며, 지원 중단된 기능들이 많기 때문에 V2 버전을 사용하는 것을 권장하고 있습니다.  지금부터 설명하는 내용은 Python 언어를 기반으로 설명하겠습니다.


    Nonce: replay attack(동시접속 혹은 중복주문 과 같은 공격)을 방지하기 위해서 unix timestamp를 사용합니다. unix timestamp는 밀리세컨드까지 포함해야 합니다. 즉, Timestamp * 1000의 값을 필요로 합니다.


    HTTP Method: 위에서 설명한 POST방식의 메소드가 필요하다는 내용입니다.


    HTTP Content-Type: application/json 형태로 이루어진 헤더 정보가 필요합니다.


    HTTP Header: X-COINONE-PAYLOAD, X-COINONE-SIGNATURE로 이루어진 헤더가 필요합니다.


    Generate X-COINONE-PAYLOAD: PAYLOAD는 BASE64로 인코딩된 정보가 필요합니다. BASE64는 인코딩 방식을 말합니다.


    여기에서도 모르는 단어가 많이 등장합니다. HTTP의 Content-Type이 application/json 형태로 이루어지는 말은 어떤 내용인지 궁금할 것입니다. 이것을 이해하기 위해서는 JSON이 무엇인지 알아야겠죠. 


    JSON이란?

    JSON은 JavaScript Object Notation의 약자로써 속성-값 쌍으로 이루어진 데이터 오브젝트를 전달하기 위해 인간이 읽을 수 있는 텍스트를 사용하는 개방형 표준 포맷입니다. 주로 인터넷에서 자료를 주고 받을 때 그 자료를 표현하는 방법으로 알려져 있는데요. 무엇보다도 JSON을 사용하는 이유는 가벼운 데이터로 빠르게 클라이언트와 서버가 정보를 주고 받을 수 있는 점입니다. 조금 더 구체적으로 들어가면 JSON(JavaScript Object Notation)의 공식 인터넷 미디어 타입은 application/json 으로 사용되며 확장자는 .json으로 사용됩니다.

    위키백과 참조


      1. 매개 변수 객체에서 JSON 문자열을 생성합니다. (넌스 값 포함)


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import simplejson as json
    import time
     
    param = {
      'access_token': ACCESS_TOKEN,
      'price'500000,
      'qty'1.1234
      'nonce'int(time.time()*1000)
    }
     
    #to json
    json_param = json.dumps(param)
    cs
    Nonce 값을 파라미터에 같이 추가해줘야 합니다. 코드는 위와 같이 입력합니다. 파라미터(param)에 Nonce값과 access_token과 함께 dictionary 데이터로 만들어 줍니다. 그 다음 json.dumps(param)으로 가공합니다. 

    *Nonce는 현재 시간
    *json.dumps(param)는 JSON object 문자열로 인코딩하라는 내용입니다.

      2. X-COINONE-PAYLOAD


    1
    2
    3
    4
    import base64
     
    #X-COINONE-PAYLOAD
    payload = base64.b64encode(json_param)
    cs

    앞서 표에서 얘기한 X-COINONE-PAYLOAD는 base64로 인코딩하기 위해서는 위와 같은 내용으로 입력합니다. 다. 매개 변수 객체에서 JSON 문자열로 생성한 json_param을 base64방식으로 인코딩하는 방법입니다. 위와 같이 인코딩하는 이유는 암호화를 해서 보안성을 강화하기 위한 과정입니다.


      3. X-COINONE-SIGNATURE


    1
    2
    3
    4
    5
    import hashlib
    import hmac
     
    #X-COINONE-SIGNATURE
    signature = hmac.new(str(SECRET_KEY).upper(), str(PAYLOAD), hashlib.sha512).hexdigest();
    cs

     X-COINONE-SIGNATURE는 X-COINONE-PAYLOAD로 만들어진 객체를 다시 한번 암호화 하게 됩니다. HMAC모듈을 이용해서 sha512 암호화방식으로 해싱하게 됩니다.


    참고


    HTTP Request

    1
    2
    3
    4
    5
    6
    7
    response = client.post(URL,
        format='json',
        data=X-COINONE-PAYLOAD,
        **{
          'X-COINONE-PAYLOAD': X-COINONE-PAYLOAD,
          'X-COINONE-SIGNATURE': get_signature(X-COINONE-PAYLOAD, SECRET_KEY)
        })
    cs

    이제 최종적으로 서버측에 HTTP Request 요청하는 일만 남았습니다.


    1. 매개 변수 객체에서 JSON 문자열을 생성합니다.

    2. X-COINONE-PAYLOAD

    3. X-COINONE-SIGNATURE


    위와 같이 데이터를 가공한 다음

    post방식으로 헤더정보와 함께  위와 같은 코드를 전송하게 되면, 성공적으로 서버측에 요청을 할 수 있습니다. 위와 내용을 모두 정리해서 코드로 작성하면 아래와 같습니다.


      전체 정리코드


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    import simplejson as json
    import time
    import base64
    import hashlib
    import hmac
     
    ACCESS_TOKEN = '액세스 토큰 입력'
    SECRET_KEY = '시크릿 키 입력'
     
    def get_encoded_payload(payload):
      #add nonce
      payload[u'nonce'= int(time.time()*1000)
     
      dumped_json = json.dumps(payload)
      encoded_json = base64.b64encode(dumped_json)
      return encoded_json
     
    def get_signature(encoded_payload, secret_key):
      signature = hmac.new(str(secret_key).upper(), str(encoded_payload), hashlib.sha512);
      return signature.hexdigest()
     
    def get_response(url, payload):
      encoded_payload = get_encoded_payload(payload)
      response = client.post(APIURL + url,
        format='json',
        data=encoded_payload,
        **{
          'X-COINONE-PAYLOAD': encoded_payload,
          'X-COINONE-SIGNATURE': get_signature(encoded_payload, SECRET_KEY)
        })
      return response
     
    def get_balance():
      url = '/balance'
      payload = {
        'access_token': ACCESS_TOKEN,
      }
     
      response = get_response(url, payload)
      content = json.loads(response.content)
     
      return content
     
    if __name__   == "__main__":
        print get_balance()
    cs


    get_balance() 함수에서 샘플로 밸런스(잔액정보)를 조회하는 코드를 입력한 것입니다. 밸런스가 아닌 매수주문, 자신의 계좌정보 조회와 같은 내용은 url 부분과 payload 부분의 값을 바꿔주면 응용이 가능합니다.


    coinone의 경우 이렇게 API를 어렵게 해 놓아서 이해하기가 쉽지는 않을 것입니다. 그러나 그만큼 보안에 신경을 쓰고 있다는 신뢰는 얻을 수 있겠죠. 


    최대한 쉽게 풀어서 설명한다고 했는데 쉽지 않은 것 같습니다. 본문에 있는 내용이 보다 더 쉽게 이해할 수 있도록 관련 용어에 대한 내용은 링크를 참조하시기 바랍니다.


    잘못된 내용이나 추가로 궁금한 점이 있다면, 댓글로 남겨주세요. 블로그 주제로 적합하다고 판단되면, 포스팅과 제가 아는 범위 내에서 최대한 설명해드리겠습니다.


    ▶참조: 코인원 API Documentation


Designed by Tistory.