tradingkit 0.1.0

Exchange-agnostic trading library for equities and crypto
Documentation
//! Shared trading domain models used across exchange implementations.

use serde::{Deserialize, Serialize};
use serde_json::Value;

/// Side of an order.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum OrderSide {
    Buy,
    Sell,
}

/// Order execution type.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum OrderType {
    Market,
    Limit,
    Stop,
    StopLimit,
    TrailingStop,
}

/// Time-in-force policy.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum TimeInForce {
    Day,
    Gtc,
    Ioc,
    Fok,
    Opg,
    Cls,
}

/// Exchange asset class.
///
/// # Examples
///
/// ```rust
/// use tradingkit::model::AssetClass;
///
/// let asset_class = AssetClass::UsOption;
/// assert!(matches!(asset_class, AssetClass::UsOption));
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AssetClass {
    UsEquity,
    UsOption,
    Crypto,
    #[serde(other)]
    Unknown,
}

/// Exchange order class.
///
/// # Examples
///
/// ```rust
/// use tradingkit::model::OrderClass;
///
/// let order_class = OrderClass::Mleg;
/// assert!(matches!(order_class, OrderClass::Mleg));
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum OrderClass {
    Simple,
    Bracket,
    Oco,
    Oto,
    Mleg,
    #[serde(other)]
    Unknown,
}

/// Position intent for option orders and option legs.
///
/// # Examples
///
/// ```rust
/// use tradingkit::model::PositionIntent;
///
/// let intent = PositionIntent::BuyToOpen;
/// assert!(matches!(intent, PositionIntent::BuyToOpen));
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PositionIntent {
    BuyToOpen,
    BuyToClose,
    SellToOpen,
    SellToClose,
}

/// Order lifecycle state returned by an exchange.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum OrderStatus {
    New,
    PartiallyFilled,
    Filled,
    DoneForDay,
    Canceled,
    Expired,
    Replaced,
    PendingCancel,
    PendingReplace,
    Accepted,
    PendingNew,
    AcceptedForBidding,
    Stopped,
    Rejected,
    Suspended,
    Calculated,
    #[serde(other)]
    Unknown,
}

/// High-level order query status used for list calls.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum OrderQueryStatus {
    Open,
    Closed,
    All,
}

/// Sort direction for list endpoints.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum SortDirection {
    Asc,
    Desc,
}

/// Canonical request used to submit a new order.
///
/// # Examples
///
/// ```rust
/// use tradingkit::model::{AssetClass, OrderRequest, OrderSide, OrderType, TimeInForce};
///
/// let request = OrderRequest {
///     symbol: Some("AAPL".to_string()),
///     asset_class: Some(AssetClass::UsEquity),
///     qty: Some("1".to_string()),
///     notional: None,
///     side: Some(OrderSide::Buy),
///     order_type: OrderType::Limit,
///     time_in_force: TimeInForce::Day,
///     order_class: Some(tradingkit::model::OrderClass::Simple),
///     limit_price: Some("200".to_string()),
///     stop_price: None,
///     trail_price: None,
///     trail_percent: None,
///     client_order_id: Some("strategy-entry".to_string()),
///     extended_hours: Some(false),
///     position_intent: None,
///     legs: None,
/// };
///
/// assert_eq!(request.symbol.as_deref(), Some("AAPL"));
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct OrderRequest {
    pub symbol: Option<String>,
    pub asset_class: Option<AssetClass>,
    pub qty: Option<String>,
    pub notional: Option<String>,
    pub side: Option<OrderSide>,
    pub order_type: OrderType,
    pub time_in_force: TimeInForce,
    pub order_class: Option<OrderClass>,
    pub limit_price: Option<String>,
    pub stop_price: Option<String>,
    pub trail_price: Option<String>,
    pub trail_percent: Option<String>,
    pub client_order_id: Option<String>,
    pub extended_hours: Option<bool>,
    pub position_intent: Option<PositionIntent>,
    pub legs: Option<Vec<OrderLeg>>,
}

/// Canonical request used to update an existing open order.
///
/// # Examples
///
/// ```rust
/// use tradingkit::model::{ReplaceOrderRequest, TimeInForce};
///
/// let request = ReplaceOrderRequest {
///     qty: Some("2".to_string()),
///     time_in_force: Some(TimeInForce::Gtc),
///     limit_price: Some("210".to_string()),
///     stop_price: None,
///     trail: None,
///     client_order_id: Some("strategy-replace".to_string()),
/// };
///
/// assert_eq!(request.qty.as_deref(), Some("2"));
/// ```
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct ReplaceOrderRequest {
    pub qty: Option<String>,
    pub time_in_force: Option<TimeInForce>,
    pub limit_price: Option<String>,
    pub stop_price: Option<String>,
    pub trail: Option<String>,
    pub client_order_id: Option<String>,
}

/// Canonical request used to filter order listings.
///
/// # Examples
///
/// ```rust
/// use tradingkit::model::{ListOrdersRequest, OrderQueryStatus, OrderSide, SortDirection};
///
/// let request = ListOrdersRequest {
///     status: Some(OrderQueryStatus::Open),
///     limit: Some(50),
///     after: None,
///     until: None,
///     direction: Some(SortDirection::Desc),
///     nested: Some(true),
///     side: Some(OrderSide::Buy),
/// };
///
/// assert_eq!(request.limit, Some(50));
/// ```
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct ListOrdersRequest {
    pub status: Option<OrderQueryStatus>,
    pub limit: Option<u16>,
    pub after: Option<String>,
    pub until: Option<String>,
    pub direction: Option<SortDirection>,
    pub nested: Option<bool>,
    pub side: Option<OrderSide>,
}

/// Canonical description of one option leg in a multi-leg order.
///
/// # Examples
///
/// ```rust
/// use tradingkit::model::{OrderLeg, OrderSide, PositionIntent};
///
/// let leg = OrderLeg {
///     symbol: "AAPL250117C00190000".to_string(),
///     ratio_qty: "1".to_string(),
///     side: OrderSide::Buy,
///     position_intent: Some(PositionIntent::BuyToOpen),
/// };
///
/// assert_eq!(leg.ratio_qty, "1");
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct OrderLeg {
    pub symbol: String,
    pub ratio_qty: String,
    pub side: OrderSide,
    pub position_intent: Option<PositionIntent>,
}

/// Canonical order returned by exchange adapters.
///
/// # Examples
///
/// ```rust
/// use tradingkit::model::{
///     AssetClass, Order, OrderClass, OrderSide, OrderStatus, OrderType, PositionIntent,
///     TimeInForce,
/// };
///
/// let order = Order {
///     id: "order-1".to_string(),
///     client_order_id: Some("client-1".to_string()),
///     symbol: Some("AAPL240119C00190000".to_string()),
///     asset_class: Some(AssetClass::UsOption),
///     qty: Some("1".to_string()),
///     notional: None,
///     filled_qty: Some("0".to_string()),
///     side: Some(OrderSide::Buy),
///     order_type: OrderType::Limit,
///     time_in_force: TimeInForce::Day,
///     status: OrderStatus::New,
///     order_class: Some(OrderClass::Simple),
///     limit_price: Some("2.00".to_string()),
///     stop_price: None,
///     trail_price: None,
///     trail_percent: None,
///     created_at: Some("2026-01-01T00:00:00Z".to_string()),
///     extended_hours: Some(false),
///     position_intent: Some(PositionIntent::BuyToOpen),
///     ratio_qty: None,
///     legs: None,
/// };
///
/// assert_eq!(order.symbol.as_deref(), Some("AAPL240119C00190000"));
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Order {
    pub id: String,
    pub client_order_id: Option<String>,
    pub symbol: Option<String>,
    pub asset_class: Option<AssetClass>,
    pub qty: Option<String>,
    pub notional: Option<String>,
    pub filled_qty: Option<String>,
    pub side: Option<OrderSide>,
    pub order_type: OrderType,
    pub time_in_force: TimeInForce,
    pub status: OrderStatus,
    pub order_class: Option<OrderClass>,
    pub limit_price: Option<String>,
    pub stop_price: Option<String>,
    pub trail_price: Option<String>,
    pub trail_percent: Option<String>,
    pub created_at: Option<String>,
    pub extended_hours: Option<bool>,
    pub position_intent: Option<PositionIntent>,
    pub ratio_qty: Option<String>,
    pub legs: Option<Vec<OrderLeg>>,
}

/// Minimal trading account snapshot shared across exchanges.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TradingAccount {
    pub id: String,
    pub account_number: Option<String>,
    pub status: String,
    pub currency: String,
    pub buying_power: Option<String>,
    pub equity: Option<String>,
}

/// Latest option trade snapshot for one contract.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct OptionTradeSnapshot {
    pub price: Option<f64>,
    pub size: Option<u64>,
    pub timestamp: Option<String>,
}

/// Latest option quote snapshot for one contract.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct OptionQuoteSnapshot {
    pub bid_price: Option<f64>,
    pub bid_size: Option<u64>,
    pub ask_price: Option<f64>,
    pub ask_size: Option<u64>,
    pub timestamp: Option<String>,
}

/// Option greeks snapshot for one contract.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct OptionGreeks {
    pub delta: Option<f64>,
    pub gamma: Option<f64>,
    pub theta: Option<f64>,
    pub vega: Option<f64>,
    pub rho: Option<f64>,
}

/// One option-contract snapshot returned from an option chain query.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct OptionChainSnapshot {
    pub symbol: String,
    pub latest_trade: Option<OptionTradeSnapshot>,
    pub latest_quote: Option<OptionQuoteSnapshot>,
    pub implied_volatility: Option<f64>,
    pub greeks: Option<OptionGreeks>,
}

/// Per-order status returned by bulk cancelation endpoints.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CanceledOrderResponse {
    pub id: String,
    pub status: u16,
    pub body: Option<Value>,
}