From 5e8aa9832fcf47ec09bd5a28700054a286209735 Mon Sep 17 00:00:00 2001 From: James Maslek Date: Fri, 16 Feb 2024 11:36:17 -0500 Subject: [PATCH 01/16] Split out a rolling submenu for the rolling functions --- .../quantitative_router.py | 154 +-------- .../rolling/rolling_router.py | 316 ++++++++++++++++++ .../openbb_quantitative/statistics.py | 38 +++ 3 files changed, 358 insertions(+), 150 deletions(-) create mode 100644 openbb_platform/extensions/quantitative/openbb_quantitative/rolling/rolling_router.py create mode 100644 openbb_platform/extensions/quantitative/openbb_quantitative/statistics.py diff --git a/openbb_platform/extensions/quantitative/openbb_quantitative/quantitative_router.py b/openbb_platform/extensions/quantitative/openbb_quantitative/quantitative_router.py index ac1392ae0be3..e86043bf330a 100644 --- a/openbb_platform/extensions/quantitative/openbb_quantitative/quantitative_router.py +++ b/openbb_platform/extensions/quantitative/openbb_quantitative/quantitative_router.py @@ -13,7 +13,9 @@ get_target_columns, ) from openbb_core.provider.abstract.data import Data -from pydantic import NonNegativeFloat, PositiveInt +from pydantic import PositiveInt + +from openbb_quantitative.rolling.rolling_router import router as rolling_router from .helpers import get_fama_raw, validate_window from .models import ( @@ -28,6 +30,7 @@ ) router = Router(prefix="") +router.include_router(rolling_router) @router.command(methods=["POST"]) @@ -195,54 +198,6 @@ def get_omega_ratio(df_target: pd.Series, threshold: float) -> float: return OBBject(results=results) -@router.command(methods=["POST"]) -def kurtosis( - data: List[Data], target: str, window: PositiveInt = 21, index: str = "date" -) -> OBBject[List[Data]]: - """Get the rolling Kurtosis. - - Kurtosis provides insights into the shape of the data's distribution, particularly the heaviness of its tails. - Kurtosis is a statistical measure that reveals whether the data points tend to cluster around the mean or if - outliers are more common than a normal distribution would suggest. A higher kurtosis indicates more data points are - found in the tails, suggesting potential volatility or risk. - This analysis is crucial for understanding the underlying characteristics of your data, helping to anticipate - extreme events or anomalies over a specified window of time. - - Parameters - ---------- - data : List[Data] - Time series data. - target : str - Target column name. - window : PositiveInt - Window size. - index : str, optional - Index column name, by default "date" - - Returns - ------- - OBBject[List[Data]] - Kurtosis. - - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() - >>> obb.quantitative.kurtosis(data=stock_data, target="close", window=252) - """ - import pandas_ta as ta # pylint: disable=import-outside-toplevel # type: ignore - - df = basemodel_to_df(data, index=index) - series_target = get_target_column(df, target) - validate_window(series_target, window) - results = ( - ta.kurtosis(close=series_target, length=window).dropna().reset_index(drop=False) - ) - results = df_to_basemodel(results) - - return OBBject(results=results) - - @router.command(methods=["POST"]) def unitroot_test( data: List[Data], @@ -433,107 +388,6 @@ def sortino_ratio( return OBBject(results=results_) -@router.command(methods=["POST"]) -def skewness( - data: List[Data], target: str, window: PositiveInt = 21, index: str = "date" -) -> OBBject[List[Data]]: - """Get Rolling Skewness. - - Skewness is a statistical measure that reveals the degree of asymmetry of a distribution around its mean. - Positive skewness indicates a distribution with an extended tail to the right, while negative skewness shows a tail - that stretches left. Understanding skewness can provide insights into potential biases in data and help anticipate - the nature of future data points. It's particularly useful for identifying the likelihood of extreme outcomes in - financial returns, enabling more informed decision-making based on the distribution's shape over a specified period. - - Parameters - ---------- - data : List[Data] - Time series data. - target : str - Target column name. - window : PositiveInt - Window size. - index : str, optional - Index column name, by default "date" - Returns - ------- - OBBject[List[Data]] - Skewness. - - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() - >>> obb.quantitative.skewness(data=stock_data, target="close", window=252) - """ - import pandas_ta as ta # pylint: disable=import-outside-toplevel # type: ignore - - df = basemodel_to_df(data, index=index) - series_target = get_target_column(df, target) - validate_window(series_target, window) - results = ( - ta.skew(close=series_target, length=window).dropna().reset_index(drop=False) - ) - results = df_to_basemodel(results) - - return OBBject(results=results) - - -@router.command(methods=["POST"]) -def quantile( - data: List[Data], - target: str, - window: PositiveInt = 21, - quantile_pct: NonNegativeFloat = 0.5, - index: str = "date", -) -> OBBject[List[Data]]: - """Get Rolling Quantile. - - Quantile is a statistical measure that identifies the value below which a given percentage of observations in a - group of data falls. By setting the quantile percentage, you can determine any point in the distribution, not just - the median. Whether you're interested in the median, quartiles, or any other position within your data's range, - this tool offers a precise way to understand the distribution's characteristics. - It's especially useful for identifying outliers, understanding dispersion, and setting thresholds for - decision-making based on the distribution of data over a specified period. - - Parameters - ---------- - data : List[Data] - Time series data. - target : str - Target column name. - window : PositiveInt - Window size. - quantile_pct : NonNegativeFloat, optional - Quantile to get - Returns - ------- - OBBject[List[Data]] - Quantile. - - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() - >>> obb.quantitative.quantile(data=stock_data, target="close", window=252, quantile_pct=0.25) - >>> obb.quantitative.quantile(data=stock_data, target="close", window=252, quantile_pct=0.75) - """ - import pandas_ta as ta # pylint: disable=import-outside-toplevel # type: ignore - - df = basemodel_to_df(data, index=index) - series_target = get_target_column(df, target) - validate_window(series_target, window) - df_median = ta.median(close=series_target, length=window).to_frame() - df_quantile = ta.quantile(series_target, length=window, q=quantile_pct).to_frame() - results = ( - pd.concat([df_median, df_quantile], axis=1).dropna().reset_index(drop=False) - ) - - results_ = df_to_basemodel(results) - - return OBBject(results=results_) - - @router.command(methods=["POST"]) def summary(data: List[Data], target: str) -> OBBject[SummaryModel]: """Get Summary Statistics. diff --git a/openbb_platform/extensions/quantitative/openbb_quantitative/rolling/rolling_router.py b/openbb_platform/extensions/quantitative/openbb_quantitative/rolling/rolling_router.py new file mode 100644 index 000000000000..e595f3c31942 --- /dev/null +++ b/openbb_platform/extensions/quantitative/openbb_quantitative/rolling/rolling_router.py @@ -0,0 +1,316 @@ +"""Rolling submenu of quantitative models for rolling statistics.""" + +from typing import List + +import pandas as pd +from openbb_core.app.model.obbject import OBBject +from openbb_core.app.router import Router +from openbb_core.app.utils import ( + basemodel_to_df, + df_to_basemodel, + get_target_column, +) +from openbb_core.provider.abstract.data import Data +from pydantic import NonNegativeFloat, PositiveInt + +from ..helpers import validate_window +from ..statistics import ( + kurtosis as _kurtosis, + mean as _mean, + skew as _skew, + std_dev as _std, + var as _var, +) + +router = Router(prefix="/rolling") + + +@router.command(methods=["POST"]) +def skew( + data: List[Data], target: str, window: PositiveInt = 21, index: str = "date" +) -> OBBject[List[Data]]: + """Get Rolling Skew. + + Skew is a statistical measure that reveals the degree of asymmetry of a distribution around its mean. + Positive skewness indicates a distribution with an extended tail to the right, while negative skewness shows a tail + that stretches left. Understanding skewness can provide insights into potential biases in data and help anticipate + the nature of future data points. It's particularly useful for identifying the likelihood of extreme outcomes in + financial returns, enabling more informed decision-making based on the distribution's shape over a specified period. + + Parameters + ---------- + data : List[Data] + Time series data. + target : str + Target column name. + window : PositiveInt + Window size. + index : str, optional + Index column name, by default "date" + Returns + ------- + OBBject[List[Data]] + Rolling skew. + + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() + # We typically will want to use the returns for analysis + >>> returns = stock_data["close"].pct_change().dropna() + >>> obb.quantitative.rolling.skew(data=returns, target="close", window=252) + """ + + df = basemodel_to_df(data, index=index) + series_target = get_target_column(df, target) + series_target.name = f"rolling_skew_{window}" + validate_window(series_target, window) + results = ( + series_target.rolling(window).apply(_skew).dropna().reset_index(drop=False) + ) + results = df_to_basemodel(results) + + return OBBject(results=results) + + +@router.command(methods=["POST"]) +def variance( + data: List[Data], target: str, window: PositiveInt = 21, index: str = "date" +) -> OBBject[List[Data]]: + """ + Calculate the rolling variance of a target column within a given window size. + + Variance measures the dispersion of a set of data points around their mean. It is a key metric for + assessing the volatility and stability of financial returns or other time series data over a specified rolling window. + + Parameters: + data: List[Data] + The time series data as a list of data points. + target: str + The name of the column for which to calculate variance. + window: PositiveInt + The number of observations used for calculating the rolling measure. + index: str, optional + The name of the index column, default is "date". + + Returns: + OBBject[List[Data]] + An object containing the rolling variance values. + + Examples: + -------- + >>> from openbb import obb + >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() + >>> returns = stock_data["close"].pct_change().dropna() + >>> obb.quantitative.rolling.variance(data=returns, target="close", window=252) + """ + df = basemodel_to_df(data, index=index) + series_target = get_target_column(df, target) + series_target.name = f"rolling_var_{window}" + validate_window(series_target, window) + results = series_target.rolling(window).apply(_var).dropna().reset_index(drop=False) + results = df_to_basemodel(results) + + return OBBject(results=results) + + +@router.command(methods=["POST"]) +def stdev( + data: List[Data], target: str, window: PositiveInt = 21, index: str = "date" +) -> OBBject[List[Data]]: + """ + Calculate the rolling standard deviation of a target column within a given window size. + + Standard deviation is a measure of the amount of variation or dispersion of a set of values. + It is widely used to assess the risk and volatility of financial returns or other time series data + over a specified rolling window. It is the square root of the variance. + + Parameters: + data: List[Data] + The time series data as a list of data points. + target: str + The name of the column for which to calculate standard deviation. + window: PositiveInt + The number of observations used for calculating the rolling measure. + index: str, optional + The name of the index column, default is "date". + + Returns: + OBBject[List[Data]] + An object containing the rolling standard deviation values. + + + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() + # We typically will want to use the returns for analysis + >>> returns = stock_data["close"].pct_change().dropna() + >>> obb.quantitative.rolling.stdev(data=returns, target="close", window=252) + """ + + df = basemodel_to_df(data, index=index) + series_target = get_target_column(df, target) + series_target.name = f"rolling_stdev_{window}" + validate_window(series_target, window) + results = series_target.rolling(window).apply(_std).dropna().reset_index(drop=False) + results = df_to_basemodel(results) + + return OBBject(results=results) + + +@router.command(methods=["POST"]) +def kurtosis( + data: List[Data], target: str, window: PositiveInt = 21, index: str = "date" +) -> OBBject[List[Data]]: + """ + Calculate the rolling kurtosis of a target column within a given window size. + + Kurtosis measures the "tailedness" of the probability distribution of a real-valued random variable. + High kurtosis indicates a distribution with heavy tails (outliers), suggesting a higher risk of extreme outcomes. + Low kurtosis indicates a distribution with lighter tails (less outliers), suggesting less risk of extreme outcomes. + This function helps in assessing the risk of outliers in financial returns or other time series data over a specified + rolling window. + + Parameters: + data: List[Data] + The time series data as a list of data points. + target: str + The name of the column for which to calculate kurtosis. + window: PositiveInt + The number of observations used for calculating the rolling measure. + index: str, optional + The name of the index column, default is "date". + + Returns: + OBBject[List[Data]] + An object containing the rolling kurtosis values. + + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() + # We typically will want to use the returns for analysis + >>> returns = stock_data["close"].pct_change().dropna() + >>> obb.quantitative.rolling.kurtosis(data=returns, target="close", window=252) + """ + + df = basemodel_to_df(data, index=index) + series_target = get_target_column(df, target) + series_target.name = f"rolling_kurtosis_{window}" + validate_window(series_target, window) + results = ( + series_target.rolling(window).apply(_kurtosis).dropna().reset_index(drop=False) + ) + results = df_to_basemodel(results) + + return OBBject(results=results) + + +@router.command(methods=["POST"]) +def quantile( + data: List[Data], + target: str, + window: PositiveInt = 21, + quantile_pct: NonNegativeFloat = 0.5, + index: str = "date", +) -> OBBject[List[Data]]: + """ + Calculate the rolling quantile of a target column within a given window size at a specified quantile percentage. + + Quantiles are points dividing the range of a probability distribution into intervals with equal probabilities, + or dividing the sample in the same way. This function is useful for understanding the distribution of data + within a specified window, allowing for analysis of trends, identification of outliers, and assessment of risk.. + + Parameters: + data: List[Data] + The time series data as a list of data points. + target: str + The name of the column for which to calculate the quantile. + window: PositiveInt + The number of observations used for calculating the rolling measure. + quantile_pct: NonNegativeFloat, optional + The quantile percentage to calculate (e.g., 0.5 for median), default is 0.5. + index: str, optional + The name of the index column, default is "date". + + Returns: + OBBject[List[Data]] + An object containing the rolling quantile values with the median. + + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() + >>> obb.quantitative.quantile(data=stock_data, target="close", window=252, quantile_pct=0.25) + >>> obb.quantitative.quantile(data=stock_data, target="close", window=252, quantile_pct=0.75) + """ + + df = basemodel_to_df(data, index=index) + series_target = get_target_column(df, target) + validate_window(series_target, window) + roll = series_target.rolling(window) + df_median = roll.median() + df_quantile = roll.quantile(quantile_pct) + results = ( + pd.concat( + [df_median, df_quantile], + axis=1, + keys=[ + f"rolling_median_{window}", + f"rolling_quantile_{quantile_pct}_{window}", + ], + ) + .dropna() + .reset_index(drop=False) + ) + + results = df_to_basemodel(results) + + return OBBject(results=results) + + +@router.command(methods=["POST"]) +def mean( + data: List[Data], target: str, window: PositiveInt = 21, index: str = "date" +) -> OBBject[List[Data]]: + """ + Calculate the rolling mean (average) of a target column within a given window size. + + The rolling mean is a simple moving average that calculates the average of a target variable over a specified window. + This function is widely used in financial analysis to smooth short-term fluctuations and highlight longer-term trends + or cycles in time series data. + + Parameters: + data: List[Data] + The time series data as a list of data points. + target: str + The name of the column for which to calculate the mean. + window: PositiveInt + The number of observations used for calculating the rolling measure. + index: str, optional + The name of the index column, default is "date". + + Returns: + OBBject[List[Data]] + An object containing the rolling mean values. + + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() + # We typically will want to use the returns for analysis + >>> returns = stock_data["close"].pct_change().dropna() + >>> obb.quantitative.rolling.mean(data=returns, target="close", window=252) + """ + + df = basemodel_to_df(data, index=index) + series_target = get_target_column(df, target) + series_target.name = f"rolling_mean_{window}" + validate_window(series_target, window) + results = ( + series_target.rolling(window).apply(_mean).dropna().reset_index(drop=False) + ) + results = df_to_basemodel(results) + + return OBBject(results=results) diff --git a/openbb_platform/extensions/quantitative/openbb_quantitative/statistics.py b/openbb_platform/extensions/quantitative/openbb_quantitative/statistics.py new file mode 100644 index 000000000000..b47890cc1fe3 --- /dev/null +++ b/openbb_platform/extensions/quantitative/openbb_quantitative/statistics.py @@ -0,0 +1,38 @@ +"""Statistics Functions""" + +from typing import Union + +from numpy import ( + mean as mean_, + ndarray, + std, + var as var_, +) +from pandas import DataFrame, Series +from scipy import stats + + +def kurtosis(data: Union[DataFrame, Series, ndarray]) -> float: + """Kurtosis is a measure of the "tailedness" of the probability distribution of a real-valued random variable.""" + return stats.kurtosis(data) + + +def skew(data: Union[DataFrame, Series, ndarray]) -> float: + """Skewness is a measure of the asymmetry of the probability distribution of a + real-valued random variable about its mean.""" + return stats.skew(data) + + +def mean(data: Union[DataFrame, Series, ndarray]) -> float: + """Mean is the average of the numbers.""" + return mean_(data) + + +def std_dev(data: Union[DataFrame, Series, ndarray]) -> float: + """Standard deviation is a measure of the amount of variation or dispersion of a set of values.""" + return std(data) + + +def var(data: Union[DataFrame, Series, ndarray]) -> float: + """Variance is a measure of the amount of variation or dispersion of a set of values.""" + return var_(data) From 5c50e22e9205949bde25ffd422ea5bf3aaf759d3 Mon Sep 17 00:00:00 2001 From: James Maslek Date: Fri, 16 Feb 2024 12:21:51 -0500 Subject: [PATCH 02/16] Make a performance and a stats submenu. --- .../performance/performance_router.py | 205 ++++++++++++++ .../quantitative_router.py | 194 +------------ .../openbb_quantitative/stats/stats_router.py | 256 ++++++++++++++++++ 3 files changed, 468 insertions(+), 187 deletions(-) create mode 100644 openbb_platform/extensions/quantitative/openbb_quantitative/performance/performance_router.py create mode 100644 openbb_platform/extensions/quantitative/openbb_quantitative/stats/stats_router.py diff --git a/openbb_platform/extensions/quantitative/openbb_quantitative/performance/performance_router.py b/openbb_platform/extensions/quantitative/openbb_quantitative/performance/performance_router.py new file mode 100644 index 000000000000..317f5dde6853 --- /dev/null +++ b/openbb_platform/extensions/quantitative/openbb_quantitative/performance/performance_router.py @@ -0,0 +1,205 @@ +from typing import List + +import numpy as np +import pandas as pd +from openbb_core.app.model.obbject import OBBject +from openbb_core.app.router import Router +from openbb_core.app.utils import ( + basemodel_to_df, + df_to_basemodel, + get_target_column, +) +from openbb_core.provider.abstract.data import Data +from pydantic import PositiveInt + +from ..helpers import validate_window +from ..models import ( + OmegaModel, +) + +router = Router(prefix="/performance") + + +@router.command(methods=["POST"]) +def omega_ratio( + data: List[Data], + target: str, + threshold_start: float = 0.0, + threshold_end: float = 1.5, +) -> OBBject[List[OmegaModel]]: + """Calculate the Omega Ratio. + + The Omega Ratio is a sophisticated metric that goes beyond traditional performance measures by considering the + probability of achieving returns above a given threshold. It offers a more nuanced view of risk and reward, + focusing on the likelihood of success rather than just average outcomes. + + Parameters + ---------- + data : List[Data] + Time series data. + target : str + Target column name. + threshold_start : float, optional + Start threshold, by default 0.0 + threshold_end : float, optional + End threshold, by default 1.5 + + Returns + ------- + OBBject[List[OmegaModel]] + Omega ratios. + + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() + >>> obb.quantitative.omega_ratio(data=stock_data, target="close") + """ + df = basemodel_to_df(data) + series_target = get_target_column(df, target) + + epsilon = 1e-6 # to avoid division by zero + + def get_omega_ratio(df_target: pd.Series, threshold: float) -> float: + """Get omega ratio.""" + daily_threshold = (threshold + 1) ** np.sqrt(1 / 252) - 1 + excess = df_target - daily_threshold + numerator = excess[excess > 0].sum() + denominator = -excess[excess < 0].sum() + epsilon + + return numerator / denominator + + threshold = np.linspace(threshold_start, threshold_end, 50) + results = [] + for i in threshold: + omega_ = get_omega_ratio(series_target, i) + results.append(OmegaModel(threshold=i, omega=omega_)) + + return OBBject(results=results) + + +@router.command(methods=["POST"]) +def sharpe_ratio( + data: List[Data], + target: str, + rfr: float = 0.0, + window: PositiveInt = 252, + index: str = "date", +) -> OBBject[List[Data]]: + """Get Rolling Sharpe Ratio. + + This function calculates the Sharpe Ratio, a metric used to assess the return of an investment compared to its risk. + By factoring in the risk-free rate, it helps you understand how much extra return you're getting for the extra + volatility that you endure by holding a riskier asset. The Sharpe Ratio is essential for investors looking to + compare the efficiency of different investments, providing a clear picture of potential rewards in relation to their + risks over a specified period. Ideal for gauging the effectiveness of investment strategies, it offers insights into + optimizing your portfolio for maximum return on risk. + + Parameters + ---------- + data : List[Data] + Time series data. + target : str + Target column name. + rfr : float, optional + Risk-free rate, by default 0.0 + window : PositiveInt, optional + Window size, by default 252 + index : str, optional + + Returns + ------- + OBBject[List[Data]] + Sharpe ratio. + + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() + >>> returns = stock_data["close"].pct_change().dropna() + >>> obb.quantitative.sharpe_ratio(data=returns, target="close") + # To specify a risk-free rate and a window + >>> obb.quantitative.sharpe_ratio(data=returns, target="close", rfr=0.03, window=126) + """ + df = basemodel_to_df(data, index=index) + series_target = get_target_column(df, target) + validate_window(series_target, window) + series_target.name = f"sharpe_{window}" + returns = series_target.pct_change().dropna().rolling(window).sum() + std = series_target.rolling(window).std() / np.sqrt(window) + results = ((returns - rfr) / std).dropna().reset_index(drop=False) + + results = df_to_basemodel(results) + + return OBBject(results=results) + + +@router.command(methods=["POST"]) +def sortino_ratio( + data: List[Data], + target: str, + target_return: float = 0.0, + window: PositiveInt = 252, + adjusted: bool = False, + index: str = "date", +) -> OBBject[List[Data]]: + """Get rolling Sortino Ratio. + + The Sortino Ratio enhances the evaluation of investment returns by distinguishing harmful volatility + from total volatility. Unlike other metrics that treat all volatility as risk, this command specifically assesses + the volatility of negative returns relative to a target or desired return. + It's particularly useful for investors who are more concerned with downside risk than with overall volatility. + By calculating the Sortino Ratio, investors can better understand the risk-adjusted return of their investments, + focusing on the likelihood and impact of negative returns. + This approach offers a more nuanced tool for portfolio optimization, especially in strategies aiming + to minimize the downside. + + For method & terminology see: + http://www.redrockcapital.com/Sortino__A__Sharper__Ratio_Red_Rock_Capital.pdf + + Parameters + ---------- + data : List[Data] + Time series data. + target : str + Target column name. + target_return : float, optional + Target return, by default 0.0 + window : PositiveInt, optional + Window size, by default 252 + adjusted : bool, optional + Adjust sortino ratio to compare it to sharpe ratio, by default False + index:str + Index column for input data + Returns + ------- + OBBject[List[Data]] + Sortino ratio. + + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() + >>> returns = stock_data["close"].pct_change().dropna() + >>> obb.quantitative.sortino_ratio(data=stock_data, target="close") + >>> obb.quantitative.sortino_ratio(data=stock_data, target="close", target_return=0.01, window=126, adjusted=True) + """ + df = basemodel_to_df(data, index=index) + series_target = get_target_column(df, target) + validate_window(series_target, window) + returns = series_target.pct_change().dropna().rolling(window).sum() + downside_deviation = returns.rolling(window).apply( + lambda x: (x.values[x.values < 0]).std() / np.sqrt(252) * 100 + ) + results = ( + ((returns - target_return) / downside_deviation) + .dropna() + .reset_index(drop=False) + ) + + if adjusted: + results = results / np.sqrt(2) + + results_ = df_to_basemodel(results) + + return OBBject(results=results_) diff --git a/openbb_platform/extensions/quantitative/openbb_quantitative/quantitative_router.py b/openbb_platform/extensions/quantitative/openbb_quantitative/quantitative_router.py index e86043bf330a..58b753a0df97 100644 --- a/openbb_platform/extensions/quantitative/openbb_quantitative/quantitative_router.py +++ b/openbb_platform/extensions/quantitative/openbb_quantitative/quantitative_router.py @@ -2,28 +2,28 @@ from typing import List, Literal -import numpy as np import pandas as pd from openbb_core.app.model.obbject import OBBject from openbb_core.app.router import Router from openbb_core.app.utils import ( basemodel_to_df, - df_to_basemodel, get_target_column, get_target_columns, ) from openbb_core.provider.abstract.data import Data -from pydantic import PositiveInt +from openbb_quantitative.performance.performance_router import ( + router as performance_router, +) from openbb_quantitative.rolling.rolling_router import router as rolling_router +from openbb_quantitative.stats.stats_router import router as stats_router -from .helpers import get_fama_raw, validate_window +from .helpers import get_fama_raw from .models import ( ADFTestModel, CAPMModel, KPSSTestModel, NormalityModel, - OmegaModel, SummaryModel, TestModel, UnitRootModel, @@ -31,6 +31,8 @@ router = Router(prefix="") router.include_router(rolling_router) +router.include_router(stats_router) +router.include_router(performance_router) @router.command(methods=["POST"]) @@ -140,64 +142,6 @@ def capm(data: List[Data], target: str) -> OBBject[CAPMModel]: return OBBject(results=results) -@router.command(methods=["POST"]) -def omega_ratio( - data: List[Data], - target: str, - threshold_start: float = 0.0, - threshold_end: float = 1.5, -) -> OBBject[List[OmegaModel]]: - """Calculate the Omega Ratio. - - The Omega Ratio is a sophisticated metric that goes beyond traditional performance measures by considering the - probability of achieving returns above a given threshold. It offers a more nuanced view of risk and reward, - focusing on the likelihood of success rather than just average outcomes. - - Parameters - ---------- - data : List[Data] - Time series data. - target : str - Target column name. - threshold_start : float, optional - Start threshold, by default 0.0 - threshold_end : float, optional - End threshold, by default 1.5 - - Returns - ------- - OBBject[List[OmegaModel]] - Omega ratios. - - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() - >>> obb.quantitative.omega_ratio(data=stock_data, target="close") - """ - df = basemodel_to_df(data) - series_target = get_target_column(df, target) - - epsilon = 1e-6 # to avoid division by zero - - def get_omega_ratio(df_target: pd.Series, threshold: float) -> float: - """Get omega ratio.""" - daily_threshold = (threshold + 1) ** np.sqrt(1 / 252) - 1 - excess = df_target - daily_threshold - numerator = excess[excess > 0].sum() - denominator = -excess[excess < 0].sum() + epsilon - - return numerator / denominator - - threshold = np.linspace(threshold_start, threshold_end, 50) - results = [] - for i in threshold: - omega_ = get_omega_ratio(series_target, i) - results.append(OmegaModel(threshold=i, omega=omega_)) - - return OBBject(results=results) - - @router.command(methods=["POST"]) def unitroot_test( data: List[Data], @@ -264,130 +208,6 @@ def unitroot_test( return OBBject(results=unitroot_summary) -@router.command(methods=["POST"]) -def sharpe_ratio( - data: List[Data], - target: str, - rfr: float = 0.0, - window: PositiveInt = 252, - index: str = "date", -) -> OBBject[List[Data]]: - """Get Rolling Sharpe Ratio. - - This function calculates the Sharpe Ratio, a metric used to assess the return of an investment compared to its risk. - By factoring in the risk-free rate, it helps you understand how much extra return you're getting for the extra - volatility that you endure by holding a riskier asset. The Sharpe Ratio is essential for investors looking to - compare the efficiency of different investments, providing a clear picture of potential rewards in relation to their - risks over a specified period. Ideal for gauging the effectiveness of investment strategies, it offers insights into - optimizing your portfolio for maximum return on risk. - - Parameters - ---------- - data : List[Data] - Time series data. - target : str - Target column name. - rfr : float, optional - Risk-free rate, by default 0.0 - window : PositiveInt, optional - Window size, by default 252 - index : str, optional - - Returns - ------- - OBBject[List[Data]] - Sharpe ratio. - - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() - >>> obb.quantitative.sharpe_ratio(data=stock_data, target="close") - >>> obb.quantitative.sharpe_ratio(data=stock_data, target="close", rfr=0.01, window=126) - """ - df = basemodel_to_df(data, index=index) - series_target = get_target_column(df, target) - validate_window(series_target, window) - series_target.name = f"sharpe_{window}" - returns = series_target.pct_change().dropna().rolling(window).sum() - std = series_target.rolling(window).std() / np.sqrt(window) - results = ((returns - rfr) / std).dropna().reset_index(drop=False) - - results = df_to_basemodel(results) - - return OBBject(results=results) - - -@router.command(methods=["POST"]) -def sortino_ratio( - data: List[Data], - target: str, - target_return: float = 0.0, - window: PositiveInt = 252, - adjusted: bool = False, - index: str = "date", -) -> OBBject[List[Data]]: - """Get rolling Sortino Ratio. - - The Sortino Ratio enhances the evaluation of investment returns by distinguishing harmful volatility - from total volatility. Unlike other metrics that treat all volatility as risk, this command specifically assesses - the volatility of negative returns relative to a target or desired return. - It's particularly useful for investors who are more concerned with downside risk than with overall volatility. - By calculating the Sortino Ratio, investors can better understand the risk-adjusted return of their investments, - focusing on the likelihood and impact of negative returns. - This approach offers a more nuanced tool for portfolio optimization, especially in strategies aiming - to minimize the downside. - - For method & terminology see: - http://www.redrockcapital.com/Sortino__A__Sharper__Ratio_Red_Rock_Capital.pdf - - Parameters - ---------- - data : List[Data] - Time series data. - target : str - Target column name. - target_return : float, optional - Target return, by default 0.0 - window : PositiveInt, optional - Window size, by default 252 - adjusted : bool, optional - Adjust sortino ratio to compare it to sharpe ratio, by default False - index:str - Index column for input data - Returns - ------- - OBBject[List[Data]] - Sortino ratio. - - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() - >>> obb.quantitative.sortino_ratio(data=stock_data, target="close") - >>> obb.quantitative.sortino_ratio(data=stock_data, target="close", target_return=0.01, window=126, adjusted=True) - """ - df = basemodel_to_df(data, index=index) - series_target = get_target_column(df, target) - validate_window(series_target, window) - returns = series_target.pct_change().dropna().rolling(window).sum() - downside_deviation = returns.rolling(window).apply( - lambda x: (x.values[x.values < 0]).std() / np.sqrt(252) * 100 - ) - results = ( - ((returns - target_return) / downside_deviation) - .dropna() - .reset_index(drop=False) - ) - - if adjusted: - results = results / np.sqrt(2) - - results_ = df_to_basemodel(results) - - return OBBject(results=results_) - - @router.command(methods=["POST"]) def summary(data: List[Data], target: str) -> OBBject[SummaryModel]: """Get Summary Statistics. diff --git a/openbb_platform/extensions/quantitative/openbb_quantitative/stats/stats_router.py b/openbb_platform/extensions/quantitative/openbb_quantitative/stats/stats_router.py new file mode 100644 index 000000000000..fbf4e78d337a --- /dev/null +++ b/openbb_platform/extensions/quantitative/openbb_quantitative/stats/stats_router.py @@ -0,0 +1,256 @@ +"""Rolling submenu of quantitative models for rolling statistics.""" + +from typing import List + +import pandas as pd +from openbb_core.app.model.obbject import OBBject +from openbb_core.app.router import Router +from openbb_core.app.utils import ( + basemodel_to_df, + df_to_basemodel, + get_target_column, +) +from openbb_core.provider.abstract.data import Data +from pydantic import NonNegativeFloat + +from ..statistics import ( + kurtosis as _kurtosis, + mean as _mean, + skew as _skew, + std_dev as _std, + var as _var, +) + +router = Router(prefix="/stats") + + +@router.command(methods=["POST"]) +def skew( + data: List[Data], + target: str, +) -> OBBject[List[Data]]: + """Get the skew. of tthe data set + + Skew is a statistical measure that reveals the degree of asymmetry of a distribution around its mean. + Positive skewness indicates a distribution with an extended tail to the right, while negative skewness shows a tail + that stretches left. Understanding skewness can provide insights into potential biases in data and help anticipate + the nature of future data points. It's particularly useful for identifying the likelihood of extreme outcomes in + financial returns, enabling more informed decision-making based on the distribution's shape over a specified period. + + Parameters + ---------- + data : List[Data] + Time series data. + target : str + Target column name. + + Returns + ------- + OBBject[List[Data]] + Rolling skew. + + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() + # We typically will want to use the returns for analysis + >>> returns = stock_data["close"].pct_change().dropna() + >>> obb.quantitative.stats.skew(data=returns, target="close") + """ + + df = basemodel_to_df(data) + series_target = get_target_column(df, target) + results = pd.DataFrame([_skew(series_target)], columns=["skew"]) + results = df_to_basemodel(results) + + return OBBject(results=results) + + +@router.command(methods=["POST"]) +def variance(data: List[Data], target: str) -> OBBject[List[Data]]: + """ + Calculate the variance of a target column. + + Variance measures the dispersion of a set of data points around their mean. It is a key metric for + assessing the volatility and stability of financial returns or other time series data. + + Parameters: + data: List[Data] + The time series data as a list of data points. + target: str + The name of the column for which to calculate variance. + + Returns: + OBBject[List[Data]] + An object containing the rolling variance values. + + Examples: + -------- + >>> from openbb import obb + >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() + >>> returns = stock_data["close"].pct_change().dropna() + >>> obb.quantitative.rolling.variance(data=returns, target="close") + """ + df = basemodel_to_df(data) + series_target = get_target_column(df, target) + results = pd.DataFrame([_var(series_target)], columns=["variance"]) + results = df_to_basemodel(results) + + return OBBject(results=results) + + +@router.command(methods=["POST"]) +def stdev(data: List[Data], target: str) -> OBBject[List[Data]]: + """ + Calculate the rolling standard deviation of a target column. + + Standard deviation is a measure of the amount of variation or dispersion of a set of values. + It is widely used to assess the risk and volatility of financial returns or other time series data + It is the square root of the variance. + + Parameters: + data: List[Data] + The time series data as a list of data points. + target: str + The name of the column for which to calculate standard deviation. + + + Returns: + OBBject[List[Data]] + An object containing the rolling standard deviation values. + + + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() + # We typically will want to use the returns for analysis + >>> returns = stock_data["close"].pct_change().dropna() + >>> obb.quantitative.stats.stdev(data=returns, target="close") + """ + + df = basemodel_to_df(data) + series_target = get_target_column(df, target) + results = pd.DataFrame([_std(series_target)], columns=["stdev"]) + results = df_to_basemodel(results) + + return OBBject(results=results) + + +@router.command(methods=["POST"]) +def kurtosis(data: List[Data], target) -> OBBject[List[Data]]: + """ + Calculate the rolling kurtosis of a target column. + + Kurtosis measures the "tailedness" of the probability distribution of a real-valued random variable. + High kurtosis indicates a distribution with heavy tails (outliers), suggesting a higher risk of extreme outcomes. + Low kurtosis indicates a distribution with lighter tails (less outliers), suggesting less risk of extreme outcomes. + This function helps in assessing the risk of outliers in financial returns or other time series data. + + Parameters: + data: List[Data] + The time series data as a list of data points. + target: str + The name of the column for which to calculate kurtosis. + + Returns: + OBBject[List[Data]] + An object containing the kurtosis value + + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() + # We typically will want to use the returns for analysis + >>> returns = stock_data["close"].pct_change().dropna() + >>> obb.quantitative.stats.kurtosis(data=returns, target="close") + """ + + df = basemodel_to_df(data) + series_target = get_target_column(df, target) + results = pd.DataFrame([_kurtosis(series_target)], columns=["kurtosis"]) + results = df_to_basemodel(results) + + return OBBject(results=results) + + +@router.command(methods=["POST"]) +def quantile( + data: List[Data], + target: str, + quantile_pct: NonNegativeFloat = 0.5, +) -> OBBject[List[Data]]: + """ + Calculate the quantile of a target column at a specified quantile percentage. + + Quantiles are points dividing the range of a probability distribution into intervals with equal probabilities, + or dividing the sample in the same way. + + Parameters: + data: List[Data] + The time series data as a list of data points. + target: str + The name of the column for which to calculate the quantile. + quantile_pct: NonNegativeFloat, optional + The quantile percentage to calculate (e.g., 0.5 for median), default is 0.5. + + Returns: + OBBject[List[Data]] + An object containing the rolling quantile values with the median. + + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() + >>> returns = stock_data["close"].pct_change().dropna() + >>> obb.quantitative.stats.quantile(data=returns, target="close") + >>> obb.quantitative.stats.quantile(data=returns, target="close") + """ + + df = basemodel_to_df( + data, + ) + series_target = get_target_column(df, target) + results = pd.DataFrame( + [series_target.quantile(quantile_pct)], columns=[f"{quantile_pct}_quantile"] + ) + results = df_to_basemodel(results) + return OBBject(results=results) + + +@router.command(methods=["POST"]) +def mean( + data: List[Data], + target: str, +) -> OBBject[List[Data]]: + """ + Calculate the mean (average) of a target column. + + The rolling mean is a simple moving average that calculates the average of a target variable. + This function is widely used in financial analysis to smooth short-term fluctuations and highlight longer-term trends + or cycles in time series data. + + Parameters: + data: List[Data] + The time series data as a list of data points. + target: str + The name of the column for which to calculate the mean. + Returns: + OBBject[List[Data]] + An object containing the mean value. + + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() + # We typically will want to use the returns for analysis + >>> returns = stock_data["close"].pct_change().dropna() + >>> obb.quantitative.stats.mean(data=returns, target="close") + """ + + df = basemodel_to_df(data) + series_target = get_target_column(df, target) + results = pd.DataFrame([_mean(series_target)], columns=["mean"]) + results = df_to_basemodel(results) + + return OBBject(results=results) From acaea5e60864ba874b75fd6bc9e7e3f044f49ab5 Mon Sep 17 00:00:00 2001 From: James Maslek Date: Fri, 16 Feb 2024 14:37:29 -0500 Subject: [PATCH 03/16] Test the statistics functions --- .../quantitative/tests/test_statistics.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 openbb_platform/extensions/quantitative/tests/test_statistics.py diff --git a/openbb_platform/extensions/quantitative/tests/test_statistics.py b/openbb_platform/extensions/quantitative/tests/test_statistics.py new file mode 100644 index 000000000000..1e2e18affa91 --- /dev/null +++ b/openbb_platform/extensions/quantitative/tests/test_statistics.py @@ -0,0 +1,25 @@ +from openbb_quantitative.statistics import kurtosis, skew, mean, std_dev, var +import pandas as pd +import pytest + +test_data = pd.Series([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) + + +def test_kurtosis(): + assert kurtosis(test_data) == pytest.approx(-1.224, abs=1e-3) + + +def test_skew(): + assert skew(test_data) == pytest.approx(0.0, abs=1e-3) + + +def test_mean(): + assert mean(test_data) == pytest.approx(5.5, abs=1e-3) + + +def test_std_dev(): + assert std_dev(test_data) == pytest.approx(2.872, abs=1e-3) + + +def test_mean(): + assert mean(test_data) == pytest.approx(5.5, abs=1e-3) From 8377c54dd0e3519b7db001cb0305bd17c3e71009 Mon Sep 17 00:00:00 2001 From: James Maslek Date: Fri, 16 Feb 2024 14:45:52 -0500 Subject: [PATCH 04/16] lint --- .../extensions/quantitative/tests/test_statistics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openbb_platform/extensions/quantitative/tests/test_statistics.py b/openbb_platform/extensions/quantitative/tests/test_statistics.py index 1e2e18affa91..b19a2d0968c0 100644 --- a/openbb_platform/extensions/quantitative/tests/test_statistics.py +++ b/openbb_platform/extensions/quantitative/tests/test_statistics.py @@ -1,6 +1,6 @@ -from openbb_quantitative.statistics import kurtosis, skew, mean, std_dev, var import pandas as pd import pytest +from openbb_quantitative.statistics import kurtosis, mean, skew, std_dev test_data = pd.Series([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) From f70875a39eaafca89c87c6ccf792c3270da921ac Mon Sep 17 00:00:00 2001 From: James Maslek Date: Fri, 16 Feb 2024 15:03:58 -0500 Subject: [PATCH 05/16] lint --- .../quantitative/openbb_quantitative/stats/stats_router.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openbb_platform/extensions/quantitative/openbb_quantitative/stats/stats_router.py b/openbb_platform/extensions/quantitative/openbb_quantitative/stats/stats_router.py index fbf4e78d337a..6bc01b93fd29 100644 --- a/openbb_platform/extensions/quantitative/openbb_quantitative/stats/stats_router.py +++ b/openbb_platform/extensions/quantitative/openbb_quantitative/stats/stats_router.py @@ -13,7 +13,7 @@ from openbb_core.provider.abstract.data import Data from pydantic import NonNegativeFloat -from ..statistics import ( +from openbb_quantitative.statistics import ( kurtosis as _kurtosis, mean as _mean, skew as _skew, @@ -29,7 +29,7 @@ def skew( data: List[Data], target: str, ) -> OBBject[List[Data]]: - """Get the skew. of tthe data set + """Get the skew. of the data set Skew is a statistical measure that reveals the degree of asymmetry of a distribution around its mean. Positive skewness indicates a distribution with an extended tail to the right, while negative skewness shows a tail From 19fcff893cde556192a2ee61b028cc56c073487b Mon Sep 17 00:00:00 2001 From: James Maslek Date: Tue, 20 Feb 2024 10:02:13 -0500 Subject: [PATCH 06/16] dupe test --- .../quantitative/openbb_quantitative/stats/stats_router.py | 3 +-- .../extensions/quantitative/tests/test_statistics.py | 4 ---- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/openbb_platform/extensions/quantitative/openbb_quantitative/stats/stats_router.py b/openbb_platform/extensions/quantitative/openbb_quantitative/stats/stats_router.py index 6bc01b93fd29..248a07112c35 100644 --- a/openbb_platform/extensions/quantitative/openbb_quantitative/stats/stats_router.py +++ b/openbb_platform/extensions/quantitative/openbb_quantitative/stats/stats_router.py @@ -11,8 +11,6 @@ get_target_column, ) from openbb_core.provider.abstract.data import Data -from pydantic import NonNegativeFloat - from openbb_quantitative.statistics import ( kurtosis as _kurtosis, mean as _mean, @@ -20,6 +18,7 @@ std_dev as _std, var as _var, ) +from pydantic import NonNegativeFloat router = Router(prefix="/stats") diff --git a/openbb_platform/extensions/quantitative/tests/test_statistics.py b/openbb_platform/extensions/quantitative/tests/test_statistics.py index b19a2d0968c0..bfdf9c06fe63 100644 --- a/openbb_platform/extensions/quantitative/tests/test_statistics.py +++ b/openbb_platform/extensions/quantitative/tests/test_statistics.py @@ -13,10 +13,6 @@ def test_skew(): assert skew(test_data) == pytest.approx(0.0, abs=1e-3) -def test_mean(): - assert mean(test_data) == pytest.approx(5.5, abs=1e-3) - - def test_std_dev(): assert std_dev(test_data) == pytest.approx(2.872, abs=1e-3) From 9165dd11aec65fd0aff46bebb670a8268dafabf6 Mon Sep 17 00:00:00 2001 From: James Maslek Date: Tue, 20 Feb 2024 10:07:34 -0500 Subject: [PATCH 07/16] pylint --- .../openbb_quantitative/performance/performance_router.py | 4 ++-- .../openbb_quantitative/rolling/rolling_router.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openbb_platform/extensions/quantitative/openbb_quantitative/performance/performance_router.py b/openbb_platform/extensions/quantitative/openbb_quantitative/performance/performance_router.py index 317f5dde6853..31c19f3ee412 100644 --- a/openbb_platform/extensions/quantitative/openbb_quantitative/performance/performance_router.py +++ b/openbb_platform/extensions/quantitative/openbb_quantitative/performance/performance_router.py @@ -12,8 +12,8 @@ from openbb_core.provider.abstract.data import Data from pydantic import PositiveInt -from ..helpers import validate_window -from ..models import ( +from openbb_quantitative.helpers import validate_window +from openbb_quantitative.models import ( OmegaModel, ) diff --git a/openbb_platform/extensions/quantitative/openbb_quantitative/rolling/rolling_router.py b/openbb_platform/extensions/quantitative/openbb_quantitative/rolling/rolling_router.py index e595f3c31942..8900d4a41235 100644 --- a/openbb_platform/extensions/quantitative/openbb_quantitative/rolling/rolling_router.py +++ b/openbb_platform/extensions/quantitative/openbb_quantitative/rolling/rolling_router.py @@ -13,8 +13,8 @@ from openbb_core.provider.abstract.data import Data from pydantic import NonNegativeFloat, PositiveInt -from ..helpers import validate_window -from ..statistics import ( +from openbb_quantitative.helpers import validate_window +from openbb_quantitative.statistics import ( kurtosis as _kurtosis, mean as _mean, skew as _skew, From e73eaead89b5b96902d599645c45faa3efb4292c Mon Sep 17 00:00:00 2001 From: James Maslek Date: Tue, 20 Feb 2024 10:16:00 -0500 Subject: [PATCH 08/16] ruff --- .../openbb_quantitative/performance/performance_router.py | 3 +-- .../quantitative/openbb_quantitative/rolling/rolling_router.py | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/openbb_platform/extensions/quantitative/openbb_quantitative/performance/performance_router.py b/openbb_platform/extensions/quantitative/openbb_quantitative/performance/performance_router.py index 31c19f3ee412..882be73a7eb8 100644 --- a/openbb_platform/extensions/quantitative/openbb_quantitative/performance/performance_router.py +++ b/openbb_platform/extensions/quantitative/openbb_quantitative/performance/performance_router.py @@ -10,12 +10,11 @@ get_target_column, ) from openbb_core.provider.abstract.data import Data -from pydantic import PositiveInt - from openbb_quantitative.helpers import validate_window from openbb_quantitative.models import ( OmegaModel, ) +from pydantic import PositiveInt router = Router(prefix="/performance") diff --git a/openbb_platform/extensions/quantitative/openbb_quantitative/rolling/rolling_router.py b/openbb_platform/extensions/quantitative/openbb_quantitative/rolling/rolling_router.py index 8900d4a41235..c89f47df3fa0 100644 --- a/openbb_platform/extensions/quantitative/openbb_quantitative/rolling/rolling_router.py +++ b/openbb_platform/extensions/quantitative/openbb_quantitative/rolling/rolling_router.py @@ -11,8 +11,6 @@ get_target_column, ) from openbb_core.provider.abstract.data import Data -from pydantic import NonNegativeFloat, PositiveInt - from openbb_quantitative.helpers import validate_window from openbb_quantitative.statistics import ( kurtosis as _kurtosis, @@ -21,6 +19,7 @@ std_dev as _std, var as _var, ) +from pydantic import NonNegativeFloat, PositiveInt router = Router(prefix="/rolling") From bf7ae193dbe7ddeb88987f91b5fdb196b54e8a26 Mon Sep 17 00:00:00 2001 From: James Maslek Date: Tue, 20 Feb 2024 13:18:17 -0500 Subject: [PATCH 09/16] Try tests quick --- .../integration/test_quantitative_python.py | 4 ++-- .../rolling/rolling_router.py | 21 ++++++++++++------- .../extensions/tests/utils/router_testers.py | 2 +- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/openbb_platform/extensions/quantitative/integration/test_quantitative_python.py b/openbb_platform/extensions/quantitative/integration/test_quantitative_python.py index f41f26613b17..bc6ec69e6e6a 100644 --- a/openbb_platform/extensions/quantitative/integration/test_quantitative_python.py +++ b/openbb_platform/extensions/quantitative/integration/test_quantitative_python.py @@ -256,11 +256,11 @@ def test_quantitative_sortino_ratio(params, data_type, obb): ], ) @pytest.mark.integration -def test_quantitative_skewness(params, data_type, obb): +def test_quantitative_skew(params, data_type, obb): params = {p: v for p, v in params.items() if v} params["data"] = get_data(data_type) - result = obb.quantitative.skewness(**params) + result = obb.quantitative.rolling.skew(**params) assert result assert isinstance(result, OBBject) assert len(result.results) > 0 diff --git a/openbb_platform/extensions/quantitative/openbb_quantitative/rolling/rolling_router.py b/openbb_platform/extensions/quantitative/openbb_quantitative/rolling/rolling_router.py index c89f47df3fa0..146189556c4a 100644 --- a/openbb_platform/extensions/quantitative/openbb_quantitative/rolling/rolling_router.py +++ b/openbb_platform/extensions/quantitative/openbb_quantitative/rolling/rolling_router.py @@ -46,6 +46,7 @@ def skew( Window size. index : str, optional Index column name, by default "date" + Returns ------- OBBject[List[Data]] @@ -93,8 +94,9 @@ def variance( The name of the index column, default is "date". Returns: - OBBject[List[Data]] - An object containing the rolling variance values. + ------- + OBBject[List[Data]] + An object containing the rolling variance values. Examples: -------- @@ -135,8 +137,9 @@ def stdev( The name of the index column, default is "date". Returns: - OBBject[List[Data]] - An object containing the rolling standard deviation values. + ------- + OBBject[List[Data]] + An object containing the rolling standard deviation values. Examples @@ -182,8 +185,9 @@ def kurtosis( The name of the index column, default is "date". Returns: - OBBject[List[Data]] - An object containing the rolling kurtosis values. + ------- + OBBject[List[Data]] + An object containing the rolling kurtosis values. Examples -------- @@ -234,8 +238,9 @@ def quantile( The name of the index column, default is "date". Returns: - OBBject[List[Data]] - An object containing the rolling quantile values with the median. + ------- + OBBject[List[Data]] + An object containing the rolling quantile values with the median. Examples -------- diff --git a/openbb_platform/extensions/tests/utils/router_testers.py b/openbb_platform/extensions/tests/utils/router_testers.py index ed8127fe8d36..bbc9ff3b7ec5 100644 --- a/openbb_platform/extensions/tests/utils/router_testers.py +++ b/openbb_platform/extensions/tests/utils/router_testers.py @@ -132,7 +132,7 @@ def check_router_model_functions_signature() -> List[str]: ) if expected_return_type not in str(function.__annotations__["return"]): missing_return_type.append( - f"{function.__name__} in {router_name}" + f"{function.__name__} in {router_name} " f"doesn't have the expected return type: {expected_return_type}" ) From f7814ebb7396f842fbe9e628f102150c1b02df58 Mon Sep 17 00:00:00 2001 From: James Maslek Date: Tue, 20 Feb 2024 13:50:46 -0500 Subject: [PATCH 10/16] black magic signature funcs --- .../rolling/rolling_router.py | 22 ++--- .../openbb_quantitative/statistics.py | 21 +++-- .../openbb_quantitative/stats/stats_router.py | 87 ++++++++++--------- 3 files changed, 72 insertions(+), 58 deletions(-) diff --git a/openbb_platform/extensions/quantitative/openbb_quantitative/rolling/rolling_router.py b/openbb_platform/extensions/quantitative/openbb_quantitative/rolling/rolling_router.py index 146189556c4a..4f9fc25b871a 100644 --- a/openbb_platform/extensions/quantitative/openbb_quantitative/rolling/rolling_router.py +++ b/openbb_platform/extensions/quantitative/openbb_quantitative/rolling/rolling_router.py @@ -13,11 +13,11 @@ from openbb_core.provider.abstract.data import Data from openbb_quantitative.helpers import validate_window from openbb_quantitative.statistics import ( - kurtosis as _kurtosis, - mean as _mean, - skew as _skew, - std_dev as _std, - var as _var, + kurtosis_, + mean_, + skew_, + std_dev_, + var_, ) from pydantic import NonNegativeFloat, PositiveInt @@ -66,7 +66,7 @@ def skew( series_target.name = f"rolling_skew_{window}" validate_window(series_target, window) results = ( - series_target.rolling(window).apply(_skew).dropna().reset_index(drop=False) + series_target.rolling(window).apply(skew_).dropna().reset_index(drop=False) ) results = df_to_basemodel(results) @@ -109,7 +109,7 @@ def variance( series_target = get_target_column(df, target) series_target.name = f"rolling_var_{window}" validate_window(series_target, window) - results = series_target.rolling(window).apply(_var).dropna().reset_index(drop=False) + results = series_target.rolling(window).apply(var_).dropna().reset_index(drop=False) results = df_to_basemodel(results) return OBBject(results=results) @@ -155,7 +155,9 @@ def stdev( series_target = get_target_column(df, target) series_target.name = f"rolling_stdev_{window}" validate_window(series_target, window) - results = series_target.rolling(window).apply(_std).dropna().reset_index(drop=False) + results = ( + series_target.rolling(window).apply(std_dev_).dropna().reset_index(drop=False) + ) results = df_to_basemodel(results) return OBBject(results=results) @@ -203,7 +205,7 @@ def kurtosis( series_target.name = f"rolling_kurtosis_{window}" validate_window(series_target, window) results = ( - series_target.rolling(window).apply(_kurtosis).dropna().reset_index(drop=False) + series_target.rolling(window).apply(kurtosis_).dropna().reset_index(drop=False) ) results = df_to_basemodel(results) @@ -313,7 +315,7 @@ def mean( series_target.name = f"rolling_mean_{window}" validate_window(series_target, window) results = ( - series_target.rolling(window).apply(_mean).dropna().reset_index(drop=False) + series_target.rolling(window).apply(mean_).dropna().reset_index(drop=False) ) results = df_to_basemodel(results) diff --git a/openbb_platform/extensions/quantitative/openbb_quantitative/statistics.py b/openbb_platform/extensions/quantitative/openbb_quantitative/statistics.py index b47890cc1fe3..db137ac77cfc 100644 --- a/openbb_platform/extensions/quantitative/openbb_quantitative/statistics.py +++ b/openbb_platform/extensions/quantitative/openbb_quantitative/statistics.py @@ -3,36 +3,39 @@ from typing import Union from numpy import ( - mean as mean_, + mean as mean_np, ndarray, std, - var as var_, + var as var_np, ) from pandas import DataFrame, Series from scipy import stats +# Because python is weird and these being the same name as the fastapi router functions +# which overwrites the function signature, we add the _ after the function name -def kurtosis(data: Union[DataFrame, Series, ndarray]) -> float: + +def kurtosis_(data: Union[DataFrame, Series, ndarray]) -> float: """Kurtosis is a measure of the "tailedness" of the probability distribution of a real-valued random variable.""" return stats.kurtosis(data) -def skew(data: Union[DataFrame, Series, ndarray]) -> float: +def skew_(data: Union[DataFrame, Series, ndarray]) -> float: """Skewness is a measure of the asymmetry of the probability distribution of a real-valued random variable about its mean.""" return stats.skew(data) -def mean(data: Union[DataFrame, Series, ndarray]) -> float: +def mean_(data: Union[DataFrame, Series, ndarray]) -> float: """Mean is the average of the numbers.""" - return mean_(data) + return mean_np(data) -def std_dev(data: Union[DataFrame, Series, ndarray]) -> float: +def std_dev_(data: Union[DataFrame, Series, ndarray]) -> float: """Standard deviation is a measure of the amount of variation or dispersion of a set of values.""" return std(data) -def var(data: Union[DataFrame, Series, ndarray]) -> float: +def var_(data: Union[DataFrame, Series, ndarray]) -> float: """Variance is a measure of the amount of variation or dispersion of a set of values.""" - return var_(data) + return var_np(data) diff --git a/openbb_platform/extensions/quantitative/openbb_quantitative/stats/stats_router.py b/openbb_platform/extensions/quantitative/openbb_quantitative/stats/stats_router.py index 248a07112c35..56dc8d535b17 100644 --- a/openbb_platform/extensions/quantitative/openbb_quantitative/stats/stats_router.py +++ b/openbb_platform/extensions/quantitative/openbb_quantitative/stats/stats_router.py @@ -12,11 +12,11 @@ ) from openbb_core.provider.abstract.data import Data from openbb_quantitative.statistics import ( - kurtosis as _kurtosis, - mean as _mean, - skew as _skew, - std_dev as _std, - var as _var, + kurtosis_, + mean_, + skew_, + std_dev_, + var_, ) from pydantic import NonNegativeFloat @@ -59,7 +59,7 @@ def skew( df = basemodel_to_df(data) series_target = get_target_column(df, target) - results = pd.DataFrame([_skew(series_target)], columns=["skew"]) + results = pd.DataFrame([skew_(series_target)], columns=["skew"]) results = df_to_basemodel(results) return OBBject(results=results) @@ -73,15 +73,17 @@ def variance(data: List[Data], target: str) -> OBBject[List[Data]]: Variance measures the dispersion of a set of data points around their mean. It is a key metric for assessing the volatility and stability of financial returns or other time series data. - Parameters: - data: List[Data] - The time series data as a list of data points. - target: str - The name of the column for which to calculate variance. + Parameters + ---------- + data: List[Data] + The time series data as a list of data points. + target: str + The name of the column for which to calculate variance. - Returns: - OBBject[List[Data]] - An object containing the rolling variance values. + Returns + ------- + OBBject[List[Data]] + An object containing the rolling variance values. Examples: -------- @@ -92,7 +94,7 @@ def variance(data: List[Data], target: str) -> OBBject[List[Data]]: """ df = basemodel_to_df(data) series_target = get_target_column(df, target) - results = pd.DataFrame([_var(series_target)], columns=["variance"]) + results = pd.DataFrame([var_(series_target)], columns=["variance"]) results = df_to_basemodel(results) return OBBject(results=results) @@ -107,16 +109,18 @@ def stdev(data: List[Data], target: str) -> OBBject[List[Data]]: It is widely used to assess the risk and volatility of financial returns or other time series data It is the square root of the variance. - Parameters: - data: List[Data] - The time series data as a list of data points. - target: str - The name of the column for which to calculate standard deviation. + Parameters + ---------- + data: List[Data] + The time series data as a list of data points. + target: str + The name of the column for which to calculate standard deviation. - Returns: - OBBject[List[Data]] - An object containing the rolling standard deviation values. + Returns + ------- + OBBject[List[Data]] + An object containing the rolling standard deviation values. Examples @@ -130,7 +134,7 @@ def stdev(data: List[Data], target: str) -> OBBject[List[Data]]: df = basemodel_to_df(data) series_target = get_target_column(df, target) - results = pd.DataFrame([_std(series_target)], columns=["stdev"]) + results = pd.DataFrame([std_dev_(series_target)], columns=["stdev"]) results = df_to_basemodel(results) return OBBject(results=results) @@ -146,15 +150,16 @@ def kurtosis(data: List[Data], target) -> OBBject[List[Data]]: Low kurtosis indicates a distribution with lighter tails (less outliers), suggesting less risk of extreme outcomes. This function helps in assessing the risk of outliers in financial returns or other time series data. - Parameters: + Parameters data: List[Data] The time series data as a list of data points. target: str The name of the column for which to calculate kurtosis. - Returns: - OBBject[List[Data]] - An object containing the kurtosis value + Returns + ------ + OBBject[List[Data]] + An object containing the kurtosis value Examples -------- @@ -167,7 +172,7 @@ def kurtosis(data: List[Data], target) -> OBBject[List[Data]]: df = basemodel_to_df(data) series_target = get_target_column(df, target) - results = pd.DataFrame([_kurtosis(series_target)], columns=["kurtosis"]) + results = pd.DataFrame([kurtosis_(series_target)], columns=["kurtosis"]) results = df_to_basemodel(results) return OBBject(results=results) @@ -193,9 +198,10 @@ def quantile( quantile_pct: NonNegativeFloat, optional The quantile percentage to calculate (e.g., 0.5 for median), default is 0.5. - Returns: - OBBject[List[Data]] - An object containing the rolling quantile values with the median. + Returns + ------- + OBBject[List[Data]] + An object containing the rolling quantile values with the median. Examples -------- @@ -229,12 +235,15 @@ def mean( This function is widely used in financial analysis to smooth short-term fluctuations and highlight longer-term trends or cycles in time series data. - Parameters: - data: List[Data] - The time series data as a list of data points. - target: str - The name of the column for which to calculate the mean. - Returns: + Parameters + ---------- + data: List[Data] + The time series data as a list of data points. + target: str + The name of the column for which to calculate the mean. + + Returns + ------- OBBject[List[Data]] An object containing the mean value. @@ -249,7 +258,7 @@ def mean( df = basemodel_to_df(data) series_target = get_target_column(df, target) - results = pd.DataFrame([_mean(series_target)], columns=["mean"]) + results = pd.DataFrame([mean_(series_target)], columns=["mean"]) results = df_to_basemodel(results) return OBBject(results=results) From 9dc89591965119804c3a991e69bcc497933f6f9d Mon Sep 17 00:00:00 2001 From: James Maslek Date: Tue, 20 Feb 2024 13:57:06 -0500 Subject: [PATCH 11/16] fix my custom tests --- .../quantitative/tests/test_statistics.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/openbb_platform/extensions/quantitative/tests/test_statistics.py b/openbb_platform/extensions/quantitative/tests/test_statistics.py index bfdf9c06fe63..11c1ce2ec439 100644 --- a/openbb_platform/extensions/quantitative/tests/test_statistics.py +++ b/openbb_platform/extensions/quantitative/tests/test_statistics.py @@ -1,21 +1,25 @@ import pandas as pd import pytest -from openbb_quantitative.statistics import kurtosis, mean, skew, std_dev +from openbb_quantitative.statistics import kurtosis_, mean_, skew_, std_dev_, var_ test_data = pd.Series([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) def test_kurtosis(): - assert kurtosis(test_data) == pytest.approx(-1.224, abs=1e-3) + assert kurtosis_(test_data) == pytest.approx(-1.224, abs=1e-3) def test_skew(): - assert skew(test_data) == pytest.approx(0.0, abs=1e-3) + assert skew_(test_data) == pytest.approx(0.0, abs=1e-3) def test_std_dev(): - assert std_dev(test_data) == pytest.approx(2.872, abs=1e-3) + assert std_dev_(test_data) == pytest.approx(2.872, abs=1e-3) def test_mean(): - assert mean(test_data) == pytest.approx(5.5, abs=1e-3) + assert mean_(test_data) == pytest.approx(5.5, abs=1e-3) + + +def test_var(): + assert var_(test_data) == pytest.approx(8.25, abs=1e-3) From 17977082cba892de085bc0ba4f08a28f3fd13fdc Mon Sep 17 00:00:00 2001 From: James Maslek Date: Tue, 20 Feb 2024 15:16:24 -0500 Subject: [PATCH 12/16] Fix the existing imports/urls --- .../integration/test_quantitative_python.py | 10 +++++----- .../extensions/tests/test_integration_tests_python.py | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/openbb_platform/extensions/quantitative/integration/test_quantitative_python.py b/openbb_platform/extensions/quantitative/integration/test_quantitative_python.py index bc6ec69e6e6a..f2b73ce10cc1 100644 --- a/openbb_platform/extensions/quantitative/integration/test_quantitative_python.py +++ b/openbb_platform/extensions/quantitative/integration/test_quantitative_python.py @@ -121,7 +121,7 @@ def test_quantitative_omega_ratio(params, data_type, obb): params = {p: v for p, v in params.items() if v} params["data"] = get_data(data_type) - result = obb.quantitative.omega_ratio(**params) + result = obb.quantitative.performance.omega_ratio(**params) assert result assert isinstance(result, OBBject) @@ -138,7 +138,7 @@ def test_quantitative_kurtosis(params, data_type, obb): params = {p: v for p, v in params.items() if v} params["data"] = get_data(data_type) - result = obb.quantitative.kurtosis(**params) + result = obb.quantitative.rolling.kurtosis(**params) assert result assert isinstance(result, OBBject) assert len(result.results) > 0 @@ -207,7 +207,7 @@ def test_quantitative_sharpe_ratio(params, data_type, obb): params = {p: v for p, v in params.items() if v} params["data"] = get_data(data_type) - result = obb.quantitative.sharpe_ratio(**params) + result = obb.quantitative.performance.sharpe_ratio(**params) assert result assert isinstance(result, OBBject) @@ -244,7 +244,7 @@ def test_quantitative_sortino_ratio(params, data_type, obb): params = {p: v for p, v in params.items() if v} params["data"] = get_data(data_type) - result = obb.quantitative.sortino_ratio(**params) + result = obb.quantitative.performance.sortino_ratio(**params) assert result assert isinstance(result, OBBject) @@ -296,7 +296,7 @@ def test_quantitative_quantile(params, data_type, obb): params = {p: v for p, v in params.items() if v} params["data"] = get_data(data_type) - result = obb.quantitative.quantile(**params) + result = obb.quantitative.rolling.quantile(**params) assert result assert isinstance(result, OBBject) assert len(result.results) > 0 diff --git a/openbb_platform/extensions/tests/test_integration_tests_python.py b/openbb_platform/extensions/tests/test_integration_tests_python.py index 110034fa82f4..8940db9821e6 100644 --- a/openbb_platform/extensions/tests/test_integration_tests_python.py +++ b/openbb_platform/extensions/tests/test_integration_tests_python.py @@ -59,13 +59,13 @@ def test_charting_extension_function_coverage() -> None: assert missing_items == [], "\n".join(missing_items) -def test_missing_api_integration_tests() -> None: +def test_missing_python_integration_tests() -> None: """Check if there are missing tests.""" missing = check_missing_integration_tests(test_type="python") assert not missing, "\n".join(missing) -def test_outdated_api_integration_tests() -> None: +def test_outdated_python_integration_tests() -> None: """Check if there are outdated tests.""" outdated = check_outdated_integration_tests(test_type="python") assert not outdated, "\n".join(outdated) From e7bb9eb54b2e9bf46896cc4dbd65ef08351b9e80 Mon Sep 17 00:00:00 2001 From: James Maslek Date: Tue, 20 Feb 2024 15:16:51 -0500 Subject: [PATCH 13/16] push the api update --- .../integration/test_quantitative_api.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/openbb_platform/extensions/quantitative/integration/test_quantitative_api.py b/openbb_platform/extensions/quantitative/integration/test_quantitative_api.py index 6bcaa5da5e06..a0e9414d6eaf 100644 --- a/openbb_platform/extensions/quantitative/integration/test_quantitative_api.py +++ b/openbb_platform/extensions/quantitative/integration/test_quantitative_api.py @@ -132,7 +132,7 @@ def test_quantitative_omega_ratio(params, data_type): data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) - url = f"http://0.0.0.0:8000/api/v1/quantitative/omega_ratio?{query_str}" + url = f"http://0.0.0.0:8000/api/v1/quantitative/performance/omega_ratio?{query_str}" result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -151,7 +151,7 @@ def test_quantitative_kurtosis(params, data_type): data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) - url = f"http://0.0.0.0:8000/api/v1/quantitative/kurtosis?{query_str}" + url = f"http://0.0.0.0:8000/api/v1/quantitative/rolling/kurtosis?{query_str}" result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -217,7 +217,9 @@ def test_quantitative_sharpe_ratio(params, data_type): data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) - url = f"http://0.0.0.0:8000/api/v1/quantitative/sharpe_ratio?{query_str}" + url = ( + f"http://0.0.0.0:8000/api/v1/quantitative/performance/sharpe_ratio?{query_str}" + ) result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -256,7 +258,9 @@ def test_quantitative_sortino_ratio(params, data_type): data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) - url = f"http://0.0.0.0:8000/api/v1/quantitative/sortino_ratio?{query_str}" + url = ( + f"http://0.0.0.0:8000/api/v1/quantitative/performance/sortino_ratio?{query_str}" + ) result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -274,7 +278,7 @@ def test_quantitative_skewness(params, data_type): data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) - url = f"http://0.0.0.0:8000/api/v1/quantitative/skewness?{query_str}" + url = f"http://0.0.0.0:8000/api/v1/quantitative/rolling/skew?{query_str}" result = requests.post(url, headers=get_headers(), timeout=60, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -311,7 +315,7 @@ def test_quantitative_quantile(params, data_type): data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) - url = f"http://0.0.0.0:8000/api/v1/quantitative/quantile?{query_str}" + url = f"http://0.0.0.0:8000/api/v1/quantitative/rolling/quantile?{query_str}" result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 From a2fa336fc955a3b1e3efe9b3fa6d936c6133a2b1 Mon Sep 17 00:00:00 2001 From: James Maslek Date: Tue, 20 Feb 2024 15:25:44 -0500 Subject: [PATCH 14/16] okay I figured out whats going on --- .../integration/test_quantitative_api.py | 12 +- .../integration/test_quantitative_python.py | 120 +++++++++++++++++- 2 files changed, 120 insertions(+), 12 deletions(-) diff --git a/openbb_platform/extensions/quantitative/integration/test_quantitative_api.py b/openbb_platform/extensions/quantitative/integration/test_quantitative_api.py index a0e9414d6eaf..6fea39ecc340 100644 --- a/openbb_platform/extensions/quantitative/integration/test_quantitative_api.py +++ b/openbb_platform/extensions/quantitative/integration/test_quantitative_api.py @@ -127,7 +127,7 @@ def test_quantitative_capm(params, data_type): ], ) @pytest.mark.integration -def test_quantitative_omega_ratio(params, data_type): +def test_quantitative_performance_omega_ratio(params, data_type): params = {p: v for p, v in params.items() if v} data = json.dumps(get_data(data_type)) @@ -146,7 +146,7 @@ def test_quantitative_omega_ratio(params, data_type): ], ) @pytest.mark.integration -def test_quantitative_kurtosis(params, data_type): +def test_quantitative_rolling_kurtosis(params, data_type): params = {p: v for p, v in params.items() if v} data = json.dumps(get_data(data_type)) @@ -212,7 +212,7 @@ def test_quantitative_unitroot_test(params, data_type): ], ) @pytest.mark.integration -def test_quantitative_sharpe_ratio(params, data_type): +def test_quantitative_performance_sharpe_ratio(params, data_type): params = {p: v for p, v in params.items() if v} data = json.dumps(get_data(data_type)) @@ -253,7 +253,7 @@ def test_quantitative_sharpe_ratio(params, data_type): ], ) @pytest.mark.integration -def test_quantitative_sortino_ratio(params, data_type): +def test_quantitative_performance_sortino_ratio(params, data_type): params = {p: v for p, v in params.items() if v} data = json.dumps(get_data(data_type)) @@ -273,7 +273,7 @@ def test_quantitative_sortino_ratio(params, data_type): ], ) @pytest.mark.integration -def test_quantitative_skewness(params, data_type): +def test_quantitative_rolling_skew(params, data_type): params = {p: v for p, v in params.items() if v} data = json.dumps(get_data(data_type)) @@ -310,7 +310,7 @@ def test_quantitative_skewness(params, data_type): ], ) @pytest.mark.integration -def test_quantitative_quantile(params, data_type): +def test_quantitative_rolling_quantile(params, data_type): params = {p: v for p, v in params.items() if v} data = json.dumps(get_data(data_type)) diff --git a/openbb_platform/extensions/quantitative/integration/test_quantitative_python.py b/openbb_platform/extensions/quantitative/integration/test_quantitative_python.py index f2b73ce10cc1..32e60f9f8f9d 100644 --- a/openbb_platform/extensions/quantitative/integration/test_quantitative_python.py +++ b/openbb_platform/extensions/quantitative/integration/test_quantitative_python.py @@ -117,7 +117,7 @@ def test_quantitative_capm(params, data_type, obb): ], ) @pytest.mark.integration -def test_quantitative_omega_ratio(params, data_type, obb): +def test_quantitative_performance_omega_ratio(params, data_type, obb): params = {p: v for p, v in params.items() if v} params["data"] = get_data(data_type) @@ -134,7 +134,7 @@ def test_quantitative_omega_ratio(params, data_type, obb): ], ) @pytest.mark.integration -def test_quantitative_kurtosis(params, data_type, obb): +def test_quantitative_rolling_kurtosis(params, data_type, obb): params = {p: v for p, v in params.items() if v} params["data"] = get_data(data_type) @@ -203,7 +203,7 @@ def test_quantitative_unitroot_test(params, data_type, obb): ], ) @pytest.mark.integration -def test_quantitative_sharpe_ratio(params, data_type, obb): +def test_quantitative_performance_sharpe_ratio(params, data_type, obb): params = {p: v for p, v in params.items() if v} params["data"] = get_data(data_type) @@ -240,7 +240,7 @@ def test_quantitative_sharpe_ratio(params, data_type, obb): ], ) @pytest.mark.integration -def test_quantitative_sortino_ratio(params, data_type, obb): +def test_quantitative_performance_sortino_ratio(params, data_type, obb): params = {p: v for p, v in params.items() if v} params["data"] = get_data(data_type) @@ -256,7 +256,7 @@ def test_quantitative_sortino_ratio(params, data_type, obb): ], ) @pytest.mark.integration -def test_quantitative_skew(params, data_type, obb): +def test_quantitative_rolling_skew(params, data_type, obb): params = {p: v for p, v in params.items() if v} params["data"] = get_data(data_type) @@ -292,7 +292,7 @@ def test_quantitative_skew(params, data_type, obb): ], ) @pytest.mark.integration -def test_quantitative_quantile(params, data_type, obb): +def test_quantitative_rolling_quantile(params, data_type, obb): params = {p: v for p, v in params.items() if v} params["data"] = get_data(data_type) @@ -317,3 +317,111 @@ def test_quantitative_summary(params, data_type, obb): result = obb.quantitative.summary(**params) assert result assert isinstance(result, OBBject) + + +@parametrize( + "params, data_type", + [ + ( + { + "data": "", + "target": "close", + "window": "10", + "quantile_pct": "", + "index": "date", + }, + "equity", + ), + ( + { + "data": "", + "target": "high", + "window": "50", + "quantile_pct": "0.6", + "index": "date", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_quantitative_rolling_stdev(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.quantitative.rolling.stdev(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@parametrize( + "params, data_type", + [ + ( + { + "data": "", + "target": "close", + "window": "10", + "quantile_pct": "", + "index": "date", + }, + "equity", + ), + ( + { + "data": "", + "target": "high", + "window": "50", + "quantile_pct": "0.6", + "index": "date", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_quantitative_rolling_mean(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.quantitative.rolling.mean(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@parametrize( + "params, data_type", + [ + ( + { + "data": "", + "target": "close", + "window": "10", + "quantile_pct": "", + "index": "date", + }, + "equity", + ), + ( + { + "data": "", + "target": "high", + "window": "50", + "quantile_pct": "0.6", + "index": "date", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_quantitative_rolling_variance(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.quantitative.rolling.variance(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 From 46ab5f42a772b047ac97c2afc648a14e7a823755 Mon Sep 17 00:00:00 2001 From: James Maslek Date: Tue, 20 Feb 2024 16:31:27 -0500 Subject: [PATCH 15/16] this should be all of them --- .../integration/test_quantitative_api.py | 184 ++++++++++++++++++ .../integration/test_quantitative_python.py | 117 +++++++++++ 2 files changed, 301 insertions(+) diff --git a/openbb_platform/extensions/quantitative/integration/test_quantitative_api.py b/openbb_platform/extensions/quantitative/integration/test_quantitative_api.py index 6fea39ecc340..10e9432d1f8e 100644 --- a/openbb_platform/extensions/quantitative/integration/test_quantitative_api.py +++ b/openbb_platform/extensions/quantitative/integration/test_quantitative_api.py @@ -284,6 +284,60 @@ def test_quantitative_rolling_skew(params, data_type): assert result.status_code == 200 +@parametrize( + "params, data_type", + [ + ({"data": "", "target": "close", "window": "220", "index": "date"}, "equity"), + ], +) +@pytest.mark.integration +def test_quantitative_rolling_variance(params, data_type): + params = {p: v for p, v in params.items() if v} + data = json.dumps(get_data(data_type)) + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/quantitative/rolling/variance?{query_str}" + result = requests.post(url, headers=get_headers(), timeout=60, data=data) + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@parametrize( + "params, data_type", + [ + ({"data": "", "target": "close", "window": "220", "index": "date"}, "equity"), + ], +) +@pytest.mark.integration +def test_quantitative_rolling_stdev(params, data_type): + params = {p: v for p, v in params.items() if v} + data = json.dumps(get_data(data_type)) + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/quantitative/rolling/stdev?{query_str}" + result = requests.post(url, headers=get_headers(), timeout=60, data=data) + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@parametrize( + "params, data_type", + [ + ({"data": "", "target": "close", "window": "220", "index": "date"}, "equity"), + ], +) +@pytest.mark.integration +def test_quantitative_rolling_mean(params, data_type): + params = {p: v for p, v in params.items() if v} + data = json.dumps(get_data(data_type)) + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/quantitative/rolling/mean?{query_str}" + result = requests.post(url, headers=get_headers(), timeout=60, data=data) + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + @parametrize( "params, data_type", [ @@ -338,3 +392,133 @@ def test_quantitative_summary(params, data_type): result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 + + +############ +# quantitative/stats +############ + + +@parametrize( + "params, data_type", + [ + ({"data": "", "target": "close", "index": "date"}, "equity"), + ], +) +@pytest.mark.integration +def test_quantitative_stats_skew(params, data_type): + params = {p: v for p, v in params.items() if v} + data = json.dumps(get_data(data_type)) + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/quantitative/stats/skew?{query_str}" + result = requests.post(url, headers=get_headers(), timeout=60, data=data) + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@parametrize( + "params, data_type", + [ + ({"data": "", "target": "close", "index": "date"}, "equity"), + ], +) +@pytest.mark.integration +def test_quantitative_stats_kurtosis(params, data_type): + params = {p: v for p, v in params.items() if v} + data = json.dumps(get_data(data_type)) + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/quantitative/stats/kurtosis?{query_str}" + result = requests.post(url, headers=get_headers(), timeout=60, data=data) + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@parametrize( + "params, data_type", + [ + ({"data": "", "target": "close", "index": "date"}, "equity"), + ], +) +@pytest.mark.integration +def test_quantitative_stats_mean(params, data_type): + params = {p: v for p, v in params.items() if v} + data = json.dumps(get_data(data_type)) + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/quantitative/stats/mean?{query_str}" + result = requests.post(url, headers=get_headers(), timeout=60, data=data) + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@parametrize( + "params, data_type", + [ + ({"data": "", "target": "close", "index": "date"}, "equity"), + ], +) +@pytest.mark.integration +def test_quantitative_stats_stdev(params, data_type): + params = {p: v for p, v in params.items() if v} + data = json.dumps(get_data(data_type)) + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/quantitative/stats/stdev?{query_str}" + result = requests.post(url, headers=get_headers(), timeout=60, data=data) + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@parametrize( + "params, data_type", + [ + ({"data": "", "target": "close", "index": "date"}, "equity"), + ], +) +@pytest.mark.integration +def test_quantitative_stats_variance(params, data_type): + params = {p: v for p, v in params.items() if v} + data = json.dumps(get_data(data_type)) + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/quantitative/stats/variance?{query_str}" + result = requests.post(url, headers=get_headers(), timeout=60, data=data) + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@parametrize( + "params, data_type", + [ + ( + { + "data": "", + "target": "close", + "quantile_pct": "", + "index": "date", + }, + "equity", + ), + ( + { + "data": "", + "target": "high", + "quantile_pct": "0.6", + "index": "date", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_quantitative_stats_quantile(params, data_type): + params = {p: v for p, v in params.items() if v} + data = json.dumps(get_data(data_type)) + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/quantitative/stats/quantile?{query_str}" + result = requests.post(url, headers=get_headers(), timeout=10, data=data) + assert isinstance(result, requests.Response) + assert result.status_code == 200 diff --git a/openbb_platform/extensions/quantitative/integration/test_quantitative_python.py b/openbb_platform/extensions/quantitative/integration/test_quantitative_python.py index 32e60f9f8f9d..5c55621a8ae0 100644 --- a/openbb_platform/extensions/quantitative/integration/test_quantitative_python.py +++ b/openbb_platform/extensions/quantitative/integration/test_quantitative_python.py @@ -425,3 +425,120 @@ def test_quantitative_rolling_variance(params, data_type, obb): assert result assert isinstance(result, OBBject) assert len(result.results) > 0 + + +@parametrize( + "params, data_type", + [ + ({"data": "", "target": "close"}, "equity"), + ], +) +@pytest.mark.integration +def test_quantitative_stats_skew(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.quantitative.stats.skew(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@parametrize( + "params, data_type", + [ + ({"data": "", "target": "close"}, "equity"), + ], +) +@pytest.mark.integration +def test_quantitative_stats_kurtosis(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.quantitative.stats.kurtosis(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@parametrize( + "params, data_type", + [ + ({"data": "", "target": "close"}, "equity"), + ], +) +@pytest.mark.integration +def test_quantitative_stats_variance(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.quantitative.stats.variance(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@parametrize( + "params, data_type", + [ + ({"data": "", "target": "close"}, "equity"), + ], +) +@pytest.mark.integration +def test_quantitative_stats_stdev(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.quantitative.stats.stdev(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@parametrize( + "params, data_type", + [ + ({"data": "", "target": "close"}, "equity"), + ], +) +@pytest.mark.integration +def test_quantitative_stats_mean(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.quantitative.stats.mean(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@parametrize( + "params, data_type", + [ + ( + { + "data": "", + "target": "close", + "quantile_pct": "", + }, + "equity", + ), + ( + { + "data": "", + "target": "close", + "quantile_pct": "0.6", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_quantitative_stats_quantile(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.quantitative.stats.quantile(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 From fa4cb5192e9d92efa90e8982d81e7bcab7148968 Mon Sep 17 00:00:00 2001 From: James Maslek Date: Wed, 21 Feb 2024 11:01:03 -0500 Subject: [PATCH 16/16] Correct docstringing examples --- .../performance/performance_router.py | 51 ++++---- .../rolling/rolling_router.py | 101 ++++++++------- .../openbb_quantitative/stats/stats_router.py | 118 +++++++++--------- 3 files changed, 130 insertions(+), 140 deletions(-) diff --git a/openbb_platform/extensions/quantitative/openbb_quantitative/performance/performance_router.py b/openbb_platform/extensions/quantitative/openbb_quantitative/performance/performance_router.py index 882be73a7eb8..92bd611a3802 100644 --- a/openbb_platform/extensions/quantitative/openbb_quantitative/performance/performance_router.py +++ b/openbb_platform/extensions/quantitative/openbb_quantitative/performance/performance_router.py @@ -19,7 +19,14 @@ router = Router(prefix="/performance") -@router.command(methods=["POST"]) +@router.command( + methods=["POST"], + examples=[ + 'stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df()', + 'returns = stock_data["close"].pct_change().dropna()', + 'obb.quantitative.omega_ratio(data=returns, target="close")', + ], +) def omega_ratio( data: List[Data], target: str, @@ -47,12 +54,6 @@ def omega_ratio( ------- OBBject[List[OmegaModel]] Omega ratios. - - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() - >>> obb.quantitative.omega_ratio(data=stock_data, target="close") """ df = basemodel_to_df(data) series_target = get_target_column(df, target) @@ -77,7 +78,14 @@ def get_omega_ratio(df_target: pd.Series, threshold: float) -> float: return OBBject(results=results) -@router.command(methods=["POST"]) +@router.command( + methods=["POST"], + examples=[ + 'stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df()', + 'returns = stock_data["close"].pct_change().dropna()', + 'obb.quantitative.sharpe_ratio(data=returns, target="close")', + ], +) def sharpe_ratio( data: List[Data], target: str, @@ -110,15 +118,6 @@ def sharpe_ratio( ------- OBBject[List[Data]] Sharpe ratio. - - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() - >>> returns = stock_data["close"].pct_change().dropna() - >>> obb.quantitative.sharpe_ratio(data=returns, target="close") - # To specify a risk-free rate and a window - >>> obb.quantitative.sharpe_ratio(data=returns, target="close", rfr=0.03, window=126) """ df = basemodel_to_df(data, index=index) series_target = get_target_column(df, target) @@ -133,7 +132,15 @@ def sharpe_ratio( return OBBject(results=results) -@router.command(methods=["POST"]) +@router.command( + methods=["POST"], + examples=[ + 'stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df()', + 'returns = stock_data["close"].pct_change().dropna()', + 'obb.quantitative.sortino_ratio(data=stock_data, target="close")', + 'obb.quantitative.sortino_ratio(data=stock_data, target="close", target_return=0.01, window=126, adjusted=True)', + ], +) def sortino_ratio( data: List[Data], target: str, @@ -174,14 +181,6 @@ def sortino_ratio( ------- OBBject[List[Data]] Sortino ratio. - - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() - >>> returns = stock_data["close"].pct_change().dropna() - >>> obb.quantitative.sortino_ratio(data=stock_data, target="close") - >>> obb.quantitative.sortino_ratio(data=stock_data, target="close", target_return=0.01, window=126, adjusted=True) """ df = basemodel_to_df(data, index=index) series_target = get_target_column(df, target) diff --git a/openbb_platform/extensions/quantitative/openbb_quantitative/rolling/rolling_router.py b/openbb_platform/extensions/quantitative/openbb_quantitative/rolling/rolling_router.py index 4f9fc25b871a..6e464083aea4 100644 --- a/openbb_platform/extensions/quantitative/openbb_quantitative/rolling/rolling_router.py +++ b/openbb_platform/extensions/quantitative/openbb_quantitative/rolling/rolling_router.py @@ -24,7 +24,14 @@ router = Router(prefix="/rolling") -@router.command(methods=["POST"]) +@router.command( + methods=["POST"], + examples=[ + 'stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df()', + 'returns = stock_data["close"].pct_change().dropna()', + 'obb.quantitative.rolling.skew(data=returns, target="close")', + ], +) def skew( data: List[Data], target: str, window: PositiveInt = 21, index: str = "date" ) -> OBBject[List[Data]]: @@ -52,13 +59,6 @@ def skew( OBBject[List[Data]] Rolling skew. - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() - # We typically will want to use the returns for analysis - >>> returns = stock_data["close"].pct_change().dropna() - >>> obb.quantitative.rolling.skew(data=returns, target="close", window=252) """ df = basemodel_to_df(data, index=index) @@ -73,7 +73,14 @@ def skew( return OBBject(results=results) -@router.command(methods=["POST"]) +@router.command( + methods=["POST"], + examples=[ + 'stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df()', + 'returns = stock_data["close"].pct_change().dropna()', + 'obb.quantitative.rolling.variance(data=returns, target="close", window=252)', + ], +) def variance( data: List[Data], target: str, window: PositiveInt = 21, index: str = "date" ) -> OBBject[List[Data]]: @@ -97,13 +104,6 @@ def variance( ------- OBBject[List[Data]] An object containing the rolling variance values. - - Examples: - -------- - >>> from openbb import obb - >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() - >>> returns = stock_data["close"].pct_change().dropna() - >>> obb.quantitative.rolling.variance(data=returns, target="close", window=252) """ df = basemodel_to_df(data, index=index) series_target = get_target_column(df, target) @@ -115,7 +115,14 @@ def variance( return OBBject(results=results) -@router.command(methods=["POST"]) +@router.command( + methods=["POST"], + examples=[ + 'stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df()', + 'returns = stock_data["close"].pct_change().dropna()', + 'obb.quantitative.rolling.stdev(data=returns, target="close", window=252)', + ], +) def stdev( data: List[Data], target: str, window: PositiveInt = 21, index: str = "date" ) -> OBBject[List[Data]]: @@ -140,15 +147,6 @@ def stdev( ------- OBBject[List[Data]] An object containing the rolling standard deviation values. - - - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() - # We typically will want to use the returns for analysis - >>> returns = stock_data["close"].pct_change().dropna() - >>> obb.quantitative.rolling.stdev(data=returns, target="close", window=252) """ df = basemodel_to_df(data, index=index) @@ -163,7 +161,14 @@ def stdev( return OBBject(results=results) -@router.command(methods=["POST"]) +@router.command( + methods=["POST"], + examples=[ + 'stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df()', + 'returns = stock_data["close"].pct_change().dropna()', + 'obb.quantitative.rolling.kurtosis(data=returns, target="close", window=252)', + ], +) def kurtosis( data: List[Data], target: str, window: PositiveInt = 21, index: str = "date" ) -> OBBject[List[Data]]: @@ -190,14 +195,6 @@ def kurtosis( ------- OBBject[List[Data]] An object containing the rolling kurtosis values. - - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() - # We typically will want to use the returns for analysis - >>> returns = stock_data["close"].pct_change().dropna() - >>> obb.quantitative.rolling.kurtosis(data=returns, target="close", window=252) """ df = basemodel_to_df(data, index=index) @@ -212,7 +209,15 @@ def kurtosis( return OBBject(results=results) -@router.command(methods=["POST"]) +@router.command( + methods=["POST"], + examples=[ + 'stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df()', + 'returns = stock_data["close"].pct_change().dropna()', + 'obb.quantitative.rolling.quantile(data=returns, target="close", window=252, quantile_pct=0.25)', + 'obb.quantitative.rolling.quantile(data=returns, target="close", window=252, quantile_pct=0.75)', + ], +) def quantile( data: List[Data], target: str, @@ -243,13 +248,6 @@ def quantile( ------- OBBject[List[Data]] An object containing the rolling quantile values with the median. - - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() - >>> obb.quantitative.quantile(data=stock_data, target="close", window=252, quantile_pct=0.25) - >>> obb.quantitative.quantile(data=stock_data, target="close", window=252, quantile_pct=0.75) """ df = basemodel_to_df(data, index=index) @@ -276,7 +274,14 @@ def quantile( return OBBject(results=results) -@router.command(methods=["POST"]) +@router.command( + methods=["POST"], + examples=[ + 'stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df()', + 'returns = stock_data["close"].pct_change().dropna()', + 'obb.quantitative.rolling.mean(data=returns, target="close", window=252)', + ], +) def mean( data: List[Data], target: str, window: PositiveInt = 21, index: str = "date" ) -> OBBject[List[Data]]: @@ -300,14 +305,6 @@ def mean( Returns: OBBject[List[Data]] An object containing the rolling mean values. - - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() - # We typically will want to use the returns for analysis - >>> returns = stock_data["close"].pct_change().dropna() - >>> obb.quantitative.rolling.mean(data=returns, target="close", window=252) """ df = basemodel_to_df(data, index=index) diff --git a/openbb_platform/extensions/quantitative/openbb_quantitative/stats/stats_router.py b/openbb_platform/extensions/quantitative/openbb_quantitative/stats/stats_router.py index 56dc8d535b17..deff80471b1d 100644 --- a/openbb_platform/extensions/quantitative/openbb_quantitative/stats/stats_router.py +++ b/openbb_platform/extensions/quantitative/openbb_quantitative/stats/stats_router.py @@ -23,7 +23,14 @@ router = Router(prefix="/stats") -@router.command(methods=["POST"]) +@router.command( + methods=["POST"], + examples=[ + 'stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df()', + 'returns = stock_data["close"].pct_change().dropna()', + 'obb.quantitative.stats.skew(data=returns, target="close")', + ], +) def skew( data: List[Data], target: str, @@ -47,14 +54,6 @@ def skew( ------- OBBject[List[Data]] Rolling skew. - - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() - # We typically will want to use the returns for analysis - >>> returns = stock_data["close"].pct_change().dropna() - >>> obb.quantitative.stats.skew(data=returns, target="close") """ df = basemodel_to_df(data) @@ -65,7 +64,14 @@ def skew( return OBBject(results=results) -@router.command(methods=["POST"]) +@router.command( + methods=["POST"], + examples=[ + 'stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df()', + 'returns = stock_data["close"].pct_change().dropna()', + 'obb.quantitative.stats.variance(data=returns, target="close")', + ], +) def variance(data: List[Data], target: str) -> OBBject[List[Data]]: """ Calculate the variance of a target column. @@ -84,13 +90,6 @@ def variance(data: List[Data], target: str) -> OBBject[List[Data]]: ------- OBBject[List[Data]] An object containing the rolling variance values. - - Examples: - -------- - >>> from openbb import obb - >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() - >>> returns = stock_data["close"].pct_change().dropna() - >>> obb.quantitative.rolling.variance(data=returns, target="close") """ df = basemodel_to_df(data) series_target = get_target_column(df, target) @@ -100,7 +99,14 @@ def variance(data: List[Data], target: str) -> OBBject[List[Data]]: return OBBject(results=results) -@router.command(methods=["POST"]) +@router.command( + methods=["POST"], + examples=[ + 'stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df()', + 'returns = stock_data["close"].pct_change().dropna()', + 'obb.quantitative.stats.stdev(data=returns, target="close")', + ], +) def stdev(data: List[Data], target: str) -> OBBject[List[Data]]: """ Calculate the rolling standard deviation of a target column. @@ -116,20 +122,10 @@ def stdev(data: List[Data], target: str) -> OBBject[List[Data]]: target: str The name of the column for which to calculate standard deviation. - Returns ------- OBBject[List[Data]] An object containing the rolling standard deviation values. - - - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() - # We typically will want to use the returns for analysis - >>> returns = stock_data["close"].pct_change().dropna() - >>> obb.quantitative.stats.stdev(data=returns, target="close") """ df = basemodel_to_df(data) @@ -140,7 +136,14 @@ def stdev(data: List[Data], target: str) -> OBBject[List[Data]]: return OBBject(results=results) -@router.command(methods=["POST"]) +@router.command( + methods=["POST"], + examples=[ + 'stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df()', + 'returns = stock_data["close"].pct_change().dropna()', + 'obb.quantitative.stats.kurtosis(data=returns, target="close")', + ], +) def kurtosis(data: List[Data], target) -> OBBject[List[Data]]: """ Calculate the rolling kurtosis of a target column. @@ -160,14 +163,6 @@ def kurtosis(data: List[Data], target) -> OBBject[List[Data]]: ------ OBBject[List[Data]] An object containing the kurtosis value - - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() - # We typically will want to use the returns for analysis - >>> returns = stock_data["close"].pct_change().dropna() - >>> obb.quantitative.stats.kurtosis(data=returns, target="close") """ df = basemodel_to_df(data) @@ -178,7 +173,14 @@ def kurtosis(data: List[Data], target) -> OBBject[List[Data]]: return OBBject(results=results) -@router.command(methods=["POST"]) +@router.command( + methods=["POST"], + examples=[ + 'stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df()', + 'returns = stock_data["close"].pct_change().dropna()', + 'obb.quantitative.stats.quantile(data=returns, target="close", quantile_pct=0.75)', + ], +) def quantile( data: List[Data], target: str, @@ -190,26 +192,19 @@ def quantile( Quantiles are points dividing the range of a probability distribution into intervals with equal probabilities, or dividing the sample in the same way. - Parameters: - data: List[Data] - The time series data as a list of data points. - target: str - The name of the column for which to calculate the quantile. - quantile_pct: NonNegativeFloat, optional - The quantile percentage to calculate (e.g., 0.5 for median), default is 0.5. + Parameters + ---------- + data: List[Data] + The time series data as a list of data points. + target: str + The name of the column for which to calculate the quantile. + quantile_pct: NonNegativeFloat, optional + The quantile percentage to calculate (e.g., 0.5 for median), default is 0.5. Returns ------- OBBject[List[Data]] An object containing the rolling quantile values with the median. - - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() - >>> returns = stock_data["close"].pct_change().dropna() - >>> obb.quantitative.stats.quantile(data=returns, target="close") - >>> obb.quantitative.stats.quantile(data=returns, target="close") """ df = basemodel_to_df( @@ -223,7 +218,14 @@ def quantile( return OBBject(results=results) -@router.command(methods=["POST"]) +@router.command( + methods=["POST"], + examples=[ + 'stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df()', + 'returns = stock_data["close"].pct_change().dropna()', + 'obb.quantitative.stats.mean(data=returns, target="close")', + ], +) def mean( data: List[Data], target: str, @@ -246,14 +248,6 @@ def mean( ------- OBBject[List[Data]] An object containing the mean value. - - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.equity.price.historical(symbol="TSLA", start_date="2023-01-01", provider="fmp").to_df() - # We typically will want to use the returns for analysis - >>> returns = stock_data["close"].pct_change().dropna() - >>> obb.quantitative.stats.mean(data=returns, target="close") """ df = basemodel_to_df(data)