cred Overview

What is cred?

cred is a python package for modeling commercial real estate debt. It is designed to enable quickly building and analyzing cash flows for common debt structures while still retaining the flexibility to easily customize debt terms and performance scenarios.

Homepage: https://github.com/jordanhitchcock/cred

Documentation: https://cred.readthedocs.io/en/latest/

Tutorials: Beginner’s Guide to Building a Loan with cred

Installation

pip install cred

Quickstart

The main interface for creating cash flows for loans with regular interest periods is PeriodicBorrowing. The FixedRateBorrowing subclass is a convenient class representation of fixed rate debt. The example below builds a borrowing object for a 1 year loan with monthly interest payments at 5.0%.

from datetime import date

from cred import FixedRateBorrowing, Monthly

loan = FixedRateBorrowing(
    start_date=date(2020, 1, 1),
    end_date=date(2021, 1, 1),
    freq=Monthly(months=1),
    initial_principal=10_000_000,
    coupon=0.05
)

loan.schedule()

Other convenience arguments than can be defined at initialization include the amortization method (default is interest only) and day count convention (default is actual / 360). See the documentation for additional detail.

Calling borrowing.schedule() returns a pandas.DataFrame with the scheduled cash flows:

index  start_date    end_date payment_date  bop_principal  interest_rate  interest_payment  principal_payment       payment  eop_principal
0      2020-01-01  2020-02-01   2020-02-01       10000000           0.05      43055.555556                  0  4.305556e+04       10000000
1      2020-02-01  2020-03-01   2020-03-01       10000000           0.05      40277.777778                  0  4.027778e+04       10000000
2      2020-03-01  2020-04-01   2020-04-01       10000000           0.05      43055.555556                  0  4.305556e+04       10000000
3      2020-04-01  2020-05-01   2020-05-01       10000000           0.05      41666.666667                  0  4.166667e+04       10000000
4      2020-05-01  2020-06-01   2020-06-01       10000000           0.05      43055.555556                  0  4.305556e+04       10000000
5      2020-06-01  2020-07-01   2020-07-01       10000000           0.05      41666.666667                  0  4.166667e+04       10000000
6      2020-07-01  2020-08-01   2020-08-01       10000000           0.05      43055.555556                  0  4.305556e+04       10000000
7      2020-08-01  2020-09-01   2020-09-01       10000000           0.05      43055.555556                  0  4.305556e+04       10000000
8      2020-09-01  2020-10-01   2020-10-01       10000000           0.05      41666.666667                  0  4.166667e+04       10000000
9      2020-10-01  2020-11-01   2020-11-01       10000000           0.05      43055.555556                  0  4.305556e+04       10000000
10     2020-11-01  2020-12-01   2020-12-01       10000000           0.05      41666.666667                  0  4.166667e+04       10000000
11     2020-12-01  2021-01-01   2021-01-01       10000000           0.05      43055.555556           10000000  1.004306e+07              0

Floating Rate Example

Floating rate borrowing objects require subclassing PeriodicBorrowing and implementing the interest_rate method. The interest_rate method is called as part of the loop that calculates loan values for each period. It receives an InterestPeriod object which contains index, start_date, and end_date attributes for the period.

The example below generates random index resets between 1.5% and 2.0% and adds a 2.0% margin to calculate total interest.

import random
from cred import PeriodicBorrowing

class MyFloatingRateLoanType(PeriodicBorrowing):

    def interest_rate(self, period):
        return random.uniform(0.015, 0.02) + 0.02

floating_loan = MyFloatingRateLoanType(
    start_date=date(2020, 1, 1),
    end_date=date(2021, 1, 1),
    freq=Monthly(months=1),
    initial_principal=10_000_000
)

floating_loan.schedule()

Output:

index  start_date    end_date payment_date  bop_principal  interest_rate  interest_payment  principal_payment       payment  eop_principal
0      2020-01-01  2020-02-01   2020-02-01       10000000       0.039226      33778.233665                  0  3.377823e+04       10000000
1      2020-02-01  2020-03-01   2020-03-01       10000000       0.036212      29170.599256                  0  2.917060e+04       10000000
2      2020-03-01  2020-04-01   2020-04-01       10000000       0.039830      34298.387753                  0  3.429839e+04       10000000
3      2020-04-01  2020-05-01   2020-05-01       10000000       0.037286      31072.075651                  0  3.107208e+04       10000000
4      2020-05-01  2020-06-01   2020-06-01       10000000       0.038355      33027.953727                  0  3.302795e+04       10000000
5      2020-06-01  2020-07-01   2020-07-01       10000000       0.036090      30074.908731                  0  3.007491e+04       10000000
# ...

Custom implementations of other cash flow and data fields can similarly be modified by subclassing and overriding the applicable method.

Adding Custom Fields to the Borrowing Schedule

In addition to modifying current schedule columns, new fields can easily be added to the schedule as well. The example below adds two new columns:

  • NOI: Net operating income for each month ($60,000 per month, growing monthly at an annual rate of 3.0%)
  • DSCR: The debt service coverage ratio for each month based on a constant 6.44% debt service multiple (approximately the debt multiple for a 30 year amortizing loan with 5% interest)

set_period_values is the main method inside schedule that sets period values. Since the two new methods are called after the super class sets its period values, the new columns will be appended to the right side of the schedule.

class MyCustomLoanType(MyFloatingRateLoanType):

    def noi(self, period):
        return 60000 * (1 + 0.03 / 12 * period.index)

    def dscr(self, period):
        return period.noi / period.interest_payment

    def set_period_values(self, period):
        super().set_period_values(period)
        period.add_display_field(self.noi(period), 'noi')
        period.add_display_field(self.dscr(period), 'dscr')

custom_loan = MyCustomLoanType(
    start_date=date(2020, 1, 1),
    end_date=date(2021, 1, 1),
    freq=Monthly(months=1),
    initial_principal=10_000_000
)

custom_loan.schedule()

Result (scroll all the way to the right):

index  start_date    end_date payment_date  bop_principal  interest_rate  interest_payment  principal_payment       payment  eop_principal      noi      dscr
0      2020-01-01  2020-02-01   2020-02-01       10000000       0.036185      31159.351494                  0  3.115935e+04       10000000  60000.0  1.925586
1      2020-02-01  2020-03-01   2020-03-01       10000000       0.035363      28486.801992                  0  2.848680e+04       10000000  60150.0  2.111504
2      2020-03-01  2020-04-01   2020-04-01       10000000       0.035551      30613.195658                  0  3.061320e+04       10000000  60300.0  1.969739
3      2020-04-01  2020-05-01   2020-05-01       10000000       0.037290      31075.189753                  0  3.107519e+04       10000000  60450.0  1.945282
4      2020-05-01  2020-06-01   2020-06-01       10000000       0.037907      32642.384490                  0  3.264238e+04       10000000  60600.0  1.856482
5      2020-06-01  2020-07-01   2020-07-01       10000000       0.037355      31129.007229                  0  3.112901e+04       10000000  60750.0  1.951556
# ...

Accessing Period Values

In addition to accessing the entire loan schedule through the schedule method, values for individual periods can be accessed through the borrowing.period method. This method takes the zero-based index of the target period and returns the schedule values for the period as a dictionary.

self.period is the recommended way to recursively pull in values from previous periods when setting period values. For example, after the initial period the beginning-of-period principal (bop_principal) balance is equal to the previous period’s ending value. The implementation for the bop_principal method is:

def bop_principal(self, period):
    if period.index == 0:
        return self.initial_principal
    return self.period(period.index - 1).eop_principal

Note

Always reference the current period with the period argument and not through self.period as doing so will cause infinite recursion problems.

Accessing values from previous periods provides a simple and intuitive way to implement recursive calculations, for example capitalizing interest expense for a construction loan.

Period Value Caching

Certain debt assumptions may change during project evaluation or may be unknown prior to building the cash flows. The clearest example is interest rates which change second by second.

In order to avoid accidentally using stale values, Borrowing objects do not store schedule values. They are recalculated any time schedule or period is called. This means that it is safe to update borrowing attributes, and any attribute changes will be reflected in subsequent calls.

Recalculating values for every period could hamper performance if many recursive look-ups exist, however the schedule method is smart and caches previous period values during execution of the method.

Additionally, borrowings have a context manager that will enable period caching on entry and purge cached values on exit.