Recursively Breaking Circularity
How I spend my weekends thinking about LAMBDA() puzzles

Recursively Breaking Circularity

With the arrival of spilled array formulas in Excel it is now possible to both use good practice modelling techniques such as FAST and dramatically reduce the number of formulas in your workbook. When combined with the judicious use of custom LAMBDA() functions to centralise generic calculation logic, the result is a professional model that not only is much faster to build but also much easier to audit. As inspiration, Craig Hatmaker has a great series of articles and YouTube videos explaining the "5g Modelling" concept on his Beyond Excel website.

The main way this is achieved by only using only one formula for each time series. But this can get tricky because each formula must be able to calculate the entire time series based only on the inputs initially provided. This makes the traditional corkscrew calculation block, where the closing value is simply a SUM() of the opening value from the prior period and period movements impossible. Instead you need to use the new SCAN() function to generate the closing balance for each period based solely on the initial opening balance and period movements.

Other modelling challenges such as figuring out the movements of a revolving credit facility require techniques such as using the difference of a cumulative sum of cash flows. Once you learn the basic techniques though, it's straight forward to incorporate them into your model and get rid of the excess formula bloat that causes problems.

The Problem of Circularity

But all experienced modellers know that there is one problem in financial models that is hard to avoid: circularity. The classic example of this is to calculate the interest expense for a period on a revolving credit facility. Calculating this requires being able to calculate the size of the cash sweep at the end of the period to the facility, which requires knowing the cash available, which requires knowing the interest expense.

Over the years the main approaches to solving circularity in financial models has boiled down to the following approaches:

  1. Simplifying the model - e.g., using only the opening balance instead of the average balance.
  2. Solving algebraically - best approach, but not always possible.
  3. Creating a worksheet that manually iterates to the answer for every period - disadvantages of both formula bloat and the need to estimate in advance how many iterations are needed.
  4. Using iterative calculations - fastest approach but a bad idea for many reasons not least of which is that Excel will stop warning you about any additional circularities you add into your model.
  5. Adding a "break" or hardcoded value - this is achieved by using VBA or Office Script to iteratively copy and paste a live calculated value until the delta between the live and hardcoded values is below some threshold. It's simple and reliable, but with the downside that with every assumption change the process needs to be repeated.

An additional ingenious approach using a comprehensive VBA User Defined Function (UDF) to create a parallel model has been detailed by Edward Bodmer . This approach can take a while to get your head around, but can handle an astonishing amount of financial modelling complexity. The only problem is that it is reliant on VBA so is restricted to the desktop versions of Excel.

The LAMBDA() Approach

As part of developing an internal course for financial analysts, I recently created a three statement model based on the FAST modelling approach but with spilled array formulas. It's a relatively simple model, but it needs to cope with a revolving credit facility, the ability to earn interest on cash balances as well as incur costs on average borrowed funds, and deal with a tax asset created from prior/current period losses.

In the first version of the model, an Office Script was used in place of the traditional VBA script to create an interest expense break that would work anywhere. While functional the frustration of having to run the script every time an assumption was updated soon became apparent and I looked for a better way.

Inspired the VBA UDF approach and the sample formula in Craig Hatmaker 's article LAMBDA Recursion, I set out to see if a LAMBDA() UDF could be the universal solution I had been looking for. The result is a prototype LAMBDA() UDF called CircularityBreakλ() with the following inputs that can be single values or an array of period values:

  • CashBeforeIntTax - the free cash flow before interest and tax.
  • NonCashExpenses - non-cash expenses that can be deducted for tax purposes.
  • OpeningDebt - the opening balance of the revolving credit facility.
  • DebtRate - the nominal annual interest rate for the RCF.
  • OpeningCash - the opening balance of cash on hand.
  • [CashRate] - optional the nominal annual interest rate for excess cash.
  • [DaysInPeriod] - optional the number of days in the period which is used to convert the nominal interest rates to daily compounding effective rates.
  • [OpeningTaxAsset] - optional the opening balance of the tax asset.
  • [TaxRate] - optional the tax rate to apply to earnings after non-cash expenses.
  • [CashMinimum] - optional the minimum amount of cash to hold at the end of the period.
  • [Labels?] - optional boolean value as to whether or not to include labels for the formula output.

You can find the full code for the prototype LAMBDA() on my Gist, but here is what it looks like in practice to use prior to full testing:

CircularityBreakλ() in action

As you can see it returns a table of values that can optionally be brought into another part of the model with the CHOOSECOLS() function. If you want to see it in action check out the CircularityBreakλ Tester.xlsx workbook.

Tricks of the Trade

The function is a bit long to paste in a LinkedIn article but it includes a couple of interesting tricks I wanted to highlight:

  1. The power of the accumulator in the REDUCE function is used to pass the multiple closing balance sheet values into the next iteration of the loop.
  2. The recursive function, cbRecursionλ, is defined within a LET statement within the helper LAMBDA() of the REDUCE() function. While this may not be optimal in terms of efficiency, it does mean that the function has access to all of the previously defined variables without having to explicitly pass them. In fact the only parameters for this function are the function itself, avgDebt, and avgCash.
  3. The output value ( _outputMulti ) is wrapped in a LAMBDA() with no parameters. The advantage of doing this is that it means that this complex step is not evaluated unless explicitly called (it becomes a Thunk). So if earlier error checks result in an error or some other value being returned, time isn't wasted on the recursion.

Final Thoughts

Creating this function has been a very interesting exercise that has really confirmed to me the potential for LAMBDA() UDFs to replace many VBA UDFs in practice. In particular, the power of the accumulator in REDUCE() to pass multiple values and the use of recursion to iterate has really impressed me. With a function like this the desktop constraint of finance models may soon be a thing of the past.

Feel free to use and modify this function with attribution in your own models, and let me know in the comments if you find it useful!

Ivan Klykov

DeFi | VC | M&A | Corporate Finance

5 个月

Thanks for sharing, Chis Great job done I belive that you can add one extra point to your list, as It is also worth mentioning that the best way to get rid of circularity issues is to build models on the monthly basis, where interest could be recorded as payables at the month of accrurals (calculated on the average dabt balance) and further paid next month (actually it is quite similar to what happens in reality). Such a logic is also applicable to taxes (that are dependent on interest). Taxes are also recorded as payables and are actually paid in the future with no impact of other cashflows that occur during the period of payment And I can not agree with the second part of your 2. on your list. I am 99,9% sure that it is always possible to find an algebraic solution. I found such a solutions to many of issues, such as committed debt with a target equity/ capital ratio + idc/ commitment fees, upfront fees; DSRA, Sculpting, etc You just have to carefuly solve equtions and use flags. As far as revolver is concerened, I solved this problem within a weekend and shared this solution here in LinedIn almost 10 years ago:) althout there were no interest on access cash in that model, but it is not a big deal to add it.

回复
Craig Hatmaker

Microsoft MVP | BXL | 5g Modeling Founder

10 个月

Respect

  • 该图片无替代文字
Chinmaya Amte

Valuation, Modeling, Analytics || 55K+ Followers || MS Excel (Spreadsheet) Expert || Project Finance || Trainer & Cool Mentor || De-centralization

10 个月

Thank you so much Chris White for a detailed blog. I have also learnt about the usecase of Lambda to break circularity from Craig Hatmaker. Very revolutionary approach, hope industry adopts it soon.

要查看或添加评论,请登录

Chris White的更多文章

  • The Power of Self-Reflection

    The Power of Self-Reflection

    In an era where capturing the perfect selfie has become a daily ritual, what if we turned the camera inward and took a…

  • Beating the Procrastination Demons

    Beating the Procrastination Demons

    Introduction In the 1926 novel The Sun Also Rises by Ernest Hemingway, the character Mike Campbell was asked about his…

    1 条评论

社区洞察

其他会员也浏览了