liveBook · Manning

Fsolve

As explained in Build a Robo-Advisor with Python (From Scratch)

fsolve is a SciPy function that numerically solves nonlinear equations by finding their roots.

fsolve is a SciPy root-finding function that numerically searches for a value making an equation f(x) equal to zero. In financial calculations, that value can be an internal rate of return or a pretax withdrawal amount. section 7.1.2 section 9.2.2

Solving for a dollar-weighted return

Define the cash-flow equation, then call fsolve with the function and an initial estimate: root = fsolve(f, 0). For cash flows of 10, 10, and -19.8, the solution is approximately -0.0066, or -0.66%. section 7.1.2

A generalized financial root-finding function can:

  • Accept arbitrary cash flows and dates through extra arguments passed with args=(cashflows, dates). section 7.1.2
  • Convert YYYY-mm-dd strings to datetime objects and discount each cash flow according to elapsed days. section 7.1.2
  • Solve tax gross-up equations by returning gross withdrawal minus calculated tax minus desired net spending. section 9.2.2
Listing 7.1 Solving for a dollar-weighted return
from scipy.optimize import fsolve

def f(x):
    return 10+10/(1+x)-19.8/(1+x)**2
root = fsolve(f, 0)
print('The Dollar-Weighted Return is ', root.item())  #1

> The Dollar-Weighted Return is  -0.0066    

Use the compact listing to see the minimum fsolve call for an internal-rate-of-return calculation. section 7.1.2

Listing 7.2 General solution for the dollar-weighted return
from datetime import datetime

cashflows = [10, 10, -19.8]
dates = ['2020-01-01', '2021-01-01', '2022-01-01']
dates = [datetime.strptime(dt, '%Y-%m-%d') for dt in dates]   #1

def f(x, *args):
    cashflows, dates = args
    sumPVs = 0
    for i in range(len(cashflows)):
        Y = (dates[i] - dates[0]).days/365   #2
        sumPVs += cashflows[i]/(1+x)**Y
    return sumPVs
root = fsolve(f, 0, args=(cashflows, dates))   #3
print('The Dollar-Weighted Return is ', root.item())

Use the generalized listing to inspect date handling, arbitrary cash flows, and argument passing. section 7.1.2

Listing 9.3 Solving for the required pretax withdrawal amount
from scipy.optimize import fsolve

def gross_up(gross, net, TaxTable, deduction):
    tax = calc_taxes(gross, TaxTable, deduction)
    return gross - tax - net

root = fsolve(gross_up, 0, args=(120000, TaxTable, 13850))
print('The pre-tax withdrawal amount is ', root.item())

Use the tax-withdrawal listing to see fsolve applied to a nonlinear after-tax spending constraint. section 9.2.2

How Build a Robo-Advisor with Python (From Scratch) covers it

How deep it goes
Explains function construction, starting estimates, extra arguments, date conversion, and worked financial implementations.
Examples
Uses dollar-weighted return cash flows, dated cash flows, and a pretax IRA withdrawal solving for $120,000 after taxes.
What it assumes
Assumes a definable equation, cash-flow or tax inputs, and an initial estimate for the unknown value.
What you can do after
Apply it via SciPy to calculate an internal rate of return or solve a tax-adjusted withdrawal requirement.
How it connects
Connects root finding with internal rate of return, net present value, cash-flow discounting, taxation, and retirement withdrawals.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Build a Robo-Advisor with Python (From Scratch). The section links point to the parts of the book it draws on.