use chrono::{DateTime, Duration, FixedOffset, Utc};
use hyperliquid_backtest::prelude::*;
use hyperliquid_backtest::funding_report::*;
use std::fs::File;
use std::io::Write;
#[tokio::main]
async fn main() -> Result<()> {
println!("Hyperliquid Funding Report Example");
println!("=================================\n");
let end_time = Utc::now().timestamp() as u64;
let start_time = end_time - (30 * 24 * 3600);
println!("Fetching BTC/USD data for the last 30 days...");
let data = HyperliquidData::fetch_btc("1h", start_time, end_time).await?;
println!("Data fetched: {} data points from {} to {}\n",
data.len(),
data.datetime.first().unwrap().format("%Y-%m-%d %H:%M"),
data.datetime.last().unwrap().format("%Y-%m-%d %H:%M"));
println!("Running backtest with SMA crossover strategy...");
let mut strategy = Strategy::new();
let short_period = 10;
let long_period = 30;
strategy.init(Box::new(move |_ctx, _data| {
}));
strategy.next(Box::new(move |ctx, data| {
if data.index < long_period {
return;
}
let mut short_sum = 0.0;
let mut long_sum = 0.0;
for i in 0..short_period {
short_sum += data.close[data.index - i];
}
for i in 0..long_period {
long_sum += data.close[data.index - i];
}
let short_sma = short_sum / short_period as f64;
let long_sma = long_sum / long_period as f64;
let position = ctx.position();
if short_sma > long_sma && position <= 0.0 {
ctx.entry_qty(1.0);
} else if short_sma < long_sma && position >= 0.0 {
ctx.entry_qty(-1.0);
}
}));
let rs_data = data.to_rs_backtester_data();
let commission = HyperliquidCommission {
maker_rate: 0.0002, taker_rate: 0.0005, funding_enabled: true,
};
let mut backtest = HyperliquidBacktest::new(
data.clone(),
strategy,
10000.0, commission,
);
backtest.calculate_with_funding();
println!("Generating funding report...");
let funding_report = backtest.funding_report()?;
println!("\nFunding Report Summary:");
println!("{}", funding_report.summary());
let detailed_report = funding_report.detailed_report()?;
let report_file = "funding_report.md";
let mut file = File::create(report_file)?;
file.write_all(detailed_report.as_bytes())?;
println!("\nDetailed report saved to {}", report_file);
let metrics_csv = funding_report.export_metrics_by_period_to_csv()?;
let metrics_file = "funding_metrics.csv";
let mut file = File::create(metrics_file)?;
file.write_all(metrics_csv.as_bytes())?;
println!("Funding metrics by period saved to {}", metrics_file);
let payments_csv = funding_report.to_csv()?;
let payments_file = "funding_payments.csv";
let mut file = File::create(payments_file)?;
file.write_all(payments_csv.as_bytes())?;
println!("Funding payments data saved to {}", payments_file);
let viz_data = funding_report.visualization_data()?;
println!("\nVisualization data generated with {} funding rate points", viz_data.rates.len());
let pnl_breakdown = funding_report.calculate_funding_pnl_breakdown();
println!("\nFunding PnL Breakdown:");
println!("Long positions: ${:.2} ({}%)",
pnl_breakdown.long_net,
(pnl_breakdown.long_percentage * 100.0).round());
println!("Short positions: ${:.2} ({}%)",
pnl_breakdown.short_net,
(pnl_breakdown.short_percentage * 100.0).round());
println!("Total: ${:.2}", pnl_breakdown.total_net);
match funding_report.analyze_funding_regimes() {
Ok(regime_analysis) => {
println!("\nFunding Regime Analysis:");
println!("Total regimes detected: {}", regime_analysis.total_regimes);
println!("Positive regimes: {}", regime_analysis.positive_regimes);
println!("Negative regimes: {}", regime_analysis.negative_regimes);
println!("Neutral regimes: {}", regime_analysis.neutral_regimes);
println!("Average regime duration: {:.1} periods", regime_analysis.avg_regime_duration);
},
Err(e) => {
println!("\nCould not analyze funding regimes: {}", e);
}
}
println!("\nExample completed successfully!");
Ok(())
}