binance-api-node
binance-api-node copied to clipboard
Opening an order with TP/SL
I have been trying (unsuccessfully) to create a function that would follow this rough format
function limitOrderWithSlAndTp(quantity, price, sl, tp) {
// Unsure what to put here...
}
Ideally this opens up an order on a futures market with a take profit and stop loss? There are other options of setting up a take profit and stop loss manually, but I believe there should be an easy way to do this (referring to how in the GUI there is an area for TP/SL)?
Thank you for the help!
There is no easy way to do this. You must send 3 different requests. I am inserting an example of my own code here for you.Ask what you don't understand. I hope I can help you.
import { Client, ExchangeInfo, RoundStep } from './Binance'
import { FuturesOrderType_LT, Symbol, SymbolLotSizeFilter, OrderSide_LT, SymbolPriceFilter } from 'binance-api-node'
async function GetAvailableBalance() {
return (await Client.futuresAccountInfo()).availableBalance
}
async function CalculatePercentageBalance(percentage: number) {
return (Number(await GetAvailableBalance()) * percentage) / 100
}
async function CalculateQuantity(symbol: string, size: number) {
let FilterSymbolObject: Symbol<FuturesOrderType_LT> = ExchangeInfo.find((Pair) => {
return Pair.symbol === symbol
})!
let StepSizeFilter = FilterSymbolObject.filters.find((Filter) => {
return Filter.filterType === 'MARKET_LOT_SIZE'
}) as SymbolLotSizeFilter
let StepSize = StepSizeFilter ? StepSizeFilter.stepSize : ''
const lastPrice = (await Client.futuresMarkPrice()).find((p) => p.symbol === symbol)!.markPrice
let quantity = size / parseFloat(lastPrice)
quantity = RoundStep(String(quantity), StepSize)
return quantity
}
async function RoundPrice(symbol: string, price: number) {
let FilterSymbolObject: Symbol<FuturesOrderType_LT> = ExchangeInfo.find((Pair) => {
return Pair.symbol === symbol
})!
let TickSizeFilter = FilterSymbolObject.filters.find((Filter) => {
return Filter.filterType === 'PRICE_FILTER'
}) as SymbolPriceFilter
let org_price = RoundStep(String(price), TickSizeFilter.tickSize)
return org_price
}
async function GetPositions(symbol: string) {
let positions = (await Client.futuresAccountInfo()).positions
let position = positions.find((p) => p.symbol === symbol)
if (parseFloat(position?.positionAmt!) === 0) return undefined
return position
}
async function CancelPosition(symbol: string) {
let position = await GetPositions(symbol)
if (!position) return
await Client.futuresOrder({
symbol: symbol,
side: position.positionAmt.startsWith('-') ? 'BUY' : 'SELL',
reduceOnly: 'true',
type: 'MARKET',
positionSide: 'BOTH',
quantity: position.positionAmt.replace('-', '')
})
}
async function CreatePosition(symbol: string, side: OrderSide_LT, quantity: number, tp_percent?: number, sl_percent?: number) {
let pos = await Client.futuresOrder({
symbol: symbol,
side: side, // 'BUY' veya 'SELL'
type: 'MARKET', // Piyasa emri
quantity: String(quantity)
})
let markPrice = (await Client.futuresMarkPrice()).find((p) => p.symbol === symbol)!.markPrice
if (tp_percent && tp_percent !== 0) {
let stopPrice = parseFloat(markPrice)
if (side === 'BUY') stopPrice *= tp_percent / 100 + 1
else stopPrice *= 1 - tp_percent / 100
stopPrice = await RoundPrice(symbol, stopPrice)
await Client.futuresOrder({
symbol: symbol,
type: 'TAKE_PROFIT_MARKET',
quantity: String(quantity),
stopPrice: String(stopPrice),
side: side === 'BUY' ? 'SELL' : 'BUY',
closePosition: 'true',
positionSide: 'BOTH',
timeInForce: 'GTE_GTC'
})
}
if (sl_percent && sl_percent !== 0) {
let stopPrice = parseFloat(markPrice)
if (side === 'BUY') stopPrice *= 1 - sl_percent / 100
else stopPrice *= 1 + sl_percent / 100
stopPrice = await RoundPrice(symbol, stopPrice)
await Client.futuresOrder({
symbol: symbol,
type: 'STOP_MARKET',
quantity: String(quantity),
stopPrice: String(stopPrice),
side: side === 'BUY' ? 'SELL' : 'BUY',
closePosition: 'true',
positionSide: 'BOTH',
timeInForce: 'GTE_GTC'
})
}
return pos
}
export default { GetAvailableBalance, CalculatePercentageBalance, GetPositions, CreatePosition, CancelPosition, CalculateQuantity }
@farukborann thank you for this! Just one more question - assuming the take profit gets hit, does the stop loss order cancel? I wouldn't want the order to just linger around.
@farukborann thanks for sharing the code its awesome. Can you also share a methods ExchangeInfo, RoundStep code ? Thanks
@farukborann thank you for this! Just one more question - assuming the take profit gets hit, does the stop loss order cancel? I wouldn't want the order to just linger around.
They will stop because the orders are related to each other. It's a bit late, but I wanted to write for information.
RoundStep
Yes, for sure!
export let ExchangeInfo: Symbol<FuturesOrderType_LT>[]
async function GetExchangeInfo() {
try {
const _exchangeInfo = await Client.futuresExchangeInfo()
ExchangeInfo = _exchangeInfo.symbols
Log('Exchange info loaded successfuly.')
} catch (err) {
Log('Error getting exchange info, retrying in 5 seconds.')
setTimeout(GetExchangeInfo, 5000)
}
}
GetExchangeInfo()
// Integers do not require rounding
export function RoundStep(Quantity: string, StepSize: string) {
if (Number.isInteger(Quantity)) return Number(Quantity)
const qtyString = parseFloat(Quantity).toFixed(16)
const desiredDecimals = Math.max(StepSize.indexOf('1') - 1, 0)
const decimalIndex = qtyString.indexOf('.')
return parseFloat(qtyString.slice(0, decimalIndex + desiredDecimals + 1))
}
You must to be run GetExchangeInfo function for one time in my structre but you can change this code for yourself. Btw i found RoundStep function somewhere on stackoverflow but i dont remember where.