Template
What is a Template?
A Template
in Luna is akin to an architectural plan for a house.
While a Contract
is the actual house built based on that plan, a Template
is the reusable blueprint that the house can be built from.
Various elements like Participants
, Parameters
and Clauses
are predefined in a Template
.
These elements work together to form the Template
, allowing you to quickly scale to multiple Contracts that adhere to the same basic rules but have their own specific details.
Why is the Concept of a Template Needed?
The utility of a Template
lies in its ability to standardize and scale. In financial services, many deals, instruments or contracts share a common structure but differ in the finer details.
A Template
allows you to capture this commonality, making it easier to generate new Contracts
by only adjusting the values that differ, such as counterparties, interest rates or loan amounts.
Example: Revolving Credit Facilities in Private Credit
Consider a private credit firm offering revolving credit facilities to various businesses. The foundational rules — like how interest accrues or what constitutes a default — are consistent across different businesses. However, each business might have a different credit rules or interest rate structure.
Here, a Template
serves as the pro-forma structure, outlining the general rules and structure of a revolving credit facility.
For each business that needs a facility, a new Contract
is "built" off this Template
by filling in the specific details like credit limit and interest rate.
This approach ensures both consistency and efficiency.
Components of a Template
Clauses
Contracts run logic contained in Clauses - which are documented in detail on the next page
Participant Roles
Participants
represent entities that can participate in a Contract
. They may represent a single person or a group of people like a company or other group.
When a Participant is involved in a Contract they may take on one or more Roles
within a Contract. A Role
is a way of referring to a specific Participant
onto a Contract
.
For example a loan might contain two Roles, a Lender and a Borrower. The Borrower may be a Participant
representing an individual customer and the Lender might represent a company.
With Roles
established we can refer to the Participant via the Role for the remainder of the Template.
from luna import Template, Role, clause, Context
from luna.accounting.basic_account_chart import initialise_basic_account_chart
class Example(Template):
lender = Role()
borrower = Role()
servicer = Role(count=2) # roles can have more than one participant
@clause(on_init=True)
def initialise_investor_bsa(self, ctx: Context):
created_charts = initialise_basic_account_chart(
ctx,
operator=self.lender,
currency_unit="GBP",
)
...
Parameters
Parameters
are data inputs that vary between Contracts that result from a Template
.
For example: the interest rate on a personal loan, or the monthly charge on a phone contract.
Parameters
are static and their values are set when a Contract is created from a Template
.
Parameters
are static for the duration of a Contract and cannot be updated / overridden by a Contract
.
Parameter Types
When you write a Template you specify Parameters and their associated types. Parameters must be one of the following types:
- Integer
- String
- Boolean
- Decimal
- List (optionally typed based on content)
- Dictionary
- Set
- Datetime
- Date
- Time Delta
- LunaModel - explained in detail here
Examples of Parameters
of each available type:
from datetime import date, datetime, timedelta
from decimal import Decimal
from typing import List
from luna import Template, Parameter, clause, Context
from luna.types import LunaModel
class SomeLunaModel(LunaModel):
foo: str
bar: Decimal
class Example(Template):
param_int = Parameter(type_=int, description="some integer param")
param_str = Parameter(type_=str, description="some string param")
param_bool = Parameter(type_=bool, description="some boolean param")
param_dec = Parameter(type_=Decimal, description="some decimal param")
param_list = Parameter(type_=List[str], description="some list param")
param_dict = Parameter(type_=dict, description="some dictionary param")
param_set = Parameter(type_=set, description="some set param")
param_datetime = Parameter(type_=datetime, description="some datetime param")
param_date = Parameter(type_=date, description="some date param")
param_timedelta = Parameter(type_=timedelta, description="some time delta param")
param_model = Parameter(type_=SomeLunaModel, description="some LunaModel param")
@clause()
def my_clause(self, ctx: Context):
if self.param_bool:
do_something(
self.param_int +1,
[x for x in self.param_list if x.starts],
self.param_model.foo,
self.param_model.bar
)
Variables
Variables
are used in a Template for storing and manipulating data. They are used when values are required to be persisted, for example across different Clauses
or for record keeping purposes.
You can define Variables
in any format you like (unlike Parameters, which are restricted as per above), and use them for any logic or manipulation you want.
Some examples of Variables
being declared:
from luna import Template, Variable
class Example(Template):
my_var1 = Variable(type_=int, value=1)
my_var2 = Variable(type_=str, value="this is a variable")
my_var3 = Variable(type_=bool, value=True)
my_var4 = Variable(type_=float, value=3.14)
param_model = Variable(type_=List[SomeLunaModel], value=[])
You must specify their initial value, but this can be None
which is the overwritten later - e.g. in a Clause
.
my_var5 = Variable(type_=int, value=None)
Their value can also be set from a Python function:
def create_value(x,y):
return x + y
my_var6 = Variable(type_=int, value=create_value(2,7))
You can use any Python library as part of your Variable declaration:
from decimal import Decimal
import numpy
import torch
my_var7 = Variable(type_=Decimal, value=3.14)
my_var8 = Variable(type_=numpy.array, value=numpy.array([1,2,3,4,5]))
my_var9 = Variable(type_=torch.tensor, value=torch.tensor[[[0, 1, 2, 3, 4],[5, 6, 7, 8, 9]],
[[10, 11, 12, 13, 14],[15, 16, 17, 18, 19]],
[[20, 21, 22, 23, 24],[25, 26, 27, 28, 29]],])
Constants
A Constant
is like a Variable
, but it cannot change.
It can be set in the Template
(so all contracts have the same value), or it can be set once (and only once) at Contract
initialisation.
from luna import Template, Constant, Context, clause
class Example(Template):
const = Constant(type_=int)
given_const = Constant(type_=str, value="foo")
@clause(on_init=True)
def set_constant(self):
self.const = 14
@clause()
def my_clause(self, ctx: Context):
do_something(
self.const +1,
self.given_const
)
Linked Templates
What are Linked Templates?
Linked Templates in Luna facilitate the creation of interdependent contracts. You create these links at the contract definition stage during the template creation.
*Example: Master Service Agreement Consider a Master Service Agreement (MSA) as the primary template. You could link this MSA to various other contract templates, such as specific service agreements, partnership contracts, or supply agreements. As children of the MSA template, each subsidiary contract inherits key terms and conditions from the MSA. Any updates or changes to the MSA template automatically update all linked subsidiary contracts, ensuring uniformity and compliance across all related agreements.
Why are they useful?
You define the links between templates using the Luna contract language, allowing for flexible relationships between templates. This flexibility proves useful for complex contractual relationships where data sharing is integral.
In the example below, we show two Templates
which are linked and allow information sharing between them. Specifically, the child template (TemplateB
) can
Linked Templates enhance the contract creation process for agreements that have complex abstractions, or where composability is important. They promote consistency across these contracts and automate the generation of related contracts, thereby reducing manual effort and the risk of errors.
See also the Linked Contracts section in the Contracts page
class TemplateA(Template):
var = Variable(type_=int, value=1)
children = Variable(type_=list, value=[])
@clause()
def my_children(self):
self.children = self.child_contracts.all()
class TemplateB(Template):
template_a = Link(template=TemplateA)
var = Variable(type_=int, value=1)
parents = Variable(type_=list, value=[])
@clause()
def my_parents(self):
self.parents = self.parent_contracts.all()
@clause()
def myclause(self):
self.var += self.template_a.contract.var
Secrets
Secrets Management
Secrets can be managed directly via the API or via the settings tab in the UI.
Contract Integration
Once a secret exists in your organization you can reference its value directly in your templates. The value will be securely injected at runtime.
import requests
class TemplateSecret(Template):
API_KEY = Secret(name="My API Key") # this is the name of the secret shown via the API, or management console
@clause(occurrences=1)
def use_secret(self):
response = requests.get("https://my.api.com", headers={"Authorization": self.API_KEY})
Security
Secrets are securely stored in AWS Secrets Manager and can only be retrieved by authorized contracts.