1use crate::data::HyperliquidData;
4use crate::errors::{HyperliquidBacktestError, Result};
5use crate::backtest::{HyperliquidBacktest, FundingPayment};
6use std::fs::File;
7use std::io::Write;
8
9pub fn export_funding_payments_to_csv(payments: &[FundingPayment], file_path: &str) -> Result<()> {
11 let mut file = File::create(file_path)
12 .map_err(|e| HyperliquidBacktestError::data_conversion(format!("Failed to create file {}: {}", file_path, e)))?;
13
14 writeln!(file, "timestamp,funding_rate,position_size,price,payment_amount")
16 .map_err(|e| HyperliquidBacktestError::data_conversion(format!("Failed to write header: {}", e)))?;
17
18 for payment in payments {
20 let timestamp = payment.timestamp.format("%Y-%m-%d %H:%M:%S").to_string();
21
22 writeln!(
23 file,
24 "{},{:.8},{},{:.2},{:.2}",
25 timestamp,
26 payment.funding_rate,
27 payment.position_size,
28 payment.mark_price,
29 payment.payment_amount
30 ).map_err(|e| HyperliquidBacktestError::data_conversion(format!("Failed to write payment data: {}", e)))?;
31 }
32
33 Ok(())
34}
35
36pub fn export_funding_rate_history(data: &HyperliquidData, file_path: &str) -> Result<()> {
38 let mut file = File::create(file_path)
39 .map_err(|e| HyperliquidBacktestError::data_conversion(format!("Failed to create file {}: {}", file_path, e)))?;
40
41 writeln!(file, "timestamp,funding_rate")
43 .map_err(|e| HyperliquidBacktestError::data_conversion(format!("Failed to write header: {}", e)))?;
44
45 for i in 0..data.datetime.len() {
47 let timestamp = data.datetime[i].format("%Y-%m-%d %H:%M:%S").to_string();
48 let funding_rate = data.funding_rates[i];
49
50 if !funding_rate.is_nan() {
52 writeln!(
53 file,
54 "{},{:.8}",
55 timestamp,
56 funding_rate
57 ).map_err(|e| HyperliquidBacktestError::data_conversion(format!("Failed to write funding rate data: {}", e)))?;
58 }
59 }
60
61 Ok(())
62}
63
64pub trait EnhancedCsvExport {
66 fn export_to_csv(&self, file_path: &str) -> Result<()>;
68
69 fn export_to_csv_extended(&self, file_path: &str, include_funding: bool, include_trading: bool, include_total: bool) -> Result<()>;
71}
72
73pub trait EnhancedCsvExportExt {
75 fn export_to_csv(&self, file_path: &str) -> Result<()>;
77
78 fn export_to_csv_extended(&self, file_path: &str, include_funding: bool, include_trading: bool, include_total: bool) -> Result<()>;
80}
81
82impl EnhancedCsvExport for HyperliquidBacktest {
83 fn export_to_csv(&self, file_path: &str) -> Result<()> {
84 let mut file = File::create(file_path)
85 .map_err(|e| HyperliquidBacktestError::data_conversion(format!("Failed to create file {}: {}", file_path, e)))?;
86
87 writeln!(file, "timestamp,price,position,equity,funding_rate,funding_pnl")
89 .map_err(|e| HyperliquidBacktestError::data_conversion(format!("Failed to write header: {}", e)))?;
90
91 for i in 0..self.data().len() {
93 let timestamp = self.data().datetime[i].format("%Y-%m-%d %H:%M:%S").to_string();
94 let price = self.data().close[i];
95 let position = 0.0; let equity = self.initial_capital() + self.funding_pnl[i]; let funding_rate = if i < self.data().funding_rates.len() {
98 self.data().funding_rates[i]
99 } else {
100 f64::NAN
101 };
102
103 writeln!(
104 file,
105 "{},{:.2},{:.2},{:.2},{:.8},{:.2}",
106 timestamp,
107 price,
108 position,
109 equity,
110 if funding_rate.is_nan() { 0.0 } else { funding_rate },
111 self.funding_pnl[i]
112 ).map_err(|e| HyperliquidBacktestError::data_conversion(format!("Failed to write data row: {}", e)))?;
113 }
114
115 Ok(())
116 }
117
118 fn export_to_csv_extended(&self, file_path: &str, include_funding: bool, include_trading: bool, include_total: bool) -> Result<()> {
119 let mut file = File::create(file_path)
120 .map_err(|e| HyperliquidBacktestError::data_conversion(format!("Failed to create file {}: {}", file_path, e)))?;
121
122 let mut header = String::from("timestamp,price,position,equity");
124
125 if include_funding {
126 header.push_str(",funding_rate,funding_pnl");
127 }
128
129 if include_trading {
130 header.push_str(",trading_pnl");
131 }
132
133 if include_total {
134 header.push_str(",total_pnl");
135 }
136
137 writeln!(file, "{}", header)
139 .map_err(|e| HyperliquidBacktestError::data_conversion(format!("Failed to write header: {}", e)))?;
140
141 for i in 0..self.data().len() {
143 let timestamp = self.data().datetime[i].format("%Y-%m-%d %H:%M:%S").to_string();
144 let price = self.data().close[i];
145 let position = 0.0; let equity = self.initial_capital() + self.funding_pnl[i]; let mut row = format!("{},{:.2},{:.2},{:.2}", timestamp, price, position, equity);
149
150 if include_funding {
151 let funding_rate = if i < self.data().funding_rates.len() {
152 self.data().funding_rates[i]
153 } else {
154 f64::NAN
155 };
156
157 row.push_str(&format!(",{:.8},{:.2}",
158 if funding_rate.is_nan() { 0.0 } else { funding_rate },
159 self.funding_pnl[i]
160 ));
161 }
162
163 if include_trading {
164 row.push_str(&format!(",{:.2}", self.trading_pnl[i]));
165 }
166
167 if include_total {
168 let total_pnl = self.funding_pnl[i] + self.trading_pnl[i];
169 row.push_str(&format!(",{:.2}", total_pnl));
170 }
171
172 writeln!(file, "{}", row)
173 .map_err(|e| HyperliquidBacktestError::data_conversion(format!("Failed to write data row: {}", e)))?;
174 }
175
176 Ok(())
177 }
178}
179
180impl EnhancedCsvExportExt for HyperliquidBacktest {
181 fn export_to_csv(&self, file_path: &str) -> Result<()> {
182 <Self as EnhancedCsvExport>::export_to_csv(self, file_path)
183 }
184
185 fn export_to_csv_extended(&self, file_path: &str, include_funding: bool, include_trading: bool, include_total: bool) -> Result<()> {
186 <Self as EnhancedCsvExport>::export_to_csv_extended(self, file_path, include_funding, include_trading, include_total)
187 }
188}
189
190pub struct StrategyComparisonData {
192 pub strategies: Vec<HyperliquidBacktest>,
194}
195
196impl StrategyComparisonData {
197 pub fn export_to_csv(&self, file_path: &str) -> Result<()> {
199 let mut file = File::create(file_path)
200 .map_err(|e| HyperliquidBacktestError::data_conversion(format!("Failed to create file {}: {}", file_path, e)))?;
201
202 let mut header = String::from("timestamp,price");
204
205 for strategy in &self.strategies {
206 let strategy_name = strategy.strategy_name().replace(" ", "_");
207 header.push_str(&format!(",{}_position,{}_equity", strategy_name, strategy_name));
208 }
209
210 writeln!(file, "{}", header)
212 .map_err(|e| HyperliquidBacktestError::data_conversion(format!("Failed to write header: {}", e)))?;
213
214 if self.strategies.is_empty() {
216 return Ok(());
217 }
218
219 let data_len = self.strategies[0].data().len();
220
221 for i in 0..data_len {
223 let timestamp = self.strategies[0].data().datetime[i].format("%Y-%m-%d %H:%M:%S").to_string();
224 let price = self.strategies[0].data().close[i];
225
226 let mut row = format!("{},{:.2}", timestamp, price);
227
228 for strategy in &self.strategies {
229 let position = 0.0; let equity = strategy.initial_capital() +
231 (if i < strategy.funding_pnl.len() { strategy.funding_pnl[i] } else { 0.0 }) +
232 (if i < strategy.trading_pnl.len() { strategy.trading_pnl[i] } else { 0.0 });
233
234 row.push_str(&format!(",{:.2},{:.2}", position, equity));
235 }
236
237 writeln!(file, "{}", row)
238 .map_err(|e| HyperliquidBacktestError::data_conversion(format!("Failed to write data row: {}", e)))?;
239 }
240
241 Ok(())
242 }
243}