Integral Calculus and Differential Equations › Ordinary Differential Equations in Finance

Michael BrenndoerferOctober 28, 20258 min read
Quantitative FinanceAll chapters
Part 1 of 15Mathematical Foundations
Chapter 7Integral Calculus and Differential Equations › Ordinary Differential Equations in Finance
Reading section 6 of 14 · Ordinary Differential Equations in Finance · 8 minSection 6 of 14 · 8 min

Book contents

Section 6 of 14 · 8 min read

Integral Calculus and Differential Equations › Ordinary Differential Equations in FinanceOne section · Sections in this chapter6 / 14 ↓
Reading tools Definitions & inline links

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

Differential equations describe relationships between quantities and their rates of change. Algebraic equations relate values directly, while differential equations relate a function to its derivatives and state the rules its path must satisfy. In finance, they model how portfolios, prices, and economic variables evolve over time, capturing the dynamic nature of markets and economies.

Differential equations encode rules of change that, when solved, reveal the trajectory of a system. If we know that interest rates revert toward a long-term mean, or that a portfolio grows proportionally to its value plus deposits, we can express these dynamics as differential equations and solve for the resulting paths.

First-Order Linear ODEs

A first-order linear ordinary differential equation has the form:

dydt+P(t)y=Q(t)\frac{dy}{dt} + P(t) y = Q(t)

The term "first-order" means the equation involves only the first derivative of yy, not higher derivatives. "Linear" means yy and its derivative appear only to the first power and are not multiplied together. On an interval where PP and QQ are continuous (or satisfy suitable integrability conditions), an integrating factor gives the general solution.

The general solution uses an integrating factor μ(t)=e∫P(t)dt\mu(t) = e^{\int P(t) dt}:

y(t)=1μ(t)[∫μ(t)Q(t) dt+C]y(t) = \frac{1}{\mu(t)} \left[ \int \mu(t) Q(t) \, dt + C \right]

where:

  • y(t)y(t): the unknown function we are solving for
  • P(t)P(t): coefficient function multiplying yy
  • Q(t)Q(t): forcing function (the non-homogeneous term)
  • μ(t)=e∫P(t)dt\mu(t) = e^{\int P(t) dt}: integrating factor that makes the left side a perfect derivative
  • CC: constant of integration, determined by initial conditions

The integrating factor works because multiplying by μ(t)\mu(t) converts the left side into ddt[μ(t)y]\frac{d}{dt}[\mu(t) y], allowing direct integration. This technique appears frequently in differential equations. By multiplying through by just the right function, we transform a seemingly intractable equation into one we can solve by straightforward integration.

Example: Portfolio Growth with Continuous Deposits

Suppose you have a portfolio earning continuous return rr and you make continuous deposits at rate DD per year. This models a retirement account where you contribute regularly while earning investment returns. The portfolio value V(t)V(t) satisfies:

dVdt=rV+D\frac{dV}{dt} = rV + D

This equation says: the rate of change of your portfolio equals the investment return (rVrV, proportional to what you have) plus your deposits (DD, a constant flow). This is a first-order linear ODE with P(t)=−rP(t) = -r and Q(t)=DQ(t) = D. The integrating factor is μ(t)=e−rt\mu(t) = e^{-rt}.

Multiplying both sides by μ(t)\mu(t):

e−rtdVdt−re−rtV=De−rte^{-rt} \frac{dV}{dt} - r e^{-rt} V = D e^{-rt}

The left side is the derivative of e−rtVe^{-rt} V:

ddt(e−rtV)=De−rt\frac{d}{dt}\left( e^{-rt} V \right) = D e^{-rt}

This is the key insight: what looked like two separate terms is a single derivative. For r≠0r \ne 0, we can integrate directly:

e−rtV=−Dre−rt+Ce^{-rt} V = -\frac{D}{r} e^{-rt} + C V(t)=Cert−DrV(t) = Ce^{rt} - \frac{D}{r}

With initial condition V(0)=V0V(0) = V_0:

V0=C−Dr  ⟹  C=V0+DrV_0 = C - \frac{D}{r} \implies C = V_0 + \frac{D}{r}

For r≠0r \ne 0, the solution is:

V(t)=(V0+Dr)ert−Dr=V0ert+Dr(ert−1)V(t) = \left( V_0 + \frac{D}{r} \right) e^{rt} - \frac{D}{r} = V_0 e^{rt} + \frac{D}{r}\left( e^{rt} - 1 \right)

The first term represents growth of the initial investment, while the second represents accumulated deposits with their earned interest. This decomposition is intuitive. Your wealth at any time is the sum of what your original stake has grown to, plus what your contributions have accumulated.

For r>0r>0, D/rD/r is the time-zero value of a perpetual continuous cash-flow rate DD discounted continuously at rate rr. In the finite-horizon accumulation formula, the stable term is D expm1⁡(rt)/rD\,\operatorname{expm1}(rt)/r. Its continuous limit at r=0r=0 is DtDt, so the zero-rate solution is V(t)=V0+DtV(t)=V_0+Dt rather than a division by zero.

In[20]:
Code
import numpy as np


def portfolio_value(t, V0, r, D):
    """Analytical solution for portfolio with continuous deposits."""
    return V0 + D * t if r == 0 else V0 * np.exp(r * t) + D * np.expm1(r * t) / r


# Parameters
V0 = 10000  # Initial investment
r = 0.07  # 7% annual return
D = 6000  # $6,000 annual deposits

t = np.linspace(0, 30, 300)
V = portfolio_value(t, V0, r, D)

# Components
growth_component = V0 * np.exp(r * t)
deposit_component = portfolio_value(t, 0, r, D)
Out[21]:
Visualization
Stacked area chart showing portfolio value split between initial investment growth and deposit accumulation over 30 years.
Portfolio growth over 30 years from the initial investment and continuous deposits. For these parameters, repeated deposits and their accumulated interest form the larger component; both components earn the same return.
Out[22]:
Console
After 30 years:
  Initial investment: $10,000
  Total deposits: $180,000
  Final portfolio value: $695,904.83
  Investment gain: $505,904.83

The power of compounding is evident: starting with $10,000 and depositing $6,000 per year, the portfolio grows to over $600,000 after 30 years, with investment gains far exceeding the total contributions.

Numerical Solution of ODEs

Numerical methods can compute trajectories when a convenient known closed form is unavailable. Time-varying coefficients and nonlinearity do not automatically prevent analytical solutions. The examples below also use numerical evaluation when a benchmark formula is known. The SciPy function odeint wraps LSODA and uses requested local-error tolerances; success does not guarantee a chosen global error. SciPy recommends solve_ivp for new code.

Example: Mean-Reverting Interest Rates

The constant-parameter Vasicek model is the stochastic differential equation drt=κ(θ−rt)dt+σrdWtdr_t=\kappa(\theta-r_t)dt+\sigma_r dW_t. Assume κ>0\kappa>0 for mean reversion. The ODE below retains only its drift and also governs the conditional mean m(t)=E[rt∣r0]m(t)=\mathbb{E}[r_t\mid r_0]:

dmdt=κ(θ−m)\frac{dm}{dt} = \kappa(\theta - m)

where:

  • mm: deterministic drift trajectory, equivalently the conditional mean rate at time tt
  • κ>0\kappa>0: speed of mean reversion (larger positive values mean faster reversion)
  • θ\theta: long-term mean interest rate
  • σr\sigma_r: short-rate volatility in the full SDE; dWtdW_t is a Brownian increment

Mean reversion is a modeling assumption, not a claim that central banks or arbitrage impose hard bounds. In the drift ODE, m>θm>\theta gives a negative derivative and m<θm<\theta gives a positive derivative; larger κ\kappa means faster convergence of the conditional mean. For positive elapsed time and nonzero volatility, the constant-parameter Vasicek rate has a nondegenerate Gaussian law with support on the entire real line. At time zero or zero volatility, its conditional law is degenerate instead.

This conditional-mean ODE has the analytical solution m(t)=θ+(r0−θ)e−κtm(t)=\theta+(r_0-\theta)e^{-\kappa t}, but we solve it numerically to demonstrate the technique:

In[23]:
Code
import numpy as np
from scipy.integrate import odeint


def vasicek_drift(r, t, kappa, theta):
    """Drift term of Vasicek model (deterministic part)."""
    return kappa * (theta - r)


# Model parameters
kappa = 0.5  # Mean reversion speed
theta = 0.05  # Long-term mean (5%)

# Solve for different starting rates
t = np.linspace(0, 10, 100)
r0_values = [0.02, 0.05, 0.08]  # Starting rates

solutions = {}
for r0 in r0_values:
    solutions[r0] = odeint(vasicek_drift, r0, t, args=(kappa, theta)).flatten()
Out[24]:
Visualization
Three deterministic Vasicek drift or conditional-mean trajectories approaching 5% from initial rates of 2%, 5%, and 8%; these are not simulated stochastic paths.
Deterministic solutions of the Vasicek drift ODE, equivalently conditional-mean trajectories of the constant-parameter Vasicek SDE. Starting means above or below 5% approach 5% over time.

The mean reversion parameter κ=0.5\kappa = 0.5 implies a half-life of ln⁡(2)/κ≈1.4\ln(2)/\kappa \approx 1.4 years: after about 1.4 years, the conditional mean's deviation has halved, not every individual stochastic path's deviation.

Out[25]:
Console
Mean reversion speed (kappa): 0.5
Long-term mean (theta): 5.0%
Half-life of deviation: 1.39 years