UNPKG

huobi-api

Version:
191 lines (165 loc) 4.49 kB
import axios from 'axios'; import * as moment from 'moment'; import * as CryptoJS from 'crypto-js'; import * as HmacSHA256 from 'crypto-js/hmac-sha256'; const DEFAULT_HEADERS = { 'Content-Type': 'application/json;charset=utf-8', 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36' }; enum STATUS { 'OK' = 'ok', 'ERROR' = 'error' } /** * 火币API调用 * * - accessKey: 必填, 通过火币官方申请 https://huobiapi.github.io/docs/spot/v1/cn/#185368440e * - secretKey: 必填, 通过火币官方申请 https://huobiapi.github.io/docs/spot/v1/cn/#185368440e * - proxy: 选填, 需要代理时填入, default false * - hostname: 选填, 调用火币的hostname, default 'api.huobi.pro' * - timeout: 选填, 接口调用超时时间, 单位ms, default 30000 * * @export * @class HuobiRestAPI */ export class HuobiRestAPI { private accessKey: string; private secretKey: string; private httpsConfig: Object; hostname: string; protocol: string; proxy: { host: string, port: number } | false; constructor({ accessKey, secretKey, proxy = false, hostname = 'api.huobi.pro', timeout = 30000 }: { accessKey: string, secretKey: string, proxy?: { host: string, port: number } | false, timeout?: number hostname?: string }) { if (!accessKey || !secretKey) { throw 'Params Missing: accessKey or secretKey'; } this.accessKey = accessKey; this.secretKey = secretKey; this.hostname = hostname; this.protocol = 'https'; this.proxy = proxy; this.httpsConfig = { timeout, headers: DEFAULT_HEADERS }; } get host() { return `${this.protocol}://${this.hostname}`; } get(path: string, params?: Object) { return this.request('GET', path, params); } post(path: string, params?: Object) { return this.request('POST', path, params); } request(method: 'GET' | 'POST', path: string, params?: Object) { if (method !== 'GET' && method !== 'POST') { throw 'method only be GET or POST'; } path = this.foramtPath(path); const { paramsStr, originalParams } = this.signParams({ path, method, params }); if (method === 'GET') { return this.fetch(`${path}?${paramsStr}`, { method }); } return this.fetch(`${path}?${paramsStr}`, { method, data: originalParams }); } private signParams({ method, path, params }: { method: 'GET' | 'POST'; path: String; params?: Object; }): { originalParams: Object, paramsStr: string, signature: string } { if (!path.startsWith('/')) { throw 'path must starts with \/'; } const needSignature = !path.startsWith('/market'); let originalParams; if (needSignature) { originalParams = { AccessKeyId: this.accessKey, SignatureMethod: 'HmacSHA256', SignatureVersion: '2', Timestamp: moment.utc().format('YYYY-MM-DDTHH:mm:ss'), ...params }; } else { originalParams = { ...params }; } const paramsArr = []; for (const item in originalParams) { paramsArr.push(`${item}=${encodeURIComponent(originalParams[item])}`); } const pStr = paramsArr.sort().join('&'); if (!needSignature) { return { originalParams, signature: '', paramsStr: pStr }; } const meta = [method, this.hostname, path, pStr].join('\n'); const hash = HmacSHA256(meta, this.secretKey); const signature = encodeURIComponent(CryptoJS.enc.Base64.stringify(hash)); return { signature, originalParams, paramsStr: `${pStr}&Signature=${signature}` }; } private foramtPath(path: string): string { path = path.trim(); if (!path.startsWith('/')) { path = `/${ path }`; } if (path.endsWith('/')) { path = path.substring(0, path.length - 1); } return path; } private fetch(path, options: Object) { const url = `${this.host}${path}`; return axios({ url, ...options, ...this.httpsConfig, proxy: this.proxy }).then((res) => { if (res.status !== 200) { throw res; } return res.data; }).then((data) => { const status: string = data.status.toLowerCase(); if (status !== STATUS.OK) { throw data; } return data; }); } }