Skip to navigation

Credit Card Fraud Detection Solution

View as Markdown

Credit card fraud happens when a stolen or compromised card is used to make transactions the cardholder never authorized. This example makes three kinds of prediction: it scores each transaction for how likely it is to be fraudulent, it looks ahead from each card to flag the cards most likely to see fraud in the coming days, and it does the same for each merchant. Kumo Engine learns all three from each transaction’s own details and from the card and merchant history connected to it in the graph.

Data and relationships

The example assumes a prepared Kumo Engine graph, a matching snapshot, and these tables:

TableRequired columnsPurpose
transactionstransaction_id, credit_card_id, merchant_id, timestamp, labelThe recorded transactions, with an optional amount. label records how a transaction turned out, where 1 means fraudulent and 0 means legitimate, so it is the value the scoring query predicts rather than a feature. timestamp is the time column.
credit_cardscredit_card_id as the primary keyThe card each transaction was made on, with attributes like cc_open_date and credit_limit. A static entity table with no time column.
merchantsmerchant_id as the primary keyThe merchant each transaction was made at, with attributes like category. A static entity table with no time column.

Connect transactions.credit_card_id to credit_cards.credit_card_id and transactions.merchant_id to merchants.merchant_id, and mark transaction_id as the primary key of the transactions table and timestamp as its time column. That time column places every transaction in time, so each prediction is trained and scored using only information recorded before the point it looks ahead from. The credit_cards and merchants tables carry no time column, so they act as static entities that describe the card and the merchant rather than a stream of events. There is no direct foreign key from transactions to a further table such as a users or accounts grandparent, so the forward-looking queries below run at the credit_cards or merchants grain that a transaction points to directly.

Score each transaction for fraud

This query scores each transaction for whether it is fraudulent. The target transactions.label = 1 turns the recorded outcome into a yes or no label, and FOR EACH transactions.transaction_id produces one prediction per transaction. Because the transactions table is anchored at its timestamp time column, each score uses only the card and merchant history recorded before that transaction. Predict the comparison label = 1 rather than the raw label column, which Kumo Engine reads as a numeric target and scores as a regression. This is the real-time authorization decision made as each transaction arrives.

from kumo.engine import PredictiveQuery
query_text = (
"PREDICT transactions.label = 1 "
"FOR EACH transactions.transaction_id"
)
query = PredictiveQuery(graph, query_text)
query.validate()
print(query.get_task_type()) # binary_classification

Predict future fraud on a card

Scoring a transaction only helps once it has been attempted. To catch a card that has been compromised, this query looks ahead for each card and predicts whether it will see a fraudulent transaction in the next 7 days. SUM(transactions.label, 0, 7, days) adds up the fraud labels of the card’s transactions over the 7 days that follow the scoring time, and > 0 turns that into a yes or no label. Because legitimate transactions carry a label of 0, only a fraudulent one moves the sum above zero, so this flags future fraud specifically rather than ordinary spending. The forward window is anchored at the scoring time you run the query, not at each card’s own transactions, and it is the half-open interval that opens just after the scoring time and closes seven days later, (scoring time, scoring time + 7 days]. This supports proactive card-compromise monitoring.

query_text = (
"PREDICT SUM(transactions.label, 0, 7, days) > 0 "
"FOR EACH credit_cards.credit_card_id"
)
query = PredictiveQuery(graph, query_text)
query.validate()
print(query.get_task_type()) # binary_classification

Predict future fraud at a merchant

The same forward-looking approach applies to merchants. This query looks ahead for each merchant and predicts whether any of its transactions in the next 30 days will be fraudulent. SUM(transactions.label, 0, 30, days) adds up the fraud labels of the merchant’s transactions over the 30 days that follow the scoring time, and > 0 turns that into a yes or no label. As with the card query, the window is anchored at the scoring time you run the query, not at each merchant’s own transactions, and it is the half-open interval (scoring time, scoring time + 30 days] shared across every merchant scored in the same run. This supports monitoring merchants for elevated fraud, such as a compromised payment terminal.

query_text = (
"PREDICT SUM(transactions.label, 0, 30, days) > 0 "
"FOR EACH merchants.merchant_id"
)
query = PredictiveQuery(graph, query_text)
query.validate()
print(query.get_task_type()) # binary_classification

Reading the predictions

The scoring query returns a probability that a transaction is fraudulent, which lets a fraud team rank transactions as they arrive and place an authorization hold or a step-up authentication challenge on the riskiest ones. The two forward-looking queries return a probability that a card, or a merchant, will see a fraudulent transaction within the next several days, which supports watching for cards and merchants that are trending toward fraud before a loss is booked, such as freezing a likely-compromised card or reviewing a merchant whose terminal may have been tampered with.

All three are estimates from past behavior, not guarantees. The transaction score is anchored at the moment each transaction arrives, and the two forward-looking queries are anchored at the time you run them, so in every case the model uses only information recorded before that anchor and never sees the outcome it predicts. The 7-day and 30-day windows are starting points; widen or narrow the number of days to match how quickly your team needs to act. Refresh the predictions as new transactions arrive so each decision reflects the latest card and merchant history.