From dd3889aba4c15d443ff0f8bc72ead39a33163495 Mon Sep 17 00:00:00 2001 From: Jerry Inyang Date: Fri, 19 Jul 2024 12:30:02 +0100 Subject: [PATCH 1/3] updated pandas freq string "M" (deprecated) to "ME" --- .gitignore | 1 + quantstats/reports.py | 96 ++++++++++++++++++++++++++++--------------- quantstats/utils.py | 11 ++--- 3 files changed, 69 insertions(+), 39 deletions(-) diff --git a/.gitignore b/.gitignore index c3604b2a..2e57fef0 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ Icon /tests .vscode Icon +.DS_Store \ No newline at end of file diff --git a/quantstats/reports.py b/quantstats/reports.py index de34b9c3..c803fe51 100644 --- a/quantstats/reports.py +++ b/quantstats/reports.py @@ -18,21 +18,29 @@ # See the License for the specific language governing permissions and # limitations under the License. -import pandas as _pd -import numpy as _np -from math import sqrt as _sqrt, ceil as _ceil -from datetime import datetime as _dt -from base64 import b64encode as _b64encode import re as _regex -from tabulate import tabulate as _tabulate -from . import __version__, stats as _stats, utils as _utils, plots as _plots -from dateutil.relativedelta import relativedelta +from base64 import b64encode as _b64encode +from datetime import datetime as _dt from io import StringIO +from math import ceil as _ceil +from math import sqrt as _sqrt + +import numpy as _np +import pandas as _pd +from dateutil.relativedelta import relativedelta +from tabulate import tabulate as _tabulate + +from . import __version__ +from . import plots as _plots +from . import stats as _stats +from . import utils as _utils try: - from IPython.display import display as iDisplay, HTML as iHTML + from IPython.display import HTML as iHTML + from IPython.display import display as iDisplay except ImportError: - from IPython.core.display import display as iDisplay, HTML as iHTML + from IPython.core.display import HTML as iHTML + from IPython.core.display import display as iDisplay def _get_trading_periods(periods_per_year=252): @@ -66,7 +74,6 @@ def html( match_dates=True, **kwargs, ): - if output is None and not _utils._in_notebook(): raise ValueError("`output` must be specified") @@ -501,7 +508,6 @@ def full( match_dates=True, **kwargs, ): - # prepare timeseries if match_dates: returns = returns.dropna() @@ -651,7 +657,6 @@ def basic( match_dates=True, **kwargs, ): - # prepare timeseries if match_dates: returns = returns.dropna() @@ -731,7 +736,6 @@ def metrics( match_dates=True, **kwargs, ): - if match_dates: returns = returns.dropna() returns.index = returns.index.tz_localize(None) @@ -930,13 +934,20 @@ def metrics( metrics["~~~~~~~~~~"] = blank metrics["Expected Daily %%"] = ( - _stats.expected_return(df, compounded=compounded, prepare_returns=False) * pct + _stats.expected_return(df, compounded=compounded, prepare_returns=False) + * pct ) metrics["Expected Monthly %%"] = ( - _stats.expected_return(df, compounded=compounded, aggregate="M", prepare_returns=False) * pct + _stats.expected_return( + df, compounded=compounded, aggregate="M", prepare_returns=False + ) + * pct ) metrics["Expected Yearly %%"] = ( - _stats.expected_return(df, compounded=compounded, aggregate="A", prepare_returns=False) * pct + _stats.expected_return( + df, compounded=compounded, aggregate="A", prepare_returns=False + ) + * pct ) metrics["Kelly Criterion %"] = ( _stats.kelly_criterion(df, prepare_returns=False) * pct @@ -957,7 +968,7 @@ def metrics( metrics["Max Consecutive Losses *int"] = _stats.consecutive_losses(df) metrics["Gain/Pain Ratio"] = _stats.gain_to_pain_ratio(df, rf) - metrics["Gain/Pain (1M)"] = _stats.gain_to_pain_ratio(df, rf, "M") + metrics["Gain/Pain (1M)"] = _stats.gain_to_pain_ratio(df, rf, "ME") # if mode.lower() == 'full': # metrics['GPR (3M)'] = _stats.gain_to_pain_ratio(df, rf, "Q") # metrics['GPR (6M)'] = _stats.gain_to_pain_ratio(df, rf, "2Q") @@ -1004,19 +1015,26 @@ def metrics( # best/worst if mode.lower() == "full": metrics["~~~"] = blank - metrics["Best Day %"] = _stats.best(df, compounded=compounded, prepare_returns=False) * pct + metrics["Best Day %"] = ( + _stats.best(df, compounded=compounded, prepare_returns=False) * pct + ) metrics["Worst Day %"] = _stats.worst(df, prepare_returns=False) * pct metrics["Best Month %"] = ( - _stats.best(df, compounded=compounded, aggregate="M", prepare_returns=False) * pct + _stats.best(df, compounded=compounded, aggregate="M", prepare_returns=False) + * pct ) metrics["Worst Month %"] = ( _stats.worst(df, aggregate="M", prepare_returns=False) * pct ) metrics["Best Year %"] = ( - _stats.best(df, compounded=compounded, aggregate="A", prepare_returns=False) * pct + _stats.best(df, compounded=compounded, aggregate="A", prepare_returns=False) + * pct ) metrics["Worst Year %"] = ( - _stats.worst(df, compounded=compounded, aggregate="A", prepare_returns=False) * pct + _stats.worst( + df, compounded=compounded, aggregate="A", prepare_returns=False + ) + * pct ) # dd @@ -1031,20 +1049,35 @@ def metrics( if mode.lower() == "full": metrics["~~~~~"] = blank metrics["Avg. Up Month %"] = ( - _stats.avg_win(df, compounded=compounded, aggregate="M", prepare_returns=False) * pct + _stats.avg_win( + df, compounded=compounded, aggregate="M", prepare_returns=False + ) + * pct ) metrics["Avg. Down Month %"] = ( - _stats.avg_loss(df, compounded=compounded, aggregate="M", prepare_returns=False) * pct + _stats.avg_loss( + df, compounded=compounded, aggregate="M", prepare_returns=False + ) + * pct ) metrics["Win Days %%"] = _stats.win_rate(df, prepare_returns=False) * pct metrics["Win Month %%"] = ( - _stats.win_rate(df, compounded=compounded, aggregate="M", prepare_returns=False) * pct + _stats.win_rate( + df, compounded=compounded, aggregate="M", prepare_returns=False + ) + * pct ) metrics["Win Quarter %%"] = ( - _stats.win_rate(df, compounded=compounded, aggregate="Q", prepare_returns=False) * pct + _stats.win_rate( + df, compounded=compounded, aggregate="Q", prepare_returns=False + ) + * pct ) metrics["Win Year %%"] = ( - _stats.win_rate(df, compounded=compounded, aggregate="A", prepare_returns=False) * pct + _stats.win_rate( + df, compounded=compounded, aggregate="A", prepare_returns=False + ) + * pct ) if "benchmark" in df: @@ -1212,7 +1245,6 @@ def plots( match_dates=True, **kwargs, ): - benchmark_colname = kwargs.get("benchmark_title", "Benchmark") strategy_colname = kwargs.get("strategy_title", "Strategy") active = kwargs.get("active", "False") @@ -1579,9 +1611,7 @@ def _download_html(html, filename="quantstats-tearsheet.html"): a.download="{{filename}}"; a.hidden=true;document.body.appendChild(a); a.innerHTML="download report"; - a.click();""".replace( - "\n", "" - ), + a.click();""".replace("\n", ""), ) jscode = jscode.replace("{{html}}", _regex.sub(" +", " ", html.replace("\n", ""))) if _utils._in_notebook(): @@ -1594,9 +1624,7 @@ def _open_html(html): " ", """""".replace( - "\n", "" - ), + """.replace("\n", ""), ) jscode = jscode.replace("{{html}}", _regex.sub(" +", " ", html.replace("\n", ""))) if _utils._in_notebook(): diff --git a/quantstats/utils.py b/quantstats/utils.py index 51eff8db..c4b52fba 100644 --- a/quantstats/utils.py +++ b/quantstats/utils.py @@ -17,13 +17,15 @@ # See the License for the specific language governing permissions and # limitations under the License. -import io as _io import datetime as _dt -import pandas as _pd +import inspect +import io as _io + import numpy as _np +import pandas as _pd import yfinance as _yf + from . import stats as _stats -import inspect def _mtd(df): @@ -259,10 +261,9 @@ def _prepare_benchmark(benchmark=None, period="max", rf=0.0, prepare_returns=Tru benchmark = benchmark[benchmark.columns[0]].copy() if isinstance(period, _pd.DatetimeIndex) and set(period) != set(benchmark.index): - # Adjust Benchmark to Strategy frequency benchmark_prices = to_prices(benchmark, base=1) - new_index = _pd.date_range(start=period[0], end=period[-1], freq="D") + new_index = _pd.date_range(start=period[0], end=period[-1], freq="d") benchmark = ( benchmark_prices.reindex(new_index, method="bfill") .reindex(period) From e4ca8ee3994fb4f7e845eaa26ef6a6f471509138 Mon Sep 17 00:00:00 2001 From: Jerry Inyang Date: Fri, 19 Jul 2024 14:25:06 +0100 Subject: [PATCH 2/3] corrected deprecated operation on a copy of a dataframe --- quantstats/_plotting/core.py | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/quantstats/_plotting/core.py b/quantstats/_plotting/core.py index 4a29f499..48b048b8 100644 --- a/quantstats/_plotting/core.py +++ b/quantstats/_plotting/core.py @@ -26,19 +26,13 @@ pass import matplotlib.dates as _mdates -from matplotlib.ticker import ( - FormatStrFormatter as _FormatStrFormatter, - FuncFormatter as _FuncFormatter, -) - -import pandas as _pd import numpy as _np +import pandas as _pd import seaborn as _sns -from .. import ( - stats as _stats, - utils as _utils, -) +from matplotlib.ticker import FormatStrFormatter as _FormatStrFormatter +from matplotlib.ticker import FuncFormatter as _FuncFormatter +from .. import stats as _stats _sns.set( font_scale=1.1, @@ -112,7 +106,6 @@ def plot_returns_bars( savefig=None, show=True, ): - if match_volatility and benchmark is None: raise ValueError("match_volatility requires passing of " "benchmark.") if match_volatility and benchmark is not None: @@ -265,7 +258,6 @@ def plot_timeseries( savefig=None, show=True, ): - colors, ls, alpha = _get_colors(grayscale) returns.fillna(0, inplace=True) @@ -419,7 +411,6 @@ def plot_histogram( savefig=None, show=True, ): - # colors = ['#348dc1', '#003366', 'red'] # if grayscale: # colors = ['silver', 'gray', 'black'] @@ -616,7 +607,6 @@ def plot_rolling_stats( savefig=None, show=True, ): - colors, _, _ = _get_colors(grayscale) fig, ax = _plt.subplots(figsize=figsize) @@ -745,7 +735,6 @@ def plot_rolling_beta( savefig=None, show=True, ): - colors, _, _ = _get_colors(grayscale) fig, ax = _plt.subplots(figsize=figsize) @@ -889,7 +878,6 @@ def plot_longest_drawdowns( savefig=None, show=True, ): - colors = ["#348dc1", "#003366", "red"] if grayscale: colors = ["#000000"] * 3 @@ -1001,7 +989,6 @@ def plot_distribution( savefig=None, show=True, ): - colors = _FLATUI_COLORS if grayscale: colors = ["#f9f9f9", "#dddddd", "#bbbbbb", "#999999", "#808080"] @@ -1013,16 +1000,16 @@ def plot_distribution( apply_fnc = _stats.comp if compounded else _np.sum port["Weekly"] = port["Daily"].resample("W-MON").apply(apply_fnc) - port["Weekly"].ffill(inplace=True) + port["Weekly"] = port["Weekly"].ffill() port["Monthly"] = port["Daily"].resample("M").apply(apply_fnc) - port["Monthly"].ffill(inplace=True) + port["Monthly"] = port["Monthly"].ffill() port["Quarterly"] = port["Daily"].resample("Q").apply(apply_fnc) - port["Quarterly"].ffill(inplace=True) + port["Quarterly"] = port["Quarterly"].ffill() port["Yearly"] = port["Daily"].resample("A").apply(apply_fnc) - port["Yearly"].ffill(inplace=True) + port["Yearly"] = port["Yearly"].ffill() fig, ax = _plt.subplots(figsize=figsize) ax.spines["top"].set_visible(False) @@ -1118,7 +1105,6 @@ def plot_table( savefig=None, show=False, ): - if columns is not None: try: tbl.columns = columns From cafc136d676445061d636fbd8ae93e2d05cf6105 Mon Sep 17 00:00:00 2001 From: Jerry Inyang Date: Fri, 19 Jul 2024 14:37:15 +0100 Subject: [PATCH 3/3] updated pandas freq labels --- quantstats/_plotting/core.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/quantstats/_plotting/core.py b/quantstats/_plotting/core.py index 48b048b8..6de1c009 100644 --- a/quantstats/_plotting/core.py +++ b/quantstats/_plotting/core.py @@ -1002,13 +1002,13 @@ def plot_distribution( port["Weekly"] = port["Daily"].resample("W-MON").apply(apply_fnc) port["Weekly"] = port["Weekly"].ffill() - port["Monthly"] = port["Daily"].resample("M").apply(apply_fnc) + port["Monthly"] = port["Daily"].resample("ME").apply(apply_fnc) port["Monthly"] = port["Monthly"].ffill() - port["Quarterly"] = port["Daily"].resample("Q").apply(apply_fnc) + port["Quarterly"] = port["Daily"].resample("QE").apply(apply_fnc) port["Quarterly"] = port["Quarterly"].ffill() - port["Yearly"] = port["Daily"].resample("A").apply(apply_fnc) + port["Yearly"] = port["Daily"].resample("YE").apply(apply_fnc) port["Yearly"] = port["Yearly"].ffill() fig, ax = _plt.subplots(figsize=figsize)