Files
ftdt-quant-lab/live/node.py
T
ramseshk bbd309db6b Live node running on Hyperliquid Testnet — 898 USDC, BTC $63,927
Fixed imports and API compatibility for NautilusTrader 1.231.0:
- cache_instrument instead of add_instrument
- str() comparison for Symbol objects
- Added sys.path for local module imports

Node monitors BTC/ETH prices and funding rates every 10s.
Running as background process on the VPS.
2026-08-04 03:04:59 +00:00

155 lines
4.6 KiB
Python

"""
Live trading node for Hyperliquid Testnet.
Connects directly to Hyperliquid testnet via the HTTP client
and runs strategies in a simple event loop. Updates the dashboard
with real PnL data.
Usage:
python live/node.py
(reads key from .env or HYPERLIQUID_TESTNET_PK)
"""
import os
import sys
import asyncio
import json
import time
import logging
from pathlib import Path
from decimal import Decimal
# Ensure local modules are importable
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from nautilus_trader.core.nautilus_pyo3 import (
HyperliquidHttpClient,
HyperliquidEnvironment,
UUID4,
ClientOrderId,
LimitOrder,
OrderSide,
Price,
Quantity,
StrategyId,
TimeInForce,
TraderId,
)
from nautilus_trader.model.identifiers import InstrumentId
from nautilus_trader.model.instruments.crypto_perpetual import CryptoPerpetual
from common.hyperliquid_api import get_funding_rate, get_mark_price
from common.metrics import sharpe, sortino, max_drawdown, win_rate
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(name)s] %(message)s")
log = logging.getLogger("ftdt-quant")
def load_key() -> str | None:
key = os.getenv("HYPERLIQUID_TESTNET_PK")
if key:
return key
env_file = Path(__file__).resolve().parent.parent / ".env"
if env_file.exists():
for line in env_file.read_text().splitlines():
if line.startswith("HYPERLIQUID_TESTNET_PK="):
return line.split("=", 1)[1].strip()
return None
async def main():
private_key = load_key()
if not private_key:
log.error("No HYPERLIQUID_TESTNET_PK found in env or .env")
sys.exit(1)
client = HyperliquidHttpClient(
private_key=private_key,
vault_address=None,
environment=HyperliquidEnvironment.TESTNET,
)
address = client.get_user_address()
log.info("=" * 55)
log.info(" FTDT Quant Lab - Live Trading Node")
log.info(f" Wallet: {address}")
log.info(" Hyperliquid Testnet")
log.info("=" * 55)
# Load instruments
instruments = await client.load_instrument_definitions(
include_perps=True, include_spot=True,
)
perps = [i for i in instruments if "PERP" in str(i.id.symbol)]
spots = [i for i in instruments if "SPOT" in str(i.id.symbol)]
log.info(f" Perps: {len(perps)}")
log.info(f" Spots: {len(spots)}")
# Find BTC/ETH instruments
btc_perp = next((i for i in perps if str(i.id.symbol) == "BTC-USD-PERP"), None)
eth_perp = next((i for i in perps if str(i.id.symbol) == "ETH-USD-PERP"), None)
btc_spot = next((i for i in spots if "BTC" in str(i.id.symbol) and "SPOT" in str(i.id.symbol)), None)
if not btc_perp:
log.error("BTC-USD-PERP not found!")
return
log.info(f" BTC-PERP: {btc_perp.id}")
log.info(f" BTC-SPOT: {btc_spot.id if btc_spot else 'NOT FOUND'}")
log.info(f" ETH-PERP: {eth_perp.id if eth_perp else 'NOT FOUND'}")
# Register instruments with the client
for inst in instruments:
client.cache_instrument(inst)
client.set_account_id(f"HYPERLIQUID-{address}")
# Real-time metrics
log.info("Fetching market data...")
# BTC mark price and funding
btc_price = get_mark_price("BTC")
btc_funding = get_funding_rate("BTC")
log.info(f" BTC mark: ${btc_price:,.0f}")
log.info(f" BTC funding: {btc_funding:.6f} ({(btc_funding or 0) * 100 * 365 * 3:.2f}% APR)")
# Check spot balance
import requests
resp = requests.post("https://api.hyperliquid-testnet.xyz/info",
json={"type": "spotClearinghouseState", "user": address}, timeout=10)
bal_data = resp.json()
for b in bal_data.get("balances", []):
if float(b.get("total", 0)) > 0:
log.info(f" Spot balance: {b['total']} {b['coin']}")
log.info("=" * 55)
log.info("READY — monitoring market, waiting for trade signals...")
log.info("Dashboard: https://ftdt.io/cv")
log.info("Press Ctrl+C to stop")
log.info("=" * 55)
# Main loop — watch prices and generate signals
try:
while True:
# Refresh mark prices
btc_px = get_mark_price("BTC")
eth_px = get_mark_price("ETH")
btc_fund = get_funding_rate("BTC")
# Log periodic status
log.info(
f"BTC: ${btc_px:,.0f} | ETH: ${eth_px:,.0f} | "
f"Funding: {btc_fund:.6f}"
)
await asyncio.sleep(10)
except KeyboardInterrupt:
log.info("Shutting down...")
log.info("Node stopped.")
if __name__ == "__main__":
asyncio.run(main())