Skip to content

Commit 78865fc

Browse files
authored
Merge pull request #963 from ccxt/demo-trading-support
feat(client): add demo trading
2 parents 399dff4 + b849b7a commit 78865fc

File tree

6 files changed

+70
-26
lines changed

6 files changed

+70
-26
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ Actively maintained, typed, and safe SDK for the Binance REST APIs and Websocket
5757

5858
### Features
5959
- Spot, Margin, Futures and Delivery API
60-
- Testnet support
60+
- Demo trading support
61+
- Testnet support (deprecated)
6162
- Proxy support (REST and WS)
6263
- Customizable HTTP headers
6364
- Customizable request parameters

src/node-binance-api.ts

Lines changed: 54 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -30,24 +30,33 @@ export default class Binance {
3030
domain = 'com';
3131
base = `https://api.binance.${this.domain}/api/`;
3232
baseTest = `https://testnet.binance.vision/api/`;
33+
baseDemo = `https://demo-api.binance.com/api/`;
3334
wapi = `https://api.binance.${this.domain}/wapi/`;
3435
sapi = `https://api.binance.${this.domain}/sapi/`;
3536
fapi = `https://fapi.binance.${this.domain}/fapi/`;
3637
dapi = `https://dapi.binance.${this.domain}/dapi/`;
3738
fapiTest = `https://testnet.binancefuture.com/fapi/`;
39+
fapiDemo = `https://demo-fapi.binance.com/fapi/`;
3840
dapiTest = `https://testnet.binancefuture.com/dapi/`;
41+
dapiDemo = `https://demo-dapi.binance.com/dapi/`;
3942
fstream = `wss://fstream.binance.${this.domain}/stream?streams=`;
4043
fstreamSingle = `wss://fstream.binance.${this.domain}/ws/`;
4144
fstreamSingleTest = `wss://stream.binancefuture.${this.domain}/ws/`;
45+
fstreamSingleDemo = `wss://fstream.binancefuture.com/ws/`;
4246
fstreamTest = `wss://stream.binancefuture.${this.domain}/stream?streams=`;
47+
fstreamDemo = `wss://fstream.binancefuture.com/stream?streams=`;
4348
dstream = `wss://dstream.binance.${this.domain}/stream?streams=`;
4449
dstreamSingle = `wss://dstream.binance.${this.domain}/ws/`;
4550
dstreamSingleTest = `wss://dstream.binancefuture.${this.domain}/ws/`;
51+
dstreamSingleDemo = `wss://dstream.binancefuture.com/ws/`;
4652
dstreamTest = `wss://dstream.binancefuture.${this.domain}/stream?streams=`;
53+
dstreamDemo = `wss://dstream.binancefuture.com/stream?streams=`;
4754
stream = `wss://stream.binance.${this.domain}:9443/ws/`;
4855
streamTest = `wss://stream.testnet.binance.vision/ws/`;
56+
streamDemo = `wss://demo-stream.binance.com/ws/`;
4957
combineStream = `wss://stream.binance.${this.domain}:9443/stream?streams=`;
5058
combineStreamTest = `wss://stream.testnet.binance.vision/stream?streams=`;
59+
combineStreamDemo = `wss://demo-stream.binance.com/stream?streams=`;
5160

5261
verbose = false;
5362

@@ -65,7 +74,8 @@ export default class Binance {
6574
APISECRET: string = undefined;
6675
PRIVATEKEY: string = undefined;
6776
PRIVATEKEYPASSWORD: string = undefined;
68-
test = false;
77+
test = false; // sandbox mode
78+
demo = false; // demo mode
6979

7080
timeOffset: number = 0;
7181

@@ -150,6 +160,7 @@ export default class Binance {
150160
keepAlive: true,
151161
verbose: false,
152162
test: false,
163+
demo: false,
153164
hedgeMode: false,
154165
localAddress: false,
155166
family: 4,
@@ -214,8 +225,10 @@ export default class Binance {
214225
if (this.Options.PRIVATEKEY) this.PRIVATEKEY = this.Options.PRIVATEKEY;
215226
if (this.Options.PRIVATEKEYPASSWORD) this.PRIVATEKEYPASSWORD = this.Options.PRIVATEKEYPASSWORD;
216227
if (this.Options.test) this.test = true;
228+
if (this.Options.demo) this.demo = true;
217229
if (this.Options.headers) this.headers = this.Options.Headers;
218230
if (this.Options.domain) this.domain = this.Options.domain;
231+
if (this.Options.httpsProxy) this.httpsProxy = this.Options.httpsProxy;
219232
}
220233

221234
async setOptions(opt = {}): Promise<Binance> {
@@ -233,6 +246,7 @@ export default class Binance {
233246
extend = (...args: any[]) => Object.assign({}, ...args);
234247

235248
getSpotUrl() {
249+
if (this.Options.demo) return this.baseDemo;
236250
if (this.Options.test) return this.baseTest;
237251
return this.base;
238252
}
@@ -242,30 +256,53 @@ export default class Binance {
242256
}
243257

244258
getFapiUrl() {
259+
if (this.Options.demo) return this.fapiDemo;
245260
if (this.Options.test) return this.fapiTest;
246261
return this.fapi;
247262
}
248263

249264
getDapiUrl() {
265+
if (this.Options.demo) return this.dapiDemo;
250266
if (this.Options.test) return this.dapiTest;
251267
return this.dapi;
252268
}
253269

254270
getCombineStreamUrl() {
271+
if (this.Options.demo) return this.combineStreamDemo;
255272
if (this.Options.test) return this.combineStreamTest;
256273
return this.combineStream;
257274
}
258275

259276
getStreamUrl() {
277+
if (this.Options.demo) return this.streamDemo;
260278
if (this.Options.test) return this.streamTest;
261279
return this.stream;
262280
}
263281

264-
getFStreamUrl() {
282+
getDStreamSingleUrl() {
283+
if (this.Options.demo) return this.dstreamSingleDemo;
284+
if (this.Options.test) return this.dstreamSingleTest;
285+
return this.dstreamSingle;
286+
}
287+
288+
getFStreamSingleUrl() {
289+
if (this.Options.demo) return this.fstreamSingleDemo;
265290
if (this.Options.test) return this.fstreamSingleTest;
266291
return this.fstreamSingle;
267292
}
268293

294+
getFStreamUrl() {
295+
if (this.Options.demo) return this.fstreamDemo;
296+
if (this.Options.test) return this.fstreamTest;
297+
return this.fstream;
298+
}
299+
300+
getDStreamUrl() {
301+
if (this.Options.demo) return this.dstreamDemo;
302+
if (this.Options.test) return this.dstreamTest;
303+
return this.dstream;
304+
}
305+
269306
uuid22(a?: any) {
270307
return a ? (a ^ Math.random() * 16 >> a / 4).toString(16) : (([1e7] as any) + 1e3 + 4e3 + 8e5).replace(/[018]/g, this.uuid22);
271308
}
@@ -1462,14 +1499,14 @@ export default class Binance {
14621499
host: this.parseProxy(socksproxy)[1],
14631500
port: this.parseProxy(socksproxy)[2]
14641501
});
1465-
ws = new WebSocket((this.getFStreamUrl()) + endpoint, { agent });
1502+
ws = new WebSocket((this.getFStreamSingleUrl()) + endpoint, { agent });
14661503
} else if (httpsproxy) {
14671504
const config = url.parse(httpsproxy);
14681505
const agent = new HttpsProxyAgent(config);
14691506
if (this.Options.verbose) this.Options.log(`futuresSubscribeSingle: using proxy server: ${agent}`);
1470-
ws = new WebSocket((this.getFStreamUrl()) + endpoint, { agent });
1507+
ws = new WebSocket((this.getFStreamSingleUrl()) + endpoint, { agent });
14711508
} else {
1472-
ws = new WebSocket((this.getFStreamUrl()) + endpoint);
1509+
ws = new WebSocket((this.getFStreamSingleUrl()) + endpoint);
14731510
}
14741511

14751512
if (this.Options.verbose) this.Options.log('futuresSubscribeSingle: Subscribed to ' + endpoint);
@@ -1517,14 +1554,14 @@ export default class Binance {
15171554
host: this.parseProxy(socksproxy)[1],
15181555
port: this.parseProxy(socksproxy)[2]
15191556
});
1520-
ws = new WebSocket((this.Options.test ? this.fstreamTest : this.fstream) + queryParams, { agent });
1557+
ws = new WebSocket(this.getFStreamUrl() + queryParams, { agent });
15211558
} else if (httpsproxy) {
15221559
if (this.Options.verbose) this.Options.log(`futuresSubscribe: using proxy server ${httpsproxy}`);
15231560
const config = url.parse(httpsproxy);
15241561
const agent = new HttpsProxyAgent(config);
1525-
ws = new WebSocket((this.Options.test ? this.fstreamTest : this.fstream) + queryParams, { agent });
1562+
ws = new WebSocket(this.getFStreamUrl() + queryParams, { agent });
15261563
} else {
1527-
ws = new WebSocket((this.Options.test ? this.fstreamTest : this.fstream) + queryParams);
1564+
ws = new WebSocket(this.getFStreamUrl() + queryParams);
15281565
}
15291566

15301567
ws.reconnect = this.Options.reconnect;
@@ -2175,14 +2212,14 @@ export default class Binance {
21752212
host: this.parseProxy(socksproxy)[1],
21762213
port: this.parseProxy(socksproxy)[2]
21772214
});
2178-
ws = new WebSocket((this.Options.test ? this.dstreamSingleTest : this.dstreamSingle) + endpoint, { agent });
2215+
ws = new WebSocket((this.getDStreamSingleUrl()) + endpoint, { agent });
21792216
} else if (httpsproxy) {
21802217
const config = url.parse(httpsproxy);
21812218
const agent = new HttpsProxyAgent(config);
21822219
if (this.Options.verbose) this.Options.log(`deliverySubscribeSingle: using proxy server: ${agent}`);
2183-
ws = new WebSocket((this.Options.test ? this.dstreamSingleTest : this.dstreamSingle) + endpoint, { agent });
2220+
ws = new WebSocket((this.getDStreamSingleUrl()) + endpoint, { agent });
21842221
} else {
2185-
ws = new WebSocket((this.Options.test ? this.dstreamSingleTest : this.dstreamSingle) + endpoint);
2222+
ws = new WebSocket((this.getDStreamSingleUrl()) + endpoint);
21862223
}
21872224

21882225
if (this.Options.verbose) this.Options.log('deliverySubscribeSingle: Subscribed to ' + endpoint);
@@ -2229,14 +2266,14 @@ export default class Binance {
22292266
host: this.parseProxy(socksproxy)[1],
22302267
port: this.parseProxy(socksproxy)[2]
22312268
});
2232-
ws = new WebSocket((this.Options.test ? this.dstreamTest : this.dstream) + queryParams, { agent });
2269+
ws = new WebSocket((this.getDStreamUrl()) + queryParams, { agent });
22332270
} else if (httpsproxy) {
22342271
if (this.Options.verbose) this.Options.log(`deliverySubscribe: using proxy server ${httpsproxy}`);
22352272
const config = url.parse(httpsproxy);
22362273
const agent = new HttpsProxyAgent(config);
2237-
ws = new WebSocket((this.Options.test ? this.dstreamTest : this.dstream) + queryParams, { agent });
2274+
ws = new WebSocket((this.getDStreamUrl()) + queryParams, { agent });
22382275
} else {
2239-
ws = new WebSocket((this.Options.test ? this.dstreamTest : this.dstream) + queryParams);
2276+
ws = new WebSocket((this.getDStreamUrl()) + queryParams);
22402277
}
22412278

22422279
ws.reconnect = this.Options.reconnect;
@@ -5663,7 +5700,8 @@ export default class Binance {
56635700
* @param {Function} subscribed_callback - subscription callback
56645701
*/
56655702
userFutureData(all_updates_callback?: Callback, margin_call_callback?: Callback, account_update_callback?: Callback, order_update_callback?: Callback, subscribed_callback?: Callback, account_config_update_callback?: Callback) {
5666-
const url = (this.Options.test) ? this.fapiTest : this.fapi;
5703+
// const url = (this.Options.test) ? this.fapiTest : this.fapi;
5704+
const url = this.getFapiUrl();
56675705

56685706
const reconnect = () => {
56695707
if (this.Options.reconnect) this.userFutureData(all_updates_callback, margin_call_callback, account_update_callback, order_update_callback, subscribed_callback);
@@ -5708,7 +5746,7 @@ export default class Binance {
57085746
order_update_callback?: Callback,
57095747
subscribed_callback?: Callback
57105748
) {
5711-
const url = this.Options.test ? this.dapiTest : this.dapi;
5749+
const url = this.getDapiUrl();
57125750

57135751
const reconnect = async () => {
57145752
if (this.Options.reconnect)

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ export interface IConstructorArgs {
136136
useServerTime: boolean;
137137
reconnect: boolean;
138138
test: boolean;
139+
demo: boolean;
139140
hedgeMode: boolean;
140141
httpsProxy: string;
141142
socksProxy: string;

tests/binance-class-live.test.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,15 @@ let debug = function (x) {
3737
const binance = new Binance().options({
3838
APIKEY: 'X4BHNSimXOK6RKs2FcKqExquJtHjMxz5hWqF0BBeVnfa5bKFMk7X0wtkfEz0cPrJ',
3939
APISECRET: 'x8gLihunpNq0d46F2q0TWJmeCDahX5LMXSlv3lSFNbMI3rujSOpTDKdhbcmPSf2i',
40-
test: true
40+
test: true,
41+
httpsProxy: 'http://188.245.226.105:8911'
4142
});
4243

4344
const futuresBinance = new Binance().options({
44-
APIKEY: '227719da8d8499e8d3461587d19f259c0b39c2b462a77c9b748a6119abd74401',
45-
APISECRET: 'b14b935f9cfacc5dec829008733c40da0588051f29a44625c34967b45c11d73c',
46-
hedgeMode: true,
47-
test: true
45+
APIKEY: 'HjhMFvuF1veWQVdUbLIy7TiCYe9fj4W6sEukmddD8TM9kPVRHMK6nS2SdV5mwE5u',
46+
APISECRET: 'Suu9pWcO9zbvVuc6cSQsVuiiw2DmmA8DgHrUfePF9s2RtaHa0zxK3eAF4MfIk7Pd',
47+
demo: true,
48+
httpsProxy: 'http://188.245.226.105:8911'
4849
});
4950

5051
/*global describe*/
@@ -307,6 +308,7 @@ describe('Futures MarketBuy', function () {
307308
assert(res['orderId'] !== undefined)
308309
futuresOrderId = res['orderId'];
309310
} catch (e) {
311+
console.error(e);
310312
const exceptionA = '{"code":-2010,"msg":"Account has insufficient balance for requested action."}';
311313
const exceptionB = '{"code":-2019,"msg":"Margin is insufficient."}'
312314
const eStr = e.toString();

tests/binance-ws-futures.test.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ const TIMEOUT = 40000;
1313

1414

1515
const futuresBinance = new Binance().options({
16-
APIKEY: '227719da8d8499e8d3461587d19f259c0b39c2b462a77c9b748a6119abd74401',
17-
APISECRET: 'b14b935f9cfacc5dec829008733c40da0588051f29a44625c34967b45c11d73c',
16+
APIKEY: 'HjhMFvuF1veWQVdUbLIy7TiCYe9fj4W6sEukmddD8TM9kPVRHMK6nS2SdV5mwE5u',
17+
APISECRET: 'Suu9pWcO9zbvVuc6cSQsVuiiw2DmmA8DgHrUfePF9s2RtaHa0zxK3eAF4MfIk7Pd',
1818
hedgeMode: true,
19-
test: true
19+
demo: true,
20+
httpsProxy: 'http://188.245.226.105:8911'
2021
});
2122

2223

tests/binance-ws-spot.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ const TIMEOUT = 40000;
1515
const binance = new Binance().options({
1616
APIKEY: 'X4BHNSimXOK6RKs2FcKqExquJtHjMxz5hWqF0BBeVnfa5bKFMk7X0wtkfEz0cPrJ',
1717
APISECRET: 'x8gLihunpNq0d46F2q0TWJmeCDahX5LMXSlv3lSFNbMI3rujSOpTDKdhbcmPSf2i',
18-
test: true
18+
test: true,
19+
httsProxy: 'http://188.245.226.105:8911'
1920
});
2021

2122
const futuresBinance = new Binance().options({

0 commit comments

Comments
 (0)