11. Skip to content

11. Transaction Monitoring

This microservice is intended to monitor financial transactions reported by a third-party application. The purpose of this service is to be able to detect unusual transactions in order to immediately alert the interested parties of the financial institution.

The monitoring is done using a rule-based system.

11.1 Transaction Monitoring rules

The transactions that are reported by the entity to the Trak.e monitoring service are evaluated by means of certain configurable rules in the service, for the automatic detection of unusual operations in order to generate alerts. These alerts are then studied by analysts from the aforementioned entity for better analysis and decision making.

In particular, Trak.e employs a self-developed rule-based system. A rule-based system is one that uses rules defined by human experts in a certain domain to make deductions and perform actions. They are also called simply expert systems: they are considered a way of representing the knowledge of experts within an automatic system. Rules are usually in the form of if-then statements; that is, they are of the form if A then B.

Each active rule is executed once for each injected transaction. It is allowed to add any number of file monitoring rules, but at most 50 can be active at the same time.

11.2 Implementation

The rules follow the scheme seen for the risk matrix and the calculation of the transactional profile: rules written in Python language.

11.2.1 Parameters

The available variables are:

  • profile: When evaluating the rule, the variable is placed in the execution context. profile contains the digital profile that is currently being processed. All the attributes detailed in the section Digital Profiles is available on this object.

  • transaction: Contains the information of the transaction that is currently being processing. All attributes reported by the entity through the transaction API are available.

  • hist_trxs: If the rule includes information about the transactionality of the digital profile, you can access the transaction history of the profile with this name. The variable is placed in the execution context in the format of pandas.DataFrame, where each row of the DataFrame represents a transaction and the columns of the DataFrame are the attributes of transactions (using underscore as separator in case of being nested attributes). It is possible that the history is empty (no rows or columns) if the customer does not record transactions.

11.2.2 Rule result

The rule must define a variable named SHOULD_RAISE whose value must be a truth value indicating whether it should generate a new alert. The possible values and their interpretation are:

  • True: The rule was evaluated and unusual behavior was determined, so that an alert will be raised in trak.e so that an analyst of the entity manage and solve about the anomaly.
  • False: The rule was evaluated and it was determined that there were no behaviors unusual.
  • None: The rule was not evaluated. Either it does not apply, or some critical value is missing to do it.

Note

Context: In addition to the True, False and None values used to interpret the evaluation of the rule, the context of the evaluation is returned, particularly those defined public variables. This allows saving values calculated during the execution of the rule and that they are available at the time of viewing the results.

Note

For security reasons, it is not possible to use the complete Python language when defining a rule, but only a subset of the language. In particular, imports are not allowed. However, for the convenience of the programmer, some modules, classes, and functions included in Python can be used.

Note

Modules, classes and functions allowed when defining rules:

  • Decimal class of the decimal module: This class allows handling decimal numbers with fixed and arbitrary precision. You can check the documentation for this class here.
  • pandas module: This module allows data analysis to be carried out in a fast, powerful, flexible and simple way. In particular, it is necessary to work with the history of transactions. Note that, as in most of the examples on the page, in the rules one refers to this module using the abbreviation pd. The module documentation can be found here.
  • datetime class of the datetime module: This class allows you to flexibly represent and work with dates and times. Note that in trak.e all the dates and times are represented in milliseconds since the Epoch, so after working the dates must be converted to this format before making comparisons with the attributes of the file or transactions. Documentation for this class can be found at here.
  • timedelta class of the datetime module: This class allows representing and working with time intervals. As an example, the difference between two objects of type datetime will be an object of type timedelta. The documentation for the class can be found here.
  • strptime function: This function allows to grammatically parse a date and time in text format and return the corresponding datetime object. The function receives two arguments; the date and time, and the string that describes the format. Some examples:
    >>> strptime("20-06-2021", "%d-%m-%Y")
    datetime.datetime(2021, 6, 20, 0, 0)
    >>> strptime("20-06-21", "%d-%m-%y")
    datetime.datetime(2021, 6, 20, 0, 0)
    >>> strptime("20-06-21, 20:08", "%d-%m-%y, %H:%M")
    datetime.datetime(2021, 6, 20, 20, 8)
    
  • json module: This module contains functions to work comfortably with JSON objects. The module documentation can be found here.
  • math module: Contains several useful mathematical functions. The documentation is here.
  • In addition, some useful functions can be used: max, min, sum, all, any, round, len, isinstance and range.
  • In addition, the standard constructors str, int, float, list, tuple, dict, set and bool can be used.
  • The standard exceptions IndexError and KeyError are also defined in the context, so you can handle them in your code.

11.3 Examples

11.3.1 Exceeds number of transactions

  • Description: Something very common is to evaluate the number of transactions that I make a client in a certain period. A basic rule to evaluate this is the following:
init = datetime.now().replace(hour=0, minute=0, second=0,
                              microsecond=0) - timedelta(days=30)
init_timestamp = int(init.timestamp()) * 1000
cant_trx = hist_trxs[(hist_trxs["timestamp"] >= init_timestamp) & (
    hist_trxs["side"] == transaction.side)].shape[0]
SHOULD_RAISE =  cant_trx >= 20

11.3.2 Exceeds fixed amount

  • Description: This rule evaluates if the cumulative total of transactions exceeds a predetermined fixed amount.
init = datetime.now().replace(hour=0, minute=0, second=0,
                              microsecond=0) - timedelta(days=30)
init_timestamp = int(init.timestamp()) * 1000

total_amount = hist_trxs[
    (hist_trxs["timestamp"] >= init_timestamp) & (
        hist_trxs["side"] == transaction.side)
].amount.sum().item()

SHOULD_RAISE = total_amount + transaction.amount >= 1e7

11.3.3 Exceeds transactional profile

  • Description: This rule evaluates the transactions and verifies that, accumulating the amount with the rest of the operations of the period, the profile does not exceeds its assigned transactional profile.
transactional_profile = profile["transactional_profile_amount"]
if not transactional_profile:
    SHOULD_RAISE = None

transactional_profile_period = 31536000000  # 365*24*60*60*1000

now = transaction["timestamp"]
from_ = now - transactional_profile_period

sum_amount_deposit = sum(hist_trxs[hist_trxs["side"] == "deposit"][hist_trxs["timestamp"] > from_]["amount"])
sum_amount_extraction = sum(hist_trxs[hist_trxs["side"] == "extraction"][hist_trxs["timestamp"] > from_]["amount"])

if transaction.side == "deposit":
    sum_amount_deposit += transaction.amount
elif transaction.side == "extraction":
    sum_amount_extraction += transaction.amount

if sum_amount_deposit + sum_amount_extraction > transactional_profile:
    SHOULD_RAISE = True
else:
    SHOULD_RAISE = False

11.3.4 Sudden profile change

  • Description: This rule evaluates the transactions looking for anomalies in the amounts operated by the client. To do this, it compares the current month with its historical average of deposits and withdrawals made.

  • Parameters:

Parameter Meaning Type Default value
side The rule can be set up to work with deposits or with extraction Literal "deposit" or "extraction" "deposit"
profile_change_min_seniority This rule need the profile to have a certain seniority, a history of transaction in the entity that allows us to determine a reliable historical average. The rule will only be evaluated for those profiles that have been created some time ago integer, representing a time in milliseconds int(1.814e10) (210 days)
profile_change_lookback_period This parameters controls how much time will we consider in order to define de average behavior. integer, representing a time in milliseconds int(1.555e10) (180 days)
profile_change_min_threshold This parameter represents a minimum amount of interest, and serves to avoid having an excess of alerts for small amounts of money. If the behavior of the current month is less than this value the rule will not be evaluated. Floating, representing an amount of money. 350000
profile_change_tolerable_deviation Dictionary that represents, for natural and legal persons and according to the level of risk, how much transactionality is allowed to deviate from the average behavior. Dictionary of dictionary of floats, representing a percentage change. *0.6 for low-risk legal persons
*0.4 for medium-risk legal persons
*0.25 for high-risk legal persons
*0.8 for low-risk natural persons
*0.6 for medium-risk natural persons
*0.4 for high risk natural persons
# allowed sides: "deposit"/"extraction"
side = "deposit"

# profile_change_min_seniority: Profile newer than this will not trigger the
# rule. Defaults to  210 days i.e. 7 months
profile_change_min_seniority: int = int(1.814e10)

# profile_change_lookback_period. How long will the rule look back in order to
# define a usual behavior. Defaults to 180 days, i.e. 6 months
profile_change_lookback_period: int = int(1.555e10)

# Minimum amount of money that will trigger the rule.If current month behavior
# is less than this threshold, the rule is not applied.
profile_change_min_threshold: float = 350000

# threshold for each person type and risk value. These are floating point
# numbers between 0 and 1 representing a percentage that the profile might
# overcome each month.
tolerable_deviation = dict(
    legal_person=dict(
        low=0.6,
        medium=0.4,
        high=0.25,
    ),
    natural_person=dict(
        low=0.8,
        medium=0.6,
        high=0.4,
    ),
)

timestamp = transaction.timestamp
created_at = profile.created_at
person_type = profile.person_type
risk = profile.risk


if not timestamp or not created_at or not person_type or not risk:
    SHOULD_RAISE = None
elif transaction.side != side:
    SHOULD_RAISE = None
elif timestamp - created_at < profile_change_min_seniority:
    # Not enough seniority to consider this profile
    SHOULD_RAISE = None
else:
    trx_now = datetime.fromtimestamp(timestamp // 1000)
    period_end = int(trx_now.replace(day=1, hour=0, minute=0, second=0, microsecond=0).timestamp() * 1000)
    period_init = period_end - profile_change_lookback_period
    one_month = int(2.592e9)

    this_month_behavior = (
        hist_trxs.loc[(hist_trxs["timestamp"] >= period_end) & (hist_trxs["side"] == side)].amount.sum()
        + transaction["amount"]
    )

    if this_month_behavior < profile_change_min_threshold:
        SHOULD_RAISE = None
    else:

        average_behavior = (
            hist_trxs.loc[
                (period_init <= hist_trxs["timestamp"])
                & (hist_trxs["timestamp"] < period_end)
                & (hist_trxs["side"] == side)
            ].amount.sum()
            * one_month
            / profile_change_lookback_period
        )

        deviation = (this_month_behavior - average_behavior) / this_month_behavior
        SHOULD_RAISE = bool(deviation > tolerable_deviation[person_type][risk])
Resources
transaction_transaction
transaction_rule
transaction_summary

11.4 API Documentation

API