Move 17 library crates into crates/, CLI binary into bin/fxt, consolidate 10 test crates into testing/, split config crate from deployment config files. Root directory reduced from 38+ to ~17 directories. All Cargo.toml paths and build.rs proto refs updated. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
844 lines
27 KiB
Rust
844 lines
27 KiB
Rust
//! Comprehensive Asset Classification Configuration System
|
|
//!
|
|
//! This module provides production-ready asset classification capabilities with:
|
|
//! - Sophisticated asset class hierarchies
|
|
//! - Dynamic trading parameter configuration
|
|
//! - Pattern-based symbol matching with regex support
|
|
//! - Database-backed configuration with hot-reload
|
|
//! - Volatility profiling and risk management integration
|
|
|
|
use chrono::{DateTime, Datelike, NaiveTime, Utc};
|
|
use regex::Regex;
|
|
use rust_decimal::Decimal;
|
|
use serde::{Deserialize, Serialize};
|
|
use std::collections::HashMap;
|
|
use uuid::Uuid;
|
|
|
|
/// Comprehensive asset classification enum with detailed sub-categories
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
|
pub enum AssetClass {
|
|
/// Equity instruments with sector-specific characteristics
|
|
Equity {
|
|
sector: EquitySector,
|
|
market_cap: MarketCapTier,
|
|
region: GeographicRegion,
|
|
},
|
|
/// Futures contracts with underlying asset classification
|
|
Future {
|
|
underlying: FutureType,
|
|
expiry_type: ExpiryType,
|
|
exchange: String,
|
|
},
|
|
/// Foreign exchange pairs with specific characteristics
|
|
Forex {
|
|
base: String,
|
|
quote: String,
|
|
pair_type: ForexPairType,
|
|
},
|
|
/// Cryptocurrency assets with network and type classification
|
|
Crypto {
|
|
network: String,
|
|
crypto_type: CryptoType,
|
|
market_cap_rank: Option<u32>,
|
|
},
|
|
/// Commodity instruments with category classification
|
|
Commodity {
|
|
category: CommodityType,
|
|
storage_type: StorageType,
|
|
},
|
|
/// Fixed income securities
|
|
FixedIncome {
|
|
instrument_type: FixedIncomeType,
|
|
credit_rating: CreditRating,
|
|
maturity: MaturityBucket,
|
|
},
|
|
/// Derivatives and structured products
|
|
Derivative {
|
|
underlying_class: Box<AssetClass>,
|
|
derivative_type: DerivativeType,
|
|
},
|
|
/// Unknown or unclassified assets (conservative defaults)
|
|
Unknown,
|
|
}
|
|
|
|
/// Equity sector classifications aligned with industry standards
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
|
pub enum EquitySector {
|
|
Technology,
|
|
Healthcare,
|
|
Financial,
|
|
ConsumerDiscretionary,
|
|
ConsumerStaples,
|
|
Industrial,
|
|
Energy,
|
|
Materials,
|
|
Utilities,
|
|
RealEstate,
|
|
CommunicationServices,
|
|
}
|
|
|
|
/// Market capitalization tiers for equity classification
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
|
pub enum MarketCapTier {
|
|
LargeCap, // > $10B
|
|
MidCap, // $2B - $10B
|
|
SmallCap, // $300M - $2B
|
|
MicroCap, // < $300M
|
|
}
|
|
|
|
/// Geographic regions for asset classification
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
|
pub enum GeographicRegion {
|
|
NorthAmerica,
|
|
Europe,
|
|
Asia,
|
|
EmergingMarkets,
|
|
Global,
|
|
}
|
|
|
|
/// Future contract underlying asset types
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
|
pub enum FutureType {
|
|
Equity,
|
|
Currency,
|
|
Commodity,
|
|
Interest,
|
|
Volatility,
|
|
}
|
|
|
|
/// Futures expiry categorization
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
|
pub enum ExpiryType {
|
|
Weekly,
|
|
Monthly,
|
|
Quarterly,
|
|
Annual,
|
|
}
|
|
|
|
/// Forex pair type classification
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
|
pub enum ForexPairType {
|
|
Major, // EUR/USD, GBP/USD, USD/JPY, etc.
|
|
Minor, // Cross-currency pairs without USD
|
|
Exotic, // Emerging market currencies
|
|
JPYPair, // Special handling for JPY pairs
|
|
}
|
|
|
|
/// Cryptocurrency type classification
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
|
pub enum CryptoType {
|
|
Bitcoin,
|
|
Ethereum,
|
|
Stablecoin,
|
|
AltcoinMajor, // Top 20 market cap
|
|
AltcoinMinor, // Beyond top 20
|
|
DeFi,
|
|
GameFi,
|
|
Meme,
|
|
}
|
|
|
|
/// Commodity categories
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
|
pub enum CommodityType {
|
|
PreciousMetals,
|
|
Energy,
|
|
Agricultural,
|
|
IndustrialMetals,
|
|
Livestock,
|
|
}
|
|
|
|
/// Storage characteristics for commodities
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
|
pub enum StorageType {
|
|
Physical,
|
|
Financial,
|
|
}
|
|
|
|
/// Fixed income instrument types
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
|
pub enum FixedIncomeType {
|
|
Government,
|
|
Corporate,
|
|
Municipal,
|
|
InflationProtected,
|
|
}
|
|
|
|
/// Credit rating classifications
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
|
pub enum CreditRating {
|
|
AAA,
|
|
AA,
|
|
A,
|
|
BBB,
|
|
BB,
|
|
B,
|
|
CCC,
|
|
Unrated,
|
|
}
|
|
|
|
/// Maturity buckets for fixed income
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
|
pub enum MaturityBucket {
|
|
ShortTerm, // < 2 years
|
|
MediumTerm, // 2-10 years
|
|
LongTerm, // > 10 years
|
|
}
|
|
|
|
/// Derivative instrument types
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
|
pub enum DerivativeType {
|
|
Option,
|
|
Swap,
|
|
Forward,
|
|
Structured,
|
|
}
|
|
|
|
/// Comprehensive volatility profile with regime-aware parameters
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct VolatilityProfile {
|
|
/// Base annual volatility (standard market conditions)
|
|
pub base_annual_volatility: f64,
|
|
/// Stress volatility multiplier for high-stress periods
|
|
pub stress_volatility_multiplier: f64,
|
|
/// Intraday volatility pattern (hourly multipliers)
|
|
pub intraday_pattern: Vec<f64>,
|
|
/// Volatility clustering parameter (GARCH-like)
|
|
pub volatility_persistence: f64,
|
|
/// Jump risk probability and magnitude
|
|
pub jump_risk: JumpRiskProfile,
|
|
}
|
|
|
|
/// Jump risk characteristics
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct JumpRiskProfile {
|
|
/// Probability of large price jumps per day
|
|
pub jump_probability: f64,
|
|
/// Average magnitude of jumps (as fraction of price)
|
|
pub jump_magnitude: f64,
|
|
/// Maximum expected jump size
|
|
pub max_jump_size: f64,
|
|
}
|
|
|
|
/// Dynamic trading parameters that adapt to market conditions
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct TradingParameters {
|
|
/// Position sizing constraints
|
|
pub position_limits: PositionLimits,
|
|
/// Risk management thresholds
|
|
pub risk_thresholds: RiskThresholds,
|
|
/// Execution parameters
|
|
pub execution_config: ExecutionConfig,
|
|
/// Market making parameters (if applicable)
|
|
pub market_making: Option<MarketMakingConfig>,
|
|
}
|
|
|
|
/// Position sizing and exposure limits
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct PositionLimits {
|
|
/// Maximum position size as fraction of portfolio NAV
|
|
pub max_position_fraction: f64,
|
|
/// Maximum leverage allowed for this asset
|
|
pub max_leverage: f64,
|
|
/// Concentration limit (max % of total positions in this asset class)
|
|
pub concentration_limit: f64,
|
|
/// Minimum position size (to avoid micro-positions)
|
|
pub min_position_size: Decimal,
|
|
}
|
|
|
|
/// Risk management thresholds and limits
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct RiskThresholds {
|
|
/// VaR limit as fraction of portfolio
|
|
pub var_limit: f64,
|
|
/// Daily loss limit
|
|
pub daily_loss_limit: f64,
|
|
/// Stop-loss threshold
|
|
pub stop_loss_threshold: f64,
|
|
/// Volatility circuit breaker threshold
|
|
pub volatility_circuit_breaker: f64,
|
|
/// Maximum drawdown before position reduction
|
|
pub max_drawdown_threshold: f64,
|
|
}
|
|
|
|
/// Execution configuration parameters
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ExecutionConfig {
|
|
/// Preferred order types for this asset
|
|
pub preferred_order_types: Vec<OrderType>,
|
|
/// Tick size for price increments
|
|
pub tick_size: Decimal,
|
|
/// Minimum order size
|
|
pub min_order_size: Decimal,
|
|
/// Maximum order size before breaking up
|
|
pub max_order_size: Decimal,
|
|
/// Execution time constraints
|
|
pub time_in_force_default: TimeInForce,
|
|
/// Slippage tolerance
|
|
pub slippage_tolerance: f64,
|
|
}
|
|
|
|
/// Market making specific configuration
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct MarketMakingConfig {
|
|
/// Bid-ask spread targets
|
|
pub target_spread: f64,
|
|
/// Inventory limits
|
|
pub max_inventory: Decimal,
|
|
/// Quote size
|
|
pub quote_size: Decimal,
|
|
/// Refresh frequency
|
|
pub refresh_frequency: std::time::Duration,
|
|
}
|
|
|
|
/// Order type enumeration
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub enum OrderType {
|
|
Market,
|
|
Limit,
|
|
Stop,
|
|
StopLimit,
|
|
Hidden,
|
|
Iceberg,
|
|
}
|
|
|
|
/// Time in force options
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub enum TimeInForce {
|
|
Day,
|
|
GoodTillCancel,
|
|
ImmediateOrCancel,
|
|
FillOrKill,
|
|
GTD, // Good Till Date
|
|
}
|
|
|
|
/// Symbol pattern matching configuration with compiled regex
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct AssetConfig {
|
|
/// UUID for database storage
|
|
pub id: Uuid,
|
|
/// Human-readable name for this configuration
|
|
pub name: String,
|
|
/// Regex pattern for symbol matching
|
|
pub symbol_pattern: String,
|
|
/// Compiled regex (not serialized, rebuilt on load)
|
|
#[serde(skip)]
|
|
pub compiled_pattern: Option<Regex>,
|
|
/// Asset class classification
|
|
pub asset_class: AssetClass,
|
|
/// Volatility profile
|
|
pub volatility_profile: VolatilityProfile,
|
|
/// Trading parameters
|
|
pub trading_parameters: TradingParameters,
|
|
/// Priority for pattern matching (higher = checked first)
|
|
pub priority: u32,
|
|
/// Whether this configuration is active
|
|
pub is_active: bool,
|
|
/// Creation timestamp
|
|
pub created_at: DateTime<Utc>,
|
|
/// Last update timestamp
|
|
pub updated_at: DateTime<Utc>,
|
|
/// Trading hours (if applicable)
|
|
pub trading_hours: Option<TradingHours>,
|
|
/// Settlement details
|
|
pub settlement_config: SettlementConfig,
|
|
}
|
|
|
|
/// Trading hours configuration
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct TradingHours {
|
|
/// Regular trading session start
|
|
pub market_open: NaiveTime,
|
|
/// Regular trading session end
|
|
pub market_close: NaiveTime,
|
|
/// Pre-market session (if available)
|
|
pub pre_market: Option<(NaiveTime, NaiveTime)>,
|
|
/// After-hours session (if available)
|
|
pub after_hours: Option<(NaiveTime, NaiveTime)>,
|
|
/// Timezone for these hours
|
|
pub timezone: String,
|
|
/// Days of week when trading is active (0=Sunday, 6=Saturday)
|
|
pub trading_days: Vec<u8>,
|
|
}
|
|
|
|
/// Settlement configuration
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct SettlementConfig {
|
|
/// Settlement period (T+n days)
|
|
pub settlement_days: u32,
|
|
/// Settlement currency
|
|
pub settlement_currency: String,
|
|
/// Whether physical delivery is possible
|
|
pub physical_settlement: bool,
|
|
}
|
|
|
|
/// Asset classification manager with caching and hot-reload capabilities
|
|
#[allow(clippy::module_name_repetitions)]
|
|
pub struct AssetClassificationManager {
|
|
/// Asset configurations indexed by priority
|
|
configs: Vec<AssetConfig>,
|
|
/// Explicit symbol mappings for fast lookup
|
|
symbol_cache: HashMap<String, AssetClass>,
|
|
/// Last configuration reload timestamp
|
|
last_reload: DateTime<Utc>,
|
|
/// Configuration reload interval
|
|
reload_interval: std::time::Duration,
|
|
}
|
|
|
|
impl AssetClassificationManager {
|
|
/// Create a new asset classification manager
|
|
#[allow(clippy::unwrap_used)] // Uses hardcoded values that are guaranteed to be valid
|
|
pub fn new() -> Self {
|
|
Self {
|
|
configs: Vec::new(),
|
|
symbol_cache: HashMap::new(),
|
|
last_reload: Utc::now(),
|
|
reload_interval: std::time::Duration::from_secs(300), // 5 minutes
|
|
}
|
|
}
|
|
|
|
/// Load configurations from database
|
|
///
|
|
/// # Errors
|
|
///
|
|
/// Returns error if:
|
|
/// - Regex pattern compilation fails
|
|
/// - Configuration validation fails
|
|
/// - Database access fails
|
|
///
|
|
/// # Errors
|
|
/// Returns error if the operation fails
|
|
pub async fn load_configurations(
|
|
&mut self,
|
|
configs: Vec<AssetConfig>,
|
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
self.configs = configs;
|
|
// Sort by priority (highest first)
|
|
self.configs.sort_by(|a, b| b.priority.cmp(&a.priority));
|
|
|
|
// Compile regex patterns
|
|
for config in &mut self.configs {
|
|
match Regex::new(&config.symbol_pattern) {
|
|
Ok(regex) => config.compiled_pattern = Some(regex),
|
|
Err(e) => {
|
|
tracing::warn!(
|
|
"Failed to compile regex pattern '{}': {}",
|
|
config.symbol_pattern,
|
|
e
|
|
);
|
|
config.is_active = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
self.last_reload = Utc::now();
|
|
tracing::info!(
|
|
"Loaded {} asset classification configurations",
|
|
self.configs.len()
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
/// Classify a symbol using the configured rules
|
|
pub fn classify_symbol(&self, symbol: &str) -> AssetClass {
|
|
let symbol_upper = symbol.to_uppercase();
|
|
|
|
// Check cache first
|
|
if let Some(asset_class) = self.symbol_cache.get(&symbol_upper) {
|
|
return asset_class.clone();
|
|
}
|
|
|
|
// Check pattern rules in priority order
|
|
for config in &self.configs {
|
|
if !config.is_active {
|
|
continue;
|
|
}
|
|
|
|
if let Some(ref regex) = config.compiled_pattern {
|
|
if regex.is_match(&symbol_upper) {
|
|
return config.asset_class.clone();
|
|
}
|
|
}
|
|
}
|
|
|
|
AssetClass::Unknown
|
|
}
|
|
|
|
/// Get complete asset configuration for a symbol
|
|
pub fn get_asset_config(&self, symbol: &str) -> Option<&AssetConfig> {
|
|
let symbol_upper = symbol.to_uppercase();
|
|
|
|
for config in &self.configs {
|
|
if !config.is_active {
|
|
continue;
|
|
}
|
|
|
|
if let Some(ref regex) = config.compiled_pattern {
|
|
if regex.is_match(&symbol_upper) {
|
|
return Some(config);
|
|
}
|
|
}
|
|
}
|
|
|
|
None
|
|
}
|
|
|
|
/// Get volatility profile for a symbol
|
|
pub fn get_volatility_profile(&self, symbol: &str) -> Option<&VolatilityProfile> {
|
|
self.get_asset_config(symbol)
|
|
.map(|config| &config.volatility_profile)
|
|
}
|
|
|
|
/// Get trading parameters for a symbol
|
|
pub fn get_trading_parameters(&self, symbol: &str) -> Option<&TradingParameters> {
|
|
self.get_asset_config(symbol)
|
|
.map(|config| &config.trading_parameters)
|
|
}
|
|
|
|
/// Get daily volatility estimate for a symbol
|
|
pub fn get_daily_volatility(&self, symbol: &str) -> f64 {
|
|
if let Some(profile) = self.get_volatility_profile(symbol) {
|
|
#[allow(clippy::float_arithmetic)]
|
|
let result = profile.base_annual_volatility / 252.0_f64.sqrt();
|
|
result
|
|
} else {
|
|
#[allow(clippy::float_arithmetic)]
|
|
let result = 0.5 / 252.0_f64.sqrt();
|
|
result // Default high volatility
|
|
}
|
|
}
|
|
|
|
/// Get position sizing recommendation
|
|
pub fn get_position_size_recommendation(
|
|
&self,
|
|
symbol: &str,
|
|
portfolio_nav: Decimal,
|
|
) -> Option<Decimal> {
|
|
if let Some(config) = self.get_asset_config(symbol) {
|
|
let max_fraction = config
|
|
.trading_parameters
|
|
.position_limits
|
|
.max_position_fraction;
|
|
if let Some(decimal_fraction) = Decimal::from_f64_retain(max_fraction) {
|
|
portfolio_nav
|
|
.checked_mul(decimal_fraction)
|
|
.or(Some(Decimal::ZERO))
|
|
} else {
|
|
Some(Decimal::ZERO)
|
|
}
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
/// Check if symbol is within trading hours
|
|
pub fn is_trading_active(&self, symbol: &str, timestamp: DateTime<Utc>) -> bool {
|
|
if let Some(config) = self.get_asset_config(symbol) {
|
|
if let Some(ref trading_hours) = config.trading_hours {
|
|
// Simplified check - in production would need proper timezone handling
|
|
let weekday = timestamp
|
|
.weekday()
|
|
.num_days_from_sunday()
|
|
.try_into()
|
|
.unwrap_or(0u8);
|
|
trading_hours.trading_days.contains(&weekday)
|
|
} else {
|
|
true // No trading hours restriction
|
|
}
|
|
} else {
|
|
true // Default to always active for unknown symbols
|
|
}
|
|
}
|
|
|
|
/// Add explicit symbol mapping to cache
|
|
pub fn cache_symbol_mapping(&mut self, symbol: String, asset_class: AssetClass) {
|
|
self.symbol_cache.insert(symbol.to_uppercase(), asset_class);
|
|
}
|
|
|
|
/// Clear symbol cache
|
|
pub fn clear_cache(&mut self) {
|
|
self.symbol_cache.clear();
|
|
}
|
|
|
|
/// Check if configuration needs reload
|
|
pub fn needs_reload(&self) -> bool {
|
|
Utc::now().signed_duration_since(self.last_reload)
|
|
> chrono::Duration::from_std(self.reload_interval).unwrap_or_default()
|
|
}
|
|
|
|
/// Get all active configurations
|
|
pub fn get_active_configurations(&self) -> Vec<&AssetConfig> {
|
|
self.configs
|
|
.iter()
|
|
.filter(|config| config.is_active)
|
|
.collect()
|
|
}
|
|
|
|
/// Get configurations by asset class
|
|
pub fn get_configurations_by_class(&self, asset_class: &AssetClass) -> Vec<&AssetConfig> {
|
|
self.configs
|
|
.iter()
|
|
.filter(|config| config.is_active && &config.asset_class == asset_class)
|
|
.collect()
|
|
}
|
|
}
|
|
|
|
#[allow(clippy::unwrap_used)] // Default impl uses hardcoded values that are guaranteed to be valid
|
|
impl Default for AssetClassificationManager {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
/// Create default asset configurations for common instruments
|
|
#[allow(clippy::unwrap_used)] // Uses hardcoded values that are guaranteed to be valid
|
|
pub fn create_default_configurations() -> Vec<AssetConfig> {
|
|
let mut configs = Vec::new();
|
|
let now = Utc::now();
|
|
|
|
// Blue chip US equities
|
|
configs.push(AssetConfig {
|
|
id: Uuid::new_v4(),
|
|
name: "Blue Chip US Equities".to_owned(),
|
|
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_owned(),
|
|
compiled_pattern: None,
|
|
asset_class: AssetClass::Equity {
|
|
sector: EquitySector::Technology,
|
|
market_cap: MarketCapTier::LargeCap,
|
|
region: GeographicRegion::NorthAmerica,
|
|
},
|
|
volatility_profile: VolatilityProfile {
|
|
base_annual_volatility: 0.25,
|
|
stress_volatility_multiplier: 2.0,
|
|
intraday_pattern: vec![1.0_f64; 24], // Flat pattern for simplicity
|
|
volatility_persistence: 0.85,
|
|
jump_risk: JumpRiskProfile {
|
|
jump_probability: 0.02,
|
|
jump_magnitude: 0.05,
|
|
max_jump_size: 0.15,
|
|
},
|
|
},
|
|
trading_parameters: TradingParameters {
|
|
position_limits: PositionLimits {
|
|
max_position_fraction: 0.20,
|
|
max_leverage: 2.0,
|
|
concentration_limit: 0.30,
|
|
min_position_size: Decimal::from(100_i64),
|
|
},
|
|
risk_thresholds: RiskThresholds {
|
|
var_limit: 0.05,
|
|
daily_loss_limit: 0.03,
|
|
stop_loss_threshold: 0.10,
|
|
volatility_circuit_breaker: 0.05,
|
|
max_drawdown_threshold: 0.15,
|
|
},
|
|
execution_config: ExecutionConfig {
|
|
preferred_order_types: vec![OrderType::Limit, OrderType::Market],
|
|
tick_size: Decimal::new(1, 2), // 0.01
|
|
min_order_size: Decimal::from(1_i64),
|
|
max_order_size: Decimal::from(10000_i64),
|
|
time_in_force_default: TimeInForce::Day,
|
|
slippage_tolerance: 0.001,
|
|
},
|
|
market_making: None,
|
|
},
|
|
priority: 100,
|
|
is_active: true,
|
|
created_at: now,
|
|
updated_at: now,
|
|
trading_hours: Some(TradingHours {
|
|
market_open: NaiveTime::from_hms_opt(9, 30, 0).unwrap_or_default(),
|
|
market_close: NaiveTime::from_hms_opt(16, 0, 0).unwrap_or_default(),
|
|
pre_market: Some((NaiveTime::from_hms_opt(4, 0, 0).unwrap_or_default(), NaiveTime::from_hms_opt(9, 30, 0).unwrap_or_default())),
|
|
after_hours: Some((NaiveTime::from_hms_opt(16, 0, 0).unwrap_or_default(), NaiveTime::from_hms_opt(20, 0, 0).unwrap_or_default())),
|
|
timezone: "America/New_York".to_owned(),
|
|
trading_days: vec![1, 2, 3, 4, 5], // Monday-Friday
|
|
}),
|
|
settlement_config: SettlementConfig {
|
|
settlement_days: 2,
|
|
settlement_currency: "USD".to_owned(),
|
|
physical_settlement: false,
|
|
},
|
|
});
|
|
|
|
// Major cryptocurrency pairs
|
|
configs.push(AssetConfig {
|
|
id: Uuid::new_v4(),
|
|
name: "Major Cryptocurrencies".to_owned(),
|
|
symbol_pattern: "^(BTC|ETH|BTCUSD|ETHUSD|BTCUSDT|ETHUSDT).*$".to_owned(),
|
|
compiled_pattern: None,
|
|
asset_class: AssetClass::Crypto {
|
|
network: "Bitcoin".to_owned(),
|
|
crypto_type: CryptoType::Bitcoin,
|
|
market_cap_rank: Some(1),
|
|
},
|
|
volatility_profile: VolatilityProfile {
|
|
base_annual_volatility: 0.80,
|
|
stress_volatility_multiplier: 3.0,
|
|
intraday_pattern: vec![1.0_f64; 24],
|
|
volatility_persistence: 0.90,
|
|
jump_risk: JumpRiskProfile {
|
|
jump_probability: 0.05,
|
|
jump_magnitude: 0.10,
|
|
max_jump_size: 0.30,
|
|
},
|
|
},
|
|
trading_parameters: TradingParameters {
|
|
position_limits: PositionLimits {
|
|
max_position_fraction: 0.10,
|
|
max_leverage: 1.5,
|
|
concentration_limit: 0.15,
|
|
min_position_size: Decimal::new(1, 3), // 0.001
|
|
},
|
|
risk_thresholds: RiskThresholds {
|
|
var_limit: 0.10,
|
|
daily_loss_limit: 0.05,
|
|
stop_loss_threshold: 0.15,
|
|
volatility_circuit_breaker: 0.15,
|
|
max_drawdown_threshold: 0.25,
|
|
},
|
|
execution_config: ExecutionConfig {
|
|
preferred_order_types: vec![OrderType::Limit, OrderType::Market],
|
|
tick_size: Decimal::new(1, 2), // 0.01
|
|
min_order_size: Decimal::new(1, 3), // 0.001
|
|
max_order_size: Decimal::from(100_i64),
|
|
time_in_force_default: TimeInForce::GoodTillCancel,
|
|
slippage_tolerance: 0.005,
|
|
},
|
|
market_making: None,
|
|
},
|
|
priority: 90,
|
|
is_active: true,
|
|
created_at: now,
|
|
updated_at: now,
|
|
trading_hours: None, // 24/7 trading
|
|
settlement_config: SettlementConfig {
|
|
settlement_days: 0,
|
|
settlement_currency: "USD".to_owned(),
|
|
physical_settlement: true,
|
|
},
|
|
});
|
|
|
|
// Major forex pairs
|
|
configs.push(AssetConfig {
|
|
id: Uuid::new_v4(),
|
|
name: "Major Forex Pairs".to_owned(),
|
|
symbol_pattern: "^(EUR|GBP|USD|JPY|AUD|CAD|CHF|NZD)(USD|EUR|GBP|JPY)$".to_owned(),
|
|
compiled_pattern: None,
|
|
asset_class: AssetClass::Forex {
|
|
base: "EUR".to_owned(),
|
|
quote: "USD".to_owned(),
|
|
pair_type: ForexPairType::Major,
|
|
},
|
|
volatility_profile: VolatilityProfile {
|
|
base_annual_volatility: 0.12,
|
|
stress_volatility_multiplier: 2.5,
|
|
intraday_pattern: vec![1.0_f64; 24],
|
|
volatility_persistence: 0.80,
|
|
jump_risk: JumpRiskProfile {
|
|
jump_probability: 0.01,
|
|
jump_magnitude: 0.02,
|
|
max_jump_size: 0.08,
|
|
},
|
|
},
|
|
trading_parameters: TradingParameters {
|
|
position_limits: PositionLimits {
|
|
max_position_fraction: 0.30,
|
|
max_leverage: 10.0,
|
|
concentration_limit: 0.40,
|
|
min_position_size: Decimal::from(1000_i64),
|
|
},
|
|
risk_thresholds: RiskThresholds {
|
|
var_limit: 0.03,
|
|
daily_loss_limit: 0.02,
|
|
stop_loss_threshold: 0.05,
|
|
volatility_circuit_breaker: 0.03,
|
|
max_drawdown_threshold: 0.10,
|
|
},
|
|
execution_config: ExecutionConfig {
|
|
preferred_order_types: vec![OrderType::Limit, OrderType::Market],
|
|
tick_size: Decimal::new(1, 5), // 0.00001
|
|
min_order_size: Decimal::from(1000_i64),
|
|
max_order_size: Decimal::from(10_000_000_i64),
|
|
time_in_force_default: TimeInForce::GoodTillCancel,
|
|
slippage_tolerance: 0.0002_f64,
|
|
},
|
|
market_making: Some(MarketMakingConfig {
|
|
target_spread: 0.0001_f64,
|
|
max_inventory: Decimal::from(100_000_i64),
|
|
quote_size: Decimal::from(10000_i64),
|
|
refresh_frequency: std::time::Duration::from_millis(100),
|
|
}),
|
|
},
|
|
priority: 80,
|
|
is_active: true,
|
|
created_at: now,
|
|
updated_at: now,
|
|
trading_hours: None, // 24/5 trading
|
|
settlement_config: SettlementConfig {
|
|
settlement_days: 2,
|
|
settlement_currency: "USD".to_owned(),
|
|
physical_settlement: false,
|
|
},
|
|
});
|
|
|
|
configs
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[allow(clippy::unwrap_used, clippy::expect_used)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[tokio::test]
|
|
async fn test_symbol_classification() {
|
|
let mut manager = AssetClassificationManager::new();
|
|
let configs = create_default_configurations();
|
|
manager.load_configurations(configs).await.unwrap();
|
|
|
|
// Test blue chip classification
|
|
match manager.classify_symbol("AAPL") {
|
|
AssetClass::Equity {
|
|
sector: EquitySector::Technology,
|
|
..
|
|
} => (),
|
|
_ => panic!("AAPL should be classified as Technology equity"),
|
|
}
|
|
|
|
// Test crypto classification
|
|
match manager.classify_symbol("BTCUSD") {
|
|
AssetClass::Crypto {
|
|
crypto_type: CryptoType::Bitcoin,
|
|
..
|
|
} => (),
|
|
_ => panic!("BTCUSD should be classified as Bitcoin crypto"),
|
|
}
|
|
|
|
// Test unknown symbol
|
|
assert_eq!(manager.classify_symbol("UNKNOWN"), AssetClass::Unknown);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_volatility_profile() {
|
|
let mut manager = AssetClassificationManager::new();
|
|
let configs = create_default_configurations();
|
|
manager.load_configurations(configs).await.unwrap();
|
|
|
|
let profile = manager.get_volatility_profile("AAPL").unwrap();
|
|
assert_eq!(profile.base_annual_volatility, 0.25);
|
|
|
|
let daily_vol = manager.get_daily_volatility("AAPL");
|
|
assert!((daily_vol - (0.25 / 252.0_f64.sqrt())).abs() < 1e-10);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_trading_parameters() {
|
|
let mut manager = AssetClassificationManager::new();
|
|
let configs = create_default_configurations();
|
|
manager.load_configurations(configs).await.unwrap();
|
|
|
|
let params = manager.get_trading_parameters("AAPL").unwrap();
|
|
assert_eq!(params.position_limits.max_position_fraction, 0.20);
|
|
assert_eq!(params.position_limits.max_leverage, 2.0);
|
|
}
|
|
}
|