Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Opening an order with TP/SL #644

Open
caiowakamatsu opened this issue Sep 14, 2023 · 5 comments
Open

Opening an order with TP/SL #644

caiowakamatsu opened this issue Sep 14, 2023 · 5 comments

Comments

@caiowakamatsu
Copy link

caiowakamatsu commented Sep 14, 2023

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!

@farukborann
Copy link
Contributor

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 }

@caiowakamatsu
Copy link
Author

@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.

@Dzielinski93
Copy link

@farukborann thanks for sharing the code its awesome. Can you also share a methods ExchangeInfo, RoundStep code ? Thanks

@farukborann
Copy link
Contributor

farukborann commented Feb 16, 2024

@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.

@farukborann
Copy link
Contributor

farukborann commented Feb 16, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants