💰

【ハイリキ】Hyperliquid Python SDK×GitHub Codespaces環境にてExchange API取引するまで

2025/01/19に公開

前回記事ではGoogle ColaboとHyperliquid Python SDKを使ってinfo APIから情報を引っ張ってくるまでをやってみました。
今回は実際のbotを動作させるのに近い環境の構築とその中でExchange APIを使って実際に取引させてみるまでやってみました。
https://zenn.dev/kayato/articles/8691c03c66c259

公式ドキュメント

Hyperliquid Docs API

https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api

hyperliquid-python-sdk

https://github.com/hyperliquid-dex/hyperliquid-python-sdk

環境構築の背景

クリプト界隈ではScamが多く存在するのでコンテナ環境であってもローカルでverifyできてないコードを動作させるのは怖いなと思いクラウド環境ベースで実装できるものを探していました。
botter系の記事を見ながらGitHub Codespacesを活用するのが手軽そうだったのでこちらを活用して今回は構築しました。

GitHub Codespaces / VSCodeの準備

環境構築についてはまちゅけんさんの以下のエントリーが大変参考になりました。
https://zenn.dev/mtkn1/articles/a455bb8732e52e

初期導入で大まかにやること

(詳細はまちゅけんさんの記事をご覧ください)

1.VS Codeのインストール

https://code.visualstudio.com/Download

2.VS CodeのGitHub Codespaces用のテンプレートをインストール

https://marketplace.visualstudio.com/items?itemName=GitHub.codespaces

3.まちゅけんさんのテンプレートを引っ張ってくる

https://github.com/MtkN1/bot-dev-container

Codespacesの起動方法

  1. Codespacesの横の「+」ボタンから起動
  2. 起動後にBranchの横ボタンからメニューを開いて「Open in Visiual Studio Code」を選択するとVS Codeにて編集可能になる

利用量の確認ページ


以下ページから確認可能
https://github.com/settings/billing/summary

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を入手します
https://www.alchemy.com/faucets/arbitrum-sepolia

mock USDCの入手

Hyperliquidで使うUSDCを入手します
https://app.hyperliquid-testnet.xyz/drip

Hyperliquid APIキー入手

メインネットとテストネットでそれぞれ異なるので注意してください

Mainnet

https://app.hyperliquid.xyz/API

Testnet

https://app.hyperliquid-testnet.xyz/API

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

https://github.com/hyperliquid-dex/hyperliquid-python-sdk/blob/master/examples/example_utils.py

basic_spot_order.py

https://github.com/hyperliquid-dex/hyperliquid-python-sdk/blob/master/examples/basic_spot_order.py

signing_test.py

https://github.com/hyperliquid-dex/hyperliquid-python-sdk/blob/master/tests/signing_test.py

その他

  • 最初はコーディングからエラー修正までLLMに任せっきりの方向でやってましたがエラー解消がにっちもさっちもいかなくなり、結局ある程度のプログラミングの知見が必要なフェーズだと感じました。
    • Pythonいじるのも久しぶりだったので以下復習してから臨んだら問題解決早まりました。

https://zenn.dev/kayato/articles/88db83b368c844

セットアップしたgitのレポジトリ

https://github.com/KayatoX/bot-dev-container-HyperliquidSDK/tree/main

Discussion