Experimental features are early versions for users to test before final release. We work hard to ensure that every available Simudyne SDK feature is thoroughly tested, but these experimental features may have minor bugs we still need to work on.
If you have any comments or find any bugs, please share with support@simudyne.com.
Introduction
The simudyne.core.abm.finkit.balance.sheet.lending module allows us to quickly and succinctly implement models that concern themselves with lenders, borrowers and their underlying balance sheets. This is achieved through a set of new APIs and visualisation features on the console. The API sits on top of core-abm allowing you to implement financial models that can be extended to include interactions between any number of different agents.
Let's take a whistle-stop tour of the key concepts involved in implementing a balance sheet model.
FinancialAgent
The toolkit exposes two new FinancialAgents, Borrowers and Lenders that you are free to extend. Both these agent types have a balance sheet and an income statement.
Balance sheet
The balance sheet keeps track of the assets, liabilities and equity of the agents in our models. On creation, each agent is initialised with a balance sheet object. Initially, it is created with default categories of assets, liabilities and equity as well as a separate cash category under assets.
In addition to default assets and liabilities, you can also register you own line items. line items add a further layer of granularity to assets and liabilities. For example, a loan may be a mortgage or a personal loan. We will discuss how to create and register custom line items as well as how they are used below.
Income statement
When you create your own FinancialAgent you will need to implement the following methods.
These afford us the notion of an income statement. At each tick the net result of credits minus debits is calculated and added to the cash item on the balance sheet, this is done for you by the toolkit. You are free to return variables held on the agents or call your own methods that return doubles. This way the income statement of your financial agents can change over time. Note as well that FinancialAgent#initialiseWithCash is called at setup and allows you to initialise your agents with cash.
Borrowers
Borrowers can be used to represent households, people, or any other agent you wish to request loans. They expose two methods which, allow you to request and pay loans without worrying about the underlying message passing or link manipulation. They can also be initialised with cash which appears as an asset on their balance sheet, and they have the notion of income gained through defining debits and credits.
To create a borrower with this functionality simply extend the Borrower class. You will need to override the functionality associated with income statements which we will discuss in detail later on.
Agents default if they enter a state of negative equity
You need to initialise borrowers with cash or only request loans once they have had a positive income flow. Requesting a loan reduces equity on account of the interest paid on loans. If an agent reaches a state of negative equity, they default and play no further role in the simulation. If you would like to change this behaviour simply override the `FinancialAgent#handleDefault` method
Borrower#requestLoan
You can request a loan by creating an Action and calling Borrower#requestLoan. This method takes a LoanApplication object as an argument and sends the application along their MarketLink to the lender. The loan application allows you to construct the loan fluidly. If you are using an IDE the chained methods should auto-complete and the code will not compile until a valid loan has been sent.
Loan request API example.
publicstatic Action<Household>requestLoan(){return Action.create(Household.class, h ->{
h.requestLoan(Borrower.newLoan().ofType(LoanType.UNCOLLATERALIZED).withPrinciple(10).withTerm(12).withInterest(Borrower.interest().fixedOrVariable(FixedOrVariable.FIXED).simComAmo(InterestType.SIMPLE).withAPR(2)));});}
There are a limited number of configuration options available. For example, the toolkit currently only supports uncollateralized loans. The option has been left in to display how the API will function once the toolkit has matured.
Borrower#payLoans
Loans are paid off by calling Borrower#payLoans on an action you define. This method updates the assets and liabilities of the borrower and sends a payment message to the lender of the loan, automatically updating the lender's balance sheets also. Once a loan is paid off the loan is then removed from both lender and borrower.
Lenders are the other side of the loan transaction system. They represent anyone you want to model as a provider of loans, such as banks. They expose a single functionality which is the issuing of loans while again keeping track of loans and payments for you.
Lender#issueLoan
To issue loans create an Action and call Lender#issueLoan, this methods expects loan requests sent from borrowers previously in the sequence.
Once a Lender has issued a loan it adds a Loan link to the requesting borrower. The borrower receives notification of the loan and automatically generates a Repayment link back to the lender. These links are not for you to worry about and are used by the SDK to automate the payment process during the loan lifetime.
LendingModel
The LendingModel is an extension to the AgentBasedModel. All models wanting to run with lenders and borrowers must use LendingModel. Running the simulation in a LendingModel takes care of calculating the income statement at each tick and checks to see if agents have defaulted. It also provides a number of helper methods for creating topologies of borrowers and lenders.
Creating a loan transaction market
Loan transaction markets represent the lender, borrowers and their underlying connections. They are created by calling LendingModel#generateLoanTopology.
Processing income statements
Borrowers and lenders have the notion of an income statement, comprised of credits and debits. At the start of every step, before your logic is executed financial agents update their balance sheets with their income. Two instances will cause an agent to stop. Firstly, if their equity drops below zero. Secondly, if the cash in their balance sheet falls below zero, in these cases, the agent will stop participating in the model and handle defaulting. If a borrower defaults all outstanding loans are written off, this has a knock on effect on the lender where a reduction in equity is the result of projected interest profits being written off. When banks default they simply stop and borrower loans are unaffected.
Example
Let’s create a simple model to simulate different borrowers interacting with a single bank. For simplicity, and to get an intuitive idea of how the balance sheet works, we will limit ourselves to a single bank with a small business and a household acting as borrowers.
Classes required
Let's start by generating the relevant classes; we need two classes
extending Borrower one for our small business and one for the household. We will need a single Lender for our bank. We also need a LendingModel to run everything inside.
We will go ahead and define the credits, debits and initialisation cash directly in the return of our methods, of course in your models these could be functions or even comprised of messages from other agents.
When you are done we should have the following code.
Class representing a small business SmallBusiness.java
We need to create our LoanTransactions. If you have used the SDK previously, these are simply Groups of lenders and borrowers connected by a link from the lenders to the borrower. However, the API wraps this extrapolates this logic. Simply call generateLoanTopology in the setup passing in the class of the borrower, you can then chain the withBorrowers passing in different borrower types and finally call build to generate a loan topology.
The class our models will run in LendingModel.java.
You may be wondering how these agents are connected if we do not specify links. They are created with a specific link which is used when you request a loan. You are free to connect these groups with additional links to other agents but requesting a loan will always use this hidden market link so you do not have to worry about it.
Requesting Loans
Our SmallBusiness and Household now need to request loans of varying size. All borrowers expose a requestLoan method thart can be used inside an action to allow borrowers to request loans.
Create an action and call requestLoan
static Action<Household>requestLoan(){return Action.create(Household.class, h -> h.requestLoan());}
Configure the loan
Request loan takes a LoanApplication object. To create a new loan use the method newLoan. This will set up a chain of method calls that need to be executed in order to fully configure the loan. Currently you can configure the following options.
Type: This represents if the loan is collateralised or not. Currently there is only functionality to have uncolateralised loans but soon this will be expanded.
Principle: This is the principle value of the loan before interest.
Term: This is the number of months the loan will last in months.
Interest: Is the interest on the loan. It has a further configuration and is initialised by calling interest().
Fixed or Variable: Currently there is only the functionality to have fixed interest loans but soon it will be possible for lenders to change loan prices.
SimpleCompound or Amortised: Currently all interest is simple, again this will be expanded upon.
APR: This represents the amount of interest being charged on the loan.
After chaining the relevant methods our household loan could look like this. It is an uncolateralised loan with a principle of 10. The loan will last 12 months with simple interest charged at 2 percent.
static Action<Household>requestLoan(){return Action.create(
Household.class,
h ->{
h.requestLoan(
Borrower.newLoan().ofType(LoanType.UNCOLLATERALIZED).withPrinciple(10).withTerm(12).withInterest(
Borrower.interest().fixedOrVariable(FixedOrVariable.FIXED).simComAmo(InterestType.SIMPLE).withAPR(2)));});}
This will send a message to our bank where it can be processed. Similarly our SmallBusiness can requests loan in the same way.
static Action<SmallBusiness>requestLoan(){return Action.create(
SmallBusiness.class,
h ->{
h.requestLoan(
Borrower.newLoan().ofType(LoanType.UNCOLLATERALIZED).withPrinciple(1000).withTerm(24).withInterest(
Borrower.interest().fixedOrVariable(FixedOrVariable.FIXED).simComAmo(InterestType.SIMPLE).withAPR(2)));});}
Custom line items (optional)
At the moment the bank will receive almost exactly the same loan but in reality, a small business loan would appear separately from mortgages on some balance sheets. If you wish to distinguish between loan types create an enum that implements LineItem we can pass these to our newLoan which will in turn differentiate our loan types on the balance sheet.
static Action<Household>requestLoan(){return Action.create(
Household.class,
h ->{
h.requestLoan(newLoan(LineItems.MORTGAGE).ofType(LoanType.UNCOLLATERALIZED).withPrinciple(1000).withTerm(24).withInterest(interest().fixedOrVariable(FixedOrVariable.FIXED).simComAmo(InterestType.SIMPLE).withAPR(2)));});}
In order for the borrower to know about the line item they must also register them. This can be done in a block in the borrower class.
{registerLineItems(LineItems.MORTGAGE);}
Implementing the lender
There is a method exposed to all Lenders that gets all loan applications received and accepts them. At the moment all loans are accepted but, in the future, you will be able to predicate if a loan is accepted or not. To accept loans, add the following action to your lender class.
Override the step method of the LendingModel. Our household requests a loan at tick 6 and our small business requests one at tick 9. The lender must issue the loan in the same sequence. We must also call the payLoans method at each tick.