use std::time::{SystemTime, UNIX_EPOCH};
use crate::clob::{
error::{ClobError, Result},
types::{TickSize, RoundConfig},
};
pub fn get_current_unix_time_secs() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
}
pub fn get_current_unix_time_millis() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis() as u64
}
pub fn round_to_tick(value: f64, tick_size: TickSize) -> f64 {
let tick = tick_size.as_f64();
(value / tick).round() * tick
}
pub fn round_price(price: f64, tick_size: TickSize) -> f64 {
let rounded = round_to_tick(price, tick_size);
rounded.clamp(0.0, 1.0)
}
pub fn round_size(size: f64, tick_size: TickSize) -> f64 {
round_to_tick(size, tick_size).max(0.0)
}
pub fn calculate_amount(price: f64, size: f64) -> f64 {
price * size
}
pub fn round_amount(amount: f64, tick_size: TickSize) -> f64 {
round_to_tick(amount, tick_size).max(0.0)
}
pub fn create_round_config(tick_size: TickSize) -> RoundConfig {
RoundConfig {
price: tick_size.as_f64(),
size: tick_size.as_f64(),
amount: tick_size.as_f64(),
}
}
pub fn validate_price(price: f64) -> Result<()> {
if price < 0.0 || price > 1.0 {
return Err(ClobError::InvalidOrder(format!(
"Price must be between 0 and 1, got: {}",
price
)));
}
Ok(())
}
pub fn validate_size(size: f64) -> Result<()> {
if size <= 0.0 {
return Err(ClobError::InvalidOrder(format!(
"Size must be positive, got: {}",
size
)));
}
Ok(())
}
pub fn validate_amount(amount: f64) -> Result<()> {
if amount <= 0.0 {
return Err(ClobError::InvalidOrder(format!(
"Amount must be positive, got: {}",
amount
)));
}
Ok(())
}
pub fn to_raw_amount(value: f64) -> String {
let raw = (value * 1_000_000.0).round() as u64;
raw.to_string()
}
pub fn to_raw_amount_decimal(value: rust_decimal::Decimal) -> String {
(value * rust_decimal::Decimal::from(1_000_000))
.round_dp(0)
.to_string()
}
pub fn from_raw_amount(raw: &str) -> Result<f64> {
let amount: u64 = raw.parse().map_err(|e| {
ClobError::InvalidOrder(format!("Invalid raw amount '{}': {}", raw, e))
})?;
Ok(amount as f64 / 1_000_000.0)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_current_unix_time_secs() {
let timestamp = get_current_unix_time_secs();
assert!(timestamp > 1_600_000_000); }
#[test]
fn test_get_current_unix_time_millis() {
let timestamp = get_current_unix_time_millis();
assert!(timestamp > 1_600_000_000_000); }
#[test]
fn test_round_to_tick() {
let rounded = round_to_tick(0.567, TickSize::ZeroPointZeroOne);
assert!((rounded - 0.57).abs() < 0.0001);
}
#[test]
fn test_round_price() {
let rounded = round_price(0.567, TickSize::ZeroPointZeroOne);
assert!((rounded - 0.57).abs() < 0.0001);
let clamped = round_price(1.5, TickSize::ZeroPointZeroOne);
assert_eq!(clamped, 1.0);
let clamped = round_price(-0.5, TickSize::ZeroPointZeroOne);
assert_eq!(clamped, 0.0);
}
#[test]
fn test_round_size() {
let rounded = round_size(123.456, TickSize::ZeroPointOne);
assert_eq!(rounded, 123.5);
let rounded = round_size(-10.0, TickSize::ZeroPointOne);
assert_eq!(rounded, 0.0);
}
#[test]
fn test_calculate_amount() {
let amount = calculate_amount(0.5, 100.0);
assert_eq!(amount, 50.0);
}
#[test]
fn test_round_amount() {
let rounded = round_amount(123.456, TickSize::ZeroPointZeroOne);
assert!((rounded - 123.46).abs() < 0.0001);
}
#[test]
fn test_validate_price() {
assert!(validate_price(0.5).is_ok());
assert!(validate_price(0.0).is_ok());
assert!(validate_price(1.0).is_ok());
assert!(validate_price(-0.1).is_err());
assert!(validate_price(1.1).is_err());
}
#[test]
fn test_validate_size() {
assert!(validate_size(1.0).is_ok());
assert!(validate_size(100.0).is_ok());
assert!(validate_size(0.0).is_err());
assert!(validate_size(-1.0).is_err());
}
#[test]
fn test_validate_amount() {
assert!(validate_amount(1.0).is_ok());
assert!(validate_amount(100.0).is_ok());
assert!(validate_amount(0.0).is_err());
assert!(validate_amount(-1.0).is_err());
}
#[test]
fn test_to_raw_amount() {
assert_eq!(to_raw_amount(1.0), "1000000");
assert_eq!(to_raw_amount(0.5), "500000");
assert_eq!(to_raw_amount(123.456), "123456000");
}
#[test]
fn test_from_raw_amount() {
assert_eq!(from_raw_amount("1000000").unwrap(), 1.0);
assert_eq!(from_raw_amount("500000").unwrap(), 0.5);
assert_eq!(from_raw_amount("123456000").unwrap(), 123.456);
}
#[test]
fn test_round_trip_conversion() {
let original = 123.456;
let raw = to_raw_amount(original);
let converted = from_raw_amount(&raw).unwrap();
assert_eq!(converted, original);
}
#[test]
fn test_create_round_config() {
let config = create_round_config(TickSize::ZeroPointZeroOne);
assert_eq!(config.price, 0.01);
assert_eq!(config.size, 0.01);
assert_eq!(config.amount, 0.01);
}
}