/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 | | } |