diff --git a/.circleci/config.yml b/.circleci/config.yml index 46e35f3..f7c4bf5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,6 +3,7 @@ version: 2 shared: &shared working_directory: ~/finsim + resource_class: medium+ steps: - checkout diff --git a/.github/workflow/publish-to-pypi.yaml b/.github/workflows/publish-to-pypi.yaml similarity index 100% rename from .github/workflow/publish-to-pypi.yaml rename to .github/workflows/publish-to-pypi.yaml diff --git a/.github/workflow/tests.yaml b/.github/workflows/tests.yaml similarity index 80% rename from .github/workflow/tests.yaml rename to .github/workflows/tests.yaml index 21eb372..62c9e8b 100644 --- a/.github/workflow/tests.yaml +++ b/.github/workflows/tests.yaml @@ -2,9 +2,10 @@ name: Python package CI on: push: - branches: [ master ] + branches: + - '**' pull_request: - branches: [ master ] + branches: [ master, develop ] jobs: build_and_test: @@ -23,9 +24,9 @@ jobs: - name: Install dependencies run: | pip install --user --upgrade pip - pip install --user --upgrade setuptools wheel Cython + pip install --user --upgrade setuptools wheel pip cache purge pip install --user . pip install --user .[test] - name: Run tests - run: pytest --doctest-cython + run: pytest diff --git a/pyproject.toml b/pyproject.toml index 1fe7af9..6441df7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "finsim" -version = "1.2.0" +version = "1.2.1" authors = [ {name = "Kwan Yuet Stephen Ho", email = "stephenhky@yahoo.com.hk"} ] @@ -37,7 +37,6 @@ dependencies = [ "openpyxl>=3.1.0", "numba", "typing-extensions", - "nptyping", "pydantic" ] diff --git a/src/finsim/estimate/fit.py b/src/finsim/estimate/fit.py index ea90da5..64f390a 100644 --- a/src/finsim/estimate/fit.py +++ b/src/finsim/estimate/fit.py @@ -1,9 +1,9 @@ -from typing import Literal +from typing import Literal, Annotated from itertools import product import numpy as np -from nptyping import NDArray, Shape, Float, Datetime64 +from numpy.typing import NDArray from .constants import dividing_factors_dict from .native.pyfit import python_fit_BlackScholesMerton_model, python_fit_multivariate_BlackScholesMerton_model @@ -12,8 +12,8 @@ # Note: always round-off to seconds first, but flexible about the unit to be used. def fit_BlackScholesMerton_model( - timestamps: NDArray[Shape["*"], Datetime64], - prices: NDArray[Shape["*"], Float], + timestamps: Annotated[NDArray[np.datetime64], Literal["1D array"]], + prices: Annotated[NDArray[np.float64], Literal["1D array"]], unit: Literal['second', 'minute', 'hour', 'day', 'year']='year' ) -> tuple[float, float]: """Fit a Black-Scholes-Merton model to price data to estimate rate of return and volatility. @@ -46,10 +46,10 @@ def fit_BlackScholesMerton_model( def fit_multivariate_BlackScholesMerton_model( - timestamps: NDArray[Shape["*"], Datetime64], - multiprices: NDArray[Shape["*, *"], Float], + timestamps: Annotated[NDArray[np.datetime64], Literal["1D array"]], + multiprices: Annotated[NDArray[np.float64], Literal["1D array"]], unit: Literal['second', 'minute', 'hour', 'day', 'year']='year', -) -> tuple[NDArray[Shape["*"], Float], NDArray[Shape["*, *"], Float]]: +) -> tuple[Annotated[NDArray[np.float64], Literal["1D array"]], Annotated[NDArray[np.float64], Literal["2D array"]]]: """Fit a multivariate Black-Scholes-Merton model to price data for multiple assets. This function estimates the parameters of the multivariate Black-Scholes-Merton model, @@ -82,9 +82,9 @@ def fit_multivariate_BlackScholesMerton_model( ######## routines below are for time-weighted portfolio building def fit_timeweighted_BlackScholesMerton_model( - timestamps: NDArray[Shape["*"], Datetime64], - prices: NDArray[Shape["*"], Float], - weights: NDArray[Shape["*"], Float], + timestamps: Annotated[NDArray[np.datetime64], Literal["1D array"]], + prices: Annotated[NDArray[np.float64], Literal["1D array"]], + weights: Annotated[NDArray[np.float64], Literal["1D array"]], unit: Literal['second', 'minute', 'hour', 'day', 'year']='year' ) -> tuple[float, float]: """Fit a time-weighted Black-Scholes-Merton model to price data. @@ -120,11 +120,11 @@ def fit_timeweighted_BlackScholesMerton_model( def fit_timeweighted_multivariate_BlackScholesMerton_model( - timestamps: NDArray[Shape["*"], Datetime64], - multiprices: NDArray[Shape["*, *"], Float], - weights: NDArray[Shape["*"], Float], + timestamps: Annotated[NDArray[np.datetime64], Literal["1D array"]], + multiprices: Annotated[NDArray[np.float64], Literal["2D array"]], + weights: Annotated[NDArray[np.float64], Literal["1D array"]], unit: Literal['second', 'minute', 'hour', 'day', 'year']='year' -) -> tuple[NDArray[Shape["*"], Float], NDArray[Shape["*, *"], Float]]: +) -> tuple[Annotated[NDArray[np.float64], Literal["1D array"]], Annotated[NDArray[np.float64], Literal["2D array"]]]: """Fit a time-weighted multivariate Black-Scholes-Merton model to price data for multiple assets. This function estimates the parameters of the multivariate Black-Scholes-Merton model diff --git a/src/finsim/estimate/native/pyfit.py b/src/finsim/estimate/native/pyfit.py index 83367ab..c72c53b 100644 --- a/src/finsim/estimate/native/pyfit.py +++ b/src/finsim/estimate/native/pyfit.py @@ -1,13 +1,15 @@ +from typing import Literal, Annotated + import numpy as np +from numpy.typing import NDArray import numba as nb -from nptyping import NDArray, Shape, Float @nb.njit def python_fit_BlackScholesMerton_model( - ts: NDArray[Shape["*"], Float], - prices: NDArray[Shape["*"], Float] + ts: Annotated[NDArray[np.float64], Literal["1D array"]], + prices: Annotated[NDArray[np.float64], Literal["1D array"]] ) -> tuple[float, float]: """Fit a Black-Scholes-Merton model to price data using Python implementation. @@ -35,9 +37,9 @@ def python_fit_BlackScholesMerton_model( @nb.njit def python_fit_multivariate_BlackScholesMerton_model( - ts: NDArray[Shape["*"], Float], - multiprices: NDArray[Shape["*, *"], Float] -) -> tuple[NDArray[Shape["*"], Float], NDArray[Shape["*, *"], Float]]: + ts: Annotated[NDArray[np.float64], Literal["1D array"]], + multiprices: Annotated[NDArray[np.float64], Literal["2D array"]] +) -> tuple[Annotated[NDArray[np.float64], Literal["1D array"]], Annotated[NDArray[np.float64], Literal["2D array"]]]: """Fit a multivariate Black-Scholes-Merton model to price data for multiple assets. This function estimates the parameters of the multivariate Black-Scholes-Merton model, diff --git a/src/finsim/estimate/native/pyrisk.py b/src/finsim/estimate/native/pyrisk.py index 72541ea..67c82da 100644 --- a/src/finsim/estimate/native/pyrisk.py +++ b/src/finsim/estimate/native/pyrisk.py @@ -1,13 +1,15 @@ +from typing import Literal, Annotated + import numpy as np +from numpy.typing import NDArray import numba as nb -from nptyping import NDArray, Shape, Float @nb.njit def python_estimate_downside_risk( - ts: NDArray[Shape["*"], Float], - prices: NDArray[Shape["*"], Float], + ts: Annotated[NDArray[np.float64], Literal["1D array"]], + prices: Annotated[NDArray[np.float64], Literal["1D array"]], target_return: float ) -> float: """Estimate the downside risk of an asset based on historical price data. @@ -35,8 +37,8 @@ def python_estimate_downside_risk( @nb.njit def python_estimate_upside_risk( - ts: NDArray[Shape["*"], Float], - prices: NDArray[Shape["*"], Float], + ts: Annotated[NDArray[np.float64], Literal["1D array"]], + prices: Annotated[NDArray[np.float64], Literal["1D array"]], target_return: float ) -> float: """Estimate the upside risk of an asset based on historical price data. diff --git a/src/finsim/estimate/risk.py b/src/finsim/estimate/risk.py index f0c469a..83e1681 100644 --- a/src/finsim/estimate/risk.py +++ b/src/finsim/estimate/risk.py @@ -1,17 +1,17 @@ -from typing import Literal +from typing import Literal, Annotated import numpy as np +from numpy.typing import NDArray from scipy import stats -from nptyping import NDArray, Shape, Float, Datetime64 from .native.pyrisk import python_estimate_downside_risk, python_estimate_upside_risk from .constants import dividing_factors_dict def estimate_downside_risk( - timestamps: NDArray[Shape["*"], Datetime64], - prices: NDArray[Shape["*"], Float], + timestamps: Annotated[NDArray[np.datetime64], Literal["1D array"]], + prices: Annotated[NDArray[np.float64], Literal["1D array"]], target_return: float, unit: Literal['second', 'minute', 'hour', 'day', 'year']='year' ) -> float: @@ -43,8 +43,8 @@ def estimate_downside_risk( def estimate_upside_risk( - timestamps: NDArray[Shape["*"], Datetime64], - prices: NDArray[Shape["*"], Float], + timestamps: Annotated[NDArray[np.datetime64], Literal["1D array"]], + prices: Annotated[NDArray[np.float64], Literal["1D array"]], target_return: float, unit: Literal['second', 'minute', 'hour', 'day', 'year'] = 'year' ) -> float: @@ -76,9 +76,9 @@ def estimate_upside_risk( def estimate_beta( - timestamps: NDArray[Shape["*"], Datetime64], - prices: NDArray[Shape["*"], Float], - market_prices: NDArray[Shape["*"], Float], + timestamps: Annotated[NDArray[np.datetime64], Literal["1D array"]], + prices: Annotated[NDArray[np.float64], Literal["1D array"]], + market_prices: Annotated[NDArray[np.float64], Literal["1D array"]], unit: Literal['second', 'minute', 'hour', 'day', 'year']='year' ) -> float: """Estimate the beta coefficient of an asset relative to market performance. diff --git a/src/finsim/portfolio/optimize/metrics.py b/src/finsim/portfolio/optimize/metrics.py index 657cd62..fca3385 100644 --- a/src/finsim/portfolio/optimize/metrics.py +++ b/src/finsim/portfolio/optimize/metrics.py @@ -1,15 +1,16 @@ -from typing import Literal +from typing import Literal, Annotated -from nptyping import NDArray, Shape, Float +import numpy as np +from numpy.typing import NDArray from .native.pymetrics import python_sharpe_ratio, python_mpt_costfunction, python_mpt_entropy_costfunction def sharpe_ratio( - weights: NDArray[Shape["*"], Float], - r: NDArray[Shape["*"], Float], - cov: NDArray[Shape["*, *"], Float], + weights: Annotated[NDArray[np.float64], Literal["1D array"]], + r: Annotated[NDArray[np.float64], Literal["1D array"]], + cov: Annotated[NDArray[np.float64], Literal["2D array"]], rf: float ) -> float: """Calculate the Sharpe ratio for a portfolio. @@ -27,9 +28,9 @@ def sharpe_ratio( def mpt_costfunction( - weights: NDArray[Shape["*"], Float], - r: NDArray[Shape["*"], Float], - cov: NDArray[Shape["*, *"], Float], + weights: Annotated[NDArray[np.float64], Literal["1D array"]], + r: Annotated[NDArray[np.float64], Literal["1D array"]], + cov: Annotated[NDArray[np.float64], Literal["2D array"]], rf: float, lamb: float, V0: float=10. @@ -52,9 +53,9 @@ def mpt_costfunction( def mpt_entropy_costfunction( - weights: NDArray[Shape["*"], Float], - r: NDArray[Shape["*"], Float], - cov: NDArray[Shape["*, *"], Float], + weights: Annotated[NDArray[np.float64], Literal["1D array"]], + r: Annotated[NDArray[np.float64], Literal["1D array"]], + cov: Annotated[NDArray[np.float64], Literal["2D array"]], rf: float, lamb0: float, lamb1: float, diff --git a/src/finsim/portfolio/optimize/native/pymetrics.py b/src/finsim/portfolio/optimize/native/pymetrics.py index f52f75f..2c0cb97 100644 --- a/src/finsim/portfolio/optimize/native/pymetrics.py +++ b/src/finsim/portfolio/optimize/native/pymetrics.py @@ -1,14 +1,16 @@ +from typing import Literal, Annotated + import numpy as np +from numpy.typing import NDArray import numba as nb -from nptyping import NDArray, Shape, Float @nb.njit def python_sharpe_ratio( - weights: NDArray[Shape["*"], Float], - r: NDArray[Shape["*"], Float], - cov: NDArray[Shape["*, *"], Float], + weights: Annotated[NDArray[np.float64], Literal["1D array"]], + r: Annotated[NDArray[np.float64], Literal["1D array"]], + cov: Annotated[NDArray[np.float64], Literal["2D array"]], rf: float ) -> float: """Calculate the Sharpe ratio for a portfolio using Python/Numba. @@ -30,9 +32,9 @@ def python_sharpe_ratio( @nb.njit def python_mpt_costfunction( - weights: NDArray[Shape["*"], Float], - r: NDArray[Shape["*"], Float], - cov: NDArray[Shape["*, *"], Float], + weights: Annotated[NDArray[np.float64], Literal["1D array"]], + r: Annotated[NDArray[np.float64], Literal["1D array"]], + cov: Annotated[NDArray[np.float64], Literal["2D array"]], rf: float, lamb: float, V0: float=10. @@ -57,9 +59,9 @@ def python_mpt_costfunction( @nb.njit def python_mpt_entropy_costfunction( - weights: NDArray[Shape["*"], Float], - r: NDArray[Shape["*"], Float], - cov: NDArray[Shape["*, *"], Float], + weights: Annotated[NDArray[np.float64], Literal["1D array"]], + r: Annotated[NDArray[np.float64], Literal["1D array"]], + cov: Annotated[NDArray[np.float64], Literal["2D array"]], rf: float, lamb0: float, lamb1: float, diff --git a/src/finsim/portfolio/optimize/numerics.py b/src/finsim/portfolio/optimize/numerics.py index 7d102e7..0b20f8b 100644 --- a/src/finsim/portfolio/optimize/numerics.py +++ b/src/finsim/portfolio/optimize/numerics.py @@ -3,15 +3,15 @@ from functools import partial from itertools import product from datetime import datetime -from typing import Optional +from typing import Optional, Literal, Annotated from os import PathLike import numpy as np +from numpy.typing import NDArray import pandas as pd from scipy.optimize import minimize, OptimizeResult from tqdm import tqdm import numba as nb -from nptyping import NDArray, Shape, Float from .metrics import sharpe_ratio, mpt_costfunction, mpt_entropy_costfunction from ..helper import align_timestamps_stock_dataframes @@ -23,7 +23,7 @@ @nb.njit(nb.float64(nb.float64[:], nb.float64, nb.int32)) def getarrayelementminusminvalue( - array: NDArray[Shape["*"], Float], + array: Annotated[NDArray[np.float64], Literal["1D array"]], minvalue: float, index: int ) -> float: @@ -41,7 +41,10 @@ def getarrayelementminusminvalue( @nb.njit(nb.float64(nb.float64[:], nb.float64)) -def checksumarray(array: NDArray[Shape["*"], Float], total: float) -> float: +def checksumarray( + array: Annotated[NDArray[np.float64], Literal["1D array"]], + total: float +) -> float: """Calculate the difference between a total and the sum of an array. Args: @@ -55,8 +58,8 @@ def checksumarray(array: NDArray[Shape["*"], Float], total: float) -> float: def optimized_portfolio_on_sharperatio( - r: NDArray[Shape["*"], Float], - cov: NDArray[Shape["*, *"], Float], + r: Annotated[NDArray[np.float64], Literal["1D array"]], + cov: Annotated[NDArray[np.float64], Literal["2D array"]], rf: float, minweight: float=0. ) -> OptimizeResult: @@ -91,8 +94,8 @@ def optimized_portfolio_on_sharperatio( def optimized_portfolio_mpt_costfunction( - r: NDArray[Shape["*"], Float], - cov: NDArray[Shape["*, *"], Float], + r: Annotated[NDArray[np.float64], Literal["1D array"]], + cov: Annotated[NDArray[np.float64], Literal["2D array"]], rf: float, lamb: float, V0: float=10. @@ -129,8 +132,8 @@ def optimized_portfolio_mpt_costfunction( def optimized_portfolio_mpt_entropy_costfunction( - r: NDArray[Shape["*"], Float], - cov: NDArray[Shape["*, *"], Float], + r: Annotated[NDArray[np.float64], Literal["1D array"]], + cov: Annotated[NDArray[np.float64], Literal["2D array"]], rf: float, lamb0: float, lamb1: float, @@ -208,7 +211,7 @@ def get_BlackScholesMerton_stocks_estimation( progressbar: bool=True, cacheddir: Optional[PathLike | str]=None, include_dividends: bool=False -) -> tuple[NDArray[Shape["*"], Float], NDArray[Shape["*, *"], Float]]: +) -> tuple[Annotated[NDArray[np.float64], Literal["1D array"]], Annotated[NDArray[np.float64], Literal["2D array"]]]: """Get Black-Scholes-Merton model estimations for a list of stocks. Args: @@ -289,7 +292,7 @@ def get_stocks_timeweighted_estimation( progressbar: bool=True, cacheddir: Optional[PathLike | str]=None, include_dividends: bool=False -) -> tuple[NDArray[Shape["*"], Float], NDArray[Shape["*, *"], Float]]: +) -> tuple[Annotated[NDArray[np.float64], Literal["1D array"]], Annotated[NDArray[np.float64], Literal["2D array"]]]: """Get time-weighted estimations for a list of stocks. Args: diff --git a/src/finsim/portfolio/optimize/policy.py b/src/finsim/portfolio/optimize/policy.py index 1954f22..37e21dc 100644 --- a/src/finsim/portfolio/optimize/policy.py +++ b/src/finsim/portfolio/optimize/policy.py @@ -1,18 +1,18 @@ from itertools import product from abc import ABC, abstractmethod -from typing import Any, Optional +from typing import Any, Optional, Literal, Annotated import numpy as np +from numpy.typing import NDArray import pandas as pd import numba as nb -from nptyping import NDArray, Shape, Float from .numerics import optimized_portfolio_on_sharperatio, optimized_portfolio_mpt_costfunction, optimized_portfolio_mpt_entropy_costfunction @nb.njit -def mat_to_list(mat: NDArray[Shape["*, *"], Float]) -> list[list[float]]: +def mat_to_list(mat: Annotated[NDArray[np.float64], Literal["2D array"]]) -> list[list[float]]: """Convert a 2D numpy array to a list of lists. Args: @@ -39,8 +39,8 @@ class OptimizedWeightingPolicy(ABC): def __init__( self, rf: float, - r: Optional[NDArray[Shape["*"], Float]]=None, - cov: Optional[NDArray[Shape["*, *"], Float]]=None, + r: Optional[Annotated[NDArray[np.float64], Literal["1D array"]]]=None, + cov: Optional[Annotated[NDArray[np.float64], Literal["2D array"]]]=None, symbols: Optional[list[str]]=None ): """Initialize the OptimizedWeightingPolicy. @@ -67,8 +67,8 @@ def __init__( @abstractmethod def optimize( self, - r: NDArray[Shape["*"], Float], - cov: NDArray[Shape["*, *"], Float], + r: Annotated[NDArray[np.float64], Literal["1D array"]], + cov: Annotated[NDArray[np.float64], Literal["2D array"]], symbols: Optional[list[str]]=None ) -> None: """Optimize the portfolio weights. @@ -91,11 +91,11 @@ def portfolio_symbols(self) -> list[str]: @property @abstractmethod - def weights(self) -> NDArray[Shape["*"], Float]: + def weights(self) -> Annotated[NDArray[np.float64], Literal["1D array"]]: """Get the optimized weights for each asset. Returns: - NDArray[Shape["*"], Float]: Array of optimized weights + Annotated[NDArray[np.float64], Literal["1D array"]]: Array of optimized weights """ raise NotImplemented() @@ -120,11 +120,11 @@ def volatility(self) -> float: raise NotImplemented() @property - def correlation_matrix(self) -> NDArray[Shape["*, *"], Float]: + def correlation_matrix(self) -> Annotated[NDArray[np.float64], Literal["2D array"]]: """Get the correlation matrix of the optimized portfolio. Returns: - NDArray[Shape["*, *"], Float]: Correlation matrix of the portfolio + Annotated[NDArray[np.float64], Literal["2D array"]]: Correlation matrix of the portfolio """ corr = np.zeros(self.cov.shape) for i, j in product(range(self.cov.shape[0]), range(self.cov.shape[1])): @@ -187,8 +187,8 @@ class OptimizedWeightingPolicyUsingMPTSharpeRatio(OptimizedWeightingPolicy): def __init__( self, rf: float, - r: Optional[NDArray[Shape["*"], Float]]=None, - cov: Optional[NDArray[Shape["*, *"], Float]]=None, + r: Optional[Annotated[NDArray[np.float64], Literal["1D array"]]]=None, + cov: Optional[Annotated[NDArray[np.float64], Literal["2D array"]]]=None, symbols: Optional[list[str]]=None, minweight: float=0. ): @@ -208,8 +208,8 @@ def __init__( def optimize( self, - r: NDArray[Shape["*"], Float], - cov: NDArray[Shape["*, *"], Float], + r: Annotated[NDArray[np.float64], Literal["1D array"]], + cov: Annotated[NDArray[np.float64], Literal["2D array"]], symbols: Optional[list[str]]=None ) -> None: """Optimize the portfolio weights using the Sharpe ratio. @@ -232,11 +232,11 @@ def optimize( self.optimized_volatility = np.sqrt(np.sum(sqweights * self.cov)) @property - def weights(self) -> NDArray[Shape["*"], Float]: + def weights(self) -> Annotated[NDArray[np.float64], Literal["1D array"]]: """Get the optimized weights for each asset. Returns: - NDArray[Shape["*"], Float]: Array of optimized weights + Annotated[NDArray[np.float64], Literal["1D array"]]: Array of optimized weights """ return self.optimized_weights @@ -298,8 +298,8 @@ class OptimizedWeightingPolicyUsingMPTCostFunction(OptimizedWeightingPolicy): def __init__( self, rf: float, - r: NDArray[Shape["*"], Float], - cov: NDArray[Shape["*, *"], Float], + r: Annotated[NDArray[np.float64], Literal["1D array"]], + cov: Annotated[NDArray[np.float64], Literal["2D array"]], symbols: Optional[list[str]]=None, lamb: float=0.0, V0: float=10.0 @@ -323,8 +323,8 @@ def __init__( def optimize( self, - r: NDArray[Shape["*"], Float], - cov: NDArray[Shape["*, *"], Float], + r: Annotated[NDArray[np.float64], Literal["1D array"]], + cov: Annotated[NDArray[np.float64], Literal["2D array"]], symbols: Optional[list[str]]=None ) -> None: """Optimize the portfolio weights using the MPT cost function. @@ -348,11 +348,11 @@ def optimize( self.optimized_volatility = np.sqrt(np.sum(sqweights * self.cov)) @property - def weights(self) -> NDArray[Shape["*"], Float]: + def weights(self) -> Annotated[NDArray[np.float64], Literal["1D array"]]: """Get the optimized weights for each asset. Returns: - NDArray[Shape["*"], Float]: Array of optimized weights + Annotated[NDArray[np.float64], Literal["1D array"]]: Array of optimized weights """ return self.optimized_weights @@ -416,8 +416,8 @@ class OptimizedWeightingPolicyUsingMPTEntropyCostFunction(OptimizedWeightingPoli def __init__( self, rf: float, - r: NDArray[Shape["*"], Float]=None, - cov: NDArray[Shape["*, *"], Float]=None, + r: Annotated[NDArray[np.float64], Literal["1D array"]]=None, + cov: Annotated[NDArray[np.float64], Literal["2D array"]]=None, symbols: Optional[list[str]]=None, lamb0: float=0.0, lamb1: float=0.0, @@ -444,8 +444,8 @@ def __init__( def optimize( self, - r: NDArray[Shape["*"], Float], - cov: NDArray[Shape["*, *"], Float], + r: Annotated[NDArray[np.float64], Literal["1D array"]], + cov: Annotated[NDArray[np.float64], Literal["2D array"]], symbols: Optional[list[str]]=None ) -> None: """Optimize the portfolio weights using the MPT entropy cost function. @@ -469,11 +469,11 @@ def optimize( self.optimized_volatility = np.sqrt(np.sum(sqweights * self.cov)) @property - def weights(self) -> NDArray[Shape["*"], Float]: + def weights(self) -> Annotated[NDArray[np.float64], Literal["1D array"]]: """Get the optimized weights for each asset. Returns: - NDArray[Shape["*"], Float]: Array of optimized weights + Annotated[NDArray[np.float64], Literal["1D array"]]: Array of optimized weights """ return self.optimized_weights diff --git a/src/finsim/portfolio/portfolio.py b/src/finsim/portfolio/portfolio.py index 54de67a..10c8482 100644 --- a/src/finsim/portfolio/portfolio.py +++ b/src/finsim/portfolio/portfolio.py @@ -3,7 +3,7 @@ import logging import sys from collections import defaultdict -from typing import Any, Optional +from typing import Any, Optional, Literal, Annotated from os import PathLike from io import TextIOWrapper if sys.version_info < (3, 11): @@ -11,9 +11,10 @@ else: from typing import Self +import numpy as np +from numpy.typing import NDArray from tqdm import tqdm import pandas as pd -from nptyping import NDArray, Shape, Float from ..data.preader import get_yahoofinance_data, get_symbol_closing_price from .optimize.policy import OptimizedWeightingPolicy @@ -364,11 +365,11 @@ def portfolio_symbols(self) -> list[str]: return self.policy.portfolio_symbols @property - def weights(self) -> NDArray[Shape["*"], Float]: + def weights(self) -> Annotated[NDArray[np.float64], Literal["1D array"]]: """Get the optimized weights for each asset. Returns: - NDArray[Shape["*"], Float]: Array of optimized weights + Annotated[NDArray[np.float64], Literal["1D array"]]: Array of optimized weights """ return self.policy.weights @@ -391,11 +392,11 @@ def volatility(self) -> float: return self.policy.volatility @property - def correlation_matrix(self) -> NDArray[Shape["*, *"], Float]: + def correlation_matrix(self) -> Annotated[NDArray[np.float64], Literal["2D array"]]: """Get the correlation matrix of the optimized portfolio. Returns: - NDArray[Shape["*, *"], Float]: Correlation matrix of the portfolio + Annotated[NDArray[np.float64], Literal["2D array"]]: Correlation matrix of the portfolio """ return self.policy.correlation_matrix diff --git a/src/finsim/simulation/stock.py b/src/finsim/simulation/stock.py index f590d5e..a5106f2 100644 --- a/src/finsim/simulation/stock.py +++ b/src/finsim/simulation/stock.py @@ -1,9 +1,10 @@ from abc import ABC, abstractmethod from math import log, exp +from typing import Literal, Annotated import numpy as np -from nptyping import NDArray, Shape, Float +from numpy.typing import NDArray class AbstractStochasticValue(ABC): @@ -46,7 +47,7 @@ def generate_time_series( T: float, dt: float, nbsimulations: int=1 - ) -> NDArray[Shape["*"], Float]: + ) -> Annotated[NDArray[np.float64], Literal["1D array"]]: """Generate a time series of stock prices using the Black-Scholes-Merton model. Args: @@ -88,7 +89,12 @@ def __init__(self, x0: float, theta: float, kappa: float, sigma: float): self.kappa = kappa self.sigma = sigma - def generate_time_series(self, T: float, dt: float, nbsimulations: int=1) -> NDArray[Shape["*"], Float]: + def generate_time_series( + self, + T: float, + dt: float, + nbsimulations: int=1 + ) -> Annotated[NDArray[np.float64], Literal["1D array"]]: """Generate a time series of values using the square root diffusion process. Args: @@ -139,7 +145,7 @@ def __init__(self, S0: float, r: float, v0: float, theta: float, kappa: float, s self.logS0 = log(self.S0) self.rho = np.array([[1., self.rho], [self.rho, 1.]]) - def generate_time_series(self, T: float, dt: float, nbsimulations: int=1) -> NDArray[Shape["*"], Float]: + def generate_time_series(self, T: float, dt: float, nbsimulations: int=1) -> Annotated[NDArray[np.float64], Literal["1D array"]]: """Generate a time series of stock prices using the Heston model. Args: @@ -202,7 +208,7 @@ def __init__(self, S0: float, r: float, sigma: float, mu: float, lamb: float, de self.logS0 = log(self.S0) - def generate_time_series(self, T: float, dt: float, nbsimulations: int=1) -> NDArray[Shape["*"], Float]: + def generate_time_series(self, T: float, dt: float, nbsimulations: int=1) -> Annotated[NDArray[np.float64], Literal["1D array"]]: """Generate a time series of stock prices using the Merton jump-diffusion model. Args: