hyperliquid-backtest 0.1.2

Comprehensive Rust library for backtesting trading strategies with Hyperliquid data, funding rates, and perpetual futures mechanics
Documentation
use std::collections::HashMap;
use std::str::FromStr;
use std::sync::{Arc, Mutex};
use chrono::{DateTime, FixedOffset, Utc};
use ethers::signers::LocalWallet;
use tokio::test;

use crate::live_trading::{LiveTradingEngine, LiveTradingError};
use crate::trading_mode::{ApiConfig, RiskConfig};
use crate::unified_data::{
    Position, OrderRequest, OrderResult, MarketData, 
    OrderSide, OrderType, TimeInForce, OrderStatus,
    TradingStrategy, Signal
};

// Mock implementation of TradingStrategy for testing
struct MockStrategy;

impl TradingStrategy for MockStrategy {
    fn name(&self) -> &str {
        "MockStrategy"
    }
    
    fn on_market_data(&mut self, _data: &MarketData) -> Result<Vec<OrderRequest>, String> {
        // Return no orders for testing
        Ok(Vec::new())
    }
    
    fn on_order_fill(&mut self, _fill: &crate::unified_data::OrderFill) -> Result<(), String> {
        Ok(())
    }
    
    fn on_funding_payment(&mut self, _payment: &crate::unified_data::FundingPayment) -> Result<(), String> {
        Ok(())
    }
    
    fn get_current_signals(&self) -> HashMap<String, Signal> {
        HashMap::new()
    }
}

// Helper function to create a test wallet
fn create_test_wallet() -> LocalWallet {
    // This is a test private key, never use in production
    let private_key = "0000000000000000000000000000000000000000000000000000000000000001";
    LocalWallet::from_str(private_key).unwrap()
}

// Helper function to create a test API config
fn create_test_api_config() -> ApiConfig {
    ApiConfig {
        api_key: "test_key".to_string(),
        api_secret: "test_secret".to_string(),
        endpoint: "https://api.hyperliquid-testnet.xyz".to_string(),
        use_testnet: true,
        timeout_ms: 5000,
    }
}

// Helper function to create a test risk config
fn create_test_risk_config() -> RiskConfig {
    RiskConfig::default()
}

// Note: These tests are commented out because they require mocking the Hyperliquid API
// In a real implementation, we would use mocks to test the LiveTradingEngine

/*
#[tokio::test]
async fn test_live_trading_engine_creation() {
    let wallet = create_test_wallet();
    let risk_config = create_test_risk_config();
    let api_config = create_test_api_config();
    
    let engine = LiveTradingEngine::new(wallet, risk_config, api_config).await;
    assert!(engine.is_ok());
}

#[tokio::test]
async fn test_connect_disconnect() {
    let wallet = create_test_wallet();
    let risk_config = create_test_risk_config();
    let api_config = create_test_api_config();
    
    let mut engine = LiveTradingEngine::new(wallet, risk_config, api_config).await.unwrap();
    
    // Connect
    let result = engine.connect().await;
    assert!(result.is_ok());
    assert!(engine.is_connected);
    
    // Disconnect
    let result = engine.disconnect().await;
    assert!(result.is_ok());
    assert!(!engine.is_connected);
}

#[tokio::test]
async fn test_execute_order() {
    let wallet = create_test_wallet();
    let risk_config = create_test_risk_config();
    let api_config = create_test_api_config();
    
    let mut engine = LiveTradingEngine::new(wallet, risk_config, api_config).await.unwrap();
    
    // Connect
    engine.connect().await.unwrap();
    
    // Create order
    let order = OrderRequest {
        symbol: "BTC".to_string(),
        side: OrderSide::Buy,
        order_type: OrderType::Market,
        quantity: 0.01,
        price: None,
        reduce_only: false,
        time_in_force: TimeInForce::ImmediateOrCancel,
    };
    
    // Execute order
    let result = engine.execute_order(order).await;
    assert!(result.is_ok());
    
    // Check order history
    assert_eq!(engine.get_order_history().len(), 1);
}

#[tokio::test]
async fn test_emergency_stop() {
    let wallet = create_test_wallet();
    let risk_config = create_test_risk_config();
    let api_config = create_test_api_config();
    
    let mut engine = LiveTradingEngine::new(wallet, risk_config, api_config).await.unwrap();
    
    // Connect
    engine.connect().await.unwrap();
    
    // Activate emergency stop
    engine.emergency_stop();
    assert!(engine.is_emergency_stop_active());
    
    // Create order
    let order = OrderRequest {
        symbol: "BTC".to_string(),
        side: OrderSide::Buy,
        order_type: OrderType::Market,
        quantity: 0.01,
        price: None,
        reduce_only: false,
        time_in_force: TimeInForce::ImmediateOrCancel,
    };
    
    // Execute order should fail
    let result = engine.execute_order(order).await;
    assert!(result.is_err());
    assert!(matches!(result.unwrap_err(), LiveTradingError::EmergencyStop));
    
    // Deactivate emergency stop
    engine.deactivate_emergency_stop();
    assert!(!engine.is_emergency_stop_active());
}

#[tokio::test]
async fn test_start_stop_trading() {
    let wallet = create_test_wallet();
    let risk_config = create_test_risk_config();
    let api_config = create_test_api_config();
    
    let mut engine = LiveTradingEngine::new(wallet, risk_config, api_config).await.unwrap();
    
    // Create strategy
    let strategy = Box::new(MockStrategy);
    
    // Start trading in a separate task
    let handle = tokio::spawn(async move {
        let result = engine.start_trading(strategy).await;
        assert!(result.is_ok());
        engine
    });
    
    // Wait a bit
    tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
    
    // Get the engine back and stop trading
    let mut engine = handle.abort();
    engine.stop_trading();
    assert!(!engine.is_running);
}
*/