4
집중하다
1064
수행원

DEX 거래소 퀀트 실전 (1) - dYdX v4 사용 가이드

만든 날짜: 2024-12-24 17:09:32, 업데이트 날짜: 2024-12-26 21:41:46
comments   0
hits   233

[TOC]

DEX 거래소 퀀트 실전 (1) - dYdX v4 사용 가이드

서문

탈중앙화 거래소(DEX)가 암호화폐 거래 분야에서 빠르게 성장하면서 퀀트 트레이더들도 이러한 플랫폼을 활용한 효율적인 자동화 거래로 전환하고 있습니다. dYdX는 가장 인기 있는 탈중앙화 거래 플랫폼 중 하나로, 강력한 거래 기능과 영구 선물 거래를 지원하며 최신 버전인 v4에서는 성능과 사용자 경험이 더욱 개선되어 많은 알고리즘 트레이더들의 주력 플랫폼으로 자리매김했습니다.

본 아티클에서는 dYdX v4에서의 알고리즘 트레이딩 실전 적용 방법을 소개합니다. API를 이용한 거래 실행, 시장 데이터 수집, 계좌 관리 등의 핵심 내용을 다룹니다.

  • 테스트 환경 전환
  • 시장 정보 조회
  • 주문 정보, 포지션 정보 확인
  • 주문 실행
  • 서브 계좌 관리
  • 노드 메소드 요청

dYdX v4 DEX

  • dYdX 테스트넷 앱 페이지

DEX 거래소 퀀트 실전 (1) - dYdX v4 사용 가이드

  • dYdX v3와 마찬가지로 거래 시 dYdX 토큰 보상이 발생합니다.

DEX 거래소 퀀트 실전 (1) - dYdX v4 사용 가이드

지갑 연결, 로그인, 설정 정보

기존 dYdX v3 프로토콜 DEX 거래소는 서비스가 종료되었으며, 현재 dYdX v4 앱 주소는 다음과 같습니다:

https://dydx.trade/trade/ETH-USD

앱 페이지 접속 후 우측 상단의 지갑 연결 버튼을 통해 QR 코드 스캔으로 지갑을 연결할 수 있습니다.

테스트넷 환경에서 먼저 기능을 익히고 싶은 경우 다음 테스트넷을 이용하시기 바랍니다:

https://v4.testnet.dydx.exchange/trade/ETH-USD

동일하게 우측 상단 지갑 연결 버튼을 클릭한 후 서명 인증을 완료하면 됩니다. 지갑 연결 성공 시 자동으로 dydx v4 주소가 생성되며, 앱 페이지 우측 상단에 해당 주소가 표시됩니다. 해당 주소 클릭 시 입금, 출금, 자산 이체 등의 메뉴가 나타납니다. dYdX 메인넷(프로덕션 환경)과 테스트넷의 주요 차이점 중 하나는 테스트넷에서 입금 버튼 클릭 시 자동으로 faucet을 통해 300 USDC 테스트 자금이 충전된다는 점입니다. 실제 거래를 위해서는 USDC 자금을 직접 입금해야 하며, 다양한 자산과 체인을 통한 입금이 지원됩니다.

  • dYdX v4 계좌 주소 dYdX v4 계좌 주소는 지갑 주소에서 파생되며, dydx1xxxxxxxxxxxxxxxxxxxxq2ge5jr4nzfeljxxxx 형식의 ‘dydx1’ 접두사로 시작하는 주소입니다. 해당 주소는 블록체인 탐색기에서 조회 가능합니다.

  • 니모닉 구문 우측 상단 메뉴에서 ‘비밀 복구 구문 내보내기’ 버튼을 클릭하면 현재 dYdX 계좌의 니모닉 구문을 확인할 수 있습니다. FMZ 플랫폼에 거래소를 추가할 때 이 니모닉 구문 설정이 필요합니다.

니모닉 구문은 FMZ 플랫폼에 직접 설정하거나 호스팅 머신에 로컬 저장할 수 있으며, 본문의 실전 예제에서 관련 사용법을 시연합니다.

메인넷, 테스트넷 차이점

테스트넷 환경은 메인넷과 몇 가지 측면에서 차이가 존재합니다. 주요 차이점을 간략히 정리합니다.

  • 서브 계좌 자산 이체 메인넷은 subAccountNumber >= 128인 서브 계좌에서 포지션이 없을 경우 자동으로 subAccountNumber 0 계좌로 자산을 이체하는 청소 메커니즘이 존재합니다. 테스트넷에서는 해당 메커니즘이 없거나(혹은 트리거 조건이 상이하여) 테스트 과정에서 작동하지 않았습니다.

  • 일부 토큰 명칭 네이티브 토큰 dydx의 명칭 차이: 메인넷 DYDX vs 테스트넷 Dv4TNT

  • 주소 설정(체인 ID, 노드 주소, 인덱서 주소 등) 주요 설정값 예시:

    • 메인넷: 인덱서 주소: https://indexer.dydx.trade 체인 ID: dydx-mainnet-1 REST 노드: https://dydx-dao-api.polkachu.com:443

    • 테스트넷: 인덱서 주소: https://indexer.v4testnet.dydx.exchange 체인 ID: dydx-testnet-4 REST 노드: https://dydx-testnet-api.polkachu.com

dYdX v4 프로토콜 아키텍처

dYdX v4 프로토콜은 Cosmos 생태계 기반으로 개발되었으며, dYdX v4 DEX 시스템의 거래 관련 구성 요소는 크게 2가지로 구분됩니다:

  • 시장 정보, 계좌 정보 조회를 담당하는 인덱서
  • dydx 블록체인의 주문 메시지, 주문 취소 메시지, 자산 이체 메시지 처리

인덱서

인덱서 서비스는 REST 프로토콜과 WebSocket 프로토콜을 제공합니다.

  • REST 프로토콜 REST 인터페이스를 통해 시장 정보 조회, 계좌 정보, 포지션 정보, 주문 정보 확인이 가능하며 FMZ 플랫폼에서는 표준화된 API 인터페이스로 래핑되어 있습니다.

  • WebSocket 프로토콜 FMZ 플랫폼에서는 Dial 함수를 이용해 WebSocket 연결을 생성하고 시장 정보 등을 구독할 수 있습니다.

dydx v4 인덱서는 중앙화 거래소와 유사한 데이터 갱신 지연 이슈가 존재합니다. 예를 들어 주문 후 즉시 조회 시 주문 정보가 나타나지 않을 수 있으므로, 특정 작업 후 Sleep(n) 함수를 이용해 수 초간 대기할 것을 권장합니다.

다음은 Dial 함수를 이용해 WebSocket API 연결을 생성하고 오더북 데이터를 구독하는 예시 코드입니다:

function dYdXIndexerWSconnManager(streamingPoint) {
    var self = {}
    self.base = streamingPoint
    self.wsThread = null

    // 구독
    self.CreateWsThread = function (msgSubscribe) {
        self.wsThread = threading.Thread(function (streamingPoint, msgSubscribe) {
            // 오더북
            var orderBook = null 

            // 오더북 업데이트
            var updateOrderbook = function(orderbook, update) {
                // bids 업데이트
                if (update.bids) {
                    update.bids.forEach(([price, size]) => {
                        const priceFloat = parseFloat(price)
                        const sizeFloat = parseFloat(size)

                        if (sizeFloat === 0) {
                            // price에 해당하는 매수 주문 삭제
orderbook.bids = orderbook.bids.filter(bid => parseFloat(bid.price) !== priceFloat)
                        } else {
                            // 매수 주문 업데이트 또는 추가
                            orderbook.bids = orderbook.bids.filter(bid => parseFloat(bid.price) !== priceFloat)
                            orderbook.bids.push({price: price, size: size})
                            // 가격 기준 내림차순 정렬
                            orderbook.bids.sort((a, b) => parseFloat(b.price) - parseFloat(a.price))
                        }
                    })
                }

                // asks 업데이트
                if (update.asks) {
                    update.asks.forEach(([price, size]) => {
                        const priceFloat = parseFloat(price)
                        const sizeFloat = parseFloat(size)

                        if (sizeFloat === 0) {
                            // 가격이 price인 매도 주문 삭제
                            orderbook.asks = orderbook.asks.filter(ask => parseFloat(ask.price) !== priceFloat)
                        } else {
                            // 매도 주문 업데이트 또는 추가
                            orderbook.asks = orderbook.asks.filter(ask => parseFloat(ask.price) !== priceFloat)
                            orderbook.asks.push({price: price, size: size})
                            // 가격 기준 오름차순 정렬
                            orderbook.asks.sort((a, b) => parseFloat(a.price) - parseFloat(b.price))
                        }
                    })
                }

                return orderbook
            }

            var conn = Dial(`${streamingPoint}|reconnect=true&payload=${JSON.stringify(msgSubscribe)}`)
            if (!conn) {
                Log("createWsThread failed.")
                return
            }
            while (true) {
                var data = conn.read()
                if (data) {
                    var msg = null                    
                    try {
                        msg = JSON.parse(data)
                        if (msg["type"] == "subscribed") {
                            orderBook = msg["contents"]
                            threading.currentThread().postMessage(orderBook)
                        } else if (msg["type"] == "channel_data") {
                            orderBook = updateOrderbook(orderBook, msg["contents"])
                            threading.currentThread().postMessage(orderBook)
                        }
                    } catch (e) {
                        Log("e.name:", e.name, "e.stack:", e.stack, "e.message:", e.message)
                    }
                }
            }
        }, streamingPoint, msgSubscribe)
    }

    // 리스닝
    self.Peek = function () {
        return self.wsThread.peekMessage()
    }

    return self
}

function main() {
    // 실서버 : wss://indexer.dydx.trade/v4/ws
    // 테스트넷 : wss://indexer.v4testnet.dydx.exchange/v4/ws

    var symbol = "ETH-USD"
    var manager = dYdXIndexerWSconnManager("wss://indexer.dydx.trade/v4/ws")
매니저.크리에이트Ws스레드({"type": "subscribe", "channel": "v4_orderbook", "id": 심볼})

    var 레드코드 = "#FF0000"
    var 그린코드 = "#006400"
    while (true) {
        var 뎁스테이블 = {type: "table", title: 심볼 + " / 뎁스", cols: ["레벨", "가격", "수량"], rows: []}
        var 뎁스 = 매니저.픽()
        if (뎁스) {
            for (var i = 0; i < 뎁스.asks.length; i++) {
                if (i > 9) {
                    break
                }
                var ask = 뎁스.asks[i]
                뎁스테이블.rows.push(["매도호가 " + (i + 1) + 그린코드, ask.price + 그린코드, ask.size + 그린코드])
            }
            뎁스테이블.rows.reverse()

            for (var i = 0; i < 뎁스.bids.length; i++) {
                if (i > 9) {
                    break
                }
                var bid = 뎁스.bids[i]
                뎁스테이블.rows.push(["매수호가 " + (i + 1) + 레드코드, bid.price + 레드코드, bid.size + 레드코드])
            }
        }
        로그상태(_D(), "\n`" + JSON.stringify(뎁스테이블) + "`")
    }
}

dYdX 체인 노드 메시지 브로드캐스팅

거래에서 가장 빈번히 사용되는 메시지 유형은 주문 메시지, 주문 취소 메시지, 자금 이체 메시지입니다.

  • 주문 메시지 요약

    {
    "@type": "/dydxprotocol.clob.MsgPlaceOrder",
    "order": {
      "orderId": {
        "subaccountId": {
          "owner": "xxx"
        },
        "clientId": xxx,
        "orderFlags": 64,
        "clobPairId": 1
      },
      "side": "SIDE_BUY",
      "quantums": "2000000",
      "subticks": "3500000000",
      "goodTilBlockTime": 1742295981
    }
    }
    
    • 리미트 오더: FMZ 플랫폼에서 캡슐화된 주문 함수 사용 시, 리미트 오더의 orderFlags 값은 ORDER_FLAGS_LONG_TERM = 64 # 롱텀 오더로 설정됩니다. dydx v4 프로토콜 제약에 따라 최대 유효기간(90일)이 적용되며, 모든 dydx v4 오더 유형은 유효기간을 갖습니다.
    • 마켓 오더: FMZ 플랫폼의 마켓 오더 함수 사용 시 orderFlags 값은 ORDER_FLAGS_SHORT_TERM = 0 # 숏텀 오더로 설정됩니다. dydx v4 프로토콜 권장사항:

    // 매도의 경우 오라클 가격 -5%, 매수의 경우 오라클 가격 +5%로 설정 권장

    진정한 의미의 마켓 오더가 아니므로 오라클 가격에 5% 슬리피지를 적용합니다. 숏텀 오더의 유효기간 설정은 블록 높이 기반으로, 현재 블록 +10 블록 높이로 설정됩니다.

    • 오더 ID: 체인에서 직접 실행되는 주문 특성상 인덱서 생성 오더 ID가 존재하지 않습니다. 고유성 보장 및 조회 정확성을 위해 다음 요소들을 영문 콤마로 구분하여 구성합니다:

      • 거래 쌍
      • dydx 현 계정 주소
      • 서브어카운트 번호 (subaccountNumber)
      • clientId (랜덤 생성)
      • clobPairId (상품 ID)
      • orderFlags
      • goodTilData (밀리초)
  • 주문 취소 메시지 요약

    {
    "@type": "/dydxprotocol.clob.MsgCancelOrder",
    "orderId": {
      "subaccountId": {
        "owner": "xxx"
      },
      "clientId": 2585872024,
      "orderFlags": 64,
      "clobPairId": 1
    },
    "goodTilBlockTime": 1742295981
    }
    

FMZ 플랫폼 주문 인터페이스에서 반환된 오더 ID를 입력해야 합니다.

  • 자금 이체 메시지 요약
  {
    "@type": "/dydxprotocol.sending.MsgCreateTransfer",
    "transfer": {
      "sender": {
        "owner": "xxx"
      },
      "recipient": {
        "owner": "xxx",
        "number": 128
      },
      "amount": "10000000"
    }
  }

dydx v4 주소 하위에 다수 서브어카운트 생성 가능하며, subAccountNumber 0은 최초 생성된 메인 어카운트입니다. 128 이상의 서브어카운트는 격리 마진 거래에 사용되며 최소 20 USDC 자금이 필요합니다. 예시: subAccountNumber 0 -> 128 이체 또는 128 -> 0 이체 가능. 이체 시 가스비 발생하며 USDC 또는 dydx 토큰으로 결제 가능합니다.

FMZ 플랫폼 dYdX v4 실습

앞서 설명한 래핑 세부사항을 바탕으로 구체적인 사용 방법을 실습해 보겠습니다. 본 가이드에서는 dYdX v4 테스트넷을 데모 환경으로 사용하며, 테스트넷은 메인넷과 거의 동일한 구조를 가지며 자동 화이트 기능으로 테스트 자산을 획득할 수 있습니다. 호스팅 서비스 배포 과정은 생략하고 FMZ 플랫폼에서 실제 거래 테스트를 진행합니다.

1. 설정 구성

암호화폐 지갑(본 예시에서는 imToken 지갑 사용)으로 dYdX v4 앱에 성공적으로 연결한 후 테스트 자산을 수령하고, 현재 dYdX v4 계정(지갑에서 파생된)의 니모닉 구문을 추출합니다.

DEX 거래소 퀀트 실전 (1) - dYdX v4 사용 가이드

추출한 니모닉 구문을 FMZ 플랫폼에 설정합니다. 본 예시에서는 로컬 파일 방식을 사용하며(직접 입력도 가능, 플랫폼에 암호화된 형태로 설정되므로 평문 노출 없음):

  • 니모닉 파일: mnemonic.txt

DEX 거래소 퀀트 실전 (1) - dYdX v4 사용 가이드

호스팅 서비스 디렉토리 내 실제 거래 ID 폴더에 위치시킵니다(다른 디렉토리 사용 시 경로 지정 필요).

  • FMZ 플랫폼에서 거래소 설정

https://www.fmz.com/m/platforms/add

니모닉 구문 입력란에 file:///mnemonic.txt 작성. 실제 경로: 호스팅_서비스_디렉토리/logs/storage/594291.

DEX 거래소 퀀트 실전 (1) - dYdX v4 사용 가이드

2. dydx v4 테스트넷 전환

function main() {
    // 테스트넷 체인의 인덱서 주소로 전환
    exchange.SetBase("https://indexer.v4testnet.dydx.exchange")

    // 테스트넷 체인의 ChainId 설정 
    exchange.IO("chainId", "dydx-testnet-4")

    // 테스트넷 체인의 REST 노드 주소 전환
    exchange.IO("restApiBase", "https://dydx-testnet-api.polkachu.com")

    // 계정 정보 읽기 테스트
    Log(exchange.GetAccount()) 
}

테스트넷 계정 정보 확인 결과:

{
	"Info": {
		"subaccounts": [{
			"address": "dydx1fzsndj35a26maujxff88q2ge5jr4nzfeljn2ez",
			"subaccountNumber": 0,
			"equity": "300.386228",
			"latestProcessedBlockHeight": "28193227",
			"freeCollateral": "300.386228",
			"openPerpetualPositions": {},
			"assetPositions": {
				"USDC": {
					"subaccountNumber": 0,
					"size": "300.386228",
					"symbol": "USDC",
					"side": "LONG",
					"assetId": "0"
				}
			},
			"marginEnabled": true,
			"updatedAtHeight": "28063818"
		}, {
			"address": "dydx1fzsndj35a26maujxff88q2ge5jr4nzfeljn2ez",
			"equity": "0",
			"freeCollateral": "0",
			"openPerpetualPositions": {},
			"marginEnabled": true,
			"subaccountNumber": 1,
			"assetPositions": {},
			"updatedAtHeight": "27770289",
			"latestProcessedBlockHeight": "28193227"
		}, {
			"equity": "0",
			"openPerpetualPositions": {},
			"marginEnabled": true,
			"updatedAtHeight": "28063818",
			"latestProcessedBlockHeight": "28193227",
			"subaccountNumber": 128,
			"freeCollateral": "0",
			"assetPositions": {},
			"address": "dydx1fzsndj35a26maujxff88q2ge5jr4nzfeljn2ez"
		}],
		"totalTradingRewards": "0.021744179376211564"
	},
	"Stocks": 0,
	"FrozenStocks": 0,
	"Balance": 300.386228,
	"FrozenBalance": 0,
	"Equity": 300.386228,
	"UPnL": 0
}

3. 시장 정보 조회

메인넷 환경에서 테스트 수행 (테스트넷 미전환 상태)

function main() {
    var markets = exchange.GetMarkets()
    if (!markets) {
        throw "get markets error"
    }
    var tbl = {type: "table", title: "test markets", cols: ["key", "Symbol", "BaseAsset", "QuoteAsset", "TickSize", "AmountSize", "PricePrecision", "AmountPrecision", "MinQty", "MaxQty", "MinNotional", "MaxNotional", "CtVal"], rows: []}
    for (var symbol in markets) {
        var market = markets[symbol]
        tbl.rows.push([symbol, market.Symbol, market.BaseAsset, market.QuoteAsset, market.TickSize, market.AmountSize, market.PricePrecision, market.AmountPrecision, market.MinQty, market.MaxQty, market.MinNotional, market.MaxNotional, market.CtVal])
    }
```js
function main() {
    // 테스트넷 인덱서 주소 변경
    exchange.SetBase("https://indexer.v4testnet.dydx.exchange")

    // 테스트넷 ChainId 설정
    exchange.IO("chainId", "dydx-testnet-4")

    // 테스트넷 REST 노드 주소 변경
    exchange.IO("restApiBase", "https://dydx-testnet-api.polkachu.com")

    // 지정가 주문, 매도 주문
    var idSell = exchange.CreateOrder("ETH_USD.swap", "sell", 4000, 0.002)
    var idBuy = exchange.CreateOrder("ETH_USD.swap", "buy", 3000, 0.003)

    // 시장가 주문
    var idMarket = exchange.CreateOrder("ETH_USD.swap", "buy", -1, 0.01)

    Log("idSell:", idSell)
    Log("idBuy:", idBuy)
    Log("idMarket:", idMarket)
}

DEX 거래소 퀀트 실전 (1) - dYdX v4 사용 가이드

dYdX v4 앱 페이지:

DEX 거래소 퀀트 실전 (1) - dYdX v4 사용 가이드

5. 주문 정보

테스트넷에 미체결 주문 조회 및 취소 테스트:

function main() {    
    // 테스트넷 인덱서 주소 변경
    exchange.SetBase("https://indexer.v4testnet.dydx.exchange")

    // 테스트넷 ChainId 설정
    exchange.IO("chainId", "dydx-testnet-4")

    // 테스트넷 REST 노드 주소 변경
    exchange.IO("restApiBase", "https://dydx-testnet-api.polkachu.com")

    var orders = exchange.GetOrders()
    Log("orders:", orders)
    for (var order of orders) {
        exchange.CancelOrder(order.Id, order)
        Sleep(2000)
    }

    var tbl = {type: "table", title: "GetOrders 테스트", cols: ["주문ID", "가격", "수량", "체결수량", "평균가", "상태", "유형", "오프셋", "계약유형"], rows: []}
    for (var order of orders) {
        tbl.rows.push([order.Id, order.Price, order.Amount, order.DealAmount, order.AvgPrice, order.Status, order.Type, order.Offset, order.ContractType])
    }
    LogStatus("`" + JSON.stringify(tbl) +  "`")
}

DEX 거래소 퀀트 실전 (1) - dYdX v4 사용 가이드

6. 포지션 정보 조회

function main() {
    // 테스트넷 인덱서 주소 변경
    exchange.SetBase("https://indexer.v4testnet.dydx.exchange")

    // 테스트넷 ChainId 설정
    exchange.IO("chainId", "dydx-testnet-4")

    // 테스트넷 REST 노드 주소 변경
    exchange.IO("restApiBase", "https://dydx-testnet-api.polkachu.com")

    var p1 = exchange.GetPositions("USD.swap")
    var p2 = exchange.GetPositions("ETH_USD.swap")
    var p3 = exchange.GetPositions()
    var p4 = exchange.GetPositions("SOL_USD.swap")

    var tbls = []
    for (var positions of [p1, p2, p3, p4]) {
        var tbl = {type: "table", title: "GetPosition/GetPositions 테스트", cols: ["심볼", "수량", "가격", "동결수량", "유형", "수익", "증거금", "계약유형", "증거금률"], rows: []}
        for (var p of positions) {
            tbl.rows.push([p.Symbol, p.Amount, p.Price, p.FrozenAmount, p.Type, p.Profit, p.Margin, p.ContractType, p.MarginLevel])
        } 
        tbls.push(tbl)
    }

    LogStatus("`" + JSON.stringify(tbls) +  "`")
}

DEX 거래소 퀀트 실전 (1) - dYdX v4 사용 가이드

7. 서브 계정 관리

function main() {
    // 테스트넷 인덱서 주소 변경
    exchange.SetBase("https://indexer.v4testnet.dydx.exchange")

    // 테스트넷 ChainId 설정
    exchange.IO("chainId", "dydx-testnet-4")

    // 테스트넷 REST 노드 주소 변경
exchange.IO("restApiBase", "https://dydx-testnet-api.polkachu.com")

    // 서브 계정 번호 0 -> 128 : 20 USDC 전송, Gas Fee는 adv4tnt (dydx 토큰)
    var ret = exchange.IO("transferUSDCToSubaccount", 0, 128, "adv4tnt", 20)  
    Log("ret:", ret)

    // 서브 계정 번호 128로 전환 후 계좌 정보 조회
    exchange.IO("subAccountNumber", 128)

    var account = exchange.GetAccount()
    Log("account:", account)
}

DEX 거래소 퀀트 실전 (1) - dYdX v4 사용 가이드

subAccountNumber 128 서브 계정으로 전환 후 GetAccount 반환 데이터:

{
	"Info": {
		"subaccounts": [{
			"subaccountNumber": 0,
			"assetPositions": {
				"USDC": {
					"size": "245.696892",
					"symbol": "USDC",
					"side": "LONG",
					"assetId": "0",
					"subaccountNumber": 0
				}
			},
			"updatedAtHeight": "28194977",
			"latestProcessedBlockHeight": "28195008",
			"address": "dydx1fzsndj35a26maujxff88q2ge5jr4nzfeljn2ez",
			"freeCollateral": "279.5022142346",
			"openPerpetualPositions": {
				"ETH-USD": {
					"closedAt": null,
					"size": "0.01",
					"maxSize": "0.01",
					"exitPrice": null,
					"unrealizedPnl": "-0.17677323",
					"subaccountNumber": 0,
					"status": "OPEN",
					"createdAt": "2024-12-26T03:36:09.264Z",
					"createdAtHeight": "28194494",
					"sumClose": "0",
					"netFunding": "0",
					"market": "ETH-USD",
					"side": "LONG",
					"entryPrice": "3467.2",
					"realizedPnl": "0",
					"sumOpen": "0.01"
				}
			},
			"marginEnabled": true,
			"equity": "280.19211877"
		}, {
			"openPerpetualPositions": {},
			"assetPositions": {},
			"marginEnabled": true,
			"latestProcessedBlockHeight": "28195008",
			"address": "dydx1fzsndj35a26maujxff88q2ge5jr4nzfeljn2ez",
			"subaccountNumber": 1,
			"equity": "0",
			"freeCollateral": "0",
			"updatedAtHeight": "27770289"
		}, {
			"openPerpetualPositions": {},
			"updatedAtHeight": "28194977",
			"latestProcessedBlockHeight": "28195008",
			"address": "dydx1fzsndj35a26maujxff88q2ge5jr4nzfeljn2ez",
			"subaccountNumber": 128,
			"assetPositions": {
				"USDC": {
					"assetId": "0",
					"subaccountNumber": 128,
					"size": "20",
					"symbol": "USDC",
					"side": "LONG"
				}
			},
			"marginEnabled": true,
			"equity": "20",
			"freeCollateral": "20"
		}],
		"totalTradingRewards": "0.021886899964446858"
	},
	"Stocks": 0,
	"FrozenStocks": 0,
	"Balance": 20,
	"FrozenBalance": 0,
	"Equity": 20,
	"UPnL": 0
}

subAccountNumber 128 서브 계정에 20 USDC가 입금된 것을 확인할 수 있습니다.

8. TxHash 획득 및 REST 노드 API 호출

주문 기준 TxHash 획득 방법, IO를 통한 REST 노드 호출 테스트

주문의 TxHash 획득 방법: 거래소 객체 dydx는 TxHash를 캐시하며 주문 ID로 조회 가능합니다. 단 전략 중단 시 캐시된 주문 tx 해시 매핑은 초기화됩니다.

function main() {
    // 테스트넷 인덱서 주소 변경
    exchange.SetBase("https://indexer.v4testnet.dydx.exchange")

    // 테스트넷 ChainId 설정 
    exchange.IO("chainId", "dydx-testnet-4")

    // 테스트넷 REST 노드 주소 변경
    exchange.IO("restApiBase", "https://dydx-testnet-api.polkachu.com")

    var id1 = exchange.CreateOrder("ETH_USD.swap", "buy", 3000, 0.002)
    var hash1 = exchange.IO("getTxHash", id1)
    Log("id1:", id1, "hash1:", hash1)
var id2 = exchange.CreateOrder("ETH_USD.swap", "buy", 2900, 0.003)
    var hash2 = exchange.IO("getTxHash", id2)
    Log("id2:", id2, "hash2:", hash2)
    
    // 매핑 테이블 초기화는 다음을 사용: exchange.IO("getTxHash", "")
    var arr = [hash1, hash2]
    
    Sleep(10000)
    for (var txHash of arr) {
        // GET https://docs.cosmos.network   /cosmos/tx/v1beta1/txs/{hash}
        var ret = exchange.IO("api", "GET", "/cosmos/tx/v1beta1/txs/" + txHash)
        Log("ret:", ret)
    }
}

DEX 거래소 퀀트 실전 (1) - dYdX v4 사용 가이드

TxHash 조회를 통해 확인된 메시지:

var ret = exchange.IO(“api”, “GET”, “/cosmos/tx/v1beta1/txs/” + txHash)

내용이 너무 길어 일부만 발췌하여 예시:

{
	"tx_response": {
		"codespace": "",
		"code": 0,
		"logs": [],
		"info": "",
		"height": "28195603",
		"data": "xxx",
		"raw_log": "",
		"gas_wanted": "-1",
		"gas_used": "0",
		"tx": {
			"@type": "/cosmos.tx.v1beta1.Tx",
			"body": {
				"messages": [{
					"@type": "/dydxprotocol.clob.MsgPlaceOrder",
					"order": {
						"good_til_block_time": 1742961542,
						"condition_type": "CONDITION_TYPE_UNSPECIFIED",
						"order_id": {
							"clob_pair_id": 1,
							"subaccount_id": {
								"owner": "xxx",
								"number": 0
							},
							"client_id": 2999181974,
							"order_flags": 64
						},
						"side": "SIDE_BUY",
						"quantums": "3000000",
						"client_metadata": 0,
						"conditional_order_trigger_subticks": "0",
						"subticks": "2900000000",
						"time_in_force": "TIME_IN_FORCE_UNSPECIFIED",
						"reduce_only": false
					}
				}],
				"memo": "FMZ",
				"timeout_height": "0",
				"extension_options": [],
				"non_critical_extension_options": []
			},
      ...

테스트 종료

상기 테스트는 최신 호스팅 버전을 기반으로 수행되었으며, dYdX v4 DEX 지원을 위해 최신 호스팅 프로그램 다운로드가 필요합니다

지원해 주셔서 감사합니다. 감사합니다.