Files
foxhunt/crates/common/src/trading.rs
jgrusewski 9c3d741a08 refactor: restructure repo — crates/, bin/, testing/ layout
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>
2026-02-25 11:56:00 +01:00

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"),
}
}
}