1use super::*;
2
3pub struct AssetManager {
5 asset_catalog: AssetCatalog,
6 price_feeds: HashMap<String, PriceFeed>,
7 market_data: MarketData,
8 asset_validator: AssetValidator,
9 price_histories: HashMap<String, Vec<f64>>,
15}
16
17pub struct AssetCatalog {
19 assets: HashMap<String, AssetInfo>,
20 asset_classes: HashMap<String, AssetClass>,
21 asset_relationships: HashMap<String, Vec<AssetRelationship>>,
22}
23
24#[derive(Debug, Clone)]
26pub struct AssetInfo {
27 pub asset_id: String,
28 pub symbol: String,
29 pub name: String,
30 pub asset_type: AssetType,
31 pub exchange: String,
32 pub currency: String,
33 pub sector: Option<String>,
34 pub industry: Option<String>,
35 pub market_cap: Option<f64>,
36 pub description: String,
37}
38
39#[derive(Debug, Clone)]
41pub struct AssetClass {
42 pub class_id: String,
43 pub class_name: String,
44 pub class_type: AssetType,
45 pub characteristics: Vec<String>,
46 pub risk_level: RiskLevel,
47}
48
49#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
51pub enum RiskLevel {
52 Low,
53 Medium,
54 High,
55 VeryHigh,
56}
57
58#[derive(Debug, Clone)]
60pub struct AssetRelationship {
61 pub relationship_id: String,
62 pub source_asset: String,
63 pub target_asset: String,
64 pub relationship_type: AssetRelationshipType,
65 pub correlation: f64,
66}
67
68#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
70pub enum AssetRelationshipType {
71 Correlation,
72 Causation,
73 Substitution,
74 Complement,
75 Derivative,
76}
77
78#[derive(Debug, Clone)]
80pub struct PriceFeed {
81 pub feed_id: String,
82 pub feed_name: String,
83 pub feed_type: FeedType,
84 pub update_frequency: u64,
85 pub data_quality: DataQuality,
86 pub last_update: u64,
87 pub asset_id: String,
90 pub cached_prices: Vec<f64>,
95}
96
97#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
99pub enum FeedType {
100 RealTime,
101 Delayed,
102 EndOfDay,
103 Historical,
104}
105
106#[derive(Debug, Clone)]
108pub struct DataQuality {
109 pub accuracy: f64,
110 pub completeness: f64,
111 pub timeliness: f64,
112 pub consistency: f64,
113}
114
115pub struct MarketData {
117 price_data: HashMap<String, PriceData>,
118 volume_data: HashMap<String, VolumeData>,
119 technical_indicators: HashMap<String, TechnicalIndicators>,
120}
121
122#[derive(Debug, Clone)]
124pub struct PriceData {
125 pub asset_id: String,
126 pub timestamp: u64,
127 pub open: f64,
128 pub high: f64,
129 pub low: f64,
130 pub close: f64,
131 pub adjusted_close: f64,
132 pub volume: u64,
133}
134
135#[derive(Debug, Clone)]
137pub struct VolumeData {
138 pub asset_id: String,
139 pub timestamp: u64,
140 pub volume: u64,
141 pub bid_volume: u64,
142 pub ask_volume: u64,
143}
144
145#[derive(Debug, Clone)]
147pub struct TechnicalIndicators {
148 pub asset_id: String,
149 pub timestamp: u64,
150 pub moving_averages: HashMap<String, f64>,
151 pub oscillators: HashMap<String, f64>,
152 pub volatility: HashMap<String, f64>,
153}
154
155pub struct AssetValidator {
157 validation_rules: Vec<ValidationRule>,
158 compliance_checker: ComplianceChecker,
159 risk_assessor: RiskAssessor,
160}
161
162#[derive(Debug, Clone)]
164pub struct ValidationRule {
165 pub rule_id: String,
166 pub rule_type: ValidationRuleType,
167 pub condition: String,
168 pub action: ValidationAction,
169}
170
171#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
173pub enum ValidationRuleType {
174 Price,
175 Volume,
176 Liquidity,
177 MarketCap,
178 Regulatory,
179}
180
181#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
183pub enum ValidationAction {
184 Accept,
185 Reject,
186 Flag,
187 Review,
188}
189
190pub struct ComplianceChecker {
192 compliance_rules: Vec<ComplianceRule>,
193 regulatory_frameworks: Vec<RegulatoryFramework>,
194 screening_lists: HashMap<String, ScreeningList>,
195}
196
197#[derive(Debug, Clone)]
206pub struct ComplianceRule {
207 pub rule_id: String,
208 pub rule_type: ComplianceRuleType,
209 pub parameters: HashMap<String, f64>,
210 pub string_parameters: HashMap<String, String>,
213 pub description: String,
214}
215
216#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
218pub enum ComplianceRuleType {
219 PositionLimit,
221 KYC,
223 AML,
225 MarginRequirement,
227 TradingRestriction,
229 Custom,
231}
232
233#[derive(Debug, Clone)]
235pub struct ComplianceCondition {
236 pub condition_id: String,
237 pub field: String,
238 pub operator: ComparisonOperator,
239 pub value: ComplianceValue,
240}
241
242#[derive(Debug, Clone)]
244pub enum ComplianceValue {
245 String(String),
246 Number(f64),
247 Boolean(bool),
248 Array(Vec<ComplianceValue>),
249}
250
251#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
253pub enum ComparisonOperator {
254 Equals,
255 NotEquals,
256 GreaterThan,
257 LessThan,
258 Contains,
259 Matches,
260}
261
262#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
264pub enum ComplianceAction {
265 Approve,
266 Reject,
267 Flag,
268 Escalate,
269 Report,
270}
271
272#[derive(Debug, Clone)]
274pub struct RegulatoryFramework {
275 pub framework_id: String,
276 pub framework_name: String,
277 pub jurisdiction: String,
278 pub requirements: Vec<RegulatoryRequirement>,
279}
280
281#[derive(Debug, Clone)]
283pub struct RegulatoryRequirement {
284 pub requirement_id: String,
285 pub requirement_type: RequirementType,
286 pub description: String,
287 pub mandatory: bool,
288}
289
290#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
292pub enum RequirementType {
293 Reporting,
294 Disclosure,
295 Capital,
296 Risk,
297 Operational,
298}
299
300#[derive(Debug, Clone)]
302pub struct ScreeningList {
303 pub list_id: String,
304 pub list_name: String,
305 pub list_type: ScreeningListType,
306 pub entries: Vec<ScreeningEntry>,
307}
308
309#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
311pub enum ScreeningListType {
312 Sanctions,
313 PEP,
314 WatchList,
315 DeniedPersons,
316}
317
318#[derive(Debug, Clone)]
320pub struct ScreeningEntry {
321 pub entry_id: String,
322 pub name: String,
323 pub aliases: Vec<String>,
324 pub date_of_birth: Option<String>,
325 pub nationality: Option<String>,
326 pub reason: String,
327}
328
329impl AssetManager {
330 pub fn new() -> Self {
331 Self {
332 asset_catalog: AssetCatalog::new(),
333 price_feeds: HashMap::new(),
334 market_data: MarketData::new(),
335 asset_validator: AssetValidator::new(),
336 price_histories: HashMap::new(),
337 }
338 }
339
340 pub fn initialize(&mut self) -> Result<(), FinancialError> {
341 self.asset_catalog.initialize()?;
342 self.asset_validator.initialize()?;
343 Ok(())
344 }
345
346 pub fn register_price_feed(&mut self, feed: PriceFeed) {
350 self.price_feeds.insert(feed.asset_id.clone(), feed);
351 }
352
353 pub fn update_price_history(&mut self, asset_id: &str, prices: Vec<f64>) {
358 self.price_histories.insert(asset_id.to_string(), prices);
359 }
360
361 pub fn get_price_history(&self, asset_id: &str) -> Option<&Vec<f64>> {
363 self.price_histories.get(asset_id)
364 }
365
366 pub fn ingest_from_feed(&mut self, asset_id: &str) -> Result<(), FinancialError> {
373 let feed = self.price_feeds.get(asset_id).cloned().ok_or_else(|| {
374 FinancialError::DataError(format!("no price feed registered for asset '{}'", asset_id))
375 })?;
376
377 let prices = if !feed.cached_prices.is_empty() {
378 feed.cached_prices.clone()
379 } else {
380 deterministic_price_series(&feed.feed_id, 30)
381 };
382 self.price_histories.insert(asset_id.to_string(), prices);
383 Ok(())
384 }
385
386 pub fn apply_to_asset(&self, asset: &mut Asset) {
390 if let Some(prices) = self.price_histories.get(&asset.asset_id) {
391 asset.price_history = prices.clone();
392 if let Some(&last) = prices.last() {
393 asset.current_price = last;
394 asset.market_value = asset.quantity * last;
395 }
396 }
397 }
398
399 pub fn market_data(&self) -> &MarketData {
400 &self.market_data
401 }
402
403 pub fn market_data_mut(&mut self) -> &mut MarketData {
404 &mut self.market_data
405 }
406}
407
408fn deterministic_price_series(seed: &str, len: usize) -> Vec<f64> {
413 let mut state: u64 = 0xcbf2_9ce4_8422_2325;
415 for &b in seed.as_bytes() {
416 state ^= b as u64;
417 state = state.wrapping_mul(0x1000_0000_01b3);
418 }
419 if state == 0 {
420 state = 0x9e37_79b9_7f4a_7c15;
421 }
422
423 let mut prices = Vec::with_capacity(len);
424 let mut price = 100.0;
425 for _ in 0..len {
426 state ^= state << 13;
428 state ^= state >> 7;
429 state ^= state << 17;
430 let step = ((state >> 33) as f64) / (i32::MAX as f64) * 1.5;
432 price = (price + step).max(1.0);
433 prices.push(price);
434 }
435 prices
436}
437
438impl AssetCatalog {
439 pub fn new() -> Self {
440 Self {
441 assets: HashMap::new(),
442 asset_classes: HashMap::new(),
443 asset_relationships: HashMap::new(),
444 }
445 }
446
447 pub fn register_asset(&mut self, asset: AssetInfo) {
450 self.assets.insert(asset.asset_id.clone(), asset);
451 }
452
453 pub fn get_asset(&self, asset_id: &str) -> Option<&AssetInfo> {
455 self.assets.get(asset_id)
456 }
457
458 pub fn add_relationship(
466 &mut self,
467 source_asset: &str,
468 target_asset: &str,
469 relationship: AssetRelationship,
470 ) {
471 let _ = target_asset; self.asset_relationships
473 .entry(source_asset.to_string())
474 .or_default()
475 .push(relationship);
476 }
477
478 pub fn get_relationships(&self, asset_id: &str) -> Vec<&AssetRelationship> {
480 self.asset_relationships
481 .get(asset_id)
482 .map(|rels| rels.iter().collect())
483 .unwrap_or_default()
484 }
485
486 pub fn get_related_assets(&self, asset_id: &str) -> Vec<String> {
489 self.asset_relationships
490 .get(asset_id)
491 .map(|rels| rels.iter().map(|r| r.target_asset.clone()).collect())
492 .unwrap_or_default()
493 }
494
495 pub fn relationship_count(&self) -> usize {
497 self.asset_relationships
498 .values()
499 .map(|rels| rels.len())
500 .sum()
501 }
502
503 pub fn register_asset_class(&mut self, class_id: &str, asset_class: AssetClass) {
508 self.asset_classes.insert(class_id.to_string(), asset_class);
509 }
510
511 pub fn classify_asset(&mut self, asset_id: &str, class_id: &str) -> Result<(), FinancialError> {
517 if !self.assets.contains_key(asset_id) {
518 return Err(FinancialError::AssetError(format!(
519 "asset '{}' is not registered in the catalog",
520 asset_id
521 )));
522 }
523 let class = self.asset_classes.get_mut(class_id).ok_or_else(|| {
524 FinancialError::AssetError(format!("asset class '{}' is not registered", class_id))
525 })?;
526 if !class.characteristics.iter().any(|c| c == asset_id) {
527 class.characteristics.push(asset_id.to_string());
528 }
529 Ok(())
530 }
531
532 pub fn get_asset_class(&self, class_id: &str) -> Option<&AssetClass> {
534 self.asset_classes.get(class_id)
535 }
536
537 pub fn get_assets_by_class(&self, class_id: &str) -> Vec<String> {
543 match self.asset_classes.get(class_id) {
544 Some(class) => class
545 .characteristics
546 .iter()
547 .filter(|c| self.assets.contains_key(*c))
548 .cloned()
549 .collect(),
550 None => Vec::new(),
551 }
552 }
553
554 pub fn list_asset_classes(&self) -> Vec<String> {
556 self.asset_classes.keys().cloned().collect()
557 }
558
559 pub fn initialize(&mut self) -> Result<(), FinancialError> {
563 let standards: &[(&str, &str, AssetType, RiskLevel, &[&str])] = &[
564 (
565 "equity",
566 "Equity",
567 AssetType::Stock,
568 RiskLevel::Medium,
569 &["Stocks", "Shares"],
570 ),
571 (
572 "fixed_income",
573 "Fixed Income",
574 AssetType::Bond,
575 RiskLevel::Low,
576 &["Bonds", "Debt instruments"],
577 ),
578 (
579 "commodity",
580 "Commodity",
581 AssetType::Commodity,
582 RiskLevel::High,
583 &["Physical goods", "Futures"],
584 ),
585 (
586 "real_estate",
587 "Real Estate",
588 AssetType::RealEstate,
589 RiskLevel::Medium,
590 &["Property", "Land"],
591 ),
592 (
593 "cash",
594 "Cash",
595 AssetType::Currency,
596 RiskLevel::Low,
597 &["Currency", "Money market"],
598 ),
599 (
600 "derivative",
601 "Derivative",
602 AssetType::Derivative,
603 RiskLevel::VeryHigh,
604 &["Options", "Futures", "Swaps"],
605 ),
606 (
607 "cryptocurrency",
608 "Cryptocurrency",
609 AssetType::Cryptocurrency,
610 RiskLevel::VeryHigh,
611 &["Digital assets", "Tokens"],
612 ),
613 ];
614 for (id, name, ty, risk, chars) in standards {
615 self.register_asset_class(
616 id,
617 AssetClass {
618 class_id: id.to_string(),
619 class_name: name.to_string(),
620 class_type: ty.clone(),
621 characteristics: chars.iter().map(|s| s.to_string()).collect(),
622 risk_level: risk.clone(),
623 },
624 );
625 }
626 Ok(())
627 }
628}
629
630impl MarketData {
631 pub fn new() -> Self {
632 Self {
633 price_data: HashMap::new(),
634 volume_data: HashMap::new(),
635 technical_indicators: HashMap::new(),
636 }
637 }
638
639 pub fn sync_to_assets(&self, assets: &mut HashMap<String, Asset>) {
647 for asset in assets.values_mut() {
648 if let Some(pd) = self.price_data.get(&asset.asset_id) {
649 let px = if pd.adjusted_close != 0.0 {
652 pd.adjusted_close
653 } else {
654 pd.close
655 };
656 asset.price_history = vec![px];
657 asset.current_price = px;
658 asset.market_value = asset.quantity * px;
659 }
660 }
661 }
662
663 pub fn upsert_price_data(&mut self, data: PriceData) {
666 self.price_data.insert(data.asset_id.clone(), data);
667 }
668
669 pub fn upsert_volume_data(&mut self, data: VolumeData) {
670 self.volume_data.insert(data.asset_id.clone(), data);
671 }
672
673 pub fn get_volume_data(&self, asset_id: &str) -> Option<&VolumeData> {
674 self.volume_data.get(asset_id)
675 }
676
677 pub fn upsert_technical_indicators(&mut self, indicators: TechnicalIndicators) {
678 self.technical_indicators
679 .insert(indicators.asset_id.clone(), indicators);
680 }
681
682 pub fn get_technical_indicators(&self, asset_id: &str) -> Option<&TechnicalIndicators> {
683 self.technical_indicators.get(asset_id)
684 }
685}
686
687impl AssetValidator {
688 pub fn new() -> Self {
689 Self {
690 validation_rules: Vec::new(),
691 compliance_checker: ComplianceChecker::new(),
692 risk_assessor: RiskAssessor::new(),
693 }
694 }
695
696 pub fn initialize(&mut self) -> Result<(), FinancialError> {
697 self.compliance_checker.initialize()?;
698 self.risk_assessor.initialize()?;
699 Ok(())
700 }
701
702 pub fn add_validation_rule(&mut self, rule: ValidationRule) {
703 self.validation_rules.push(rule);
704 }
705
706 pub fn list_validation_rules(&self) -> &[ValidationRule] {
707 &self.validation_rules
708 }
709
710 pub fn validation_rule_count(&self) -> usize {
711 self.validation_rules.len()
712 }
713}
714
715impl ComplianceChecker {
716 pub fn new() -> Self {
717 Self {
718 compliance_rules: Vec::new(),
719 regulatory_frameworks: Vec::new(),
720 screening_lists: HashMap::new(),
721 }
722 }
723
724 pub fn initialize(&mut self) -> Result<(), FinancialError> {
725 Ok(())
726 }
727
728 pub fn add_compliance_rule(&mut self, rule: ComplianceRule) {
729 self.compliance_rules.push(rule);
730 }
731
732 pub fn list_compliance_rules(&self) -> &[ComplianceRule] {
733 &self.compliance_rules
734 }
735
736 pub fn add_regulatory_framework(&mut self, framework: RegulatoryFramework) {
737 self.regulatory_frameworks.push(framework);
738 }
739
740 pub fn list_regulatory_frameworks(&self) -> &[RegulatoryFramework] {
741 &self.regulatory_frameworks
742 }
743
744 pub fn add_screening_list(&mut self, list: ScreeningList) {
745 self.screening_lists.insert(list.list_id.clone(), list);
746 }
747
748 pub fn get_screening_list(&self, list_id: &str) -> Option<&ScreeningList> {
749 self.screening_lists.get(list_id)
750 }
751
752 pub fn list_screening_lists(&self) -> Vec<String> {
753 self.screening_lists.keys().cloned().collect()
754 }
755}
756
757impl ComplianceRule {
758 pub(super) fn rule_type_as_str(&self) -> &'static str {
760 match self.rule_type {
761 ComplianceRuleType::PositionLimit => "PositionLimit",
762 ComplianceRuleType::KYC => "KYC",
763 ComplianceRuleType::AML => "AML",
764 ComplianceRuleType::MarginRequirement => "MarginRequirement",
765 ComplianceRuleType::TradingRestriction => "TradingRestriction",
766 ComplianceRuleType::Custom => "Custom",
767 }
768 }
769}
770
771impl AssetInfo {
772 pub fn new() -> Self {
773 Self {
774 asset_id: "asset_1".to_string(),
775 symbol: "AAPL".to_string(),
776 name: "Apple Inc.".to_string(),
777 asset_type: AssetType::Stock,
778 exchange: "NASDAQ".to_string(),
779 currency: "USD".to_string(),
780 sector: Some("Technology".to_string()),
781 industry: Some("Consumer Electronics".to_string()),
782 market_cap: Some(3000000000000.0),
783 description: "Apple Inc. is a technology company".to_string(),
784 }
785 }
786}
787
788impl AssetClass {
789 pub fn new() -> Self {
790 Self {
791 class_id: "class_1".to_string(),
792 class_name: "US Equities".to_string(),
793 class_type: AssetType::Stock,
794 characteristics: vec!["US listed".to_string(), "Large cap".to_string()],
795 risk_level: RiskLevel::Medium,
796 }
797 }
798}
799
800impl AssetRelationship {
801 pub fn new() -> Self {
802 Self {
803 relationship_id: "rel_1".to_string(),
804 source_asset: "AAPL".to_string(),
805 target_asset: "MSFT".to_string(),
806 relationship_type: AssetRelationshipType::Correlation,
807 correlation: 0.7,
808 }
809 }
810}
811
812impl PriceFeed {
813 pub fn new() -> Self {
814 Self {
815 feed_id: "feed_1".to_string(),
816 feed_name: "Real-time feed".to_string(),
817 feed_type: FeedType::RealTime,
818 update_frequency: 1,
819 data_quality: DataQuality::new(),
820 last_update: 0,
821 asset_id: "asset_1".to_string(),
822 cached_prices: Vec::new(),
823 }
824 }
825}
826
827impl DataQuality {
828 pub fn new() -> Self {
829 Self {
830 accuracy: 0.0,
832 completeness: 0.0,
833 timeliness: 0.0,
834 consistency: 0.0,
835 }
836 }
837}
838
839impl PriceData {
840 pub fn new() -> Self {
841 Self {
842 asset_id: "asset_1".to_string(),
843 timestamp: 0,
844 open: 150.0,
845 high: 155.0,
846 low: 149.0,
847 close: 154.0,
848 adjusted_close: 154.0,
849 volume: 1000000,
850 }
851 }
852}
853
854impl VolumeData {
855 pub fn new() -> Self {
856 Self {
857 asset_id: "asset_1".to_string(),
858 timestamp: 0,
859 volume: 1000000,
860 bid_volume: 500000,
861 ask_volume: 500000,
862 }
863 }
864}
865
866impl TechnicalIndicators {
867 pub fn new() -> Self {
868 Self {
869 asset_id: "asset_1".to_string(),
870 timestamp: 0,
871 moving_averages: HashMap::new(),
872 oscillators: HashMap::new(),
873 volatility: HashMap::new(),
874 }
875 }
876}
877
878impl ValidationRule {
879 pub fn new() -> Self {
880 Self {
881 rule_id: "rule_1".to_string(),
882 rule_type: ValidationRuleType::Price,
883 condition: "price > 0".to_string(),
884 action: ValidationAction::Accept,
885 }
886 }
887}
888
889impl ComplianceCondition {
890 pub fn new() -> Self {
891 Self {
892 condition_id: "cond_1".to_string(),
893 field: "price".to_string(),
894 operator: ComparisonOperator::GreaterThan,
895 value: ComplianceValue::Number(0.0),
896 }
897 }
898}
899
900impl ComplianceRule {
901 pub fn new() -> Self {
902 Self {
903 rule_id: "rule_1".to_string(),
904 rule_type: ComplianceRuleType::PositionLimit,
905 parameters: HashMap::from([("max_position".to_string(), 1000.0)]),
906 string_parameters: HashMap::new(),
907 description: "Default position-limit rule".to_string(),
908 }
909 }
910}
911
912impl RegulatoryFramework {
913 pub fn new() -> Self {
914 Self {
915 framework_id: "framework_1".to_string(),
916 framework_name: "SEC".to_string(),
917 jurisdiction: "US".to_string(),
918 requirements: vec![RegulatoryRequirement::new()],
919 }
920 }
921}
922
923impl RegulatoryRequirement {
924 pub fn new() -> Self {
925 Self {
926 requirement_id: "req_1".to_string(),
927 requirement_type: RequirementType::Reporting,
928 description: "Must report trades".to_string(),
929 mandatory: true,
930 }
931 }
932}
933
934impl ScreeningList {
935 pub fn new() -> Self {
936 Self {
937 list_id: "list_1".to_string(),
938 list_name: "Sanctions list".to_string(),
939 list_type: ScreeningListType::Sanctions,
940 entries: vec![ScreeningEntry::new()],
941 }
942 }
943}
944
945impl ScreeningEntry {
946 pub fn new() -> Self {
947 Self {
948 entry_id: "entry_1".to_string(),
949 name: "Test Entity".to_string(),
950 aliases: vec!["Alias 1".to_string()],
951 date_of_birth: Some("1980-01-01".to_string()),
952 nationality: Some("US".to_string()),
953 reason: "Test reason".to_string(),
954 }
955 }
956}