[TOC]
Com a rápida ascensão dos DEXs no mercado de negociação de criptomoedas, os comerciantes quantificados começaram a se voltar para essas plataformas para efetivamente automatizar suas transações. Como uma das plataformas de negociação descentralizadas mais populares, a dYdX oferece funções de negociação poderosas, com suporte para negociação de contratos permanentes de futuros, e sua versão mais recente v4 também otimiza o desempenho e a experiência do usuário, tornando-se a preferência de muitos comerciantes quantificados.
Este artigo irá explicar como quantificar as práticas de negociação no dYdX v4, incluindo como usar sua API para negociar, obter dados de mercado e gerenciar contas.
dYdX Testing Web App Página
edYdX v3
E, também, as transações geram recompensas.dYdX
O que é que você está a fazer?
O anterior protocolo dYdX v3 DEX foi desligado e o endereço do aplicativo dYdX v4 é:
Depois de abrir a página do aplicativo, no canto superior direito, há um botão para conectar a carteira e o código de varredura para conectar a carteira.
Se você quiser se familiarizar com testes de ambiente de testes, você pode usar testes de rede:
Também é no canto superior direito, clique no botão Conectar carteira, varrer o código Conectar carteira, confirmação de assinatura. Após o sucesso da conexão da carteira, um endereço dydx v4 será gerado automaticamente. No canto superior direito da página do aplicativo, o endereço será exibido e um menu será exibido.
Endereço da conta dYdX v4
O endereço da conta dYdX v4 é derivado do endereço da carteira.dydx1xxxxxxxxxxxxxxxxxxxxq2ge5jr4nzfeljxxxx
, é o endereço de início do dydx1. Este endereço pode ser consultado no blockchain explorers.
Palavras auxiliares Pode-se clicar no botão "Exportar Password" no menu no canto superior direito para exportar a palavra-passe do atual endereço da conta dYdX. É necessário configurar essa palavra-passe quando se adiciona uma troca na plataforma FMZ.
O auxiliar pode ser configurado diretamente na plataforma FMZ, ou pode ser mantido localmente no administrador, quando se usa o objeto de troca dydx v4, o conteúdo do arquivo do auxiliar será lido e demonstrado na seção prática deste artigo.
O ambiente da rede de teste tem algumas diferenças em relação ao ambiente da rede principal.
subAccountNumber >= 128
Se a subconta do ID não estiver em custódia, os ativos serão automaticamente despejados para a subconta com o SubAccountNumber 0.
No teste, descobriu-se que a rede de teste não tinha esse mecanismo (ou que as condições de gatilho eram diferentes e não havia gatilho na rede de teste).DYDX
A rede de testesDv4TNT
Página inicial:
O endereço do índice:https://indexer.dydx.trade
ID da cadeia:dydx-mainnet-1
O REST é um ponto:https://dydx-dao-api.polkachu.com:443
A rede de testes:
O endereço do índice:https://indexer.v4testnet.dydx.exchange
ID da cadeia:dydx-testnet-4
O REST é um ponto:https://dydx-testnet-api.polkachu.com
O protocolo dYdX v4 é baseado no desenvolvimento do ecossistema cosmos.
O serviço de índices fornece o protocolo REST e o protocolo Websocket.
Protocolo REST A interface do protocolo REST suporta consultas de informações de mercado, informações de contas, informações de estoque, informações de pedidos e outras consultas, que estão embaladas na plataforma FMZ como uma interface API unificada para a plataforma.
Protocolo WebSocket Na plataforma FMZ, é possível criar conexões Websocket, informações sobre o mercado de assinaturas e outras informações usando a função Dial.
É necessário prestar atenção a um problema que os índices do dydx v4 têm com todas as transações centralizadas, as atualizações de dados não são tão oportunas, por exemplo, às vezes a consulta imediata após a encomenda pode não ser feita.Sleep(n)
O blogueiro também escreveu sobre o assunto:
Aqui está um exemplo de como usar a função Dial para criar uma conexão Websocket API e assinar um pedido de dados finos:
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() {
// real : wss://indexer.dydx.trade/v4/ws
// simulate : 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) + "`")
}
}
Os mais usados em transações são mensagens de pedido, mensagens de cancelamento e mensagens de transferência.
Resumo das notícias da encomenda
{
"@type": "/dydxprotocol.clob.MsgPlaceOrder",
"order": {
"orderId": {
"subaccountId": {
"owner": "xxx"
},
"clientId": xxx,
"orderFlags": 64,
"clobPairId": 1
},
"side": "SIDE_BUY",
"quantums": "2000000",
"subticks": "3500000000",
"goodTilBlockTime": 1742295981
}
}
Lista de preços limitados:
A função de envio de pedidos na plataforma FMZ e o valor do OrderFlags usado para encomendas de preços limitados são:ORDER_FLAGS_LONG_TERM = 64 # 长期订单
O prazo de validade do pedido é de 90 dias (todos os tipos de pedido em Dydx v4 são válidos).
Lista de preços:
A função de transferência de pedidos, que é encapsulada na plataforma FMZ, e a ordem Flags usada para os pedidos de pedidos de preço de mercado são:ORDER_FLAGS_SHORT_TERM = 0 # 短期订单
A partir de agora, o site está disponível para download gratuito e gratuito.
// Recomendação definida para o preço do oráculo - 5% ou menos para VENDER, preço do oráculo + 5% para COMPRAR
Como não é uma lista de preços de mercado real, o preço do pronóstico é usado, com um preço de deslizamento menos 5% como lista de preços de mercado. O prazo de validade da ordem curta também é diferente do prazo de validade da ordem longa. A ordem curta usa um prazo de validade de alto bloco, que, de acordo com a recomendação do dydx v4, é definido como o bloco atual + 10 blocos de altura e não é válido.
ID do pedido: Como a operação de encomenda é executada diretamente na cadeia, não há um ID de encomenda gerado pelo índice após a transmissão da mensagem e não é possível usar o pedido do índice como valor de retorno da função de encomenda da plataforma. Para garantir a unicidade do ID de encomenda e a precisão da consulta do pedido, o ID de encomenda retornado é composto pela seguinte informação:
Resumo do recado
{
"@type": "/dydxprotocol.clob.MsgCancelOrder",
"orderId": {
"subaccountId": {
"owner": "xxx"
},
"clientId": 2585872024,
"orderFlags": 64,
"clobPairId": 1
},
"goodTilBlockTime": 1742295981
}
O ID da encomenda deve ser enviado para a interface de encomenda da plataforma FMZ.
Resumo das transferências
{
"@type": "/dydxprotocol.sending.MsgCreateTransfer",
"transfer": {
"sender": {
"owner": "xxx"
},
"recipient": {
"owner": "xxx",
"number": 128
},
"amount": "10000000"
}
}
Atualmente, é possível criar várias contas subalternas sob o endereço dydx v4, sendo que o subAccountNumber 0 é o primeiro subconto a ser criado automaticamente, o subAccountNumber é maior do que o ID de subconta igual a 128 para transações por variedade de estoque, exigindo um mínimo de 20 USD de ativos. Por exemplo, pode ser feito a partir do subAccountNumber 0 -> 128 ou pode ser feito a partir do subAccountNumber 128 -> 0. O desvio requer consumir a taxa de gás. A taxa de gás pode ser usada com o token USDC, dydx.
O conteúdo acima descreve brevemente alguns detalhes do pacote. Em seguida, vamos praticar um uso específico, aqui usando uma rede de teste da dYdX v4 para uma demonstração, a rede de teste é basicamente compatível com a rede principal, e há uma torneira automática que pode receber os ativos de teste.
Após conectar com sucesso o aplicativo dYdX v4 com a carteira de criptomoedas (a carteira imToken que eu uso aqui), receba o ativo de teste e exporte a palavra-passe para a conta atual dYdX v4 (derivada da carteira).
Configure a palavra auxiliar na plataforma FMZ, onde configuramos usando o método de arquivo local (também pode ser preenchido diretamente e configurado para a plataforma, a palavra auxiliar é em configuração post-criptografia, não em texto aberto).
助记词文件:mnemonic.txt
Coloque-o no diretório de pasta de ID de disco real, sob o diretório do administrador, e, claro, também pode ser colocado em outros diretórios (desde que seja necessário escrever um caminho específico para a configuração).
Configurar a troca no FMZ
A caixa de edição de palavras-chave é preenchida:file:///mnemonic.txt
O caminho mais prático é:托管者所在目录/logs/storage/594291
。
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())
}
Para ler as informações da conta do teste:
{
"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
}
Sem mudar para a rede de teste, teste na rede principal.
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])
}
LogStatus("`" + JSON.stringify(tbl) + "`")
}
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)
}
A página do aplicativo dYdX v4:
A rede de testes coloca dois pedidos antecipadamente, obtém o pedido atual e cancela o pedido.
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: "test 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) + "`")
}
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: "test 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) + "`")
}
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")
// subAccountNumber 0 -> 128 : 20 USDC , 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)
}
Para mudar para o subAccountNumber para o subaccount número 128, o GetAccount retorna dados:
{
"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
}
O subAccountNumber pode ser visto como uma subconta de 128, transferida para 20 USD.
Obtenção de TxHash, método de teste de IO para chamar os nós REST, com base em pedidos
Como obter o TxHash das ordens, o objeto dydx do exchange armazenará o TxHash e poderá ser consultado com o ID da ordem.
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)
}
}
A mensagem foi consultada pelo TxHash:
var ret =exchange.IO("api",
GET , /cosmos/tx/v1beta1/txs/ + txHash)
O conteúdo é muito longo e algumas partes são apresentadas:
{
"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": []
},
...
O teste acima, baseado no mais recente host, requer o download do mais recente host para suportar o dYdX v4 DEX
Obrigado pelo apoio, obrigado por ler.