Part I: The Query Patterns That Matter
In cash management, "interconnected risk" means understanding how liquidity, regulatory constraints, corporate structure, and payment infrastructure interact. After deploying graph-native treasury systems across multiple fintechs, five query patterns consistently deliver 90% of the risk insight value. These are not academic exercises—they are the workhorses of production treasury intelligence.
Pattern 1: Hierarchical Liquidity Roll-Up with Ownership Weighting
The Question: "What is our true available liquidity in EUR across the entire group, accounting for minority-owned subsidiaries where we can’t freely sweep?"
Why It’s Critical: Naive SUM() across all entities overstates usable liquidity by 20-40% in multinationals with joint ventures. Regulators (ECB, MAS) require ownership-weighted consolidation for LCR reporting.
MATCH path = (cg:CorporateGroup {group_id: $gid})-[:CONTAINS]->(le:LegalEntity)
-[:HAS_ACCOUNT]->(ba:BankAccount)-[:DENOMINATED_IN]->(cur:Currency {code: 'EUR'})
MATCH (ba)-[:HAS_POSITION]->(cp:CashPosition {is_current: true})
// Walk ownership chain to compute effective control percentage
WITH le, ba, cp, cur,
[rel IN relationships(path) WHERE type(rel) = 'OWNED_BY' | rel.pct] AS ownership_chain,
reduce(pct = 100.0, o IN [rel IN relationships(path)
WHERE type(rel) = 'OWNED_BY'] | pct * o / 100.0) AS effective_pct
RETURN
le.name AS entity,
cp.available_balance AS gross_balance,
cp.available_balance * effective_pct / 100.0 AS weighted_available,
effective_pct,
CASE WHEN effective_pct < 50 THEN true ELSE false END AS minority_controlled
ORDER BY weighted_available DESCRisk Insight Revealed: Identifies "phantom liquidity"—cash that appears on consolidated reports but cannot be mobilized during stress. This directly impacts LCR compliance and intraday funding plans.
Pattern 2: Cross-Jurisdictional Sweep Feasibility with Constraint Intersection
The Question: "Can we move $30M from our Shanghai WFOE to the Singapore treasury hub today, and what regulatory limits apply?"
Why It’s Critical: Transfer feasibility depends on the intersection of source jurisdiction rules, destination jurisdiction rules, bilateral treaty terms, and internal policy. No single document contains the complete answer.
MATCH (src:LegalEntity {name: $source})-[:SUBJECT_TO]->(sj:RegulatoryJurisdiction)
MATCH (tgt:LegalEntity {name: $target})-[:SUBJECT_TO]->(tj:RegulatoryJurisdiction)
// Find ALL constraints that apply to this specific corridor
MATCH (constraint:CashMovementConstraint)
WHERE constraint.applies_to_jurisdiction IN [sj.code, tj.code, sj.code + '-' + tj.code]
AND constraint.effective_date <= datetime()
AND (constraint.expiry_date IS NULL OR constraint.expiry_date > datetime())
// Check pool connectivity
OPTIONAL MATCH (src)-[:HAS_ACCOUNT]->(sa:BankAccount)
-[:PARTICIPATES_IN]->(pool:CashPool)<-[:PARTICIPATES_IN]-
(ta:BankAccount)<-[:HAS_ACCOUNT]-(tgt)
RETURN
sj.code AS source_jur,
tj.code AS target_jur,
collect({
constraint_type: constraint.type,
limit_amount: constraint.limit_amount,
limit_currency: constraint.currency,
frequency: constraint.frequency,
approval_required: constraint.requires_approval,
regulation_ref: constraint.regulation_code
}) AS applicable_constraints,
pool IS NOT NULL AS pool_connected,
pool.pool_type AS pool_typeRisk Insight Revealed: Produces a constraint intersection matrix rather than a simple yes/no. A $30M transfer might be within SAFE’s annual repatriation limit but exceed the daily PBOC settlement cap—this pattern catches both simultaneously.
Pattern 3: Contagion Path Analysis Through Banking Relationships
The Question: "If Bank X enters resolution, which of our entities lose access to critical payment rails, and what is the cascading liquidity impact?"
Why It’s Critical: Corporate treasuries concentrate banking relationships for efficiency. A single bank failure can paralyze payments across dozens of entities. Relational models cannot trace second-order effects.
// Start from distressed bank
MATCH (bank:Bank {name: $bank_name})
// First order: direct account exposure
MATCH (bank)<-[:HELD_AT]-(ba:BankAccount)<-[:HAS_ACCOUNT]-(le:LegalEntity)
MATCH (ba)-[:HAS_POSITION]->(cp:CashPosition {is_current: true})
// Second order: entities that depend on affected entities for sweeps
OPTIONAL MATCH (le)<-[:OWNED_BY]-(parent:LegalEntity)
OPTIONAL MATCH (le)-[:PARTICIPATES_IN]->(pool:CashPool)<-[:PARTICIPATES_IN]-
(sibling_ba:BankAccount)<-[:HAS_ACCOUNT]-(sibling:LegalEntity)
// Third order: payment rail dependencies
OPTIONAL MATCH (ba)-[:SENT_PAYMENT]->(pi:PaymentInstruction)-[:VIA_RAIL]->(rail:PaymentRail)
WHERE pi.last_used > datetime() - duration('P30D')
RETURN
le.name AS directly_affected,
cp.available_balance AS exposed_balance,
collect(DISTINCT sibling.name) AS cascade_entities,
sum(cp.available_balance) + sum(DISTINCT 0) AS total_direct_exposure,
collect(DISTINCT rail.name) AS disrupted_rails,
parent.name AS parent_entity,
pool.pool_id AS affected_pool
ORDER BY total_direct_exposure DESCRisk Insight Revealed: Maps the liquidity blast radius of a counterparty event. Enables pre-positioning contingency funding at unaffected banks before stress materializes.
Pattern 4: Temporal Position Drift Detection
The Question: "Which entities have cash positions that deviate significantly from their 30-day baseline, and is this explained by scheduled flows or anomalous?"
Why It’s Critical: Static position snapshots miss behavioral anomalies. A subsidiary whose balance dropped 60% overnight might be experiencing fraud, operational error, or unreported capital calls. Time-series analysis on graph nodes detects drift that threshold alerts miss.
MATCH (le:LegalEntity {name: $entity})-[:HAS_ACCOUNT]->(ba:BankAccount)
-[:DENOMINATED_IN]->(cur:Currency {code: $currency})
MATCH (ba)-[:HAS_POSITION]->(current:CashPosition {is_current: true})
// Retrieve historical positions for baseline computation
MATCH (ba)-[:HAS_POSITION]->(hist:CashPosition)
WHERE hist.as_of >= datetime() - duration('P30D')
AND hist.as_of < current.as_of
WITH le, cur, current,
avg(hist.available_balance) AS avg_30d,
stDev(hist.available_balance) AS stddev_30d,
percentileDisc(hist.available_balance, 0.5) AS median_30d,
count(hist) AS sample_count
WHERE sample_count >= 10 // Sufficient history
WITH le, cur, current, avg_30d, stddev_30d, median_30d,
(current.available_balance - avg_30d) / CASE WHEN stddev_30d = 0 THEN 1 ELSE stddev_30d END AS z_score,
(current.available_balance - median_30d) / CASE WHEN median_30d = 0 THEN 1 ELSE median_30d END AS pct_drift
WHERE abs(z_score) > 2.0 OR abs(pct_drift) > 0.40
RETURN
le.name AS entity,
current.available_balance AS current_balance,
round(avg_30d, 2) AS baseline_avg,
round(z_score, 2) AS z_score,
round(pct_drift * 100, 1) AS pct_drift,
current.as_of AS as_of
ORDER BY abs(z_score) DESCRisk Insight Revealed: Distinguishes signal from noise in position monitoring. Z-score normalization enables cross-entity comparison regardless of absolute balance magnitude. Feeds directly into AML/fraud investigation workflows.
Pattern 5: Policy-Position Compliance Gap Detection
The Question: "Show me every entity whose current cash position violates an active treasury policy or regulatory requirement."
Why It’s Critical: Policies and regulations are typically stored as documents; positions as data. The compliance gap exists in the relationship between them—a relationship that neither vector search nor SQL can natively express.
// Match entities to their binding policies
MATCH (le:LegalEntity)-[:BOUND_BY]->(tp:TreasuryPolicy {status: 'ACTIVE'})
WHERE tp.effective_date <= datetime()
AND (tp.expiry_date IS NULL OR tp.expiry_date > datetime())
// Get current position
MATCH (le)-[:HAS_ACCOUNT]->(ba:BankAccount)-[:HAS_POSITION]->(cp:CashPosition {is_current: true})
// Evaluate policy constraints against actual position
WITH le, tp, cp,
CASE
WHEN tp.min_balance_threshold IS NOT NULL
AND cp.available_balance < tp.min_balance_threshold
THEN {violation: 'BELOW_MINIMUM',
threshold: tp.min_balance_threshold,
actual: cp.available_balance}
WHEN tp.max_single_bank_concentration IS NOT NULL
AND cp.available_balance > tp.max_single_bank_concentration
THEN {violation: 'EXCEEDS_CONCENTRATION_LIMIT',
threshold: tp.max_single_bank_concentration,
actual: cp.available_balance}
WHEN tp.required_currency_diversification IS NOT NULL
THEN null // Requires multi-account aggregation; handled separately
ELSE null
END AS violation
WHERE violation IS NOT NULL
RETURN
le.name AS entity,
tp.title AS policy,
tp.policy_id,
violation.violation AS violation_type,
violation.threshold,
violation.actual,
tp.remediation_guidance
ORDER BY violation.actual ASCRisk Insight Revealed: Transforms passive policy documents into active compliance monitoring. Instead of annual policy attestation, treasury gets real-time violation detection grounded in actual positions.

Part II: End-to-End Implementation — Multi-Agent LangGraph RAG for Cash Management
Now we operationalize these five patterns into a production system.
Real-Time Use Case: Morning Liquidity Briefing with Regulatory Compliance Validation
Scenario: Every business day at 06:00 local time, the regional treasurer asks:
"Give me my morning liquidity brief. Highlight any policy violations, trapped cash, and whether yesterday’s EUR sweep from Netherlands BV executed within ECB guidelines. Also flag any position drift anomalies."
This single question triggers all five query patterns, RAG retrieval of ECB guidelines, and synthesis into an actionable briefing—all within a persistent session that remembers yesterday’s follow-ups.
Step 1: State Schema Encapsulating All Five Patterns
from typing import Annotated, List, Dict, Any, Optional
from typing_extensions import TypedDict
from langgraph.graph.message import add_messages
class TreasuryBriefingState(TypedDict):
"""State for daily liquidity briefing with full risk coverage."""
messages: Annotated[List[Any], add_messages]
# Session identity
session_id: str
treasurer_id: str
group_id: str
briefing_date: str
# Pattern 1: Hierarchical roll-up
weighted_liquidity_by_currency: Dict[str, Dict[str, float]]
minority_controlled_entities: List[str]
# Pattern 2: Sweep feasibility (for referenced corridors)
sweep_corridor_analysis: List[Dict[str, Any]]
# Pattern 3: Contagion exposure
bank_concentration_risks: List[Dict[str, Any]]
# Pattern 4: Position drift
drift_anomalies: List[Dict[str, Any]]
# Pattern 5: Policy violations
compliance_violations: List[Dict[str, Any]]
# RAG context
relevant_regulatory_guidance: List[Dict[str, str]]
applicable_treasury_policies: List[Dict[str, str]]
# Output
briefing_summary: Optional[str]
action_items: List[Dict[str, Any]]
risk_rating: str # LOW, MODERATE, ELEVATED, CRITICAL
# Control
phase: str
requires_escalation: bool
error_log: List[str]
Join the conversation! Your thoughts help the community grow.