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}