[TOC]
分散型取引所 (DEX) が暗号通貨取引の分野で急速に上昇するにつれて,定量化トレーダーはこれらのプラットフォームに効率的な自動取引のために徐々に移行し始めています. dYdXは,最も人気のある分散型取引プラットフォームの1つであり,強力な取引機能を提供し,フューチャー永久契約取引をサポートしています.
この記事では,dYdX v4で取引の量化実践,APIの取引,市場データ取得,アカウント管理などについて説明します.
dYdXテストウェブアプリページ
そしてdYdX v3
取引は報酬を生み出します.dYdX
トークン.
前回のdYdX v3プロトコルのDEX取引所はオフラインになり,現在dYdX v4アプリの住所は:
このアプリのページを開くと,右上角には"財布接続"のボタンが表示されます.
テストネットの環境テストに慣れたい場合は,テストネットを使用できます.
同じように右上角に接続財布ボタンをクリックし,スキャード接続財布,署名確認.財布接続が成功すると自動的にdydx v4アドレスが生成され,アプリページの右上角にこのアドレスが表示され,クリックするとメニューがポップアップします. 充電,提票,送金などの操作が含まれます. dYdX主ネットワーク (生産環境) とテストネットワークの違いの1つは,テストネットのクリック充電は自動的にタップを使用して300USDCの資産をテストします.
dYdX v4 アカウント アドレス
dYdX v4 アカウント アドレスはウォレット アドレスの由来で,dYdX v4 アカウント アドレスは以下のように見えます.dydx1xxxxxxxxxxxxxxxxxxxxq2ge5jr4nzfeljxxxx
,はdydx1の開始アドレスである. このアドレスについては,ブロックチェーンのエクスプローラーで問い合わせることができます.
補足語 上右端のメニューの"パスワードを輸出する"ボタンをクリックして,現在のdYdXアドレスアカウントのセールスワードを輸出できます. FMZプラットフォームに取引所を追加する際にセールスワードを設定する必要があります.
助詞はFMZプラットフォームで直接設定できる.また,托管者がローカルに保存できる.dydx v4の交換オブジェクトを使用するときに,助詞の記録ファイルの内容を読み取ります.
テストネットワーク環境は,主ネットワーク環境といくつかの点で異なる.以下はいくつかの簡単なリストです.
subAccountNumber >= 128
このIDのサブアカウントが保管されていない場合は,サブアカウント番号が0であるサブアカウントに自動的に資産をクリアします.
テストでは,テストネットにそのようなメカニズムがないことが判明 (またはトリガー条件が異なるため,テストネットではトリガーされていない).DYDX
テストネットDv4TNT
トップページ:
索引の住所: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プロトコルはcosmosエコ開発に基づいている. dYdX v4 DEXシステム取引に関する内容は主に2つの部分で構成されている:
インデクサーサービスは,REST プロトコルとWebSocket プロトコルを提供している.
REST プロトコル RESTプロトコルインターフェイスは,市場情報,口座情報,保有情報,注文情報などの問い合わせをサポートし,FMZプラットフォームではプラットフォーム統一APIインターフェイスとしてパッケージ化されています.
WebSocket プロトコル FMZプラットフォームでは,Websocket接続,サブスクリプション市場などの情報をDial機能で作成できます.
Dydx v4のインデックスと中央集権取引のすべてに同じ問題があることに注意する必要があります. データ更新は,時折,注文後にすぐに問い合わせる場合など,特定の操作後に問い合わせることができません.Sleep(n)
投稿者:
Dial を使って Websocket API 接続を作成し, Order Thin Data を購読する例を紹介します.
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) + "`")
}
}
取引において最もよく使われるのは,注文メッセージ,撤回メッセージ,送金メッセージである.
オーダーニュース要約
{
"@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 プロトコルの提案では:
// オラクル価格に設定する推奨 - SELL の場合は5%以下,ORACLE の場合は5%以下
本格的な市場価格表ではないため,予想機価格と5%減滑価格を市場価格表として採用する.短期オーダーの有効期限設定も長期オーダーとは異なり,短期オーダーの有効期限はブロックの高さで,dydx v4の推奨に従って現在のブロック+10ブロックの高さに設定された後に有効でない.
オーダーID: 注文処理はチェーン上で直接実行されるため,メッセージの放送後にはインデックス生成された注文IDがないため,プラットフォームの注文関数の返却値としてインデックス注文を使用することはできません.注文IDのユニーク性と注文クエリの正確性を確保するために,返された注文IDは以下の情報から構成されています (英語のコマ間隔):
撤収されたニュース要約
{
"@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アドレスでは多くのサブアカウントを作成できます.サブアカウント番号は0で,サブアカウント番号は128に等しいサブアカウントIDよりも高く,株ごとに取引する品種に最低20USDCの資産が必要です. 例えば,サブアカウント番号0 -> 128から,またはサブアカウント番号128 -> 0から,[スライド]を行うにはガス料金を消費する必要があります.ガス料金はUSDC,dydxトークンを使用できます.
上記の内容は,いくつかのパッケージの詳細を簡潔に説明しています. 次に,実際に使用することを実践しましょう. ここで,dYdX v4のテストネットを使用してデモします. テストネットは主ネットワークと基本的には一致しています.
dYdX v4 App (ここではimTokenウォレットを使用しています) を暗号通貨ウォレットで成功裏に接続した後,テスト資産を取得し,現在の dYdX v4 アカウント (ウォレットから派生した) を出力します.
FMZプラットフォームで補記詞を配置します. ここで,ローカルファイル方式で配置します. (または直接記入してプラットフォームに配置できます.補記詞は暗号化後の配置で,明文ではありません).
助记词文件:mnemonic.txt
管理者のディレクトリの下にある実体ディスクIDフォルダのディレクトリに,もちろん他のディレクトリにも置くことができます.
FMZで取引所配置
字幕の編集欄に記入してください:file:///mnemonic.txt
具体的には,次の通りです.托管者所在目录/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())
}
テストネットのアカウントの情報をご覧下さい.
{
"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
}
テストネットワークに切り替わらず,主ネットワークでテストします
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)
}
dYdX v4 アプリのページ:
テストネットは2つの注文を事前に掲示し,テストは現在の掲示板を取得し,注文を撤回します.
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)
}
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
}
サブアカウント番号は128のサブアカウントで,20USDCに変換されています.
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)
}
}
TxHashで検索されたメッセージ:
バール=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をサポートする必要があります.
ありがとうございました. ありがとうございました.