Skip to content

Geometric Brownian motion

An asset whose price follows geometric Brownian motion.

  • Name: geometric_brownian_motion
  • Field: economics/finance
  • Formalism: stochastic differential equations
  • References: Black and Scholes 1973

The geometric_brownian_motion example

Example

Factory

@register_model(
    field='economics/finance',
    tags=('asset', 'sde'),
    references=('Black and Scholes 1973',),
    defaults={'until': 1.0, 'dt': 0.01},
)
def geometric_brownian_motion(price: float = 100.0, *, mu: float = 0.05, sigma: float = 0.2) -> System:
    """Return an asset whose price follows geometric Brownian motion.

    Args:
        price:
            The initial price.

        mu:
            The drift.

        sigma:
            The volatility.

    Returns:
        system:
            The model, with the asset `asset`.

    Examples:
        >>> from skcomplex.models.economics.finance import geometric_brownian_motion
        >>> geometric_brownian_motion(sigma=0.3).interactions[0].sigma
        0.3
    """
    return System(
        elements=[AssetElement('asset', price=price)],
        interactions=[GeometricBrownianMotionInteraction('gbm', target='asset', mu=mu, sigma=sigma)],
    )

Components

class AssetElement(Element):
    """Traded asset described by its price.

    Args:
        label:
            The label of the asset.

        visual:
            How the element is drawn, or `None` for `default_visual_`.

        price:
            The initial price.

    Examples:
        >>> from skcomplex.models.economics.finance import AssetElement
        >>> AssetElement('stock', price=100.0).price
        100.0
    """

    def __init__(
        self,
        label: str,
        visual: VisualSpec | None = None,
        *,
        price: Annotated[float, StateSpec(domain=RealSet.open(0, oo))] = 1.0,
    ) -> None:
        super().__init__(label, visual=visual)
        self.price = price

class GeometricBrownianMotionInteraction(RateInteraction):
    """Geometric Brownian motion, `dS = mu * S * dt + sigma * S * dW`, of the price of an asset.

    The Wiener process `dW` is independent by default, or process `index` of a `WienerProcessSpec` that correlates it
    with the motions of other assets.

    Args:
        label:
            The label of the interaction.

        target:
            The path of the asset.

        mu:
            The drift.

        sigma:
            The volatility.

        process:
            The Wiener processes, one of which drives the price, or `None` for an independent process.

        index:
            The process of `process` that drives the price, or `None` without processes.

    Examples:
        >>> from skcomplex.models.economics.finance import GeometricBrownianMotionInteraction
        >>> from skcomplex.specs import WienerProcessSpec
        >>> GeometricBrownianMotionInteraction('gbm', target='stock', sigma=0.3).sigma
        0.3
        >>> market = WienerProcessSpec('market', dimension=2)
        >>> GeometricBrownianMotionInteraction('gbm', target='stock', process=market, index=1).check().index
        1
    """

    def __init__(
        self,
        label: str,
        *,
        target: Annotated[str, RoleSpec()],
        mu: Annotated[float, ParameterSpec()] = 0.05,
        sigma: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 0.2,
        process: Annotated[WienerProcessSpec | None, RoleSpec(noise=True)] = None,
        index: int | None = None,
    ) -> None:
        super().__init__(label, target=target, state='price', process=process, index=index)
        self.mu = mu
        self.sigma = sigma

    def _get_rate(self, namespace: SimpleNamespace) -> object:
        """Return the drift `mu * S`.

        Args:
            namespace:
                The namespace of the symbols the rate can use.

        Returns:
            rate:
                The drift.
        """
        return namespace.mu * namespace.target.price

    def _get_noise(self, namespace: SimpleNamespace) -> object:
        """Return the noise amplitude `sigma * S`.

        Args:
            namespace:
                The namespace of the symbols the noise can use.

        Returns:
            noise:
                The noise amplitude.
        """
        return namespace.sigma * namespace.target.price

Equations

asset.price: diff(price_asset(t), t) == mu_gbm*price_asset(t)