Structure your Financial Model

Jeroen Bouma
6 min readMar 5, 2024

--

To understand how to structure a model, the following guidelines are given. These guidelines are meant to be used as a reference and are not meant to be followed strictly. The most important thing is that the model is structured in a way that is understandable and maintainable.

The preferred logic of creating a model is the Model, View and Controller (MVC) pattern. This is a pattern in software design commonly used to implement user interfaces, data, and controlling logic. It emphasizes a separation between the software’s business logic and display. This “separation of concerns” provides for a better division of labor and improved maintenance.

The following diagrams illustrate the different flows of the MVC pattern depending on how you structured your model and what the purpose of your model is.

What makes the MVC pattern so powerful is that it is immediately clear in the structure of the model what modules do what. If I am looking for the calculation of the Gross Margin ratio or want to understand what data is being passed on for this calculation, I know exactly where to look (the model and controller respectively).

This guide is part of a series related to building financial models:

The Data Layer

The data layer of the application which is where the data is manipulated. This can be anything from calculations to simple data transformations. The importance of these models is that they work on any dataset and have as little requirements as feasibly possible.

For example, when calculating the Gross Margin, it should not be required to have a dataset with a specific column name. Instead, it should be possible to pass two Series, Arrays, Floats or Integers to do this calculation. This is referred to as the ability of doing “dumb” calculations, there is no flavouring done for the specific need.

See the simplicity of such a model here and below:

Each function is categorized in a specific module. For example, the Gross Margin calculation is categorized under the profitability_model.py module which contains all of the other profitability ratios. The same applies to the other ratio categories such as liquidity, solvency, efficiency and valuation which can be found in liquidity_model.py, solvency_model.py, efficiency_model.py and valuation_model.py respectively.

The Visualization Layer

This is meant to display data in a specific way. This can be a table, a graph, a dashboard, etc. It is definitely possible that the DataFrame that the model produces can already be considered as a “View” and therefore this is an optional addition.

See below how such a view could look like:

Similar to the Data Layer, the Visualization Layer is also split up into different modules. For example, the Gross Margin plot is categorized under the profitability_view.py module which contains all of the other plot functionalities for the profitability ratios. The same applies to the other ratio categories such as liquidity, solvency, efficiency and valuation which can be found in liquidity_view.py, solvency_view.py, efficiency_view.py and valuation_view.py respectively.

The Controlling Layer

These are functionalities that tie both the Model and View together. This is what adds in the dataset-specific flavouring. E.g. when calculating the Gross Margin, the controller will pass onto the model the correct columns of the dataset that contain the Revenue and the Cost of Goods Sold.

Note that within the Controller there are no calculations done, this is purely meant to pass on the correct data to the View or Model. The controller functions are almost always wrapped inside a class, e.g. for the Gross Margin calculation, this is wrapped inside the Ratios class as follows (see the actual code here):

Different from the Data and Visualization Layer, the Controller is not split up into different modules. This is because the Controller is meant to be the “glue” between the Model and the View. So in this case, this function would fit in the ratios_controller.py module.

As shown below, it is also possible to have multiple Controllers. For example, the Finance Toolkit has a toolkit_controller.py which is the main controller that is used to initialize the Toolkit.

This controller is then used to initialize the ratios_controller.py which is used to calculate the ratios. This is done to ensure that the Toolkit Controller is not overloaded with functions and that the Ratios Controller can be used separately. For example, in the toolkit_controller.py, the Ratios Controller is initialized as follows (see actual code here):

The Supportive Layer

Next to the Models, Views and Controller modules, it can be helpful to have a helpers module. This module is meant to contain functions that are used throughout the entirety of the package.

For example, a function that calculates the growth of a pd.Series or pd.DataFrame. This function can be used in multiple places (calculating growth can be relevant for ratios, technical indicators, performance metrics and more) and therefore it is useful to put it into the helpers module. See the actual code here and below an example:

Other type of helper functions could be reading datasets from xlsx or csv files and handling common errors. It is often not necessary to split up the helpers module into different modules.

Combining Everything

As seen in the Setting up your Project page, the Model, View and Controller for Gross Margin calculation will be named profitability_model.py, profitability_view.py and ratios_controller.py respectively. The helpers.py module will be placed in the root of the package.

The same methodology is applied to the Finance Toolkit in which the structure of the last graph, as depicted at the top of this page, is used.

Following this structure, this would be how it looks when executing each:

Which returns the following dataset leveraging the actual financial statements:

Alternatively, the growth of the Gross Margin can be calculated as follows:

Which returns the growth of the Gross Margin leveraging the same financial statements:

--

--

Jeroen Bouma

With Experience and Education in the area of Quantitative Finance, my ambition is to continuously improve in the area of Quant Finance and Python Programming.