Coverage Report

Created: 2025-10-06 12:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/home/jgrusewski/Work/foxhunt/common/src/trading.rs
Line
Count
Source
1
//! Trading-specific types and enums
2
//!
3
//! This module contains the canonical definitions for all trading-related
4
//! types used across the Foxhunt HFT system. This is the single source
5
//! of truth for all trading types.
6
7
use chrono::{DateTime, Utc};
8
use rust_decimal::Decimal;
9
use serde::{Deserialize, Serialize};
10
use std::fmt;
11
12
// ELIMINATED: Re-exports removed to force explicit imports
13
// REMOVED: TimeInForce duplicate - use canonical definition from common::types
14
15
// Currency moved to canonical source: common::types::Currency
16
17
/// Tick type for market data
18
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
19
#[cfg_attr(feature = "database", derive(sqlx::Type))]
20
#[cfg_attr(
21
    feature = "database",
22
    sqlx(type_name = "tick_type", rename_all = "snake_case")
23
)]
24
pub enum TickType {
25
    /// Trade tick
26
    Trade,
27
    /// Bid price update
28
    Bid,
29
    /// Ask price update
30
    Ask,
31
    /// Quote update (bid and ask)
32
    Quote,
33
}
34
35
impl fmt::Display for TickType {
36
    /// Format the tick type for display
37
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38
0
        match self {
39
0
            Self::Trade => write!(f, "TRADE"),
40
0
            Self::Bid => write!(f, "BID"),
41
0
            Self::Ask => write!(f, "ASK"),
42
0
            Self::Quote => write!(f, "QUOTE"),
43
        }
44
0
    }
45
}
46
47
/// Order book action type
48
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
49
pub enum BookAction {
50
    /// Update price level
51
    Update,
52
    /// Delete price level
53
    Delete,
54
    /// Clear entire book
55
    Clear,
56
}
57
58
impl fmt::Display for BookAction {
59
    /// Format the book action for display
60
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61
0
        match self {
62
0
            Self::Update => write!(f, "UPDATE"),
63
0
            Self::Delete => write!(f, "DELETE"),
64
0
            Self::Clear => write!(f, "CLEAR"),
65
        }
66
0
    }
67
}
68
69
/// Market regime classification
70
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
71
pub enum MarketRegime {
72
    /// Normal market conditions
73
    Normal,
74
    /// Crisis/stress market conditions
75
    Crisis,
76
    /// Trending market (strong directional movement)
77
    Trending,
78
    /// Sideways/ranging market (low volatility)
79
    Sideways,
80
    /// Bull market (sustained upward trend)
81
    Bull,
82
    /// Bear market (sustained downward trend)
83
    Bear,
84
}
85
86
impl fmt::Display for MarketRegime {
87
    /// Format the market regime for display
88
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89
0
        match self {
90
0
            Self::Normal => write!(f, "NORMAL"),
91
0
            Self::Crisis => write!(f, "CRISIS"),
92
0
            Self::Trending => write!(f, "TRENDING"),
93
0
            Self::Sideways => write!(f, "SIDEWAYS"),
94
0
            Self::Bull => write!(f, "BULL"),
95
0
            Self::Bear => write!(f, "BEAR"),
96
        }
97
0
    }
98
}
99
100
/// Core Quantity type using fixed-point arithmetic for precise calculations
101
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
102
pub struct Quantity {
103
    /// Internal representation using 6 decimal places (scale factor of 1,000,000)
104
    value: u64,
105
}
106
107
impl Quantity {
108
    /// Scale factor for fixed-point arithmetic (6 decimal places)
109
    pub const SCALE: u64 = 1_000_000;
110
111
    /// Zero quantity
112
    pub const ZERO: Self = Self { value: 0 };
113
114
    /// Create a new quantity from a floating-point value
115
0
    pub fn new(value: f64) -> Result<Self, &'static str> {
116
0
        if value < 0.0 {
117
0
            return Err("Quantity cannot be negative");
118
0
        }
119
0
        if !value.is_finite() {
120
0
            return Err("Quantity must be finite");
121
0
        }
122
123
0
        let scaled = (value * Self::SCALE as f64).round() as u64;
124
0
        Ok(Self { value: scaled })
125
0
    }
126
127
    /// Create from raw internal value
128
0
    pub const fn from_raw(value: u64) -> Self {
129
0
        Self { value }
130
0
    }
131
132
    /// Get raw internal value
133
0
    pub const fn raw(&self) -> u64 {
134
0
        self.value
135
0
    }
136
137
    /// Convert to floating-point value
138
0
    pub fn to_f64(&self) -> f64 {
139
0
        self.value as f64 / Self::SCALE as f64
140
0
    }
141
142
    /// Convert to decimal
143
0
    pub fn to_decimal(&self) -> Decimal {
144
0
        Decimal::new(self.value as i64, 6)
145
0
    }
146
147
    /// Add two quantities
148
0
    pub fn add(&self, other: Self) -> Self {
149
0
        Self {
150
0
            value: self.value + other.value,
151
0
        }
152
0
    }
153
154
    /// Subtract two quantities
155
0
    pub fn subtract(&self, other: Self) -> Self {
156
0
        Self {
157
0
            value: self.value.saturating_sub(other.value),
158
0
        }
159
0
    }
160
}
161
162
impl fmt::Display for Quantity {
163
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164
0
        write!(f, "{:.6}", self.to_f64())
165
0
    }
166
}
167
168
impl std::ops::Add for Quantity {
169
    type Output = Self;
170
171
0
    fn add(self, other: Self) -> Self::Output {
172
0
        Self {
173
0
            value: self.value + other.value,
174
0
        }
175
0
    }
176
}
177
178
impl std::ops::Sub for Quantity {
179
    type Output = Self;
180
181
0
    fn sub(self, other: Self) -> Self::Output {
182
0
        Self {
183
0
            value: self.value.saturating_sub(other.value),
184
0
        }
185
0
    }
186
}
187
188
/// Order event for tracking order lifecycle
189
#[derive(Debug, Clone, Serialize, Deserialize)]
190
pub struct OrderEvent {
191
    /// Unique order identifier
192
    pub order_id: String,
193
    /// Trading symbol
194
    pub symbol: String,
195
    /// Order type (Market, Limit, etc.)
196
    pub order_type: OrderType,
197
    /// Order side (Buy/Sell)
198
    pub side: OrderSide,
199
    /// Order quantity
200
    pub quantity: Quantity,
201
    /// Order price (None for market orders)
202
    pub price: Option<Decimal>,
203
    /// Event timestamp
204
    pub timestamp: DateTime<Utc>,
205
    /// Strategy identifier
206
    pub strategy_id: String,
207
    /// Type of order event
208
    pub event_type: OrderEventType,
209
    /// Previous quantity for modifications
210
    pub previous_quantity: Option<Quantity>,
211
    /// Previous price for modifications
212
    pub previous_price: Option<Decimal>,
213
    /// Reason for cancellation or modification
214
    pub reason: Option<String>,
215
}
216
217
/// Types of order events
218
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
219
pub enum OrderEventType {
220
    /// Order was placed
221
    Placed,
222
    /// Order was modified
223
    Modified,
224
    /// Order was cancelled
225
    Cancelled,
226
    /// Order was rejected
227
    Rejected,
228
    /// Order expired
229
    Expired,
230
}
231
232
impl fmt::Display for OrderEventType {
233
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
234
0
        match self {
235
0
            Self::Placed => write!(f, "PLACED"),
236
0
            Self::Modified => write!(f, "MODIFIED"),
237
0
            Self::Cancelled => write!(f, "CANCELLED"),
238
0
            Self::Rejected => write!(f, "REJECTED"),
239
0
            Self::Expired => write!(f, "EXPIRED"),
240
        }
241
0
    }
242
}
243
244
/// Order type enumeration
245
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
246
pub enum OrderType {
247
    /// Market order - execute immediately at best available price
248
    Market,
249
    /// Limit order - execute only at specified price or better
250
    Limit,
251
    /// Stop order - becomes market order when stop price is reached
252
    Stop,
253
    /// Stop-limit order - becomes limit order when stop price is reached
254
    StopLimit,
255
}
256
257
impl fmt::Display for OrderType {
258
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
259
0
        match self {
260
0
            Self::Market => write!(f, "MARKET"),
261
0
            Self::Limit => write!(f, "LIMIT"),
262
0
            Self::Stop => write!(f, "STOP"),
263
0
            Self::StopLimit => write!(f, "STOP_LIMIT"),
264
        }
265
0
    }
266
}
267
268
/// Order side enumeration
269
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
270
pub enum OrderSide {
271
    /// Buy order
272
    Buy,
273
    /// Sell order
274
    Sell,
275
}
276
277
impl fmt::Display for OrderSide {
278
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279
0
        match self {
280
0
            Self::Buy => write!(f, "BUY"),
281
0
            Self::Sell => write!(f, "SELL"),
282
        }
283
0
    }
284
}