hyperliquid_backtest/
backtest.rs

1//! # Enhanced Backtesting Functionality with Hyperliquid-specific Features
2//!
3//! This module provides enhanced backtesting capabilities that extend the rs-backtester framework
4//! with Hyperliquid-specific features including funding rate calculations, maker/taker fee structures,
5//! and perpetual futures mechanics.
6//!
7//! ## Key Features
8//!
9//! - **Funding Rate Integration**: Automatic calculation of funding payments based on position size
10//! - **Enhanced Commission Structure**: Separate maker/taker rates matching Hyperliquid's fee structure
11//! - **Perpetual Futures Support**: Complete support for perpetual futures trading mechanics
12//! - **Advanced Reporting**: Detailed reports separating trading PnL from funding PnL
13//! - **Seamless Integration**: Drop-in replacement for rs-backtester with enhanced features
14//!
15//! ## Usage Examples
16//!
17//! ### Basic Enhanced Backtesting
18//!
19//! ```rust,no_run
20//! use hyperliquid_backtest::prelude::*;
21//!
22//! #[tokio::main]
23//! async fn main() -> Result<(), HyperliquidBacktestError> {
24//!     // Fetch data
25//!     let data = HyperliquidData::fetch("BTC", "1h", start_time, end_time).await?;
26//!     
27//!     // Create strategy
28//!     let strategy = enhanced_sma_cross(10, 20, Default::default())?;
29//!     
30//!     // Set up enhanced backtest
31//!     let mut backtest = HyperliquidBacktest::new(
32//!         data,
33//!         strategy,
34//!         10000.0, // $10,000 initial capital
35//!         HyperliquidCommission::default(),
36//!     )?;
37//!     
38//!     // Run backtest with funding calculations
39//!     backtest.calculate_with_funding()?;
40//!     
41//!     // Get comprehensive results
42//!     let report = backtest.enhanced_report()?;
43//!     println!("Total Return: {:.2}%", report.total_return * 100.0);
44//!     println!("Trading PnL: ${:.2}", report.trading_pnl);
45//!     println!("Funding PnL: ${:.2}", report.funding_pnl);
46//!     
47//!     Ok(())
48//! }
49//! ```
50//!
51//! ### Custom Commission Structure
52//!
53//! ```rust,no_run
54//! use hyperliquid_backtest::prelude::*;
55//!
56//! // Create custom commission structure
57//! let commission = HyperliquidCommission::new(
58//!     0.0001, // 0.01% maker rate
59//!     0.0003, // 0.03% taker rate
60//!     true,   // Enable funding calculations
61//! );
62//!
63//! let mut backtest = HyperliquidBacktest::new(
64//!     data,
65//!     strategy,
66//!     50000.0,
67//!     commission,
68//! )?;
69//! ```
70//!
71//! ### Funding-Only Analysis
72//!
73//! ```rust,no_run
74//! use hyperliquid_backtest::prelude::*;
75//!
76//! // Disable trading fees to analyze funding impact only
77//! let commission = HyperliquidCommission::new(0.0, 0.0, true);
78//!
79//! let mut backtest = HyperliquidBacktest::new(data, strategy, 10000.0, commission)?;
80//! backtest.calculate_with_funding()?;
81//!
82//! let funding_report = backtest.funding_report()?;
83//! println!("Pure funding PnL: ${:.2}", funding_report.net_funding_pnl);
84//! ```
85
86use crate::data::HyperliquidData;
87use crate::errors::{HyperliquidBacktestError, Result};
88use chrono::{DateTime, FixedOffset, Timelike};
89use serde::{Deserialize, Serialize};
90use std::collections::hash_map::DefaultHasher;
91use std::hash::{Hash, Hasher};
92use std::path::Path;
93use std::fs::File;
94use std::io::Write;
95
96/// Order type for commission calculation
97#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
98pub enum OrderType {
99    /// Market order (typically taker)
100    Market,
101    /// Limit order that adds liquidity (maker)
102    LimitMaker,
103    /// Limit order that removes liquidity (taker)
104    LimitTaker,
105}
106
107/// Trading scenario for commission calculation
108#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
109pub enum TradingScenario {
110    /// Opening a new position
111    OpenPosition,
112    /// Closing an existing position
113    ClosePosition,
114    /// Reducing position size
115    ReducePosition,
116    /// Increasing position size
117    IncreasePosition,
118}
119
120/// Commission structure for Hyperliquid trading
121#[derive(Debug, Clone, Serialize, Deserialize)]
122pub struct HyperliquidCommission {
123    /// Maker fee rate (typically lower)
124    pub maker_rate: f64,
125    /// Taker fee rate (typically higher)
126    pub taker_rate: f64,
127    /// Whether to include funding payments in calculations
128    pub funding_enabled: bool,
129}
130
131impl Default for HyperliquidCommission {
132    fn default() -> Self {
133        Self {
134            maker_rate: 0.0002,  // 0.02% maker fee (Hyperliquid standard)
135            taker_rate: 0.0005,  // 0.05% taker fee (Hyperliquid standard)
136            funding_enabled: true,
137        }
138    }
139}
140
141impl HyperliquidCommission {
142    /// Create a new HyperliquidCommission with custom rates
143    pub fn new(maker_rate: f64, taker_rate: f64, funding_enabled: bool) -> Self {
144        Self {
145            maker_rate,
146            taker_rate,
147            funding_enabled,
148        }
149    }
150
151    /// Calculate trading fee based on order type
152    pub fn calculate_fee(&self, order_type: OrderType, trade_value: f64) -> f64 {
153        let rate = match order_type {
154            OrderType::Market | OrderType::LimitTaker => self.taker_rate,
155            OrderType::LimitMaker => self.maker_rate,
156        };
157        trade_value * rate
158    }
159
160    /// Calculate fee for a specific trading scenario
161    pub fn calculate_scenario_fee(
162        &self,
163        scenario: TradingScenario,
164        order_type: OrderType,
165        trade_value: f64,
166    ) -> f64 {
167        // Base fee calculation
168        let base_fee = self.calculate_fee(order_type, trade_value);
169        
170        // Apply scenario-specific adjustments if needed
171        match scenario {
172            TradingScenario::OpenPosition => base_fee,
173            TradingScenario::ClosePosition => base_fee,
174            TradingScenario::ReducePosition => base_fee,
175            TradingScenario::IncreasePosition => base_fee,
176        }
177    }
178
179    /// Convert to rs-backtester Commission (uses taker rate as default)
180    pub fn to_rs_backtester_commission(&self) -> rs_backtester::backtester::Commission {
181        rs_backtester::backtester::Commission {
182            rate: self.taker_rate,
183        }
184    }
185
186    /// Validate commission rates
187    pub fn validate(&self) -> Result<()> {
188        if self.maker_rate < 0.0 || self.maker_rate > 1.0 {
189            return Err(HyperliquidBacktestError::validation(
190                format!("Invalid maker rate: {}. Must be between 0.0 and 1.0", self.maker_rate)
191            ));
192        }
193        if self.taker_rate < 0.0 || self.taker_rate > 1.0 {
194            return Err(HyperliquidBacktestError::validation(
195                format!("Invalid taker rate: {}. Must be between 0.0 and 1.0", self.taker_rate)
196            ));
197        }
198        if self.maker_rate > self.taker_rate {
199            return Err(HyperliquidBacktestError::validation(
200                "Maker rate should typically be lower than taker rate".to_string()
201            ));
202        }
203        Ok(())
204    }
205}
206
207/// Strategy for determining order types in backtesting
208#[derive(Debug, Clone, Serialize, Deserialize)]
209pub enum OrderTypeStrategy {
210    /// Always use market orders (taker fees)
211    AlwaysMarket,
212    /// Always use limit maker orders (maker fees)
213    AlwaysMaker,
214    /// Mixed strategy with specified maker percentage
215    Mixed { maker_percentage: f64 },
216    /// Adaptive strategy based on market conditions
217    Adaptive,
218}
219
220impl OrderTypeStrategy {
221    /// Get the order type for a given trade index
222    pub fn get_order_type(&self, trade_index: usize) -> OrderType {
223        match self {
224            OrderTypeStrategy::AlwaysMarket => OrderType::Market,
225            OrderTypeStrategy::AlwaysMaker => OrderType::LimitMaker,
226            OrderTypeStrategy::Mixed { maker_percentage } => {
227                // Use deterministic hashing to ensure consistent results
228                let mut hasher = DefaultHasher::new();
229                trade_index.hash(&mut hasher);
230                let hash_value = hasher.finish();
231                let normalized = (hash_value as f64) / (u64::MAX as f64);
232                
233                if normalized < *maker_percentage {
234                    OrderType::LimitMaker
235                } else {
236                    OrderType::Market
237                }
238            }
239            OrderTypeStrategy::Adaptive => {
240                // Simple adaptive strategy: alternate between maker and taker
241                if trade_index % 2 == 0 {
242                    OrderType::LimitMaker
243                } else {
244                    OrderType::Market
245                }
246            }
247        }
248    }
249}
250
251/// Commission statistics for reporting
252#[derive(Debug, Clone, Serialize, Deserialize)]
253pub struct CommissionStats {
254    /// Total commission paid
255    pub total_commission: f64,
256    /// Total maker fees paid
257    pub maker_fees: f64,
258    /// Total taker fees paid
259    pub taker_fees: f64,
260    /// Number of maker orders
261    pub maker_orders: usize,
262    /// Number of taker orders
263    pub taker_orders: usize,
264    /// Average commission rate
265    pub average_rate: f64,
266    /// Ratio of maker to total orders
267    pub maker_taker_ratio: f64,
268}
269
270/// Individual funding payment record
271#[derive(Debug, Clone, Serialize, Deserialize)]
272pub struct FundingPayment {
273    /// Timestamp of the funding payment
274    pub timestamp: DateTime<chrono::FixedOffset>,
275    /// Position size at the time of funding
276    pub position_size: f64,
277    /// Funding rate applied
278    pub funding_rate: f64,
279    /// Funding payment amount (positive = received, negative = paid)
280    pub payment_amount: f64,
281    /// Mark price at the time of funding
282    pub mark_price: f64,
283}
284
285/// Enhanced metrics for Hyperliquid backtesting
286#[derive(Debug, Clone, Serialize, Deserialize)]
287pub struct EnhancedMetrics {
288    /// Total return including funding
289    pub total_return_with_funding: f64,
290    /// Total return from trading only
291    pub trading_only_return: f64,
292    /// Total return from funding only
293    pub funding_only_return: f64,
294    /// Sharpe ratio including funding
295    pub sharpe_ratio_with_funding: f64,
296    /// Maximum drawdown including funding
297    pub max_drawdown_with_funding: f64,
298    /// Number of funding payments received
299    pub funding_payments_received: usize,
300    /// Number of funding payments paid
301    pub funding_payments_paid: usize,
302    /// Average funding rate
303    pub average_funding_rate: f64,
304    /// Funding rate volatility
305    pub funding_rate_volatility: f64,
306}
307
308impl Default for EnhancedMetrics {
309    fn default() -> Self {
310        Self {
311            total_return_with_funding: 0.0,
312            trading_only_return: 0.0,
313            funding_only_return: 0.0,
314            sharpe_ratio_with_funding: 0.0,
315            max_drawdown_with_funding: 0.0,
316            funding_payments_received: 0,
317            funding_payments_paid: 0,
318            average_funding_rate: 0.0,
319            funding_rate_volatility: 0.0,
320        }
321    }
322}
323
324/// Commission tracking for detailed reporting
325#[derive(Debug, Clone, Serialize, Deserialize)]
326pub struct CommissionTracker {
327    /// Total maker fees paid
328    pub total_maker_fees: f64,
329    /// Total taker fees paid
330    pub total_taker_fees: f64,
331    /// Number of maker orders
332    pub maker_order_count: usize,
333    /// Number of taker orders
334    pub taker_order_count: usize,
335}
336
337impl Default for CommissionTracker {
338    fn default() -> Self {
339        Self {
340            total_maker_fees: 0.0,
341            total_taker_fees: 0.0,
342            maker_order_count: 0,
343            taker_order_count: 0,
344        }
345    }
346}
347
348impl CommissionTracker {
349    /// Add a commission entry
350    pub fn add_commission(
351        &mut self,
352        _timestamp: chrono::DateTime<chrono::FixedOffset>,
353        order_type: OrderType,
354        _trade_value: f64,
355        commission_paid: f64,
356        _scenario: TradingScenario,
357    ) {
358        match order_type {
359            OrderType::LimitMaker => {
360                self.total_maker_fees += commission_paid;
361                self.maker_order_count += 1;
362            }
363            OrderType::Market | OrderType::LimitTaker => {
364                self.total_taker_fees += commission_paid;
365                self.taker_order_count += 1;
366            }
367        }
368    }
369
370    /// Get total commission paid
371    pub fn total_commission(&self) -> f64 {
372        self.total_maker_fees + self.total_taker_fees
373    }
374
375    /// Get average commission rate
376    pub fn average_commission_rate(&self) -> f64 {
377        let total_orders = self.maker_order_count + self.taker_order_count;
378        if total_orders > 0 {
379            self.total_commission() / total_orders as f64
380        } else {
381            0.0
382        }
383    }
384
385    /// Get maker/taker ratio
386    pub fn maker_taker_ratio(&self) -> f64 {
387        let total_orders = self.maker_order_count + self.taker_order_count;
388        if total_orders > 0 {
389            self.maker_order_count as f64 / total_orders as f64
390        } else {
391            0.0
392        }
393    }
394}
395
396/// Enhanced report structure with Hyperliquid-specific metrics
397#[derive(Debug, Clone, Serialize, Deserialize)]
398pub struct EnhancedReport {
399    /// Strategy name
400    pub strategy_name: String,
401    /// Symbol/ticker
402    pub ticker: String,
403    /// Initial capital
404    pub initial_capital: f64,
405    /// Final equity
406    pub final_equity: f64,
407    /// Total return
408    pub total_return: f64,
409    /// Number of trades
410    pub trade_count: usize,
411    /// Win rate
412    pub win_rate: f64,
413    /// Profit factor
414    pub profit_factor: f64,
415    /// Sharpe ratio
416    pub sharpe_ratio: f64,
417    /// Max drawdown
418    pub max_drawdown: f64,
419    /// Enhanced metrics including funding
420    pub enhanced_metrics: EnhancedMetrics,
421    /// Commission statistics
422    pub commission_stats: CommissionStats,
423    /// Funding payment summary
424    pub funding_summary: FundingSummary,
425}
426
427/// Funding payment summary for reporting
428#[derive(Debug, Clone, Serialize, Deserialize)]
429pub struct FundingSummary {
430    /// Total funding paid
431    pub total_funding_paid: f64,
432    /// Total funding received
433    pub total_funding_received: f64,
434    /// Net funding (received - paid)
435    pub net_funding: f64,
436    /// Number of funding payments
437    pub funding_payment_count: usize,
438    /// Average funding payment
439    pub average_funding_payment: f64,
440    /// Average funding rate
441    pub average_funding_rate: f64,
442    /// Funding rate volatility
443    pub funding_rate_volatility: f64,
444    /// Funding contribution to total return
445    pub funding_contribution_percentage: f64,
446}
447
448/// Enhanced backtesting engine with Hyperliquid-specific features
449#[derive(Clone)]
450pub struct HyperliquidBacktest {
451    /// Underlying rs-backtester Backtest instance
452    pub base_backtest: Option<rs_backtester::backtester::Backtest>,
453    /// Original Hyperliquid data with funding information
454    pub data: HyperliquidData,
455    /// Strategy name for identification
456    pub strategy_name: String,
457    /// Initial capital for the backtest
458    pub initial_capital: f64,
459    /// Commission configuration
460    pub commission_config: HyperliquidCommission,
461    /// Commission tracking
462    pub commission_tracker: CommissionTracker,
463    /// Order type strategy for commission calculation
464    pub order_type_strategy: OrderTypeStrategy,
465    /// Funding PnL tracking (separate from trading PnL)
466    pub funding_pnl: Vec<f64>,
467    /// Trading PnL tracking (without funding)
468    pub trading_pnl: Vec<f64>,
469    /// Total PnL tracking (trading + funding)
470    pub total_pnl: Vec<f64>,
471    /// Total funding paid (negative values)
472    pub total_funding_paid: f64,
473    /// Total funding received (positive values)
474    pub total_funding_received: f64,
475    /// Funding payment history
476    pub funding_payments: Vec<FundingPayment>,
477    /// Enhanced metrics
478    pub enhanced_metrics: EnhancedMetrics,
479}
480
481impl HyperliquidBacktest {
482    /// Create a new HyperliquidBacktest instance
483    pub fn new(
484        data: HyperliquidData,
485        strategy_name: String,
486        initial_capital: f64,
487        commission: HyperliquidCommission,
488    ) -> Self {
489        Self {
490            base_backtest: None,
491            data,
492            strategy_name,
493            initial_capital,
494            commission_config: commission,
495            commission_tracker: CommissionTracker::default(),
496            order_type_strategy: OrderTypeStrategy::Mixed { maker_percentage: 0.5 },
497            funding_pnl: Vec::new(),
498            trading_pnl: Vec::new(),
499            total_pnl: Vec::new(),
500            total_funding_paid: 0.0,
501            total_funding_received: 0.0,
502            funding_payments: Vec::new(),
503            enhanced_metrics: EnhancedMetrics::default(),
504        }
505    }
506    
507    /// Access the base backtest instance
508    pub fn base_backtest(&self) -> Option<&rs_backtester::backtester::Backtest> {
509        self.base_backtest.as_ref()
510    }
511    
512    /// Access the base backtest instance mutably
513    pub fn base_backtest_mut(&mut self) -> Option<&mut rs_backtester::backtester::Backtest> {
514        self.base_backtest.as_mut()
515    }
516
517    /// Set the order type strategy for commission calculation
518    pub fn with_order_type_strategy(mut self, strategy: OrderTypeStrategy) -> Self {
519        self.order_type_strategy = strategy;
520        self
521    }
522
523    /// Initialize the underlying rs-backtester with converted data
524    pub fn initialize_base_backtest(&mut self) -> Result<()> {
525        // Validate data before proceeding
526        self.data.validate_all_data()?;
527
528        // Convert HyperliquidData to rs-backtester Data format
529        let rs_data = self.data.to_rs_backtester_data();
530
531        // Create rs-backtester Backtest instance
532        let rs_commission = self.commission_config.to_rs_backtester_commission();
533        
534        // Create a simple do_nothing strategy as default
535        let strategy = rs_backtester::strategies::do_nothing(rs_data.clone());
536        
537        let backtest = rs_backtester::backtester::Backtest::new(
538            rs_data,
539            strategy,
540            self.initial_capital,
541            rs_commission,
542        );
543
544        self.base_backtest = Some(backtest);
545        
546        // Initialize PnL tracking vectors
547        self.funding_pnl = vec![0.0; self.data.len()];
548        self.trading_pnl = vec![0.0; self.data.len()];
549        self.total_pnl = vec![0.0; self.data.len()];
550
551        Ok(())
552    }
553
554    /// Calculate backtest results including funding payments
555    /// This method applies funding payments to positions based on funding rates and timing
556    pub fn calculate_with_funding(&mut self) -> Result<()> {
557        // Ensure we have a base backtest to work with
558        if self.base_backtest.is_none() {
559            return Err(HyperliquidBacktestError::validation(
560                "Base backtest must be initialized before calculating funding"
561            ));
562        }
563
564        // Get the data length
565        let data_len = self.data.len();
566        
567        // Initialize funding and trading PnL vectors if not already done
568        if self.funding_pnl.len() != data_len {
569            self.funding_pnl = vec![0.0; data_len];
570        }
571        if self.trading_pnl.len() != data_len {
572            self.trading_pnl = vec![0.0; data_len];
573        }
574
575        // Reset funding totals
576        self.total_funding_paid = 0.0;
577        self.total_funding_received = 0.0;
578        self.funding_payments.clear();
579
580        // Simulate position tracking (in a real implementation, this would come from strategy signals)
581        let current_position = 0.0;
582        let mut cumulative_funding_pnl = 0.0;
583
584        // Process each data point
585        for i in 0..data_len {
586            let timestamp = self.data.datetime[i];
587            let price = self.data.close[i];
588
589            // Check if this is a funding payment time (every 8 hours)
590            if self.is_funding_time(timestamp) {
591                // Get funding rate for this timestamp
592                if let Some(funding_rate) = self.get_funding_rate_for_timestamp(timestamp) {
593                    // Calculate funding payment
594                    let funding_payment = self.calculate_funding_payment(
595                        current_position,
596                        funding_rate,
597                        price,
598                    );
599
600                    // Apply funding payment
601                    cumulative_funding_pnl += funding_payment;
602                    
603                    // Track funding payment
604                    if funding_payment > 0.0 {
605                        self.total_funding_received += funding_payment;
606                    } else {
607                        self.total_funding_paid += funding_payment.abs();
608                    }
609
610                    // Record funding payment
611                    self.funding_payments.push(FundingPayment {
612                        timestamp,
613                        position_size: current_position,
614                        funding_rate,
615                        payment_amount: funding_payment,
616                        mark_price: price,
617                    });
618                }
619            }
620
621            // Store cumulative funding PnL
622            self.funding_pnl[i] = cumulative_funding_pnl;
623            
624            // Calculate trading PnL (total PnL minus funding PnL)
625            // This would normally come from the base backtest results
626            self.trading_pnl[i] = 0.0; // Placeholder - would be calculated from actual trades
627        }
628
629        // Update enhanced metrics
630        self.update_enhanced_metrics()?;
631
632        Ok(())
633    }
634
635    /// Calculate funding payments with position tracking
636    /// This version allows external position tracking for more accurate funding calculations
637    pub fn calculate_with_funding_and_positions(&mut self, positions: &[f64]) -> Result<()> {
638        // Ensure we have a base backtest to work with
639        if self.base_backtest.is_none() {
640            return Err(HyperliquidBacktestError::validation(
641                "Base backtest must be initialized before calculating funding"
642            ));
643        }
644
645        let data_len = self.data.len();
646
647        // Validate positions array length
648        if positions.len() != data_len {
649            return Err(HyperliquidBacktestError::validation(
650                "Positions array length must match data length"
651            ));
652        }
653
654        // Initialize funding and trading PnL vectors if not already done
655        if self.funding_pnl.len() != data_len {
656            self.funding_pnl = vec![0.0; data_len];
657        }
658        if self.trading_pnl.len() != data_len {
659            self.trading_pnl = vec![0.0; data_len];
660        }
661
662        // Reset funding totals
663        self.total_funding_paid = 0.0;
664        self.total_funding_received = 0.0;
665        self.funding_payments.clear();
666
667        let mut cumulative_funding_pnl = 0.0;
668
669        // Process each data point
670        for i in 0..data_len {
671            let timestamp = self.data.datetime[i];
672            let price = self.data.close[i];
673            let position_size = positions[i];
674
675            // Check if this is a funding payment time (every 8 hours)
676            if self.is_funding_time(timestamp) {
677                // Get funding rate for this timestamp
678                if let Some(funding_rate) = self.get_funding_rate_for_timestamp(timestamp) {
679                    // Calculate funding payment
680                    let funding_payment = self.calculate_funding_payment(
681                        position_size,
682                        funding_rate,
683                        price,
684                    );
685
686                    // Apply funding payment
687                    cumulative_funding_pnl += funding_payment;
688                    
689                    // Track funding payment
690                    if funding_payment > 0.0 {
691                        self.total_funding_received += funding_payment;
692                    } else {
693                        self.total_funding_paid += funding_payment.abs();
694                    }
695
696                    // Record funding payment
697                    self.funding_payments.push(FundingPayment {
698                        timestamp,
699                        position_size,
700                        funding_rate,
701                        payment_amount: funding_payment,
702                        mark_price: price,
703                    });
704                }
705            }
706
707            // Store cumulative funding PnL
708            self.funding_pnl[i] = cumulative_funding_pnl;
709        }
710
711        // Update enhanced metrics
712        self.update_enhanced_metrics()?;
713
714        Ok(())
715    }
716
717    /// Check if a given timestamp is a funding payment time (every 8 hours)
718    /// Hyperliquid funding payments occur at 00:00, 08:00, and 16:00 UTC
719    pub fn is_funding_time(&self, timestamp: DateTime<FixedOffset>) -> bool {
720        let hour = timestamp.hour();
721        hour % 8 == 0 && timestamp.minute() == 0 && timestamp.second() == 0
722    }
723
724    /// Get funding rate for a specific timestamp from the data
725    pub fn get_funding_rate_for_timestamp(&self, timestamp: DateTime<FixedOffset>) -> Option<f64> {
726        self.data.get_funding_rate_at(timestamp)
727    }
728
729    /// Calculate funding payment based on position size, funding rate, and mark price
730    /// Formula: funding_payment = position_size * funding_rate * mark_price
731    /// Positive payment means funding received, negative means funding paid
732    pub fn calculate_funding_payment(&self, position_size: f64, funding_rate: f64, mark_price: f64) -> f64 {
733        // If no position, no funding payment
734        if position_size == 0.0 {
735            return 0.0;
736        }
737
738        // Calculate funding payment
739        // For long positions: pay funding when rate is positive, receive when negative
740        // For short positions: receive funding when rate is positive, pay when negative
741        let funding_payment = -position_size * funding_rate * mark_price;
742        
743        funding_payment
744    }
745
746    /// Update enhanced metrics based on current funding and trading data
747    fn update_enhanced_metrics(&mut self) -> Result<()> {
748        if self.funding_pnl.is_empty() {
749            return Ok(());
750        }
751
752        // Calculate funding-only return
753        let final_funding_pnl = self.funding_pnl.last().unwrap_or(&0.0);
754        self.enhanced_metrics.funding_only_return = final_funding_pnl / self.initial_capital;
755
756        // Calculate trading-only return
757        let final_trading_pnl = self.trading_pnl.last().unwrap_or(&0.0);
758        self.enhanced_metrics.trading_only_return = final_trading_pnl / self.initial_capital;
759
760        // Calculate total return with funding
761        self.enhanced_metrics.total_return_with_funding = 
762            self.enhanced_metrics.trading_only_return + self.enhanced_metrics.funding_only_return;
763
764        // Count funding payments
765        self.enhanced_metrics.funding_payments_received = 
766            self.funding_payments.iter().filter(|p| p.payment_amount > 0.0).count();
767        self.enhanced_metrics.funding_payments_paid = 
768            self.funding_payments.iter().filter(|p| p.payment_amount < 0.0).count();
769
770        // Calculate average funding rate
771        if !self.funding_payments.is_empty() {
772            let total_funding_rate: f64 = self.funding_payments.iter().map(|p| p.funding_rate).sum();
773            self.enhanced_metrics.average_funding_rate = total_funding_rate / self.funding_payments.len() as f64;
774
775            // Calculate funding rate volatility (standard deviation)
776            let mean_rate = self.enhanced_metrics.average_funding_rate;
777            let variance: f64 = self.funding_payments.iter()
778                .map(|p| (p.funding_rate - mean_rate).powi(2))
779                .sum::<f64>() / self.funding_payments.len() as f64;
780            self.enhanced_metrics.funding_rate_volatility = variance.sqrt();
781        }
782
783        // Calculate maximum drawdown with funding
784        let mut peak = self.initial_capital;
785        let mut max_drawdown = 0.0;
786        
787        for i in 0..self.funding_pnl.len() {
788            let total_value = self.initial_capital + self.trading_pnl[i] + self.funding_pnl[i];
789            if total_value > peak {
790                peak = total_value;
791            }
792            let drawdown = (peak - total_value) / peak;
793            if drawdown > max_drawdown {
794                max_drawdown = drawdown;
795            }
796        }
797        self.enhanced_metrics.max_drawdown_with_funding = -max_drawdown;
798
799        // Calculate Sharpe ratio with funding (simplified version)
800        if self.funding_pnl.len() > 1 {
801            let returns: Vec<f64> = (1..self.funding_pnl.len())
802                .map(|i| {
803                    let prev_total = self.initial_capital + self.trading_pnl[i-1] + self.funding_pnl[i-1];
804                    let curr_total = self.initial_capital + self.trading_pnl[i] + self.funding_pnl[i];
805                    if prev_total > 0.0 {
806                        (curr_total - prev_total) / prev_total
807                    } else {
808                        0.0
809                    }
810                })
811                .collect();
812
813            if !returns.is_empty() {
814                let mean_return = returns.iter().sum::<f64>() / returns.len() as f64;
815                let variance = returns.iter()
816                    .map(|r| (r - mean_return).powi(2))
817                    .sum::<f64>() / returns.len() as f64;
818                let std_dev = variance.sqrt();
819                
820                if std_dev > 0.0 {
821                    self.enhanced_metrics.sharpe_ratio_with_funding = mean_return / std_dev;
822                }
823            }
824        }
825
826        Ok(())
827    }
828
829    // Getter methods
830    pub fn data(&self) -> &HyperliquidData { &self.data }
831    pub fn strategy_name(&self) -> &str { &self.strategy_name }
832    pub fn initial_capital(&self) -> f64 { self.initial_capital }
833    pub fn commission_config(&self) -> &HyperliquidCommission { &self.commission_config }
834    pub fn funding_pnl(&self) -> &[f64] { &self.funding_pnl }
835    pub fn trading_pnl(&self) -> &[f64] { &self.trading_pnl }
836    pub fn total_funding_paid(&self) -> f64 { self.total_funding_paid }
837    pub fn total_funding_received(&self) -> f64 { self.total_funding_received }
838    pub fn funding_payments(&self) -> &[FundingPayment] { &self.funding_payments }
839    pub fn enhanced_metrics(&self) -> &EnhancedMetrics { &self.enhanced_metrics }
840    pub fn is_initialized(&self) -> bool { self.base_backtest.is_some() }
841
842    pub fn validate(&self) -> Result<()> {
843        self.commission_config.validate()?;
844        self.data.validate_all_data()?;
845        if self.initial_capital <= 0.0 {
846            return Err(HyperliquidBacktestError::validation("Initial capital must be positive"));
847        }
848        if self.strategy_name.is_empty() {
849            return Err(HyperliquidBacktestError::validation("Strategy name cannot be empty"));
850        }
851        Ok(())
852    }
853
854    /// Get commission statistics
855    pub fn commission_stats(&self) -> CommissionStats {
856        CommissionStats {
857            total_commission: self.commission_tracker.total_commission(),
858            maker_fees: self.commission_tracker.total_maker_fees,
859            taker_fees: self.commission_tracker.total_taker_fees,
860            maker_orders: self.commission_tracker.maker_order_count,
861            taker_orders: self.commission_tracker.taker_order_count,
862            average_rate: self.commission_tracker.average_commission_rate(),
863            maker_taker_ratio: self.commission_tracker.maker_taker_ratio(),
864        }
865    }
866
867    /// Calculate trade commission based on order type strategy
868    pub fn calculate_trade_commission(
869        &self,
870        trade_value: f64,
871        trade_index: usize,
872        scenario: TradingScenario,
873    ) -> (OrderType, f64) {
874        let order_type = self.order_type_strategy.get_order_type(trade_index);
875        let commission = self.commission_config.calculate_scenario_fee(scenario, order_type, trade_value);
876        (order_type, commission)
877    }
878
879    /// Track commission for reporting
880    pub fn track_commission(
881        &mut self,
882        timestamp: DateTime<chrono::FixedOffset>,
883        order_type: OrderType,
884        trade_value: f64,
885        commission_paid: f64,
886        scenario: TradingScenario,
887    ) {
888        self.commission_tracker.add_commission(
889            timestamp,
890            order_type,
891            trade_value,
892            commission_paid,
893            scenario,
894        );
895    }
896
897    /// Generate a funding summary for reporting
898    pub fn funding_summary(&self) -> FundingSummary {
899        let net_funding = self.total_funding_received - self.total_funding_paid;
900        let funding_payment_count = self.funding_payments.len();
901        
902        let average_funding_payment = if funding_payment_count > 0 {
903            let total_payments: f64 = self.funding_payments.iter()
904                .map(|p| p.payment_amount)
905                .sum();
906            total_payments / funding_payment_count as f64
907        } else {
908            0.0
909        };
910        
911        let funding_contribution_percentage = if self.enhanced_metrics.total_return_with_funding != 0.0 {
912            (self.enhanced_metrics.funding_only_return / self.enhanced_metrics.total_return_with_funding) * 100.0
913        } else {
914            0.0
915        };
916        
917        FundingSummary {
918            total_funding_paid: self.total_funding_paid,
919            total_funding_received: self.total_funding_received,
920            net_funding,
921            funding_payment_count,
922            average_funding_payment,
923            average_funding_rate: self.enhanced_metrics.average_funding_rate,
924            funding_rate_volatility: self.enhanced_metrics.funding_rate_volatility,
925            funding_contribution_percentage,
926        }
927    }
928
929    /// Generate an enhanced report with Hyperliquid-specific metrics
930    pub fn enhanced_report(&self) -> Result<EnhancedReport> {
931        // Ensure we have a base backtest
932        let base_backtest = match &self.base_backtest {
933            Some(backtest) => backtest,
934            None => return Err(HyperliquidBacktestError::validation(
935                "Base backtest must be initialized before generating a report"
936            )),
937        };
938        
939        // Calculate basic metrics from base backtest
940        let final_equity = if let Some(last_position) = base_backtest.position().last() {
941            if let Some(last_close) = self.data.close.last() {
942                if let Some(last_account) = base_backtest.account().last() {
943                    last_position * last_close + last_account
944                } else {
945                    self.initial_capital
946                }
947            } else {
948                self.initial_capital
949            }
950        } else {
951            self.initial_capital
952        };
953        
954        let total_return = (final_equity - self.initial_capital) / self.initial_capital;
955        
956        // Calculate trade statistics
957        let mut trade_count = 0;
958        let mut win_count = 0;
959        let mut profit_sum = 0.0;
960        let mut loss_sum = 0.0;
961        
962        // Count trades and calculate win rate
963        let orders = base_backtest.orders();
964        if orders.len() > 1 {
965            for i in 1..orders.len() {
966                if orders[i] != orders[i-1] && orders[i] != rs_backtester::orders::Order::NULL {
967                    trade_count += 1;
968                    
969                    // Simple profit calculation (this is a simplification)
970                    if i < self.data.close.len() - 1 {
971                        let entry_price = self.data.close[i];
972                        let exit_price = self.data.close[i+1];
973                        let profit = match orders[i] {
974                            rs_backtester::orders::Order::BUY => exit_price - entry_price,
975                            rs_backtester::orders::Order::SHORTSELL => entry_price - exit_price,
976                            _ => 0.0,
977                        };
978                        
979                        if profit > 0.0 {
980                            win_count += 1;
981                            profit_sum += profit;
982                        } else {
983                            loss_sum += profit.abs();
984                        }
985                    }
986                }
987            }
988        }
989        
990        let win_rate = if trade_count > 0 {
991            win_count as f64 / trade_count as f64
992        } else {
993            0.0
994        };
995        
996        let profit_factor = if loss_sum > 0.0 {
997            profit_sum / loss_sum
998        } else if profit_sum > 0.0 {
999            f64::INFINITY
1000        } else {
1001            0.0
1002        };
1003        
1004        // Calculate Sharpe ratio (simplified)
1005        let sharpe_ratio = self.enhanced_metrics.sharpe_ratio_with_funding;
1006        
1007        // Calculate max drawdown
1008        let max_drawdown = self.enhanced_metrics.max_drawdown_with_funding;
1009        
1010        // Create enhanced report
1011        let report = EnhancedReport {
1012            strategy_name: self.strategy_name.clone(),
1013            ticker: self.data.symbol.clone(),
1014            initial_capital: self.initial_capital,
1015            final_equity,
1016            total_return,
1017            trade_count,
1018            win_rate,
1019            profit_factor,
1020            sharpe_ratio,
1021            max_drawdown,
1022            enhanced_metrics: self.enhanced_metrics.clone(),
1023            commission_stats: self.commission_stats(),
1024            funding_summary: self.funding_summary(),
1025        };
1026        
1027        Ok(report)
1028    }
1029
1030    /// Print enhanced report to console
1031    pub fn print_enhanced_report(&self) -> Result<()> {
1032        let report = self.enhanced_report()?;
1033        
1034        println!("\n=== HYPERLIQUID BACKTEST REPORT ===");
1035        println!("Strategy: {}", report.strategy_name);
1036        println!("Symbol: {}", report.ticker);
1037        println!("Period: {} to {}", 
1038            self.data.datetime.first().unwrap_or(&DateTime::parse_from_rfc3339("1970-01-01T00:00:00Z").unwrap()),
1039            self.data.datetime.last().unwrap_or(&DateTime::parse_from_rfc3339("1970-01-01T00:00:00Z").unwrap())
1040        );
1041        println!("Initial Capital: ${:.2}", report.initial_capital);
1042        println!("Final Equity: ${:.2}", report.final_equity);
1043        
1044        // Print base report metrics
1045        println!("\n--- Base Performance Metrics ---");
1046        println!("Total Return: {:.2}%", report.total_return * 100.0);
1047        println!("Sharpe Ratio: {:.2}", report.sharpe_ratio);
1048        println!("Max Drawdown: {:.2}%", report.max_drawdown * 100.0);
1049        println!("Win Rate: {:.2}%", report.win_rate * 100.0);
1050        println!("Profit Factor: {:.2}", report.profit_factor);
1051        println!("Trade Count: {}", report.trade_count);
1052        
1053        // Print enhanced metrics
1054        println!("\n--- Enhanced Performance Metrics (with Funding) ---");
1055        println!("Total Return (with Funding): {:.2}%", report.enhanced_metrics.total_return_with_funding * 100.0);
1056        println!("Trading-Only Return: {:.2}%", report.enhanced_metrics.trading_only_return * 100.0);
1057        println!("Funding-Only Return: {:.2}%", report.enhanced_metrics.funding_only_return * 100.0);
1058        println!("Sharpe Ratio (with Funding): {:.2}", report.enhanced_metrics.sharpe_ratio_with_funding);
1059        println!("Max Drawdown (with Funding): {:.2}%", report.enhanced_metrics.max_drawdown_with_funding * 100.0);
1060        
1061        // Print commission statistics
1062        println!("\n--- Commission Statistics ---");
1063        println!("Total Commission: ${:.2}", report.commission_stats.total_commission);
1064        println!("Maker Fees: ${:.2} ({} orders)", 
1065            report.commission_stats.maker_fees, 
1066            report.commission_stats.maker_orders
1067        );
1068        println!("Taker Fees: ${:.2} ({} orders)", 
1069            report.commission_stats.taker_fees, 
1070            report.commission_stats.taker_orders
1071        );
1072        println!("Average Commission Rate: {:.4}%", report.commission_stats.average_rate * 100.0);
1073        println!("Maker/Taker Ratio: {:.2}", report.commission_stats.maker_taker_ratio);
1074        
1075        // Print funding summary
1076        println!("\n--- Funding Summary ---");
1077        println!("Total Funding Paid: ${:.2}", report.funding_summary.total_funding_paid);
1078        println!("Total Funding Received: ${:.2}", report.funding_summary.total_funding_received);
1079        println!("Net Funding: ${:.2}", report.funding_summary.net_funding);
1080        println!("Funding Payments: {}", report.funding_summary.funding_payment_count);
1081        println!("Average Funding Payment: ${:.2}", report.funding_summary.average_funding_payment);
1082        println!("Average Funding Rate: {:.6}%", report.funding_summary.average_funding_rate * 100.0);
1083        println!("Funding Rate Volatility: {:.6}%", report.funding_summary.funding_rate_volatility * 100.0);
1084        println!("Funding Contribution: {:.2}% of total return", report.funding_summary.funding_contribution_percentage);
1085        
1086        println!("\n=== END OF REPORT ===\n");
1087        
1088        Ok(())
1089    }
1090
1091    /// Export backtest results to CSV
1092    pub fn export_to_csv<P: AsRef<Path>>(&self, path: P) -> Result<()> {
1093        // Ensure we have a base backtest
1094        if self.base_backtest.is_none() {
1095            return Err(HyperliquidBacktestError::validation(
1096                "Base backtest must be initialized before exporting to CSV"
1097            ));
1098        }
1099        
1100        // Create CSV writer
1101        let file = File::create(path)?;
1102        let mut wtr = csv::Writer::from_writer(file);
1103        
1104        // Write header
1105        wtr.write_record(&[
1106            "Timestamp",
1107            "Open",
1108            "High",
1109            "Low",
1110            "Close",
1111            "Volume",
1112            "Funding Rate",
1113            "Position",
1114            "Trading PnL",
1115            "Funding PnL",
1116            "Total PnL",
1117            "Equity",
1118        ])?;
1119        
1120        // Write data rows
1121        for i in 0..self.data.len() {
1122            let timestamp = self.data.datetime[i].to_rfc3339();
1123            let open = self.data.open[i].to_string();
1124            let high = self.data.high[i].to_string();
1125            let low = self.data.low[i].to_string();
1126            let close = self.data.close[i].to_string();
1127            let volume = self.data.volume[i].to_string();
1128            
1129            // Get funding rate (if available)
1130            let funding_rate = match self.get_funding_rate_for_timestamp(self.data.datetime[i]) {
1131                Some(rate) => rate.to_string(),
1132                None => "".to_string(),
1133            };
1134            
1135            // Get position (placeholder - would come from base backtest)
1136            let position = "0.0".to_string(); // Placeholder
1137            
1138            // Get PnL values
1139            let trading_pnl = if i < self.trading_pnl.len() {
1140                self.trading_pnl[i].to_string()
1141            } else {
1142                "0.0".to_string()
1143            };
1144            
1145            let funding_pnl = if i < self.funding_pnl.len() {
1146                self.funding_pnl[i].to_string()
1147            } else {
1148                "0.0".to_string()
1149            };
1150            
1151            // Calculate total PnL and equity
1152            let total_pnl = (
1153                self.trading_pnl.get(i).unwrap_or(&0.0) + 
1154                self.funding_pnl.get(i).unwrap_or(&0.0)
1155            ).to_string();
1156            
1157            let equity = (
1158                self.initial_capital + 
1159                self.trading_pnl.get(i).unwrap_or(&0.0) + 
1160                self.funding_pnl.get(i).unwrap_or(&0.0)
1161            ).to_string();
1162            
1163            // Write row
1164            wtr.write_record(&[
1165                &timestamp,
1166                &open,
1167                &high,
1168                &low,
1169                &close,
1170                &volume,
1171                &funding_rate,
1172                &position,
1173                &trading_pnl,
1174                &funding_pnl,
1175                &total_pnl,
1176                &equity,
1177            ])?;
1178        }
1179        
1180        // Flush writer
1181        wtr.flush()?;
1182        
1183        Ok(())
1184    }
1185
1186    /// Export funding payments to CSV
1187    pub fn export_funding_to_csv<P: AsRef<Path>>(&self, path: P) -> Result<()> {
1188        // Create CSV writer
1189        let file = File::create(path)?;
1190        let mut wtr = csv::Writer::from_writer(file);
1191        
1192        // Write header
1193        wtr.write_record(&[
1194            "Timestamp",
1195            "Position Size",
1196            "Funding Rate",
1197            "Mark Price",
1198            "Payment Amount",
1199        ])?;
1200        
1201        // Write funding payment rows
1202        for payment in &self.funding_payments {
1203            wtr.write_record(&[
1204                &payment.timestamp.to_rfc3339(),
1205                &payment.position_size.to_string(),
1206                &payment.funding_rate.to_string(),
1207                &payment.mark_price.to_string(),
1208                &payment.payment_amount.to_string(),
1209            ])?;
1210        }
1211        
1212        // Flush writer
1213        wtr.flush()?;
1214        
1215        Ok(())
1216    }
1217
1218    /// Generate a detailed funding report
1219    pub fn funding_report(&self) -> Result<crate::funding_report::FundingReport> {
1220        use crate::funding_report::FundingReport;
1221        
1222        // Ensure we have a base backtest
1223        if self.base_backtest.is_none() {
1224            return Err(HyperliquidBacktestError::validation(
1225                "Base backtest must be initialized before generating a funding report"
1226            ));
1227        }
1228        
1229        // Get position sizes and values
1230        let mut position_sizes = Vec::with_capacity(self.data.len());
1231        let mut position_values = Vec::with_capacity(self.data.len());
1232        
1233        // Get positions from base backtest if available, otherwise use zeros
1234        if let Some(base_backtest) = &self.base_backtest {
1235            let positions = base_backtest.position();
1236            
1237            for i in 0..self.data.len() {
1238                let position_size = if i < positions.len() {
1239                    positions[i]
1240                } else {
1241                    0.0
1242                };
1243                
1244                position_sizes.push(position_size);
1245                position_values.push(position_size * self.data.close[i]);
1246            }
1247        } else {
1248            // Fill with zeros if no base backtest
1249            position_sizes = vec![0.0; self.data.len()];
1250            position_values = vec![0.0; self.data.len()];
1251        }
1252        
1253        // Calculate total trading PnL
1254        let trading_pnl = if let Some(last) = self.trading_pnl.last() {
1255            *last
1256        } else {
1257            0.0
1258        };
1259        
1260        // Calculate total funding PnL
1261        let funding_pnl = if let Some(last) = self.funding_pnl.last() {
1262            *last
1263        } else {
1264            0.0
1265        };
1266        
1267        // Create funding report
1268        FundingReport::new(
1269            &self.data.symbol,
1270            &self.data,
1271            &position_values,
1272            self.funding_payments.clone(),
1273            funding_pnl,
1274        )
1275    }
1276
1277    /// Export enhanced report to CSV
1278    pub fn export_report_to_csv<P: AsRef<Path>>(&self, path: P) -> Result<()> {
1279        let report = self.enhanced_report()?;
1280        
1281        // Create CSV writer
1282        let file = File::create(path)?;
1283        let mut wtr = csv::Writer::from_writer(file);
1284        
1285        // Write header and data as key-value pairs
1286        wtr.write_record(&["Metric", "Value"])?;
1287        
1288        // Strategy information
1289        wtr.write_record(&["Strategy", &report.strategy_name])?;
1290        wtr.write_record(&["Symbol", &report.ticker])?;
1291        wtr.write_record(&["Initial Capital", &report.initial_capital.to_string()])?;
1292        wtr.write_record(&["Final Equity", &report.final_equity.to_string()])?;
1293        
1294        // Base metrics
1295        wtr.write_record(&["Total Return", &(report.total_return * 100.0).to_string()])?;
1296        wtr.write_record(&["Sharpe Ratio", &report.sharpe_ratio.to_string()])?;
1297        wtr.write_record(&["Max Drawdown", &(report.max_drawdown * 100.0).to_string()])?;
1298        wtr.write_record(&["Win Rate", &(report.win_rate * 100.0).to_string()])?;
1299        wtr.write_record(&["Profit Factor", &report.profit_factor.to_string()])?;
1300        wtr.write_record(&["Trade Count", &report.trade_count.to_string()])?;
1301        
1302        // Enhanced metrics
1303        wtr.write_record(&["Total Return (with Funding)", &(report.enhanced_metrics.total_return_with_funding * 100.0).to_string()])?;
1304        wtr.write_record(&["Trading-Only Return", &(report.enhanced_metrics.trading_only_return * 100.0).to_string()])?;
1305        wtr.write_record(&["Funding-Only Return", &(report.enhanced_metrics.funding_only_return * 100.0).to_string()])?;
1306        wtr.write_record(&["Sharpe Ratio (with Funding)", &report.enhanced_metrics.sharpe_ratio_with_funding.to_string()])?;
1307        wtr.write_record(&["Max Drawdown (with Funding)", &(report.enhanced_metrics.max_drawdown_with_funding * 100.0).to_string()])?;
1308        
1309        // Commission statistics
1310        wtr.write_record(&["Total Commission", &report.commission_stats.total_commission.to_string()])?;
1311        wtr.write_record(&["Maker Fees", &report.commission_stats.maker_fees.to_string()])?;
1312        wtr.write_record(&["Taker Fees", &report.commission_stats.taker_fees.to_string()])?;
1313        wtr.write_record(&["Maker Orders", &report.commission_stats.maker_orders.to_string()])?;
1314        wtr.write_record(&["Taker Orders", &report.commission_stats.taker_orders.to_string()])?;
1315        wtr.write_record(&["Average Commission Rate", &(report.commission_stats.average_rate * 100.0).to_string()])?;
1316        wtr.write_record(&["Maker/Taker Ratio", &report.commission_stats.maker_taker_ratio.to_string()])?;
1317        
1318        // Funding summary
1319        wtr.write_record(&["Total Funding Paid", &report.funding_summary.total_funding_paid.to_string()])?;
1320        wtr.write_record(&["Total Funding Received", &report.funding_summary.total_funding_received.to_string()])?;
1321        wtr.write_record(&["Net Funding", &report.funding_summary.net_funding.to_string()])?;
1322        wtr.write_record(&["Funding Payments", &report.funding_summary.funding_payment_count.to_string()])?;
1323        wtr.write_record(&["Average Funding Payment", &report.funding_summary.average_funding_payment.to_string()])?;
1324        wtr.write_record(&["Average Funding Rate", &(report.funding_summary.average_funding_rate * 100.0).to_string()])?;
1325        wtr.write_record(&["Funding Rate Volatility", &(report.funding_summary.funding_rate_volatility * 100.0).to_string()])?;
1326        wtr.write_record(&["Funding Contribution", &report.funding_summary.funding_contribution_percentage.to_string()])?;
1327        
1328        // Flush writer
1329        wtr.flush()?;
1330        
1331        Ok(())
1332    }
1333}