Практика количественных стратегий на 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

После открытия страницы приложения, в правом верхнем углу находится кнопка подключения кошелька. Для тестирования в тестовой сети можно использовать:

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

Аналогично, подключите кошелёк через сканирование QR-кода и подпишите запрос. После успешного подключения автоматически генерируется адрес dYdX v4, который отображается в правом верхнем углу. В выпадающем меню доступны операции пополнения, вывода и трансферов. Основное отличие тестовой сети от основной: при пополнении в тестовой сети автоматически зачисляется 300 USDC для тестирования. Для реальной торговли требуется внесение USDC через поддерживаемые сети.

  • Адрес аккаунта dYdX v4 Адрес формируется на основе адреса кошелька и имеет вид: dydx1xxxxxxxxxxxxxxxxxxxxq2ge5jr4nzfeljxxxx (начинается с dydx1). Его можно проверить в блокчейн-эксплорерах.

  • Мнемоническая фраза Через меню «Экспорт пароля» можно получить мнемоническую фразу текущего аккаунта. Для добавления биржи на платформе FMZ требуется настройка этой фразы.

Мнемонику можно напрямую настроить на FMZ или хранить локально. В практической части статьи будет продемонстрирована работа с объектом биржи dydx v4.

Различия между основной и тестовой сетями

Тестовая сеть имеет некоторые отличия от основной:

  • Трансферы между субаккаунтами. В основной сети действует механизм автоматического перемещения активов при subAccountNumber >= 128, если нет открытых позиций. В тестовой сети этот механизм отсутствует (или условия срабатывания отличаются).

  • Названия токенов. Нативный токен: DYDX (основная сеть), Dv4TNT (тестовая).

  • Конфигурация адресов (chain ID, ноды и т.д.):

    • Основная сеть: Индексатор: https://indexer.dydx.trade Chain ID: dydx-mainnet-1 REST нода: https://dydx-dao-api.polkachu.com:443

    • Тестовая сеть: Индексатор: https://indexer.v4testnet.dydx.exchange Chain ID: dydx-testnet-4 REST нода: https://dydx-testnet-api.polkachu.com

Архитектура протокола dYdX v4

dYdX v4 разработан на экосистеме Cosmos. Основные компоненты:

  • Индексатор для запросов рыночных данных и информации об аккаунтах.
  • Блокчейн dydx для обработки ордеров, отмен и трансферов.

Индексатор

Индексатор предоставляет REST и WebSocket API.

  • REST API Обеспечивает доступ к рыночным данным, информации об аккаунтах, позициях и ордерах. На платформе FMZ реализована унифицированная обёртка.

  • WebSocket API Для подключения через WebSocket на FMZ используется функция Dial. Пример подписки на стакан:

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))
                        }
                    })
                }

                // Обновление асков
                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("Не удалось создать поток WebSocket.")
                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")
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) + "`")
    }
}

Распространение сообщений узла цепи 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 имеют срок действия).

    • Рыночный ордер: Для рыночных ордеров используется значение orderFlags: ORDER_FLAGS_SHORT_TERM = 0 # Краткосрочный ордер. В соответствии с рекомендациями dydx v4:

    // Рекомендуется устанавливать цену: oracle price -5% для SELL, oracle price +5% для BUY

    Поскольку это не истинный рыночный ордер, используется цена оракула с добавлением/вычитанием 5% для имитации рыночного исполнения. Срок действия краткосрочных ордеров также отличается - используется высота блока (goodTilBlock), рекомендуется устанавливать текущий блок +10.

    • Order ID: Поскольку операция размещения ордера выполняется непосредственно в блокчейне, после трансляции сообщения не генерируется индексный order ID. Для обеспечения уникальности и точности запросов, order ID формируется из следующих компонентов (разделены запятыми):

      • Торговая пара
      • Текущий адрес аккаунта dydx
      • Номер субаккаунта (subaccountNumber)
      • clientId (генерируется случайно)
      • clobPairId (ID инструмента)
      • orderFlags
      • goodTilData (в миллисекундах)
  • Сводка сообщения об отмене ордера

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

Требуется передача order ID, возвращенного API размещения ордера платформы FMZ.

  • Сводка сообщения о переводе средств
  {
    "@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. Транзакции перевода требуют оплаты Gas Fee, которая может быть списана в USDC или токенах dydx.

Практическое руководство по работе с dYdX v4 на платформе FMZ

Вышеизложенный материал кратко объяснил некоторые аспекты инкапсуляции. Теперь перейдем к практическому применению, используя тестовую сеть dYdX v4 для демонстрации. Тестовая сеть практически идентична основной сети и имеет автоматический кран для получения тестовых активов. Процедура развертывания опекуна (хранителя) не будет подробно описываться — создадим реальный тест на платформе FMZ.

1. Конфигурация

После успешного подключения криптокошелька к приложению dYdX v4 (в примере используется imToken) и получения тестовых активов экспортируем мнемоническую фразу текущего аккаунта 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 API
    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: "тестовые рынки", cols: ["key", "Символ", "Базовый актив", "Котируемый актив", "Шаг цены", "Шаг объема", "Точность цены", "Точность объема", "Мин. объем", "Макс. объем", "Мин. номинал", "Макс. номинал", "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、Размещение ордеров

```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 App:

Практика количественных стратегий на 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", "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) +  "`")
}

Практика количественных стратегий на 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: ["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) +  "`")
}

Практика количественных стратегий на 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")
    
    // Пример управления субаккаунтами
    var subaccounts = exchange.GetSubAccounts()
    Log("Субаккаунты:", subaccounts)
    
    // Создание нового субаккаунта
    var newSubAcc = exchange.CreateSubAccount("subacc01")
    Log("Создан субаккаунт:", newSubAcc)
    
    // Установка активного субаккаунта
    exchange.SetSubAccount("subacc01")
    
    var balance = exchange.GetBalance()
    LogStatus("Баланс активного субаккаунта:", balance)
}

Практика количественных стратегий на DEX (1): Руководство по dYdX v4 exchange.IO(“restApiBase”, “https://dydx-testnet-api.polkachu.com”)

// Перевод 20 USDC с subAccountNumber 0 на 128, комиссия Gas Fee выплачивается в токене adv4tnt (dydx token)
var ret = exchange.IO("transferUSDCToSubaccount", 0, 128, "adv4tnt", 20)  
Log("ret:", ret)

// Переключение на субаккаунт subAccountNumber 128 и проверка данных аккаунта
exchange.IO("subAccountNumber", 128)

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

}


![Практика количественных стратегий на DEX (1): Руководство по dYdX v4](/upload/asset/175bd506389b9e8966ca.png)

Данные аккаунта после переключения на subAccountNumber 128:

```JSON
{
	"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-узла

Получение TxHash по ордеру через вызов REST-интерфейса узла

Для получения TxHash ордера объект биржи dydx сохраняет хэш в кеше, который можно запросить по ID ордера. Однако при остановке стратегии кешированные сопоставления хэшей транзакций заказов очищаются.

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 требуется скачать актуальную версию опекуна.

Благодарим за поддержку и внимание к материалу.