Navbar
python javascript go

주소

고팍스 웹소켓 API 서버의 주소는 아래와 같습니다.

wss://wsapi.gopax.co.kr

인증

import time, base64, hmac, hashlib, json
from urllib.parse import quote
from websocket import create_connection  # pip install websocket-client

API_KEY = '<입력하세요>'
SECRET = '<입력하세요>'

timestamp = str(int(time.time() * 1000))
msg = 't' + timestamp
key = base64.b64decode(SECRET)
signature = base64.b64encode(
  hmac.new(key, str(msg).encode('utf-8'), hashlib.sha512).digest()
).decode()

url = 'wss://wsapi.gopax.co.kr?apiKey={}&timestamp={}&signature={}'
url = url.format(quote(API_KEY), timestamp, quote(signature))
ws_conn = create_connection(url, timeout=10)
ws_conn.settimeout(None)

request = {
  'i': 1,  # 생략해도 무방
  'n': 'SubscribeToOrderBook',
  'o': {'tradingPairName': 'BCH-KRW'}
}
ws_conn.send(json.dumps(request))

while True:
  raw_response = ws_conn.recv()
  if raw_response.startswith('"primus::ping::'):  # 큰따옴표 유념 요망
    ws_conn.send('"primus::pong::' + raw_response[15:])
  else:
    response = json.loads(raw_response)
    print(json.dumps(response, indent=2))
const crypto = require('crypto');
const WebSocket = require('ws');

const API_KEY = '<입력하세요>';
const SECRET = '<입력하세요>';

const timestamp = new Date().getTime();
const msg = `t${timestamp}`;
const key = Buffer.from(SECRET, 'base64');
const signature = crypto.createHmac('sha512', key).update(msg).digest('base64');

const url = 'wss://wsapi.gopax.co.kr'
  + `?apiKey=${encodeURIComponent(API_KEY)}`
  + `&timestamp=${timestamp}`
  + `&signature=${encodeURIComponent(signature)}`;

const ws = new WebSocket(url, { handshakeTimeout: 10000 });

ws.on('open', () => {
  ws.send(JSON.stringify({
    i: 1, // 생략해도 무방
    n: 'SubscribeToOrderBook',
    o: { tradingPairName: 'BCH-KRW' },
  }));
});

ws.on('message', (rawResponse) => {
  if (rawResponse.startsWith('"primus::ping::')) { // 큰따옴표 유념 요망
    ws.send(`"primus::pong::${rawResponse.slice(15)}`);
  } else {
    const response = JSON.parse(rawResponse);
    console.dir(response, { depth: null });
  }
});

ws.on('close', () => console.log('disconnected'));
ws.on('error', e => console.log('error occurs', e.message));
package main

import (
    "crypto/hmac"
    "crypto/sha512"
    "encoding/base64"
    "encoding/json"
    "log"
    "net/url"
    "strconv"
    "strings"
    "time"

    "github.com/gorilla/websocket"
)

const (
    apiKey = "<입력하세요>"
    secret = "<입력하세요>"
)

func main() {
    timestamp := strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10)
    msg := "t" + timestamp
    key, _ := base64.StdEncoding.DecodeString(secret)
    mac := hmac.New(sha512.New, key)
    mac.Write([]byte(msg))
    signature := base64.StdEncoding.EncodeToString(mac.Sum(nil))

    url := "wss://wsapi.gopax.co.kr" +
        "?apiKey=" + url.QueryEscape(apiKey) +
        "&timestamp=" + timestamp +
        "&signature=" + url.QueryEscape(signature)

    websocket.DefaultDialer.HandshakeTimeout = 10 * time.Second
    ws, resp, err := websocket.DefaultDialer.Dial(url, nil)
    if err != nil {
        log.Fatal(err, " | ", resp)
        panic("cannot connect")
    }
    defer ws.Close()

    jsonBytes, _ := json.Marshal(map[string]interface{}{
        "i": 1, // 생략해도 무방
        "n": "SubscribeToOrderBook",
        "o": map[string]interface{}{
            "tradingPairName": "BCH-KRW",
        },
    })
    ws.WriteMessage(websocket.TextMessage, jsonBytes)

    for {
        _, rawResponseBytes, err := ws.ReadMessage()
        if err != nil {
            panic(err)
        }
        rawResponse := string(rawResponseBytes)
        if strings.HasPrefix(rawResponse, "\"primus::ping::") { // 큰따옴표 유념 요망
            ws.WriteMessage(websocket.TextMessage,
                []byte("\"primus::pong::"+rawResponse[15:]))
        } else {
            response := make(map[string]interface{})
            json.Unmarshal([]byte(rawResponse), &response)
            log.Print(response)
        }
    }
}

고팍스 웹소켓 API을 사용하기 위해서는 고팍스 웹사이트를 통해 발급받은 API Key 및 Secret을 필요로 합니다.

커넥션 연결 및 인증 과정은 다음과 같이 이루어집니다.

  1. 현재 시점의 timestamp를 소수점 없이 밀리세컨드 단위로 구합니다.
  2. 't' + timestamp를 secret으로 서명하여 signature를 생성합니다.
  3. 커넥션을 오픈하면서 apiKey, timestamp, signature를 쿼리스트링으로 전송합니다.
  4. 커넥션이 정상적으로 오픈되면 인증에 성공한 것이며 이제부터 데이터를 구독할 수 있습니다.

서버는 커넥션 활성화 여부를 확인하기 위해 30초에 한 번씩 ping을 보냅니다 (예: "primus::ping::1591318325140"). 사용자는 ping을 받은 후 30초 이내로 pong으로 응답해야 합니다 (예: "primus::pong::1591318325140"). 그렇지 않을 경우 서버는 커넥션이 비활성화되었다고 간주하여 커넥션을 끊습니다.

개인 API

개인 API는 인증 과정이 반드시 필요한 API입니다.

미체결 주문 구독

미체결 주문 목록을 구독합니다.

요청

{ "n": "SubscribeToOrders", "o": {} }

응답

미체결 주문 목록이 응답으로 리턴됩니다.

{ "n": "SubscribeToOrders", "o": { "data": [ { "orderId": 327347, # 오더 ID "status": 1, # 1(미체결), 2(취소됨), 3(전체 체결됨), 4(일부 체결됨), 5(예약됨) "side": 2, # 1(구매), 2(판매) "type": 1, # 1(지정가), 2(시장가) "price": 5500000, # 가격 "orgAmount": 1, # 최초 수량 "remainAmount": 1, # 미체결된 수량 "createdAt": 1597218137, # 주문이 들어간 시간 "updatedAt": 1597218137, # 마지막 업데이트 시간 (아직 변경 없을 경우 주문 시간과 동일) "tradedBaseAmount": 0, # 체결된 Base Asset 수량 (이 예제에서는 ZEC가 단위) "tradedQuoteAmount": 0, # 체결된 Quote Asset 수량 (이 예제에서는 KRW이 단위) "feeAmount": 0, # 거래 수수료 "feeAssetName": "KRW", # 거래 수수료 자산 "rewardAmount": 0, # 거래 리워드 "rewardAssetName": "ZEC", # 거래 리워드 자산 "timeInForce": 0, # 0(gtc), 1(post only), 2(ioc), 3(fok) "protection": 1, # 1(적용하지 않음), 2(적용함) "forcedCompletionReason": 0, # 0(해당 없음), 1(timeInForce), 2(protection) "stopPrice": 0, # 감시 가격 (예약가 주문일 경우만 0이 아닌 값) "takerFeeAmount": 0, # 테이커 포지션으로 지불한 수수료 "tradingPairName": "ZEC-KRW" # 오더북 }, ... ] } }

델타 푸시

구독 이후 발생하는 모든 델타(변경분)는 커넥션을 통해 실시간으로 푸시됩니다.

0.2 ZEC를 5,500,500 KRW/ZEC로 구매하는 주문 발생 시 아래와 같이 주문의 델타가 푸시됩니다.

{ "i": -1, # 델타 푸시일 경우는 항상 -1 "n": "OrderEvent", "o": { "orderId": 327347, "status": 4, # 4(일부 체결됨)로 변경됨 "side": 2, "type": 1, "price": 5500000, "orgAmount": 1, "remainAmount": 0.8, # 미체결된 수량이 0.2 감소 "createdAt": 1597218137, "updatedAt": 1599093631, # 마지막 업데이트 시간이 갱신됨 "tradedBaseAmount": 0.2, # 0.2 ZEC이 나감 "tradedQuoteAmount": 1100000, # 1,100,000 KRW이 들어옴 "feeAmount": 440, # 거래 수수료 (이 예제에서는 0.04% 부과) "feeAssetName": "KRW", # 거래 수수료 자산 "rewardAmount": 0, # 거래 리워드 "rewardAssetName": "ZEC", # 거래 리워드 자산 "timeInForce": 0, "protection": 1, "forcedCompletionReason": 0, "stopPrice": 0, "takerFeeAmount": 0, "tradingPairName": "ZEC-KRW" } }

잔고 구독

잔고 목록을 구독합니다.

요청

{ "n": "SubscribeToBalances", "o": {} }

응답

잔고 목록이 응답으로 리턴됩니다.

{ "n": "SubscribeToBalances", "o": { "result": true, "data": [ ... { "assetId": 7, # 자산 ID "avail": 989.4998, # 주문 가능 보유 수량 "hold": 2, # 오더북에 올라가 있는 수량 "pendingWithdrawal": 0, # 출금 처리 중인 수량 "blendedPrice": 429413.08986192, # 매수 평균가 "lastUpdatedAt": 1599097996.779, # 마지막 업데이트 시간 "isoAlpha3": "ZEC" # 자산 이름 }, ... } }

델타 푸시

구독 이후 발생하는 모든 델타(변경분)는 커넥션을 통해 실시간으로 푸시됩니다.

잔여수량이 1 ZEC인 주문을 취소할 경우 아래와 같이 잔고의 델타가 푸시됩니다.

{ "i": -1, # 델타 푸시일 경우는 항상 -1 "n": "BalanceEvent", "o": { "assetId": 7, "avail": 990.4998, # 주문 가능 보유 수량 +1 "hold": 1, # 오더북에 올라가 있는 수량 -1 "pendingWithdrawal": 0, "blendedPrice": 429413.08986192, "lastUpdatedAt": 1599098077.27, "isoAlpha3": "ZEC" } }

체결 내역 구독

체결 내역을 구독합니다.

요청

{ "n": "SubscribeToTrades", "o": {} }

응답

빈 응답이 리턴됩니다.

{ "n": "SubscribeToTrades", "o": {} }

델타 푸시

구독 이후 발생하는 모든 델타(변경분)는 커넥션을 통해 실시간으로 푸시됩니다.

{ "i": -1, "n": "TradeEvent", "o": { "tradeId": 74072, # 체결 ID "orderId": 453529, # 오더 ID "side": 2, # 1(구매), 2(판매) "type": 1, # 1(지정가), 2(시장가) "baseAmount": 0.01, # 체결된 Base Asset 수량 (이 예제에서는 ZEC가 단위) "quoteAmount": 1, # 체결된 Quote Asset 수량 (이 예제에서는 KRW가 단위) "fee": 0.0004, # 수수료 (리워드일 경우 음수) "feeAssetName": "KRW", # 수수료 자산 (수수료가 음수일 경우 리워드 자산) "price": 100, # 가격 "isSelfTrade": false, # 자전 체결 여부 "occurredAt": 1603932107, # 체결 시간 "tradingPairName": "ZEC-KRW" # 오더북 } }

공개 API

공개 API는 인증 과정 없이 이용할 수 있습니다. 즉, Query String에 인증에 요구되는 내용을 넣지 않고도 호출할 수 있는 API입니다.

오더북 구독

import time, base64, hmac, hashlib, json, traceback
from urllib.parse import quote
from websocket import create_connection  # pip install websocket-client

API_KEY = '<입력하세요>'
SECRET = '<입력하세요>'

timestamp = str(int(time.time() * 1000))
msg = 't' + timestamp
key = base64.b64decode(SECRET)
signature = base64.b64encode(
  hmac.new(key, str(msg).encode('utf-8'), hashlib.sha512).digest()
).decode()

url = 'wss://wsapi.gopax.co.kr?apiKey={}&timestamp={}&signature={}'
url = url.format(quote(API_KEY), timestamp, quote(signature))
ws_conn = create_connection(url, timeout=10)
ws_conn.settimeout(None)


def receive_response(ws_conn):
  resp_str = ws_conn.recv()
  while '::ping::' in resp_str:
    ws_conn.send(resp_str.replace('::ping::', '::pong::'))
    resp_str = ws_conn.recv()
  return json.loads(resp_str)


class OrderBook:
  def __init__(self):
    self.ask_entries = dict()
    self.bid_entries = dict()

  def update(self, response):
    for side, old_entries in [('ask', self.ask_entries), ('bid', self.bid_entries)]:
      new_entries = response['o'][side]
      for new_entry in new_entries:
        old_entry = old_entries.get(new_entry['price'], None)
        is_truly_newer = not old_entry \
                         or new_entry['updatedAt'] > old_entry['updatedAt'] \
                         or (new_entry['updatedAt'] == old_entry['updatedAt']
                             and new_entry['entryId'] > old_entry['entryId'])
        assert is_truly_newer
        if new_entry['volume'] == 0 and old_entry:
          del old_entries[new_entry['price']]
        else:
          old_entries[new_entry['price']] = new_entry

  def print(self, size=6):
    data_to_print = [
      ('ASK', sorted(list(self.ask_entries.keys()))[:size][::-1], self.ask_entries),
      ('BID', sorted(list(self.bid_entries.keys()))[::-1][:size], self.bid_entries),
    ]
    print('\n\n\n\nUpdated at ' + str(int(time.time() * 1000)))
    for label, prices, entries in data_to_print:
      print('=' * 35)
      for price in prices:
        print(label, '\t', price, '\t', format(entries[price]['volume'], '.8f'))
    print('=' * 35)


ws_conn.send(json.dumps({
  'n': 'SubscribeToOrderBook',
  'o': {'tradingPairName': 'BTC-KRW'},
}))
ob = OrderBook()
while True:
  ob.update(receive_response(ws_conn))
  ob.print()

한 커넥션으로 최대 50개의 오더북(호가창)을 구독할 수 있습니다.

요청 파라미터

파라미터 필수 설명
tradingPairName O 거래쌍 (e.g. "BCH-KRW")
limit X 응답으로 오는 오더북 최대 사이즈로 최솟값이 20이며, 생략할 경우 전체 오더북이 리턴됨

요청

{ "n": "SubscribeToOrderBook", "o": { "tradingPairName": "BCH-KRW" # 구독하고자 하는 오더북 } }

응답

오더북이 통째로 응답으로 리턴됩니다.

{ "n": "SubscribeToOrderBook", "o": { "ask": [ # 판매 엔트리 목록 { "entryId": 50, # 업데이트 시퀀스 (+1씩 증가) "price": 5100000, # 가격 "volume": 2.5, # 수량 "updatedAt": 1599089007.614 # 마지막 업데이트 시간 } ], "bid": [ # 구매 엔트리 목록 { "entryId": 48, "price": 5000000, "volume": 5, "updatedAt": 1599088982.102 }, { "entryId": 49, "price": 4500000, "volume": 3, "updatedAt": 1599088992.407 } ], "tradingPairName": "BCH-KRW", "maxEntryId": 50 # 업데이트 시퀀스 최대값 } }

델타 푸시

구독 이후 발생하는 모든 델타(변경분)는 커넥션을 통해 실시간으로 푸시됩니다.

가격 450,000 KRW에 수량 6.7 BCH를 판매하는 주문 발생 시 아래와 같이 오더북의 델타가 푸시됩니다.

{ "i": -1, # 델타 푸시일 경우는 항상 -1 "n": "OrderBookEvent", "o": { "ask": [], "bid": [ { "entryId": 51, "price": 5000000, "volume": 0, "updatedAt": 1599089169.282 }, { "entryId": 52, "price": 4500000, "volume": 1.3, "updatedAt": 1599089169.282 } ], "tradingPairName": "BCH-KRW" } }

거래쌍 구독

오더북을 포함하여 거래쌍 관련하여 제공되는 모든 종류의 데이터를 구독합니다.

제공되는 데이터의 종류는 오더북(OrderBookEvent)과 공개 체결 내역(PublicTradeEvent)입니다.

요청 파라미터

오더북 구독과 동일합니다.

요청

{ "n": "SubscribeToTradingPair", "o": { "tradingPairName": "BCH-KRW" # 구독하고자 하는 오더북 } }

응답

오더북 구독과 동일합니다.

델타 푸시

구독 이후 발생하는 모든 델타(변경분)는 커넥션을 통해 실시간으로 푸시됩니다.

오더북(OrderBookEvent) 양식은 오더북 구독과 동일합니다.

공개 체결 내역(PublicTradeEvent) 양식은 다음과 같습니다.

{ "i": -1, # 델타 푸시일 경우는 항상 -1 "n": "PublicTradeEvent", "o": { "tradeId": 75021, # 체결 내역 ID "baseAmount": 0.01, # 체결된 Base Asset 수량 (이 예제에서는 BCH가 단위) "quoteAmount": 1, # 체결된 Quote Asset 수량 (이 예제에서는 KRW가 단위) "price": 100, # 가격 "isBuy": true, # true(구매), false(판매) "occurredAt": 1609756772, # 체결 시간 "tradingPairName": "BCH-KRW" # 오더북 } }

티커 구독

전체 티커를 구독합니다.

요청

{ "n": "SubscribeToTickers", "o": {} }

응답

전체 티커 목록과 정보가 출력됩니다.

{ 'n': 'SubscribeToTickers', 'o': { 'data': [ { 'baseVolume': 0.02, # 최근 24시간 누적 거래량 (base 자산 단위로 이 예시에서는 BTC) 'high': 37300000, # 최근 24시간 최고 거래 가격 'highestBid': 0, # 오더북 상의 현재 가장 높은 매수 가격 'last': 37300000, # 오더북 상의 현재 가격 'lastTraded': 1665640796413, # 최근 거래 시각(Unix time) 'low': 37300000, # 최근 24시간 최저 거래 가격 'lowestAsk': 0, # 오더북 상의 현재 가장 낮은 매도 가격 'open': 37300000, # 거래시작가, 한국시간(KST) 기준 00시 이후 첫 거래가 체결된 가격 'quoteVolume': 746000, # 최근 24시간 누적 거래량 (quote 자산 단위로 이 예시에서는 KRW) 'tradingPairName': 'BTC-KRW' # 오더북 }, ... { 'baseVolume': 0, 'high': 9000000, 'highestBid': 9000000, 'last': 9000000, 'lastTraded': 1629260634639, 'low': 9000000, 'lowestAsk': 9000000, 'open': 9000000, 'quoteVolume': 0, 'tradingPairName': 'UNC-KRW' } ] } }

델타 푸시

구독 이후 발생하는 변경들은 커넥션을 통해 1초씩 모아서 푸시됩니다.

{ 'i': -1, 'n': 'TickerEvent', 'o': { 'BTC-KRW': { 'baseVolume': 0.02, 'high': 37300000, 'highestBid': 0, 'last': 37300000, 'lastTraded': 1665640796413, 'low': 37300000, 'lowestAsk': 0, 'open': 37300000, 'quoteVolume': 746000, 'tradingPairName': 'BTC-KRW' } ... } }

변경이력

2022-10-25

2022-10-14

2021-01-12

2020-10-29

2020-10-06

2020-09-23