Case Study 2: Adding Fraud Detection to the Banking System
Background
Community Bank has experienced a steady increase in fraudulent activity over the past two years. The losses are modest by large-bank standards -- $340,000 in the most recent fiscal year -- but the trend is accelerating. The compliance officer has identified three categories of fraud that the current capstone application does not detect:
Velocity fraud: Rapid sequences of transactions that exploit the delay between authorization and posting. A common pattern is a compromised debit card used at multiple ATMs within minutes, withdrawing the daily limit at each machine before the batch cycle posts the transactions and reduces the available balance.
Structuring: Deliberate splitting of large cash transactions into amounts just below the $10,000 Currency Transaction Report threshold. While the capstone already generates CTRs for transactions at or above $10,000, it does not aggregate multiple transactions by the same customer within a single day to detect structuring.
Account takeover: Unauthorized access to customer accounts, often following a social engineering attack. The indicators include address changes followed by large withdrawals, password resets followed by wire transfers, and transactions from geographic locations inconsistent with the customer's profile.
The bank's compliance committee has mandated the implementation of a fraud detection module that integrates with the existing capstone application. The module must operate in two modes: real-time screening during CICS transaction processing (to block or flag suspicious activity before it completes) and batch analysis during the nightly cycle (to detect patterns that span an entire business day).
Phase 1: Fraud Rule Definition
The Rule Engine Data Architecture
The team designs a rule-based detection system rather than a hardcoded approach, allowing the compliance team to add, modify, and disable rules without program changes. The rule definitions are stored in a DB2 table:
EXEC SQL
CREATE TABLE FRAUD_RULES
( RULE_ID CHAR(6) NOT NULL,
RULE_NAME VARCHAR(50) NOT NULL,
RULE_TYPE CHAR(2) NOT NULL,
RULE_STATUS CHAR(1) NOT NULL
DEFAULT 'A',
THRESHOLD_AMT DECIMAL(11,2),
THRESHOLD_COUNT INTEGER,
TIME_WINDOW_MIN INTEGER,
ACCT_TYPES CHAR(5),
CHANNEL_TYPES CHAR(14),
RISK_SCORE SMALLINT NOT NULL,
ACTION_CODE CHAR(1) NOT NULL,
EFFECTIVE_DATE DATE NOT NULL,
EXPIRY_DATE DATE,
PRIMARY KEY (RULE_ID) )
END-EXEC
The COBOL copybook mirrors this structure for use in both CICS and batch programs:
*================================================================*
* CAPSTONE-FRAUD-RULE.CPY *
* Fraud Detection Rule Definition Record *
*================================================================*
01 FRAUD-RULE-RECORD.
05 FR-RULE-ID PIC X(06).
05 FR-RULE-NAME PIC X(50).
05 FR-RULE-TYPE PIC X(02).
88 FR-TYPE-VELOCITY VALUE 'VL'.
88 FR-TYPE-AMOUNT VALUE 'AM'.
88 FR-TYPE-STRUCTURE VALUE 'ST'.
88 FR-TYPE-GEO VALUE 'GE'.
88 FR-TYPE-BEHAVIOR VALUE 'BH'.
88 FR-TYPE-TAKEOVER VALUE 'TO'.
05 FR-RULE-STATUS PIC X(01).
88 FR-ACTIVE VALUE 'A'.
88 FR-INACTIVE VALUE 'I'.
88 FR-TEST-MODE VALUE 'T'.
05 FR-THRESHOLD-AMT PIC S9(9)V99 COMP-3.
05 FR-THRESHOLD-COUNT PIC S9(4) COMP.
05 FR-TIME-WINDOW-MIN PIC S9(4) COMP.
05 FR-ACCT-TYPES PIC X(05).
05 FR-CHANNEL-TYPES PIC X(14).
05 FR-RISK-SCORE PIC S9(4) COMP.
05 FR-ACTION-CODE PIC X(01).
88 FR-ACTION-ALLOW VALUE 'A'.
88 FR-ACTION-FLAG VALUE 'F'.
88 FR-ACTION-HOLD VALUE 'H'.
88 FR-ACTION-BLOCK VALUE 'B'.
05 FR-EFFECTIVE-DATE PIC X(10).
05 FR-EXPIRY-DATE PIC X(10).
The team defines an initial set of rules:
| Rule ID | Name | Type | Threshold | Window | Action |
|---|---|---|---|---|---|
| VL0001 | ATM Rapid Withdrawal | VL | $500 x 3 | 15 min | Block |
| VL0002 | POS Rapid Purchase | VL | $200 x 5 | 30 min | Flag |
| AM0001 | Large Single Withdrawal | AM | $5,000 | N/A | Flag |
| AM0002 | Large Wire Transfer | AM | $25,000 | N/A | Hold |
| ST0001 | Cash Structuring - Day | ST | $10,000 cumulative | 1 day | Flag |
| ST0002 | Cash Structuring - Week | ST | $25,000 cumulative | 7 days | Flag |
| TO0001 | Address Change + Large WD | TO | $1,000 | 72 hrs | Block |
| TO0002 | New Payee + Large Transfer | TO | $2,500 | 24 hrs | Hold |
| GE0001 | Multi-State ATM Same Day | GE | N/A | 1 day | Flag |
| BH0001 | Dormant Account Activation | BH | $500 | N/A | Flag |
Loading Rules into CICS Memory
For real-time performance, the fraud rules cannot be looked up in DB2 for every transaction. The team loads the active rules into a CICS shared data table at CICS startup and refreshes them every 30 minutes or on demand:
01 WS-FRAUD-RULE-TABLE.
05 WS-FRT-COUNT PIC 9(03) VALUE ZERO.
05 WS-FRT-MAX PIC 9(03) VALUE 100.
05 WS-FRT-ENTRY OCCURS 100 TIMES.
10 WS-FRT-RULE-ID PIC X(06).
10 WS-FRT-TYPE PIC X(02).
10 WS-FRT-THRESH-AMT PIC S9(9)V99 COMP-3.
10 WS-FRT-THRESH-CNT PIC S9(4) COMP.
10 WS-FRT-WINDOW PIC S9(4) COMP.
10 WS-FRT-ACCT-TYPES PIC X(05).
10 WS-FRT-CHANNELS PIC X(14).
10 WS-FRT-RISK-SCORE PIC S9(4) COMP.
10 WS-FRT-ACTION PIC X(01).
1000-LOAD-FRAUD-RULES.
MOVE ZERO TO WS-FRT-COUNT
EXEC SQL
DECLARE RULE-CSR CURSOR FOR
SELECT RULE_ID, RULE_TYPE,
THRESHOLD_AMT, THRESHOLD_COUNT,
TIME_WINDOW_MIN, ACCT_TYPES,
CHANNEL_TYPES, RISK_SCORE,
ACTION_CODE
FROM FRAUD_RULES
WHERE RULE_STATUS IN ('A', 'T')
AND EFFECTIVE_DATE <= CURRENT DATE
AND (EXPIRY_DATE IS NULL
OR EXPIRY_DATE >= CURRENT DATE)
ORDER BY RULE_TYPE, RULE_ID
FOR FETCH ONLY
END-EXEC
EXEC SQL OPEN RULE-CSR END-EXEC
PERFORM UNTIL SQLCODE NOT = 0
OR WS-FRT-COUNT >= WS-FRT-MAX
EXEC SQL
FETCH RULE-CSR
INTO :WS-FRT-RULE-ID(WS-FRT-COUNT + 1),
:WS-FRT-TYPE(WS-FRT-COUNT + 1),
:WS-FRT-THRESH-AMT(WS-FRT-COUNT + 1),
:WS-FRT-THRESH-CNT(WS-FRT-COUNT + 1),
:WS-FRT-WINDOW(WS-FRT-COUNT + 1),
:WS-FRT-ACCT-TYPES(WS-FRT-COUNT + 1),
:WS-FRT-CHANNELS(WS-FRT-COUNT + 1),
:WS-FRT-RISK-SCORE(WS-FRT-COUNT + 1),
:WS-FRT-ACTION(WS-FRT-COUNT + 1)
END-EXEC
IF SQLCODE = 0
ADD 1 TO WS-FRT-COUNT
END-IF
END-PERFORM
EXEC SQL CLOSE RULE-CSR END-EXEC
.
Phase 2: Real-Time Transaction Screening
The Screening Interface
Every CICS transaction program in the capstone calls the fraud screening module before committing a transaction. The screening module receives the transaction details and returns an action code:
*================================================================*
* CAPFRAUD - Fraud Screening Subprogram *
* Called by all CICS transaction programs before commit *
*================================================================*
IDENTIFICATION DIVISION.
PROGRAM-ID. CAPFRAUD.
DATA DIVISION.
WORKING-STORAGE SECTION.
COPY CAPSTONE-FRAUD-RULE.
01 WS-CUMULATIVE-RISK PIC S9(4) COMP VALUE ZERO.
01 WS-MAX-ACTION PIC X(01) VALUE 'A'.
01 WS-RULE-IDX PIC 9(03).
01 WS-MATCH-COUNT PIC 9(03) VALUE ZERO.
LINKAGE SECTION.
01 LS-SCREEN-REQUEST.
05 LS-ACCT-NUMBER PIC X(10).
05 LS-CUST-NUMBER PIC X(10).
05 LS-TXN-TYPE PIC X(02).
05 LS-TXN-AMOUNT PIC S9(11)V99 COMP-3.
05 LS-TXN-CHANNEL PIC X(02).
05 LS-TXN-BRANCH PIC X(04).
05 LS-ACCT-TYPE PIC X(01).
01 LS-SCREEN-RESULT.
05 LS-ACTION-CODE PIC X(01).
88 LS-ALLOW VALUE 'A'.
88 LS-FLAG VALUE 'F'.
88 LS-HOLD VALUE 'H'.
88 LS-BLOCK VALUE 'B'.
05 LS-RISK-SCORE PIC S9(4) COMP.
05 LS-TRIGGERED-RULES PIC X(60).
05 LS-ALERT-MESSAGE PIC X(80).
PROCEDURE DIVISION USING LS-SCREEN-REQUEST
LS-SCREEN-RESULT.
0000-MAIN.
INITIALIZE LS-SCREEN-RESULT
SET LS-ALLOW TO TRUE
MOVE ZERO TO WS-CUMULATIVE-RISK
MOVE 'A' TO WS-MAX-ACTION
MOVE ZERO TO WS-MATCH-COUNT
PERFORM 1000-CHECK-VELOCITY-RULES
PERFORM 2000-CHECK-AMOUNT-RULES
PERFORM 3000-CHECK-BEHAVIOR-RULES
MOVE WS-CUMULATIVE-RISK TO LS-RISK-SCORE
MOVE WS-MAX-ACTION TO LS-ACTION-CODE
GOBACK.
Velocity Detection
The velocity check examines recent transaction history to detect rapid sequences. The team maintains a Transaction Velocity Table (TVT) in a CICS Temporary Storage queue, keyed by account number, that stores the last 20 transactions for rapid lookup:
1000-CHECK-VELOCITY-RULES.
PERFORM VARYING WS-RULE-IDX FROM 1 BY 1
UNTIL WS-RULE-IDX > WS-FRT-COUNT
IF WS-FRT-TYPE(WS-RULE-IDX) = 'VL'
PERFORM 1100-EVAL-VELOCITY-RULE
END-IF
END-PERFORM
.
1100-EVAL-VELOCITY-RULE.
* Read the velocity queue for this account
MOVE LS-ACCT-NUMBER TO WS-TVT-QUEUE-NAME
EXEC CICS READQ TS
QUEUE(WS-TVT-QUEUE-NAME)
INTO(WS-TVT-DATA)
LENGTH(WS-TVT-LENGTH)
ITEM(1)
RESP(WS-RESP)
END-EXEC
IF WS-RESP = DFHRESP(NORMAL)
* Count transactions within the time window
MOVE ZERO TO WS-WINDOW-TXN-COUNT
MOVE ZERO TO WS-WINDOW-TXN-TOTAL
PERFORM VARYING WS-TVT-IDX FROM 1 BY 1
UNTIL WS-TVT-IDX > WS-TVT-ENTRY-COUNT
PERFORM 1110-CALC-MINUTES-AGO
IF WS-MINUTES-AGO <=
WS-FRT-WINDOW(WS-RULE-IDX)
ADD 1 TO WS-WINDOW-TXN-COUNT
ADD WS-TVT-AMOUNT(WS-TVT-IDX)
TO WS-WINDOW-TXN-TOTAL
END-IF
END-PERFORM
* Check if threshold is exceeded
IF WS-WINDOW-TXN-COUNT >=
WS-FRT-THRESH-CNT(WS-RULE-IDX)
AND WS-WINDOW-TXN-TOTAL >=
WS-FRT-THRESH-AMT(WS-RULE-IDX)
PERFORM 8000-TRIGGER-RULE
END-IF
END-IF
.
1110-CALC-MINUTES-AGO.
* Calculate minutes between the TVT entry
* timestamp and the current time
EXEC CICS ASKTIME
ABSTIME(WS-CURRENT-ABSTIME)
END-EXEC
COMPUTE WS-MINUTES-AGO =
(WS-CURRENT-ABSTIME -
WS-TVT-ABSTIME(WS-TVT-IDX))
/ 60000000
.
Alert Generation
When a rule triggers, the screening module records an alert in the fraud alert table and determines the appropriate action based on the rule's action code and the cumulative risk score:
8000-TRIGGER-RULE.
ADD WS-FRT-RISK-SCORE(WS-RULE-IDX)
TO WS-CUMULATIVE-RISK
ADD 1 TO WS-MATCH-COUNT
* Record the triggered rule ID
STRING WS-FRT-RULE-ID(WS-RULE-IDX)
DELIMITED SIZE
','
DELIMITED SIZE
INTO LS-TRIGGERED-RULES
WITH POINTER WS-RULE-PTR
END-STRING
* Escalate action if this rule is more severe
EVALUATE WS-FRT-ACTION(WS-RULE-IDX)
WHEN 'B'
MOVE 'B' TO WS-MAX-ACTION
WHEN 'H'
IF WS-MAX-ACTION NOT = 'B'
MOVE 'H' TO WS-MAX-ACTION
END-IF
WHEN 'F'
IF WS-MAX-ACTION = 'A'
MOVE 'F' TO WS-MAX-ACTION
END-IF
END-EVALUATE
* Write alert record to DB2
EXEC SQL
INSERT INTO FRAUD_ALERTS
(ALERT_ID, ALERT_TIMESTAMP,
ACCT_NUMBER, CUST_NUMBER,
RULE_ID, TXN_TYPE, TXN_AMOUNT,
TXN_CHANNEL, RISK_SCORE,
ACTION_TAKEN, ALERT_STATUS)
VALUES
(NEXT VALUE FOR ALERT_SEQ,
CURRENT TIMESTAMP,
:LS-ACCT-NUMBER, :LS-CUST-NUMBER,
:WS-FRT-RULE-ID(WS-RULE-IDX),
:LS-TXN-TYPE, :LS-TXN-AMOUNT,
:LS-TXN-CHANNEL,
:WS-FRT-RISK-SCORE(WS-RULE-IDX),
:WS-FRT-ACTION(WS-RULE-IDX),
'O')
END-EXEC
.
Phase 3: Batch Fraud Analysis
Daily Structuring Detection
The batch structuring detection program runs after the daily transaction posting step and before report generation. It aggregates all cash transactions by customer and flags those that appear to be structured to avoid the $10,000 CTR threshold:
*================================================================*
* CAPFRSTC - Batch Structuring Detection *
* Processes daily transactions to detect potential structuring *
*================================================================*
IDENTIFICATION DIVISION.
PROGRAM-ID. CAPFRSTC.
DATA DIVISION.
WORKING-STORAGE SECTION.
COPY CAPSTONE-CONSTANTS.
COPY CAPSTONE-TRANSACTION.
01 WS-STRUCT-INDICATORS.
05 WS-SI-CASH-COUNT PIC 9(03) VALUE ZERO.
05 WS-SI-CASH-TOTAL PIC S9(11)V99 COMP-3
VALUE ZERO.
05 WS-SI-MAX-TXN PIC S9(9)V99 COMP-3
VALUE ZERO.
05 WS-SI-ALL-BELOW-10K PIC X(01) VALUE 'Y'.
05 WS-SI-CLOSE-TO-10K PIC 9(03) VALUE ZERO.
01 WS-PREV-CUST-NUM PIC X(10) VALUE SPACES.
01 WS-STRUCT-THRESHOLD PIC S9(9)V99 COMP-3
VALUE 10000.00.
01 WS-CLOSE-RANGE-LOW PIC S9(9)V99 COMP-3
VALUE 8000.00.
01 WS-CLOSE-RANGE-HIGH PIC S9(9)V99 COMP-3
VALUE 9999.99.
PROCEDURE DIVISION.
0000-MAIN.
PERFORM 1000-INITIALIZE
PERFORM 2000-PROCESS-TRANSACTIONS
UNTIL END-OF-FILE
* Process the last customer group
IF WS-PREV-CUST-NUM NOT = SPACES
PERFORM 3000-EVALUATE-CUSTOMER
END-IF
PERFORM 9000-TERMINATE
STOP RUN.
2000-PROCESS-TRANSACTIONS.
* Control break on customer number
IF TXN-CUST-NUMBER NOT = WS-PREV-CUST-NUM
IF WS-PREV-CUST-NUM NOT = SPACES
PERFORM 3000-EVALUATE-CUSTOMER
END-IF
MOVE TXN-CUST-NUMBER TO WS-PREV-CUST-NUM
PERFORM 2100-RESET-ACCUMULATORS
END-IF
* Accumulate cash transactions only
IF (TXN-IS-DEPOSIT OR TXN-IS-WITHDRAWAL)
AND (TXN-CHANNEL-BRANCH OR TXN-CHANNEL-ATM)
ADD 1 TO WS-SI-CASH-COUNT
ADD TXN-AMOUNT TO WS-SI-CASH-TOTAL
IF TXN-AMOUNT > WS-SI-MAX-TXN
MOVE TXN-AMOUNT TO WS-SI-MAX-TXN
END-IF
IF TXN-AMOUNT >= WS-STRUCT-THRESHOLD
MOVE 'N' TO WS-SI-ALL-BELOW-10K
END-IF
IF TXN-AMOUNT >= WS-CLOSE-RANGE-LOW
AND TXN-AMOUNT <= WS-CLOSE-RANGE-HIGH
ADD 1 TO WS-SI-CLOSE-TO-10K
END-IF
END-IF
PERFORM 2900-READ-NEXT
.
3000-EVALUATE-CUSTOMER.
* Structuring indicators:
* 1. Total cash >= $10,000
* 2. No single transaction >= $10,000
* 3. Multiple transactions (3+)
* 4. Transactions clustered near $9,999
IF WS-SI-CASH-TOTAL >= WS-STRUCT-THRESHOLD
AND WS-SI-ALL-BELOW-10K = 'Y'
AND WS-SI-CASH-COUNT >= 3
MOVE 'HIGH' TO WS-SUSPICION-LEVEL
PERFORM 3100-WRITE-ALERT
ELSE IF WS-SI-CLOSE-TO-10K >= 2
MOVE 'MEDIUM' TO WS-SUSPICION-LEVEL
PERFORM 3100-WRITE-ALERT
END-IF
.
3100-WRITE-ALERT.
INITIALIZE WS-ALERT-RECORD
MOVE WS-PREV-CUST-NUM TO ALT-CUST-NUMBER
MOVE 'ST' TO ALT-ALERT-TYPE
MOVE WS-SI-CASH-TOTAL TO ALT-TOTAL-AMOUNT
MOVE WS-SI-CASH-COUNT TO ALT-TXN-COUNT
MOVE WS-SUSPICION-LEVEL TO ALT-SEVERITY
MOVE WS-PROCESS-DATE TO ALT-DETECTION-DATE
MOVE 'CAPFRSTC' TO ALT-SOURCE-PROGRAM
WRITE ALERT-RECORD FROM WS-ALERT-RECORD
ADD 1 TO WS-ALERT-COUNT
.
Account Takeover Pattern Detection
The batch takeover detection program correlates account maintenance events with subsequent financial transactions. It reads the audit trail for the past 72 hours and flags suspicious sequences:
4000-CHECK-TAKEOVER-PATTERNS.
EXEC SQL
DECLARE TAKEOVER-CSR CURSOR FOR
SELECT A.CUST_NUMBER,
A.EVENT_TYPE,
A.EVENT_TIMESTAMP,
T.TXN_TYPE,
T.TXN_AMOUNT,
T.TXN_TIMESTAMP,
T.TXN_CHANNEL
FROM AUDIT_TRAIL A
INNER JOIN TRANSACTION_HISTORY T
ON A.CUST_NUMBER = T.CUST_NUMBER
WHERE A.EVENT_TYPE IN
('ADDR_CHG', 'PWD_RST', 'PHONE_CHG',
'EMAIL_CHG', 'NEW_PAYEE')
AND A.EVENT_TIMESTAMP >=
CURRENT TIMESTAMP - 72 HOURS
AND T.TXN_TIMESTAMP > A.EVENT_TIMESTAMP
AND T.TXN_AMOUNT >= 1000.00
AND T.TXN_TYPE IN ('WD', 'XF', 'WR')
ORDER BY A.CUST_NUMBER, A.EVENT_TIMESTAMP
FOR FETCH ONLY
END-EXEC
EXEC SQL OPEN TAKEOVER-CSR END-EXEC
PERFORM UNTIL SQLCODE NOT = 0
EXEC SQL
FETCH TAKEOVER-CSR
INTO :HV-CUST-NUM, :HV-EVENT-TYPE,
:HV-EVENT-TS, :HV-TXN-TYPE,
:HV-TXN-AMT, :HV-TXN-TS,
:HV-TXN-CHANNEL
END-EXEC
IF SQLCODE = 0
PERFORM 4100-SCORE-TAKEOVER-RISK
END-IF
END-PERFORM
EXEC SQL CLOSE TAKEOVER-CSR END-EXEC
.
4100-SCORE-TAKEOVER-RISK.
* Score based on event-to-transaction pattern
MOVE ZERO TO WS-TAKEOVER-SCORE
* Address change followed by large withdrawal
IF HV-EVENT-TYPE = 'ADDR_CHG'
AND (HV-TXN-TYPE = 'WD'
OR HV-TXN-TYPE = 'WR')
ADD 40 TO WS-TAKEOVER-SCORE
END-IF
* Password reset followed by wire transfer
IF HV-EVENT-TYPE = 'PWD_RST'
AND HV-TXN-TYPE = 'WR'
ADD 50 TO WS-TAKEOVER-SCORE
END-IF
* New payee followed by transfer
IF HV-EVENT-TYPE = 'NEW_PAYEE'
AND HV-TXN-TYPE = 'XF'
ADD 35 TO WS-TAKEOVER-SCORE
END-IF
* Multiple indicators amplify risk
IF HV-TXN-AMT >= 5000.00
ADD 20 TO WS-TAKEOVER-SCORE
END-IF
IF HV-TXN-CHANNEL = 'OL'
ADD 10 TO WS-TAKEOVER-SCORE
END-IF
* Generate alert if score exceeds threshold
IF WS-TAKEOVER-SCORE >= 50
MOVE 'HIGH' TO WS-ALERT-SEVERITY
PERFORM 4200-WRITE-TAKEOVER-ALERT
ELSE IF WS-TAKEOVER-SCORE >= 30
MOVE 'MEDIUM' TO WS-ALERT-SEVERITY
PERFORM 4200-WRITE-TAKEOVER-ALERT
END-IF
.
Phase 4: Suspicious Activity Reporting (SAR)
Regulatory Requirements
When the fraud detection system identifies activity that meets the criteria for a Suspicious Activity Report, the compliance team must file a SAR with FinCEN within 30 days. The capstone extension generates a SAR preparation file that the compliance team reviews and submits through the BSA E-Filing system.
The SAR extract record contains the fields mandated by FinCEN:
01 WS-SAR-RECORD.
05 SAR-FILING-TYPE PIC X(01).
88 SAR-INITIAL VALUE 'I'.
88 SAR-CONTINUING VALUE 'C'.
88 SAR-JOINT VALUE 'J'.
05 SAR-FILER-INFO.
10 SAR-FILER-NAME PIC X(40).
10 SAR-FILER-TIN PIC X(09).
10 SAR-FILER-ADDRESS PIC X(80).
10 SAR-FILER-RSSD PIC X(10).
05 SAR-SUBJECT-INFO.
10 SAR-SUBJ-NAME PIC X(50).
10 SAR-SUBJ-SSN-TIN PIC X(09).
10 SAR-SUBJ-DOB PIC X(10).
10 SAR-SUBJ-ADDRESS PIC X(80).
10 SAR-SUBJ-ID-TYPE PIC X(02).
10 SAR-SUBJ-ID-NUM PIC X(20).
10 SAR-SUBJ-PHONE PIC X(12).
10 SAR-SUBJ-OCCUPATION PIC X(30).
05 SAR-SUSPICIOUS-ACTIVITY.
10 SAR-SA-DATE-FROM PIC X(10).
10 SAR-SA-DATE-TO PIC X(10).
10 SAR-SA-TOTAL-AMT PIC S9(13)V99 COMP-3.
10 SAR-SA-CATEGORIES.
15 SAR-CAT-STRUCTURING PIC X(01).
15 SAR-CAT-TERRORISM PIC X(01).
15 SAR-CAT-FRAUD PIC X(01).
15 SAR-CAT-GAMING PIC X(01).
15 SAR-CAT-LAUNDERING PIC X(01).
15 SAR-CAT-ID-THEFT PIC X(01).
15 SAR-CAT-OTHER PIC X(01).
10 SAR-SA-NARRATIVE PIC X(500).
05 SAR-ACCOUNT-INFO OCCURS 5 TIMES.
10 SAR-AI-ACCT-NUM PIC X(10).
10 SAR-AI-ACCT-TYPE PIC X(01).
10 SAR-AI-TXN-COUNT PIC 9(05).
10 SAR-AI-TXN-TOTAL PIC S9(11)V99 COMP-3.
SAR Generation Batch Program
The SAR generation program reads the fraud alert table, correlates alerts with customer and transaction data, and produces SAR preparation records for compliance review:
5000-GENERATE-SAR-CANDIDATES.
EXEC SQL
DECLARE SAR-CSR CURSOR FOR
SELECT DISTINCT
A.CUST_NUMBER,
C.CUST_LAST_NAME,
C.CUST_FIRST_NAME,
C.CUST_SSN,
C.CUST_DOB,
MIN(A.ALERT_TIMESTAMP) AS FIRST_ALERT,
MAX(A.ALERT_TIMESTAMP) AS LAST_ALERT,
SUM(A.TXN_AMOUNT) AS TOTAL_AMOUNT,
COUNT(*) AS ALERT_COUNT,
MAX(A.RISK_SCORE) AS MAX_RISK
FROM FRAUD_ALERTS A
INNER JOIN CUSTOMER C
ON A.CUST_NUMBER = C.CUST_NUMBER
WHERE A.ALERT_STATUS = 'O'
AND A.RISK_SCORE >= 50
AND A.ALERT_TIMESTAMP >=
CURRENT TIMESTAMP - 30 DAYS
GROUP BY A.CUST_NUMBER,
C.CUST_LAST_NAME,
C.CUST_FIRST_NAME,
C.CUST_SSN, C.CUST_DOB
HAVING COUNT(*) >= 2
OR MAX(A.RISK_SCORE) >= 80
ORDER BY MAX(A.RISK_SCORE) DESC
FOR FETCH ONLY
END-EXEC
.
5100-BUILD-SAR-RECORD.
* Populate the filer information (the bank)
MOVE 'COMMUNITY BANK' TO SAR-FILER-NAME
MOVE '12-3456789' TO SAR-FILER-TIN
* Populate the subject information
STRING HV-CUST-LNAME DELIMITED SPACE
', ' DELIMITED SIZE
HV-CUST-FNAME DELIMITED SPACE
INTO SAR-SUBJ-NAME
END-STRING
MOVE HV-CUST-SSN TO SAR-SUBJ-SSN-TIN
MOVE HV-CUST-DOB TO SAR-SUBJ-DOB
* Populate the activity information
MOVE HV-FIRST-ALERT TO SAR-SA-DATE-FROM
MOVE HV-LAST-ALERT TO SAR-SA-DATE-TO
MOVE HV-TOTAL-AMT TO SAR-SA-TOTAL-AMT
* Set activity categories based on alert types
PERFORM 5200-SET-SAR-CATEGORIES
* Build narrative from alert details
PERFORM 5300-BUILD-NARRATIVE
.
Compliance Dashboard Report
The batch cycle produces a daily compliance summary report that the compliance officer reviews each morning:
6000-PRINT-COMPLIANCE-SUMMARY.
WRITE RPT-RECORD FROM WS-TITLE-LINE
AFTER ADVANCING PAGE
MOVE WS-TOTAL-ALERTS TO RPT-ALERT-COUNT
MOVE WS-HIGH-ALERTS TO RPT-HIGH-COUNT
MOVE WS-MEDIUM-ALERTS TO RPT-MED-COUNT
MOVE WS-LOW-ALERTS TO RPT-LOW-COUNT
MOVE WS-BLOCKED-TXNS TO RPT-BLOCKED-COUNT
MOVE WS-HELD-TXNS TO RPT-HELD-COUNT
MOVE WS-SAR-CANDIDATES TO RPT-SAR-COUNT
WRITE RPT-RECORD FROM WS-SUMMARY-SECTION
AFTER ADVANCING 2 LINES
* Top 10 highest-risk customers
WRITE RPT-RECORD FROM WS-TOP-RISK-HEADER
AFTER ADVANCING 2 LINES
PERFORM VARYING WS-TOP-IDX FROM 1 BY 1
UNTIL WS-TOP-IDX > 10
OR WS-TOP-IDX > WS-TOP-RISK-COUNT
MOVE WS-TOP-CUST(WS-TOP-IDX)
TO RPT-TOP-CUST-NUM
MOVE WS-TOP-NAME(WS-TOP-IDX)
TO RPT-TOP-CUST-NAME
MOVE WS-TOP-SCORE(WS-TOP-IDX)
TO RPT-TOP-SCORE
MOVE WS-TOP-ALERT-CT(WS-TOP-IDX)
TO RPT-TOP-ALERT-CT
MOVE WS-TOP-AMOUNT(WS-TOP-IDX)
TO RPT-TOP-AMOUNT
WRITE RPT-RECORD FROM WS-TOP-RISK-LINE
AFTER ADVANCING 1 LINE
END-PERFORM
* Rule effectiveness statistics
WRITE RPT-RECORD FROM WS-RULE-STATS-HEADER
AFTER ADVANCING 2 LINES
PERFORM VARYING WS-RULE-IDX FROM 1 BY 1
UNTIL WS-RULE-IDX > WS-ACTIVE-RULE-COUNT
MOVE WS-RULE-ID(WS-RULE-IDX)
TO RPT-RULE-ID
MOVE WS-RULE-NAME(WS-RULE-IDX)
TO RPT-RULE-NAME
MOVE WS-RULE-TRIGGERS(WS-RULE-IDX)
TO RPT-RULE-TRIGGERS
MOVE WS-RULE-CONFIRMS(WS-RULE-IDX)
TO RPT-RULE-CONFIRMS
IF WS-RULE-TRIGGERS(WS-RULE-IDX) > 0
COMPUTE RPT-RULE-ACCURACY =
WS-RULE-CONFIRMS(WS-RULE-IDX) *
100 /
WS-RULE-TRIGGERS(WS-RULE-IDX)
ELSE
MOVE ZERO TO RPT-RULE-ACCURACY
END-IF
WRITE RPT-RECORD FROM WS-RULE-STATS-LINE
AFTER ADVANCING 1 LINE
END-PERFORM
.
Results and Operational Impact
The fraud detection module was deployed in a phased rollout over three months. During the first month, all rules operated in test mode (FR-TEST-MODE), generating alerts without blocking transactions, allowing the compliance team to calibrate thresholds and eliminate false positives.
Detection effectiveness: In the first quarter of production operation, the system generated 847 alerts, of which 123 (14.5%) were confirmed as actual fraud or suspicious activity. The structuring detection identified 18 customers engaging in deliberate structuring, resulting in 12 SAR filings. The velocity detection blocked 34 ATM fraud attempts totaling $47,000 in prevented losses. The account takeover detection flagged 8 compromised accounts, all of which were confirmed by subsequent investigation.
False positive management: The initial false positive rate of 85.5% was higher than desired but consistent with industry norms for rule-based systems. The team tuned the thresholds over three months, increasing the confirmation rate to 22% by the end of the second quarter. The most effective tuning was raising the structuring detection minimum from 3 transactions to 4 and narrowing the "close to $10K" range from $8,000-$9,999 to $9,000-$9,999.
Regulatory compliance: The automated SAR preparation reduced the compliance team's SAR filing time from an average of 4 hours per report (manual data gathering and narrative writing) to 45 minutes (review and approval of system-generated content). The bank filed 31 SARs in the first two quarters, up from 8 in the prior year, demonstrating improved detection rather than increased fraud.
Performance impact: The real-time screening added an average of 12 milliseconds to each CICS transaction, well within acceptable limits. The batch fraud analysis programs added 18 minutes to the nightly batch cycle. Both impacts were considered negligible by the operations team.
Discussion Questions
-
The rule-based approach allows compliance staff to modify detection thresholds without code changes. What are the risks of making this too easy? How would you implement a change control process for fraud rules?
-
The false positive rate of 85.5% means that most alerts require human review with no resulting action. How would you design a machine-learning feedback loop within the COBOL system to improve accuracy over time?
-
The real-time velocity check uses CICS Temporary Storage queues to hold recent transaction data. What happens if the CICS region is recycled? How would you make the velocity data persistent without sacrificing performance?
-
The SAR narrative field is currently populated by concatenating alert descriptions. A regulatory examiner expects a coherent, human-readable narrative. How would you design a template-based narrative generator in COBOL?
-
As the bank grows to multiple branches (as described in Case Study 1), how does the fraud detection architecture need to change? Consider cross-branch structuring, multi-branch velocity patterns, and branch-level risk scoring.