use std::env;
use dotenvy::dotenv;
use quantoxide::{
Database,
error::Result,
models::PercentageCapped,
sync::PriceHistoryState,
trade::{BacktestConfig, BacktestEngine},
tui::{BacktestTui, TuiConfig},
};
#[path = "operators/mod.rs"]
mod operators;
#[path = "util/mod.rs"]
mod util;
use operators::cross_carry::{CrossCarryOperator, CrossCarryOperatorConfig};
use util::input;
const DEFAULT_START_BALANCE_SATS: u64 = 10_000_000;
const DEFAULT_HEDGE_PERC: f64 = 100.0;
#[tokio::main]
async fn main() -> Result<()> {
dotenv().ok();
let db_url = env::var("DATABASE_URL").map_err(|_| "`DATABASE_URL` is not set")?;
println!("Initializing database...");
let db = Database::new(&db_url).await?;
println!("Database ready. Evaluating `PriceHistoryState`...");
let price_history_state = PriceHistoryState::evaluate(&db).await?;
println!("\n{price_history_state}\n");
if price_history_state.bound_end().is_none() {
println!("Some price history must be available in the local database to run the backtest.");
println!("Run a synchronization example, in backfill mode, to fetch historical data.");
return Ok(());
}
println!("Please provide the cross-margin carry trade backtest parameters.\n");
let start_time = input::prompt_date("Start date (YYYY-MM-DD): ")?;
let start_balance = input::prompt_balance(
&format!("Start balance (sats, default: {DEFAULT_START_BALANCE_SATS}): "),
DEFAULT_START_BALANCE_SATS,
)?;
let hedge_perc = input::prompt_percentage_capped(
&format!("Hedge percentage (default: {DEFAULT_HEDGE_PERC}): "),
PercentageCapped::bounded(DEFAULT_HEDGE_PERC),
)?;
let end_time = input::prompt_date("End date (YYYY-MM-DD): ")?;
if start_balance == 0 {
return Err("start balance must be greater than zero".into());
}
println!("\nBacktest Cross-Margin Carry Trade TUI Configuration:");
println!("Start date: {}", start_time.format("%Y-%m-%d %H:%M %Z"));
println!("Start balance: {} sats", start_balance);
println!("Hedge percentage: {:.2}%", hedge_perc.as_f64());
println!("End date: {}\n", end_time.format("%Y-%m-%d %H:%M %Z"));
println!("Initializing `BacktestEngine`...");
let operator = CrossCarryOperator::boxed(CrossCarryOperatorConfig::default(), hedge_perc);
let backtest_engine = BacktestEngine::with_raw_operator(
BacktestConfig::default(),
db,
operator,
start_time,
start_balance,
end_time,
)
.await?;
println!("Initialization OK. Launching `BacktestTui`...");
let backtest_tui = BacktestTui::launch(TuiConfig::default(), None).await?;
backtest_tui.couple(backtest_engine).await?;
let final_status = backtest_tui.until_stopped().await;
println!("`BacktestTui` status: {final_status}");
Ok(())
}