1- use chrono:: { Duration , Utc } ;
1+ use chrono:: Utc ;
22use 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 ! ( "\n Backtest 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 ! ( "\n Funding 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 ! ( "\n Trade 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 ! ( "\n Commission 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 ! ( "\n Funding 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 ! ( "\n Exporting 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 ! ( "\n Running 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 ! ( "\n Comparison 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 ! ( "\n Funding 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 ! ( "\n Detailed 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 ! ( "\n Example completed successfully!" ) ;
237174
0 commit comments