MR Fortran Finance Library (MRFFL)
Primary Documentation
Author: | Mitch Richling |
Updated: | 2025-01-21 13:41:36 |
Generated: | 2025-01-21 13:41:37 |
Copyright © 2025 Mitch Richling. All rights reserved.
Table of Contents
1. Introduciton
The functionality powering the programs published in the FortranFinance repository is contained in the MR Fortran Finance Library (MRFFL). MRFFL is a collection of Fortran modules that include advanced TVM solver computations, cashflow stream analysis, US tax calculations, and some monte carlo capabilities (US interest rates & US stock market returns).
API documentation may be found here: MR Fortran Finance Library (MRFFL) API Documentation
The TVM solvers differs from other TVM software I've used in that both annuities (level/geometric/arithmatic) and lump sums are generalized. Annuity payments can stop and start on arbitrary period boundaries within the term. Similarly, lump sums can occur at any payment boundary. This means the solvers can handle a variety of lump sum & annuity types (due, ordinary, delayed, early ending). Together this allows one to solve some difficult, non-standard TVM problems.
The cashflow stream module uses a similar generalization in that cashflows can be placed at any period boundary. This eliminates the need to treat the cashflow at time zero as special. It also eliminates the need to bother with that BEGIN or END mode nonsense. Without these complications we can group cashflow sequences together without worry about the mode of each or period alignment issues that plague some other cashflow software. The module includes cashflow series builders using the same notation and variables as the TVM solvers allowing one to use the TVM solvers to find necessary values, and then use the same variables to populate a cashflow that can be further analyzed or printed.
2. MRFFL (MR Fortran Finance Library) Modules
- Financial applications
mrffl_config
- Contains parameters used to configure the rest of the modules.
mrffl_cashflows
- TVM problems involving cash flows.
mrffl_life_table
- Life table computations and some US life table data.
mrffl_percentages
- Working with percentages
mrffl_tvm12
- Classic financial calculator style TVM solver and amortization.
mrffl_tvm
- TVM solvers for: deferred lump sums and generalized level/geometric/arithmatic annuities.
mrffl_us_inflation
- Historical US inflation data. Adjust values across years. Random inflation generation via resampling.
mrffl_us_markets
- Historical stock market annual return data. Includes random return generation via resampling.
mrffl_us_taxes
- Basic US tax information and computation for filing joint or single.
mrffl_prt_sets
- Constants to specify things to print (used by
*_print
routines) mrffl_var_sets
- Constants to specify variables (used by TVM solvers to specify unknown variables)
- Support Modules
mrffl_solver
- Solves equations using bisection – used by the TVM solvers in the
tvm
&tvm12
modules. mrffl_stats
- Statistics stuff – resamplers, random variables (gamma & log-gamma), etc…
mrffl_bitset
- Bitsets used by TVM solvers for unknown variables.
3. Example Programs
You will find several programs, most of which use MRFFL, in various directories at the base of the FortranFinance repository:
cashflows
- Some simple MRFFL cashflow examples.
loans
- Illustrates how to use MRFFL TVM solvers in combination with cashflow series.
monte_carlo
- Illustrates basic resampling monte carlo for inflation and stock market returns.
retirement
- Some TVM based retirement computations.
retirement_simulation
- A comprehensive simulator for a married couple's retirement (not much MRFFL used here)
In addiont, the unit & functional tests in the MRFFL directory can provide some insight into the useage ofMRFFL:
MRFFL/functional_tests
- Functional tests for MRFFL. While these are basic MRFFL tests, they also demonstrate the API.
MRFFL/unit_tests
- Unit (well unit-like) tests for MRFFL. These also demonstrate the API; however, they can be a bit complex.
3.1. Building Example Programs With GNU Make
This section describes how to build using GNU Make. A later section describes how to build with CMake.
The GNU Make based build is the one I use day to day, and thus gets more testing and is more complete. In order to compile on your platform you may need to
modify the makefile
in each directory to use your favorite Fortran compiler. At the top of each makefile you will find something like this:
################################################ MRFFL_PATH = ../MRFFL include $(MRFFL_PATH)/tools_gfortran.mk # include $(MRFFL_PATH)/tools_flang.mk # include $(MRFFL_PATH)/tools_ifx.mk # include $(MRFFL_PATH)/tools_lfortran.mk # include $(MRFFL_PATH)/tools_nvfortran.mk include $(MRFFL_PATH)/include.mk ################################################
If you are using gfortran
on a UNIX'ish system, then you can probably just leave it as is. If you want to use a different compiler, then you may be able to
simply uncomment the appropriate line if your system is similarly configured to mine. If you are unlucky, then you may need to set some variables. In
particular, you might need to comment out the gfortran
include and add something like this:
AR := ar FC := nvfortran FFLAGS := -O3 -Wall -W -Xlinker -z -Xlinker execstack FSHFLG = -o $(MRFFL_SHARED_LIB_FILE) -shared $(MRFFL_OBJ_FILES)
The only tricky one is the FSHFLG
variable. Luckily you only need the FSHFLG
variable if you plan on building a shared library. The shared library is
completely unnecessary for making full use of the modules, so you you can safely ignore that one unless you really, really want to use a shared library. ;)
3.2. Building Example Programs With CMake
This section describes how to build using CMake. A previous section describes how to build with GNU Make.
Most of the programs in FortranFinance make use of MRFFL. When using a CMake based workflow, MRFFL is accessed via a central include directory of compiled module files and a static library. Before we can compile any examples, we must create these module files and library.
cd MRFFL/build
rm -rf *
cmake .. -DCMAKE_Fortran_COMPILER=gfortran
cmake --build .
Now that MRFFL is compiled, we can make use of it. As an example, we can build the irr
executable in the cashflows
directory like so:
cd cashflows/build
rm -rf *
cmake .. -DCMAKE_Fortran_COMPILER=gfortran
cmake --build .
4. Using MRFFL (MR Fortran Finance Library) Modules
4.1. Using CMake
In the section on building examples we build the MRFFL library using CMake. This process will result in several new files and directories:
MRFFL/build/lib/
will contain the compiled MRFFL library.MRFFL/build/modules/
will contain the generated module files.MRFFL/build/MRFFL.cmake/
a CMake include file that can be used to compile things with the MRFFL library.
For the most part, the following compilers are well supported:
- GNU Fortran
- Intel ifx
- NVIDIA HPC Fortran
- Flang
If you are using a compiler not on this list, then more configuration may be required (-DCMAKE_Fortran_FLAGS
for example).
With MRFFL compiled using CMake, we can use it from another project. The cashflows
directory contains an example CMakeLists.txt
for the irr
executable:
cmake_minimum_required(VERSION 3.30) project(MRFFL_cashflow_examples VERSION 0.1 DESCRIPTION "Examples for MRFFL_cashflows module" LANGUAGES Fortran) set(CMAKE_Fortran_PREPROCESS NO) # Find and include the MRFFL.cmake file. This will setup the MRFFL library -- it's include dirs and link dir. if(EXISTS "../../MRFFL/build/MRFFL.cmake") message(STATUS " MRFFL Search: Found: Exported") include("../MRFFL/build/MRFFL.cmake") if(NOT (EXISTS "../../MRFFL/build/modules/mrffl_config.mod") ) message(ERROR " MRFFL Search: Found exported cmake file, but MRFFL library has not been built!!!") endif() else() message(ERROR " MRFFL Search: Failed!!!") endif() add_executable(irr irr.f90) target_link_libraries(irr PRIVATE MRFFL)
That's really all we need to ake use of MRFFL. Note this CMakeLists.txt
uses MRFFL.cmake
, but this is not strictly required. The paths can be set by
hand, and the following would work instead:
cmake_minimum_required(VERSION 3.30) project(MRFFL_cashflow_examples VERSION 0.1 DESCRIPTION "Examples for MRFFL_cashflows module" LANGUAGES Fortran) set(CMAKE_Fortran_PREPROCESS NO) add_executable(irr irr.f90) target_link_libraries(irr PRIVATE MRFFL) target_include_directories(irr AFTER PRIVATE ../MRFFL/build/modules) target_link_directories(irr BEFORE PRIVATE ../MRFFL/build/lib)
4.2. Using GNU Make
All of the code is in the module source files with no external dependencies at all. So you just need to call the modules from your code, and then compile/link everything together.
To help with that compile/link part, a makefile fragment has been provided
(include.mk
). Note it works with GNU make and is designed for UNIX-like
environments (Mac OS X, Linux, MSYS2 on Windows 11, WSL on Windows 11). I mostly use MSYS2 on Windows 11 so that is where it gets the most testing. The
makefile in the functional tests directory is a good guide on how to use
include.mk
. In essence you do the following in your makefile:
- Set MRFFL_PATH in your makefile to the path of the MRFFL source directory – that's the one with the
include.mk
file. - Set FC, FFLAGS, & AR if necessary – most of the time you can use the defaults.
- Include the "
include.mk
" file in the MRFFL source directory. - Add a build rule for your program.
Your makefile will look something like this:
MRFFL_PATH = ../MRFFL # Set FC, FFLAGS, & AR here. The include below has the settings I use on my system. include $(MRFFL_PATH)/tools_gfortran.mk include $(MRFFL_PATH)/include.mk your_program : your_program.f90 $(MRFFL_OBJ_FILES) $(FC) $(FFLAGS) $^ -o $@
Note the rule in the makefile above takes the lazy approach of adding every MRFFL module as a dependency regardless of if your program actually needs them all. This is how most people use the modules because it's simple. The cost might be a couple seconds of extra compile time. You can explicitly list out the modules in the makefile if you wish. Such a rule might look like the following:
your_program : your_program.f90 mrffl_config$(OBJ_SUFFIX) mrffl_tvm$(OBJ_SUFFIX) mrffl_solver$(OBJ_SUFFIX) $(FC) $(FFLAGS) $^ -o $@
4.2.1. Notes about include.mk
4.2.1.1. Names of files
- File extensions on Windows (outside of WSL)
- Executable files use
.exe
- Shared libraries use
.dll
- Object files will
.obj
- Executable files use
- On UNIX systems (not including MSYS2)
- Executable files have no extension
- Shared libraries use
.so
- Object files will use
.o
4.2.1.2. Useful Variables
MRFFL_MOD_FILES
- All the module (
.mod
) files. These will appear in your build directory. MRFFL_OBJ_FILES
- All the object (
.obj
or.o
) files. These will appear in your build directory. MRFFL_STATIC_LIB_FILE
- The name of the static library file. It's not created by default. It will appear in your build directory if it is listed as a dependency on one of your targets.
MRFFL_SHARED_LIB_FILE
- The name of the shared library file. It's not created by default. It will appear in your build directory if it is listed as a dependency on one of your targets.
4.2.1.3. Useful Targets
all_mrffl_lib
- Builds the library files.
all_mrffl_mod
- Builds the module (
.mod
) files all_mrffl_obj
- Builds the object (
.obj
or.o
) files clean_mrffl_mod
- Deletes all the MRFFL module (
.mod
) files in the build directory. clean_mrffl_obj
- Deletes all the MRFFL object (
.obj
or.o
) files in the build directory. clean_mrffl_lib
- Deletes all the library files in the build directory.
clean_mrffl
- Simply calls the following targets:
clean_mrffl_mod
,clean_mrffl_obj
, &clean_mrffl_lib
clean_multi_mrffl
- The previous clean targets will only remove products from the current platform. For example, the
clean_mrffl_obj
target will delete object files with an extension of.obj
on windows and an extension of.o
on UNIX'ish platforms. I use the same directories to build for all platforms, so I sometimes want to clean up the build products from all platforms at once. That's what this target will do.
4.2.1.4. Static Library
A rule to make a static library is included in include.mk
. A build rule like the following should build that library and link it to your executable. Note
I'm just including the library file on the command line instead of linker like options (i.e. -L
and -l
for GNU compilers). That's because simply including
the library on the command line is broadly supported across more compilers – this way I don't have to document how to do the same thing for each one. ;)
your_program : your_program.f90 $(MRFFL_STATIC_LIB_FILE) $(FC) $(FFLAGS) $^ $(MRFFL_STATIC_LIB_FILE) -o $@
4.2.1.5. Dynamic Library (.so
and .dll
files)
A rule to make a static library is included in include.mk
. You can build it with the target clean_mrffl_lib
, or by using $(MRFFL_SHARED_LIB_FILE)
as a
dependency in your build rule. As the options to link to a shared library differ wildly across platforms and compilers/linkers, I don't provide an example of
how to do that.
5. Tested Environments
- MSYS2 running on Windows 11
- GNU Fortran (Rev2, Built by MSYS2 project) 14.2.0 : Everything works
- LFortran 0.42.0 LLVM 19.1.3 : Nothing works. Compiler crashes during compile.
- Intel ifx 2024.1.0 Build 20240308 : Everything works
- flang 19.1.6 inside clang: Nested functions cause a seg fault, and I don't know how to get the linker to do the right thing.
- Debian 12.8 running in WSL on Windows 11
- GNU Fortran (Debian 14.2.0-8) 14.2.0 from debian-testing: Everything works
- LFortran 0.42.0 LLVM 19.1.3 : Nothing works. Compiler crashes during compile.
- Intel ifx 2025.0.4 20241205 : Everything works
- nvfortran 24.11-0 64-bit target on x86-64 Linux : Everything works.
- flang-new version 19.1.6 : Everything works.