""" Live trading node for Hyperliquid Testnet. Runs all five quant strategies against the Hyperliquid testnet using NautilusTrader's event-driven architecture. Strategies share a risk manager and portfolio tracker. Usage: export HYPERLIQUID_TESTNET_PK=0x... python live/node.py """ import os import sys import asyncio from pathlib import Path from nautilus_trader.config import TradingNodeConfig from nautilus_trader.config import LiveDataEngineConfig from nautilus_trader.config import LiveRiskEngineConfig from nautilus_trader.config import LiveExecEngineConfig from nautilus_trader.model.identifiers import TraderId from nautilus_trader.common.enums import Environment from nautilus_trader.live.node import TradingNode from nautilus_trader.adapters.hyperliquid.config import ( HyperliquidDataClientConfig, HyperliquidExecClientConfig, ) from nautilus_trader.adapters.hyperliquid.factories import ( HyperliquidLiveDataClientFactory, HyperliquidLiveExecClientFactory, ) def build_node(private_key: str) -> TradingNode: """Build and configure the trading node with all strategies.""" data_config = HyperliquidDataClientConfig( environment="testnet", http_timeout_secs=30, ) exec_config = HyperliquidExecClientConfig( private_key=private_key, environment="testnet", normalize_prices=True, http_timeout_secs=30, ) node_config = TradingNodeConfig( trader_id=TraderId("FTDT-QUANT-001"), environment=Environment.LIVE, data_engine=LiveDataEngineConfig(), risk_engine=LiveRiskEngineConfig(), exec_engine=LiveExecEngineConfig(), data_clients={ "HYPERLIQUID": data_config, }, exec_clients={ "HYPERLIQUID": exec_config, }, timeout_connection=30.0, timeout_reconciliation=15.0, timeout_portfolio=15.0, timeout_disconnection=15.0, timeout_post_stop=5.0, ) node = TradingNode(config=node_config) # Register the Hyperliquid client factories node.add_data_client_factory("HYPERLIQUID", HyperliquidLiveDataClientFactory) node.add_exec_client_factory("HYPERLIQUID", HyperliquidLiveExecClientFactory) return node def register_strategies(node: TradingNode) -> None: """Register all five strategies with the trading node.""" # Import strategies here to avoid circular imports from strategies.orderbook_imbalance import ( OrderBookImbalance, OrderBookImbalanceConfig, ) from strategies.iceberg_detection import ( IcebergDetector, IcebergDetectorConfig, ) from strategies.funding_rate_arb import ( FundingRateArb, FundingRateArbConfig, ) from strategies.pairs_trading import ( PairsTrading, PairsTradingConfig, ) from strategies.avellaneda_stoikov import ( AvellanedaStoikov, AvellanedaStoikovConfig, ) # 1. Order Book Imbalance node.add_strategy( OrderBookImbalance, OrderBookImbalanceConfig( instrument_id="BTC-USD-PERP", ), ) # 2. Iceberg / TWAP Detection node.add_strategy( IcebergDetector, IcebergDetectorConfig( instrument_id="BTC-USD-PERP", ), ) # 3. Funding Rate Arbitrage node.add_strategy( FundingRateArb, FundingRateArbConfig( spot_instrument="BTC-SPOT", perp_instrument="BTC-USD-PERP", ), ) # 4. Pairs Trading (BTC/ETH) node.add_strategy( PairsTrading, PairsTradingConfig( pair=("BTC-USD-PERP", "ETH-USD-PERP"), ), ) # 5. Avellaneda-Stoikov Market Making node.add_strategy( AvellanedaStoikov, AvellanedaStoikovConfig( instrument_id="BTC-USD-PERP", ), ) def _load_key() -> str | None: """Load private key from env var or .env file.""" key = os.getenv("HYPERLIQUID_TESTNET_PK") if key: return key # Fallback: read from .env file 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: print("ERROR: Set HYPERLIQUID_TESTNET_PK environment variable") print(" or create a .env file in the project root.") sys.exit(1) print("=" * 55) print(" FTDT Quant Lab - Live Trading Node") print(" Hyperliquid Testnet") print("=" * 55) print() print("Strategies:") print(" 1. Order Book Imbalance (OFI)") print(" 2. Iceberg / TWAP Detection") print(" 3. Funding Rate Arbitrage") print(" 4. Pairs Trading (BTC/ETH)") print(" 5. Avellaneda-Stoikov Market Making") print() node = build_node(private_key) register_strategies(node) print("Connecting to Hyperliquid Testnet...") try: await node.start() print("Node started. Running strategies...") print("Press Ctrl+C to stop.") await node.run_until_stopped() except KeyboardInterrupt: print("\nShutting down...") finally: await node.stop() print("Node stopped. Goodbye.") if __name__ == "__main__": asyncio.run(main())