Move 17 library crates into crates/, CLI binary into bin/fxt, consolidate 10 test crates into testing/, split config crate from deployment config files. Root directory reduced from 38+ to ~17 directories. All Cargo.toml paths and build.rs proto refs updated. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
295 lines
8.0 KiB
Rust
295 lines
8.0 KiB
Rust
//! Trading-specific types and enums
|
|
//!
|
|
//! This module contains the canonical definitions for all trading-related
|
|
//! types used across the Foxhunt HFT system. This is the single source
|
|
//! of truth for all trading types.
|
|
|
|
use chrono::{DateTime, Utc};
|
|
use rust_decimal::Decimal;
|
|
use serde::{Deserialize, Serialize};
|
|
use std::fmt;
|
|
|
|
// ELIMINATED: Re-exports removed to force explicit imports
|
|
// REMOVED: TimeInForce duplicate - use canonical definition from common::types
|
|
|
|
// Currency moved to canonical source: common::types::Currency
|
|
|
|
/// Tick type for market data
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
#[cfg_attr(feature = "database", derive(sqlx::Type))]
|
|
#[cfg_attr(
|
|
feature = "database",
|
|
sqlx(type_name = "tick_type", rename_all = "snake_case")
|
|
)]
|
|
pub enum TickType {
|
|
/// Trade tick
|
|
Trade,
|
|
/// Bid price update
|
|
Bid,
|
|
/// Ask price update
|
|
Ask,
|
|
/// Quote update (bid and ask)
|
|
Quote,
|
|
}
|
|
|
|
impl fmt::Display for TickType {
|
|
/// Format the tick type for display
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::Trade => write!(f, "TRADE"),
|
|
Self::Bid => write!(f, "BID"),
|
|
Self::Ask => write!(f, "ASK"),
|
|
Self::Quote => write!(f, "QUOTE"),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Order book action type
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
pub enum BookAction {
|
|
/// Update price level
|
|
Update,
|
|
/// Delete price level
|
|
Delete,
|
|
/// Clear entire book
|
|
Clear,
|
|
}
|
|
|
|
impl fmt::Display for BookAction {
|
|
/// Format the book action for display
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::Update => write!(f, "UPDATE"),
|
|
Self::Delete => write!(f, "DELETE"),
|
|
Self::Clear => write!(f, "CLEAR"),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Market regime classification
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
pub enum MarketRegime {
|
|
/// Normal market conditions
|
|
Normal,
|
|
/// Crisis/stress market conditions
|
|
Crisis,
|
|
/// Trending market (strong directional movement)
|
|
Trending,
|
|
/// Sideways/ranging market (low volatility)
|
|
Sideways,
|
|
/// Bull market (sustained upward trend)
|
|
Bull,
|
|
/// Bear market (sustained downward trend)
|
|
Bear,
|
|
}
|
|
|
|
impl fmt::Display for MarketRegime {
|
|
/// Format the market regime for display
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::Normal => write!(f, "NORMAL"),
|
|
Self::Crisis => write!(f, "CRISIS"),
|
|
Self::Trending => write!(f, "TRENDING"),
|
|
Self::Sideways => write!(f, "SIDEWAYS"),
|
|
Self::Bull => write!(f, "BULL"),
|
|
Self::Bear => write!(f, "BEAR"),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Core Quantity type using fixed-point arithmetic for precise calculations
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
|
pub struct Quantity {
|
|
/// Internal representation using 6 decimal places (scale factor of `1_000_000`)
|
|
value: u64,
|
|
}
|
|
|
|
impl Quantity {
|
|
/// Scale factor for fixed-point arithmetic (6 decimal places)
|
|
pub const SCALE: u64 = 1_000_000;
|
|
|
|
/// Zero quantity
|
|
pub const ZERO: Self = Self { value: 0 };
|
|
|
|
/// Create a new quantity from a floating-point value
|
|
///
|
|
/// # Errors
|
|
/// Returns error if the operation fails
|
|
///
|
|
/// # Errors
|
|
/// Returns error if the value is negative or not finite
|
|
#[allow(clippy::float_arithmetic)]
|
|
pub fn new(value: f64) -> Result<Self, &'static str> {
|
|
if value < 0.0_f64 {
|
|
return Err("Quantity cannot be negative");
|
|
}
|
|
if !value.is_finite() {
|
|
return Err("Quantity must be finite");
|
|
}
|
|
|
|
#[allow(clippy::as_conversions)]
|
|
let scaled = (value * (Self::SCALE as f64)).round() as u64;
|
|
|
|
Ok(Self { value: scaled })
|
|
}
|
|
|
|
/// Create from raw internal value
|
|
pub const fn from_raw(value: u64) -> Self {
|
|
Self { value }
|
|
}
|
|
|
|
/// Get raw internal value
|
|
pub const fn raw(&self) -> u64 {
|
|
self.value
|
|
}
|
|
|
|
/// Convert to floating-point value
|
|
#[allow(clippy::float_arithmetic, clippy::as_conversions)]
|
|
pub fn to_f64(&self) -> f64 {
|
|
(self.value as f64) / (Self::SCALE as f64)
|
|
}
|
|
|
|
/// Convert to decimal
|
|
pub fn to_decimal(&self) -> Decimal {
|
|
Decimal::new(i64::try_from(self.value).unwrap_or(0), 6)
|
|
}
|
|
|
|
/// Add two quantities
|
|
pub const fn add(&self, other: Self) -> Self {
|
|
Self {
|
|
value: self.value.saturating_add(other.value),
|
|
}
|
|
}
|
|
|
|
/// Subtract two quantities
|
|
pub const fn subtract(&self, other: Self) -> Self {
|
|
Self {
|
|
value: self.value.saturating_sub(other.value),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Quantity {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "{:.6}", self.to_f64())
|
|
}
|
|
}
|
|
|
|
impl std::ops::Add for Quantity {
|
|
type Output = Self;
|
|
|
|
fn add(self, other: Self) -> Self::Output {
|
|
Self {
|
|
value: self.value.saturating_add(other.value),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::ops::Sub for Quantity {
|
|
type Output = Self;
|
|
|
|
fn sub(self, other: Self) -> Self::Output {
|
|
Self {
|
|
value: self.value.saturating_sub(other.value),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Order event for tracking order lifecycle
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct OrderEvent {
|
|
/// Unique order identifier
|
|
pub order_id: String,
|
|
/// Trading symbol
|
|
pub symbol: String,
|
|
/// Order type (Market, Limit, etc.)
|
|
pub order_type: OrderType,
|
|
/// Order side (Buy/Sell)
|
|
pub side: OrderSide,
|
|
/// Order quantity
|
|
pub quantity: Quantity,
|
|
/// Order price (None for market orders)
|
|
pub price: Option<Decimal>,
|
|
/// Event timestamp
|
|
pub timestamp: DateTime<Utc>,
|
|
/// Strategy identifier
|
|
pub strategy_id: String,
|
|
/// Type of order event
|
|
pub event_type: OrderEventType,
|
|
/// Previous quantity for modifications
|
|
pub previous_quantity: Option<Quantity>,
|
|
/// Previous price for modifications
|
|
pub previous_price: Option<Decimal>,
|
|
/// Reason for cancellation or modification
|
|
pub reason: Option<String>,
|
|
}
|
|
|
|
/// Types of order events
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
pub enum OrderEventType {
|
|
/// Order was placed
|
|
Placed,
|
|
/// Order was modified
|
|
Modified,
|
|
/// Order was cancelled
|
|
Cancelled,
|
|
/// Order was rejected
|
|
Rejected,
|
|
/// Order expired
|
|
Expired,
|
|
}
|
|
|
|
impl fmt::Display for OrderEventType {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::Placed => write!(f, "PLACED"),
|
|
Self::Modified => write!(f, "MODIFIED"),
|
|
Self::Cancelled => write!(f, "CANCELLED"),
|
|
Self::Rejected => write!(f, "REJECTED"),
|
|
Self::Expired => write!(f, "EXPIRED"),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Order type enumeration
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
pub enum OrderType {
|
|
/// Market order - execute immediately at best available price
|
|
Market,
|
|
/// Limit order - execute only at specified price or better
|
|
Limit,
|
|
/// Stop order - becomes market order when stop price is reached
|
|
Stop,
|
|
/// Stop-limit order - becomes limit order when stop price is reached
|
|
StopLimit,
|
|
}
|
|
|
|
impl fmt::Display for OrderType {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::Market => write!(f, "MARKET"),
|
|
Self::Limit => write!(f, "LIMIT"),
|
|
Self::Stop => write!(f, "STOP"),
|
|
Self::StopLimit => write!(f, "STOP_LIMIT"),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Order side enumeration
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
pub enum OrderSide {
|
|
/// Buy order
|
|
Buy,
|
|
/// Sell order
|
|
Sell,
|
|
}
|
|
|
|
impl fmt::Display for OrderSide {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::Buy => write!(f, "BUY"),
|
|
Self::Sell => write!(f, "SELL"),
|
|
}
|
|
}
|
|
}
|