use std::collections::HashMap;
use std::str::FromStr;
use std::sync::{Arc, Mutex};
use chrono::{DateTime, FixedOffset, Utc};
use ethers::signers::LocalWallet;
use hyperliquid_backtest::prelude::*;
use hyperliquid_backtest::live_trading::{
LiveTradingEngine, LiveTradingError, AlertLevel, AlertMessage,
RetryPolicy, SafetyCircuitBreakerConfig
};
use hyperliquid_backtest::trading_mode::{ApiConfig, RiskConfig, TradingConfig};
use hyperliquid_backtest::unified_data::{
Position, OrderRequest, OrderResult, MarketData,
OrderSide, OrderType, TimeInForce, OrderStatus,
TradingStrategy, Signal, SignalDirection, FundingPayment
};
use hyperliquid_backtest::logging::init_logger;
struct FundingArbitrageStrategy {
name: String,
symbols: Vec<String>,
funding_threshold: f64,
position_size_base: f64,
max_positions: usize,
positions: HashMap<String, f64>,
signals: HashMap<String, Signal>,
funding_rates: HashMap<String, f64>,
next_funding_times: HashMap<String, DateTime<FixedOffset>>,
last_position_change: HashMap<String, DateTime<FixedOffset>>,
min_hold_hours: i64,
}
impl FundingArbitrageStrategy {
fn new(
symbols: Vec<String>,
funding_threshold: f64,
position_size_base: f64,
max_positions: usize,
min_hold_hours: i64,
) -> Self {
Self {
name: "Funding Arbitrage Strategy".to_string(),
symbols,
funding_threshold,
position_size_base,
max_positions,
positions: HashMap::new(),
signals: HashMap::new(),
funding_rates: HashMap::new(),
next_funding_times: HashMap::new(),
last_position_change: HashMap::new(),
min_hold_hours,
}
}
fn should_take_position(&self, symbol: &str, funding_rate: f64) -> Option<OrderSide> {
if funding_rate.abs() < self.funding_threshold {
return None;
}
if let Some(position_size) = self.positions.get(symbol) {
if (funding_rate > 0.0 && *position_size < 0.0) ||
(funding_rate < 0.0 && *position_size > 0.0) {
return None;
}
if let Some(last_change) = self.last_position_change.get(symbol) {
let now = Utc::now().with_timezone(&FixedOffset::east(0));
let hours_since_change = (now - *last_change).num_hours();
if hours_since_change < self.min_hold_hours {
return None;
}
}
}
if funding_rate > 0.0 {
Some(OrderSide::Sell) } else {
Some(OrderSide::Buy) }
}
fn calculate_position_size(&self, symbol: &str, funding_rate: f64) -> f64 {
let mut size = self.position_size_base;
let scale_factor = 1.0 + (funding_rate.abs() / self.funding_threshold - 1.0).min(1.0);
size *= scale_factor;
size
}
}
impl TradingStrategy for FundingArbitrageStrategy {
fn name(&self) -> &str {
&self.name
}
fn on_market_data(&mut self, data: &MarketData) -> Result<Vec<OrderRequest>, String> {
let symbol = &data.symbol;
if !self.symbols.contains(symbol) {
return Ok(Vec::new());
}
if let Some(funding_rate) = data.funding_rate {
self.funding_rates.insert(symbol.clone(), funding_rate);
if let Some(next_funding) = data.next_funding_time {
self.next_funding_times.insert(symbol.clone(), next_funding);
}
} else {
return Ok(Vec::new());
}
let now = Utc::now().with_timezone(&FixedOffset::east(0));
let funding_rate = *self.funding_rates.get(symbol).unwrap();
let mut orders = Vec::new();
if let Some(side) = self.should_take_position(symbol, funding_rate) {
let position_size = self.calculate_position_size(symbol, funding_rate);
if let Some(current_size) = self.positions.get(symbol) {
if *current_size != 0.0 {
let close_side = if *current_size > 0.0 {
OrderSide::Sell
} else {
OrderSide::Buy
};
orders.push(OrderRequest {
symbol: symbol.clone(),
side: close_side,
order_type: OrderType::Market,
quantity: current_size.abs(),
price: None,
reduce_only: true,
time_in_force: TimeInForce::ImmediateOrCancel,
client_order_id: Some(format!("close_{}_{}", symbol, now.timestamp())),
metadata: HashMap::new(),
});
}
}
orders.push(OrderRequest {
symbol: symbol.clone(),
side,
order_type: OrderType::Market,
quantity: position_size,
price: None,
reduce_only: false,
time_in_force: TimeInForce::ImmediateOrCancel,
client_order_id: Some(format!("open_{}_{}", symbol, now.timestamp())),
metadata: HashMap::new(),
});
let direction = match side {
OrderSide::Buy => SignalDirection::Buy,
OrderSide::Sell => SignalDirection::Sell,
};
self.signals.insert(
symbol.clone(),
Signal {
symbol: symbol.clone(),
direction,
strength: (funding_rate.abs() / self.funding_threshold).min(1.0),
timestamp: now,
metadata: {
let mut metadata = HashMap::new();
metadata.insert("funding_rate".to_string(), funding_rate.to_string());
metadata.insert("position_size".to_string(), position_size.to_string());
metadata
},
}
);
}
Ok(orders)
}
fn on_order_fill(&mut self, fill: &OrderResult) -> Result<(), String> {
let symbol = &fill.symbol;
let current_position = self.positions.entry(symbol.clone()).or_insert(0.0);
match fill.side {
OrderSide::Buy => {
*current_position += fill.filled_quantity;
},
OrderSide::Sell => {
*current_position -= fill.filled_quantity;
},
}
self.last_position_change.insert(
symbol.clone(),
Utc::now().with_timezone(&FixedOffset::east(0))
);
Ok(())
}
fn on_funding_payment(&mut self, payment: &FundingPayment) -> Result<(), String> {
println!("Funding payment received: {} {} (rate: {})",
payment.symbol, payment.amount, payment.rate);
Ok(())
}
fn get_current_signals(&self) -> HashMap<String, Signal> {
self.signals.clone()
}
}
async fn perform_safety_checks(engine: &LiveTradingEngine) -> Result<bool, LiveTradingError> {
println!("\n🔍 Performing Pre-Deployment Safety Checks");
println!("----------------------------------------");
println!("1. Verifying connection to exchange...");
if !engine.is_connected() {
println!("❌ Not connected to exchange");
return Ok(false);
}
println!("✅ Connected to exchange");
println!("2. Verifying account balance...");
let account_info = engine.get_account_info().await?;
if account_info.available_balance < 100.0 {
println!("❌ Insufficient balance: ${:.2}", account_info.available_balance);
return Ok(false);
}
println!("✅ Account balance: ${:.2}", account_info.available_balance);
println!("3. Verifying market data access...");
let btc_price = engine.get_current_price("BTC").await?;
if btc_price <= 0.0 {
println!("❌ Unable to fetch market data");
return Ok(false);
}
println!("✅ Market data accessible (BTC price: ${:.2})", btc_price);
println!("4. Verifying order placement capability...");
let can_place_orders = engine.can_place_orders().await?;
if !can_place_orders {
println!("❌ Cannot place orders");
return Ok(false);
}
println!("✅ Order placement capability verified");
println!("5. Verifying risk limits...");
let risk_limits = engine.get_risk_limits().await?;
println!("✅ Risk limits verified:");
println!(" - Max position size: ${:.2}", risk_limits.max_position_size);
println!(" - Max leverage: {:.1}x", risk_limits.max_leverage);
println!(" - Max daily loss: ${:.2}", risk_limits.max_daily_loss);
println!("6. Verifying emergency stop functionality...");
if !engine.test_emergency_stop().await? {
println!("❌ Emergency stop functionality not working");
return Ok(false);
}
println!("✅ Emergency stop functionality verified");
println!("\n✅ All safety checks passed");
Ok(true)
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
init_logger();
println!("Hyperliquid Live Trading Deployment Example");
println!("=========================================");
println!("⚠️ This example simulates a live trading deployment");
println!("⚠️ No actual trades will be executed");
println!("\n1. Creating Trading Configuration");
println!("--------------------------------");
let risk_config = RiskConfig {
max_position_size_pct: 0.05, max_daily_loss_pct: 0.01, stop_loss_pct: 0.03, take_profit_pct: 0.05, max_leverage: 2.0, max_concentration_pct: 0.15, max_position_correlation: 0.5, volatility_sizing_factor: 0.3, max_portfolio_volatility_pct: 0.03, max_drawdown_pct: 0.05, };
let api_config = ApiConfig {
api_key: "your_api_key".to_string(),
api_secret: "your_api_secret".to_string(),
endpoint: "https://api.hyperliquid-testnet.xyz".to_string(),
use_testnet: true, timeout_ms: 5000,
};
let trading_config = TradingConfig::new(10000.0) .with_risk_config(risk_config)
.with_api_config(api_config)
.with_parameter("enable_alerts", "true")
.with_parameter("max_open_orders", "3");
println!("✅ Trading configuration created with conservative risk settings");
println!("\n2. Setting Up Wallet");
println!("-------------------");
let private_key = "0000000000000000000000000000000000000000000000000000000000000001";
let wallet = LocalWallet::from_str(private_key).unwrap();
println!("✅ Wallet created");
println!("📝 Wallet address: {}", wallet.address());
println!("\n3. Configuring Safety Mechanisms");
println!("-------------------------------");
let retry_policy = RetryPolicy {
max_attempts: 3,
initial_delay_ms: 500,
backoff_factor: 2.0,
max_delay_ms: 5000,
};
let safety_config = SafetyCircuitBreakerConfig {
max_consecutive_failed_orders: 3,
max_order_failure_rate: 0.3,
order_failure_rate_window: 10,
max_position_drawdown_pct: 0.10,
max_account_drawdown_pct: 0.05,
max_price_deviation_pct: 0.03,
price_deviation_window_sec: 60,
max_critical_alerts: 2,
critical_alerts_window: 10,
};
println!("✅ Safety mechanisms configured:");
println!(" - Retry policy: {} attempts with backoff", retry_policy.max_attempts);
println!(" - Circuit breaker: {:.1}% max account drawdown", safety_config.max_account_drawdown_pct * 100.0);
println!(" - Price deviation protection: {:.1}%", safety_config.max_price_deviation_pct * 100.0);
println!("\n4. Creating Live Trading Engine");
println!("------------------------------");
let mut engine = LiveTradingEngine::new(
wallet,
trading_config.risk_config.unwrap(),
trading_config.api_config.unwrap()
).await?;
engine.set_retry_policy(retry_policy);
engine.set_safety_circuit_breaker_config(safety_config);
engine.set_detailed_logging(true);
println!("✅ Live trading engine created");
println!("\n5. Initializing Safety Mechanisms");
println!("--------------------------------");
engine.init_safety_mechanisms().await?;
println!("✅ Safety mechanisms initialized");
println!("\n6. Connecting to Exchange");
println!("------------------------");
engine.connect().await?;
println!("✅ Connected to exchange");
let checks_passed = perform_safety_checks(&engine).await?;
if !checks_passed {
println!("\n❌ Safety checks failed. Aborting deployment.");
engine.disconnect().await?;
return Ok(());
}
println!("\n8. Creating Trading Strategy");
println!("---------------------------");
let strategy = Box::new(FundingArbitrageStrategy::new(
vec!["BTC".to_string(), "ETH".to_string()], 0.0002, 0.1, 2, 24, ));
println!("✅ Created Funding Arbitrage Strategy");
println!(" - Trading BTC, ETH");
println!(" - Funding threshold: 0.02% per 8h");
println!(" - Base position size: 10% of portfolio");
println!(" - Minimum hold period: 24 hours");
println!("\n9. Registering Alert Handlers");
println!("----------------------------");
engine.register_alert_handler(|alert: &AlertMessage| {
match alert.level {
AlertLevel::Info => println!("ℹ️ INFO: {}", alert.message),
AlertLevel::Warning => println!("⚠️ WARNING: {}", alert.message),
AlertLevel::Critical => println!("🚨 CRITICAL: {}", alert.message),
}
alert.level != AlertLevel::Critical
});
println!("✅ Alert handlers registered");
println!("\n10. Starting Live Trading");
println!("------------------------");
println!("⚠️ In a real deployment, this would execute actual trades");
println!("⚠️ This example will simulate trading for 30 seconds");
let engine_arc = Arc::new(Mutex::new(engine));
let engine_for_task = engine_arc.clone();
let task_handle = tokio::spawn(async move {
let mut engine = engine_for_task.lock().unwrap();
if let Err(e) = engine.start_trading(strategy).await {
eprintln!("Error in live trading: {}", e);
if engine.is_emergency_stop_active() {
eprintln!("⚠️ Emergency stop was triggered");
if let Ok(positions) = engine.get_positions() {
eprintln!("📊 Positions at emergency stop:");
for (symbol, position) in positions {
eprintln!(" {}: {} @ {:.2} (PnL: {:.2})",
symbol, position.size, position.current_price, position.unrealized_pnl);
}
}
}
}
});
println!("Live trading started. Running for 30 seconds...");
println!("(In a real application, this would run continuously)");
tokio::time::sleep(std::time::Duration::from_secs(30)).await;
println!("\n11. Performing Graceful Shutdown");
println!("-------------------------------");
{
let mut engine = engine_arc.lock().unwrap();
engine.stop_trading().await?;
println!("✅ Trading stopped");
let positions = engine.get_positions();
println!("\nFinal Positions:");
if positions.is_empty() {
println!("No open positions");
} else {
for (symbol, position) in positions {
println!("{}: {} @ ${} (PnL: ${:.2})",
symbol,
position.size,
position.current_price,
position.unrealized_pnl
);
}
}
engine.disconnect().await?;
println!("✅ Disconnected from exchange");
}
let _ = task_handle.await;
println!("\nExample completed successfully!");
println!("In a real deployment, the trading engine would continue running");
println!("and would require proper monitoring and maintenance procedures.");
Ok(())
}