Skip to content

Commit 9986592

Browse files
committed
Refactor basic backtest example to use enhanced SMA crossover strategy and improve reporting. Updated imports and removed unnecessary code for clarity. Enhanced funding impact analysis and CSV export functionality.
1 parent 46077fb commit 9986592

1 file changed

Lines changed: 65 additions & 128 deletions

File tree

examples/basic_backtest.rs

Lines changed: 65 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
1-
use chrono::{Duration, Utc};
1+
use chrono::Utc;
22
use hyperliquid_backtest::prelude::*;
3-
use rs_backtester::prelude::*;
4-
use std::fs::File;
5-
use std::io::Write;
6-
use tracing::Instrument;
73

84
/// # Basic Backtest Example
95
///
@@ -38,7 +34,7 @@ use tracing::Instrument;
3834
/// ```
3935
4036
#[tokio::main]
41-
async fn main() -> Result<(), HyperliquidBacktestError> {
37+
async fn main() -> Result<()> {
4238
// Initialize logging for better debugging and monitoring
4339
init_logger_with_level("info");
4440

@@ -52,60 +48,16 @@ async fn main() -> Result<(), HyperliquidBacktestError> {
5248
let start_time = end_time - (90 * 24 * 3600); // 90 days of data
5349

5450
println!("Fetching BTC/USD data for the last 90 days...");
55-
let data = HyperliquidData::fetch_btc("1h", start_time, end_time).await?;
51+
let data = HyperliquidData::fetch("BTC", "1h", start_time, end_time).await?;
5652

5753
println!("Data fetched: {} data points from {} to {}\n",
5854
data.len(),
5955
data.datetime.first().unwrap().format("%Y-%m-%d %H:%M"),
6056
data.datetime.last().unwrap().format("%Y-%m-%d %H:%M"));
6157

62-
// Create a simple SMA crossover strategy
58+
// Create a simple SMA crossover strategy using the enhanced_sma_cross function
6359
println!("Setting up SMA crossover strategy (10/30)...");
64-
let mut strategy = Strategy::new();
65-
66-
// Strategy parameters
67-
let short_period = 10;
68-
let long_period = 30;
69-
70-
// Initialize strategy
71-
strategy.init(Box::new(move |_ctx, _data| {
72-
// No initialization needed for this simple strategy
73-
}));
74-
75-
// Define strategy logic
76-
strategy.next(Box::new(move |ctx, data| {
77-
// Wait until we have enough data for the long SMA
78-
if data.index < long_period {
79-
return;
80-
}
81-
82-
// Calculate short and long SMAs
83-
let mut short_sum = 0.0;
84-
let mut long_sum = 0.0;
85-
86-
for i in 0..short_period {
87-
short_sum += data.close[data.index - i];
88-
}
89-
90-
for i in 0..long_period {
91-
long_sum += data.close[data.index - i];
92-
}
93-
94-
let short_sma = short_sum / short_period as f64;
95-
let long_sma = long_sum / long_period as f64;
96-
97-
// Get current position
98-
let position = ctx.position();
99-
100-
// Trading logic
101-
if short_sma > long_sma && position <= 0.0 {
102-
// Bullish crossover - go long
103-
ctx.entry_qty(1.0);
104-
} else if short_sma < long_sma && position >= 0.0 {
105-
// Bearish crossover - go short
106-
ctx.entry_qty(-1.0);
107-
}
108-
}));
60+
let strategy = enhanced_sma_cross(data.to_rs_backtester_data(), 10, 30, Default::default());
10961

11062
// Set up backtest parameters
11163
let initial_capital = 10000.0; // $10,000
@@ -122,60 +74,61 @@ async fn main() -> Result<(), HyperliquidBacktestError> {
12274
// Create and run backtest
12375
let mut backtest = HyperliquidBacktest::new(
12476
data.clone(),
125-
strategy,
77+
"SMA Crossover (10/30)".to_string(),
12678
initial_capital,
127-
commission,
79+
commission.clone(),
12880
);
12981

13082
// Run backtest with funding rates
131-
backtest.calculate_with_funding();
83+
backtest.calculate_with_funding()?;
13284

133-
// Get backtest results
134-
let stats = backtest.stats();
85+
// Get enhanced report
86+
let report = backtest.enhanced_report()?;
13587

13688
// Print backtest results
13789
println!("\nBacktest Results:");
13890
println!("----------------");
13991
println!("Initial Capital: ${:.2}", initial_capital);
140-
println!("Final Capital: ${:.2}", stats.final_capital);
141-
println!("Net Profit: ${:.2} ({:.2}%)",
142-
stats.net_profit,
143-
stats.net_profit_pct * 100.0);
144-
println!("Max Drawdown: {:.2}%", stats.max_drawdown * 100.0);
145-
println!("Win Rate: {:.2}%", stats.win_rate * 100.0);
146-
println!("Profit Factor: {:.2}", stats.profit_factor);
147-
println!("Sharpe Ratio: {:.2}", stats.sharpe_ratio);
148-
149-
// Get funding impact
150-
let funding_impact = backtest.funding_impact();
92+
println!("Final Equity: ${:.2}", report.final_equity);
93+
println!("Total Return: {:.2}%", report.total_return * 100.0);
94+
println!("Max Drawdown: {:.2}%", report.max_drawdown * 100.0);
95+
println!("Win Rate: {:.2}%", report.win_rate * 100.0);
96+
println!("Profit Factor: {:.2}", report.profit_factor);
97+
println!("Sharpe Ratio: {:.2}", report.sharpe_ratio);
98+
99+
// Get funding impact from enhanced metrics
100+
let enhanced_metrics = &report.enhanced_metrics;
151101
println!("\nFunding Rate Impact:");
152102
println!("------------------");
153-
println!("Total Funding Payments: ${:.2}", funding_impact.total_funding);
154-
println!("Funding as % of PnL: {:.2}%",
155-
if stats.net_profit != 0.0 {
156-
(funding_impact.total_funding / stats.net_profit).abs() * 100.0
157-
} else {
158-
0.0
159-
});
160-
161-
// Get trade statistics
162-
let trade_stats = backtest.trade_stats();
163-
println!("\nTrade Statistics:");
164-
println!("----------------");
165-
println!("Total Trades: {}", trade_stats.total_trades);
166-
println!("Winning Trades: {}", trade_stats.winning_trades);
167-
println!("Losing Trades: {}", trade_stats.losing_trades);
168-
println!("Average Profit per Trade: ${:.2}", trade_stats.avg_profit_per_trade);
169-
println!("Average Profit per Winning Trade: ${:.2}", trade_stats.avg_profit_per_winning_trade);
170-
println!("Average Loss per Losing Trade: ${:.2}", trade_stats.avg_loss_per_losing_trade);
103+
println!("Total Return with Funding: {:.2}%", enhanced_metrics.total_return_with_funding * 100.0);
104+
println!("Trading Only Return: {:.2}%", enhanced_metrics.trading_only_return * 100.0);
105+
println!("Funding Only Return: {:.2}%", enhanced_metrics.funding_only_return * 100.0);
106+
println!("Funding Payments Received: {}", enhanced_metrics.funding_payments_received);
107+
println!("Funding Payments Paid: {}", enhanced_metrics.funding_payments_paid);
108+
println!("Average Funding Rate: {:.4}%", enhanced_metrics.average_funding_rate * 100.0);
109+
110+
// Get commission statistics
111+
let commission_stats = &report.commission_stats;
112+
println!("\nCommission Statistics:");
113+
println!("-------------------");
114+
println!("Total Commission: ${:.2}", commission_stats.total_commission);
115+
println!("Maker Fees: ${:.2}", commission_stats.maker_fees);
116+
println!("Taker Fees: ${:.2}", commission_stats.taker_fees);
117+
println!("Maker/Taker Ratio: {:.2}", commission_stats.maker_taker_ratio);
118+
119+
// Get funding summary
120+
let funding_summary = &report.funding_summary;
121+
println!("\nFunding Summary:");
122+
println!("---------------");
123+
println!("Total Funding Paid: ${:.2}", funding_summary.total_funding_paid);
124+
println!("Total Funding Received: ${:.2}", funding_summary.total_funding_received);
125+
println!("Net Funding: ${:.2}", funding_summary.net_funding);
126+
println!("Funding Contribution: {:.2}%", funding_summary.funding_contribution_percentage * 100.0);
171127

172128
// Export results to CSV
173129
println!("\nExporting results to CSV...");
174-
let csv_data = backtest.to_csv()?;
175-
let csv_file = "basic_backtest_results.csv";
176-
let mut file = File::create(csv_file)?;
177-
file.write_all(csv_data.as_bytes())?;
178-
println!("Results exported to {}", csv_file);
130+
backtest.export_to_csv("basic_backtest_results.csv")?;
131+
println!("Results exported to basic_backtest_results.csv");
179132

180133
// Run the same backtest without funding to compare
181134
println!("\nRunning comparison backtest without funding rates...");
@@ -184,54 +137,38 @@ async fn main() -> Result<(), HyperliquidBacktestError> {
184137

185138
let mut backtest_no_funding = HyperliquidBacktest::new(
186139
data.clone(),
187-
Strategy::new_from_fn(move |ctx, data| {
188-
// Same strategy logic as above
189-
if data.index < long_period {
190-
return;
191-
}
192-
193-
let mut short_sum = 0.0;
194-
let mut long_sum = 0.0;
195-
196-
for i in 0..short_period {
197-
short_sum += data.close[data.index - i];
198-
}
199-
200-
for i in 0..long_period {
201-
long_sum += data.close[data.index - i];
202-
}
203-
204-
let short_sma = short_sum / short_period as f64;
205-
let long_sma = long_sum / long_period as f64;
206-
207-
let position = ctx.position();
208-
209-
if short_sma > long_sma && position <= 0.0 {
210-
ctx.entry_qty(1.0);
211-
} else if short_sma < long_sma && position >= 0.0 {
212-
ctx.entry_qty(-1.0);
213-
}
214-
}),
140+
"SMA Crossover (10/30) - No Funding".to_string(),
215141
initial_capital,
216142
commission_no_funding,
217143
);
218144

219-
backtest_no_funding.calculate();
145+
backtest_no_funding.calculate_with_funding()?;
220146

221-
let stats_no_funding = backtest_no_funding.stats();
147+
let report_no_funding = backtest_no_funding.enhanced_report()?;
222148

223149
println!("\nComparison Results (Without Funding):");
224150
println!("-----------------------------------");
225-
println!("Net Profit: ${:.2} ({:.2}%)",
226-
stats_no_funding.net_profit,
227-
stats_no_funding.net_profit_pct * 100.0);
151+
println!("Total Return: {:.2}%", report_no_funding.total_return * 100.0);
152+
println!("Final Equity: ${:.2}", report_no_funding.final_equity);
228153

229154
println!("\nFunding Impact on Performance:");
230155
println!("----------------------------");
231-
println!("Net Profit Difference: ${:.2}",
232-
stats.net_profit - stats_no_funding.net_profit);
156+
println!("Return Difference: {:.2}%",
157+
(report.total_return - report_no_funding.total_return) * 100.0);
158+
println!("Equity Difference: ${:.2}",
159+
report.final_equity - report_no_funding.final_equity);
233160
println!("Performance Impact: {:.2}%",
234-
((stats.net_profit / stats_no_funding.net_profit) - 1.0) * 100.0);
161+
((report.total_return / report_no_funding.total_return) - 1.0) * 100.0);
162+
163+
// Print detailed funding report
164+
println!("\nDetailed Funding Analysis:");
165+
println!("-------------------------");
166+
let funding_report = backtest.funding_report()?;
167+
println!("Total Funding Received: ${:.2}", funding_report.total_funding_received);
168+
println!("Total Funding Paid: ${:.2}", funding_report.total_funding_paid);
169+
println!("Net Funding PnL: ${:.2}", funding_report.net_funding_pnl);
170+
println!("Payment Count: {}", funding_report.payment_count);
171+
println!("Average Rate: {:.4}%", funding_report.average_rate * 100.0);
235172

236173
println!("\nExample completed successfully!");
237174

0 commit comments

Comments
 (0)