use crate::data::HyperliquidData;
use crate::backtest::{FundingPayment, HyperliquidCommission};
use chrono::{DateTime, FixedOffset, TimeZone, Duration};
use hyperliquid_rust_sdk::{CandlesSnapshotResponse, FundingHistoryResponse};
use std::collections::HashMap;
pub fn generate_mock_data(
symbol: &str,
hours: usize,
with_funding: bool,
with_gaps: bool,
) -> HyperliquidData {
let mut datetime = Vec::new();
let mut open = Vec::new();
let mut high = Vec::new();
let mut low = Vec::new();
let mut close = Vec::new();
let mut volume = Vec::new();
let mut funding_rates = Vec::new();
let base_timestamp = 1640995200;
for i in 0..hours {
if with_gaps && i % 10 == 0 {
continue;
}
let timestamp = FixedOffset::east_opt(0).unwrap()
.timestamp_opt(base_timestamp + i as i64 * 3600, 0).unwrap();
datetime.push(timestamp);
let trend = (i as f64) * 0.01;
let cycle = ((i as f64) * 0.1).sin() * 5.0;
let price = 100.0 + trend + cycle;
open.push(price - 0.5);
high.push(price + 1.0);
low.push(price - 1.0);
close.push(price);
volume.push(1000.0 + (i as f64 % 24.0) * 100.0);
if with_funding {
if timestamp.hour() % 8 == 0 {
let funding_cycle = ((i as f64) * 0.05).sin() * 0.0002;
funding_rates.push(funding_cycle);
} else {
funding_rates.push(f64::NAN);
}
} else {
funding_rates.push(f64::NAN);
}
}
HyperliquidData {
symbol: symbol.to_string(),
datetime,
open,
high,
low,
close,
volume,
funding_rates,
}
}
pub fn generate_mock_candles_response(
hours: usize,
with_gaps: bool,
) -> Vec<CandlesSnapshotResponse> {
let mut candles = Vec::new();
let base_timestamp = 1640995200;
for i in 0..hours {
if with_gaps && i % 10 == 0 {
continue;
}
let time_open = base_timestamp + i as u64 * 3600;
let time_close = time_open + 3600;
let trend = (i as f64) * 0.01;
let cycle = ((i as f64) * 0.1).sin() * 5.0;
let price = 100.0 + trend + cycle;
candles.push(CandlesSnapshotResponse {
time_open,
time_close,
open: format!("{:.2}", price - 0.5),
high: format!("{:.2}", price + 1.0),
low: format!("{:.2}", price - 1.0),
close: format!("{:.2}", price),
vlm: format!("{:.2}", 1000.0 + (i as f64 % 24.0) * 100.0),
});
}
candles
}
pub fn generate_mock_funding_history(
coin: &str,
hours: usize,
with_gaps: bool,
) -> Vec<FundingHistoryResponse> {
let mut funding_history = Vec::new();
let base_timestamp = 1640995200;
for i in 0..hours {
if i % 8 != 0 {
continue;
}
if with_gaps && i % 24 == 0 {
continue;
}
let timestamp = base_timestamp + i as u64 * 3600;
let funding_cycle = ((i as f64) * 0.05).sin() * 0.0002;
funding_history.push(FundingHistoryResponse {
coin: coin.to_string(),
funding_rate: format!("{:.8}", funding_cycle),
premium: format!("{:.8}", funding_cycle * 3.0), time: timestamp,
});
}
funding_history
}
pub fn generate_mock_funding_payments(
hours: usize,
position_size: f64,
) -> Vec<FundingPayment> {
let mut payments = Vec::new();
let base_timestamp = 1640995200;
for i in 0..hours {
if i % 8 != 0 {
continue;
}
let timestamp = FixedOffset::east_opt(0).unwrap()
.timestamp_opt(base_timestamp + i as i64 * 3600, 0).unwrap();
let funding_rate = ((i as f64) * 0.05).sin() * 0.0002;
let price = 100.0 + (i as f64) * 0.01;
let payment_amount = -position_size * funding_rate * price;
payments.push(FundingPayment {
timestamp,
funding_rate,
position_size,
price,
payment_amount,
});
}
payments
}
pub fn generate_position_sequence(
length: usize,
pattern: &str,
) -> Vec<f64> {
match pattern {
"constant_long" => vec![1.0; length],
"constant_short" => vec![-1.0; length],
"alternating" => (0..length).map(|i| if i % 2 == 0 { 1.0 } else { -1.0 }).collect(),
"increasing" => (0..length).map(|i| (i as f64 % 5.0) * 0.2).collect(),
"decreasing" => (0..length).map(|i| 1.0 - (i as f64 % 5.0) * 0.2).collect(),
"zero" => vec![0.0; length],
_ => vec![1.0; length], }
}
pub fn generate_datetime_sequence(
start_timestamp: i64,
count: usize,
interval_seconds: i64,
) -> Vec<DateTime<FixedOffset>> {
let tz = FixedOffset::east_opt(0).unwrap();
(0..count)
.map(|i| tz.timestamp_opt(start_timestamp + i as i64 * interval_seconds, 0).unwrap())
.collect()
}
pub fn generate_invalid_data() -> HashMap<&'static str, HyperliquidData> {
let mut invalid_data = HashMap::new();
invalid_data.insert("empty", HyperliquidData {
symbol: "BTC".to_string(),
datetime: Vec::new(),
open: Vec::new(),
high: Vec::new(),
low: Vec::new(),
close: Vec::new(),
volume: Vec::new(),
funding_rates: Vec::new(),
});
let mut mismatched = generate_mock_data("BTC", 24, false, false);
mismatched.open.pop(); invalid_data.insert("mismatched_lengths", mismatched);
let mut invalid_high_low = generate_mock_data("BTC", 24, false, false);
let temp = invalid_high_low.high[0];
invalid_high_low.high[0] = invalid_high_low.low[0];
invalid_high_low.low[0] = temp;
invalid_data.insert("invalid_high_low", invalid_high_low);
let mut non_chronological = generate_mock_data("BTC", 24, false, false);
let temp = non_chronological.datetime[5];
non_chronological.datetime[5] = non_chronological.datetime[10];
non_chronological.datetime[10] = temp;
invalid_data.insert("non_chronological", non_chronological);
invalid_data
}
pub fn generate_commission_configs() -> Vec<HyperliquidCommission> {
vec![
HyperliquidCommission::default(),
HyperliquidCommission::new(0.0001, 0.0003, true),
HyperliquidCommission::new(0.0, 0.0, false),
HyperliquidCommission::new(0.001, 0.002, true),
]
}