Coinone API Go언어 구현 - 작성중 :: The Obligatory Courtesy Smile

Coinone API Go언어 구현 - 작성중

Posted by 곰도리네
2017.11.27 16:00 Computing&IT/가상화폐

Coinone API written in Go
Document written in November, 27, 2017 - Still not made up.

코인원 coinone API 를 go언어로 구현한 내용을 공유합니다.

버전 0.1.9를 기준으로 작성하였습니다.
API 원본은 여기에서 확인 가능합니다.
테스팅 환경은 아래와 같습니다.
V2 API만을 구현하였습니다. 
현재 작성중입니다. 
함수 별 중복되는 부분은 앞 부분에서 설명하니 앞에서부터 읽어보시기를 권합니다.
Go언어에서 coinone의 페이로드 작성, HTTP 메소드 날리기, JSON 마샬링 등 주요 내용은 아래 내용을 Copy & Paste 하시면 급한대로 구현 가능하실겁니다. 
코드가 최적화되지 않고 급하게 짠것이라 극히 조잡하니 주의해주세요.



Operating System : Windows 10 Professional
Go Compiler: version 1.9 from golang.org
Editor : emacs for Windows 64bit



Coinone API V2 전체 설명

- V2 API는 ACCESS TOKEN과 SECRET KEY를 요구합니다. 각 키는 coinone의 API 페이지에서 획득가능합니다. ETH와 ETC는 API V2에서만 트레이딩 가능하므로 V2만 우선적으로 작성하였습니다.

- API 뒤에 V2가 붙지 않는 것은 V1과 공용으로 사용되는 API입니다. 이 경우 ACCESS TOKEN이나 SECRET KEY에 대한 구현이 필요하지 않습니다.



 ACCESS TOKEN과 SECRET KEY는 var로 먼저 지정해두면 편합니다.

var (
        ACCESSTOKEN = "액세스토큰"
        SECRETKEY = "시크릿키"
)



- Nonce 값을 구하는 함수를 작성하셔야 합니다. Nonce에 대한 설명은 Account V2에서 언급합니다. 간단하게 구현한 코드는 아래와 같습니다. unix timestamp를 얻어와도 되지만 초기값을 1로 설정해서 계속 1씩 더해주어도 됩니다. 다만, 테스팅 할때는 unix timestamp를 얻어오기를 권합니다.



var NONCE = 1

func GetNonce() int {
        NONCE = NONCE + 1
        return NONCE
}



Account V2
1. Account V2 설명
 - 위 Coinone API V2 전체설명의 내용이 적용됩니다. 이에 덧붙여서 Nonce 값이 필요합니다.
 - Nonce는 유닉스 타임스탬프(Unix Timestamp)와 같이 양의 정수로 표시되는 값입니다.(positive integer) Nonce는 replay attack을 방지하기 위한 수단으로, API 사용자는 매 요청(request)시마다 nonce값을 제시해야 합니다. 
 - Go언어에서 HTTP POST 메소드를 사용합니다. go언어에서 HTTP POST 파라메터를 HTTP Method, HTTP Content-Type, HTTP Header를 아래와 같이 채워주어야 합니다. 

HTTP Method
POST
HTTP Content-Type
application/json
HTTP Header
X-COINONE-PAYLOAD
X-COINONE-SIGNATURE


- X-COINONE-PAYLOAD를 작성하는 알고리즘은 아래와 같습니다.

PAYLOAD = BASE64(JSON(body))

JSON 스트링을 nonce값을 포함한 parameter object로부터 생성하면 됩니다.

Go언어로 작성하면 아래의 예와 같습니다. 아래는 Account V2의 Balance API의 경우 PAYLOAD를 작성하는 예제입니다. 각각의 API별로 아래와 같이 리퀘스트 하면 됩니다.

func GetPayload(data BalanceParameter) string {
        jsonString, _ := json.Marshal(data)
        return base64.StdEncoding.EncodeToString(jsonString) 
}  

위 코드에서 BalanceParameter는 아래와 같습니다. 각각의 Request, 그리고 이에 대한 코인원 서버 측 결과들의 파라메터를 json 돌리기 위해서 struct로 작성해야 합니다. 

type BalanceParameter struct {
        AccessToken string `json:"access_token"`
        Nonce       int    `json:"nonce"`
}

위의 두 코드 조각을 통해서 Balance를 얻어오는 HTTP Parameter를 작성할 수 있습니다.





- X-COINONE-SIGNATURE를 작성하는 알고리즘은 아래와 같습니다. 주의하실 점은 SECRET KEY값은 반드시 upper case 즉 대문자여야 합니다.
SIGNATURE = HMAC-SHA512(payload, secret_key.upper()).TO_HEX_DIGEST()

SIGNATURE = HMAC-SHA512(payload, secret_key.upper()).TO_HEX_DIGEST()

 이를 go언어로 구현하면 아래와 같습니다.

func GetSignature(payload string) string {
        secretkey := strings.ToUpper(SECRETKEY)
        bSecretkey := []byte(secretkey)  
        bPayload := []byte(payload) 
        hmac512 := hmac.New(sha512.New, bSecretkey)
        hmac512.Write(bPayload)
        return hex.EncodeToString(hmac512.Sum(nil))
}

 
2. 구현
 (1) Balance

func GetBalance() {
        n := GetNonce() 
        form := url.Values{}
       form.Add("access_token", ACCESSTOKEN)
        form.Add("nonce", strconv.Itoa(n))
        param := new(BalanceParameter)
        param.AccessToken = ACCESSTOKEN
        param.Nonce = n
        pl := GetPayload(*param)
        req, _ := http.NewRequest("POST",APIURL+"v2/account/balance/",bytes.NewBufferString(form.Encode()))
        req.Header.Set("X-COINONE-PAYLOAD", pl)
        req.Header.Set("X-COINONE-SIGNATURE", GetSignature(pl))
        req.Header.Set("content-Type", "application/json")
        req.Header.Set("accept", "application/json")
        client := http.Client{}
        resp, err := client.Do(req)
        if err != nil {
                panic(err) }
        defer resp.Body.Close()
        respBody, err := ioutil.ReadAll(resp.Body)
        if err == nil {
                str := string(respBody)
                println(str)
        } 
}

APIURL은 코인원의 API 서버 도메인입니다. 

현재 위 함수 본체는 json 역마샬링을 적용하지 않았습니다. json unmarshaling 하실때는 아래와 같이 Success 코드를 구현한 구조체에 연결하시면 됩니다.

type BalanceSuccess struct {
    Result    string
    ErrorCode string
    Btc  BTC
    Bch  BCH
    Eth  ETH
    Etc  ETC
    Xrp  XRP
    Qtum QTUM
    Krw  KRW
}

위의 통화 단위는 아래와 같이 구조체로 구현합니다.

type BTC struct { avail   float64 balance float64}
type BCH struct { avail   float64 balance float64}
type ETH struct { avail   float64 balance float64}
type ETC struct { avail   float64 balance float64}
type XRP struct { avail   float64 balance float64}
type QTUM struct { avail   float64 // double 64bit = bbyte balance float64}
type KRW struct { avail   int32 // long 32bit. balance int32}


 (2) Daily Balance

 // 작성중

 (3) Deposit Address

 // 작성중

 (4) User Information

 // 작성중


 (5) Virtual Account

 // 작성중

Error Code
작성중

OAuth

 // 작성중

Order V2
1. Order V2 설명


Public
1. Public 설명
개별 사용자 전용이 아닌 공용 사용 API입니다. 공용정보(Public Information)를 얻어오는 API입니다. HTTP 메소드로 GET을 사용합니다. 
분당 90회의 요청 처리가 가능합니다. (만약 초과하는 경우 10분간 블락당하므로 주의하세요.)

 (1) Orderbook

현재 주문내역을 가져옵니다.

GetPublicOrderbook 함수인자인 currency에는 아래의 값들이 들어갈 수 있습니다.

btc bch eth etc xrp qtum

함수 본체는 아래와 같습니다. Public API의 경우 GET 메소드를 사용함에 유의하세요.

func GetPublicOrderbook(currency string) {
     req, err := http.NewRequest("GET", APIURL+"orderbook/", nil)
     if err != nil {
          panic(err)
     }
     req.Header.Add("currency", currency)
     client := &http.Client{}
     resp, err := client.Do(req)
     if err != nil {
          panic(err)
     }
     defer resp.Body.Close()
     bytes, _ := ioutil.ReadAll(resp.Body)
     str := string(bytes) 
}

 (2) Recent Complete Orders

최근 완료된 주문 내역을 가져옵니다.

func GetRecentCompleteOrders(currency string, period string) {
    req, err := http.NewRequest("GET", APIURL+"trades/", nil)
    if err != nil {
        panic(err)
     }
     req.Header.Add("currency", currency)
     req.Header.Add("period", period)
     client := &http.Client{}
     resp, err := client.Do(req)
     if err != nil {
         panic(err)
     }
     defer resp.Body.Close()
     bytes, _ := ioutil.ReadAll(resp.Body)
     str := string(bytes) 
}

 (3) Ticker

func GetTicker(currency string) {
   req, err := http.NewRequest("GET", APIURL+"ticker/", nil)
   if err != nil {
        panic(err)
    }
    req.Header.Add("currency", currency)
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
      panic(err)
    }
    defer resp.Body.Close()
    bytes, _ := ioutil.ReadAll(resp.Body)
    str := string(bytes) 
}


이 댓글을 비밀 댓글로
    • alan
    • 2018.03.12 19:58 신고
    작성 계속 하는거죠? ㅠㅜ
    • 호응이 너무없었는데 찾아오시는 분이 계시긴 했었군요 ㅎㅎ To be continued..
    • ㅇㅇ
    • 2018.03.25 16:40 신고
    마진거래 하다가 봇을 이용한다는 분이 있어 검색하다 들렀네요
    음 계속 만드시는건가요? 혹시 비트맥스 봇 만드실 생각은 있으신지..
    • 지금 차익거래 엔진 제작중이예요... 아마 상용으로 판매할수 있을수도 있을 것 같네요.. 방침은 정해진건 없지만..
      비트맥스는 어떤 서비스인지 제가 리서치가 좀 필요할 것 같네요.
    • ff
    • 2018.08.03 21:22
    관리자의 승인을 기다리고 있는 댓글입니다

티스토리 툴바