Coverage for src/scrilla/analysis/integration.py: 0%
32 statements
« prev ^ index » next coverage.py v6.4.2, created at 2022-07-18 18:14 +0000
« prev ^ index » next coverage.py v6.4.2, created at 2022-07-18 18:14 +0000
1# This file is part of scrilla: https://github.com/chinchalinchin/scrilla.
3# scrilla is free software: you can redistribute it and/or modify
4# it under the terms of the GNU General Public License version 3
5# as published by the Free Software Foundation.
7# scrilla is distributed in the hope that it will be useful,
8# but WITHOUT ANY WARRANTY; without even the implied warranty of
9# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10# GNU General Public License for more details.
12# You should have received a copy of the GNU General Public License
13# along with scrilla. If not, see <https://www.gnu.org/licenses/>
14# or <https://github.com/chinchalinchin/scrilla/blob/develop/main/LICENSE>.
16"""
17A module of functions for integrating stochastic differential equations through numerial techniques like Monte Carlo simulation.
18"""
19from typing import Callable
20from scipy.stats import norm
21from scipy.integrate import quad
22from numpy import isinf, inf
23import random
25from scrilla import settings
26from scrilla.util import errors
27import scrilla.util.outputter as outputter
29logger = outputter.Logger("scrilla.analysis.integration", settings.LOG_LEVEL)
32def generate_random_walk(periods: int):
33 return [norm.ppf(random.uniform(0, 1)) for i in range(periods)]
35# Condition: E(integral of vol ^2 dt) < inf
38def verify_volatility_condition(volatility_function: Callable) -> bool:
39 r"""
40 Ito calculus requires the class of volatility functions that scale a simple Weiner process (i.e., a process with independent, identically distributed increments with mean 0 and variance 1) satisfies the following
42 $$ \int_{0}^{\infty} {\sigma (t)}^2 \,dt < \infty $$
44 In order words, since the mean of an Weiner process increment is 0 (and thus \\(Var(X) = E(X^2)\\), this inequality imposes a condition on the process described by scaling the Weiner process by the volatility function: it must have a probability distribution that actually exists. This function returns a `bool` that signals whether or not the given function satisifies this condition.
46 Parameters
47 ----------
48 1. **volatility_function** : ``Callable``
49 A function that describes the volatility of a process as a function of time measured. Must accept scalar input.
50 """
51 integral = quad(func=lambda x: volatility_function(x)**2, a=0, b=inf)
52 return not isinf(x=integral)
54# Remember forward increments!
57def ito_integral(mean_function: Callable, volatilty_function: Callable, upper_bound: float, iterations: int = 1000):
58 r"""
59 Approximates the expectation of an Ito integral from 0 to `upper_bound` for a Weiner process described by a mean and volatility that are functions of time, i.e.
61 $$ E\{d X(t)\} = E \{ \int_{0}^{\infty} {\mu (t)} \, dt + \int_{0}^{\infty} {\sigma(t)} \, dW(t) \} $$
63 Parameters
64 ----------
65 1. **mean_function** : ``Callable``
66 A function that describes the mean of a process as a function of time. Must accept scalar input.
67 2. **volatility_function** : ``Callable``
68 A function that describes the volatility of a process as a function of time. Must accept scalar input.
69 3. **upper_bound** : ``float``
70 Upper limit for the integral.
71 4. **iterations** : ``int``
72 The number of iterations in the Reimann-Stieltjes sum.
73 """
74 if verify_volatility_condition(volatility_function=volatilty_function):
75 # NOTE: Compute Ito Integral using forward increments due to the fact
76 # the value at the end of the interval can be conditioned on
77 # the value at the beginning of the interval. In other words, Ito
78 # Processes are Martingales, ie. E(X2 | X1) = X1.
79 time_delta = upper_bound / settings.ITO_STEPS
81 averaged_integral = 0
82 for j in range(iterations):
83 random_walk = generate_random_walk(settings.ITO_STEPS)
84 this_integral, current_contribution = 0, 0
85 current_contribution = 0
86 for i in range(settings.ITO_STEPS):
87 current_time = time_delta * i
88 current_mean = mean_function(current_time)
89 current_vol = volatilty_function(current_time)
90 current_contribution = current_mean * \
91 time_delta + current_vol * random_walk[i]
92 this_integral += current_contribution
93 averaged_integral += this_integral/j
95 return averaged_integral
96 raise errors.UnboundedIntegral(
97 'E(Integral(volatility_function^2 dt) from 0 to infinity < infinity)')