Market Stats
Loading...
arrow_back
回測與實盤機器人之間的差距,大部分是管線工程:打造一個 Hyperliquid 交易機器人
深入研究

回測與實盤機器人之間的差距,大部分是管線工程:打造一個 Hyperliquid 交易機器人

calendar_todayschedule11 minvisibility12
重點摘要

在一個實盤機器人裡,alpha 大概只佔 20%。另外的 80% 是管線工程:不洩漏金鑰的身分驗證、能在部分成交中存活的委託處理、一條維持新鮮的資料流,以及速率限制的紀律。這是一份針對 Hyperliquid 第一方 Python SDK 的實作建構指南——讀取狀態、用一個 agent 錢包驗證身分、下單與撤單、訂閱一條即時資料流,並圈住那些真正會弄垮機器人的營運風險。

從回測到一個實盤機器人之間的距離,大部分是管線工程:身分驗證、委託處理、一條維持新鮮的資料流,以及速率限制的紀律。
從回測到一個實盤機器人之間的距離,大部分是管線工程:身分驗證、委託處理、一條維持新鮮的資料流,以及速率限制的紀律。

回測與實盤機器人之間的差距,大部分是管線工程

每一個把策略從研究搬到生產的量化交易者,都知道那個難堪的真相:alpha 大概只佔工作的 20%。另外的 80% 是不洩漏金鑰的身分驗證、能在部分成交中存活的委託管線、一條不會無聲變舊的資料流,以及讓你不會在最糟時刻被限流的速率限制紀律。Hyperliquid 在這方面是異常友善的地盤,因為它出貨了一個第一方、MIT 授權的 Python SDK,包裝了交易所自己使用的同一套 REST 與 WebSocket API。本指南會走過如何針對那套 API 建構一個實盤交易機器人,且是以你實際會運行的方式:讀取市場狀態、用一個 API 錢包驗證身分、下單與撤單、訂閱一條即時資料流,並處理那些在生產環境中弄垮機器人的失敗模式。

這是一份給運行自動化策略的開發者的技術建構指南。它假設你熟悉 Python、私鑰處理,以及永續期貨的機制。在你讓任何一美元的部位流經它之前,先在 testnet 上把每件事都測過。

重點

  • 官方的 hyperliquid-python-sdkpip install hyperliquid-python-sdk)暴露兩個核心類別:Info 用於唯讀資料,Exchange 用於已簽署的動作。

  • 用一個 API 錢包(也稱為 agent 錢包)簽署,在 app.hyperliquid.xyz/API 產生——絕不要用你主帳戶的私鑰。

  • 一個關鍵陷阱:你用 API 錢包的金鑰簽署,卻用主帳戶的公開位址查詢。用 agent 位址去查詢會回傳空資料。

  • REST 受速率限制,為每個 IP 每分鐘 1200 的彙總權重,外加一個以位址為基礎、大約每交易 1 USDC 允許 1 次請求的限制。低延遲的即時資料請用 WebSocket。

  • 最大的營運風險不是策略風險——而是金鑰管理、無聲的資料變舊,以及未處理的部分成交。

你該用 REST API、WebSocket,還是兩者都用?

Hyperliquid 的 API 有兩種傳輸方式,而你幾乎一定兩種都會用到。位於 https://api.hyperliquid.xyz/info.../exchange 的 REST 介面是請求/回應式的:你 POST 一個描述你想要什麼的 JSON body。info 端點提供市場與帳戶狀態;exchange 端點接受已簽署的動作,例如下單與撤單。位於 wss://api.hyperliquid.xyz/ws 的 WebSocket 是推送式的——你訂閱一次,伺服器便串流推送更新。

實務上的分工很直接。凡是你按需請求的,都用 REST:啟動時的快照、決定部位大小前的帳戶狀態,以及每一筆下單或撤單(所有寫入都透過 REST 式的已簽署動作)。凡是你想要連續且快速取得的,都用 WebSocket:訂單簿、成交、中間價,以及你自己的委託/成交更新。用 REST 輪詢訂單簿既較慢,也是一種快速燒光你速率預算的方式。SDK 把兩種傳輸方式都收攏在同一個 Info 物件之後,所以切換只是一個建構子的旗標。

一個實盤機器人兩種傳輸方式都用:REST 用於按需快照與已簽署委託,WebSocket 用於訂單簿、成交與 fills 的連續低延遲串流。
一個實盤機器人兩種傳輸方式都用:REST 用於按需快照與已簽署委託,WebSocket 用於訂單簿、成交與 fills 的連續低延遲串流。

你要如何設定 SDK 並讀取市場狀態?

安裝套件,並從一個唯讀客戶端開始。Info 類別完全不需要金鑰,這讓它成為在你碰簽署之前、對連線做健全性檢查的正確位置。當你只想要請求/回應式的呼叫時,傳入 skip_ws=True

pip install hyperliquid-python-sdk
from hyperliquid.info import Info
from hyperliquid.utils import constants

# Read-only: no key required
info = Info(constants.MAINNET_API_URL, skip_ws=True)

# All mid prices, keyed by coin
mids = info.all_mids()
print("BTC mid:", mids["BTC"])

# Perp universe + context (funding, open interest, mark price, ...)
meta, asset_ctxs = info.meta_and_asset_ctxs()

# Full account state for any address (public address, not the agent)
user_state = info.user_state("0xcd5051944f780a621ee62e39e493c489668acf4d")
print("account value:", user_state["marginSummary"]["accountValue"])

注意這兩個常數:constants.TESTNET_API_URLconstants.MAINNET_API_URL。SDK 自己的範例預設用 testnet,在機器人被證實之前你也該如此。這裡的每一個方法,都記載於 SDK 的 hyperliquid/info.py,並在該倉庫的 examples/ 目錄中有對應範例。

你要如何在不洩漏金鑰的情況下驗證身分?

這是要讀兩遍的一節。Hyperliquid 把你的主帳戶——那個持有資金的錢包——與一個只被授權簽署動作的 API 錢包(文件也稱它為 agent 錢包)分開。你在 https://app.hyperliquid.xyz/API 產生並核准一個 API 錢包。這個 API 錢包能代表主帳戶下單與撤單,但它從不持有託管,且可以在不移動資金的情況下被解除註冊。這正是你為一個無人值守機器人所想要的分離:如果 API 金鑰被入侵,爆炸半徑是交易動作,而不是你餘額的提領。

官方文件明確說明的兩條規則:

  1. 主帳戶的公開位址設為 account_address。一個常見的陷阱是傳入 agent 錢包的位址,那會導致查詢結果為空。

  2. 把 API 錢包當成用完即棄。一旦一個 agent 被解除註冊,它的 nonce 狀態可能會被清除,這可能讓先前已簽署的動作被重放。文件強烈建議產生一個全新的 agent 錢包,而不是重複使用某個位址。

絕不要把密鑰寫死在程式碼裡。從一個環境變數(或一個 keystore)載入它,好讓金鑰永遠不會落到版本控制或日誌裡:

import os
import eth_account
from hyperliquid.exchange import Exchange
from hyperliquid.info import Info
from hyperliquid.utils import constants

# Secret lives in the environment, never in code
secret_key = os.environ["HL_API_SECRET_KEY"]        # API wallet private key
account_address = os.environ["HL_ACCOUNT_ADDRESS"]  # MASTER account public address

account = eth_account.Account.from_key(secret_key)

info = Info(constants.MAINNET_API_URL, skip_ws=True)
exchange = Exchange(
    account,
    constants.MAINNET_API_URL,
    account_address=account_address,  # query/target the master, sign with the agent
)

Exchange 物件現在會用 agent 金鑰簽署每一個動作,同時把它歸屬到你的主帳戶。在底層,SDK 處理 EIP-712 簽署與 nonce 管理,所以你很少直接碰到其中任何一個——但知道以下這點會有幫助:nonce 是以每個簽署者為單位追蹤的,而如果你運行多個行程,你應該給每一個各自的 API 錢包,以避免 nonce 碰撞。

你要如何下單、查單與撤單?

核心方法是 exchange.order(name, is_buy, sz, limit_px, order_type)。以下這段會掛出一筆遠低於市價、因而不會成交的限價買單,檢視回應、查詢它的狀態,然後撤銷它——這是來自 SDK basic_order.py 範例的標準來回流程。

coin = "ETH"

# Resting GTC limit buy, 0.2 ETH at $1100 (well below market -> rests)
order_result = exchange.order(
    coin, True, 0.2, 1100, {"limit": {"tif": "Gtc"}}
)
print(order_result)

if order_result["status"] == "ok":
    status = order_result["response"]["data"]["statuses"][0]
    if "resting" in status:
        oid = status["resting"]["oid"]
        # Confirm it is live on the book
        print(info.query_order_by_oid(account_address, oid))
        # Cancel by order id
        print(exchange.cancel(coin, oid))

有幾件事是老手會想釘死的。tif(time-in-force,有效時間)欄位接受 "Gtc"(good-til-cancelled,掛著)、"Ioc"(immediate-or-cancel)或 "Alo"(add-liquidity-only/post-only,驗證者會在 ALO-only 的批次中優先處理它)。回應是一個巢狀結構:永遠鑽進 response.data.statuses,並根據實際結果分支——一個狀態回來可能是 restingfillederror,而假設成功,正是機器人漏掉部位的方式。

對於市價單,SDK 提供 market_openmarket_close,它們會把你的意圖轉換成一筆受滑價容忍度限制的積極 IOC 委託:

# Market buy 0.05 ETH, price=None (use book), 1% max slippage
order_result = exchange.market_open("ETH", True, 0.05, None, 0.01)

for status in order_result["response"]["data"]["statuses"]:
    if "filled" in status:
        f = status["filled"]
        print(f"filled {f['totalSz']} @ {f['avgPx']} (oid {f['oid']})")
    elif "error" in status:
        print("order error:", status["error"])

# Flatten the position later
exchange.market_close("ETH")

那個明確的滑價參數不是裝飾。在一個稀薄的訂單簿上、或在一次波動率飆升期間,一筆沒有滑價界限的市價單,正是你印出一個遠離預期的成交價的方式。如果你的策略疊加進階委託類型,同一個 order 呼叫也承載觸發與 TWAP 變體——這在 進階委託類型:TWAP、追蹤與條件單 中另行涵蓋。

你要如何訂閱一條即時資料流?

對任何延遲敏感的東西,拿掉 skip_ws 旗標並訂閱。SDK 的 Info.subscribe(subscription, callback) 會註冊一個在每則訊息時觸發的處理常式。你可以在同一個連線上訂閱公開資料流(訂單簿、成交、中間價、K 線)與使用者專屬資料流(你的 fills、委託更新)。

info = Info(constants.MAINNET_API_URL)  # WS enabled

def on_book(msg):
    # L2 order book snapshot/update for ETH
    levels = msg["data"]["levels"]
    best_bid = levels[0][0]["px"]
    best_ask = levels[1][0]["px"]
    print("bid/ask:", best_bid, best_ask)

def on_fill(msg):
    for fill in msg["data"]["fills"]:
        print("FILL", fill["coin"], fill["sz"], "@", fill["px"])

info.subscribe({"type": "l2Book", "coin": "ETH"}, on_book)
info.subscribe({"type": "userFills", "user": account_address}, on_fill)

可用的訂閱類型包括 allMidsl2BooktradescandlebbouserEventsuserFillsorderUpdates 等等。限制是以每個 IP 為單位:最多 10 個 WebSocket 連線、1000 個訂閱,以及跨使用者專屬訂閱的 10 個不重複使用者。有些資料流只在變動時推送,所以別把沉默當成錯誤——把它當成「什麼都沒發生」。同樣這些資料流,也是鏈上監控策略的骨幹;若想看純資料的角度,見 追蹤鏈上巨鯨動向

一個最小的機器人骨架長什麼樣子?

把它組起來:一個機器人就是一個迴圈(或一個事件處理常式),它讀取狀態、做決定、採取動作,並且——關鍵地——在每一次網路呼叫上處理錯誤。下面是一個刻意簡單的骨架,它檢查一個中間價並掛出一筆有界限的委託,把每一次 API 呼叫都包在錯誤處理裡。它是一個結構性模板,而不是一個策略。

import os
import time
import eth_account
from hyperliquid.exchange import Exchange
from hyperliquid.info import Info
from hyperliquid.utils import constants

def build_clients():
    account = eth_account.Account.from_key(os.environ["HL_API_SECRET_KEY"])
    addr = os.environ["HL_ACCOUNT_ADDRESS"]
    info = Info(constants.TESTNET_API_URL, skip_ws=True)   # start on TESTNET
    exch = Exchange(account, constants.TESTNET_API_URL, account_address=addr)
    return addr, info, exch

def run():
    addr, info, exch = build_clients()
    coin, target = "ETH", 1500.0

    while True:
        try:
            mid = float(info.all_mids()[coin])
        except Exception as e:            # network / parse failure -> skip tick
            print("data error, backing off:", e)
            time.sleep(5)
            continue

        if mid <= target:
            try:
                res = exch.order(coin, True, 0.02, target, {"limit": {"tif": "Gtc"}})
                statuses = res.get("response", {}).get("data", {}).get("statuses", [])
                for s in statuses:
                    if "error" in s:
                        print("rejected:", s["error"])   # e.g. min size, insufficient margin
                    elif "resting" in s:
                        print("resting oid:", s["resting"]["oid"])
                    elif "filled" in s:
                        print("filled:", s["filled"])
            except Exception as e:
                print("order submit failed:", e)          # DO NOT blindly retry

        time.sleep(2)   # respect rate limits; never hammer the endpoint

if __name__ == "__main__":
    run()

形狀比邏輯更重要。每一次外部呼叫都被包起來。一次資料失敗會跳過這個 tick,而不是崩潰。一筆被拒的委託被檢視,而不是被當作沒事。而且有一個刻意的 sleep,好讓這個迴圈不會旋進速率限制器。把 if mid <= target 這個區塊換成真正的訊號,你就有了一個生產機器人的骨架。如果你的策略是一個對稱網格、而非單一觸發,同樣這個骨架可以直接延伸進 在 Hyperliquid 上建構一個網格交易策略 中的設計。

哪些錯誤會在生產環境中弄垮機器人?

  • 用 agent 位址查詢。 SDK 最常見的單一困惑:用 API 錢包簽署,但把主帳戶的公開位址傳給 Info 查詢並作為 account_address。agent 位址回傳的是空狀態。

  • 假設一筆委託已成交。 一個 ok 的 HTTP 狀態,只代表請求被接受了。真正的結果活在 response.data.statuses 裡,而且可能是 error。每一次都根據它分支。

  • 沒有滑價界限的市價單。 永遠傳一個滑價容忍度給 market_open。稀薄的訂單簿與波動率飆升,會懲罰赤裸的市價單。

  • 忽略以位址為基礎的速率限制。 在每個 IP 的權重預算之外,Hyperliquid 大約每累積 1 USDC 成交量允許 1 次請求,並有一個 10,000 次請求的起始緩衝。一個在小帳戶上高頻撤單/重掛的迴圈,會被限流到每 10 秒一次請求。

  • 重複使用一個已解除註冊的 API 錢包。 被清除的 nonce 狀態,可能允許重放先前已簽署的動作。產生一個全新的 agent 錢包,而不是重複使用一個。

  • 在 mainnet 上測試。TESTNET_API_URL,直到完整的委託生命週期——提交、部分成交、撤單、重連——都被證實。

真正的風險是什麼,你又要如何把它們圈住?

一個自動化設置裡危險的風險,很少是策略。它們是營運上的。

金鑰管理是首要風險。 一個 API 錢包無法提領資金,這正是你該用它、而絕不用主金鑰的原因。即便如此,把 agent 密鑰當成生產憑證來對待:從一個環境變數或密鑰管理器載入它、絕不提交它、絕不記錄它,並嚴格限縮持有它的主機範圍。如果一把金鑰被暴露,立刻解除註冊那個 agent 並發一把新的——不要重複使用那個位址。

一個 API 錢包能下單與撤單,卻永遠無法提領——所以一把被入侵的 agent 金鑰,其爆炸半徑是交易動作,而不是你的餘額。
一個 API 錢包能下單與撤單,卻永遠無法提領——所以一把被入侵的 agent 金鑰,其爆炸半徑是交易動作,而不是你的餘額。

軟體錯誤會動用真金白銀的部位。 一個簽署錯誤、部位大小計算上的差一錯誤,或一個把一筆「失敗」(其實成功了)的委託重新提交的重試迴圈,全都可能以你未曾打算的方式進行交易。用硬性的、程式碼層級的護欄把這圈住:一個最大委託大小、在每次提交前的一個最大未平倉部位檢查,以及一個會平倉並停止的 kill switch。絕不要在一個委託呼叫周圍建一個盲目的「重試直到成功」迴圈——一次逾時並不會告訴你那筆委託到底有沒有落地。

滑價與流動性。 回測假設會成交;實盤的訂單簿不欠你一次成交。用滑價把市價單框住、在策略允許之處優先用掛著的限價或 ALO 委託,並對著看得見的訂單簿深度、而非中間價來決定部位大小。

把速率限制當成一種失敗模式。 在策略進行到一半時被限流——因為你把預算花在輪詢上,而無法撤銷一筆過時的委託——是一個真實的風險。文件之所以給撤單一個較高的累積額度,正是為了讓你在被限制時仍能撤回委託,但要刻意設計你的請求預算:資料用 WebSocket、在可能之處批次化動作,並且不要忙碌輪詢。

接下來該往哪走

  • 複製 SDK 倉庫、把 examples/config.json.example 複製成 config.json,並針對 testnet 運行 examples/basic_order.py,以確認你的簽署端到端可行。

  • 一旦 REST 版本穩定,就把輪詢迴圈換成 WebSocket 訂閱作為你的資料路徑。

  • 在你把機器人指向 mainnet 之前,先加上護欄——最大大小、最大部位、kill switch。

  • 一旦核心生命週期堅不可摧,就用 TWAP 與條件單 疊加更豐富的執行。

這套 API 很慷慨,SDK 也久經使用。把一個能運作的機器人與一個昂貴的機器人區分開來的紀律,完全在於管線:用一個 agent 錢包驗證身分、查詢正確的位址、檢視每一個回應、把你的委託框住,並尊重那些限制。

來源

延伸閱讀

獨家優惠via HyperAcademy

透過HyperAcademy開始交易 — 享受4%手續費折扣

check_circle首$25M交易量4%折扣check_circle零Gas費check_circle200+永續市場
立即開始交易 →
#trading-bots#python-sdk#api-wallet#automation#websocket
trending_up
在 Hyperliquid 交易4% 手續費折扣
arrow_forward