【ハイリキ】Hyperliquid Python SDK×GitHub Codespaces環境にてExchange API取引するまで
前回記事ではGoogle ColaboとHyperliquid Python SDKを使ってinfo APIから情報を引っ張ってくるまでをやってみました。
今回は実際のbotを動作させるのに近い環境の構築とその中でExchange APIを使って実際に取引させてみるまでやってみました。
公式ドキュメント
Hyperliquid Docs API
hyperliquid-python-sdk
環境構築の背景
クリプト界隈ではScamが多く存在するのでコンテナ環境であってもローカルでverifyできてないコードを動作させるのは怖いなと思いクラウド環境ベースで実装できるものを探していました。
botter系の記事を見ながらGitHub Codespacesを活用するのが手軽そうだったのでこちらを活用して今回は構築しました。
GitHub Codespaces / VSCodeの準備
環境構築についてはまちゅけんさんの以下のエントリーが大変参考になりました。
初期導入で大まかにやること
(詳細はまちゅけんさんの記事をご覧ください)
1.VS Codeのインストール
2.VS CodeのGitHub Codespaces用のテンプレートをインストール
3.まちゅけんさんのテンプレートを引っ張ってくる
Codespacesの起動方法
- Codespacesの横の「+」ボタンから起動
- 起動後にBranchの横ボタンからメニューを開いて「Open in Visiual Studio Code」を選択するとVS Codeにて編集可能になる
利用量の確認ページ
以下ページから確認可能
hyperliquid-python-sdkの導入と初期設定
必要なSDKやライブラリーのインストール
以下VSCodeのコンソール上からコマンド叩いてHyperliquidのPythonSDKや必要になりそうなライブラリーをインストールしておきます。
poetry update
poetry add hyperliquid-python-sdk
poetry add plotly
poetry add pandas
poetry add requests
poetry add python-dotenv
※必要環境によっては不要なファイルもあるので適宜取捨選択してください。
.envファイルの作成
APIキーなど環境変数を管理するための.envファイルを作成します。
今回は以下のようなフォーマットの.envファイルを作成しています。
# .env
ENVIRONMENT=TESTNET # または MAINNET
# TESTNET設定
TESTNET_SECRET_KEY=0xxxxxxxxxxxxxxxxxxxxxxxx
TESTNET_ACCOUNT_ADDRESS=0xxxxxxxxxxxxxxxxxxxxxxxx
TESTNET_API_URL=https://api.hyperliquid-testnet.xyz
# MAINNET設定
MAINNET_SECRET_KEY=0xxxxxxxxxxxxxxxxxxxxxxxx
MAINNET_ACCOUNT_ADDRESS=0xxxxxxxxxxxxxxxxxxxxxxxx
MAINNET_API_URL=https://api.hyperliquid.xyz
gitignoreファイルの編集
.gitignoreファイル内を編集して.envファイルをスルーするように設定
Hyperliquid APIを使う上での初期設定
テストネットトークンの手配
Arbitrum ETHの入手
alchemyのページからArbitrum Sepolia ETHを入手します
mock USDCの入手
Hyperliquidで使うUSDCを入手します
Hyperliquid APIキー入手
メインネットとテストネットでそれぞれ異なるので注意してください
Mainnet
Testnet
Exchange APIでSpot取引をしてみる
サンプルコード
import os
from dotenv import load_dotenv
from eth_account import Account
from hyperliquid.exchange import Exchange
from hyperliquid.info import Info
from hyperliquid.utils.constants import TESTNET_API_URL, MAINNET_API_URL
# 環境変数を読み込み
load_dotenv(override=True)
ENVIRONMENT = os.getenv("ENVIRONMENT", "TESTNET").upper()
SECRET_KEY = os.getenv(f"{ENVIRONMENT}_SECRET_KEY")
ACCOUNT_ADDRESS = os.getenv(f"{ENVIRONMENT}_ACCOUNT_ADDRESS")
API_URL = os.getenv(f"{ENVIRONMENT}_API_URL")
def main():
if not SECRET_KEY or not ACCOUNT_ADDRESS or not API_URL:
raise ValueError(".envファイルに必要な環境変数が不足しています。")
# アカウントと取引所の初期化
account = Account.from_key(SECRET_KEY)
info = Info(base_url=API_URL, skip_ws=True)
exchange = Exchange(account, base_url=API_URL, account_address=ACCOUNT_ADDRESS)
# ユーザーのスポット残高を表示
spot_user_state = info.spot_user_state(ACCOUNT_ADDRESS)
balances = spot_user_state.get("balances", [])
if balances:
print("スポット残高:")
for balance in balances:
print(balance)
else:
print("利用可能なスポット残高がありません。")
print("DEBUG: spot_user_state:", spot_user_state)
print("DEBUG: ACCOUNT_ADDRESS:", ACCOUNT_ADDRESS)
# シンプルなスポット注文を出す
COIN_PAIR = "HYPE/USDC"
IS_BUY = True # Trueは買い注文、Falseは売り注文
SIZE = 1 # 売買数量
LIMIT_PRICE = 20 # 単価
ORDER_TYPE = {"limit": {"tif": "Gtc"}} # Gtc (Good Till Canceled) の注文
print("注文を送信中...")
order_result = exchange.order(COIN_PAIR, IS_BUY, SIZE, LIMIT_PRICE, ORDER_TYPE)
print("注文結果:", order_result)
# 注文ステータスを確認
if order_result["status"] == "ok":
status = order_result["response"]["data"]["statuses"][0]
if "resting" in status:
oid = status["resting"]["oid"]
order_status = info.query_order_by_oid(ACCOUNT_ADDRESS, oid)
print("注文ステータス:", order_status)
if __name__ == "__main__":
main()
うまくいった場合のレスポンス
スポット残高:
{'coin': 'USDC', 'token': 0, 'total': '100.0', 'hold': '0.0', 'entryNtl': '0.0'}
注文を送信中...
注文結果: {'status': 'ok', 'response': {'type': 'order', 'data': {'statuses': [{'resting': {'oid': 0000000000}}]}}}
注文ステータス: {'status': 'order', 'order': {'order': {'coin': '@60', 'side': 'B', 'limitPx': '20.0', 'sz': '0.1', 'oid': 0000000000, 'timestamp': 1737248863, 'triggerCondition': 'N/A', 'isTrigger': False, 'triggerPx': '0.0', 'children': [], 'isPositionTpsl': False, 'reduceOnly': False, 'orderType': 'Limit', 'origSz': '0.2', 'tif': 'Gtc', 'cloid': None}, 'status': 'open', 'statusTimestamp': 1737248863}}
実装時にハマったポイント
L1 error: User or API Wallet does not exist.
このエラーの解消に1週間ほどかかってしまいました。
Order result: {'status': 'err', 'response': 'L1 error: User or API Wallet 0x00000000000000000000 does not exist.'}
まずAPI Walletのアドレスが管理画面で発行したAPIキーに紐づく公開鍵になっているか確認してください。おそらく上記エラーがでている場合は不一致を起こしているはずです。
Discordコミュニティで参考にしたやりとり
エラーメッセージで検索していると同じエラーでハマっている人が結構いました。
解消したときに参考になったやりとりはこちらでした。
Make sure the field order is correct. It's important when creating a signature
フィールドの順序が正しいことを確認してください。署名を作成する際に重要です
レスポンスのアドレスの値がAPIキーから生成した公開鍵になっていなかったというのは署名の際のフィールドに何かしらの問題があるとのことでした。
ここから署名部分のコードやサンプルコードを確認して解消しました。
公開鍵の残高の反映
APIキーから紐づく公開鍵で残高チェックした際にSpotUSDCの残高が反映されなかった問題。
実際の残高操作はAPIキーに紐づく公開鍵ではなく、APIキーを発行したときにHYPEにログインしているアカウントの公開鍵に紐づいています。
API wallets (also known as agent wallets) can perform actions on behalf of your account without having withdrawal permissions. You must still use your account's public address for info requests.
APIウォレット(エージェントウォレットとも呼ばれます)は、出金権限がなくても、アカウントに代わってアクションを実行することができます。情報リクエストには、アカウントのパブリックアドレスを使用する必要があります。
キャッシュ問題
envファイルを変更しても保存してからコード実行しても反映されない問題がありました。
envファイルから読み取った設定のAPIキーをコンソール上で呼び出してみて変なキャッシュが残っていることが発覚。コンテナ再起動して解消させました。
LLM活用したビギナー開発に関するメモ
LLMのコード生成する際にインプットした情報
- SDKのディレクトリー構成
- APIドキュメントとサンプルコードを統合したPDF
- ドキュメント全体入れないと前提が崩壊することがあるのですが、全体いれると個別箇所の精度が落ちます。がどの粒度でいれるか都度調整するのはめんどくさかったので全部入れた上で強調させたい部分はテキスト部分に参照内容を入れました
- Discord内での関連エラーのやりとり
Hyperliquid Python SDKのディレクトリー構成
.
├── Makefile
├── README.md
├── SECURITY.md
├── api
│ ├── components.yaml
│ └── info
│ ├── allmids.yaml
│ ├── assetctxs.yaml
│ ├── candle.yaml
│ ├── l2book.yaml
│ └── userstate.yaml
├── assets
│ └── images
│ └── coverage.svg
├── directory_structure.txt
├── hyperliquid
│ ├── __init__.py
│ ├── api.py
│ ├── exchange.py
│ ├── info.py
│ ├── utils
│ │ ├── __init__.py
│ │ ├── constants.py
│ │ ├── error.py
│ │ ├── signing.py
│ │ └── types.py
│ └── websocket_manager.py
├── poetry.lock
├── pyproject.toml
├── requirements-ci.txt
├── requirements.txt
├── setup.cfg
SDK内で参考にした部分
example_utils.py
basic_spot_order.py
signing_test.py
その他
- 最初はコーディングからエラー修正までLLMに任せっきりの方向でやってましたがエラー解消がにっちもさっちもいかなくなり、結局ある程度のプログラミングの知見が必要なフェーズだと感じました。
- Pythonいじるのも久しぶりだったので以下復習してから臨んだら問題解決早まりました。
セットアップしたgitのレポジトリ
Discussion