UNPKG

11.1 kBPlain TextView Raw
1/// <reference path="./index.d.ts" />
2
3import {Buffer} from 'buffer';
4import * as querystring from 'querystring';
5import {createHmac} from 'crypto';
6import * as request from 'request';
7import * as cheerio from 'cheerio';
8import * as VError from 'verror';
9
10export default class BTCMarkets
11{
12 // used to convert decimal numbers to BTC Market integers. eg 0.001 BTC is 100000 when used in the BTC Markets API
13 static numberConverter = 100000000; // one hundred million
14
15 constructor(public key: string,
16 public secret: string,
17 public server: string = 'https://api.btcmarkets.net',
18 public timeout: number = 20000)
19 {}
20
21 protected privateRequest(
22 path: string,
23 params: object = {} ): Promise<object>
24 {
25 if(!this.key || !this.secret)
26 {
27 throw new VError('must provide key and secret to make this API request.');
28 }
29
30 let method = 'POST';
31 // HTTP GET is used instead of POST for endpoints
32 // under /account/ eg /account/balance or /account/{instrument}/{currency}/tradingfee
33 // or /fundtransfer/history
34 if (path.split('/')[1] === 'account' ||
35 path === '/fundtransfer/history')
36 {
37 method = 'GET';
38 }
39
40 // milliseconds elapsed between 1 January 1970 00:00:00 UTC and the given date
41 const timestamp = (new Date()).getTime();
42
43 let message;
44 if (method === 'POST')
45 {
46 message = path + "\n" +
47 timestamp + "\n" +
48 JSON.stringify(params);
49 }
50 else if (Object.keys(params).length > 0)
51 {
52 message = path + "\n" +
53 querystring.stringify(params) + "\n" +
54 timestamp + "\n";
55 }
56 else
57 {
58 message = path + "\n" +
59 timestamp + "\n";
60 }
61
62 const signer = createHmac('sha512', new Buffer(this.secret, 'base64'));
63 const signature = signer.update(message).digest('base64');
64
65 const headers = {
66 "User-Agent": "BTC Markets Javascript API Client",
67 "apikey": this.key,
68 "timestamp": timestamp,
69 "signature": signature};
70
71 const options: request.Options = {
72 url: this.server + path,
73 method: method,
74 headers: headers,
75 timeout: this.timeout,
76 json: params
77 };
78
79 if (method === 'GET')
80 {
81 options.qs = params;
82 }
83
84 const requestDesc = `${options.method} request to url ${options.url} with message ${message}`;
85
86 return this.executeRequest(options, requestDesc);
87 }
88
89 protected publicRequest(
90 instrument: BTCMarkets.instruments,
91 currency: BTCMarkets.currencies,
92 action: string,
93 params?: object): Promise<object>
94 {
95 const headers = {"User-Agent": "BTC Markets Javascript API Client"};
96
97 const path = '/market/' + instrument + '/' + currency + '/' + action;
98
99 const options = {
100 url: this.server + path,
101 method: 'GET',
102 headers: headers,
103 timeout: this.timeout,
104 json: {},
105 qs: params };
106
107 const requestDesc = `${options.method} request to url ${options.url} with parameters ${JSON.stringify(params)}`;
108
109 return this.executeRequest(options, requestDesc);
110 };
111
112 protected executeRequest(
113 options: request.OptionsWithUrl,
114 requestDesc: string): Promise<object>
115 {
116 return new Promise((resolve, reject) =>
117 {
118 request(options, function(err: any,
119 response: request.RequestResponse,
120 data: BTCMarkets.BaseResponse)
121 {
122 let error = null; // default to no errors
123
124 if(err)
125 {
126 error = new VError(err, `failed ${requestDesc} with error message ${err.message}`);
127 error.name = err.code;
128 }
129 else if (response.statusCode < 200 || response.statusCode >= 300)
130 {
131 error = new VError(`HTTP status code ${response.statusCode} returned from ${requestDesc}. Status message: ${response.statusMessage}`);
132 error.name = response.statusCode.toString();
133 }
134 else if (!data)
135 {
136 error = new VError(`failed ${requestDesc}. No data returned.`);
137 }
138 // if request was not able to parse json response into an object
139 else if (data !== Object(data))
140 {
141 // try and parse HTML body form response
142 const $ = cheerio.load(data);
143
144 const responseBody = $('body').text();
145
146 if (responseBody)
147 {
148 error = new VError(err, `Could not parse response body from ${requestDesc}\nResponse body: ${responseBody}`);
149 error.name = responseBody;
150 }
151 else
152 {
153 error = new VError(err, `Could not parse json or HTML response from ${requestDesc}`);
154 }
155 }
156 else if (data.hasOwnProperty('success') && !data.success)
157 {
158 error = new VError(`failed ${requestDesc}. Success: ${data.success}. Error message: ${data.errorMessage}`);
159 error.name = data.errorMessage;
160 }
161
162 if (error) reject(error);
163
164 resolve(data);
165 });
166 });
167 }
168
169 //
170 // Public API functions
171 //
172
173 getTick(instrument: BTCMarkets.instruments,
174 currency: BTCMarkets.currencies): Promise<BTCMarkets.Tick>
175 {
176 // @ts-ignore
177 return this.publicRequest(instrument, currency, 'tick');
178 };
179
180 getOrderBook(instrument: BTCMarkets.instruments,
181 currency: BTCMarkets.currencies): Promise<BTCMarkets.OrderBook>
182 {
183 // @ts-ignore
184 return this.publicRequest(instrument, currency, 'orderbook');
185 };
186
187 getTrades(instrument: BTCMarkets.instruments,
188 currency: BTCMarkets.currencies,
189 since?: number): Promise<BTCMarkets.Trade[]>
190 {
191 // @ts-ignore
192 return this.publicRequest(instrument, currency, 'trades', {
193 since: since}
194 );
195 };
196
197 //
198 // Private API functions
199 //
200
201 createOrder(instrument: BTCMarkets.instruments,
202 currency: BTCMarkets.currencies,
203 price: number | void = 0, // price is not needed if a market order
204 volume: number,
205 orderSide: BTCMarkets.OrderSide,
206 ordertype: BTCMarkets.OrderType,
207 clientRequestId: string | void = "", // if no client id then set to an empty string
208 triggerPrice?: number
209 ): Promise<BTCMarkets.NewOrder>
210 {
211 const params = {
212 currency: currency,
213 instrument: instrument,
214 price: ordertype == 'Market'? 0 : price, // market orders don't have a price but a value must be passed
215 volume: volume,
216 orderSide: orderSide,
217 ordertype: ordertype,
218 clientRequestId: clientRequestId,
219 triggerPrice: triggerPrice
220 };
221
222 // @ts-ignore
223 return this.privateRequest('/order/create', params);
224 };
225
226 cancelOrders(orderIds: number[]): Promise<BTCMarkets.CancelledOrders>
227 {
228 // @ts-ignore
229 return this.privateRequest('/order/cancel', {
230 orderIds: orderIds}
231 );
232 };
233
234 getOrderDetail(orderIds: number[]): Promise<BTCMarkets.Orders>
235 {
236 // @ts-ignore
237 return this.privateRequest('/order/detail', {
238 orderIds: orderIds}
239 );
240 };
241
242 getOpenOrders(instrument: BTCMarkets.instruments,
243 currency: BTCMarkets.currencies,
244 limit: number | void = 10,
245 since: number | null = null): Promise<BTCMarkets.Orders>
246 {
247 // @ts-ignore
248 return this.privateRequest('/order/open', {
249 currency: currency,
250 instrument: instrument,
251 limit: limit,
252 since: since}
253 );
254 };
255
256 getOrderHistory(instrument: BTCMarkets.instruments,
257 currency: BTCMarkets.currencies,
258 limit: number | void = 100,
259 since: number | null = null): Promise<BTCMarkets.Orders>
260 {
261 // @ts-ignore
262 return this.privateRequest('/order/history', {
263 currency: currency,
264 instrument: instrument,
265 limit: limit,
266 since: since}
267 );
268 };
269
270 getTradeHistory(instrument: BTCMarkets.instruments,
271 currency: BTCMarkets.currencies,
272 limit: number | void = 100,
273 since: number | null = null): Promise<BTCMarkets.Trades>
274 {
275 // @ts-ignore
276 return this.privateRequest('/order/trade/history', {
277 currency: currency,
278 instrument: instrument,
279 limit: limit,
280 since: since}
281 );
282 };
283
284 getAccountBalances(): Promise<BTCMarkets.Balance[]>
285 {
286 // @ts-ignore
287 return this.privateRequest('/account/balance');
288 };
289
290 getTradingFee(instrument: BTCMarkets.instruments,
291 currency: BTCMarkets.currencies): Promise<BTCMarkets.TradingFee>
292 {
293 // @ts-ignore
294 return this.privateRequest('/account/' + instrument + "/" + currency + "/" + 'tradingfee');
295 };
296
297 withdrawCrypto(amount: number,
298 address: string,
299 crypto: string): Promise<BTCMarkets.CryptoWithdrawal>
300 {
301 // @ts-ignore
302 return this.privateRequest('/fundtransfer/withdrawCrypto', {
303 amount: amount,
304 address: address,
305 currency: crypto
306 });
307 };
308
309 withdrawEFT(accountName: string,
310 accountNumber: string,
311 bankName: string,
312 bsbNumber: string,
313 amount: number): Promise<BTCMarkets.BankWithdrawal>
314 {
315 // @ts-ignore
316 return this.privateRequest('/fundtransfer/withdrawEFT', {
317 accountName: accountName,
318 accountNumber: accountNumber,
319 bankName: bankName,
320 bsbNumber: bsbNumber,
321 amount: amount,
322 currency: "AUD"
323 });
324 };
325
326 withdrawHistory(limit: number | void,
327 since: number | void,
328 indexForward: boolean | void): Promise<BTCMarkets.FundWithdrawals>
329 {
330 // @ts-ignore
331 return this.privateRequest('/fundtransfer/history', {
332 limit: limit,
333 since: since,
334 indexForward: indexForward
335 });
336 };
337}