nt_core/traits.rs
1//! Core traits for the Neural Trading system
2//!
3//! This module defines the primary interfaces that all components must implement.
4//! All traits use `async_trait` for async method support.
5
6use crate::error::Result;
7use crate::types::{Bar, MarketTick, Order, OrderBook, OrderStatus, Position, Signal, Symbol};
8use async_trait::async_trait;
9use chrono::{DateTime, Utc};
10use rust_decimal::Decimal;
11use tokio::sync::mpsc;
12
13// ============================================================================
14// Market Data Provider Trait
15// ============================================================================
16
17/// Trait for market data providers (Alpaca, Polygon, IEX, etc.)
18///
19/// Implementations should handle connection management, authentication,
20/// and data normalization.
21#[async_trait]
22pub trait MarketDataProvider: Send + Sync {
23 /// Subscribe to real-time market data for given symbols
24 ///
25 /// Returns a receiver channel that will receive market ticks as they arrive.
26 /// The provider should handle reconnection and error recovery internally.
27 ///
28 /// # Arguments
29 ///
30 /// * `symbols` - List of symbols to subscribe to
31 ///
32 /// # Returns
33 ///
34 /// Receiver channel for market ticks
35 async fn subscribe(&self, symbols: &[Symbol]) -> Result<mpsc::Receiver<MarketTick>>;
36
37 /// Unsubscribe from market data for given symbols
38 async fn unsubscribe(&self, symbols: &[Symbol]) -> Result<()>;
39
40 /// Get latest quote for a symbol
41 async fn get_latest_quote(&self, symbol: &Symbol) -> Result<MarketTick>;
42
43 /// Get historical bars for a symbol
44 ///
45 /// # Arguments
46 ///
47 /// * `symbol` - Trading symbol
48 /// * `start` - Start timestamp
49 /// * `end` - End timestamp
50 /// * `timeframe` - Bar timeframe (e.g., "1Min", "1Hour", "1Day")
51 async fn get_bars(
52 &self,
53 symbol: &Symbol,
54 start: DateTime<Utc>,
55 end: DateTime<Utc>,
56 timeframe: &str,
57 ) -> Result<Vec<Bar>>;
58
59 /// Get order book snapshot for a symbol
60 async fn get_order_book(&self, symbol: &Symbol) -> Result<OrderBook>;
61
62 /// Check if the provider is connected and healthy
63 async fn is_connected(&self) -> bool;
64}
65
66// ============================================================================
67// Strategy Trait
68// ============================================================================
69
70/// Trait for trading strategies
71///
72/// Each strategy must implement this trait to be used by the trading system.
73/// Strategies receive market data and generate trading signals.
74#[async_trait]
75pub trait Strategy: Send + Sync {
76 /// Unique identifier for this strategy
77 fn id(&self) -> &str;
78
79 /// Human-readable name for this strategy
80 fn name(&self) -> &str;
81
82 /// Strategy description
83 fn description(&self) -> &str;
84
85 /// Process incoming market data and potentially generate signals
86 ///
87 /// This method is called whenever new market data arrives for symbols
88 /// the strategy is interested in.
89 ///
90 /// # Arguments
91 ///
92 /// * `tick` - Market tick data
93 ///
94 /// # Returns
95 ///
96 /// Optional trading signal if conditions are met
97 async fn on_tick(&mut self, tick: &MarketTick) -> Result<Option<Signal>>;
98
99 /// Process bar data (OHLCV) and potentially generate signals
100 ///
101 /// This method is called when a new bar is complete.
102 ///
103 /// # Arguments
104 ///
105 /// * `bar` - OHLCV bar data
106 ///
107 /// # Returns
108 ///
109 /// Optional trading signal if conditions are met
110 async fn on_bar(&mut self, bar: &Bar) -> Result<Option<Signal>>;
111
112 /// Generate signals based on current state (called periodically)
113 ///
114 /// This method is called on a schedule (e.g., every minute) to allow
115 /// strategies to generate signals based on accumulated state.
116 ///
117 /// # Returns
118 ///
119 /// Vector of trading signals
120 async fn generate_signals(&mut self) -> Result<Vec<Signal>>;
121
122 /// Update strategy state based on order execution feedback
123 ///
124 /// This method is called when an order is filled, allowing the strategy
125 /// to update its internal state.
126 ///
127 /// # Arguments
128 ///
129 /// * `signal` - Original signal that triggered the order
130 /// * `order` - Order that was executed
131 /// * `fill_price` - Actual fill price
132 async fn on_order_filled(
133 &mut self,
134 signal: &Signal,
135 order: &Order,
136 fill_price: Decimal,
137 ) -> Result<()>;
138
139 /// Initialize the strategy with historical data
140 ///
141 /// Called once before the strategy starts processing live data.
142 /// Allows strategies to warm up indicators with historical data.
143 ///
144 /// # Arguments
145 ///
146 /// * `bars` - Historical bar data per symbol
147 async fn initialize(&mut self, bars: Vec<Bar>) -> Result<()>;
148
149 /// Validate strategy configuration
150 ///
151 /// Called during strategy setup to ensure configuration is valid.
152 fn validate(&self) -> Result<()>;
153
154 /// Get the symbols this strategy is interested in
155 fn symbols(&self) -> Vec<Symbol>;
156
157 /// Get strategy-specific risk parameters
158 fn risk_parameters(&self) -> StrategyRiskParameters;
159}
160
161/// Risk parameters specific to a strategy
162#[derive(Debug, Clone)]
163pub struct StrategyRiskParameters {
164 /// Maximum position size as percentage of portfolio (0.0-1.0)
165 pub max_position_size: f64,
166 /// Maximum leverage allowed
167 pub max_leverage: f64,
168 /// Stop loss percentage (0.0-1.0)
169 pub stop_loss_pct: f64,
170 /// Take profit percentage (0.0-1.0)
171 pub take_profit_pct: f64,
172}
173
174impl Default for StrategyRiskParameters {
175 fn default() -> Self {
176 Self {
177 max_position_size: 0.1, // 10% of portfolio
178 max_leverage: 1.0, // No leverage
179 stop_loss_pct: 0.02, // 2% stop loss
180 take_profit_pct: 0.05, // 5% take profit
181 }
182 }
183}
184
185// ============================================================================
186// Execution Engine Trait
187// ============================================================================
188
189/// Trait for order execution engines
190///
191/// Handles order routing, execution, and tracking.
192#[async_trait]
193pub trait ExecutionEngine: Send + Sync {
194 /// Place a new order
195 ///
196 /// # Arguments
197 ///
198 /// * `order` - Order to place
199 ///
200 /// # Returns
201 ///
202 /// Broker's order ID
203 async fn place_order(&self, order: Order) -> Result<String>;
204
205 /// Cancel an existing order
206 ///
207 /// # Arguments
208 ///
209 /// * `order_id` - Broker's order ID
210 async fn cancel_order(&self, order_id: &str) -> Result<()>;
211
212 /// Get order status
213 ///
214 /// # Arguments
215 ///
216 /// * `order_id` - Broker's order ID
217 async fn get_order_status(&self, order_id: &str) -> Result<OrderStatus>;
218
219 /// Get all open orders
220 async fn get_open_orders(&self) -> Result<Vec<Order>>;
221
222 /// Get current positions
223 async fn get_positions(&self) -> Result<Vec<Position>>;
224
225 /// Get position for a specific symbol
226 async fn get_position(&self, symbol: &Symbol) -> Result<Option<Position>>;
227
228 /// Close position for a symbol
229 ///
230 /// # Arguments
231 ///
232 /// * `symbol` - Symbol to close
233 /// * `percentage` - Percentage of position to close (0.0-1.0)
234 async fn close_position(&self, symbol: &Symbol, percentage: f64) -> Result<()>;
235
236 /// Close all positions
237 async fn close_all_positions(&self) -> Result<()>;
238
239 /// Get account cash balance
240 async fn get_cash_balance(&self) -> Result<Decimal>;
241
242 /// Get account equity (cash + positions)
243 async fn get_equity(&self) -> Result<Decimal>;
244}
245
246// ============================================================================
247// Risk Manager Trait
248// ============================================================================
249
250/// Trait for risk management systems
251///
252/// Validates signals and orders before execution to ensure risk limits are met.
253#[async_trait]
254pub trait RiskManager: Send + Sync {
255 /// Validate a trading signal before execution
256 ///
257 /// # Arguments
258 ///
259 /// * `signal` - Signal to validate
260 /// * `portfolio_value` - Current portfolio value
261 /// * `positions` - Current positions
262 ///
263 /// # Returns
264 ///
265 /// Ok(()) if signal passes risk checks, Err otherwise
266 async fn validate_signal(
267 &self,
268 signal: &Signal,
269 portfolio_value: Decimal,
270 positions: &[Position],
271 ) -> Result<()>;
272
273 /// Calculate position size for a signal
274 ///
275 /// Uses strategy risk parameters and portfolio constraints to determine
276 /// appropriate position size.
277 ///
278 /// # Arguments
279 ///
280 /// * `signal` - Trading signal
281 /// * `portfolio_value` - Current portfolio value
282 /// * `risk_params` - Strategy risk parameters
283 ///
284 /// # Returns
285 ///
286 /// Recommended position size (in shares)
287 async fn calculate_position_size(
288 &self,
289 signal: &Signal,
290 portfolio_value: Decimal,
291 risk_params: &StrategyRiskParameters,
292 ) -> Result<Decimal>;
293
294 /// Check if daily loss limit has been exceeded
295 async fn check_daily_loss_limit(&self, current_pnl: Decimal) -> Result<()>;
296
297 /// Check if maximum drawdown has been exceeded
298 async fn check_max_drawdown(&self, peak_equity: Decimal, current_equity: Decimal)
299 -> Result<()>;
300
301 /// Calculate portfolio risk metrics
302 async fn calculate_risk_metrics(&self, positions: &[Position]) -> Result<RiskMetrics>;
303}
304
305/// Risk metrics for portfolio
306#[derive(Debug, Clone)]
307pub struct RiskMetrics {
308 /// Value at Risk (95% confidence)
309 pub var_95: Decimal,
310 /// Value at Risk (99% confidence)
311 pub var_99: Decimal,
312 /// Conditional Value at Risk (95%)
313 pub cvar_95: Decimal,
314 /// Maximum drawdown
315 pub max_drawdown: Decimal,
316 /// Sharpe ratio
317 pub sharpe_ratio: f64,
318 /// Portfolio volatility
319 pub volatility: f64,
320 /// Beta (market correlation)
321 pub beta: f64,
322}
323
324// ============================================================================
325// Portfolio Manager Trait
326// ============================================================================
327
328/// Trait for portfolio management
329#[async_trait]
330pub trait PortfolioManager: Send + Sync {
331 /// Get current portfolio value
332 async fn get_portfolio_value(&self) -> Result<Decimal>;
333
334 /// Get all positions
335 async fn get_positions(&self) -> Result<Vec<Position>>;
336
337 /// Update position based on fill
338 async fn update_position(
339 &self,
340 symbol: &Symbol,
341 quantity: Decimal,
342 price: Decimal,
343 ) -> Result<()>;
344
345 /// Get unrealized P&L
346 async fn get_unrealized_pnl(&self) -> Result<Decimal>;
347
348 /// Get realized P&L
349 async fn get_realized_pnl(&self) -> Result<Decimal>;
350
351 /// Rebalance portfolio to target allocations
352 async fn rebalance(&self, target_allocations: Vec<(Symbol, f64)>) -> Result<()>;
353}
354
355// ============================================================================
356// Feature Extractor Trait
357// ============================================================================
358
359/// Trait for feature extraction from market data
360///
361/// Calculates technical indicators and other features for strategy use.
362#[async_trait]
363pub trait FeatureExtractor: Send + Sync {
364 /// Extract features from historical bars
365 ///
366 /// # Arguments
367 ///
368 /// * `bars` - Historical bar data
369 ///
370 /// # Returns
371 ///
372 /// Vector of feature vectors, one per bar
373 async fn extract_features(&self, bars: &[Bar]) -> Result<Vec<FeatureVector>>;
374
375 /// Get list of feature names in order
376 fn feature_names(&self) -> Vec<String>;
377}
378
379/// Feature vector extracted from market data
380#[derive(Debug, Clone)]
381pub struct FeatureVector {
382 /// Timestamp of the feature vector
383 pub timestamp: DateTime<Utc>,
384 /// Feature values in the order specified by feature_names()
385 pub values: Vec<f64>,
386}
387
388#[cfg(test)]
389mod tests {
390 use super::*;
391
392 #[test]
393 fn test_strategy_risk_parameters_default() {
394 let params = StrategyRiskParameters::default();
395 assert_eq!(params.max_position_size, 0.1);
396 assert_eq!(params.max_leverage, 1.0);
397 assert_eq!(params.stop_loss_pct, 0.02);
398 assert_eq!(params.take_profit_pct, 0.05);
399 }
400}