use crate::asset::AssetSymbol;
use crate::platform::asset_class::AssetClass;
use crate::platform::id::Id;
use crate::platform::order::amount::Amount;
use crate::platform::order::class::OrderClass;
use crate::platform::order::order_type::OrderType;
use crate::platform::order::side::OrderSide;
use crate::platform::order::status::Status;
use crate::platform::order::time_in_force::TimeInForce;
use chrono::{DateTime, TimeZone, Utc};
use num_decimal::Num;
use std::fmt::{Display, Formatter};
pub mod amount;
pub mod class;
pub mod order_type;
pub mod side;
pub mod status;
pub mod time_in_force;
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Order {
pub id: Id,
pub status: Status,
pub created_at: DateTime<Utc>,
pub updated_at: Option<DateTime<Utc>>,
pub submitted_at: Option<DateTime<Utc>>,
pub filled_at: Option<DateTime<Utc>>,
pub expired_at: Option<DateTime<Utc>>,
pub canceled_at: Option<DateTime<Utc>>,
pub asset_class: AssetClass,
pub symbol: AssetSymbol,
pub amount: Amount,
pub filled_quantity: Num,
pub order_type: OrderType,
pub class: OrderClass,
pub side: OrderSide,
pub time_in_force: TimeInForce,
pub limit_price: Option<Num>,
pub stop_price: Option<Num>,
pub trail_price: Option<Num>,
pub trail_percent: Option<Num>,
pub average_fill_price: Option<Num>,
pub extended_hours: bool,
}
impl Order {
pub fn estimated_value(&self, ask_price: f64) -> f64 {
match &self.amount {
Amount::Quantity(value) => value * ask_price,
Amount::Notional(value) => *value,
}
}
pub fn display_with_time_zone<TZ: TimeZone>(&self, time_zone: &TZ) -> String
where
TZ::Offset: Display,
{
let submitted_at_string = self.fmt_submitted_at(time_zone);
format!(
"{}{} {} of {}",
submitted_at_string, self.side, self.amount, self.symbol
)
}
fn fmt_submitted_at<TZ: TimeZone>(&self, time_zone: &TZ) -> String
where
TZ::Offset: Display,
{
self.submitted_at
.map(|t| t.with_timezone(time_zone))
.map(|t| t.format("%Y-%m-%d %H:%M").to_string())
.map(|s| s + " ")
.unwrap_or("".to_string())
}
#[cfg(test)]
pub fn fixture(symbol: AssetSymbol) -> Self {
Self {
amount: Amount::Quantity(1.0),
symbol,
..Default::default()
}
}
}
impl Display for Order {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let submitted_at_string = self.fmt_submitted_at(&Utc);
write!(
f,
"{}{} {} of {}",
submitted_at_string, self.side, self.amount, self.symbol
)
}
}
#[cfg(test)]
mod test {
use crate::asset::AssetSymbol;
use crate::date::DateTimeFixture;
use crate::platform::order::amount::Amount;
use crate::platform::order::side::OrderSide;
use crate::platform::order::Order;
#[test]
fn estimated_value_quantity() {
let order = Order {
amount: Amount::Quantity(10.0),
..Default::default()
};
let estimated_value = order.estimated_value(100.0);
assert_eq!(estimated_value, 1000.0)
}
#[test]
fn estimated_value_notational() {
let order = Order {
amount: Amount::Notional(10.0),
..Default::default()
};
let estimated_value = order.estimated_value(100.0);
assert_eq!(estimated_value, 10.0)
}
#[test]
fn display_no_submission_time() {
let order = Order {
amount: Amount::Quantity(10.0),
side: OrderSide::Buy,
symbol: AssetSymbol::new("VTI"),
..Default::default()
};
let display = order.to_string();
assert_eq!(display, "buy 10.00 units of VTI")
}
#[test]
fn display_with_time_zone() {
let order = Order {
amount: Amount::Quantity(10.0),
side: OrderSide::Buy,
symbol: AssetSymbol::new("VTI"),
submitted_at: Some(DateTimeFixture::utc()),
..Default::default()
};
let display = order.display_with_time_zone(&chrono::Utc);
assert_eq!(display, "2023-12-04 08:00 buy 10.00 units of VTI")
}
#[test]
fn display_with_submission_time() {
let order = Order {
amount: Amount::Quantity(10.0),
side: OrderSide::Buy,
symbol: AssetSymbol::new("VTI"),
submitted_at: Some(DateTimeFixture::utc()),
..Default::default()
};
let display = order.to_string();
assert_eq!(display, "2023-12-04 08:00 buy 10.00 units of VTI")
}
}