use const_decimal::Decimal;
use crate::prelude::*;
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
pub struct NoUserOrderId;
impl std::fmt::Display for NoUserOrderId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "NoUserId")
}
}
#[inline(always)]
#[mutants::skip]
pub(crate) fn min<T>(v0: T, v1: T) -> T
where
T: PartialOrd,
{
if v0 < v1 { v0 } else { v1 }
}
#[inline(always)]
#[mutants::skip]
pub(crate) fn max<T>(v0: T, v1: T) -> T
where
T: PartialOrd,
{
if v0 > v1 { v0 } else { v1 }
}
#[inline(always)]
pub fn decimal_from_f64<I: Mon<D>, const D: u8>(val: f64) -> Option<Decimal<I, D>> {
let scaling_factor = 10_f64.powi(D as i32);
let scaled: f64 = (val * scaling_factor).round();
Decimal::try_from_scaled(I::from(scaled as i64)?, D)
}
#[inline(always)]
pub fn scale<F: num::Float>(from_min: F, from_max: F, to_min: F, to_max: F, value: F) -> F {
assert2::debug_assert!(from_min <= from_max);
assert2::debug_assert!(to_min <= to_max);
to_min + ((value - from_min) * (to_max - to_min)) / (from_max - from_min)
}
#[inline(always)]
pub(crate) fn order_margin<I, const D: u8, BaseOrQuote>(
bids_notional: BaseOrQuote::PairedCurrency,
asks_notional: BaseOrQuote::PairedCurrency,
init_margin_req: Decimal<I, D>,
position: &Position<I, D, BaseOrQuote>,
) -> BaseOrQuote::PairedCurrency
where
I: Mon<D>,
BaseOrQuote: Currency<I, D>,
BaseOrQuote::PairedCurrency: MarginCurrency<I, D>,
{
assert2::debug_assert!(init_margin_req > Decimal::zero());
assert2::debug_assert!(init_margin_req <= Decimal::one());
use std::cmp::Ordering::*;
match position.quantity().cmp(&Zero::zero()) {
Equal => max(bids_notional, asks_notional) * init_margin_req,
Greater => max(bids_notional, asks_notional - position.notional()) * init_margin_req,
Less => max(bids_notional - position.notional(), asks_notional) * init_margin_req,
}
}
#[cfg(test)]
pub(crate) mod tests {
use const_decimal::Decimal;
use proptest::prelude::*;
use super::*;
proptest! {
#[test]
fn scale_proptest(a in 0..100) {
assert_eq!(scale(0.0, 1.0, 0.0, 100.0, a as f64), a as f64 * 100.0);
assert_eq!(scale(0.0, 100.0, 0.0, 1.0, a as f64), a as f64 / 100.0);
}
}
#[test]
fn scale_test() {
assert_eq!(scale(-1.0, 1.0, 0.0, 1.0, 0.5), 0.75);
assert_eq!(scale(-1.0, 1.0, 0.0, 1.0, -0.5), 0.25);
assert_eq!(scale(-1.0, 1.0, -1.0, 1.0, 0.5), 0.5);
}
#[test]
fn test_convert_decimals() {
assert_eq!(
Decimal::<i32, 0>::try_from_scaled(10, 1)
.expect("can convert")
.0,
1
);
}
#[test]
fn test_decimal_from_f64() {
assert_eq!(
decimal_from_f64(3.0).unwrap(),
Decimal::<i64, 5>::try_from_scaled(3, 0).unwrap()
);
assert_eq!(
decimal_from_f64(3.1).unwrap(),
Decimal::<i64, 5>::try_from_scaled(31, 1).unwrap()
);
assert_eq!(
decimal_from_f64(3.14).unwrap(),
Decimal::<i64, 5>::try_from_scaled(314, 2).unwrap()
);
assert_eq!(
decimal_from_f64(3.141).unwrap(),
Decimal::<i64, 5>::try_from_scaled(3141, 3).unwrap()
);
assert_eq!(
decimal_from_f64(3.1415).unwrap(),
Decimal::<i64, 5>::try_from_scaled(31415, 4).unwrap()
);
assert_eq!(
decimal_from_f64(3.14159).unwrap(),
Decimal::<i64, 5>::try_from_scaled(314159, 5).unwrap()
);
}
#[test]
fn no_user_order_id_display() {
assert_eq!(&NoUserOrderId.to_string(), "NoUserId");
}
}