Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions examples/oco-order.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import Binance from "../src/node-binance-api.js"


async function main() {

const binance = new Binance({
APIKEY: '',
APISECRET: '',
verbose: true
});

const symbol = 'LTCUSDT';
const side = 'SELL';
const quantity = 0.1;

const params = {
// below order
belowType: 'STOP_LOSS_LIMIT',
belowPrice: 51,
belowStopPrice: 50,
belowTimeInForce: 'GTC',


// above order
aboveType: 'TAKE_PROFIT_LIMIT',
aboveStopPrice: 160,
abovePrice: 120,
aboveTimeInForce: 'GTC',
}

const oco1 = await binance.ocoOrder(side, symbol, quantity, params);
console.log('oco1', oco1.orderListId);
console.log(oco1.orderReports[0].orderId)
console.log(oco1.orderReports[1].orderId)

}

main()


// RESPONSE

// oco1 {
// orderListId: 218029,
// contingencyType: 'OCO',
// listStatusType: 'EXEC_STARTED',
// listOrderStatus: 'EXECUTING',
// listClientOrderId: 'x-B3AUXNYV9e56b68a11d646f4b8da07',
// transactionTime: 1758726001738,
// symbol: 'LTCUSDT',
// orders: [
// {
// symbol: 'LTCUSDT',
// orderId: 21409867,
// clientOrderId: 'MVM96szzkULu3dD7eN8xrZ'
// },
// {
// symbol: 'LTCUSDT',
// orderId: 21409868,
// clientOrderId: 'yTaqP6Txvp6mwF7Oo7RWnb'
// }
// ],
// orderReports: [
// {
// symbol: 'LTCUSDT',
// orderId: 21409867,
// orderListId: 218029,
// clientOrderId: 'MVM96szzkULu3dD7eN8xrZ',
// transactTime: 1758726001738,
// price: '51.00000000',
// origQty: '0.10000000',
// executedQty: '0.00000000',
// origQuoteOrderQty: '0.00000000',
// cummulativeQuoteQty: '0.00000000',
// status: 'NEW',
// timeInForce: 'GTC',
// type: 'STOP_LOSS_LIMIT',
// side: 'SELL',
// stopPrice: '50.00000000',
// workingTime: -1,
// selfTradePreventionMode: 'EXPIRE_MAKER'
// },
// {
// symbol: 'LTCUSDT',
// orderId: 21409868,
// orderListId: 218029,
// clientOrderId: 'yTaqP6Txvp6mwF7Oo7RWnb',
// transactTime: 1758726001738,
// price: '120.00000000',
// origQty: '0.10000000',
// executedQty: '0.00000000',
// origQuoteOrderQty: '0.00000000',
// cummulativeQuoteQty: '0.00000000',
// status: 'NEW',
// timeInForce: 'GTC',
// type: 'TAKE_PROFIT_LIMIT',
// side: 'SELL',
// stopPrice: '160.00000000',
// workingTime: -1,
// selfTradePreventionMode: 'EXPIRE_MAKER'
// }
// ]
// }
79 changes: 57 additions & 22 deletions src/node-binance-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import nodeFetch from 'node-fetch';
import zip from 'lodash.zipobject';
import stringHash from 'string-hash';
// eslint-disable-next-line
import { Interval, PositionRisk, Order, FuturesOrder, PositionSide, WorkingType, OrderType, OrderStatus, TimeInForce, Callback, IConstructorArgs, OrderSide, FundingRate, CancelOrder, AggregatedTrade, Trade, MyTrade, WithdrawHistoryResponse, DepositHistoryResponse, DepositAddress, WithdrawResponse, Candle, FuturesCancelAllOpenOrder, OrderBook, Ticker, FuturesUserTrade, Account, FuturesAccountInfo, FuturesBalance, QueryOrder, HttpMethod, BookTicker, DailyStats, PremiumIndex, OpenInterest, IWebsocketsMethods, SymbolConfig } from './types.js';
import { Interval, PositionRisk, Order, FuturesOrder, PositionSide, WorkingType, OrderType, OrderStatus, TimeInForce, Callback, IConstructorArgs, OrderSide, FundingRate, CancelOrder, AggregatedTrade, Trade, MyTrade, WithdrawHistoryResponse, DepositHistoryResponse, DepositAddress, WithdrawResponse, Candle, FuturesCancelAllOpenOrder, OrderBook, Ticker, FuturesUserTrade, Account, FuturesAccountInfo, FuturesBalance, QueryOrder, HttpMethod, BookTicker, DailyStats, PremiumIndex, OpenInterest, IWebsocketsMethods, SymbolConfig, OCOOrder } from './types.js';
// export { Interval, PositionRisk, Order, FuturesOrder, PositionSide, WorkingType, OrderType, OrderStatus, TimeInForce, Callback, IConstructorArgs, OrderSide, FundingRate, CancelOrder, AggregatedTrade, Trade, MyTrade, WithdrawHistoryResponse, DepositHistoryResponse, DepositAddress, WithdrawResponse, Candle, FuturesCancelAllOpenOrder, OrderBook, Ticker, FuturesUserTrade, FuturesAccountInfo, FuturesBalance, QueryOrder } from './types';

export interface Dictionary<T> {
Expand Down Expand Up @@ -785,7 +785,7 @@ export default class Binance {
* @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#new-order-trade
* @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/public-api-endpoints#test-new-order-trade
* @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#new-order-list---oco-trade
* @param {OrderType} type - LIMIT, MARKET, STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, TAKE_PROFIT_LIMIT, LIMIT_MAKER
* @param {OrderType} type - LIMIT, MARKET, STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, TAKE_PROFIT_LIMIT, LIMIT_MAKER, OCO
* @param {OrderSide} side - BUY or SELL
* @param {string} symbol - The symbol to buy or sell
* @param {string} quantity - The quantity to buy or sell
Expand All @@ -797,52 +797,50 @@ export default class Binance {
* @return {undefined}
*/
async order(type: OrderType, side: OrderSide, symbol: string, quantity: number, price?: number, params: Dict = {}): Promise<Order> {
let endpoint = params.type === 'OCO' ? 'v3/orderList/oco' : 'v3/order';
const isOCO = type === 'OCO' || params.type === 'OCO';
let endpoint = isOCO ? 'v3/orderList/oco' : 'v3/order';
if (params.test) {
delete params.test;
endpoint += '/test';
}
const request = {
symbol: symbol,
side: side,
type: type
// type: type
Copy link

Copilot AI Sep 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The commented-out type: type assignment followed by conditional assignment creates confusion. Consider removing the commented line and keeping only the conditional assignment for clarity.

Suggested change
// type: type

Copilot uses AI. Check for mistakes.
} as Dict;
if (!isOCO) request.type = type;
if (params.quoteOrderQty && params.quoteOrderQty > 0)
request.quoteOrderQty = params.quoteOrderQty;
else
request.quantity = quantity;

if (request.type.includes('LIMIT')) {
if (!isOCO && request.type.includes('LIMIT')) {
request.price = price;
if (request.type !== 'LIMIT_MAKER') {
request.timeInForce = 'GTC';
}
}
if (request.type == 'MARKET' && typeof params.quoteOrderQty !== 'undefined') {
if (!isOCO && request.type == 'MARKET' && typeof params.quoteOrderQty !== 'undefined') {
request.quoteOrderQty = params.quoteOrderQty;
delete request.quantity;
}
if (request.type === 'OCO') {
request.price = price;
request.stopLimitPrice = params.stopLimitPrice;
request.stopLimitTimeInForce = 'GTC';
delete request.type;
// if (typeof params.listClientOrderId !== 'undefined') opt.listClientOrderId = params.listClientOrderId;
// if (typeof params.limitClientOrderId !== 'undefined') opt.limitClientOrderId = params.limitClientOrderId;
// if (typeof params.stopClientOrderId !== 'undefined') opt.stopClientOrderId = params.stopClientOrderId;
}
// if (typeof params.timeInForce !== 'undefined') opt.timeInForce = params.timeInForce;
// if (typeof params.newOrderRespType !== 'undefined') opt.newOrderRespType = params.newOrderRespType;
if (!params.newClientOrderId) {
request.newClientOrderId = this.SPOT_PREFIX + this.uuid22();
if (!params.newClientOrderId && !params.listClientOrderId) {
const id = this.SPOT_PREFIX + this.uuid22();
if (!isOCO) {
request.newClientOrderId = id;
} else {
request.listClientOrderId = id;
}
}

const allowedTypesForStopAndTrailing = ['STOP_LOSS', 'STOP_LOSS_LIMIT', 'TAKE_PROFIT', 'TAKE_PROFIT_LIMIT'];
const allowedTypesForStopAndTrailing = ['STOP_LOSS', 'STOP_LOSS_LIMIT', 'TAKE_PROFIT', 'TAKE_PROFIT_LIMIT', 'OCO'];
if (params.trailingDelta) {
request.trailingDelta = params.trailingDelta;

if (!allowedTypesForStopAndTrailing.includes(request.type)) {
throw Error('trailingDelta: Must set "type" to one of the following: STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, TAKE_PROFIT_LIMIT');
if (!isOCO && !allowedTypesForStopAndTrailing.includes(request.type)) {
throw Error('trailingDelta: Must set "type" to one of the following: STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, TAKE_PROFIT_LIMIT, OCO');
}
}

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

/**
* Create an OCO spot order
* @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#new-order-list---oco-trade
* @param {OrderSide} side - BUY or SELL
* @param {string} symbol - The symbol to buy or sell
* @param {string} quantity - The quantity to buy or sell
* @param {string} price - The price per unit to transact each unit at
* @param {object} params - additional order settings
* @param {string} params.aboveType - The type of the above order
* @param {string} params.belowType - The type of the below order
* @param {string} params.abovePrice - The price of the above order
* @param {string} params.aboveStopPrice - The stop price of the above order
* @param {string} params.aboveTrailingDelta - The trailing delta of the above order
* @param {string} params.aboveTimeInForce - The time in force of the above order
* @param {string} params.belowPrice - The price of the below order
* @param {string} params.belowStopPrice - The stop price of the below order
* @param {string} params.belowTrailingDelta - The trailing delta of the below order
* @param {string} params.belowTimeInForce - The time in force of the below order
* @return {undefined}
*/
async ocoOrder(side: OrderSide, symbol: string, quantity: number, params: Dict = {}): Promise<OCOOrder> {
const request = {
symbol: symbol,
side: side,
quantity: quantity,
} as Dict;

if (!params.listClientOrderId) {
const id = this.SPOT_PREFIX + this.uuid22();
request.listClientOrderId = id;
}

const endpoint = 'v3/orderList/oco';
const response = await this.privateSpotRequest(endpoint, this.extend(request, params), 'POST');
return response;
}

/**
* Creates a buy order
* @param {string} symbol - the symbol to buy
Expand Down
41 changes: 40 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export type OrderType =
| 'TAKE_PROFIT_MARKET'
| 'LIMIT_MAKER'
| 'TRAILING_STOP_MARKET'
| 'OCO'

export type OrderSide = 'BUY' | 'SELL'

Expand Down Expand Up @@ -601,4 +602,42 @@ export interface SymbolConfig {
isAutoAddMargin: boolean
leverage: number
maxNotionalValue: string
}
}

/**
* The response structure for an OCO order, based on Binance API documentation.
* See: https://binance-docs.github.io/apidocs/spot/en/#new-oco-trade
*/
export interface OCOOrder {
orderListId: number;
contingencyType: string;
listStatusType: string;
listOrderStatus: string;
listClientOrderId: string;
transactionTime: number;
symbol: string;
orders: Array<{
symbol: string;
orderId: number;
clientOrderId: string;
}>;
orderReports: Array<{
symbol: string;
orderId: number;
orderListId: number;
clientOrderId: string;
transactTime: number;
price: string;
origQty: string;
executedQty: string;
cummulativeQuoteQty: string;
status: string;
timeInForce: string;
type: string;
side: string;
stopPrice?: string;
icebergQty?: string;
workingTime?: number;
selfTradePreventionMode?: string;
}>;
}
Loading