Covenants

Purpose of Covenants

Covenants in the Timvero platform are used to automatically monitor the state of a loan after it has been issued. They allow business rules to be formalized, defining acceptable behavior for the borrower or other related parties, and enable regular automated checks without human intervention.

Examples of business requirements implemented through covenants:

  • The borrower’s income must not fall below the level it was at the time of loan issuance.

  • The outstanding debt must not exceed a certain percentage of the income.

  • The collateral must maintain a value above a specified threshold.


Regular and Anchored Metrics

To evaluate covenant conditions, the Timvero platform uses metrics — calculated indicators associated with subjects (such as a loan participant, collateral, etc.).

There are two types of metrics:

Regular Metrics

These are current values, calculated based on the data available at the time the covenant is executed.

Examples: totalIncome, disposableIncome, ltvRatio.

Anchored Metrics

These are fixed (anchored) values, captured at the moment the covenant becomes active (e.g., when the loan is issued).

They serve as a reference point to assess changes over time.

Example: If the borrower’s totalIncome was $5,000 at the time of loan issuance, this value is stored as an Anchored Metric and compared to the current income on a monthly basis.


Metric Mapping in Covenants

A covenant links two types of metrics:

  • Metric Mapping — the logical name of the current (regular) metric

  • Anchored Metric Mapping — the logical name of the same metric, but in its anchored (fixed-at-start) form

This allows the platform to compare the current state with the initial baseline.

For example:

totalIncome >= anchored;

In this way, covenants with anchored metrics become a key tool for post-loan monitoring, enabling the platform to react in a timely manner to deviations from expected borrower behavior.


Covenant Configuration: Field Descriptions

Example of a Configured Covenant

Covenant Name

Type: String

Description: The name of the covenant. Used only in the UI and reports. Does not affect execution.

Holder Type

Type: Dropdown list

Description: The type of entity to which the covenant is applied (e.g., Credit).

Values are populated automatically based on all implementations of the CovenantHolderResolver interface.

Each Holder Type must implement the HasCovenant interface.

Subject Type

Type: Dropdown list

Description: Specifies whose metrics will be used when evaluating the covenant.

The list of available Subject Types is generated from the getCovenantSubjects() method of the resolver corresponding to the selected Holder Type.

Examples of values: Borrower, Guarantor, Collateral

These values correspond to SubjectRecord.name() in the resolver and determine which subjects will be extracted and evaluated.

Metric Mapping

Type: Dropdown list

Description: The name of the regular metric that will be calculated for each subject.

Values are populated based on the selected Subject Type:

  • The system locates the subject’s class

  • It looks up all registered metrics for that EntityType

  • Then displays the names of those metrics

Anchored Metric Mapping

Type: Dropdown list

Description: The name of the metric whose anchored value was previously saved.

Typically, this should match the Metric Mapping.

The same list of available metrics as in Metric Mapping is used.

Product Additives

Type: Dropdown list

Description: Restricts the covenant to only those credits where the selected product is used.

Periodicity

Type: Time unit (Months, Weeks, etc.)

Description: Interval between covenant executions. Used when Execution Type = Scheduled.

Number of Periods

Type: Integer

Description: Specifies the length of the interval in the unit defined by Periodicity.

Date Time Execution

Type: Date and time

Description: The time of the first covenant execution. Used when Execution Type is set to Scheduled.

Execution Type

Type: Scheduled | On demand | On event

Description: Defines the execution mode:

  • Scheduled — runs on a schedule according to Periodicity, Number Of Period, and Date Time Execution

  • On demand — manually triggered via API or UI (may not be implemented in the platform)

  • On event — reactive execution mode (may not be implemented in the platform)

Condition (expression)

Type: JavaScript

Description: The logic used to evaluate the covenant. Supports:

  • the anchored variable — the anchored value of the metric specified in Anchored Metric Mapping;

  • a variable named after the Metric Mapping — the current value of the metric;

  • the entity itself with its parameters, accessible via the alias entity.

Example from the screenshot above.

totalIncome >= anchored;

HasCovenant interface

HasCovenant is a marker interface that indicates an entity can act as a holder of covenants.

It is used by the platform to:

  • uniquely identify entities to which covenants apply;

  • standardize processing of these entities within resolvers (CovenantHolderResolver);

  • abstract business logic from specific types (e.g., Credit, Application, etc.).

Interface Signature

public interface HasCovenant {
    Long getId();
}
  • getId() — уникальный идентификатор сущности.

How to Use

To make an entity participate in the covenant system, you need to:

  1. Implement the HasCovenant interface.

public class Credit implements HasCovenant {
    private Long id;
    
    @Override
    public Long getId() {
        return id;
    }
}
  • Makes an entity compatible with the covenant system;

  • Acts as a contract in resolvers and during covenant execution;

  • Enables flexible extension of the list of objects that covenants can be applied to.

For all implementations of HasCovenant, you must implement the interface CovenantHolderResolver<T extends HasCovenant>.

This interface is designed to link together:

  • the holder — the entity to which the covenant is attached (e.g., Credit);

  • the subject — an object associated with the holder whose metrics need to be checked (e.g., Participant);

  • the CovenantSpecification — the covenant rule.

To connect a holder to the covenant system, you need to implement three methods:

1. Stream<T> resolveTargets(CovenantSpecification specification)

Purpose: Finds all holder entities to which the given covenant applies.

Example implementation:

@Override
public Stream<Credit> resolveTargets(CovenantSpecification specification) {
    return specification.getAdditives().stream()
        .flatMap(a -> creditRepository.getAllByAdditiveId(a));
}
  • Filtering is performed based on product additives.

  • This method is used during batch covenant execution according to a schedule.

2. List<SubjectRecord<T>> getCovenantSubjects()

Purpose: Defines which subjects can be extracted from the holder. Returns meta-descriptions of the subjects (by key, type, and supplier function).

Example implementation:

@Override
public List<SubjectRecord<Credit>> getCovenantSubjects() {
    return covenantSubjects;
}

private static final List<SubjectRecord<VisionCredit>> covenantSubjects = List.of(
    SubjectRecordBuilder.<Credit>builder().name("BORROWER").subject(getBorrower()).build(),
    SubjectRecordBuilder.<Credit>builder().name("GUARANTOR").subject(getGuarantors()).build(),
    SubjectRecordBuilder.<Credit>builder().name("COLLATERAL").subject(getCollaterals()).build()
);

Each SubjectRecord defines:

  • a key (e.g., "BORROWER"),

  • the subject class,

  • a function to extract the subjects (e.g., credit -> List.of(credit.getApplication().getBorrowerParticipant()) or credit.getParticipants()).

3. Collection<CovenantSpecification> getCovenantSpecifications(T target)

Purpose: Returns all covenant rules that are applicable to a specific holder.

Example implementation:

@Override
public Collection<CovenantSpecification> getCovenantSpecifications(Credit credit) {
    return covenantSpecificationRepository.findAllByAdditiveIdAndActiveTrue(
        credit.getApplication().getCondition().getProductAdditive().getId()
    );
}

SubjectRecordBuilder

To create a subject description, the builder pattern is used:

Example implementation:

SubjectRecord<Credit> borrower = SubjectRecordBuilder.<Credit>builder()
    .name("BORROWER")
    .subject(new SubjectSupplierRecord<>(
        Participant.class,
        credit -> List.of(credit.getApplication().getBorrowerParticipant())
    ))
    .build();

The key (name) must be unique and must match the value specified in the Covenant Specification UI.


Anchored Metric Calculation

What is an Anchored Metric

An Anchored Metric is a metric value that is captured at the moment a covenant becomes active — for example, at the time a credit is issued. This value is stored separately from regular metrics and serves as a reference point for future covenant checks.

Anchored metrics are automatically calculated when an entity implementing HasCovenant (e.g., Credit) is created and when the applicable covenant has an Anchored Metric Mapping defined.

When a new object (such as Credit) is created, the Spring component CreateCreditChecker is triggered via the EntityChecker framework.

Example implementation:

@Component
public class CreateCreditChecker extends EntityChecker<Credit, UUID> {
   
   @Autowired
   private AnchoredMetricService anchoredMetricService;


   @Override
   protected void registerListeners(CheckerListenerRegistry<Credit> registry) {
       registry.entityChange().inserted();
   }


   @Override
   protected boolean isAvailable(Credit credit) {
       return true;
   }


   @Override
   protected void perform(Credit credit) {
       anchoredMetricService.calculateAnchored(credit);
   }
}

📌 Conditions

Anchored metrics are not recalculated automatically, except when explicitly triggered via recalculateAnchored(...).

If there was an error during initial anchoring (i.e. an exception), it can be detected using the method:

existsAnchoredException(...)

You can manually recalculate anchored metrics:

  • For the entire holder entity:

    anchoredMetricService.recalculateAnchored(holder);

  • For a specific subject:

    anchoredMetricService.recalculateAnchored(holder, subject);


CovenantExecution Lifecycle: Registration and Execution

1. Registering New Tasks (CovenantExecutionProducer)

The CovenantExecutionProducerTask component runs on a schedule and performs the following:

  • Retrieves all active CovenantSpecification records

  • For each specification, it calls produce(...), which:

    • Calculates the periodIndex — the index of the current execution period based on dateTimeExecution, numberOfPeriods, and periodicity

    • Checks if a CovenantExecution with the same periodIndex already exists

    • If not, it creates a new CovenantExecution record with status NEW

This scheduling is configured via application properties.

@Scheduled(initialDelayString = "${covenant.execution.producer.initialDelay}", fixedDelayString = "${covenant.execution.producer.fixedDelay}")

2. Processing New Tasks (CovenantExecutionConsumer)

The CovenantExecutionConsumer component is also triggered on a schedule and:

  • Finds the first CovenantExecution entry with status NEW (via findNewExecutionId())

  • Calls the runExecution(id) method to process the execution

It is configured via:

@Scheduled(initialDelayString = "${covenant.execution.consumer.initialDelay}", fixedDelayString = "${covenant.execution.consumer.fixedDelay}")

3. Выполнение задания (CovenantExecutionService.runExecution(...))

Execution Flow:

When execution starts:

  • The corresponding CovenantSpecification is loaded

  • The expression is compiled

  • Using the CovenantHolderResolver:

    • All applicable holder entities are retrieved via resolveTargets(...)

    • For each holder, subjects are resolved using subjectKey

  • For each subject:

    • The regular metric is calculated

    • If specified, the anchored metric is also retrieved

    • The expression is evaluated

    • A CovenantResult is saved with the evaluation state: CLEAN, VIOLATION, or EXCEPTION

  • All CovenantResults are linked to the current CovenantExecution

  • The status of CovenantExecution is updated to EVALUATED


✅ Testing Covenant Script with the Evaluate Button

The Timvero interface allows you to test a covenant’s logic before saving it using the Evaluate button. This helps ensure the expression works correctly for a given holder and subject pair.

🔹 How to Test a Covenant

1. Select Holder Type

Choose the type of object the covenant is attached to — for example, Credit.

2. Specify Subject Type

Select the type of subject whose metrics are being checked — for example, Borrower.

3. Configure Metric Mapping and Anchored Metric

Provide the name of the metric and, optionally, an anchored metric to compare against.

4. Choose an Entity in the Test Data Block

In the Entity field, select an object matching the chosen Holder Type (e.g., a specific credit).

You can begin typing the ID, client name, or description — the system will suggest matching options.

5. Write the Validation Expression

The script should be written in JavaScript. Example:

totalIncome >= anchored

6. Click the Evaluate Button

The result will appear in the Output block below, showing:

  • Execution time

  • Result: true, false, or an error if the expression is invalid


🔹 What’s Available in the Script

Variable

Description

subject

The object whose metrics are being evaluated — e.g., a Participant or Collateral.

holder

The main entity the covenant is attached to — e.g., a Credit.

metric

The current value of the selected regular metric.

anchored

The anchored (fixed) value of the metric, recorded when the covenant began.

🟣 Example

This means that the subject (e.g., a loan participant) has a current income (totalIncome) that is not lower than the income at the time of issuance (anchored), and the covenant is considered fulfilled.

Last updated

Was this helpful?