Skip to content

API Reference

finance

Module containing components for financial mathematics, including derivative pricing models.

AsianOption

Bases: Claim

Model an Asian option contingent claim.

The Asian option is a claim comes in two types: call and put. The payoff of an Asian call option is given by

\[ \max(\bar{S}_T - K, 0), \]

while the payoff of an Asian put option is given by

\[ \max(K - \bar{S}_T, 0), \]

where \(\bar{S}_T\) is the average underlying asset price over the option's life and \(K\) is the strike price.

Parameters:

Name Type Description Default
pricing_model PricingModel

A pricing model representing the underlying asset price dynamics.

required
strike Real

The strike price of the Asian option.

required
option_type str

The type of the Asian option. It can be either "call" or "put".

"call"

Raises:

Type Description
TypeError

If the strike price is not a positive real number, or if the pricing model is not a PricingModel, or if the option type is not "call" or "put".

payoff property

payoff

Return the payoff of the Asian option.

Returns:

Name Type Description
option RandomVariable

A random variable representing the payoff of the Asian option.

BinomialPricingModel

Bases: PricingModel

Binomial pricing model for a risky asset.

This class produces a binomial model for the price proccess \(S_t\) of a risky asset, often referred to generically as a stock. Beginning from its initial price \(S_0\), and given a time horizon \(T\), this model supposes that the price process evolves according to the following dynamics:

\[ S_{t+1} = S_t Z_{t+1}, \]

for each \(t=0,1,\ldots,T-1\), where \(Z_t\) is a random variable that takes the value \(u>1\) with some probability \(p\) and the value \(d = 1/u\) with probability \(1-p\). The factors \(u\) and \(d\) are called the up-factor and down-factor, respectively.

The risky asset is assumed to be traded in a market along with a non-risky asset with gross return \(R = 1 + r\) at each time step, where \(r\) is the risk-free rate. The non-risky asset is often conceptualized as a bank account with per-period interest rate \(r\).

The probability \(p\) is the real-world probability that drives the price process of the stock. However, under the no-arbitrage condition \(d < R< u\), a second probability \(q\), called the risk-neutral probability, may be defined via the equation

\[ q = \frac{R - d}{u - d}. \]

This risk-neutral probability is the key component in pricing various contingent claims using the binomial model.

As a subclass of StochasticProcess, an instance of BinomialPricingModel carries a probability_measure attribute, which corresponds to the real-world measure. The risk-neutral measure is accessible via the risk_neutral_measure property.

Parameters:

Name Type Description Default
initial_price Real

The initial price of the risky asset.

required
up_factor Real

The up-factor of the model, which must be greater than 1.

required
risk_free_rate Real

The risk-free rate of the non-risky asset, which must be positive.

required
time Time | None

The time index for the pricing model.

None
name Hashable | None

The name of the stochastic process.

'S'

Raises:

Type Description
TypeError

If any of the parameters are of the wrong type or do not satisfy the required conditions.

ValueError

If the no-arbitrage condition is violated.

Examples:

>>> from sigalg.core import Time
>>> from sigalg.finance import BinomialPricingModel
>>> S_0 = 100
>>> u = 1.1
>>> p = 0.7
>>> r = 0.01
>>> T = 3
>>> time = Time.discrete(length=T)
>>> S = BinomialPricingModel(
...     initial_price=S_0,
...     up_factor=u,
...     up_prob=p,
...     risk_free_rate=r,
...     time=time,
... ).from_enumeration()
>>> S
Stochastic process 'S':
time            0           1           2           3
trajectory
0           100.0  110.000000  121.000000  133.100000
1           100.0   90.909091  100.000000  110.000000
2           100.0   90.909091   82.644628   90.909091
3           100.0   90.909091   82.644628   75.131480

driving_process property

driving_process

Return the driving process of the binomial pricing model.

The driving process is an IID process \(Z_t\) representing the up and down movements of the underlying asset in the binomial model. It takes the value \(u\) with probability \(p\) and the value \(d\) with probability \(1-p\), where \(u\) is the up-factor, \(d\) is the down-factor, and \(p\) is the real-world probability of an up move. The driving process is defined for times \(t=1,2,\ldots,T\), where \(T\) is the final time of the model.

This property should only be used for small values of \(T\), as the number of price trajectories is equal to \(2^T\).

Returns:

Name Type Description
driving_process StochasticProcess

The driving process of the binomial pricing model.

risk_neutral_measure property

risk_neutral_measure

Later.

from_enumeration

from_enumeration(length=None, enum_mode='sparse', **kwargs)

Generate price trajectories of the binomial pricing model via enumeration.

Suppose that \(S_t\) is the price process of the underlying asset, and that \(u\) and \(d\) are the up- and down-factors of the model, respectively. Then \(S_t\) is a random walk on the set of prices

\[ \{S_0 u^m d^n : m,n \geq 0\}. \]

At a fixed time horizon \(T\), the final price \(S_T\) takes one of the \(T+1\) values in the set

\[ \{S_0 u^{n} d^{T-n} : 0\leq n \leq T\}. \]

There are exactly \(\binom{T}{n}\) many random walks (i.e., price trajectories) that terminate at the final price \(S_T = S_0 u^{n} d^{T-n}\), and thus a total of \(2^T = \sum_{n=0}^T \binom{T}{n}\) many random walks that end at some final price.

This method enumerates these price trajectories in one of two modes: either dense mode or sparse mode. In dense mode, the method enumerates all \(2^T\) price trajectories of the model. In sparse mode, the method enumerates only \(T+1\) price trajectories of the model, which are of the special forms:

\[ \begin{gather*} S_0 \to S_0 u \to S_0u^2 \to S_0u^3 \to \ldots \to S_0u^{T-1} \to S_0 u^T \\ S_0 \to S_0 d \to S_0du \to S_0du^2 \to \ldots \to S_0du^{T-2} \to S_0 du^{T-1} \\ S_0 \to S_0 d \to S_0d^2 \to S_0d^2u \to \ldots \to S_0d^2u^{T-3} \to S_0 d^2u^{T-2} \\ \cdots \quad \cdots \quad \cdots \quad \\ S_0 \to S_0 d \to S_0d^2 \to S_0d^3 \to \ldots \to S_0d^{T-1} \to S_0 d^T \end{gather*} \]

The dense mode of enumeration should only be used for small values of \(T\), as the number of price trajectories grows exponentially in \(T\).

Parameters:

Name Type Description Default
length int | None

The length of the enumeration, which must be a positive integer. If None, the length of the enumeration is taken to be the length of the time index of the model.

None
enum_mode str

The mode of enumeration, which must be either "sparse" or "dense". See above for details.

"sparse"

Raises:

Type Description
TypeError

If enum_mode is not a string or is not one of "sparse" or "dense".

replicating_portfolio

replicating_portfolio(claim)

Compute the replicating portfolio for a given contingent claim.

The core idea of a replicating portfolio is this: Suppose that an individual sells a contingent claim on an underlying asset (generically called an underlying). The seller accepts a premium from the buyer for the claim at time \(t=0\), and then at some specified maturity time \(t=T\), the seller must pay the exercise value of the claim to the buyer. The claim is a derivative, in the sense that its value depends on (or derives from) the price of the underlying. The seller is thus interested in hedging their short position on the claim against an increase in the price of the underlying, which would increase the exercise value of the claim that the seller would owe the buyer.

The underlying asset is assumed to be traded in a market that includes a bank account with risk-free, per-period interest rate \(r\). The seller's hedging strategy is to trade in the underlying asset itself, as well as hold a cash position at the bank, so that when the contingent claim matures, the seller's portfolio will cover the exercise value owed to the buyer.

The replicating portfolio thus consists of a pair \((B_t,N_t)\) of processes, indexed \(t=0,1,\ldots,T-1\), where \(B_t\) represents the cash position at time \(t\), and \(N_t\) counts the number of units of the underlying held in the portfolio at time \(t\). A third process \(V_t\) represents the total value of the portfolio, given by

\[ V_t = B_t + S_t N_t, \]

where \(S_t\) is the price of the underlying at time \(t\). A positive value of \(B_t\) represents money held in the bank accruing interest for the seller at rate \(r\), while a negative value represents a loan on which the seller pays interest at rate \(r\). A positive value of \(N_t\) represents a long position on the underlying, while a negative value represents a short position.

The replicating portfolio is self-financing, in the sense that

\[ V_t = (1+r) B_{t-1} + S_t N_{t-1} \]

for each \(t=1,2,\ldots,T\). The right-hand side of this equation represents the evolution of the value of the portfolio over the time interval \([t-1,t]\), in which the amount \(B_{t-1}\) in the bank accrues interest at rate \(r\) and the price of the underlying changes from \(S_{t-1}\) to \(S_t\). This equation says that this evolved value of the old portfolio is equal to the value \(V_t\) of the new portfolio at time \(t\).

The existence of the replicating portfolio also allows us to determine a fair, "risk-neutral" premium for the contingent claim paid by the buyer. Under the no-arbitrage assumption, this premium should coincide with the initial price

\[ V_0 = B_0 + S_0 N_0 \]

of the replicating portfolio.

Recall that from_enumeration method generates price trajectories in one of two modes: either dense mode or sparse mode. In dense mode, the method enumerates all \(2^T\) price trajectories of the model. In sparse mode, the method enumerates only the following canonical \(T+1\) price trajectories:

\[ \begin{gather*} S_0 \to S_0 u \to S_0u^2 \to S_0u^3 \to \ldots \to S_0u^{T-1} \to S_0 u^T \\ S_0 \to S_0 d \to S_0du \to S_0du^2 \to \ldots \to S_0du^{T-2} \to S_0 du^{T-1} \\ S_0 \to S_0 d \to S_0d^2 \to S_0d^2u \to \ldots \to S_0d^2u^{T-3} \to S_0 d^2u^{T-2} \\ \cdots \quad \cdots \quad \cdots \quad \\ S_0 \to S_0 d \to S_0d^2 \to S_0d^3 \to \ldots \to S_0d^{T-1} \to S_0 d^T \end{gather*} \]

where \(u\) and \(d\) are the up- and down-factors of the model. If the price trajectories have been enumerated in dense mode, then the replicating portfolio is computed via backward induction through the full binomial tree of price trajectories. If the price trajectories have been enumerated in sparse mode, and if the price process of the claim is path-independent, then the replicating portfolio is computed via backward induction through the reduced binomial tree of \(T+1\) price trajectories described above.

Parameters:

Name Type Description Default
claim Claim

The contingent claim for which to compute the replicating portfolio. The payoff of the claim must be defined on the same domain as the price process of the underlying asset, and the price trajectories of the underlying asset must have been enumerated before calling this method.

required

Raises:

Type Description
TypeError

If the claim is not an instance of Claim or if the claim payoff is not defined on the same domain as the price process of the underlying asset.

ValueError

If the price trajectories have not been enumerated.

Returns:

Type Description
bank_value, underlying_units, portfolio_value, risk_neutral_price : tuple[StochasticProcess, StochasticProcess, StochasticProcess, Real]

A tuple containing the bank account process, the underlying units process, the total portfolio value process, and the risk-neutral price of the claim.

Examples:

>>> from sigalg.core import Time
>>> from sigalg.finance import AsianOption, BinomialPricingModel, EuropeanOption
>>> S_0 = 100
>>> u = 1.1
>>> p = 0.7
>>> r = 0.01
>>> T = Time.discrete(length=3)
>>> S = BinomialPricingModel(
...     initial_price=S_0, up_factor=u, up_prob=p, risk_free_rate=r, time=T
... )
>>> S.from_enumeration(enum_mode="dense")
Stochastic process 'S':
time          0           1           2           3
trajectory
0           100  110.000000  121.000000  133.100000
1           100  110.000000  121.000000  110.000000
2           100  110.000000  100.000000  110.000000
3           100  110.000000  100.000000   90.909091
4           100   90.909091  100.000000  110.000000
5           100   90.909091  100.000000   90.909091
6           100   90.909091   82.644628   90.909091
7           100   90.909091   82.644628   75.131480
>>> K = 100
>>> asian_call = AsianOption(pricing_model=S, strike=K, option_type="call")
>>> B, N, V, price = S.replicating_portfolio(claim=asian_call)
>>> print(B)
Stochastic process 'bank_account_value':
time                0          1          2
trajectory
0          -38.134572 -46.564062 -17.079208
1          -38.134572 -46.564062 -17.079208
2          -38.134572 -46.564062 -22.277228
3          -38.134572 -46.564062 -22.277228
4          -38.134572  -0.560775  -1.071536
5          -38.134572  -0.560775  -1.071536
6          -38.134572  -0.560775   0.000000
7          -38.134572  -0.560775   0.000000
>>> print(N)
Stochastic process 'underlying_units':
time              0         1         2
trajectory
0           0.42436  0.497525  0.250000
1           0.42436  0.497525  0.250000
2           0.42436  0.497525  0.250000
3           0.42436  0.497525  0.250000
4           0.42436  0.006853  0.011905
5           0.42436  0.006853  0.011905
6           0.42436  0.006853 -0.000000
7           0.42436  0.006853 -0.000000
>>> print(V)
Stochastic process 'portfolio_value':
time               0         1          2          3
trajectory
0           4.301408  8.163660  13.170792  16.025000
1           4.301408  8.163660  13.170792  10.250000
2           4.301408  8.163660   2.722772   5.000000
3           4.301408  8.163660   2.722772   0.227273
4           4.301408  0.062246   0.118940   0.227273
5           4.301408  0.062246   0.118940  -0.000000
6           4.301408  0.062246  -0.000000  -0.000000
7           4.301408  0.062246  -0.000000  -0.000000
>>> print(price)
4.301408148315952
>>> S.from_enumeration(enum_mode="sparse")
Stochastic process 'S':
time            0           1           2           3
trajectory
0           100.0  110.000000  121.000000  133.100000
1           100.0   90.909091  100.000000  110.000000
2           100.0   90.909091   82.644628   90.909091
3           100.0   90.909091   82.644628   75.131480
>>> K = 100
>>> euro_call = EuropeanOption(pricing_model=S, strike=K, option_type="call")
>>> B, N, V, price = S.replicating_portfolio(claim=euro_call)
>>> print(B)
Stochastic process 'bank_account_value':
time                0          1          2
trajectory
0          -50.150931 -73.822294 -99.009901
1          -50.150931 -24.674118 -47.147572
2          -50.150931 -24.674118  -0.000000
3          -50.150931 -24.674118  -0.000000
>>> print(N)
Stochastic process 'underlying_units':
time               0         1        2
trajectory
0           0.587304  0.797939  1.00000
1           0.587304  0.301542  0.52381
2           0.587304  0.301542  0.00000
3           0.587304  0.301542  0.00000
>>> print(V)
Stochastic process 'portfolio_value':
time               0          1          2     3
trajectory
0           8.579463  13.950993  21.990099  33.1
1           8.579463   2.738827   5.233380  10.0
2           8.579463   2.738827  -0.000000  -0.0
3           8.579463   2.738827  -0.000000  -0.0
>>> print(price)
8.57946313365138

Claim

Bases: ABC

Abstract base class for various types of contingent claims.

payoff abstractmethod property

payoff

Return the payoff of the claim as a random variable.

EuropeanOption

Bases: Claim

Model a European option contingent claim.

The European option is a claim comes in two types: call and put. The payoff of a European call option is given by

\[ \max(S_T - K, 0), \]

while the payoff of a European put option is given by

\[ \max(K - S_T, 0), \]

where \(S_T\) is the underlying asset price at maturity and \(K\) is the strike price.

Parameters:

Name Type Description Default
pricing_model PricingModel

A pricing model representing the underlying asset price dynamics.

required
strike Real

The strike price of the European option.

required
option_type str

The type of the European option. It can be either "call" or "put".

"call"

Raises:

Type Description
TypeError

If the strike price is not a positive real number, or if the pricing model is not a PricingModel, or if the option type is not "call" or "put".

payoff property

payoff

Return the payoff of the European option.

Returns:

Name Type Description
option RandomVariable

A random variable representing the payoff of the European option.