ringkernel_accnet/analytics/
mod.rs1use crate::models::AccountingNetwork;
6use std::collections::HashMap;
7
8pub struct AnalyticsEngine {
10 pub thresholds: RiskThresholds,
12}
13
14#[derive(Debug, Clone)]
16pub struct RiskThresholds {
17 pub suspense_threshold: f32,
19 pub confidence_threshold: f32,
21 pub benford_chi_sq_threshold: f64,
23 pub z_score_threshold: f64,
25}
26
27impl Default for RiskThresholds {
28 fn default() -> Self {
29 Self {
30 suspense_threshold: 0.7,
31 confidence_threshold: 0.5,
32 benford_chi_sq_threshold: 15.507,
33 z_score_threshold: 3.0,
34 }
35 }
36}
37
38impl AnalyticsEngine {
39 pub fn new() -> Self {
41 Self {
42 thresholds: RiskThresholds::default(),
43 }
44 }
45
46 pub fn with_thresholds(thresholds: RiskThresholds) -> Self {
48 Self { thresholds }
49 }
50
51 pub fn analyze(&self, network: &AccountingNetwork) -> AnalyticsSnapshot {
53 let mut snapshot = AnalyticsSnapshot {
54 overall_risk: self.calculate_overall_risk(network),
55 suspense_accounts: network.statistics.suspense_account_count,
56 gaap_violations: network.statistics.gaap_violation_count,
57 fraud_patterns: network.statistics.fraud_pattern_count,
58 ..Default::default()
59 };
60
61 for account in &network.accounts {
63 snapshot.account_risks.insert(
64 account.index,
65 RiskScore {
66 total: account.risk_score,
67 suspense_component: account.suspense_score * 0.3,
68 fraud_component: if account
69 .flags
70 .has(crate::models::AccountFlags::HAS_FRAUD_PATTERN)
71 {
72 0.5
73 } else {
74 0.0
75 },
76 confidence_component: 0.0, },
78 );
79 }
80
81 snapshot.network_health = NetworkHealth {
83 balance_check: true, coverage: network.statistics.avg_confidence,
85 connectivity: network.statistics.density,
86 };
87
88 snapshot
89 }
90
91 fn calculate_overall_risk(&self, network: &AccountingNetwork) -> f32 {
93 let n = network.accounts.len().max(1) as f32;
94
95 let suspense_ratio = network.statistics.suspense_account_count as f32 / n;
97 let violation_ratio = network.statistics.gaap_violation_count as f32 / n;
98 let fraud_ratio = network.statistics.fraud_pattern_count as f32 / n;
99 let confidence_factor = 1.0 - network.statistics.avg_confidence as f32;
100
101 (0.25 * suspense_ratio
102 + 0.30 * violation_ratio
103 + 0.35 * fraud_ratio
104 + 0.10 * confidence_factor)
105 .min(1.0)
106 }
107}
108
109impl Default for AnalyticsEngine {
110 fn default() -> Self {
111 Self::new()
112 }
113}
114
115#[derive(Debug, Clone, Default)]
117pub struct AnalyticsSnapshot {
118 pub overall_risk: f32,
120 pub suspense_accounts: usize,
122 pub gaap_violations: usize,
124 pub fraud_patterns: usize,
126 pub account_risks: HashMap<u16, RiskScore>,
128 pub network_health: NetworkHealth,
130}
131
132#[derive(Debug, Clone, Default)]
134pub struct RiskScore {
135 pub total: f32,
137 pub suspense_component: f32,
139 pub fraud_component: f32,
141 pub confidence_component: f32,
143}
144
145#[derive(Debug, Clone, Default)]
147pub struct NetworkHealth {
148 pub balance_check: bool,
150 pub coverage: f64,
152 pub connectivity: f64,
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159 use uuid::Uuid;
160
161 #[test]
162 fn test_analytics_engine() {
163 let engine = AnalyticsEngine::new();
164 let network = AccountingNetwork::new(Uuid::new_v4(), 2024, 1);
165 let snapshot = engine.analyze(&network);
166
167 assert!(snapshot.overall_risk >= 0.0 && snapshot.overall_risk <= 1.0);
168 }
169}