Skip to content

Commit f18eca5

Browse files
authored
Merge pull request #665 from tiagosiebler/beautifierfuturesuserdata
feat(v3.5.5): Improve private wsKey resolution for futures userData streams, misc fixes, docs updates, util methods
2 parents 6c92b62 + 6c386bb commit f18eca5

17 files changed

Lines changed: 2076 additions & 120 deletions

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ Updated & performant JavaScript & Node.js SDK for the Binance REST APIs and WebS
5555
- Real API calls in e2e tests.
5656
- Proxy support via axios integration.
5757
- Active community support & collaboration in telegram: [Node.js Algo Traders](https://t.me/nodetraders).
58-
- QuickStart Guide: https://siebly.io/sdk/binance/javascript
58+
- QuickStart Guide: [Binance JavaScript QuickStart Guide](https://siebly.io/sdk/binance/javascript)
59+
- Binance JavaScript Tutorial: [Binance JavaScript REST API & WebSocket Tutorial](https://siebly.io/sdk/binance/javascript/tutorial)
5960

6061
## Table of Contents
6162

docs/BINANCE_SDK_QUICKSTART_GUIDE.md

Lines changed: 1492 additions & 0 deletions
Large diffs are not rendered by default.

examples/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Found something difficult to implement? Contribute to these examples and help ot
1212

1313
Samples that refer to API credentials using `process.env.API_KEY_COM` can be spawned with environment variables. Unix/macOS example:
1414
```
15-
APIKEY='apikeypastedhere' APISECRET='apisecretpastedhere' tsx examples/WebSockets/ws-userdata-listenkey.ts
15+
API_KEY_COM='apikeypastedhere' API_SECRET_COM='apisecretpastedhere' tsx "examples/WebSockets/Private(userdata)/ws-userdata-listenkey.ts"
1616
```
1717

1818
Or edit the example directly to hardcode your API keys.
@@ -90,6 +90,6 @@ High level summary for some of the available examples, but check the folder for
9090

9191
#### REST USDM Examples
9292

93-
- `rest-future-bracket-order.ts` Creates three order, entry, TP, SL and submit them all at once using `submitMultipleOrders`
94-
- `rest-usdm-order.ts` Creates single entry, using `submitNewOrder`
95-
- `rest-usdm-order-sl.ts` Modify current Stop Loss order(HedgeMode only)
93+
- `rest-future-bracket-order.ts` Creates an entry order plus TP/SL Algo Service orders.
94+
- `rest-usdm-order.ts` Creates a single entry order using `submitNewOrder`.
95+
- `rest-usdm-order-sl.ts` Modifies a Hedge Mode LONG stop-loss order using Algo Service orders.
Lines changed: 30 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,67 @@
1-
import { NewFuturesOrderParams, USDMClient } from '../../../src/index';
1+
import {
2+
FuturesNewAlgoOrderParams,
3+
USDMClient,
4+
} from '../../../src/index';
25

36
const key = process.env.API_KEY_COM || 'APIKEY';
47
const secret = process.env.API_SECRET_COM || 'APISECRET';
58

69
const client = new USDMClient({
710
api_key: key,
811
api_secret: secret,
9-
beautifyResponses: true,
12+
beautifyResponses: false,
1013
});
1114

1215
(async () => {
1316
try {
14-
// TODO: check balance and do other validations
17+
const symbol = process.env.BINANCE_EXAMPLE_SYMBOL || 'ETHUSDT';
18+
const quantity = Number(process.env.BINANCE_EXAMPLE_QUANTITY || '0.01');
1519

16-
const assetPrices = await client.getMarkPrice({
17-
symbol: 'ETHUSDT',
18-
});
19-
const markPrice: number = Number(assetPrices.markPrice);
20-
const stopLossPrice = Number((markPrice * 99.9) / 100).toFixed(2);
21-
const takeProfitPrice = Number((markPrice * 100.1) / 100).toFixed(2);
20+
const assetPrices = await client.getMarkPrice({ symbol });
21+
const markPrice = Number(assetPrices.markPrice);
22+
const stopLossPrice = ((markPrice * 99.9) / 100).toFixed(2);
23+
const takeProfitPrice = ((markPrice * 100.1) / 100).toFixed(2);
2224

23-
// create three orders
24-
// 1. entry order (GTC),
25-
// 2. take profit order (GTE_GTC),
26-
// 3. stop loss order (GTE_GTC)
27-
28-
const entryOrder: NewFuturesOrderParams<string> = {
25+
const entryOrder = {
2926
positionSide: 'BOTH',
30-
quantity: '0.01',
27+
quantity,
3128
reduceOnly: 'false',
3229
side: 'BUY',
33-
symbol: 'ETHUSDT',
30+
symbol,
3431
type: 'MARKET',
35-
};
32+
} as const;
3633

37-
const takeProfitOrder: NewFuturesOrderParams<string> = {
34+
const takeProfitOrder: FuturesNewAlgoOrderParams = {
35+
algoType: 'CONDITIONAL',
3836
positionSide: 'BOTH',
3937
priceProtect: 'TRUE',
40-
quantity: '0.01',
4138
side: 'SELL',
42-
stopPrice: takeProfitPrice,
43-
symbol: 'ETHUSDT',
44-
timeInForce: 'GTE_GTC',
39+
triggerPrice: takeProfitPrice,
40+
symbol,
4541
type: 'TAKE_PROFIT_MARKET',
4642
workingType: 'MARK_PRICE',
4743
closePosition: 'true',
4844
};
4945

50-
const stopLossOrder: NewFuturesOrderParams<string> = {
46+
const stopLossOrder: FuturesNewAlgoOrderParams = {
47+
algoType: 'CONDITIONAL',
5148
positionSide: 'BOTH',
5249
priceProtect: 'TRUE',
53-
quantity: '0.01',
5450
side: 'SELL',
55-
stopPrice: stopLossPrice,
56-
symbol: 'ETHUSDT',
57-
timeInForce: 'GTE_GTC',
51+
triggerPrice: stopLossPrice,
52+
symbol,
5853
type: 'STOP_MARKET',
5954
workingType: 'MARK_PRICE',
6055
closePosition: 'true',
6156
};
6257

63-
const openedOrder = await client
64-
.submitMultipleOrders([entryOrder, takeProfitOrder, stopLossOrder])
65-
.catch((e) => console.log(e?.body || e));
66-
console.log(openedOrder);
58+
const openedOrder = await client.submitNewOrder(entryOrder);
59+
const takeProfitAlgoOrder =
60+
await client.submitNewAlgoOrder(takeProfitOrder);
61+
const stopLossAlgoOrder = await client.submitNewAlgoOrder(stopLossOrder);
62+
63+
console.log({ openedOrder, takeProfitAlgoOrder, stopLossAlgoOrder });
6764
} catch (e) {
68-
console.log(e);
65+
console.error(e);
6966
}
7067
})();

examples/Rest/Futures/rest-usdm-order-sl.ts

Lines changed: 66 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,93 @@
1-
/* eslint-disable @typescript-eslint/no-unused-vars */
2-
import { USDMClient } from '../../../src/index';
1+
import { FuturesNewAlgoOrderParams, USDMClient } from '../../../src/index';
32

43
// or
5-
// import { USDMClient } from 'binance';
4+
// import { FuturesNewAlgoOrderParams, USDMClient } from 'binance';
65

76
const key = process.env.API_KEY_COM || 'APIKEY';
87
const secret = process.env.API_SECRET_COM || 'APISECRET';
98

109
const client = new USDMClient({
1110
api_secret: secret,
1211
api_key: key,
13-
beautifyResponses: true,
12+
beautifyResponses: false,
1413
});
1514

16-
const symbol = 'BTCUSDT';
15+
const symbol = process.env.BINANCE_EXAMPLE_SYMBOL || 'BTCUSDT';
1716

1817
async function start() {
1918
try {
20-
// ### This is for Hedge Mode Only ###
21-
// assuming you currently have a open position, and you want to modify the SL order.
19+
// Hedge Mode example: find each open hedge position and replace its SL.
20+
const positions = await client.getPositionsV3({ symbol });
21+
const hedgePositions = positions.filter((position) => {
22+
if (position.positionSide === 'LONG') {
23+
return Number(position.positionAmt) > 0;
24+
}
25+
if (position.positionSide === 'SHORT') {
26+
return Number(position.positionAmt) < 0;
27+
}
28+
return false;
29+
});
2230

23-
/**
24-
* first we get all long and short positions status
25-
* the result of this method in hedge mode is array of two objects
26-
* first index for LONG and second index for SHORT
27-
*/
28-
const [
29-
{ positionAmt: longAmount, ...long },
30-
{ positionAmt: shortAmount, ...short },
31-
]: any = await client.getPositionsV3({ symbol });
31+
if (!hedgePositions.length) {
32+
console.log('No open LONG or SHORT hedge position found');
33+
return;
34+
}
3235

33-
// if longAmount is bigger than 0 means we have open long position and if shortAmount is below 0 means we have open short position
34-
const hasLong = parseFloat(longAmount) > 0;
35-
const hasShort = parseFloat(shortAmount) < 0;
36-
const hasOpen = hasLong || hasShort;
36+
const openAlgoOrders = await client.getOpenAlgoOrders({
37+
symbol,
38+
algoType: 'CONDITIONAL',
39+
});
40+
const sdkOrderIdPrefix = client.getOrderIdPrefix();
3741

38-
// if we have any open position then we continue
39-
if (hasOpen) {
40-
// we get ourstop loss here
41-
const orders = await client.getAllOpenOrders({ symbol });
42-
const stopOrders =
43-
orders.filter(({ type }) => type === 'STOP_MARKET') ?? [];
42+
for (const position of hedgePositions) {
43+
if (
44+
position.positionSide !== 'LONG' &&
45+
position.positionSide !== 'SHORT'
46+
) {
47+
continue;
48+
}
4449

45-
// we want to modify our long position SL here
46-
if (hasLong) {
47-
// we get the StopLoss order which is realted to long
48-
const { orderId }: any = stopOrders.find(
49-
({ positionSide: ps }) => ps == 'LONG',
50-
);
50+
const positionSide = position.positionSide;
51+
const side = positionSide === 'LONG' ? 'SELL' : 'BUY';
52+
const triggerPriceMultiplier = positionSide === 'LONG' ? 0.99 : 1.01;
53+
const appOwnedStops = openAlgoOrders.filter(
54+
(order) =>
55+
order.orderType === 'STOP_MARKET' &&
56+
order.positionSide === positionSide &&
57+
order.side === side &&
58+
order.clientAlgoId.startsWith(sdkOrderIdPrefix),
59+
);
5160

52-
// if it exists, cancel it.
53-
if (orderId) {
54-
await client.cancelOrder({ symbol, orderId });
55-
}
61+
const stopLossOrder: FuturesNewAlgoOrderParams = {
62+
algoType: 'CONDITIONAL',
63+
symbol,
64+
side,
65+
positionSide,
66+
type: 'STOP_MARKET',
67+
closePosition: 'true',
68+
triggerPrice: (
69+
Number(position.markPrice) * triggerPriceMultiplier
70+
).toFixed(3),
71+
workingType: 'MARK_PRICE',
72+
priceProtect: 'TRUE',
73+
};
5674

57-
const { markPrice }: any = long;
75+
if (appOwnedStops.length > 1) {
76+
throw new Error(
77+
`More than one SDK-prefixed ${positionSide} STOP_MARKET algo order found; refusing to choose automatically.`,
78+
);
79+
}
5880

59-
// creating SL order
60-
const result = await client.submitNewOrder({
61-
symbol,
62-
side: 'SELL', // the action of order, means this order will sell which is sl for long position
63-
positionSide: 'LONG', // based on the headge mode we either LONG or SHORT, here we are doing it for our long pos
64-
timeInForce: 'GTC',
65-
type: 'STOP_MARKET',
66-
closePosition: 'true', // this is here because we don't have the position quantity value, and it means closee all quantity
67-
stopPrice: parseFloat((markPrice * 0.99).toFixed(3)), // set sl price 1% below current price
68-
workingType: 'MARK_PRICE',
69-
});
70-
console.log('SL Modifiled sell result: ', result);
81+
const existingStop = appOwnedStops[0];
82+
if (existingStop) {
83+
await client.cancelAlgoOrder({ algoId: existingStop.algoId });
7184
}
72-
} else {
73-
console.log('No Open position found');
85+
86+
const result = await client.submitNewAlgoOrder(stopLossOrder);
87+
console.log(`SL modified ${positionSide} result: `, result);
7488
}
7589
} catch (e) {
76-
console.error('market sell failed: ', e);
90+
console.error('SL update failed: ', e);
7791
}
7892
}
7993

package-lock.json

Lines changed: 24 additions & 20 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/types/futures.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1009,7 +1009,7 @@ export interface FuturesNewAlgoOrderParams {
10091009
workingType?: WorkingType;
10101010
priceMatch?: PriceMatchMode;
10111011
closePosition?: BooleanString;
1012-
priceProtect?: BooleanString;
1012+
priceProtect?: BooleanStringCapitalised;
10131013
reduceOnly?: BooleanString;
10141014
activatePrice?: numberInString;
10151015
callbackRate?: numberInString;

src/types/websockets/ws-api-requests.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -706,8 +706,8 @@ export interface WSAPINewFuturesAlgoOrderRequest<numberType = numberInString> {
706706
quantity?: numberType;
707707
reduceOnly?: BooleanString;
708708
price?: numberInString;
709-
newClientOrderId?: string;
710-
stopPrice?: numberInString;
709+
clientAlgoId?: string;
710+
triggerPrice?: numberInString;
711711
closePosition?: BooleanString;
712712
activatePrice?: numberInString;
713713
callbackRate?: numberInString;

0 commit comments

Comments
 (0)