개발자의 삶/코인

Nodejs 바이낸스 API 연동 - 매도 주문

비전공돌이 2022. 8. 5. 00:53

안녕하세요.

오늘은 어제에 이어 binance api를 활용하여 매도 주문을 넣어보겠습니다.

 

buy(symbol, quoteOrderQty) {
    client.newOrder(symbol, 'BUY', 'MARKET', {
        quoteOrderQty: quoteOrderQty
    }).then((response) => {
        console.info(response.data)
    }).catch(error => console.error(error))
}

 

어제 매수 부분을 위와 같이 바꿨었습니다.

 

매도도 비슷하게 해 보겠습니다.

sell(symbol, quantity, price) {
    client.newOrder(symbol, 'SELL', 'LIMIT', {
        quantity: quantity,
        price: price,
        timeInForce: 'GTC'
    }).then((response) => {
        console.info(response.data)
    }).catch(error => console.error(error))
}

매도는 시장가인 MARKET 대신에 지정가인 LIMIT으로 해보겠습니다.

type에 따른 필수 파라미터

바이낸스 문서를 보면 timeInForce, quantity, price 3가지가 필수라고 하네요.

판매할 수량, 판매할 가격, 그리고 timeInForce.

Time in force 종류와 설명

timeInForce는 해당 주문이 얼마나 유효한지에 대한 설정이라고 보시면 됩니다.

잘 모르겠으면 GTC로 지정하면 됩니다. 이게 일반적인 주문 방식입니다.

주문을 넣으면 체결되거나 취소하기 전까지 주문이 유효하게 남아있게 됩니다.

 

어쨌든 이제 팔아보겠습니다.

test.js를 통해 매도 주문을 넣겠습니다.

const Trade = require("./trade")

let trade = new Trade()

// trade.buy('BTCUSDT', '11')
trade.sell('BTCUSDT', 0.00000001, 30000)

 

일단 BTC를 1 사토시(=0.0000001) 를 30k에 지정가로 주문합니다.

node test.js

실행 결과 에러

예상대로 오류가 발생했습니다. 저만 예상했나요? ㅎㅎ

    data: {
      code: -1100,
      msg: "Illegal characters found in parameter 'quantity'; legal range is '^([0-9]{1,20})(\\.[0-9]{1,20})?$'."
    }

오류 응답 중 data 부분을 보겠습니다.

code와 msg가 있습니다. 이번 오휴는 quantity가 잘 못 되었나 봅니다...?.....??

이건 예상 밖이네요. 이걸 원했던 것이 아닌데;;

// trade.sell('BTCUSDT', 0.00000001, 30000)
trade.sell('BTCUSDT', '0.00000001', '30000')

파라미터는 문자열로 넘겨야 합니다.

다시 실행하면 제가 원하던 오류가 나옵니다.

data: { code: -1013, msg: 'Filter failure: LOT_SIZE' }

사실 바이낸스가 주문하기가 좀 까다로운 편입니다.

filter 항목이 많아서 그걸 다 맞춰주어야 하거든요.

이번 오휴는 LOT_SIZE 필터와 맞지 않다고 오류가 발생했습니다.

 

바이낸스 문서에 필터에 대한 페이지가 따로 있습니다.

LOT_SIZE에 대한 설명은 다음과 같습니다.

바이낸스 LOT_SIZE filter 설명

3가지 기준이 있네요. minQty, maxQty, stepSize인데요.

수량 최소치와 최대치가 있다는 얘기입니다.

stepSize는 주문할 수 있는 단위가 정해져 있다는 거고요.

이 조건을 맞춰서 주문을 넣어야 합니다.

 

자 그럼 조건에 맞도록 수치를 조정해 봅시다.

수량을 몇 개로 하면 될까요?

 

네, 거기 3번째 줄 안경 쓴 학생. 정답입니다.

모릅니다. 코인마다 제각각인데 다 외울 수도 없고 모를 수밖에 없죠.

 

그래서 바이낸스가 조회할 수 있도록 API를 제공하고 있습니다.

trade.js에 buy, sell에 이어 하나를 더 추가해봅시다.

exchangeInfo(symbol) {
    client.exchangeInfo({symbol: symbol})
        .then((response) => {
            console.info(JSON.stringify(response.data))
        }).catch(error => console.error(error))
}

특정 심볼에 대한 정보를 조회할 수 있을 겁니다.

// trade.buy('BTCUSDT', '11')
// trade.sell('BTCUSDT', '0.00000001', '30000')
trade.exchangeInfo('BTCUSDT')

test.js에 위와 같이 호출하도록 합니다.

 

{"timezone":"UTC","serverTime":1659626967105,"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":1200},{"rateLimitType":"ORDERS","interval":"SECOND","intervalNum":10,"limit":50},{"rateLimitType":"ORDERS","interval":"DAY","intervalNum":1,"limit":160000},{"rateLimitType":"RAW_REQUESTS","interval":"MINUTE","intervalNum":5,"limit":6100}],"exchangeFilters":[],"symbols":[{"symbol":"BTCUSDT","status":"TRADING","baseAsset":"BTC","baseAssetPrecision":8,"quoteAsset":"USDT","quotePrecision":8,"quoteAssetPrecision":8,"baseCommissionPrecision":8,"quoteCommissionPrecision":8,"orderTypes":["LIMIT","LIMIT_MAKER","MARKET","STOP_LOSS_LIMIT","TAKE_PROFIT_LIMIT"],"icebergAllowed":true,"ocoAllowed":true,"quoteOrderQtyMarketAllowed":true,"allowTrailingStop":true,"cancelReplaceAllowed":true,"isSpotTradingAllowed":true,"isMarginTradingAllowed":true,"filters":[{"filterType":"PRICE_FILTER","minPrice":"0.01000000","maxPrice":"1000000.00000000","tickSize":"0.01000000"},{"filterType":"PERCENT_PRICE","multiplierUp":"5","multiplierDown":"0.2","avgPriceMins":5},{"filterType":"LOT_SIZE","minQty":"0.00001000","maxQty":"9000.00000000","stepSize":"0.00001000"},{"filterType":"MIN_NOTIONAL","minNotional":"10.00000000","applyToMarket":true,"avgPriceMins":5},{"filterType":"ICEBERG_PARTS","limit":10},{"filterType":"MARKET_LOT_SIZE","minQty":"0.00000000","maxQty":"292.86757363","stepSize":"0.00000000"},{"filterType":"TRAILING_DELTA","minTrailingAboveDelta":10,"maxTrailingAboveDelta":2000,"minTrailingBelowDelta":10,"maxTrailingBelowDelta":2000},{"filterType":"MAX_NUM_ORDERS","maxNumOrders":200},{"filterType":"MAX_NUM_ALGO_ORDERS","maxNumAlgoOrders":5}],"permissions":["SPOT","MARGIN","TRD_GRP_004"]}]}

길어서 JSON.stringify를 했더니 한눈에 잘 안 들어오네요.

위와 같은 데이터 구조를 가집니다.

이 중에서 symbols는 api를 호출할 때 넣었던 심볼 수만큼 배열로 응답하게 됩니다.

저는 BTCUSDT 하나만 넣었었죠. 그래서 사이즈가 [1]입니다.

 

symbols 항목을 펼쳐보면, 하위에 많은 정보들이 있습니다.

모두 의미가 있겠지만, 지금은 filters 부분만 보겠습니다.

여러 가지 필터들이 있지만, filterType이 LOT_SIZE인 항목만 살펴보겠습니다.

최소 수량이 0.00001개네요. 최대는 9000개인데 9천 BTC가 얼마야? 부럽네요.

주문 단위도 최소 수량과 동일하게 0.00001입니다. 제 경험상 최소 수량과 주문 단위는 같더라고요.

(아닐 수도 있으니 항상 체크해보세요.)

 

어쨌든 0.00001개는 팔아야 한다는 말이네요.

그러면 그렇게 팔면 팔릴까요? 해보겠습니다.

trade.sell('BTCUSDT', '0.00001', '30000')
data: { code: -1013, msg: 'Filter failure: MIN_NOTIONAL' }

이번에는 MIN_NOTIONAL 필터에 걸렸습니다.

그러면 다시 살펴봐야겠죠? 설명과 API 조회해서 찾아보시면 됩니다.

결론부터 말하자면 최소 주문금액을 충족하지 못했습니다. 바이낸스는 10달러가 최소 주문금액입니다.

 

30000 USDT * 0.00001개 = 0.3 USDT 밖에 안됩니다.

API 조회하면 최소 10 이상이어야 한다고 나옵니다.

그러면 가격을 올리던지 수량을 올려야겠죠? 수량을 0.0004로 늘려보겠습니다.

그러면 12 달러 정도로 계산되니까 주문이 들어갈 것 같습니다.

 

{
  symbol: 'BTCUSDT',
  orderId: 12143700038,
  orderListId: -1,
  clientOrderId: 'Uz7v57KvGL3B5Dl1hPKMk8',
  transactTime: 1659627954207,
  price: '30000.00000000',
  origQty: '0.00040000',
  executedQty: '0.00000000',
  cummulativeQuoteQty: '0.00000000',
  status: 'NEW',
  timeInForce: 'GTC',
  type: 'LIMIT',
  side: 'SELL',
  fills: []
}

짜잔! 성공입니다.

 

알고 보면 별거 아닌 바이낸스 필터인데, 저는 처음에 이 부분이 참 번거롭더라고요.

여러분은 이 글 참조하셔서 쉽게 넘어가시길 바랍니다.

 

바이낸스 매도 주문도 해보았습니다.

더불어 바이낸스 API 호출 시 fliters 제약에 대한 부분도 살펴보았고요.

 

다음 글은 뭘로 할지 조금 고민이긴 하네요.

실제로 동작하는 봇을 만들어 나가는 글을 적어볼까 싶기도 합니다.

자바로 바이낸스 API 연계하는 부분을 먼저 해볼까 싶기도 하네요.

 

뭐가 되었든 가급적 꾸준하게 글을 적도록 하겠습니다.