Coverage Report

Created: 2025-10-06 12:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/home/jgrusewski/Work/foxhunt/config/src/asset_classification.rs
Line
Count
Source
1
//! Comprehensive Asset Classification Configuration System
2
//!
3
//! This module provides production-ready asset classification capabilities with:
4
//! - Sophisticated asset class hierarchies
5
//! - Dynamic trading parameter configuration
6
//! - Pattern-based symbol matching with regex support
7
//! - Database-backed configuration with hot-reload
8
//! - Volatility profiling and risk management integration
9
10
use chrono::{DateTime, Datelike, NaiveTime, Utc};
11
use log;
12
use regex::Regex;
13
use rust_decimal::{prelude::FromPrimitive, Decimal};
14
use serde::{Deserialize, Serialize};
15
use std::collections::HashMap;
16
use uuid::Uuid;
17
18
/// Comprehensive asset classification enum with detailed sub-categories
19
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
20
pub enum AssetClass {
21
    /// Equity instruments with sector-specific characteristics
22
    Equity {
23
        sector: EquitySector,
24
        market_cap: MarketCapTier,
25
        region: GeographicRegion,
26
    },
27
    /// Futures contracts with underlying asset classification
28
    Future {
29
        underlying: FutureType,
30
        expiry_type: ExpiryType,
31
        exchange: String,
32
    },
33
    /// Foreign exchange pairs with specific characteristics
34
    Forex {
35
        base: String,
36
        quote: String,
37
        pair_type: ForexPairType,
38
    },
39
    /// Cryptocurrency assets with network and type classification
40
    Crypto {
41
        network: String,
42
        crypto_type: CryptoType,
43
        market_cap_rank: Option<u32>,
44
    },
45
    /// Commodity instruments with category classification
46
    Commodity {
47
        category: CommodityType,
48
        storage_type: StorageType,
49
    },
50
    /// Fixed income securities
51
    FixedIncome {
52
        instrument_type: FixedIncomeType,
53
        credit_rating: CreditRating,
54
        maturity: MaturityBucket,
55
    },
56
    /// Derivatives and structured products
57
    Derivative {
58
        underlying_class: Box<AssetClass>,
59
        derivative_type: DerivativeType,
60
    },
61
    /// Unknown or unclassified assets (conservative defaults)
62
    Unknown,
63
}
64
65
/// Equity sector classifications aligned with industry standards
66
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
67
pub enum EquitySector {
68
    Technology,
69
    Healthcare,
70
    Financial,
71
    ConsumerDiscretionary,
72
    ConsumerStaples,
73
    Industrial,
74
    Energy,
75
    Materials,
76
    Utilities,
77
    RealEstate,
78
    CommunicationServices,
79
}
80
81
/// Market capitalization tiers for equity classification
82
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
83
pub enum MarketCapTier {
84
    LargeCap, // > $10B
85
    MidCap,   // $2B - $10B
86
    SmallCap, // $300M - $2B
87
    MicroCap, // < $300M
88
}
89
90
/// Geographic regions for asset classification
91
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
92
pub enum GeographicRegion {
93
    NorthAmerica,
94
    Europe,
95
    Asia,
96
    EmergingMarkets,
97
    Global,
98
}
99
100
/// Future contract underlying asset types
101
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
102
pub enum FutureType {
103
    Equity,
104
    Currency,
105
    Commodity,
106
    Interest,
107
    Volatility,
108
}
109
110
/// Futures expiry categorization
111
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
112
pub enum ExpiryType {
113
    Weekly,
114
    Monthly,
115
    Quarterly,
116
    Annual,
117
}
118
119
/// Forex pair type classification
120
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
121
pub enum ForexPairType {
122
    Major,   // EUR/USD, GBP/USD, USD/JPY, etc.
123
    Minor,   // Cross-currency pairs without USD
124
    Exotic,  // Emerging market currencies
125
    JPYPair, // Special handling for JPY pairs
126
}
127
128
/// Cryptocurrency type classification
129
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
130
pub enum CryptoType {
131
    Bitcoin,
132
    Ethereum,
133
    Stablecoin,
134
    AltcoinMajor, // Top 20 market cap
135
    AltcoinMinor, // Beyond top 20
136
    DeFi,
137
    GameFi,
138
    Meme,
139
}
140
141
/// Commodity categories
142
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
143
pub enum CommodityType {
144
    PreciousMetals,
145
    Energy,
146
    Agricultural,
147
    IndustrialMetals,
148
    Livestock,
149
}
150
151
/// Storage characteristics for commodities
152
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
153
pub enum StorageType {
154
    Physical,
155
    Financial,
156
}
157
158
/// Fixed income instrument types
159
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
160
pub enum FixedIncomeType {
161
    Government,
162
    Corporate,
163
    Municipal,
164
    InflationProtected,
165
}
166
167
/// Credit rating classifications
168
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
169
pub enum CreditRating {
170
    AAA,
171
    AA,
172
    A,
173
    BBB,
174
    BB,
175
    B,
176
    CCC,
177
    Unrated,
178
}
179
180
/// Maturity buckets for fixed income
181
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
182
pub enum MaturityBucket {
183
    ShortTerm,  // < 2 years
184
    MediumTerm, // 2-10 years
185
    LongTerm,   // > 10 years
186
}
187
188
/// Derivative instrument types
189
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
190
pub enum DerivativeType {
191
    Option,
192
    Swap,
193
    Forward,
194
    Structured,
195
}
196
197
/// Comprehensive volatility profile with regime-aware parameters
198
#[derive(Debug, Clone, Serialize, Deserialize)]
199
pub struct VolatilityProfile {
200
    /// Base annual volatility (standard market conditions)
201
    pub base_annual_volatility: f64,
202
    /// Stress volatility multiplier for high-stress periods
203
    pub stress_volatility_multiplier: f64,
204
    /// Intraday volatility pattern (hourly multipliers)
205
    pub intraday_pattern: Vec<f64>,
206
    /// Volatility clustering parameter (GARCH-like)
207
    pub volatility_persistence: f64,
208
    /// Jump risk probability and magnitude
209
    pub jump_risk: JumpRiskProfile,
210
}
211
212
/// Jump risk characteristics
213
#[derive(Debug, Clone, Serialize, Deserialize)]
214
pub struct JumpRiskProfile {
215
    /// Probability of large price jumps per day
216
    pub jump_probability: f64,
217
    /// Average magnitude of jumps (as fraction of price)
218
    pub jump_magnitude: f64,
219
    /// Maximum expected jump size
220
    pub max_jump_size: f64,
221
}
222
223
/// Dynamic trading parameters that adapt to market conditions
224
#[derive(Debug, Clone, Serialize, Deserialize)]
225
pub struct TradingParameters {
226
    /// Position sizing constraints
227
    pub position_limits: PositionLimits,
228
    /// Risk management thresholds
229
    pub risk_thresholds: RiskThresholds,
230
    /// Execution parameters
231
    pub execution_config: ExecutionConfig,
232
    /// Market making parameters (if applicable)
233
    pub market_making: Option<MarketMakingConfig>,
234
}
235
236
/// Position sizing and exposure limits
237
#[derive(Debug, Clone, Serialize, Deserialize)]
238
pub struct PositionLimits {
239
    /// Maximum position size as fraction of portfolio NAV
240
    pub max_position_fraction: f64,
241
    /// Maximum leverage allowed for this asset
242
    pub max_leverage: f64,
243
    /// Concentration limit (max % of total positions in this asset class)
244
    pub concentration_limit: f64,
245
    /// Minimum position size (to avoid micro-positions)
246
    pub min_position_size: Decimal,
247
}
248
249
/// Risk management thresholds and limits
250
#[derive(Debug, Clone, Serialize, Deserialize)]
251
pub struct RiskThresholds {
252
    /// VaR limit as fraction of portfolio
253
    pub var_limit: f64,
254
    /// Daily loss limit
255
    pub daily_loss_limit: f64,
256
    /// Stop-loss threshold
257
    pub stop_loss_threshold: f64,
258
    /// Volatility circuit breaker threshold
259
    pub volatility_circuit_breaker: f64,
260
    /// Maximum drawdown before position reduction
261
    pub max_drawdown_threshold: f64,
262
}
263
264
/// Execution configuration parameters
265
#[derive(Debug, Clone, Serialize, Deserialize)]
266
pub struct ExecutionConfig {
267
    /// Preferred order types for this asset
268
    pub preferred_order_types: Vec<OrderType>,
269
    /// Tick size for price increments
270
    pub tick_size: Decimal,
271
    /// Minimum order size
272
    pub min_order_size: Decimal,
273
    /// Maximum order size before breaking up
274
    pub max_order_size: Decimal,
275
    /// Execution time constraints
276
    pub time_in_force_default: TimeInForce,
277
    /// Slippage tolerance
278
    pub slippage_tolerance: f64,
279
}
280
281
/// Market making specific configuration
282
#[derive(Debug, Clone, Serialize, Deserialize)]
283
pub struct MarketMakingConfig {
284
    /// Bid-ask spread targets
285
    pub target_spread: f64,
286
    /// Inventory limits
287
    pub max_inventory: Decimal,
288
    /// Quote size
289
    pub quote_size: Decimal,
290
    /// Refresh frequency
291
    pub refresh_frequency: std::time::Duration,
292
}
293
294
/// Order type enumeration
295
#[derive(Debug, Clone, Serialize, Deserialize)]
296
pub enum OrderType {
297
    Market,
298
    Limit,
299
    Stop,
300
    StopLimit,
301
    Hidden,
302
    Iceberg,
303
}
304
305
/// Time in force options
306
#[derive(Debug, Clone, Serialize, Deserialize)]
307
pub enum TimeInForce {
308
    Day,
309
    GoodTillCancel,
310
    ImmediateOrCancel,
311
    FillOrKill,
312
    GTD, // Good Till Date
313
}
314
315
/// Symbol pattern matching configuration with compiled regex
316
#[derive(Debug, Clone, Serialize, Deserialize)]
317
pub struct AssetConfig {
318
    /// UUID for database storage
319
    pub id: Uuid,
320
    /// Human-readable name for this configuration
321
    pub name: String,
322
    /// Regex pattern for symbol matching
323
    pub symbol_pattern: String,
324
    /// Compiled regex (not serialized, rebuilt on load)
325
    #[serde(skip)]
326
    pub compiled_pattern: Option<Regex>,
327
    /// Asset class classification
328
    pub asset_class: AssetClass,
329
    /// Volatility profile
330
    pub volatility_profile: VolatilityProfile,
331
    /// Trading parameters
332
    pub trading_parameters: TradingParameters,
333
    /// Priority for pattern matching (higher = checked first)
334
    pub priority: u32,
335
    /// Whether this configuration is active
336
    pub is_active: bool,
337
    /// Creation timestamp
338
    pub created_at: DateTime<Utc>,
339
    /// Last update timestamp
340
    pub updated_at: DateTime<Utc>,
341
    /// Trading hours (if applicable)
342
    pub trading_hours: Option<TradingHours>,
343
    /// Settlement details
344
    pub settlement_config: SettlementConfig,
345
}
346
347
/// Trading hours configuration
348
#[derive(Debug, Clone, Serialize, Deserialize)]
349
pub struct TradingHours {
350
    /// Regular trading session start
351
    pub market_open: NaiveTime,
352
    /// Regular trading session end
353
    pub market_close: NaiveTime,
354
    /// Pre-market session (if available)
355
    pub pre_market: Option<(NaiveTime, NaiveTime)>,
356
    /// After-hours session (if available)
357
    pub after_hours: Option<(NaiveTime, NaiveTime)>,
358
    /// Timezone for these hours
359
    pub timezone: String,
360
    /// Days of week when trading is active (0=Sunday, 6=Saturday)
361
    pub trading_days: Vec<u8>,
362
}
363
364
/// Settlement configuration
365
#[derive(Debug, Clone, Serialize, Deserialize)]
366
pub struct SettlementConfig {
367
    /// Settlement period (T+n days)
368
    pub settlement_days: u32,
369
    /// Settlement currency
370
    pub settlement_currency: String,
371
    /// Whether physical delivery is possible
372
    pub physical_settlement: bool,
373
}
374
375
/// Asset classification manager with caching and hot-reload capabilities
376
pub struct AssetClassificationManager {
377
    /// Asset configurations indexed by priority
378
    configs: Vec<AssetConfig>,
379
    /// Explicit symbol mappings for fast lookup
380
    symbol_cache: HashMap<String, AssetClass>,
381
    /// Last configuration reload timestamp
382
    last_reload: DateTime<Utc>,
383
    /// Configuration reload interval
384
    reload_interval: std::time::Duration,
385
}
386
387
impl AssetClassificationManager {
388
    /// Create a new asset classification manager
389
17
    pub fn new() -> Self {
390
17
        Self {
391
17
            configs: Vec::new(),
392
17
            symbol_cache: HashMap::new(),
393
17
            last_reload: Utc::now(),
394
17
            reload_interval: std::time::Duration::from_secs(300), // 5 minutes
395
17
        }
396
17
    }
397
398
    /// Load configurations from database
399
14
    pub async fn load_configurations(
400
14
        &mut self,
401
14
        configs: Vec<AssetConfig>,
402
14
    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
403
14
        self.configs = configs;
404
        // Sort by priority (highest first)
405
23
        
self.configs14
.
sort_by14
(|a, b| b.priority.cmp(&a.priority));
406
407
        // Compile regex patterns
408
51
        for 
config37
in &mut self.configs {
409
37
            match Regex::new(&config.symbol_pattern) {
410
36
                Ok(regex) => config.compiled_pattern = Some(regex),
411
1
                Err(e) => {
412
1
                    log::warn!(
413
0
                        "Failed to compile regex pattern '{}': {}",
414
                        config.symbol_pattern,
415
                        e
416
                    );
417
1
                    config.is_active = false;
418
                }
419
            }
420
        }
421
422
14
        self.last_reload = Utc::now();
423
14
        log::info!(
424
0
            "Loaded {} asset classification configurations",
425
0
            self.configs.len()
426
        );
427
14
        Ok(())
428
14
    }
429
430
    /// Classify a symbol using the configured rules
431
15
    pub fn classify_symbol(&self, symbol: &str) -> AssetClass {
432
15
        let symbol_upper = symbol.to_uppercase();
433
434
        // Check cache first
435
15
        if let Some(
asset_class0
) = self.symbol_cache.get(&symbol_upper) {
436
0
            return asset_class.clone();
437
15
        }
438
439
        // Check pattern rules in priority order
440
31
        for 
config28
in &self.configs {
441
28
            if !config.is_active {
442
0
                continue;
443
28
            }
444
445
28
            if let Some(ref regex) = config.compiled_pattern {
446
28
                if regex.is_match(&symbol_upper) {
447
12
                    return config.asset_class.clone();
448
16
                }
449
0
            }
450
        }
451
452
3
        AssetClass::Unknown
453
15
    }
454
455
    /// Get complete asset configuration for a symbol
456
27
    pub fn get_asset_config(&self, symbol: &str) -> Option<&AssetConfig> {
457
27
        let symbol_upper = symbol.to_uppercase();
458
459
43
        for 
config41
in &self.configs {
460
41
            if !config.is_active {
461
0
                continue;
462
41
            }
463
464
41
            if let Some(ref regex) = config.compiled_pattern {
465
41
                if regex.is_match(&symbol_upper) {
466
25
                    return Some(config);
467
16
                }
468
0
            }
469
        }
470
471
2
        None
472
27
    }
473
474
    /// Get volatility profile for a symbol
475
11
    pub fn get_volatility_profile(&self, symbol: &str) -> Option<&VolatilityProfile> {
476
11
        self.get_asset_config(symbol)
477
11
            .map(|config| &config.volatility_profile)
478
11
    }
479
480
    /// Get trading parameters for a symbol
481
9
    pub fn get_trading_parameters(&self, symbol: &str) -> Option<&TradingParameters> {
482
9
        self.get_asset_config(symbol)
483
9
            .map(|config| &config.trading_parameters)
484
9
    }
485
486
    /// Get daily volatility estimate for a symbol
487
9
    pub fn get_daily_volatility(&self, symbol: &str) -> f64 {
488
9
        if let Some(
profile8
) = self.get_volatility_profile(symbol) {
489
8
            profile.base_annual_volatility / 252.0_f64.sqrt()
490
        } else {
491
1
            0.5 / 252.0_f64.sqrt() // Default high volatility
492
        }
493
9
    }
494
495
    /// Get position sizing recommendation
496
5
    pub fn get_position_size_recommendation(
497
5
        &self,
498
5
        symbol: &str,
499
5
        portfolio_nav: Decimal,
500
5
    ) -> Option<Decimal> {
501
5
        if let Some(config) = self.get_asset_config(symbol) {
502
5
            let max_fraction = config
503
5
                .trading_parameters
504
5
                .position_limits
505
5
                .max_position_fraction;
506
5
            if let Some(decimal_fraction) = Decimal::from_f64(max_fraction) {
507
5
                Some(portfolio_nav * decimal_fraction)
508
            } else {
509
0
                Some(Decimal::ZERO)
510
            }
511
        } else {
512
0
            None
513
        }
514
5
    }
515
516
    /// Check if symbol is within trading hours
517
2
    pub fn is_trading_active(&self, symbol: &str, timestamp: DateTime<Utc>) -> bool {
518
2
        if let Some(config) = self.get_asset_config(symbol) {
519
2
            if let Some(
ref trading_hours1
) = config.trading_hours {
520
                // Simplified check - in production would need proper timezone handling
521
1
                let weekday = timestamp.weekday().num_days_from_sunday() as u8;
522
1
                trading_hours.trading_days.contains(&weekday)
523
            } else {
524
1
                true // No trading hours restriction
525
            }
526
        } else {
527
0
            true // Default to always active for unknown symbols
528
        }
529
2
    }
530
531
    /// Add explicit symbol mapping to cache
532
0
    pub fn cache_symbol_mapping(&mut self, symbol: String, asset_class: AssetClass) {
533
0
        self.symbol_cache.insert(symbol.to_uppercase(), asset_class);
534
0
    }
535
536
    /// Clear symbol cache
537
0
    pub fn clear_cache(&mut self) {
538
0
        self.symbol_cache.clear();
539
0
    }
540
541
    /// Check if configuration needs reload
542
0
    pub fn needs_reload(&self) -> bool {
543
0
        Utc::now().signed_duration_since(self.last_reload)
544
0
            > chrono::Duration::from_std(self.reload_interval).unwrap_or_default()
545
0
    }
546
547
    /// Get all active configurations
548
2
    pub fn get_active_configurations(&self) -> Vec<&AssetConfig> {
549
2
        self.configs
550
2
            .iter()
551
2
            .filter(|config| config.is_active)
552
2
            .collect()
553
2
    }
554
555
    /// Get configurations by asset class
556
0
    pub fn get_configurations_by_class(&self, asset_class: &AssetClass) -> Vec<&AssetConfig> {
557
0
        self.configs
558
0
            .iter()
559
0
            .filter(|config| config.is_active && &config.asset_class == asset_class)
560
0
            .collect()
561
0
    }
562
}
563
564
impl Default for AssetClassificationManager {
565
0
    fn default() -> Self {
566
0
        Self::new()
567
0
    }
568
}
569
570
/// Create default asset configurations for common instruments
571
11
pub fn create_default_configurations() -> Vec<AssetConfig> {
572
11
    let mut configs = Vec::new();
573
11
    let now = Utc::now();
574
575
    // Blue chip US equities
576
11
    configs.push(AssetConfig {
577
11
        id: Uuid::new_v4(),
578
11
        name: "Blue Chip US Equities".to_string(),
579
11
        symbol_pattern: "^(AAPL|MSFT|GOOGL|AMZN|META|TSLA|NVDA|JPM|JNJ|V|PG|UNH|HD|BAC|DIS|MA|NFLX|CRM|ADBE|PYPL|INTC|CMCSA|PFE|T|VZ|MRK|WMT|KO|NKE|CVX|XOM)$".to_string(),
580
11
        compiled_pattern: None,
581
11
        asset_class: AssetClass::Equity {
582
11
            sector: EquitySector::Technology,
583
11
            market_cap: MarketCapTier::LargeCap,
584
11
            region: GeographicRegion::NorthAmerica,
585
11
        },
586
11
        volatility_profile: VolatilityProfile {
587
11
            base_annual_volatility: 0.25,
588
11
            stress_volatility_multiplier: 2.0,
589
11
            intraday_pattern: vec![1.0; 24], // Flat pattern for simplicity
590
11
            volatility_persistence: 0.85,
591
11
            jump_risk: JumpRiskProfile {
592
11
                jump_probability: 0.02,
593
11
                jump_magnitude: 0.05,
594
11
                max_jump_size: 0.15,
595
11
            },
596
11
        },
597
11
        trading_parameters: TradingParameters {
598
11
            position_limits: PositionLimits {
599
11
                max_position_fraction: 0.20,
600
11
                max_leverage: 2.0,
601
11
                concentration_limit: 0.30,
602
11
                min_position_size: Decimal::from(100),
603
11
            },
604
11
            risk_thresholds: RiskThresholds {
605
11
                var_limit: 0.05,
606
11
                daily_loss_limit: 0.03,
607
11
                stop_loss_threshold: 0.10,
608
11
                volatility_circuit_breaker: 0.05,
609
11
                max_drawdown_threshold: 0.15,
610
11
            },
611
11
            execution_config: ExecutionConfig {
612
11
                preferred_order_types: vec![OrderType::Limit, OrderType::Market],
613
11
                tick_size: "0.01".parse().unwrap(),
614
11
                min_order_size: Decimal::from(1),
615
11
                max_order_size: Decimal::from(10000),
616
11
                time_in_force_default: TimeInForce::Day,
617
11
                slippage_tolerance: 0.001,
618
11
            },
619
11
            market_making: None,
620
11
        },
621
11
        priority: 100,
622
11
        is_active: true,
623
11
        created_at: now,
624
11
        updated_at: now,
625
11
        trading_hours: Some(TradingHours {
626
11
            market_open: NaiveTime::from_hms_opt(9, 30, 0).unwrap(),
627
11
            market_close: NaiveTime::from_hms_opt(16, 0, 0).unwrap(),
628
11
            pre_market: Some((NaiveTime::from_hms_opt(4, 0, 0).unwrap(), NaiveTime::from_hms_opt(9, 30, 0).unwrap())),
629
11
            after_hours: Some((NaiveTime::from_hms_opt(16, 0, 0).unwrap(), NaiveTime::from_hms_opt(20, 0, 0).unwrap())),
630
11
            timezone: "America/New_York".to_string(),
631
11
            trading_days: vec![1, 2, 3, 4, 5], // Monday-Friday
632
11
        }),
633
11
        settlement_config: SettlementConfig {
634
11
            settlement_days: 2,
635
11
            settlement_currency: "USD".to_string(),
636
11
            physical_settlement: false,
637
11
        },
638
11
    });
639
640
    // Major cryptocurrency pairs
641
11
    configs.push(AssetConfig {
642
11
        id: Uuid::new_v4(),
643
11
        name: "Major Cryptocurrencies".to_string(),
644
11
        symbol_pattern: "^(BTC|ETH|BTCUSD|ETHUSD|BTCUSDT|ETHUSDT).*$".to_string(),
645
11
        compiled_pattern: None,
646
11
        asset_class: AssetClass::Crypto {
647
11
            network: "Bitcoin".to_string(),
648
11
            crypto_type: CryptoType::Bitcoin,
649
11
            market_cap_rank: Some(1),
650
11
        },
651
11
        volatility_profile: VolatilityProfile {
652
11
            base_annual_volatility: 0.80,
653
11
            stress_volatility_multiplier: 3.0,
654
11
            intraday_pattern: vec![1.0; 24],
655
11
            volatility_persistence: 0.90,
656
11
            jump_risk: JumpRiskProfile {
657
11
                jump_probability: 0.05,
658
11
                jump_magnitude: 0.10,
659
11
                max_jump_size: 0.30,
660
11
            },
661
11
        },
662
11
        trading_parameters: TradingParameters {
663
11
            position_limits: PositionLimits {
664
11
                max_position_fraction: 0.10,
665
11
                max_leverage: 1.5,
666
11
                concentration_limit: 0.15,
667
11
                min_position_size: "0.001".parse().unwrap(),
668
11
            },
669
11
            risk_thresholds: RiskThresholds {
670
11
                var_limit: 0.10,
671
11
                daily_loss_limit: 0.05,
672
11
                stop_loss_threshold: 0.15,
673
11
                volatility_circuit_breaker: 0.15,
674
11
                max_drawdown_threshold: 0.25,
675
11
            },
676
11
            execution_config: ExecutionConfig {
677
11
                preferred_order_types: vec![OrderType::Limit, OrderType::Market],
678
11
                tick_size: "0.01".parse().unwrap(),
679
11
                min_order_size: "0.001".parse().unwrap(),
680
11
                max_order_size: Decimal::from(100),
681
11
                time_in_force_default: TimeInForce::GoodTillCancel,
682
11
                slippage_tolerance: 0.005,
683
11
            },
684
11
            market_making: None,
685
11
        },
686
11
        priority: 90,
687
11
        is_active: true,
688
11
        created_at: now,
689
11
        updated_at: now,
690
11
        trading_hours: None, // 24/7 trading
691
11
        settlement_config: SettlementConfig {
692
11
            settlement_days: 0,
693
11
            settlement_currency: "USD".to_string(),
694
11
            physical_settlement: true,
695
11
        },
696
11
    });
697
698
    // Major forex pairs
699
11
    configs.push(AssetConfig {
700
11
        id: Uuid::new_v4(),
701
11
        name: "Major Forex Pairs".to_string(),
702
11
        symbol_pattern: "^(EUR|GBP|USD|JPY|AUD|CAD|CHF|NZD)(USD|EUR|GBP|JPY)$".to_string(),
703
11
        compiled_pattern: None,
704
11
        asset_class: AssetClass::Forex {
705
11
            base: "EUR".to_string(),
706
11
            quote: "USD".to_string(),
707
11
            pair_type: ForexPairType::Major,
708
11
        },
709
11
        volatility_profile: VolatilityProfile {
710
11
            base_annual_volatility: 0.12,
711
11
            stress_volatility_multiplier: 2.5,
712
11
            intraday_pattern: vec![1.0; 24],
713
11
            volatility_persistence: 0.80,
714
11
            jump_risk: JumpRiskProfile {
715
11
                jump_probability: 0.01,
716
11
                jump_magnitude: 0.02,
717
11
                max_jump_size: 0.08,
718
11
            },
719
11
        },
720
11
        trading_parameters: TradingParameters {
721
11
            position_limits: PositionLimits {
722
11
                max_position_fraction: 0.30,
723
11
                max_leverage: 10.0,
724
11
                concentration_limit: 0.40,
725
11
                min_position_size: Decimal::from(1000),
726
11
            },
727
11
            risk_thresholds: RiskThresholds {
728
11
                var_limit: 0.03,
729
11
                daily_loss_limit: 0.02,
730
11
                stop_loss_threshold: 0.05,
731
11
                volatility_circuit_breaker: 0.03,
732
11
                max_drawdown_threshold: 0.10,
733
11
            },
734
11
            execution_config: ExecutionConfig {
735
11
                preferred_order_types: vec![OrderType::Limit, OrderType::Market],
736
11
                tick_size: "0.00001".parse().unwrap(),
737
11
                min_order_size: Decimal::from(1000),
738
11
                max_order_size: Decimal::from(10000000),
739
11
                time_in_force_default: TimeInForce::GoodTillCancel,
740
11
                slippage_tolerance: 0.0002,
741
11
            },
742
11
            market_making: Some(MarketMakingConfig {
743
11
                target_spread: 0.0001,
744
11
                max_inventory: Decimal::from(100000),
745
11
                quote_size: Decimal::from(10000),
746
11
                refresh_frequency: std::time::Duration::from_millis(100),
747
11
            }),
748
11
        },
749
11
        priority: 80,
750
11
        is_active: true,
751
11
        created_at: now,
752
11
        updated_at: now,
753
11
        trading_hours: None, // 24/5 trading
754
11
        settlement_config: SettlementConfig {
755
11
            settlement_days: 2,
756
11
            settlement_currency: "USD".to_string(),
757
11
            physical_settlement: false,
758
11
        },
759
11
    });
760
761
11
    configs
762
11
}
763
764
#[cfg(test)]
765
mod tests {
766
    use super::*;
767
768
    #[tokio::test]
769
1
    async fn test_symbol_classification() {
770
1
        let mut manager = AssetClassificationManager::new();
771
1
        let configs = create_default_configurations();
772
1
        manager.load_configurations(configs).await.unwrap();
773
774
        // Test blue chip classification
775
1
        match manager.classify_symbol("AAPL") {
776
1
            AssetClass::Equity {
777
1
                sector: EquitySector::Technology,
778
1
                ..
779
1
            } => (),
780
1
            _ => 
panic!0
(
"AAPL should be classified as Technology equity"0
),
781
1
        }
782
1
783
1
        // Test crypto classification
784
1
        match manager.classify_symbol("BTCUSD") {
785
1
            AssetClass::Crypto {
786
1
                crypto_type: CryptoType::Bitcoin,
787
1
                ..
788
1
            } => (),
789
1
            _ => 
panic!0
(
"BTCUSD should be classified as Bitcoin crypto"0
),
790
1
        }
791
1
792
1
        // Test unknown symbol
793
1
        assert_eq!(manager.classify_symbol("UNKNOWN"), AssetClass::Unknown);
794
1
    }
795
796
    #[tokio::test]
797
1
    async fn test_volatility_profile() {
798
1
        let mut manager = AssetClassificationManager::new();
799
1
        let configs = create_default_configurations();
800
1
        manager.load_configurations(configs).await.unwrap();
801
802
1
        let profile = manager.get_volatility_profile("AAPL").unwrap();
803
1
        assert_eq!(profile.base_annual_volatility, 0.25);
804
805
1
        let daily_vol = manager.get_daily_volatility("AAPL");
806
1
        assert!((daily_vol - (0.25 / 252.0_f64.sqrt())).abs() < 1e-10);
807
1
    }
808
809
    #[tokio::test]
810
1
    async fn test_trading_parameters() {
811
1
        let mut manager = AssetClassificationManager::new();
812
1
        let configs = create_default_configurations();
813
1
        manager.load_configurations(configs).await.unwrap();
814
815
1
        let params = manager.get_trading_parameters("AAPL").unwrap();
816
1
        assert_eq!(params.position_limits.max_position_fraction, 0.20);
817
1
        assert_eq!(params.position_limits.max_leverage, 2.0);
818
1
    }
819
}