quantstats
Advanced tools
+1
-1
| Metadata-Version: 2.4 | ||
| Name: QuantStats | ||
| Version: 0.0.75 | ||
| Version: 0.0.76 | ||
| Summary: Portfolio analytics for quants | ||
@@ -5,0 +5,0 @@ Home-page: https://github.com/ranaroussi/quantstats |
| Metadata-Version: 2.4 | ||
| Name: QuantStats | ||
| Version: 0.0.75 | ||
| Version: 0.0.76 | ||
| Summary: Portfolio analytics for quants | ||
@@ -5,0 +5,0 @@ Home-page: https://github.com/ranaroussi/quantstats |
+49
-13
@@ -61,2 +61,32 @@ #!/usr/bin/env python | ||
| def normalize_timezone(data: Union[pd.Series, pd.DataFrame]) -> Union[pd.Series, pd.DataFrame]: | ||
| """ | ||
| Normalize timezone information for consistent comparisons. | ||
| If data has timezone info, converts to UTC then removes timezone info. | ||
| This ensures all data can be compared regardless of original timezone. | ||
| Parameters | ||
| ---------- | ||
| data : pd.Series or pd.DataFrame | ||
| Time series data with DatetimeIndex | ||
| Returns | ||
| ------- | ||
| pd.Series or pd.DataFrame | ||
| Data with timezone-naive DatetimeIndex | ||
| """ | ||
| if not isinstance(data.index, pd.DatetimeIndex): | ||
| return data | ||
| # If timezone aware, convert to UTC then make naive | ||
| if data.index.tz is not None: | ||
| result = data.copy() | ||
| result.index = result.index.tz_convert('UTC').tz_localize(None) | ||
| return result | ||
| # Already timezone naive, return as is | ||
| return data | ||
| def safe_resample(data: Union[pd.Series, pd.DataFrame], | ||
@@ -71,3 +101,4 @@ freq: str, | ||
| frequency aliases and aggregation methods that are compatible across | ||
| different pandas versions. | ||
| different pandas versions. It also normalizes timezones to ensure | ||
| consistent comparisons. | ||
@@ -89,3 +120,4 @@ Parameters | ||
| pd.Series or pd.DataFrame | ||
| The resampled data with the specified frequency and aggregation | ||
| The resampled data with the specified frequency and aggregation, | ||
| with timezone normalized to UTC if present, or naive if not | ||
@@ -109,30 +141,34 @@ Examples | ||
| # This approach avoids deprecation warnings and ensures compatibility | ||
| result = None | ||
| if isinstance(func_name, str): | ||
| # Map common aggregation functions to their pandas methods | ||
| if func_name == "sum": | ||
| return resampler.sum(**kwargs) | ||
| result = resampler.sum(**kwargs) | ||
| elif func_name == "mean": | ||
| return resampler.mean(**kwargs) | ||
| result = resampler.mean(**kwargs) | ||
| elif func_name == "std": | ||
| return resampler.std(**kwargs) | ||
| result = resampler.std(**kwargs) | ||
| elif func_name == "count": | ||
| return resampler.count(**kwargs) | ||
| result = resampler.count(**kwargs) | ||
| elif func_name == "min": | ||
| return resampler.min(**kwargs) | ||
| result = resampler.min(**kwargs) | ||
| elif func_name == "max": | ||
| return resampler.max(**kwargs) | ||
| result = resampler.max(**kwargs) | ||
| elif func_name == "first": | ||
| return resampler.first(**kwargs) | ||
| result = resampler.first(**kwargs) | ||
| elif func_name == "last": | ||
| return resampler.last(**kwargs) | ||
| result = resampler.last(**kwargs) | ||
| else: | ||
| # Try to find the method on the resampler object | ||
| if hasattr(resampler, func_name): | ||
| return getattr(resampler, func_name)(**kwargs) | ||
| result = getattr(resampler, func_name)(**kwargs) | ||
| else: | ||
| # Fallback to apply for custom string functions | ||
| return resampler.apply(func_name, **kwargs) | ||
| result = resampler.apply(func_name, **kwargs) | ||
| else: | ||
| # For callable functions, use apply method | ||
| return resampler.apply(func_name, **kwargs) | ||
| result = resampler.apply(func_name, **kwargs) | ||
| # Normalize timezone to ensure consistent comparisons | ||
| return normalize_timezone(result) | ||
@@ -139,0 +175,0 @@ |
@@ -164,3 +164,3 @@ #!/usr/bin/env python | ||
| # Convert to portfolio format and calculate percentage changes | ||
| returns = _utils.make_portfolio(returns.dropna(), 1, mode).pct_change().fillna(0) | ||
| returns = _utils.make_portfolio(returns.dropna(), 1, mode).pct_change(fill_method=None).fillna(0) | ||
@@ -167,0 +167,0 @@ # Use current figure size if not specified |
+23
-11
@@ -455,2 +455,7 @@ #!/usr/bin/env python | ||
| """ | ||
| # Normalize timezone for consistency before aggregation | ||
| # Convert to UTC if timezone-aware, then make naive | ||
| if hasattr(returns.index, 'tz') and returns.index.tz is not None: | ||
| returns = returns.tz_convert('UTC').tz_localize(None) | ||
| # Return original data if no period specified or daily period | ||
@@ -566,4 +571,6 @@ if period is None or "day" in period: | ||
| # Remove timezone information for consistency | ||
| data = data.tz_localize(None) | ||
| # Normalize timezone information for consistency | ||
| # Convert to UTC if timezone-aware, then make naive | ||
| if hasattr(data.index, 'tz') and data.index.tz is not None: | ||
| data = data.tz_convert('UTC').tz_localize(None) | ||
| return data | ||
@@ -608,6 +615,6 @@ | ||
| if col_clean.min() >= 0 and col_clean.max() > 1: | ||
| data[col] = data[col].pct_change() | ||
| data[col] = data[col].pct_change(fill_method=None) | ||
| # Process Series data | ||
| elif data.min() >= 0 and data.max() > 1: | ||
| data = data.pct_change() | ||
| data = data.pct_change(fill_method=None) | ||
@@ -640,4 +647,6 @@ # cleanup data - replace infinite values with NaN | ||
| # Remove timezone information for consistency | ||
| data = data.tz_localize(None) | ||
| # Normalize timezone information for consistency | ||
| # Convert to UTC if timezone-aware, then make naive | ||
| if hasattr(data.index, 'tz') and data.index.tz is not None: | ||
| data = data.tz_convert('UTC').tz_localize(None) | ||
@@ -686,4 +695,4 @@ # Cache the result | ||
| # Download data and calculate returns | ||
| df = safe_yfinance_download(proxy=proxy, **params)["Close"].pct_change() # type: ignore | ||
| df = df.tz_localize(None) | ||
| df = safe_yfinance_download(proxy=proxy, **params)["Close"].pct_change(fill_method=None) # type: ignore | ||
| df = df.fillna(0).tz_localize(None) | ||
| return df | ||
@@ -719,3 +728,3 @@ | ||
| .reindex(period) | ||
| .pct_change() | ||
| .pct_change(fill_method=None) | ||
| .fillna(0) | ||
@@ -725,4 +734,7 @@ ) | ||
| # Remove timezone information | ||
| benchmark = benchmark.tz_localize(None) | ||
| # Normalize timezone information for consistent comparisons | ||
| # Convert to UTC if timezone-aware, then make naive | ||
| if hasattr(benchmark.index, 'tz') and benchmark.index.tz is not None: | ||
| benchmark = benchmark.tz_convert('UTC').tz_localize(None) | ||
| # If already timezone-naive, no action needed | ||
@@ -729,0 +741,0 @@ # Prepare returns or return raw data |
@@ -1,1 +0,1 @@ | ||
| version = "0.0.75" | ||
| version = "0.0.76" |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
410165
1.03%9398
0.74%