Práctica Cuantitativa en DEX (1): Guía de dYdX v4

Creado el: 2024-12-24 17:09:32, Actualizado el: 2024-12-26 21:41:46
comments   0
hits   233

[TOC]

Práctica Cuantitativa en DEX (1): Guía de dYdX v4

Prefacio

Con el rápido auge de los exchanges descentralizados (DEX) en el ámbito de trading de criptomonedas, los traders cuantitativos han comenzado a migrar gradualmente hacia estas plataformas para realizar operaciones automatizadas eficientes. dYdX, como una de las plataformas de trading descentralizado más populares, ofrece funcionalidades robustas para transacciones, soportando contratos perpetuos de futuros. Su versión más reciente, v4, optimiza el rendimiento y la experiencia de usuario, convirtiéndose en la elección principal de muchos traders algorítmicos.

Este artículo explorará cómo implementar estrategias de trading cuantitativo en dYdX v4, incluyendo el uso de su API para operar, obtener datos de mercado y gestionar cuentas.

  • Cambio a entorno de pruebas
  • Consulta de información de mercado
  • Consulta de órdenes y posiciones abiertas
  • Ejecución de órdenes
  • Gestión de subcuentas
  • Solicitudes a métodos de nodos

dYdX v4 DEX

  • Interfaz de la App en Testnet

Práctica Cuantitativa en DEX (1): Guía de dYdX v4

  • Al igual que en dYdX v3, las transacciones generan recompensas en tokens dYdX.

Práctica Cuantitativa en DEX (1): Guía de dYdX v4

Conexión de wallet, autenticación y configuración

El protocolo anterior dYdX v3 para el exchange DEX ha sido descontinuado. La dirección actual de la App dYdX v4 es:

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

Tras abrir la App, el botón para conectar wallet se encuentra en la esquina superior derecha. Escanee el código QR para vincular su wallet.

Para realizar pruebas en el entorno de testnet, utilice:

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

El proceso es similar: conéctese mediante el botón superior derecho, autentique con firma digital. Tras una conexión exitosa, se generará automáticamente una dirección dYdX v4 visible en el menú superior. Desde allí se acceden operaciones como depósito, retiro y transferencias. Una diferencia clave entre mainnet y testnet es que en testnet, al hacer clic en “Depositar”, se reciben automáticamente 300 USDC de prueba mediante un faucet. Para trading real en mainnet, se requiere depositar USDC reales, proceso compatible con múltiples activos y cadenas.

  • Dirección de cuenta en dYdX v4 Derivada de la dirección del wallet, tiene el formato dydx1xxxxxxxxxxxxxxxxxxxxq2ge5jr4nzfeljxxxx (prefijo dydx1). Puede consultarse en exploradores de blockchain.

  • Frase semilla (mnemonic) Desde el menú superior, seleccione “Exportar frase secreta” para obtener el mnemonic de la cuenta. En la plataforma FMZ, este se configura al añadir el exchange.

El mnemonic puede almacenarse directamente en FMZ o gestionarse localmente. En las secciones prácticas demostraremos su uso mediante objetos de exchange dydx v4.

Diferencias entre mainnet y testnet

El entorno de testnet presenta algunas variaciones respecto a mainnet:

  • Transferencias entre subcuentas. En mainnet existe un mecanismo de consolidación para subcuentas con subAccountNumber >= 128: si no tienen posiciones abiertas, sus fondos se transfieren automáticamente a la subcuenta 0. En testnet, este comportamiento no se observó (o tiene condiciones de activación diferentes).

  • Nomenclatura de tokens. El token nativo difiere: DYDX en mainnet vs Dv4TNT en testnet.

  • Configuración de endpoints (chain IDs, nodos REST, etc.). Ejemplos de configuración:

    • Mainnet: Endpoint del indexador: https://indexer.dydx.trade Chain ID: dydx-mainnet-1 Nodo REST: https://dydx-dao-api.polkachu.com:443

    • Testnet: Endpoint del indexador: https://indexer.v4testnet.dydx.exchange Chain ID: dydx-testnet-4 Nodo REST: https://dydx-testnet-api.polkachu.com

Arquitectura del protocolo dYdX v4

El protocolo dYdX v4 se desarrolló sobre el ecosistema Cosmos. Su sistema de trading consta de dos componentes principales:

  1. Indexador: Proporciona datos de mercado, información de cuentas, etc.
  2. Blockchain dydx: Maneja órdenes, cancelaciones, transferencias.

Indexador

El servicio del indexador ofrece APIs REST y WebSocket.

  • API REST Permite consultar datos de mercado, estados de cuenta, posiciones y órdenes. En FMZ, estas interfaces están encapsuladas como APIs unificadas.

  • WebSocket Utilice la función Dial en FMZ para establecer conexiones WebSocket y suscribirse a flujos de datos.

Nota crítica: El indexador de dYdX v4 comparte una limitación común con exchanges centralizados: la latencia en actualizaciones de datos. Por ejemplo, consultar una orden inmediatamente después de crearla podría no reflejarla. Se recomienda introducir retardos (Sleep(n)) tras operaciones críticas.

Ejemplo de conexión WebSocket para obtener el libro de órdenes:

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

    // Suscripción
    self.CreateWsThread = function (msgSubscribe) {
        self.wsThread = threading.Thread(function (streamingPoint, msgSubscribe) {
            // Libro de órdenes
            var orderBook = null 

            // Actualizar libro
            var updateOrderbook = function(orderbook, update) {
                // Actualizar bids
                if (update.bids) {
                    update.bids.forEach(([price, size]) => {
                        const priceFloat = parseFloat(price)
                        const sizeFloat = parseFloat(size)

                        if (sizeFloat === 0) {
                            // Eliminar bid a precio 'price'
```javascript  
orderbook.bids = orderbook.bids.filter(bid => parseFloat(bid.price) !== priceFloat)  
                        } else {  
                            // Actualizar o agregar orden de compra  
                            orderbook.bids = orderbook.bids.filter(bid => parseFloat(bid.price) !== priceFloat)  
                            orderbook.bids.push({price: price, size: size})  
                            // Ordenar por precio en orden descendente  
                            orderbook.bids.sort((a, b) => parseFloat(b.price) - parseFloat(a.price))  
                        }  
                    })  
                }  

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

                        if (sizeFloat === 0) {  
                            // Eliminar orden de venta con precio price  
                            orderbook.asks = orderbook.asks.filter(ask => parseFloat(ask.price) !== priceFloat)  
                        } else {  
                            // Actualizar o agregar orden de venta  
                            orderbook.asks = orderbook.asks.filter(ask => parseFloat(ask.price) !== priceFloat)  
                            orderbook.asks.push({price: price, size: size})  
                            // Ordenar por precio en orden ascendente  
                            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)  
    }  

    // Monitorizar mensajes  
    self.Peek = function () {  
        return self.wsThread.peekMessage()  
    }  

    return self  
}  

function main() {  
    // real: wss://indexer.dydx.trade/v4/ws  
    // simulación: wss://indexer.v4testnet.dydx.exchange/v4/ws  

    var symbol = "ETH-USD"  
    var manager = dYdXIndexerWSconnManager("wss://indexer.dydx.trade/v4/ws")  

manager.CreateWsThread({“type”: “subscribe”, “channel”: “v4_orderbook”, “id”: symbol})

var redCode = "#FF0000"
var greenCode = "#006400"
while (true) {
    var depthTbl = {type: "table", title: symbol + " / depth", cols: ["level", "price", "amount"], rows: []}
    var depth = manager.Peek()
    if (depth) {
        for (var i = 0; i < depth.asks.length; i++) {
            if (i > 9) {
                break
            }
            var ask = depth.asks[i]
            depthTbl.rows.push(["asks " + (i + 1) + greenCode, ask.price + greenCode, ask.size + greenCode])
        }
        depthTbl.rows.reverse()

        for (var i = 0; i < depth.bids.length; i++) {
            if (i > 9) {
                break
            }
            var bid = depth.bids[i]
            depthTbl.rows.push(["bids " + (i + 1) + redCode, bid.price + redCode, bid.size + redCode])
        }
    }
    LogStatus(_D(), "\n`" + JSON.stringify(depthTbl) + "`")
}

}


### Mensajes de difusión en nodos de la cadena dYdX  
Los tipos más utilizados en transacciones son: mensajes de órdenes, cancelaciones y transferencias.  

- Resumen de mensajes de órdenes  
  ```JSON
  {
    "@type": "/dydxprotocol.clob.MsgPlaceOrder",
    "order": {
      "orderId": {
        "subaccountId": {
          "owner": "xxx"
        },
        "clientId": xxx,
        "orderFlags": 64,
        "clobPairId": 1
      },
      "side": "SIDE_BUY",
      "quantums": "2000000",
      "subticks": "3500000000",
      "goodTilBlockTime": 1742295981
    }
  }
  • Orden limitada:
    En las funciones encapsuladas por FMZ, se utiliza el valor ORDER_FLAGS_LONG_TERM = 64 # Orden de largo plazo. Según las limitaciones del protocolo dydx v4, se aplica el plazo máximo de validez de 90 días (todas las órdenes en dydx v4 tienen caducidad).

  • Orden de mercado:
    Las funciones de FMZ utilizan ORDER_FLAGS_SHORT_TERM = 0 # Orden de corto plazo. Siguiendo la recomendación del protocolo:

    // Recomendado establecer precio del oráculo -5% para VENTA, +5% para COMPRA

    Al no ser órdenes de mercado reales, se utiliza el precio del oráculo con un deslizamiento del 5%. Las órdenes de corto plazo usan expiración por altura de bloque, configurada como bloque actual +10 según sugerencia del protocolo.

  • ID de orden:
    Al ejecutarse directamente en cadena, no se genera OrderID por índices. Para garantizar unicidad y precisión en consultas, el ID se compone de (separados por comas):

    • Par de trading
    • Dirección de cuenta en dydx
    • Número de subcuenta (subaccountNumber)
    • clientId (generado aleatoriamente)
    • clobPairId (ID del instrumento)
    • orderFlags
    • goodTilData (milisegundos)
  • Resumen de mensajes de cancelación

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

Requiere el OrderID devuelto por la API de órdenes de FMZ.

  • Resumen de mensajes de transferencia
  {
    "@type": "/dydxprotocol.sending.MsgCreateTransfer",
    "transfer": {
      "sender": {
        "owner": "xxx"
      },
      "recipient": {
        "owner": "xxx",
        "number": 128
      },
      "amount": "10000000"
    }
  }

En dydx v4, cada dirección puede crear múltiples subcuentas. La subcuenta 0 es creada automáticamente, mientras que las subcuentas con número ≥128 se usan para trading con margen aislado, requiriendo mínimo 20 USDC.
Ejemplo: transferencias entre subcuenta 0 ↔ 128. Las transferencias consumen Gas Fee, pagadero en USDC o token dydx.

Práctica de dYdX v4 en la plataforma FMZ

El contenido anterior explica brevemente algunos detalles de encapsulamiento. Ahora procederemos a implementar el uso específico utilizando la testnet de dYdX v4 para demostración. La testnet es prácticamente idéntica a la mainnet y cuenta con un faucet automático para obtener assets de prueba. No se repetirá la operación de implementación del custodio, realizaremos pruebas en vivo directamente en FMZ.

1. Configuración

Después de conectar exitosamente una wallet de criptomonedas a la aplicación dYdX v4 (en este caso utilizamos imToken), reclame los assets de prueba y exporte el mnemónico de la cuenta actual de dYdX v4 (derivada de la wallet).

Práctica Cuantitativa en DEX (1): Guía de dYdX v4

Configure el mnemónico en la plataforma FMZ utilizando el método de archivo local (también se puede ingresar directamente, el mnemónico se configura encriptado, no en texto plano).

  • Archivo mnemónico: mnemonic.txt

Práctica Cuantitativa en DEX (1): Guía de dYdX v4

Colóquelo en el directorio del ID de operación en vivo dentro de la carpeta del custodio, aunque también puede ubicarse en otras rutas (debe especificarse la ruta completa durante la configuración).

  • Configuración del exchange en FMZ

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

En el campo de mnemónico ingrese: file:///mnemonic.txt. La ruta real corresponde a: directorio_del_custodio/logs/storage/594291.

Práctica Cuantitativa en DEX (1): Guía de dYdX v4

2. Cambio a testnet de dydx v4

function main() {
    // Cambiar la dirección del indexador de la cadena de prueba
    exchange.SetBase("https://indexer.v4testnet.dydx.exchange")

    // Modificar ChainId para testnet 
    exchange.IO("chainId", "dydx-testnet-4")

    // Cambiar dirección del nodo REST para testnet
    exchange.IO("restApiBase", "https://dydx-testnet-api.polkachu.com")

    // Prueba de lectura de información de cuenta
    Log(exchange.GetAccount()) 
}

Información de cuenta obtenida de testnet:

{
	"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. Consulta de información de mercados

Prueba realizada en mainnet sin cambiar a testnet

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])
    }
### 4. Colocación de órdenes

```js
function main() {
    // Cambiar la dirección del indexador de la cadena de prueba
    exchange.SetBase("https://indexer.v4testnet.dydx.exchange")

    // Cambiar ChainId de la cadena de prueba 
    exchange.IO("chainId", "dydx-testnet-4")

    // Cambiar dirección del nodo REST de la cadena de prueba
    exchange.IO("restApiBase", "https://dydx-testnet-api.polkachu.com")

    // Orden limitada, colocar orden
    var idSell = exchange.CreateOrder("ETH_USD.swap", "sell", 4000, 0.002)
    var idBuy = exchange.CreateOrder("ETH_USD.swap", "buy", 3000, 0.003)

    // Orden de mercado
    var idMarket = exchange.CreateOrder("ETH_USD.swap", "buy", -1, 0.01)

    Log("idVenta:", idSell)
    Log("idCompra:", idBuy)
    Log("idMercado:", idMarket)
}

Práctica Cuantitativa en DEX (1): Guía de dYdX v4

Interfaz de dYdX v4 App:

Práctica Cuantitativa en DEX (1): Guía de dYdX v4

5. Información de órdenes

Prueba de obtención y cancelación de órdenes existentes en testnet.

function main() {    
    // Cambiar dirección del indexador de la cadena de prueba
    exchange.SetBase("https://indexer.v4testnet.dydx.exchange")

    // Cambiar ChainId de la cadena de prueba
    exchange.IO("chainId", "dydx-testnet-4")

    // Cambiar dirección del nodo REST de la cadena de prueba
    exchange.IO("restApiBase", "https://dydx-testnet-api.polkachu.com")

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

    var tbl = {type: "table", title: "Prueba GetOrders", cols: ["Id", "Price", "Amount", "DealAmount", "AvgPrice", "Status", "Type", "Offset", "ContractType"], 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) +  "`")
}

Práctica Cuantitativa en DEX (1): Guía de dYdX v4

6. Consulta de información de posiciones

function main() {
    // Cambiar dirección del indexador de la cadena de prueba
    exchange.SetBase("https://indexer.v4testnet.dydx.exchange")

    // Cambiar ChainId de la cadena de prueba
    exchange.IO("chainId", "dydx-testnet-4")

    // Cambiar dirección del nodo REST de la cadena de prueba
    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: "Prueba GetPosition/GetPositions", cols: ["Symbol", "Amount", "Price", "FrozenAmount", "Type", "Profit", "Margin", "ContractType", "MarginLevel"], 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) +  "`")
}

Práctica Cuantitativa en DEX (1): Guía de dYdX v4

7. Gestión de subcuentas

function main() {
    // Cambiar dirección del indexador de la cadena de prueba
    exchange.SetBase("https://indexer.v4testnet.dydx.exchange")

    // Cambiar ChainId de la cadena de prueba
    exchange.IO("chainId", "dydx-testnet-4")

    // Cambiar dirección del nodo REST de la cadena de prueba
exchange.IO("restApiBase", "https://dydx-testnet-api.polkachu.com")

    // subAccountNumber 0 -> 128 : 20 USDC , Tarifa Gas como adv4tnt (token dydx)
    var ret = exchange.IO("transferUSDCToSubaccount", 0, 128, "adv4tnt", 20)  
    Log("ret:", ret)

    // Cambiar a subcuenta subAccountNumber 128, leer y verificar información de cuenta
    exchange.IO("subAccountNumber", 128)

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

Práctica Cuantitativa en DEX (1): Guía de dYdX v4

Cambio a subcuenta con subAccountNumber 128, datos devueltos por 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
}

Se confirma que en la subcuenta subAccountNumber 128 se han depositado 20 USDC.

8. Obtener TxHash e invocar interfaz de nodo REST

Método para obtener TxHash de órdenes mediante llamadas REST

El exchange dydx almacena en caché los TxHash, consultables mediante ID de orden. Sin embargo, el mapeo se borra al detener la estrategia.

function main() {
    // Configurar dirección del indexador para testnet
    exchange.SetBase("https://indexer.v4testnet.dydx.exchange")

    // Especificar ChainId de testnet
    exchange.IO("chainId", "dydx-testnet-4")

    // Configurar endpoint REST de testnet
    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)
    
    // Para vaciar la tabla de mapeo se puede usar: 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)
    }
}

Práctica Cuantitativa en DEX (1): Guía de dYdX v4

Mensaje obtenido al consultar mediante TxHash:

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

Contenido demasiado extenso, se muestra un extracto:

{
	"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": []
			},
      ...

FIN

Las pruebas anteriores se basan en la última versión del host. Es necesario descargar el host más reciente para compatibilidad con dYdX v4 DEX

Gracias por su apoyo y por leer este material.