use clap::Parser;
use eyre::Result;
use simulator_client::{BacktestClient, RangeBound, filter_ranges};
const SIZE_TOLERANCE: f64 = 0.1;
#[derive(Parser, Debug, Clone)]
pub struct RangesArgs {
#[arg(
long,
env = "SIMULATOR_URL",
default_value = "simulator.termina.technology"
)]
pub url: String,
#[arg(long)]
pub after: Option<String>,
#[arg(long)]
pub before: Option<String>,
#[arg(long)]
pub size: Option<u64>,
#[arg(long)]
pub json: bool,
}
pub async fn ranges(args: RangesArgs) -> Result<()> {
let after: Option<RangeBound> = args.after.as_deref().map(str::parse).transpose()?;
let before: Option<RangeBound> = args.before.as_deref().map(str::parse).transpose()?;
let client = BacktestClient::builder()
.url(format!("wss://{}/backtest", args.url))
.build();
let all = client.available_ranges().await?;
let time_bound = filter_ranges(&all, after.as_ref(), before.as_ref());
let matched: Vec<_> = match args.size {
None => time_bound,
Some(target) => time_bound
.into_iter()
.filter(|r| {
r.max_bundle_size.is_some_and(|size| {
let delta = (size as f64 - target as f64).abs();
delta / target as f64 <= SIZE_TOLERANCE
})
})
.collect(),
};
if matched.is_empty() {
if args.json {
println!("[]");
} else {
println!("No available ranges matching the given filters.");
}
return Ok(());
}
if args.json {
println!("{}", serde_json::to_string_pretty(&matched)?);
return Ok(());
}
println!(
"{:<30} {:<30} {:<15} {:<15} Bundle Size",
"Start Time (UTC)", "End Time (UTC)", "Start Slot", "End Slot"
);
println!("{}", "-".repeat(105));
for range in &matched {
let start_time = range.bundle_start_slot_utc.as_deref().unwrap_or("-");
let end_time = range.max_bundle_end_slot_utc.as_deref().unwrap_or("-");
let start_slot = range.bundle_start_slot;
let end_slot = range
.max_bundle_end_slot
.map(|s| s.to_string())
.unwrap_or_else(|| "unbounded".to_string());
let bundle_size = range.max_bundle_size;
println!(
"{start_time:<30} {end_time:<30} {start_slot:<15} {end_slot:<15} {}",
bundle_size
.map(|s| s.to_string())
.unwrap_or_else(|| "unbounded".to_string()),
);
}
Ok(())
}