Skip to content

Instantly share code, notes, and snippets.

@coccoinomane
Last active May 3, 2025 13:23
Show Gist options
  • Save coccoinomane/efeb6e213e5d34ca66ebdc72b478a215 to your computer and use it in GitHub Desktop.
Save coccoinomane/efeb6e213e5d34ca66ebdc72b478a215 to your computer and use it in GitHub Desktop.
Binance spot OCO orders via CCXT implicit API
import { Exchange, Order } from 'ccxt';
/**
* Create a spot OCO order using CCXT implicit API for Binance.
*
* An OCO order (one-cancels-the-other) contains a take profit and a stop loss
* order for the same amount.
*
* @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#new-order-list---oco-trade
*
* @param {Exchange} exchange - The exchange object
* @param {string} symbol - The symbol of the market
* @param {'buy' | 'sell'} side - The side of the order
* @param {number} amount - The amount of the order
* @param {number} takeProfitTriggerPrice - The trigger price of the take profit order
* @param {number} stopLossTriggerPrice - The trigger price of the stop loss order
* @param {number} [takeProfitLimitPrice] - The limit price of the take profit order
* @param {number} [stopLossLimitPrice] - The limit price of the stop loss order
* @returns {[Order, Order]} - The two orders created
*/
export async function createBinanceOcoOrder(
exchange: Exchange,
symbol: string,
side: 'buy' | 'sell',
amount: number,
takeProfitTriggerPrice: number,
stopLossTriggerPrice: number,
takeProfitLimitPrice?: number,
stopLossLimitPrice?: number,
): Promise<[Order, Order]> {
// Fetch market object
const markets = await exchange.loadMarkets();
const market = markets[symbol];
if (!market) {
throw new Error(`Market ${symbol} not found`);
}
// Determine parameters to send Binance
const args: any = {
symbol: market.id,
side: side.toUpperCase(),
quantity: exchange.amountToPrecision(symbol, amount),
};
if (side === 'sell') {
// Above order is a take profit order
args.aboveType = takeProfitLimitPrice ? 'TAKE_PROFIT_LIMIT' : 'TAKE_PROFIT';
args.aboveStopPrice = exchange.priceToPrecision(symbol, takeProfitTriggerPrice);
if (takeProfitLimitPrice) {
args.abovePrice = exchange.priceToPrecision(symbol, takeProfitLimitPrice);
args.aboveTimeInForce = 'GTC';
}
// Below order is a stop loss order
args.belowType = stopLossLimitPrice ? 'STOP_LOSS_LIMIT' : 'STOP_LOSS';
args.belowStopPrice = exchange.priceToPrecision(symbol, stopLossTriggerPrice);
if (stopLossLimitPrice) {
args.belowPrice = exchange.priceToPrecision(symbol, stopLossLimitPrice);
args.belowTimeInForce = 'GTC';
}
} else {
// Above order is a stop loss order
args.aboveType = stopLossLimitPrice ? 'STOP_LOSS_LIMIT' : 'STOP_LOSS';
args.aboveStopPrice = exchange.priceToPrecision(symbol, stopLossTriggerPrice);
if (stopLossLimitPrice) {
args.abovePrice = exchange.priceToPrecision(symbol, stopLossLimitPrice);
args.aboveTimeInForce = 'GTC';
}
// Below order is a take profit order
args.belowType = takeProfitLimitPrice ? 'TAKE_PROFIT_LIMIT' : 'TAKE_PROFIT';
args.belowStopPrice = exchange.priceToPrecision(symbol, takeProfitTriggerPrice);
if (takeProfitLimitPrice) {
args.belowPrice = exchange.priceToPrecision(symbol, takeProfitLimitPrice);
args.belowTimeInForce = 'GTC';
}
}
// Send the request to Binance
const response = await (exchange as any).privatePostOrderListOco(args);
// Parse the response into two orders
try {
const binanceOrders = response.orderReports;
return binanceOrders.map((o: any) => exchange.parseOrder(o));
} catch (error) {
console.error(error);
throw new Error(`createBinanceOcoOrder: Could not parse orders from Binance response. Response: ${response}`);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment