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

回測與實盤機器人之間的差距,大部分是管線工程
每一個把策略從研究搬到生產的量化交易者,都知道那個難堪的真相:alpha 大概只佔工作的 20%。另外的 80% 是不洩漏金鑰的身分驗證、能在部分成交中存活的委託管線、一條不會無聲變舊的資料流,以及讓你不會在最糟時刻被限流的速率限制紀律。Hyperliquid 在這方面是異常友善的地盤,因為它出貨了一個第一方、MIT 授權的 Python SDK,包裝了交易所自己使用的同一套 REST 與 WebSocket API。本指南會走過如何針對那套 API 建構一個實盤交易機器人,且是以你實際會運行的方式:讀取市場狀態、用一個 API 錢包驗證身分、下單與撤單、訂閱一條即時資料流,並處理那些在生產環境中弄垮機器人的失敗模式。
這是一份給運行自動化策略的開發者的技術建構指南。它假設你熟悉 Python、私鑰處理,以及永續期貨的機制。在你讓任何一美元的部位流經它之前,先在 testnet 上把每件事都測過。
重點
官方的 hyperliquid-python-sdk(
pip 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 物件之後,所以切換只是一個建構子的旗標。

你要如何設定 SDK 並讀取市場狀態?
安裝套件,並從一個唯讀客戶端開始。Info 類別完全不需要金鑰,這讓它成為在你碰簽署之前、對連線做健全性檢查的正確位置。當你只想要請求/回應式的呼叫時,傳入 skip_ws=True。
pip install hyperliquid-python-sdkfrom 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_URL 與 constants.MAINNET_API_URL。SDK 自己的範例預設用 testnet,在機器人被證實之前你也該如此。這裡的每一個方法,都記載於 SDK 的 hyperliquid/info.py,並在該倉庫的 examples/ 目錄中有對應範例。
你要如何在不洩漏金鑰的情況下驗證身分?
這是要讀兩遍的一節。Hyperliquid 把你的主帳戶——那個持有資金的錢包——與一個只被授權簽署動作的 API 錢包(文件也稱它為 agent 錢包)分開。你在 https://app.hyperliquid.xyz/API 產生並核准一個 API 錢包。這個 API 錢包能代表主帳戶下單與撤單,但它從不持有託管,且可以在不移動資金的情況下被解除註冊。這正是你為一個無人值守機器人所想要的分離:如果 API 金鑰被入侵,爆炸半徑是交易動作,而不是你餘額的提領。
官方文件明確說明的兩條規則:
把主帳戶的公開位址設為
account_address。一個常見的陷阱是傳入 agent 錢包的位址,那會導致查詢結果為空。把 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,並根據實際結果分支——一個狀態回來可能是 resting、filled 或 error,而假設成功,正是機器人漏掉部位的方式。
對於市價單,SDK 提供 market_open 與 market_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)可用的訂閱類型包括 allMids、l2Book、trades、candle、bbo、userEvents、userFills 與 orderUpdates 等等。限制是以每個 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 並發一把新的——不要重複使用那個位址。

軟體錯誤會動用真金白銀的部位。 一個簽署錯誤、部位大小計算上的差一錯誤,或一個把一筆「失敗」(其實成功了)的委託重新提交的重試迴圈,全都可能以你未曾打算的方式進行交易。用硬性的、程式碼層級的護欄把這圈住:一個最大委託大小、在每次提交前的一個最大未平倉部位檢查,以及一個會平倉並停止的 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 錢包驗證身分、查詢正確的位址、檢視每一個回應、把你的委託框住,並尊重那些限制。
來源
延伸閱讀
Hyperliquid 六分鐘:從 CEX 到鏈上永續合約的交易者備忘單
如果你能閱讀 Binance 的訂單簿,你就可以在 Hyperliquid 上交易——但底下的賬戶看起來與 CEX 賬戶完全不同。這裡是變化的內容,以及你首先需要檢查的事項。
HyperEVM 入門:錢包、Gas 和 Core 到 EVM 的轉帳。
HyperEVM 並非您需要橋接到的獨立鏈,它是 Hyperliquid 單一狀態的 EVM 部分。本內容將說明如何新增網路、獲取 HYPE 作為 gas,以及如何在 HyperCore 和 HyperEVM 之間安全地轉移資產,包括一個會銷毀您代幣的地址。
永續合約:做多、做空,以及已實現與未實現盈虧
做多還是做空、已實現還是未實現盈虧、標記價格與資金費率——為你在 Hyperliquid 的第一筆永續交易講清楚。