node-fcoin
Version:
node-fcoin is a full implementation for FCoin cryptocurrency exchange REST and WebSocket trading API.
223 lines (192 loc) • 5.67 kB
JavaScript
const fetch = require('node-fetch');
const crypto = require('crypto');
const querystring = require('querystring');
const WebSocketClient = require('websocket').client;
const EventEmitter = require('events');
const API_BASE_URL = 'https://api.fcoin.com/v2';
const WS_URL = 'wss://api.fcoin.com/v2/ws';
class FCoin extends EventEmitter {
constructor(config) {
super();
this.config = config || {};
}
getServerTime() {
return this.call('GET', `${API_BASE_URL}/public/server-time`);
}
getCurrencies() {
return this.call('GET', `${API_BASE_URL}/public/currencies`);
}
getSymbols() {
return this.call('GET', `${API_BASE_URL}/public/symbols`);
}
getTicker(symbol) {
return this.call('GET', `${API_BASE_URL}/market/ticker/${symbol}`);
}
getDepth(symbol, level = 'L20') {
return this.call('GET', `${API_BASE_URL}/market/depth/${level}/${symbol}`);
}
getTrades(symbol, before, limit = 20) {
return this.call('GET', `${API_BASE_URL}/market/trades/${symbol}`, {before, limit});
}
getCandles(symbol, resolution, before, limit = 20) {
return this.call('GET', `${API_BASE_URL}/market/candles//${resolution}/${symbol}`, {before, limit});
}
getBalance() {
return this.call('GET', `${API_BASE_URL}/accounts/balance`);
}
createOrder(symbol, side, type, price, amount) {
return this.call('POST', `${API_BASE_URL}/orders`, {amount, price, side, symbol, type});
}
getOrders(symbol, state, before, after, limit = 20) {
return this.call('GET', `${API_BASE_URL}/orders`, {after, before, limit, state, symbol});
}
getOrder(id) {
return this.call('GET', `${API_BASE_URL}/orders/${id}`);
}
cancelOrder(id) {
return this.call('POST', `${API_BASE_URL}/orders/${id}/submit-cancel`);
}
getOrderMatchResults(id) {
return this.call('GET', `${API_BASE_URL}/orders/${id}/match-results`);
}
connectWebSocket() {
this.ws = new WebSocketClient();
this.wsTopics = {};
this.wsConn = null;
this.ws.on('connectFailed', () => {
console.log('connectFailed')
this.wsconn = null;
setTimeout(() => this.ws.connect(WS_URL), 10000);
});
this.ws.on('connect', conn => {
this.wsConn = conn;
for (let topic in this.wsTopics) {
this.subscribe(topic);
}
conn.on('message', message => {
let data = JSON.parse(message.utf8Data);
let type = data.type;
if (/^ticker\./.test(type)) {
console.log(data)
let symbol = type.split('.')[1];
this.emit('ticker', {
symbol,
ticker: data.ticker
});
} else if (/^depth\./.test(type)) {
console.log(data)
let symbol = type.split('.')[2];
this.emit('depth', {
symbol,
depth: {
asks: data.asks,
bids: data.bids
}
});
} else if (/^trade\./.test(type)) {
console.log(data)
let symbol = type.split('.')[1];
this.emit('trade', {
symbol,
ts: data.ts,
trade: {
id: data.id,
side: data.side,
price: data.price,
amount: data.amount
}
});
}
});
conn.on('close', () => {
console.log('close')
this.wsConn = null;
if (this.ws) {
setTimeout(() => this.ws.connect(WS_URL), 10000);
}
});
conn.on('error', () => {
});
});
this.ws.connect(WS_URL);
}
disconnectWebSocket() {
this.ws = null;
this.wsConn.close();
}
subscribeTicker(symbol) {
this.subscribe(`ticker.${symbol}`);
}
subscribeDepth(symbol, level = 'L20') {
this.subscribe(`depth.${level}.${symbol}`);
}
subscribeTrade(symbol) {
this.subscribe(`trade.${symbol}`);
}
unsubscribeTicker(symbol) {
this.unsubscribe(`ticker.${symbol}`);
}
unsubscribeDepth(symbol, level = 'L20') {
this.unsubscribe(`depth.${level}.${symbol}`);
}
unsubscribeTrade(symbol) {
this.unsubscribe(`trade.${symbol}`);
}
call(method, url, qs) {
qs = qs || {};
let time = Date.now();
let signature = undefined;
if (this.config.key && this.config.secret)
signature = this.sign(method, url, time, qs)
return new Promise((resolve, reject) => {
fetch(`${url}?${querystring.stringify(qs)}`, {
method: method,
headers: {
'FC-ACCESS-KEY': this.config.key,
'FC-ACCESS-SIGNATURE': signature,
'FC-ACCESS-TIMESTAMP': time,
'Content-Type': 'application/json;charset=UTF-8'
}
})
.then(res => {
return res.json();
}).then(res => {
if (res.status == 0) {
resolve(res.data);
} else {
reject(new Error(`FCoin error ${res.status} ${res.msg}`));
}
})
.catch(err => reject(err));
});
}
sign(method, url, time, qs) {
let data = `${method}${url}${time}${querystring.stringify(qs)}`
data = Buffer.from(data).toString('base64');
let signature = crypto
.createHmac('sha1', this.config.secret)
.update(data)
.digest()
.toString('base64');
return signature;
}
subscribe(topic) {
this.wsTopics[topic] = new Date();
if (this.wsConn) {
this.wsConn.send(JSON.stringify({
cmd: 'sub',
args: [topic]
}));
}
}
unsubscribe(topic) {
delete this.wsTopics[topic];
if (this.wsConn) {
this.wsConn.send(JSON.stringify({
cmd: 'unsub',
args: [topic]
}));
}
}
}
module.exports = FCoin;