exchanges
Version:
bitcoin exchanges
233 lines (227 loc) • 6.61 kB
JavaScript
// const Utils = require('./utils');
const deepmerge = require('deepmerge');
const crypto = require('crypto');
const md5 = require('md5');
const _ = require('lodash');
const error = require('./errors');
const Base = require('./../base');
const kUtils = require('./utils');
const Utils = require('./../../utils');
const request = require('./../../utils/request');
const { exchangePairs } = require('./../data');
const { USER_AGENT, WS_BASE } = require('./config');
//
const { checkKey } = Utils;
//
const ALL_PAIRS = exchangePairs.okex;
function mergeArray(data, d) {
return data.concat(data, d);
}
function merge(data, d) {
return { ...data, ...d };
}
const URL = 'https://www.okex.com/api';
class Exchange extends Base {
constructor(o, options) {
super(o, options);
this.url = URL;
this.version = 'v1';
this.name = 'okex';
}
getSignature(params) {
const qstr = `${Utils.getQueryString({ ...params, api_key: this.apiKey })}&secret_key=${this.apiSecret}`;
return md5(qstr).toUpperCase();
}
async tick(o = {}) {
checkKey(o, 'pair');
const ds = await this.get('ticker', o);
return kUtils.formatTick(ds, o.pair);
}
async kline(o = {}) {
checkKey(o, 'pair');
const defaultKlineO = {
interval: '1m',
size: 2000
};
o = { ...defaultKlineO, ...o };
const opt = kUtils.formatKlineO(o);
const ds = await this.get('kline', opt);
return kUtils.formatKline(ds, o);
}
async ticks(o = {}) {
const ds = await this.tick(o);
return ds;
}
async depth(o = {}) {
const defaultO = {
size: 50
};
const opt = { ...defaultO, ...o };
const ds = await this.get('depth', opt, false);
return kUtils.formatDepth(ds);
}
// 交易状态
async orderInfo(o = {}) {
checkKey(o, ['order_id', 'pair']);
let { order_id, pair } = o;
if (Array.isArray(order_id)) order_id = order_id.join(',');
let ds = await this.post('orders_info', { order_id, pair }, true, true);
ds = kUtils.formatOrderInfo(ds, o);
return ds;
}
async activeOrders(o = {}) {
checkKey(o, ['pair']);
const ds = await this.allOrders({ ...o, status: 'UNFINISH' });
return ds;
}
async finishOrders(o = {}) {
checkKey(o, ['pair']);
const ds = await this.allOrders({ ...o, status: 'SUCCESS' });
return ds;
}
async allOrders(o = {}) { // 近2天来的order
const defaultO = {
page_length: 2000,
current_page: 0,
status: 'UNFINISH'
};
checkKey(o, ['pair']);
o = { ...defaultO, ...o };
const opt = kUtils.formatAllOrdersO(o);
const ds = await this.post('order_history', opt, true);
return kUtils.formatAllOrders(ds);
}
async balances(o = {}) {
let ds = await this.post('userinfo', o, true);
ds = kUtils.formatBalances(ds);
return ds;
}
async allBalances() {
const tasks = [this.balances(), this.futureBalances()];
let [balances, futureBalances] = Promise.all(tasks);
balances = _.keyBy(balances, 'coin');
futureBalances = _.keyBy(futureBalances, 'coin');
}
// 交易
async order(o = {}) {
o = kUtils.formatOrderO(o);
let ds = await this.post('trade', o, true);
ds = kUtils.formatOrderResult(ds);
return ds;
}
async cancelAllOrders(o = {}) {
checkKey(o, ['pair']);
const actives = await this.activeOrders({ pair: o.pair });
const order_ids = _.map(actives, (d) => {
return d.order_id;
});
const ds = await this.cancelOrder({
pair: o.pair,
order_id: order_ids
});
return ds;
}
async cancelOrder(o = {}) {
checkKey(o, ['order_id', 'pair']);
const { order_id } = o;
if (Array.isArray(order_id) && order_id.length === 0) {
return {
success: [],
error: []
};
}
const opt = kUtils.formatCancelOrderO(o);
const ds = await this.post('cancel_order', opt, true);
return kUtils.formatCancelOrder(ds);
}
async orderBook(o = {}) {
const ds = await this.get('trades', o, true, true);
return kUtils.formatOrderBook(ds);
}
async request(method = 'GET', endpoint, params = {}, isSign = false) {
params = Utils.replace(params, { pair: 'symbol' });
if (params.symbol) params.symbol = kUtils.formatPair(params.symbol);
const signedParams = {
...params,
...(isSign ? {
sign: this.getSignature(params),
api_key: this.apiKey
} : {})
};
const qstr = Utils.getQueryString(signedParams);
let url = `${URL}/${this.version}/${endpoint}.do`;
if (method === 'GET') url += `?${qstr}`;
const cType = 'application/x-www-form-urlencoded';
// console.log(signedParams, method, 'signedParams...');
const o = {
uri: url,
proxy: this.proxy,
method,
headers: {
'Content-Type': cType,
'User-Agent': USER_AGENT,
},
form: signedParams
};
let body;
try {
// console.log(o, '===');
body = await request(o);
// console.log(body);
} catch (e) {
if (e) console.log(e.message);
return;
}
// console.log(body, 'body...');
if (!body) {
throw `${endpoint}: body 返回为空`;
}
if (body.code === 500) {
throw `${endpoint}: 服务拒绝...`;
}
if (body.code === -1) {
throw `${endpoint}: ${body.msg}`;
}
if (body.error_code) {
throw error.getErrorFromCode(body.error_code);
}
// console.log(body, 'body');
return body.data || body;
}
_getPairs(filter) {
let pairs = ALL_PAIRS;
if (filter) pairs = _.filter(pairs, filter);
return _.map(pairs, d => d.pair);
}
createWs(o = {}) {
const { timeInterval, chanelString, options: opt } = o;
return (formatData, cb) => {
let data = {};
const cbf = _.throttle(() => {
const res = _.values(data);
if (res.length) {
cb(res);
data = {};
}
}, timeInterval);
//
const options = { proxy: this.proxy, willLink: ws => ws.send(chanelString) };
kUtils.subscribe('', (ds) => {
data = merge(data, formatData(ds, opt));
cbf();
}, options);
};
}
// ws接口
async wsTicks(o, cb) {
const pairs = this._getPairs(o.filter);
const chanelString = kUtils.createSpotChanelTick(pairs);
this.createWs({ chanelString }, 'pair')(kUtils.formatWsTick, cb);
}
async wsBalance(o, cb) {
const pairs = this._getPairs(o.filter);
const chanelString = kUtils.createSpotChanelBalance(pairs);
this.createWs({ chanelString })(kUtils.formatWsBalance, cb);
}
}
module.exports = Exchange;