c1da0cbe65
Replaced the placeholder live node with a proper NautilusTrader TradingNode that connects to Hyperliquid Testnet using the official adapter. Added: - common/hyperliquid_api.py: direct REST calls to Hyperliquid's info endpoint for funding rates, predicted fundings, and asset contexts - backtests/run_backtest.py: CLI runner for strategy backtests - Updated funding_rate_arb.py to fetch real funding rates instead of using a hardcoded placeholder - Added requests to requirements.txt
76 lines
2.4 KiB
Python
76 lines
2.4 KiB
Python
"""
|
|
Hyperliquid API utilities.
|
|
|
|
Direct REST calls to Hyperliquid info endpoint for data
|
|
not yet covered by the NautilusTrader adapter (funding rates,
|
|
predicted fundings, asset contexts).
|
|
"""
|
|
import requests
|
|
from typing import Any
|
|
|
|
|
|
TESTNET_API = "https://api.hyperliquid-testnet.xyz/info"
|
|
MAINNET_API = "https://api.hyperliquid.xyz/info"
|
|
|
|
|
|
def _post(api_url: str, payload: dict) -> Any:
|
|
resp = requests.post(api_url, json=payload, timeout=10)
|
|
resp.raise_for_status()
|
|
return resp.json()
|
|
|
|
|
|
def get_asset_contexts(testnet: bool = True) -> list[dict]:
|
|
"""
|
|
Fetch asset contexts including current funding rates.
|
|
Returns list of per-asset dicts with keys:
|
|
funding, openInterest, markPx, oraclePx, premium, dayNtlVlm, etc.
|
|
"""
|
|
api = TESTNET_API if testnet else MAINNET_API
|
|
data = _post(api, {"type": "metaAndAssetCtxs"})
|
|
# data[0] = universe, data[1] = asset contexts
|
|
if isinstance(data, list) and len(data) >= 2:
|
|
return data[1]
|
|
return []
|
|
|
|
|
|
def get_funding_rate(asset_name: str, testnet: bool = True) -> float | None:
|
|
"""
|
|
Get the current funding rate for a specific asset.
|
|
Funding is paid every 8 hours. Positive = longs pay shorts.
|
|
"""
|
|
ctxs = get_asset_contexts(testnet=testnet)
|
|
for ctx in ctxs:
|
|
if isinstance(ctx, dict) and ctx.get("name") == asset_name.upper():
|
|
funding_str = ctx.get("funding", "0")
|
|
return float(funding_str)
|
|
return None
|
|
|
|
|
|
def get_all_funding_rates(testnet: bool = True) -> dict[str, float]:
|
|
"""Get funding rates for all assets on Hyperliquid."""
|
|
ctxs = get_asset_contexts(testnet=testnet)
|
|
rates = {}
|
|
for ctx in ctxs:
|
|
if isinstance(ctx, dict):
|
|
name = ctx.get("name", "")
|
|
funding_str = ctx.get("funding", "0")
|
|
if name:
|
|
rates[name] = float(funding_str)
|
|
return rates
|
|
|
|
|
|
def get_predicted_funding(asset_name: str, testnet: bool = True) -> float | None:
|
|
"""
|
|
Get the predicted funding rate for the next interval.
|
|
Uses the predictedFundings endpoint.
|
|
"""
|
|
api = TESTNET_API if testnet else MAINNET_API
|
|
data = _post(api, {"type": "predictedFundings"})
|
|
if isinstance(data, list):
|
|
for item in data:
|
|
if isinstance(item, dict) and item.get("name") == asset_name.upper():
|
|
# Return the Hyperliquid-specific prediction
|
|
predicted = item.get("funding", "0")
|
|
return float(predicted)
|
|
return None
|