Package: tmval
TmValTime Value of MoneyIntroductionDocumentation|Development Blog|PyPITmVal is a package that provides tools for the valuation of various financial instruments (annuities, bonds).It can be used to study for Actuarial Exam FM, and (hopefully) used on the job for projects where time value of money is relevant.Installationpip install tmvalorgit clone https://github.com/genedan/TmVal
cd TmVal
python3 -m setup sdist bdist_wheel
cd dist
sudo pip3 install tmval*Feature HighlightsTmVal supports growth patterns that are more complex than compound
interest. In addition to supporting simple, compound, and nominal
interest, TmVal handles growth patterns that may be of theoretical
interest to actuaries, such as continuously compounded rates (Force
of Interest), polynomial growth, and arbitrary amount and
accumulation functions.TmVal provides equations of value computations for core financial
instruments in actuarial science, such as annuities, loans, and
arbitrary cash flow streams. As development is still in the alpha
stage, the types of investments TmVal supports is rapidly expanding.
I expect the package to soon offer classes for bonds, stocks, and
options.TmVal's classes are intended to correspond closely to symbols used
in actuarial notation. Well-known symbols encountered by actuaries are supported. Refer to theNotation Guidein this documentation
to see the available symbols.Amount and Accumulation FunctionsTmVal supports the core growth functions of mathematical interest
theory, theamountandaccumulationfunctions, implemented via theAmountandAccumulationclasses. These classes support all sorts of growth patterns, from simple and compound interest to more complex cases such as tiered investment accounts and polynomial growth.For instance, suppose we have the tiered investment account with
annually compounded interest rates:Required Minimum BalanceInterest Rate01%10,0002%20,0003%If we invest 18,000 today, to what value does it grow after 10 years?fromtmvalimportAccumulationdeff(t):return.05*(t**2)+.05*t+1my_acc=Accumulation(gr=f)print(my_acc.val(5))2.5If we were to invest 5000 today, how long would it take to reach 2% and
3% interest, assuming no future contributions?print(tb.get_jump_times(k=5000))[69.66071689357483,104.66350567472134]It will take almost 70 years to reach 2%, and about 105 years to reach
3%. That's a long time!Interest Rate ConversionsInterest rates are represented by a core data type in TmVal, theRateclass. This custom data type offers a convenient way to perform computations with a variety of interest rate patterns as well as conversions between them. The main patterns supported by theRateclass
are:Effective InterestEffective DiscountNominal InterestNominal DiscountForce of InterestSimple InterestSimple DiscountThe relationships between compound interest rates can be represented
with the following expression:Since there are so many varieties of rates, as well as relationships
between them, an actuary would have to write over twenty conversion
functions to handle the full spectrum of interest rates if they weren't
using a package like TmVal. The good news is that TmVal handles all
these conversions with a single method,Rate.convert_rate.For example, if we needed to convert 5% rate compounded annually to a
nominal discount rate convertible monthly, we could do the following:fromtmvalimportRatei=Rate(.05)nom_d=i.convert_rate(pattern="Nominal Discount",freq=12)print(nom_d)Pattern:NominalDiscountRate:0.048691111787194874CompoundingFrequency:12timesperyearFurthermore, we can demonstrate a conversion to nominal interest
compounded quarterly, and then to δ, the force of interest, and
then back to compound annual effective interest:nom_i=nom_d.convert_rate(pattern="Nominal Interest",freq=4)print(nom_i)Pattern:NominalInterestRate:0.04908893771615652CompoundingFrequency:4timesperyeardelta=nom_i.convert_rate(pattern="Force of Interest")print(delta)Pattern:ForceofInterestRate:0.04879016416943141i2=delta.convert_rate(pattern="Effective Interest",interval=1)print(i2)Pattern:EffectiveInterestRate:0.04999999999999938Unitoftime:1yearFor more details, seeThe Rate Class, Revisitedof theUsage Tutorial.Equations of ValueTmVal can solve for the time τ equation of value for common
financial instruments such as annuities and loans, as well as for
arbitrary cash flows. This is done via thePaymentsclass:For example, we can solve for the internal rate of return of an
investment of 10,000 at time 0 which returns 5,000 at time 1 and 6,000
at time 2:fromtmvalimportPaymentspmts=Payments(amounts=[-10000,5000,6000],times=[0,1,2])# internal rate of returnprint(pmts.irr())[0.0639410298049854,-1.5639410298049854]We can also use thePaymentsclass to
find the time-weighted yield:whereSuppose we deposit 100,000 in a bank account at time 0. It grows to
105,000 at time 1, and we immediately deposit an additional 5,000. It
then grows to 115,000 at time 2. The time-weighted yield is:pmts=Payments(amounts=[100000,5000],times=[0,1])i=pmts.time_weighted_yield(balance_times=[0,1,2],balance_amounts=[100000,105000,115000],annual=True)# time-weighted yieldprint(i)Pattern:EffectiveInterestRate:0.0477248077273309Unitoftime:1yearAnnuitiesAnnuities are one of the core financial instruments underlying life
insurance products. TmVal provides support for many kinds of annuities
via itsAnnuityclass, such as:Annuity-immediateAnnuity-duePerpetuity-immediatePerpetuity-dueArithmetically increasing annuity-immediateArithmetically increasing annuity-dueArithmetically increasing perpetuity-immediateArithmetically increasing perpetuity-dueGeometrically increasing annuity-immediateGeometrically increasing annuity-dueGeometrically increasing perpetuity-immediateGeometrically increasing perpetuity-dueLevel annuity-immediate with payments more frequent than each
interest periodContinuously-paying annuity... and many more. To see what other symbols are supported, consult theNotation Guide.Unlike other packages, which tend to use functions to represent the
different types of annuities, TmVal represents annuities as a class,
which gives it access to several methods that can be performed on the
annuity, such as equations of value. So rather than simply returning a
float value via a function, TmVal expands the manipulations that can be
done with an annuity. My aim is to allow theAnnuityclass to serve as a base
class for or be embedded in more complex insurance products.We can perform simple calculations, such as finding the present value of
a basic annuity-immediate with a five year term and a compound interest rate of 5%:fromtmvalimportAnnuityprint(Annuity(gr=.05,n=5).pv())4.329476670630819To more complex ones, such as the accumulated value of an arithmetically
increasing annuity-due with a starting payment of 5,000, and subsequent payments of 100 over a 5-year term, at 5% compound interest:ann=Annuity(amount=5000,gr=.05,n=5,aprog=100,imd='due')print(ann.sv())30113.389687500014AmortizationTmVal's Loan class has methods for
obtaining information that we might want about loans, such as
amortization schedules and outstanding loan balances.The output for several TmVal's classes are intended to be compatible
withPandas, a popular data analysis
library. The output for the Loan class's
amortization method is one such
example.For example, suppose we were to obtain a 2-year loan of 50,000, to be
paid back with monthly payments made at the end of each month. If the
interest rate were 4% convertible quarterly, what is the amortization
schedule?importpandasaspdfromtmvalimportLoan,Rategr=Rate(rate=.04,pattern="Nominal Interest",freq=4)my_loan=Loan(amt=50000,period=1/12,term=2,gr=gr,cents=True)amort=pd.DataFrame(my_loan.amortization())print(amort)timepayment_amtinterest_paidprincipal_paidremaining_balance00.00NaNNaNNaN50000.0010.082170.96166.112004.8547995.1520.172170.96159.452011.5145983.6530.252170.96152.772018.1943965.4640.332170.96146.072024.8941940.5650.422170.96139.342031.6239908.9460.502170.96132.592038.3737870.5770.582170.96125.822045.1435825.4380.672170.96119.022051.9433773.4990.752170.96112.212058.7531714.74100.832170.96105.372065.5929649.14110.922170.9698.502072.4627576.68121.002170.9691.622079.3425497.34131.082170.9684.712086.2523411.09141.172170.9677.782093.1821317.91151.252170.9670.822100.1419217.77161.332170.9663.852107.1117110.66171.422170.9656.852114.1114996.55181.502170.9649.822121.1412875.41191.582170.9642.782128.1810747.22201.672170.9635.712135.258611.97211.752170.9628.612142.356469.62221.832170.9621.492149.474320.16231.922170.9614.352156.612163.55242.002170.747.192163.55-0.00Using the Loan class's
olb_r method, we can calculate the
outstanding loan balance at any time, such as after 1 year, using theretrospective method:print(my_loan.olb_r(t=1))25497.34126843426Now, what if we choose to overpay during the first two months, with
payments of 3,000 each, and then returning to normal payments? What is
the outstanding loan balance after 1 year?pmts=Payments(amounts=[3000]*2+[2170.06]*10,times=[(x+1)/12forxinrange(12)])print(my_loan.olb_r(t=1,payments=pmts))23789.6328174795Development StatusTmVal is currently in the alpha stage of development. In the coming
weeks, I expect to add many more features, such as:BondsStocksOptionsImmunizationI anticipate declaring the project to be in beta stage once I've
incorporated all of the main concepts on the syllabus of the SOA's
financial mathematics exam. The beta stage of the project will involve
the construction of a testing suite to insure the accuracy of the
computations in preparation for commercial use.Further ReadingGo ahead and give TmVal a try! The next section is theInstallation and Quickstartfollowed by
theUsage Tutorial. For technical
documentation, consult theAPI Reference, which links to the source code of the project.