Nautilus Trader Skill
Comprehensive assistance with NautilusTrader development. Includes complete Hyperliquid mainnet integration with SDK patch for live trading.
Overview
This skill covers:
- Strategy development with NautilusTrader
- Backtesting using the Parquet data catalog
- Live trading deployment on Hyperliquid mainnet
- SDK patch for Hyperliquid price precision requirements
When to Use
- Building trading strategies with NautilusTrader
- Running backtests with historical data
- Deploying strategies to Hyperliquid mainnet
- Debugging NautilusTrader adapter issues
- Working with multi-timeframe (MTF) indicators
Prerequisites
Core Dependencies
# NautilusTrader (backtesting + live trading framework)
pip install nautilus_trader
# Hyperliquid SDK (for live trading patch)
pip install hyperliquid-python-sdk eth-account python-dotenv
# Data handling
pip install pandas numpy
Verify Installation
import nautilus_trader
print(f"Nautilus Trader: {nautilus_trader.__version__}")
# Tested with v1.222.0
Environment Variables
Create a .env file for Hyperliquid credentials:
HYPERLIQUID_PK=your_private_key_without_0x_prefix
HYPERLIQUID_VAULT=0xYourVaultAddressHere
Quick Start
1. Apply the Hyperliquid Patch (for live trading)
# CRITICAL: Import patch BEFORE Nautilus Trader
import hyperliquid_patch
# Then import Nautilus normally
from nautilus_trader.adapters.hyperliquid import HYPERLIQUID
from nautilus_trader.live.node import TradingNode
2. Basic Strategy Template
from nautilus_trader.trading.strategy import Strategy
from nautilus_trader.config import StrategyConfig
from nautilus_trader.model.data import Bar, BarType
from nautilus_trader.model.enums import OrderSide, TimeInForce
from nautilus_trader.model.identifiers import InstrumentId
from decimal import Decimal
class MyStrategyConfig(StrategyConfig):
instrument_id: str
bar_type: str
trade_size: Decimal = Decimal("0.1")
class MyStrategy(Strategy):
def __init__(self, config: MyStrategyConfig):
super().__init__(config)
self.instrument_id = InstrumentId.from_str(config.instrument_id)
self.bar_type = BarType.from_str(config.bar_type)
self.trade_size = config.trade_size
def on_start(self):
self.instrument = self.cache.instrument(self.instrument_id)
self.subscribe_bars(self.bar_type)
def on_bar(self, bar: Bar):
# Your strategy logic here
pass
def on_stop(self):
self.close_all_positions(self.instrument_id)
Strategy Development
Heiken Ashi Indicator
from nautilus_trader.indicators.base.indicator import Indicator
from nautilus_trader.model.data import Bar
class HeikenAshi(Indicator):
"""Heiken Ashi candle smoothing indicator."""
def __init__(self):
super().__init__([])
self.ha_open = 0.0
self.ha_close = 0.0
self.ha_high = 0.0
self.ha_low = 0.0
self._prev_ha_open = None
self._prev_ha_close = None
self.initialized = False
def handle_bar(self, bar: Bar) -> None:
o, h, l, c = float(bar.open), float(bar.high), float(bar.low), float(bar.close)
self.ha_close = (o + h + l + c) / 4
if self._prev_ha_open is None:
self.ha_open = (o + c) / 2
else:
self.ha_open = (self._prev_ha_open + self._prev_ha_close) / 2
self.ha_high = max(h, self.ha_open, self.ha_close)
self.ha_low = min(l, self.ha_open, self.ha_close)
self._prev_ha_open = self.ha_open
self._prev_ha_close = self.ha_close
self.initialized = True
@property
def is_bullish(self) -> bool:
return self.ha_close > self.ha_open
@property
def is_bearish(self) -> bool:
return self.ha_close < self.ha_open
def reset(self) -> None:
self._prev_ha_open = None
self._prev_ha_close = None
self.initialized = False
Multi-Timeframe EMA Strategy
See references/hyperliquid.md for complete MTF EMA + Heiken Ashi strategy implementation.
Key concepts:
- HTF (Higher Timeframe): Determines trend direction via EMA crossover
- LTF (Lower Timeframe): Entry timing via Heiken Ashi confirmation
- Entry: HA color change in trend direction
- Exit: HA color reversal
Backtesting
Engine Setup
from nautilus_trader.backtest.engine import BacktestEngine, BacktestEngineConfig
from nautilus_trader.model.currencies import USD
from nautilus_trader.model.enums import AccountType, OmsType
from nautilus_trader.model.identifiers import Venue
from nautilus_trader.model.objects import Money
from nautilus_trader.persistence.catalog import ParquetDataCatalog
from decimal import Decimal
def run_backtest():
config = BacktestEngineConfig(
trader_id="BACKTESTER-001",
logging_level="INFO",
)
engine = BacktestEngine(config=config)
# Add venue
engine.add_venue(
venue=Venue("HYPERLIQUID"),
oms_type=OmsType.NETTING,
account_type=AccountType.MARGIN,
base_currency=USD,
starting_balances=[Money(100_000, USD)],
)
# Load data from catalog
catalog = ParquetDataCatalog("./data_catalog")
instruments = catalog.instruments()
for instrument in instruments:
engine.add_instrument(instrument)
bars = catalog.bars()
engine.add_data(bars)
# Add strategy
strategy = MyStrategy(config=MyStrategyConfig(
instrument_id="SOL-USD.HYPERLIQUID",
bar_type="SOL-USD.HYPERLIQUID-5-MINUTE-LAST-EXTERNAL",
trade_size=Decimal("1.0"),
))
engine.add_strategy(strategy)
# Run
engine.run()
# Results
print(engine.trader.generate_account_report(Venue("HYPERLIQUID")))
print(engine.trader.generate_order_fills_report())
print(engine.trader.generate_positions_report())
engine.dispose()
Data Catalog
See references/backtesting.md and references/data.md for detailed catalog operations:
ParquetDataCatalog- Query and manage Parquet data filesBarDataWrangler- Convert pandas DataFrames to Nautilus Bar objectswrite_data()- Persist data to catalogquery()- Retrieve data with time filters
Live Trading on Hyperliquid
Node Configuration
import os
from dotenv import load_dotenv
load_dotenv()
# CRITICAL: Apply patch BEFORE Nautilus imports
import hyperliquid_patch
from nautilus_trader.adapters.hyperliquid import (
HYPERLIQUID,
HyperliquidDataClientConfig,
HyperliquidExecClientConfig,
)
from nautilus_trader.live.node import TradingNode, TradingNodeConfig
from nautilus_trader.config import LiveDataEngineConfig, LiveExecEngineConfig
def main():
node_config = TradingNodeConfig(
trader_id="LIVE-001",
data_engine=LiveDataEngineConfig(),
exec_engine=LiveExecEngineConfig(),
)
node = TradingNode(config=node_config)
data_config = HyperliquidDataClientConfig(
wallet_address=os.getenv("HYPERLIQUID_VAULT"),
is_testnet=False,
)
exec_config = HyperliquidExecClientConfig(
wallet_address=os.getenv("HYPERLIQUID_VAULT"),
private_key=os.getenv("HYPERLIQUID_PK"),
is_testnet=False,
)
node.build()
# Add your strategy
strategy = MyStrategy(config=my_config)
node.trader.add_strategy(strategy)
node.run()
if __name__ == "__main__":
main()
Set Leverage (One-Time Setup)
from hyperliquid.exchange import Exchange
from hyperliquid.utils import constants
from eth_account import Account
import os
private_key = os.getenv("HYPERLIQUID_PK")
if not private_key.startswith("0x"):
private_key = "0x" + private_key
account = Account.from_key(private_key)
exchange = Exchange(account, constants.MAINNET_API_URL)
# Set 10x leverage for SOL (cross margin)
exchange.update_leverage(10, "SOL", is_cross=True)
Network Latency
For best performance, deploy on AWS ap-northeast-1 (Tokyo):
- Ping to Hyperliquid CloudFront: ~1ms
- API latency: ~28ms
Hyperliquid SDK Patch
The Problem
Nautilus Trader v1.222.0 has bugs in the Hyperliquid adapter:
- Rust HTTP client serialization causes type mismatches
- Price precision exceeds Hyperliquid's 5 significant figure limit
The Solution
Bypass the buggy adapter using the official Hyperliquid Python SDK. The patch file is located at references/hyperliquid_patch.py.
Price Precision Rules
Hyperliquid requires maximum 5 significant figures for all prices:
| Price | Valid? | Sig Figs |
|---|---|---|
| $139.05 | Yes | 5 |
| $139.054 | No | 6 |
| $1.2345 | Yes | 5 |
| $1.23456 | No | 6 |
| $12345 | Yes | 5 |
| $123456 | No | 6 |
Usage
# CRITICAL: Import patch BEFORE any Nautilus imports
import hyperliquid_patch
# Then import Nautilus normally
from nautilus_trader.adapters.hyperliquid import HYPERLIQUID
The patch auto-applies on import and handles:
- Price formatting to 5 significant figures
- Rounding up for buys, down for sells (ensures fills)
- SDK-based order submission bypassing Rust client
Verified Working
Tested on Hyperliquid Mainnet 2025-01-12:
SELL 0.72 SOL @ $143.38 - FILLED
BUY 0.71 SOL @ $143.39 - FILLED
Configuration
File Structure
your_trading_project/
├── .env # Credentials (gitignored)
├── hyperliquid_patch.py # SDK patch for live trading
├── heiken_ashi.py # Heiken Ashi indicator
├── my_strategy.py # Strategy implementation
├── backtest.py # Backtest runner
├── live.py # Live trading runner
└── data_catalog/ # Parquet data for backtesting
Bar Type Format
{symbol}.{venue}-{step}-{aggregation}-{price_type}-{source}
Examples:
SOL-USD.HYPERLIQUID-1-HOUR-LAST-EXTERNAL
SOL-USD.HYPERLIQUID-5-MINUTE-LAST-EXTERNAL
BTC-USD.HYPERLIQUID-15-MINUTE-LAST-EXTERNAL
Troubleshooting
Order Rejected: Invalid Price
Ensure prices have max 5 significant figures. Use the format_price_5_sigfigs() function from the patch.
Connection Error
- Check
.envhas correctHYPERLIQUID_PKandHYPERLIQUID_VAULT - Verify private key format (with or without
0xprefix) - Confirm vault address is correct
Patch Not Applied
Ensure import hyperliquid_patch comes BEFORE any Nautilus imports.
Missing Data in Backtest
- Verify data catalog path exists
- Check instrument IDs match between data and strategy config
- Ensure bar types are correctly formatted
Position Not Closing
Check that reduce_only=True is set on exit orders for netting accounts.
Reference Files
Detailed documentation is available in references/:
| File | Description |
|---|---|
hyperliquid.md | Complete Hyperliquid integration guide |
hyperliquid_patch.py | SDK patch source code |
strategies.md | Strategy patterns and examples |
backtesting.md | Data catalog and backtest API |
data.md | Data handling and wrangling |
getting_started.md | NautilusTrader fundamentals |
concepts.md | Core concepts and architecture |
api.md | Full API reference |
Use view to read specific reference files when detailed information is needed.