주소
고팍스 웹소켓 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={}×tamp={}&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)}`
+ `×tamp=${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) +
"×tamp=" + 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을 필요로 합니다.
커넥션 연결 및 인증 과정은 다음과 같이 이루어집니다.
- 현재 시점의 timestamp를 소수점 없이 밀리세컨드 단위로 구합니다.
- 't' + timestamp를 secret으로 서명하여 signature를 생성합니다.
- 커넥션을 오픈하면서 apiKey, timestamp, signature를 쿼리스트링으로 전송합니다.
- 커넥션이 정상적으로 오픈되면 인증에 성공한 것이며 이제부터 데이터를 구독할 수 있습니다.
서버는 커넥션 활성화 여부를 확인하기 위해 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={}×tamp={}&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
- (기능 추가) 공개 API
- (기능 추가) 데이터 구독 - 티커 구독
- (기능 변경) 데이터 구독 - 공개 API로 변경 (오더북 구독, 거래쌍 구독)
2022-10-14
- (기능 변경) 주문 구독 - 수수료/리워드 자산 이름 추가
- (기능 변경) 체결 내역 구독 - 수수료 자산 이름 추가
2021-01-12
- (기능 추가) 데이터 구독 - 거래쌍 구독 (오더북 & 공개 체결 내역)
2020-10-29
- (기능 추가) 데이터 구독 - 체결 내역
2020-10-06
- 베타 서비스 종료 및 정식 출시
- (기능 개선) SubscribeToOrderBook에 limit 파라미터 추가
2020-09-23
- (기능 추가) 인증 및 주요 데이터 구독 - 오더북/미체결 주문/잔고