use super::account::AccountMode;
use super::contract::AssetClass;
use super::identifiers::{AccountId, AuditEventId, ContractId, LocalUserId};
use super::money::{CurrencyCode, Money, Quantity};
use super::order::OrderSide;
use rust_decimal::Decimal;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use uuid::Uuid;
#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct OrderIntentId(Uuid);
#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct ValidatedOrderId(Uuid);
#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct OrderPreviewId(Uuid);
macro_rules! impl_preview_uuid_id {
($type_name:ident) => {
impl $type_name {
#[must_use]
pub fn new() -> Self {
Self(Uuid::now_v7())
}
#[must_use]
pub const fn as_uuid(&self) -> Uuid {
self.0
}
pub fn parse(value: &str) -> Result<Self, super::error::GatewayError> {
let uuid = Uuid::parse_str(value).map_err(|_| {
super::error::GatewayError::new(
super::error::ErrorCode::OrderValidationFailed,
concat!(stringify!($type_name), " must be a valid UUID"),
false,
Some("Use an identifier returned by the gateway".to_string()),
)
})?;
Ok(Self(uuid))
}
}
impl Default for $type_name {
fn default() -> Self {
Self::new()
}
}
};
}
impl_preview_uuid_id!(OrderIntentId);
impl_preview_uuid_id!(ValidatedOrderId);
impl_preview_uuid_id!(OrderPreviewId);
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case", tag = "kind")]
pub enum OrderContractInput {
ContractId {
contract_id: ContractId,
},
Query {
symbol: String,
asset_class: AssetClass,
currency: CurrencyCode,
exchange: Option<String>,
},
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum PreviewOrderType {
Limit,
Market,
Stop,
StopLimit,
TrailingStop,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum TimeInForce {
Day,
GoodTillCancelled,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct OrderIntent {
pub intent_id: OrderIntentId,
pub account_id: AccountId,
pub account_mode: AccountMode,
pub contract: OrderContractInput,
pub side: OrderSide,
pub quantity: Quantity,
pub order_type: PreviewOrderType,
pub limit_price: Option<Money>,
pub stop_price: Option<Money>,
pub trailing_amount: Option<Money>,
#[schemars(with = "Option<String>")]
pub trailing_percent: Option<Decimal>,
pub time_in_force: TimeInForce,
pub rationale: Option<String>,
pub created_by: LocalUserId,
#[serde(with = "time::serde::rfc3339")]
#[schemars(with = "String")]
pub created_at: OffsetDateTime,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct ValidatedOrder {
pub validated_order_id: ValidatedOrderId,
pub preview_id: OrderPreviewId,
pub intent_id: OrderIntentId,
pub account_id: AccountId,
pub contract_id: ContractId,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub symbol: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub asset_class: Option<AssetClass>,
pub side: OrderSide,
pub quantity: Quantity,
pub order_type: PreviewOrderType,
pub limit_price: Option<Money>,
pub stop_price: Option<Money>,
pub trailing_amount: Option<Money>,
#[schemars(with = "Option<String>")]
pub trailing_percent: Option<Decimal>,
pub time_in_force: TimeInForce,
#[serde(with = "time::serde::rfc3339")]
#[schemars(with = "String")]
pub expires_at: OffsetDateTime,
pub warnings: Vec<String>,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct OrderPreview {
pub preview_id: OrderPreviewId,
pub validated_order_id: ValidatedOrderId,
pub estimated_cost: Money,
pub estimated_commission: Option<Money>,
pub margin_impact: Option<Money>,
pub warnings: Vec<String>,
#[serde(with = "time::serde::rfc3339")]
#[schemars(with = "String")]
pub expires_at: OffsetDateTime,
pub audit_event_id: AuditEventId,
}