hyperliquid_backtest/
strategies.rs

1//! # Strategy Implementations for Hyperliquid Backtesting
2//!
3//! This module provides enhanced trading strategies specifically designed for Hyperliquid
4//! perpetual futures trading, including funding rate awareness and advanced signal processing.
5//!
6//! ## Key Features
7//!
8//! - **Funding-Aware Strategies**: Strategies that incorporate funding rate data into decision making
9//! - **Enhanced Technical Indicators**: Traditional indicators enhanced with perpetual futures mechanics
10//! - **Signal Strength Classification**: Multi-level signal strength for better risk management
11//! - **Configurable Parameters**: Flexible configuration for different market conditions
12//! - **Strategy Composition**: Combine multiple strategies for sophisticated trading logic
13//!
14//! ## Available Strategies
15//!
16//! ### 1. Funding Arbitrage Strategy
17//!
18//! Exploits funding rate inefficiencies by taking positions when funding rates exceed thresholds.
19//!
20//! ```rust,no_run
21//! use hyperliquid_backtest::prelude::*;
22//!
23//! #[tokio::main]
24//! async fn main() -> Result<(), HyperliquidBacktestError> {
25//!     let data = HyperliquidData::fetch("BTC", "1h", start_time, end_time).await?;
26//!     
27//!     // Create funding arbitrage strategy with 0.01% threshold
28//!     let strategy = funding_arbitrage_strategy(0.0001)?;
29//!     
30//!     let mut backtest = HyperliquidBacktest::new(
31//!         data,
32//!         strategy,
33//!         10000.0,
34//!         HyperliquidCommission::default(),
35//!     )?;
36//!     
37//!     backtest.calculate_with_funding()?;
38//!     let report = backtest.funding_report()?;
39//!     
40//!     println!("Funding arbitrage return: {:.2}%", report.net_funding_pnl / 10000.0 * 100.0);
41//!     
42//!     Ok(())
43//! }
44//! ```
45//!
46//! ### 2. Enhanced SMA Cross Strategy
47//!
48//! Traditional SMA crossover enhanced with funding rate considerations.
49//!
50//! ```rust,no_run
51//! use hyperliquid_backtest::prelude::*;
52//!
53//! let funding_config = FundingAwareConfig {
54//!     funding_threshold: 0.0001,  // 0.01% threshold
55//!     funding_weight: 0.3,        // 30% weight to funding signal
56//!     use_funding_direction: true,
57//!     use_funding_prediction: false,
58//! };
59//!
60//! let strategy = enhanced_sma_cross(10, 20, funding_config)?;
61//! ```
62//!
63//! ## Strategy Configuration
64//!
65//! ### Funding Awareness Configuration
66//!
67//! ```rust,no_run
68//! use hyperliquid_backtest::prelude::*;
69//!
70//! let config = FundingAwareConfig {
71//!     funding_threshold: 0.0001,      // Minimum funding rate to consider (0.01%)
72//!     funding_weight: 0.5,            // Weight of funding signal (0.0 to 1.0)
73//!     use_funding_direction: true,    // Consider funding rate direction
74//!     use_funding_prediction: true,   // Use funding rate predictions
75//! };
76//! ```
77//!
78//! ### Signal Strength Levels
79//!
80//! - **Strong**: High confidence signals (>80% historical accuracy)
81//! - **Medium**: Moderate confidence signals (60-80% historical accuracy)  
82//! - **Weak**: Low confidence signals (<60% historical accuracy)
83//!
84//! ## Custom Strategy Development
85//!
86//! ### Implementing HyperliquidStrategy Trait
87//!
88//! ```rust,ignore
89//! use hyperliquid_backtest::prelude::*;
90//!
91//! struct MyCustomStrategy {
92//!     funding_config: FundingAwareConfig,
93//!     // ... other fields
94//! }
95//!
96//! impl HyperliquidStrategy for MyCustomStrategy {
97//!     fn funding_config(&self) -> &FundingAwareConfig {
98//!         &self.funding_config
99//!     }
100//!     
101//!     fn set_funding_config(&mut self, config: FundingAwareConfig) {
102//!         self.funding_config = config;
103//!     }
104//!     
105//!     fn process_funding(&self, funding_rate: f64) -> TradingSignal {
106//!         // Custom funding processing logic
107//!         if funding_rate.abs() > self.funding_config.funding_threshold {
108//!             TradingSignal::new(
109//!                 if funding_rate > 0.0 { 1.0 } else { -1.0 },
110//!                 SignalStrength::Strong
111//!             )
112//!         } else {
113//!             TradingSignal::new(0.0, SignalStrength::Weak)
114//!         }
115//!     }
116//!     
117//!     fn combine_signals(&self, base_signal: f64, funding_signal: &TradingSignal) -> f64 {
118//!         // Custom signal combination logic
119//!         let funding_weight = match funding_signal.strength {
120//!             SignalStrength::Strong => self.funding_config.funding_weight,
121//!             SignalStrength::Medium => self.funding_config.funding_weight * 0.7,
122//!             SignalStrength::Weak => self.funding_config.funding_weight * 0.3,
123//!         };
124//!         
125//!         base_signal * (1.0 - funding_weight) + funding_signal.position * funding_weight
126//!     }
127//! }
128//! ```
129
130use rs_backtester::strategies::Strategy;
131use rs_backtester::datas::Data;
132use serde::{Deserialize, Serialize};
133
134// Note: These modules need to be implemented
135// pub use crate::strategies::trading_strategy::{
136//     TradingStrategy, StrategyConfig, StrategyState, StrategyParam, BaseTradingStrategy
137// };
138
139// pub use crate::strategies::funding_arbitrage_strategy::{
140//     FundingArbitrageStrategy, create_funding_arbitrage_strategy
141// };
142// pub use crate::strategies::enhanced_sma_strategy::{
143//     EnhancedSmaStrategy, create_enhanced_sma_strategy
144// };
145// pub use crate::strategies::strategy_template::{
146//     StrategyTemplate, create_strategy_template
147// };
148
149/// Signal strength for trading decisions
150#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
151pub enum SignalStrength {
152    /// Strong signal (high confidence)
153    Strong,
154    /// Medium signal (moderate confidence)
155    Medium,
156    /// Weak signal (low confidence)
157    Weak,
158}
159
160/// Trading signal with position size and strength
161#[derive(Debug, Clone, Serialize, Deserialize)]
162pub struct TradingSignal {
163    /// Position size (-1.0 to 1.0)
164    pub position: f64,
165    /// Signal strength
166    pub strength: SignalStrength,
167}
168
169impl TradingSignal {
170    /// Create a new TradingSignal
171    pub fn new(position: f64, strength: SignalStrength) -> Self {
172        Self {
173            position,
174            strength,
175        }
176    }
177    
178    /// Check if signal is long
179    pub fn is_long(&self) -> bool {
180        self.position > 0.0
181    }
182    
183    /// Check if signal is short
184    pub fn is_short(&self) -> bool {
185        self.position < 0.0
186    }
187    
188    /// Check if signal is neutral
189    pub fn is_neutral(&self) -> bool {
190        self.position == 0.0
191    }
192}
193
194/// Configuration for funding-aware strategies
195#[derive(Debug, Clone, Serialize, Deserialize)]
196pub struct FundingAwareConfig {
197    /// Threshold for significant funding rate
198    pub funding_threshold: f64,
199    /// Weight of funding signal in overall strategy
200    pub funding_weight: f64,
201    /// Whether to use funding direction in strategy
202    pub use_funding_direction: bool,
203    /// Whether to use funding prediction in strategy
204    pub use_funding_prediction: bool,
205}
206
207impl Default for FundingAwareConfig {
208    fn default() -> Self {
209        Self {
210            funding_threshold: 0.0001, // 0.01% per 8h
211            funding_weight: 0.5,       // 50% weight to funding
212            use_funding_direction: true,
213            use_funding_prediction: true,
214        }
215    }
216}
217
218/// Trait for Hyperliquid-specific strategies
219pub trait HyperliquidStrategy {
220    /// Get funding-aware configuration
221    fn funding_config(&self) -> &FundingAwareConfig;
222    
223    /// Set funding-aware configuration
224    fn set_funding_config(&mut self, config: FundingAwareConfig);
225    
226    /// Process funding rate information
227    fn process_funding(&self, funding_rate: f64) -> TradingSignal;
228    
229    /// Combine funding signal with base strategy signal
230    fn combine_signals(&self, base_signal: f64, funding_signal: &TradingSignal) -> f64;
231}
232
233/// Create a funding arbitrage strategy
234pub fn funding_arbitrage_strategy(data: Data, threshold: f64) -> Strategy {
235    // Create a new strategy (placeholder implementation)
236    let strategy = Strategy {
237        name: format!("Funding Arbitrage (threshold: {})", threshold),
238        choices: Vec::new(),
239        indicator: None,
240    };
241    
242    // We need to implement the strategy logic differently since the Strategy struct
243    // from rs-backtester doesn't match our expected interface
244    // For now, we'll return a placeholder implementation
245    
246    strategy
247}
248
249/// Create an enhanced SMA cross strategy with funding awareness
250pub fn enhanced_sma_cross(
251    data: Data,
252    fast_period: usize,
253    slow_period: usize,
254    funding_config: FundingAwareConfig,
255) -> Strategy {
256    // Create a new strategy (placeholder implementation)
257    let strategy = Strategy {
258        name: format!("Enhanced SMA Cross ({}, {})", fast_period, slow_period),
259        choices: Vec::new(),
260        indicator: None,
261    };
262    
263    // We need to implement the strategy logic differently since the Strategy struct
264    // from rs-backtester doesn't match our expected interface
265    // For now, we'll return a placeholder implementation
266    
267    strategy
268}
269
270/// Funding arbitrage strategy implementation
271pub struct FundingArbitrageStrategy {
272    /// Threshold for taking positions
273    threshold: f64,
274    /// Funding-aware configuration
275    funding_config: FundingAwareConfig,
276}
277
278impl FundingArbitrageStrategy {
279    /// Create a new FundingArbitrageStrategy
280    pub fn new(threshold: f64) -> Self {
281        Self {
282            threshold,
283            funding_config: FundingAwareConfig::default(),
284        }
285    }
286}
287
288impl HyperliquidStrategy for FundingArbitrageStrategy {
289    fn funding_config(&self) -> &FundingAwareConfig {
290        &self.funding_config
291    }
292    
293    fn set_funding_config(&mut self, config: FundingAwareConfig) {
294        self.funding_config = config;
295    }
296    
297    fn process_funding(&self, funding_rate: f64) -> TradingSignal {
298        if funding_rate.abs() <= self.threshold {
299            return TradingSignal::new(0.0, SignalStrength::Weak);
300        }
301        
302        let position = if funding_rate > 0.0 { 1.0 } else { -1.0 };
303        let strength = if funding_rate.abs() > self.threshold * 2.0 {
304            SignalStrength::Strong
305        } else {
306            SignalStrength::Medium
307        };
308        
309        TradingSignal::new(position, strength)
310    }
311    
312    fn combine_signals(&self, base_signal: f64, funding_signal: &TradingSignal) -> f64 {
313        if funding_signal.is_neutral() {
314            return base_signal;
315        }
316        
317        let weight = match funding_signal.strength {
318            SignalStrength::Strong => self.funding_config.funding_weight,
319            SignalStrength::Medium => self.funding_config.funding_weight * 0.7,
320            SignalStrength::Weak => self.funding_config.funding_weight * 0.3,
321        };
322        
323        let combined = base_signal * (1.0 - weight) + funding_signal.position * weight;
324        
325        // Normalize to -1.0, 0.0, or 1.0
326        if combined > 0.3 {
327            1.0
328        } else if combined < -0.3 {
329            -1.0
330        } else {
331            0.0
332        }
333    }
334}
335
336/// Enhanced SMA cross strategy with funding awareness
337pub struct EnhancedSmaStrategy {
338    /// Fast period for SMA
339    fast_period: usize,
340    /// Slow period for SMA
341    slow_period: usize,
342    /// Funding-aware configuration
343    funding_config: FundingAwareConfig,
344}
345
346impl EnhancedSmaStrategy {
347    /// Create a new EnhancedSmaStrategy
348    pub fn new(fast_period: usize, slow_period: usize) -> Self {
349        Self {
350            fast_period,
351            slow_period,
352            funding_config: FundingAwareConfig::default(),
353        }
354    }
355    
356    /// Calculate SMA for a given period
357    fn calculate_sma(&self, data: &[f64], period: usize) -> f64 {
358        if data.len() < period {
359            return 0.0;
360        }
361        
362        let sum: f64 = data[data.len() - period..].iter().sum();
363        sum / period as f64
364    }
365}
366
367impl HyperliquidStrategy for EnhancedSmaStrategy {
368    fn funding_config(&self) -> &FundingAwareConfig {
369        &self.funding_config
370    }
371    
372    fn set_funding_config(&mut self, config: FundingAwareConfig) {
373        self.funding_config = config;
374    }
375    
376    fn process_funding(&self, funding_rate: f64) -> TradingSignal {
377        if !self.funding_config.use_funding_direction || 
378           funding_rate.abs() <= self.funding_config.funding_threshold {
379            return TradingSignal::new(0.0, SignalStrength::Weak);
380        }
381        
382        let position = if funding_rate > 0.0 { 1.0 } else { -1.0 };
383        let strength = if funding_rate.abs() > self.funding_config.funding_threshold * 2.0 {
384            SignalStrength::Medium
385        } else {
386            SignalStrength::Weak
387        };
388        
389        TradingSignal::new(position, strength)
390    }
391    
392    fn combine_signals(&self, base_signal: f64, funding_signal: &TradingSignal) -> f64 {
393        if funding_signal.is_neutral() {
394            return base_signal;
395        }
396        
397        // If signals agree, strengthen the position
398        if (base_signal > 0.0 && funding_signal.is_long()) || 
399           (base_signal < 0.0 && funding_signal.is_short()) {
400            return base_signal;
401        }
402        
403        // If signals disagree, reduce the position based on funding strength
404        let weight = match funding_signal.strength {
405            SignalStrength::Strong => self.funding_config.funding_weight,
406            SignalStrength::Medium => self.funding_config.funding_weight * 0.5,
407            SignalStrength::Weak => self.funding_config.funding_weight * 0.2,
408        };
409        
410        base_signal * (1.0 - weight)
411    }
412}