Skip to content

Commit 981f5cd

Browse files
committed
feat(client): add ocoOrder and example
1 parent 78865fc commit 981f5cd

3 files changed

Lines changed: 158 additions & 21 deletions

File tree

examples/oco-order.ts

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import Binance from "../src/node-binance-api.js"
2+
3+
4+
async function main() {
5+
6+
const binance = new Binance({
7+
APIKEY: '',
8+
APISECRET: '',
9+
verbose: true
10+
});
11+
12+
const symbol = 'LTCUSDT';
13+
const side = 'SELL';
14+
const quantity = 0.1;
15+
16+
const params = {
17+
// below order
18+
belowType: 'STOP_LOSS_LIMIT',
19+
belowPrice: 51,
20+
belowStopPrice: 50,
21+
belowTimeInForce: 'GTC',
22+
23+
24+
// above order
25+
aboveType: 'TAKE_PROFIT_LIMIT',
26+
aboveStopPrice: 160,
27+
abovePrice: 120,
28+
aboveTimeInForce: 'GTC',
29+
}
30+
31+
const oco1 = await binance.ocoOrder(side, symbol, quantity, params);
32+
console.log('oco1', oco1);
33+
34+
}
35+
36+
main()
37+
38+
39+
// RESPONSE
40+
41+
// oco1 {
42+
// orderListId: 218029,
43+
// contingencyType: 'OCO',
44+
// listStatusType: 'EXEC_STARTED',
45+
// listOrderStatus: 'EXECUTING',
46+
// listClientOrderId: 'x-B3AUXNYV9e56b68a11d646f4b8da07',
47+
// transactionTime: 1758726001738,
48+
// symbol: 'LTCUSDT',
49+
// orders: [
50+
// {
51+
// symbol: 'LTCUSDT',
52+
// orderId: 21409867,
53+
// clientOrderId: 'MVM96szzkULu3dD7eN8xrZ'
54+
// },
55+
// {
56+
// symbol: 'LTCUSDT',
57+
// orderId: 21409868,
58+
// clientOrderId: 'yTaqP6Txvp6mwF7Oo7RWnb'
59+
// }
60+
// ],
61+
// orderReports: [
62+
// {
63+
// symbol: 'LTCUSDT',
64+
// orderId: 21409867,
65+
// orderListId: 218029,
66+
// clientOrderId: 'MVM96szzkULu3dD7eN8xrZ',
67+
// transactTime: 1758726001738,
68+
// price: '51.00000000',
69+
// origQty: '0.10000000',
70+
// executedQty: '0.00000000',
71+
// origQuoteOrderQty: '0.00000000',
72+
// cummulativeQuoteQty: '0.00000000',
73+
// status: 'NEW',
74+
// timeInForce: 'GTC',
75+
// type: 'STOP_LOSS_LIMIT',
76+
// side: 'SELL',
77+
// stopPrice: '50.00000000',
78+
// workingTime: -1,
79+
// selfTradePreventionMode: 'EXPIRE_MAKER'
80+
// },
81+
// {
82+
// symbol: 'LTCUSDT',
83+
// orderId: 21409868,
84+
// orderListId: 218029,
85+
// clientOrderId: 'yTaqP6Txvp6mwF7Oo7RWnb',
86+
// transactTime: 1758726001738,
87+
// price: '120.00000000',
88+
// origQty: '0.10000000',
89+
// executedQty: '0.00000000',
90+
// origQuoteOrderQty: '0.00000000',
91+
// cummulativeQuoteQty: '0.00000000',
92+
// status: 'NEW',
93+
// timeInForce: 'GTC',
94+
// type: 'TAKE_PROFIT_LIMIT',
95+
// side: 'SELL',
96+
// stopPrice: '160.00000000',
97+
// workingTime: -1,
98+
// selfTradePreventionMode: 'EXPIRE_MAKER'
99+
// }
100+
// ]
101+
// }

src/node-binance-api.ts

Lines changed: 56 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -785,7 +785,7 @@ export default class Binance {
785785
* @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#new-order-trade
786786
* @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/public-api-endpoints#test-new-order-trade
787787
* @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#new-order-list---oco-trade
788-
* @param {OrderType} type - LIMIT, MARKET, STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, TAKE_PROFIT_LIMIT, LIMIT_MAKER
788+
* @param {OrderType} type - LIMIT, MARKET, STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, TAKE_PROFIT_LIMIT, LIMIT_MAKER, OCO
789789
* @param {OrderSide} side - BUY or SELL
790790
* @param {string} symbol - The symbol to buy or sell
791791
* @param {string} quantity - The quantity to buy or sell
@@ -797,52 +797,50 @@ export default class Binance {
797797
* @return {undefined}
798798
*/
799799
async order(type: OrderType, side: OrderSide, symbol: string, quantity: number, price?: number, params: Dict = {}): Promise<Order> {
800-
let endpoint = params.type === 'OCO' ? 'v3/orderList/oco' : 'v3/order';
800+
const isOCO = type === 'OCO' || params.type === 'OCO';
801+
let endpoint = isOCO ? 'v3/orderList/oco' : 'v3/order';
801802
if (params.test) {
802803
delete params.test;
803804
endpoint += '/test';
804805
}
805806
const request = {
806807
symbol: symbol,
807808
side: side,
808-
type: type
809+
// type: type
809810
} as Dict;
811+
if (!isOCO) request.type = type;
810812
if (params.quoteOrderQty && params.quoteOrderQty > 0)
811813
request.quoteOrderQty = params.quoteOrderQty;
812814
else
813815
request.quantity = quantity;
814816

815-
if (request.type.includes('LIMIT')) {
817+
if (!isOCO && request.type.includes('LIMIT')) {
816818
request.price = price;
817819
if (request.type !== 'LIMIT_MAKER') {
818820
request.timeInForce = 'GTC';
819821
}
820822
}
821-
if (request.type == 'MARKET' && typeof params.quoteOrderQty !== 'undefined') {
823+
if (!isOCO && request.type == 'MARKET' && typeof params.quoteOrderQty !== 'undefined') {
822824
request.quoteOrderQty = params.quoteOrderQty;
823825
delete request.quantity;
824826
}
825-
if (request.type === 'OCO') {
826-
request.price = price;
827-
request.stopLimitPrice = params.stopLimitPrice;
828-
request.stopLimitTimeInForce = 'GTC';
829-
delete request.type;
830-
// if (typeof params.listClientOrderId !== 'undefined') opt.listClientOrderId = params.listClientOrderId;
831-
// if (typeof params.limitClientOrderId !== 'undefined') opt.limitClientOrderId = params.limitClientOrderId;
832-
// if (typeof params.stopClientOrderId !== 'undefined') opt.stopClientOrderId = params.stopClientOrderId;
833-
}
834827
// if (typeof params.timeInForce !== 'undefined') opt.timeInForce = params.timeInForce;
835828
// if (typeof params.newOrderRespType !== 'undefined') opt.newOrderRespType = params.newOrderRespType;
836-
if (!params.newClientOrderId) {
837-
request.newClientOrderId = this.SPOT_PREFIX + this.uuid22();
829+
if (!params.newClientOrderId && !params.listClientOrderId) {
830+
const id = this.SPOT_PREFIX + this.uuid22();
831+
if (!isOCO) {
832+
request.newClientOrderId = id;
833+
} else {
834+
request.listClientOrderId = id;
835+
}
838836
}
839837

840-
const allowedTypesForStopAndTrailing = ['STOP_LOSS', 'STOP_LOSS_LIMIT', 'TAKE_PROFIT', 'TAKE_PROFIT_LIMIT'];
838+
const allowedTypesForStopAndTrailing = ['STOP_LOSS', 'STOP_LOSS_LIMIT', 'TAKE_PROFIT', 'TAKE_PROFIT_LIMIT', 'OCO'];
841839
if (params.trailingDelta) {
842840
request.trailingDelta = params.trailingDelta;
843841

844-
if (!allowedTypesForStopAndTrailing.includes(request.type)) {
845-
throw Error('trailingDelta: Must set "type" to one of the following: STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, TAKE_PROFIT_LIMIT');
842+
if (!isOCO && !allowedTypesForStopAndTrailing.includes(request.type)) {
843+
throw Error('trailingDelta: Must set "type" to one of the following: STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, TAKE_PROFIT_LIMIT, OCO');
846844
}
847845
}
848846

@@ -856,8 +854,8 @@ export default class Binance {
856854
// if (typeof params.icebergQty !== 'undefined') request.icebergQty = params.icebergQty;
857855
if (params.stopPrice) {
858856
request.stopPrice = params.stopPrice;
859-
if (!allowedTypesForStopAndTrailing.includes(request.type)) {
860-
throw Error('stopPrice: Must set "type" to one of the following: STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, TAKE_PROFIT_LIMIT');
857+
if (!isOCO && !allowedTypesForStopAndTrailing.includes(request.type)) {
858+
throw Error('stopPrice: Must set "type" to one of the following: STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, TAKE_PROFIT_LIMIT, OCO');
861859
}
862860
}
863861
const response = await this.privateSpotRequest(endpoint, this.extend(request, params), 'POST');
@@ -875,6 +873,43 @@ export default class Binance {
875873
return response;
876874
}
877875

876+
/**
877+
* Create a OCO spot order
878+
* @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#new-order-list---oco-trade
879+
* @param {OrderSide} side - BUY or SELL
880+
* @param {string} symbol - The symbol to buy or sell
881+
* @param {string} quantity - The quantity to buy or sell
882+
* @param {string} price - The price per unit to transact each unit at
883+
* @param {object} params - additional order settings
884+
* @param {string} params.aboveType - The type of the above order
885+
* @param {string} params.belowType - The type of the below order
886+
* @param {string} params.abovePrice - The price of the above order
887+
* @param {string} params.aboveStopPrice - The stop price of the above order
888+
* @param {string} params.aboveTrailingDelta - The trailing delta of the above order
889+
* @param {string} params.aboveTimeInForce - The time in force of the above order
890+
* @param {string} params.belowPrice - The price of the below order
891+
* @param {string} params.belowStopPrice - The stop price of the below order
892+
* @param {string} params.belowTrailingDelta - The trailing delta of the below order
893+
* @param {string} params.belowTimeInForce - The time in force of the below order
894+
* @return {undefined}
895+
*/
896+
async ocoOrder(side: OrderSide, symbol: string, quantity: number, params: Dict = {}): Promise<any> {
897+
const request = {
898+
symbol: symbol,
899+
side: side,
900+
quantity: quantity,
901+
} as Dict;
902+
903+
if (!params.listClientOrderId) {
904+
const id = this.SPOT_PREFIX + this.uuid22();
905+
request.listClientOrderId = id;
906+
}
907+
908+
const endpoint = 'v3/orderList/oco';
909+
const response = await this.privateSpotRequest(endpoint, this.extend(request, params), 'POST');
910+
return response;
911+
}
912+
878913
/**
879914
* Creates a buy order
880915
* @param {string} symbol - the symbol to buy

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export type OrderType =
3030
| 'TAKE_PROFIT_MARKET'
3131
| 'LIMIT_MAKER'
3232
| 'TRAILING_STOP_MARKET'
33+
| 'OCO'
3334

3435
export type OrderSide = 'BUY' | 'SELL'
3536

0 commit comments

Comments
 (0)