Brownian motion, or pedesis, is the random motion of particles suspended in a fluid (a liquid or a gas) resulting from their collision with the fast-moving molecules in the fluid.
This pattern describes a fluid at thermal equilibrium, defined by a given temperature. Within such a fluid, there exists no preferential direction of flow (as in transport phenomena). More specifically, the fluid's overall linear and angular momenta remain null over time.
The kinetic energies of the molecular Brownian motions, together with those of molecular rotations and vibrations, sum up to the caloric component of a fluid's internal energy.
This motion is named after the botanist Robert Brown, who first described the phenomenon in 1827, while looking through a microscope at pollen of the plant Clarkia pulchella immersed in water.
In 1905, almost eighty years later, theoretical physicist Albert Einstein published a seminal paper where he modeled the motion of the pollen as being moved by individual water molecules, making one of his first major scientific contributions. This explanation of Brownian motion served as convincing evidence that atoms and molecules exist and was further verified experimentally by Jean Perrin in 1908.
Also read this enjoyable paper commemorating the 100-year of Einstein's paper.
The many-body interactions that yield the Brownian pattern cannot be solved by a model accounting for every involved molecule. In consequence, only probabilistic models applied to molecular populations can be employed to describe it. This is why it is described as a purely stochastic process in its modern form and applications.
In mathematics, the Wiener process is a real valued continuous-time stochastic process named in honor of American mathematician Norbert Wiener for his investigations on the mathematical properties of the one-dimensional Brownian motion.
It is one of the best known stochastic processes with stationary independent increments and find frequent applications in a wide range of fields covering pure and applied mathematics, economics, quantitative finance, evolutionary biology, and physics.
For example, it plays a vital role in stochastic calculus, diffusion processes and even potential theory. In applied mathematics, the Wiener process is used to represent the integral of a white noise Gaussian process, and so is useful as a model of noise in electronics engineering (see Brownian noise), instrument errors in filtering theory and unknown forces in control theory.
In physics, it is used to study Brownian motion, the diffusion of minute particles suspended in fluid, and other types of diffusion via the Fokker–Planck and Langevin equations. It also forms the basis for the rigorous path integral formulation of quantum mechanics (by the Feynman–Kac formula, a solution to the Schrödinger equation can be represented in terms of the Wiener process) and the study of eternal inflation in physical cosmology. It is also prominent in the mathematical theory of finance, in particular the Black–Scholes option pricing model.
Brownian
class¶In the Python code below, we define a class Brownian
with a few useful methods,
gen_random_walk()
: Generates motion from the Random Walk processgen_normal()
: Generates motion by drawing from the Normal Distributionstock_price()
: Models a stock price using so-called 'Geometric Brownian Motion'import numpy as np
import matplotlib.pyplot as plt
class Brownian():
"""
A Brownian motion class constructor
"""
def __init__(self,x0=0):
"""
Init class
"""
assert (type(x0)==float or type(x0)==int or x0 is None), "Expect a float or None for the initial value"
self.x0 = float(x0)
def gen_random_walk(self,n_step=100):
"""
Generate motion by random walk
Arguments:
n_step: Number of steps
Returns:
A NumPy array with `n_steps` points
"""
# Warning about the small number of steps
if n_step < 30:
print("WARNING! The number of steps is small. It may not generate a good stochastic process sequence!")
w = np.ones(n_step)*self.x0
for i in range(1,n_step):
# Sampling from the Normal distribution with probability 1/2
yi = np.random.choice([1,-1])
# Weiner process
w[i] = w[i-1]+(yi/np.sqrt(n_step))
return w
def gen_normal(self,n_step=100):
"""
Generate motion by drawing from the Normal distribution
Arguments:
n_step: Number of steps
Returns:
A NumPy array with `n_steps` points
"""
if n_step < 30:
print("WARNING! The number of steps is small. It may not generate a good stochastic process sequence!")
w = np.ones(n_step)*self.x0
for i in range(1,n_step):
# Sampling from the Normal distribution
yi = np.random.normal()
# Weiner process
w[i] = w[i-1]+(yi/np.sqrt(n_step))
return w
def stock_price(
self,
s0=100,
mu=0.2,
sigma=0.68,
deltaT=52,
dt=0.1
):
"""
Models a stock price S(t) using the Weiner process W(t) as
`S(t) = S(0).exp{(mu-(sigma^2/2).t)+sigma.W(t)}`
Arguments:
s0: Iniital stock price, default 100
mu: 'Drift' of the stock (upwards or downwards), default 1
sigma: 'Volatility' of the stock, default 1
deltaT: The time period for which the future prices are computed, default 52 (as in 52 weeks)
dt (optional): The granularity of the time-period, default 0.1
Returns:
s: A NumPy array with the simulated stock prices over the time-period deltaT
"""
n_step = int(deltaT/dt)
time_vector = np.linspace(0,deltaT,num=n_step)
# Stock variation
stock_var = (mu-(sigma**2/2))*time_vector
# Forcefully set the initial value to zero for the stock price simulation
self.x0=0
# Weiner process (calls the `gen_normal` method)
weiner_process = sigma*self.gen_normal(n_step)
# Add two time series, take exponent, and multiply by the initial stock price
s = s0*(np.exp(stock_var+weiner_process))
return s
We can use a basic stochastic process such as Random Walk, to generate the data points for a Brownian motion.
b = Brownian()
for i in range(4):
plt.plot(b.gen_random_walk(1000))
plt.show()
We can generate Brownian motion data by drawing from Normal distribution.
b = Brownian(20)
for i in range(4):
plt.plot(b.gen_normal(1000))
plt.show()
Here we simulate multiple scenarios with for 52 time periods (imagining 52 weeks a year). Note, all the stock proces start at the same point but evolve randomly along different trajectories.
Note that the dynamics is controlled by the mean and variance parameters of the underlying Normal distribution, which, somehow emulate the growth trend and the 'volatility' of the stock. For example, a stock with positive growth trend will have a positive mean. For this particular simulation, the choice of mean (mu) is 0.2 and the choice of standard deviation (square root of the variance) is 0.68.
We define a utility function for plotting first.
def plot_stock_price(mu,sigma):
"""
Plots stock price for multiple scenarios
"""
plt.figure(figsize=(9,4))
for i in range(5):
plt.plot(b.stock_price(mu=mu,
sigma=sigma,
dt=0.1))
plt.legend(['Scenario-'+str(i) for i in range(1,6)],
loc='upper left')
plt.hlines(y=100,xmin=0,xmax=520,
linestyle='--',color='k')
plt.show()
Note that, although the scenarios look sufficiently stochastic, they have a downward trend. This is because even with a positive mean, we have a slightly high spread or volatility.
plot_stock_price(mu=0.2,sigma=0.7)
We simulate the stock price again with a slightly less volatility (but with the same mean as before) and get a completely different outcome this time. The trend looks neutral i.e. some scenario shows an increase in the stock price after the 52-week time period, whereas some show a decrease.
plot_stock_price(mu=0.2,sigma=0.65)
If we lower the volatility even more, we get a clear positive trend.
plot_stock_price(mu=0.2,sigma=0.6)
In the following example, we show a two-dimensional Brownian motion much like the actual suspended particle in the fluid medium goes through.
b1 = Brownian()
b2 = Brownian()
x = b1.gen_normal(1000)
y = b2.gen_normal(1000)
plt.plot(x,y,c='b')
xmax,xmin,ymax,ymin = x.max(),x.min(),y.max(),y.min()
scale_factor = 1.25
xmax,xmin,ymax,ymin = xmax*scale_factor,xmin*scale_factor,ymax*scale_factor,ymin*scale_factor
plt.xlim(xmin,xmax)
plt.ylim(ymin,ymax)
plt.show()