quantoxide 0.5.4

Rust framework for developing, backtesting, and deploying Bitcoin futures trading strategies.
Documentation
//! Example demonstrating how to run the backtest process with a raw operator, using its TUI
//! abstraction.

use std::env;

use dotenvy::dotenv;

use quantoxide::{
    Database,
    error::Result,
    sync::PriceHistoryState,
    trade::{BacktestConfig, BacktestEngine},
    tui::{BacktestTui, TuiConfig},
};

#[path = "operators/mod.rs"]
mod operators;
#[path = "util/mod.rs"]
mod util;

use operators::raw::RawOperatorTemplate;
use util::input;

#[tokio::main]
async fn main() -> Result<()> {
    dotenv().ok();

    let pg_url = env::var("POSTGRES_DB_URL").expect("POSTGRES_DB_URL must be set");

    println!("Initializing database...");

    let db = Database::new(&pg_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 backtest parameters.");
    println!(
        "Note: There must be data available for the lookback period, if any, of the raw operator used.\n"
    );

    let start_time = input::prompt_date("Start date (YYYY-MM-DD): ")?;

    let start_balance =
        input::prompt_balance("Start balance (sats, default: 10000000): ", 10_000_000)?;

    let end_time = input::prompt_date("End date (YYYY-MM-DD): ")?;

    println!("\nBacktest Configuration:");
    println!("Start date: {}", start_time.format("%Y-%m-%d %H:%M %Z"));
    println!("Start balance: {}", start_balance);
    println!("End date: {}\n", end_time.format("%Y-%m-%d %H:%M %Z"));

    println!("Launching `BacktestTui`...");

    let backtest_tui = BacktestTui::launch(TuiConfig::default(), None).await?;

    // Direct `stdout`/`stderr` outputs will corrupt the TUI. Use `backtest_tui.log()` instead
    backtest_tui
        .log("Initializing  `BacktestEngine`...".into())
        .await?;

    // In general, no TUI logger should be provided to Trade Operators in backtests. Since many
    // iterations will be processed per second, excessive per-iteration logging would create a
    // severe performance bottleneck.

    let operator = RawOperatorTemplate::new();

    let backtest_engine = BacktestEngine::with_raw_operator(
        BacktestConfig::default(),
        db,
        operator,
        start_time,
        start_balance,
        end_time,
    )
    .await?;

    backtest_tui
        .log("Initialization OK. Coupling `BacktestEngine`...".into())
        .await?;

    backtest_tui.couple(backtest_engine).await?;

    let final_status = backtest_tui.until_stopped().await;
    println!("`BacktestTui` status: {final_status}");

    Ok(())
}