Portfolio analytics for quants - comprehensive statistical analysis, risk assessment, and performance visualization for quantitative finance
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Extended pandas functionality that seamlessly integrates QuantStats statistical analysis, risk assessment, plotting, and reporting capabilities directly into pandas DataFrames and Series for fluid method chaining and integrated workflows.
Enable QuantStats methods on pandas objects for method chaining.
def extend_pandas():
"""
Extend pandas DataFrames and Series with QuantStats methods.
This function adds all QuantStats statistical, plotting, and utility functions
as methods to pandas PandasObject, enabling method chaining syntax.
After calling this function, you can use QuantStats functions directly on
pandas objects: df.sharpe(), series.max_drawdown(), etc.
Returns:
None
"""extend_pandas()Once extend_pandas() is called, all the following methods become available on pandas DataFrames and Series:
All statistical functions from quantstats.stats become available as DataFrame/Series methods:
# Performance ratios
.sharpe(rf=0.0, periods=252, annualize=True, smart=False)
.sortino(rf=0, periods=252, annualize=True, smart=False)
.calmar(prepare_returns=True, periods=252)
.treynor_ratio(benchmark, periods=252.0, rf=0.0)
.omega(rf=0.0, required_return=0.0, periods=252)
# Risk metrics
.volatility(periods=252, annualize=True, prepare_returns=True)
.max_drawdown()
.value_at_risk(sigma=1, confidence=0.95, prepare_returns=True)
.var(sigma=1, confidence=0.95, prepare_returns=True)
.conditional_value_at_risk(sigma=1, confidence=0.95, prepare_returns=True)
.cvar(sigma=1, confidence=0.95, prepare_returns=True)
# Return analysis
.cagr(rf=0.0, compounded=True, periods=252)
.expected_return(aggregate=None, compounded=True, prepare_returns=True)
.avg_return(aggregate=None, compounded=True, prepare_returns=True)
.best(aggregate=None, compounded=True, prepare_returns=True)
.worst(aggregate=None, compounded=True, prepare_returns=True)
# Win/loss analysis
.win_rate(aggregate=None, compounded=True, prepare_returns=True)
.avg_win(aggregate=None, compounded=True, prepare_returns=True)
.avg_loss(aggregate=None, compounded=True, prepare_returns=True)
.consecutive_wins(aggregate=None, compounded=True, prepare_returns=True)
.consecutive_losses(aggregate=None, compounded=True, prepare_returns=True)
# Advanced metrics
.kelly_criterion(prepare_returns=True)
.ulcer_index()
.tail_ratio(cutoff=0.95, prepare_returns=True)
.skew(prepare_returns=True)
.kurtosis(prepare_returns=True)Rolling window statistical calculations:
.rolling_sharpe(rf=0, periods=252, window=126, annualize=True, prepare_returns=True)
.rolling_sortino(rf=0, periods=252, window=126, annualize=True, prepare_returns=True)
.rolling_volatility(periods=252, window=126, min_periods=None, annualize=True, prepare_returns=True)
.rolling_greeks(benchmark, periods=252, prepare_returns=True)Compare performance against benchmarks:
.r_squared(benchmark, prepare_returns=True)
.r2(benchmark) # Alias for r_squared
.information_ratio(benchmark, prepare_returns=True)
.greeks(benchmark, periods=252.0, prepare_returns=True)
.compare(benchmark, aggregate=None, compounded=True, round_vals=None)Data preparation and conversion functions:
.to_returns(rf=0.0)
.to_prices(base=1e5)
.to_log_returns(rf=0.0, nperiods=None)
.log_returns(rf=0.0, nperiods=None)
.to_excess_returns(rf, nperiods=None)
.rebase(base=100.0)
.aggregate_returns(period=None, compounded=True)
.exponential_stdev(window=30, is_halflife=False)
.multi_shift(shift=3)Date filtering utilities:
.mtd() # Month-to-date
.qtd() # Quarter-to-date
.ytd() # Year-to-date
.curr_month() # Current month
.date(dates) # Filter by specific datesAll visualization functions with plot_ prefix:
.plot_snapshot(grayscale=False, figsize=(10, 8), title="Portfolio Summary", ...)
.plot_earnings(start_balance=1e5, mode="comp", ...)
.plot_returns(benchmark=None, ...)
.plot_daily_returns(benchmark, ...)
.plot_distribution(...)
.plot_drawdown(...)
.plot_drawdowns_periods(periods=5, ...)
.plot_histogram(benchmark=None, resample="ME", ...)
.plot_log_returns(benchmark=None, ...)
.plot_rolling_beta(benchmark, window1=126, window2=252, ...)
.plot_rolling_sharpe(benchmark=None, rf=0.0, period=126, ...)
.plot_rolling_sortino(benchmark=None, rf=0.0, period=126, ...)
.plot_rolling_volatility(benchmark=None, period=126, ...)
.plot_yearly_returns(benchmark=None, ...)
.plot_monthly_heatmap(benchmark=None, ...)Generate performance metrics:
.metrics(benchmark=None, rf=0.0, display=True, mode="basic", compounded=True, ...)import quantstats as qs
import pandas as pd
import numpy as np
# Enable pandas integration
qs.extend_pandas()
# Create sample returns
dates = pd.date_range('2020-01-01', '2023-12-31', freq='D')
returns = pd.Series(np.random.normal(0.001, 0.02, len(dates)), index=dates)
# Use method chaining for analysis
analysis_results = (returns
.sharpe() # Calculate Sharpe ratio
, returns.sortino() # Calculate Sortino ratio
, returns.max_drawdown() # Calculate max drawdown
, returns.cagr() # Calculate CAGR
, returns.volatility() # Calculate volatility
)
print(f"Sharpe: {analysis_results[0]:.2f}")
print(f"Sortino: {analysis_results[1]:.2f}")
print(f"Max DD: {analysis_results[2]:.2%}")
print(f"CAGR: {analysis_results[3]:.2%}")
print(f"Volatility: {analysis_results[4]:.2%}")# Complete analysis pipeline with method chaining
returns_analysis = (returns
.to_excess_returns(rf=0.02) # Convert to excess returns
.aggregate_returns('M') # Aggregate to monthly
)
# Statistical analysis
monthly_stats = {
'sharpe': returns_analysis.sharpe(),
'sortino': returns_analysis.sortino(),
'calmar': returns_analysis.calmar(),
'max_dd': returns_analysis.max_drawdown(),
'win_rate': returns_analysis.win_rate()
}
# Create visualizations
returns.plot_snapshot(title="Portfolio Performance")
returns.plot_drawdown()
returns.plot_monthly_heatmap()# Download benchmark and perform comparison
spy_returns = qs.utils.download_returns('SPY')
# Benchmarking analysis
benchmark_analysis = {
'alpha': returns.greeks(spy_returns)['alpha'],
'beta': returns.greeks(spy_returns)['beta'],
'r_squared': returns.r_squared(spy_returns),
'info_ratio': returns.information_ratio(spy_returns),
'treynor': returns.treynor_ratio(spy_returns)
}
# Comparative visualization
returns.plot_returns(benchmark=spy_returns, title="Portfolio vs SPY")
returns.plot_rolling_sharpe(benchmark=spy_returns)# Time-based filtering and analysis
ytd_performance = returns.ytd().cagr()
mtd_performance = returns.mtd().cagr()
qtd_performance = returns.qtd().cagr()
print(f"YTD Performance: {ytd_performance:.2%}")
print(f"MTD Performance: {mtd_performance:.2%}")
print(f"QTD Performance: {qtd_performance:.2%}")
# Rolling analysis
rolling_metrics = pd.DataFrame({
'rolling_sharpe': returns.rolling_sharpe(window=252),
'rolling_volatility': returns.rolling_volatility(window=252),
'rolling_sortino': returns.rolling_sortino(window=252)
})# Comprehensive risk assessment using method chaining
risk_metrics = {
'var_95': returns.var(confidence=0.95),
'cvar_95': returns.cvar(confidence=0.95),
'var_99': returns.var(confidence=0.99),
'cvar_99': returns.cvar(confidence=0.99),
'ulcer_index': returns.ulcer_index(),
'tail_ratio': returns.tail_ratio(),
'kelly': returns.kelly_criterion()
}
# Display risk metrics
for metric, value in risk_metrics.items():
print(f"{metric}: {value:.4f}")# Generate comprehensive report using pandas integration
portfolio_report = returns.metrics(
benchmark=spy_returns,
rf=0.02,
mode='full'
)
# Create all visualizations
returns.plot_snapshot(title="Complete Portfolio Analysis")
returns.plot_distribution()
returns.plot_yearly_returns(benchmark=spy_returns)
# Export results
portfolio_report.to_csv('portfolio_metrics.csv')The extend_pandas() function works by adding methods to the pandas.core.base.PandasObject class, making them available to all pandas DataFrames and Series. This approach ensures compatibility with existing pandas code while providing the full power of QuantStats analytics.
All extended methods maintain the same parameter signatures and return types as their corresponding functions in the QuantStats modules, ensuring consistent behavior whether using function calls or method chaining.
Install with Tessl CLI
npx tessl i tessl/pypi-quantstats