Financial Risk Ontology Modeling: Real-Time Risk Sensing and Relationship Network Analysis
Financial domain relationship complexity far exceeds other industries:
CoomiaPublished on August 21, 202513 min read
Share this articleTwitter / X
Financial Risk Ontology Modeling: Real-Time Risk Sensing and Relationship Network Analysis
“Series: S4 Ontology Modeling · Article 17 | Level: Intermediate | Reading Time: 18 min
#TL;DR
- Financial risk Ontology centers on the "Customer-Account-Transaction" triad — individuals, corporations, accounts, transactions, products, and risk events form a relationship network. coomia-dip uses RelationType to express complex financial semantics like guarantee relationships, fund flows, and corporate associations.
- Derived properties enable real-time risk metric computation — customer credit scores, account anomaly detection, and product risk exposure are all auto-computed via derived properties, updating in milliseconds when data changes, eliminating "T+1 risk reporting" delays.
- Relationship graph traversal discovers hidden risks — 2-3 hop traversals reveal "seemingly unrelated but actually connected" customer clusters, fund cycles, and guarantee chains, delivering risk management capabilities impossible with traditional rule engines.
#1. Unique Challenges of Financial Data
#1.1 Relationship Complexity
Code
Financial domain relationship complexity far exceeds other industries:
Individual customer Zhang San:
├── Holds 3 accounts (savings, credit card, investment)
├── Is legal representative of Company A
├── Is shareholder of Company B (30%)
├── Provided guarantee for Company C
├── Spouse Li Si is also a customer
├── Li Si is legal representative of Company D
├── Company A and Company D have fund transfers
└── Company B provides guarantee for Company A
Problem: Zhang San's loan risk depends not just on personal credit,
but also on all related companies, guarantee chains, and family members.
Traditional approach: Each relationship in different systems, analysis requires manual correlation
coomia-dip: All relationships in unified Ontology model, one query handles everything
#2. Core ObjectType Design
#2.1 Customer Model
Code
ObjectType: Individual
properties:
customerId: STRING (PK)
name: STRING
idNumber: STRING (SENSITIVE)
phone: STRING
email: STRING
occupation: STRING
annualIncome: DECIMAL
riskPreference: ENUM [CONSERVATIVE, MODERATE, AGGRESSIVE]
kycStatus: ENUM [PENDING, VERIFIED, REJECTED, EXPIRED]
kycVerifiedAt: TIMESTAMP
accounts: RELATION[] → Account
relatedCompanies: RELATION[] → CorporateRelation
familyMembers: RELATION[] → FamilyRelation
metrics:
monthlyTransactionVolume: METRIC(counter, source=core_banking)
avgDailyBalance: METRIC(gauge, source=core_banking)
loginFrequency: METRIC(counter, source=mobile_banking)
derivedProperties:
creditScore: DERIVED
expression: |
baseScore(annualIncome, occupation) * 0.3 +
paymentHistoryScore(accounts) * 0.35 +
utilizationScore(accounts) * 0.15 +
accountAgeScore(accounts) * 0.1 +
inquiryScore(creditInquiries) * 0.1
riskLevel: CASE
WHEN creditScore >= 750 THEN "LOW"
WHEN creditScore >= 600 THEN "MEDIUM"
WHEN creditScore >= 400 THEN "HIGH"
ELSE "VERY_HIGH"
END
totalAssets: SUM(Account.balance WHERE accountType IN ("SAVINGS","INVESTMENT"))
totalLiabilities: SUM(Account.balance WHERE accountType IN ("CREDIT","LOAN"))
debtToIncomeRatio: totalLiabilities / annualIncome
ObjectType: Corporate
properties:
companyId: STRING (PK)
companyName: STRING
registrationNumber: STRING
industry: STRING
registeredCapital: DECIMAL
establishedDate: DATE
legalRepresentative: RELATION → Individual
shareholders: RELATION[] → ShareholderRelation
accounts: RELATION[] → Account
guarantees: RELATION[] → GuaranteeRelation
metrics:
revenue: METRIC(counter, source=financial_reports)
netProfit: METRIC(gauge, source=financial_reports)
assetTurnoverRatio: METRIC(gauge, source=computed)
derivedProperties:
companyRiskScore: DERIVED
expression: |
financialHealthScore(revenue, netProfit) * 0.3 +
industryRiskScore(industry) * 0.2 +
guaranteeExposure(guarantees) * 0.2 +
paymentHistoryScore(accounts) * 0.3
isHighRisk: companyRiskScore < 50
totalGuaranteeAmount: SUM(GuaranteeRelation.amount)
guaranteeConcentration: MAX(GuaranteeRelation.amount) / totalGuaranteeAmount
#2.2 Accounts and Transactions
Code
ObjectType: Account
properties:
accountId: STRING (PK)
accountType: ENUM [SAVINGS, CHECKING, CREDIT, LOAN, INVESTMENT, MARGIN]
owner: RELATION → Individual | Corporate
currency: STRING
balance: DECIMAL
creditLimit: DECIMAL
interestRate: DECIMAL
openDate: DATE
status: ENUM [ACTIVE, FROZEN, CLOSED, DORMANT]
branch: RELATION → Branch
metrics:
dailyTransactionCount: METRIC(counter, source=core_banking)
dailyTransactionVolume: METRIC(counter, source=core_banking)
monthlyAvgBalance: METRIC(gauge, source=core_banking)
derivedProperties:
utilizationRate: balance / creditLimit (credit accounts)
isOverLimit: balance > creditLimit
dormantDays: TODAY() - lastTransactionDate
isDormant: dormantDays > 180
averageTransactionSize: dailyTransactionVolume / dailyTransactionCount
ObjectType: Transaction
properties:
transactionId: STRING (PK)
fromAccount: RELATION → Account
toAccount: RELATION → Account
amount: DECIMAL
currency: STRING
transactionType: ENUM [TRANSFER, PAYMENT, WITHDRAWAL, DEPOSIT, FX, INVESTMENT]
channel: ENUM [COUNTER, ATM, MOBILE, ONLINE, API]
timestamp: TIMESTAMP
status: ENUM [PENDING, COMPLETED, FAILED, REVERSED]
description: STRING
location: STRING
deviceId: STRING
ipAddress: STRING (SENSITIVE)
derivedProperties:
isLargeTransaction: amount > threshold(fromAccount.accountType)
isCrossBorder: fromAccount.currency != toAccount.currency
isUnusualTime: HOUR(timestamp) < 6 OR HOUR(timestamp) > 23
riskScore: DERIVED
expression: |
baseRisk(transactionType, amount) +
(IF(isLargeTransaction, 20, 0)) +
(IF(isCrossBorder, 15, 0)) +
(IF(isUnusualTime, 10, 0)) +
velocityRisk(fromAccount, "1h") +
locationRisk(location, fromAccount.owner)
#2.3 Risk Relationship Model
Code
RelationType: GuaranteeRelation
properties:
guarantor: RELATION → Individual | Corporate
borrower: RELATION → Individual | Corporate
guaranteeType: ENUM [JOINT, COLLATERAL, PLEDGE, LETTER_OF_CREDIT]
amount: DECIMAL
collateralType: STRING
collateralValue: DECIMAL
startDate: DATE
endDate: DATE
status: ENUM [ACTIVE, RELEASED, DEFAULTED]
RelationType: ShareholderRelation
properties:
shareholder: RELATION → Individual | Corporate
company: RELATION → Corporate
shareholdingPercentage: DECIMAL
shareType: ENUM [COMMON, PREFERRED]
beneficialOwner: BOOLEAN
RelationType: FamilyRelation
properties:
person1: RELATION → Individual
person2: RELATION → Individual
relationship: ENUM [SPOUSE, PARENT, CHILD, SIBLING]
RelationType: FundFlow
properties:
from: RELATION → Account
to: RELATION → Account
totalAmount: DECIMAL
transactionCount: INT
timeRange: STRING
flowType: ENUM [REGULAR, OCCASIONAL, SUSPICIOUS]
#3. Real-Time Risk Metric Computation
#3.1 Customer-Level Risk Dashboard
Code
Individual's risk metric DAG:
Level 0 (source data):
Transaction records, account balances, personal info, external credit bureau
Level 1 (account-level derived):
Account.utilizationRate
Account.overdueDays
Account.averageTransactionSize
Level 2 (customer-level aggregation):
Individual.totalAssets = SUM(Account.balance WHERE type IN savings/investment)
Individual.totalLiabilities = SUM(Account.balance WHERE type IN credit/loan)
Individual.maxOverdueDays = MAX(Account.overdueDays)
Level 3 (customer-level derived):
Individual.debtToIncomeRatio = totalLiabilities / annualIncome
Individual.creditScore = weighted_sum(...)
Individual.riskLevel = CASE(creditScore)
Level 4 (association risk):
Individual.guaranteeExposure = SUM(GuaranteeRelation.amount)
Individual.relatedCompanyRisk = MAX(Corporate.companyRiskScore)
Level 5 (composite risk):
Individual.compositeRiskScore = creditScore * 0.6 + guaranteeRisk * 0.2 + companyRisk * 0.2
Cascade update on transaction:
New transaction → balance changes → utilizationRate → creditScore → riskLevel
Full chain millisecond update, no waiting for T+1 reports
#3.2 Anti-Money Laundering (AML) Metrics
Code
AML-related derived properties:
ObjectType: Individual (AML extensions)
derivedProperties:
# Transaction velocity detection
transactionVelocity_1h: COUNT(Transaction WHERE timestamp > NOW() - 1h)
transactionVelocity_24h: COUNT(Transaction WHERE timestamp > NOW() - 24h)
isHighVelocity: transactionVelocity_1h > 10 OR transactionVelocity_24h > 50
# Structuring / Smurfing detection
nearThresholdTransactions: COUNT(Transaction WHERE amount BETWEEN 9000 AND 10000 AND timestamp > NOW() - 7d)
isStructuring: nearThresholdTransactions >= 3
# Cross-border transaction patterns
crossBorderFrequency: COUNT(Transaction WHERE isCrossBorder AND timestamp > NOW() - 30d)
highRiskCountryTransactions: COUNT(Transaction WHERE toAccount.country IN highRiskCountries)
# Fund cycle detection (via relation traversal)
hasFundCycle: EXISTS(
TRAVERSE fromAccount → Transaction.toAccount → Transaction.fromAccount
WHERE depth <= 3 AND endpoint == startpoint
)
# Composite AML risk
amlRiskScore: DERIVED
expression: |
(IF(isHighVelocity, 25, 0)) +
(IF(isStructuring, 30, 0)) +
(IF(crossBorderFrequency > 10, 15, 0)) +
(IF(highRiskCountryTransactions > 0, 20, 0)) +
(IF(hasFundCycle, 30, 0))
amlAlert: amlRiskScore >= 50
Alert Action:
ActionType: CreateSAR (Suspicious Activity Report)
trigger: amlAlert == true
parameters:
customerId: REQUIRED
alertType: ENUM [STRUCTURING, HIGH_VELOCITY, FUND_CYCLE, HIGH_RISK_COUNTRY]
assignedAnalyst: RELATION → Employee
dueDate: NOW() + 5d
guards:
- NOT EXISTS(SAR WHERE customerId = this.customerId AND status = "OPEN")
#4. Relationship Graph Analysis
#4.1 Associated Customer Discovery
Code
Discover hidden associations via relationship traversal:
Query: Find all customers associated with "Zhang San"
API: POST /api/v1/objects/Individual/zhang-san/graph
{
"maxDepth": 3,
"relationTypes": ["FamilyRelation", "ShareholderRelation", "GuaranteeRelation"],
"includeProperties": ["name", "creditScore", "riskLevel"]
}
Result:
Zhang San (creditScore: 720, riskLevel: MEDIUM)
├── [Spouse] Li Si (creditScore: 680, riskLevel: MEDIUM)
│ └── [Legal Rep] Company D (riskScore: 45, isHighRisk: true)
│ └── [Fund Transfer] Company A
├── [Legal Rep] Company A (riskScore: 72)
│ └── [Guarantee] ← Company B (riskScore: 55)
├── [Shareholder 30%] Company B (riskScore: 55)
│ └── [Guarantee] → Company A
└── [Guarantor] → Company C (riskScore: 38, isHighRisk: true)
Risk findings:
├── Zhang San's spouse Li Si is linked to high-risk company (Company D)
├── Company A and Company B have mutual guarantee relationship
├── Company C is high-risk, Zhang San guarantees it
└── If Company C defaults, Zhang San bears guarantee liability
→ This affects Zhang San's credit score and loan approval
#4.2 Guarantee Chain Analysis
Code
Guarantee chain risk analysis:
Query: Analyze Company A's guarantee chain
API: POST /api/v1/objects/Corporate/company-a/trace
{
"relationType": "GuaranteeRelation",
"direction": "BOTH",
"maxDepth": 5,
"includeRiskScores": true
}
Result:
Guarantee chain visualization:
Company E (riskScore: 82)
↓ guarantee $500K
Company B (riskScore: 55)
↓ guarantee $1M
Company A (riskScore: 72) ← analysis target
↓ guarantee $800K
Company C (riskScore: 38) ← HIGH RISK!
↓ guarantee $600K
Company F (riskScore: 25) ← HIGH RISK!
Risk analysis:
├── Chain depth: 5 layers
├── High-risk entities on chain: 2 (Company C, Company F)
├── Company A's downstream guarantee exposure: $800K (direct) + $600K (indirect) = $1.4M
├── If Company F defaults: Company C must cover $600K
│ → Company C likely cannot cover (riskScore=38)
│ → Company A must cover for Company C: $800K
│ → Cascade risk total: $1.4M
└── Recommendation: Reduce Company A's credit limit, require additional collateral
#4.3 Fund Flow Analysis
Code
Fund flow network analysis:
Query: Analyze fund flow network for Account A001
API: POST /api/v1/objects/Account/A001/fund-flow
{
"timeRange": {"from": "2025-01-01", "to": "2025-01-31"},
"minAmount": 100000,
"maxDepth": 3,
"detectCycles": true
}
Result:
A001 (Zhang San savings)
├──→ A002 (Company A corporate) $500K x 3 times
│ ├──→ A005 (Company D corporate) $300K x 2 times
│ │ └──→ A003 (Li Si savings) $200K x 1 time
│ │ └──→ A001 (Zhang San savings) $150K x 1 time ← FUND CYCLE!
│ └──→ A006 (Supplier E) $400K x 5 times (normal business)
└──→ A004 (Zhang San credit card) $100K x 2 times (own account)
Findings:
├── Fund cycle: A001 → A002 → A005 → A003 → A001
├── Cycle amount: $150K
├── Suspicion: Funds loop through 4 accounts across 3 related entities
├── Possible purpose: Inflating transaction volume / money laundering / asset transfer
└── Auto-trigger SAR (Suspicious Activity Report)
coomia-dip advantage:
Traditional requires analysts to manually trace fund flows
coomia-dip uses relation traversal + cycle detection automatically
Completes in seconds, covers all accounts
#5. Risk Control Rule Engine
#5.1 Rules Combined with Ontology
Code
Risk control rules implemented via Ontology ActionTypes:
ActionType: FreezeAccount
trigger:
OR:
- account.overdueDays > 90
- account.owner.amlRiskScore >= 80
- account.owner.creditScore < 300
- manual_trigger
parameters:
accountId: REQUIRED
freezeReason: ENUM [OVERDUE, AML_ALERT, FRAUD_SUSPECT, LEGAL_ORDER]
freezeLevel: ENUM [DEBIT_ONLY, FULL_FREEZE]
duration: DURATION
guards:
- account.status != "FROZEN"
- account.balance >= 0 OR freezeReason == "LEGAL_ORDER"
approvalWorkflow:
- IF freezeLevel == "FULL_FREEZE": require_approval("risk_manager")
- IF freezeReason == "LEGAL_ORDER": require_approval("compliance_officer")
sideEffects:
- NOTIFY(account.owner, "ACCOUNT_FROZEN")
- UPDATE(account.status, "FROZEN")
- LOG(auditTrail)
ActionType: AdjustCreditLimit
trigger:
OR:
- creditScore change exceeds 50 points
- debtToIncomeRatio > 0.6
- guaranteeExposure increased
parameters:
accountId: REQUIRED
newLimit: DECIMAL
adjustmentReason: STRING
guards:
- newLimit >= 0
- newLimit <= maxAllowedLimit(account.owner.creditScore)
#6. Credit Approval Model
Code
ObjectType: LoanApplication
properties:
applicationId: STRING (PK)
applicant: RELATION → Individual | Corporate
loanType: ENUM [PERSONAL, MORTGAGE, AUTO, BUSINESS, CREDIT_LINE]
requestedAmount: DECIMAL
requestedTerm: INT (months)
purpose: STRING
collateral: RELATION[] → Collateral
guarantors: RELATION[] → Individual
status: ENUM [SUBMITTED, UNDER_REVIEW, APPROVED, REJECTED, DISBURSED]
submittedAt: TIMESTAMP
decisionAt: TIMESTAMP
assignedOfficer: RELATION → Employee
derivedProperties:
autoDecision: DERIVED
expression: |
CASE
WHEN applicant.creditScore >= 750 AND requestedAmount <= preApprovedLimit THEN "AUTO_APPROVE"
WHEN applicant.creditScore < 400 THEN "AUTO_REJECT"
WHEN applicant.amlRiskScore >= 50 THEN "MANUAL_REVIEW"
WHEN applicant.debtToIncomeRatio > 0.5 THEN "MANUAL_REVIEW"
WHEN guaranteeChainRisk > 0.7 THEN "MANUAL_REVIEW"
ELSE "MANUAL_REVIEW"
END
guaranteeChainRisk: DERIVED
expression: |
TRAVERSE(applicant, "GuaranteeRelation", depth=3)
→ Count high-risk entities and their proportion on the chain
expectedLossRate: DERIVED
expression: |
PD(applicant.creditScore) * LGD(collateral) * EAD(requestedAmount)
riskAdjustedPricing: DERIVED
expression: |
baseRate + riskPremium(expectedLossRate) + operatingCostRate
#7. Regulatory Compliance Model
Code
ObjectType: RegulatoryReport
properties:
reportId: STRING (PK)
reportType: ENUM [CAR, LCR, NSFR, LR, SAR, CTR]
reportingPeriod: STRING
status: ENUM [DRAFT, SUBMITTED, ACCEPTED, REJECTED]
submittedAt: TIMESTAMP
dueDate: DATE
derivedProperties:
capitalAdequacyRatio: DERIVED
expression: |
(tier1Capital + tier2Capital) / riskWeightedAssets * 100
liquidityCoverageRatio: DERIVED
expression: |
highQualityLiquidAssets / totalNetCashOutflows30d * 100
nonPerformingLoanRatio: DERIVED
expression: |
COUNT(Account WHERE overdueDays > 90 AND accountType = "LOAN") /
COUNT(Account WHERE accountType = "LOAN") * 100
All regulatory metrics are Ontology derived properties:
├── Data computed in real-time, always shows latest values
├── Historical values auto-saved for trend analysis
├── Data lineage traceable to every transaction
└── Audit can precisely reproduce metric values at any point in time
#8. Fraud Detection Model
Code
ObjectType: FraudAlert
properties:
alertId: STRING (PK)
alertType: ENUM [IDENTITY_THEFT, CARD_FRAUD, ACCOUNT_TAKEOVER,
APPLICATION_FRAUD, INSIDER_FRAUD]
relatedTransaction: RELATION → Transaction
relatedAccount: RELATION → Account
relatedCustomer: RELATION → Individual
confidence: DECIMAL
status: ENUM [OPEN, INVESTIGATING, CONFIRMED, FALSE_POSITIVE]
assignedAnalyst: RELATION → Employee
Fraud detection derived properties (real-time):
Transaction fraud score:
fraudScore: DERIVED
expression: |
deviceRisk(deviceId, account.knownDevices) * 0.2 +
locationRisk(location, account.normalLocations) * 0.2 +
amountRisk(amount, account.averageTransactionSize) * 0.2 +
velocityRisk(account, "1h") * 0.2 +
behaviorRisk(transactionType, channel, timestamp) * 0.2
isFraudSuspect: fraudScore >= 70
Alert rules:
When isFraudSuspect == true:
1. Block transaction in real-time (if fraudScore >= 90)
2. Send SMS verification (if fraudScore 70-90)
3. Create FraudAlert record
4. Notify anti-fraud analyst
5. Freeze related accounts (if confirmed fraud)
#9. Relationship Network Overview
Code
Financial risk Ontology relationship network:
Individual ──holds──→ Account
Corporate ──holds──→ Account
Individual ──represents──→ Corporate
Individual ──shareholderOf──→ Corporate
Individual ──guarantees──→ Corporate
Individual ──familyOf──→ Individual
Corporate ──guarantees──→ Corporate
Corporate ──subsidiaryOf──→ Corporate
Account ──transfers──→ Account (via Transaction)
Individual ──applies──→ LoanApplication
LoanApplication ──securedBy──→ Collateral
FraudAlert ──relatedTo──→ Transaction
FraudAlert ──involves──→ Account
SAR ──reportedFor──→ Individual
Total relation types: 14
Total ObjectTypes: 12
Core value:
Traditional risk: Rule-based, looks at individual data only
coomia-dip risk: Network-based, sees individual + associations + contagion
"Zhang San is low-risk, but Company C he guarantees is high-risk"
→ This association risk is only discoverable through relationship graphs
#10. Comparison with Traditional Risk Management
Code
Ontology risk management vs traditional:
┌──────────────────┬────────────────────┬──────────────────────┐
│ Dimension │ coomia-dip │ Traditional Risk │
├──────────────────┼────────────────────┼──────────────────────┤
│ Data model │ Unified Ontology │ Multi-system silos │
│ Relationship │ Graph traversal │ SQL JOIN, 2-3 tables │
│ Metric compute │ Real-time derived │ T+1 batch compute │
│ Rule execution │ ActionType + Guard │ Separate rule engine │
│ Cycle detection │ Built-in graph algo│ Requires graph DB │
│ Regulatory rpt │ Auto derived props │ Manual data extract │
│ Audit trail │ Property lineage │ Report-level trace │
│ Extensibility │ Add ObjectType │ Alter tables/fields │
│ Dev efficiency │ Schema-driven │ Requires dev + test │
└──────────────────┴────────────────────┴──────────────────────┘
#Key Takeaways
- Financial Ontology's core is the relationship network, not isolated entities — a customer's risk depends not only on their own data but on guarantee chains, equity relationships, family ties, and fund flows. coomia-dip RelationType naturally supports this complex relationship modeling.
- Derived properties enable "real-time risk management" — credit scores, AML metrics, and fraud scores are all auto-computed via derived properties, updating in milliseconds on transactions, upgrading from "T+1 risk reports" to "real-time risk sensing."
- Relationship graph traversal is Ontology risk management's unique capability — 2-3 hop traversals discover hidden associated customers, fund cycles, and guarantee chain risks that traditional SQL JOINs and rule engines cannot achieve.
- ActionTypes close the loop from "detecting risk" to "handling risk" — account freezing, limit adjustment, SAR submission, and loan rejection all execute through Ontology Action mechanisms with approval workflows and audit logs.
- Regulatory compliance metrics are a natural application of derived properties — capital adequacy ratio, liquidity coverage ratio, and NPL ratio are auto-computed through Ontology, with data traceable to every transaction for audit requirements.
#Next Article
The next article S4-18 Healthcare Ontology Modeling discusses how to express the healthcare domain's complex semantics using the Ontology model — patients, encounters, diagnoses, prescriptions, lab results, and medical devices — how to handle healthcare data privacy compliance, and how relationship networks enable disease spectrum analysis.
#ontology #financial-risk #aml #credit-scoring #fraud-detection #guarantee-chain #fund-flow #compliance #graph-analysis