Skip to content

Metrics & Analytics

The metrics module computes 50+ financial and risk metrics from ISO trade data. The pipeline flows through a layered architecture: Schema adapts ISO-specific DataFrames, Series wraps daily time series, Calculator produces metric dataclasses, and Engine provides the public facade.

Quick Start

from progridpy.metrics import MetricsEngine

engine = MetricsEngine(iso_name="MISO", df_iso=trade_df)
result = engine.compute()

print(f"Sharpe Ratio: {engine.sharpe_overall:.2f}")
print(f"Max Drawdown: ${engine.max_drawdown:,.2f}")
print(f"Win Rate: {engine.win_rate_pct:.1f}%")

MetricsEngine

MetricsEngine is the primary entry point. It accepts an ISO name and a pandas DataFrame of trade data.

from progridpy.metrics import MetricsEngine

engine = MetricsEngine(iso_name="MISO", df_iso=trade_df)

Constructor Parameters

Parameter Type Description
iso_name str ISO identifier: "MISO", "SPP", or "ERCOT"
df_iso pd.DataFrame Trade data with ISO-specific columns

Computing Metrics

Call compute() to run the full calculation pipeline. This method is idempotent -- subsequent calls return the cached result.

result = engine.compute()  # Returns MetricsResult

Convenience Properties

After calling compute(), access key metrics directly:

engine.estimated_risk     # float -- Estimated risk value
engine.sharpe_overall     # float -- Overall Sharpe ratio
engine.sortino_overall    # float -- Overall Sortino ratio
engine.win_rate_pct       # float -- Win rate percentage
engine.max_drawdown       # float -- Maximum drawdown
engine.calendar_returns   # dict[int, float] -- Calendar returns by year

Call compute() first

Accessing any property before calling compute() raises RuntimeError.

MetricsResult

MetricsResult groups all metrics into five dataclass categories:

result = engine.compute()

result.risk        # RiskMetrics
result.returns     # ReturnMetrics
result.ratios      # RatioMetrics
result.drawdowns   # DrawdownMetrics
result.streaks     # StreakMetrics

Exporting to DataFrame

Flatten all metrics into a two-column DataFrame:

metrics_df = engine.metrics_df  # DataFrame with 'metric' and 'value' columns
metrics_df.to_csv("metrics_report.csv", index=False)

The to_frame() method on MetricsResult produces the same output:

df = result.to_frame()

Accessing Metric Groups

RiskMetrics

risk = result.risk

risk.estimated_risk     # Estimated daily risk
risk.cvar_1             # CVaR at 1%
risk.cvar_5             # CVaR at 5%
risk.var_99             # VaR at 99%
risk.var_95             # VaR at 95%
risk.var_90             # VaR at 90%
risk.capital_required   # ISO-specific capital requirement (or None)

ReturnMetrics

returns = result.returns

returns.mean_daily           # Mean daily return
returns.median_daily         # Median daily return
returns.mean_monthly_30d     # Mean 30-day rolling return
returns.median_monthly_30d   # Median 30-day rolling return
returns.mean_annual_365d     # Mean 365-day rolling return
returns.median_annual_365d   # Median 365-day rolling return
returns.calendar_returns     # dict[int, float] -- total gains per year
returns.calendar_returns_risk_adjusted  # dict[int, float] -- gains / estimated risk per year

RatioMetrics

ratios = result.ratios

ratios.mean_daily_over_estimated_risk      # Mean daily / estimated risk
ratios.median_daily_over_estimated_risk    # Median daily / estimated risk
ratios.mean_monthly_over_estimated_risk    # Mean monthly / estimated risk
ratios.median_monthly_over_estimated_risk  # Median monthly / estimated risk
ratios.mean_annual_over_estimated_risk     # Mean annual / estimated risk
ratios.median_annual_over_estimated_risk   # Median annual / estimated risk
ratios.sharpe_overall                      # Overall Sharpe ratio
ratios.sortino_overall                     # Overall Sortino ratio
ratios.sharpe_by_year                      # dict[int, float]
ratios.sortino_by_year                     # dict[int, float]
ratios.mean_annual_calendar_over_estimated_risk    # Mean calendar annual / risk
ratios.median_annual_calendar_over_estimated_risk  # Median calendar annual / risk

DrawdownMetrics

dd = result.drawdowns

dd.worst_daily_loss             # Worst single-day loss
dd.worst_daily_loss_date        # Date of worst daily loss
dd.worst_7d_loss                # Worst 7-day rolling loss
dd.worst_7d_loss_start_date     # Start date of worst 7-day period
dd.worst_7d_loss_end_date       # End date of worst 7-day period
dd.worst_1m_30d_loss            # Worst 30-day loss
dd.worst_3m_90d_loss            # Worst 90-day loss
dd.worst_6m_180d_loss           # Worst 180-day loss
dd.worst_12m_365d_loss          # Worst 365-day loss
dd.max_drawdown                 # Worst any-period loss (peak-to-trough)
# Each period also has start_date, end_date, and /estimated_risk variants

StreakMetrics

streaks = result.streaks

streaks.worst_any_period_start_date       # Start of worst any-period loss
streaks.worst_any_period_end_date         # End of worst any-period loss
streaks.worst_any_period_length_days      # Length in days
streaks.longest_loss_period_start_date    # Start of longest consecutive loss
streaks.longest_loss_period_end_date      # End of longest consecutive loss
streaks.longest_loss_period_length_days   # Length in days
streaks.longest_period_loss               # Total loss during longest loss period
streaks.win_rate_pct                      # Percentage of profitable days
streaks.pct_1m_periods_with_loss          # % of 1-month periods with net loss
streaks.pct_3m_periods_with_loss          # % of 3-month periods with net loss
streaks.pct_6m_periods_with_loss          # % of 6-month periods with net loss
streaks.pct_12m_periods_with_loss         # % of 12-month periods with net loss

Complete Metrics Reference

Risk Metrics (7)

Metric Field Description
Estimated Risk estimated_risk Estimated daily risk
CVaR 1% cvar_1 Conditional Value at Risk at 1%
CVaR 5% cvar_5 Conditional Value at Risk at 5%
VaR 99% var_99 Value at Risk at 99th percentile
VaR 95% var_95 Value at Risk at 95th percentile
VaR 90% var_90 Value at Risk at 90th percentile
Capital Required capital_required ISO-specific capital requirement

Return Metrics (8+)

Metric Field Description
Mean Daily Return mean_daily Average daily gain
Median Daily Return median_daily Median daily gain
Mean Monthly Return mean_monthly_30d Average 30-day rolling sum
Median Monthly Return median_monthly_30d Median 30-day rolling sum
Mean Annual Return mean_annual_365d Average 365-day rolling sum
Median Annual Return median_annual_365d Median 365-day rolling sum
Calendar Returns calendar_returns Total gains per calendar year
Calendar Returns (Risk-Adj) calendar_returns_risk_adjusted Gains / estimated risk per year

Ratio Metrics (12+)

Metric Field Description
Mean Daily / Risk mean_daily_over_estimated_risk Daily return normalized by risk
Median Daily / Risk median_daily_over_estimated_risk Median daily / risk
Mean Monthly / Risk mean_monthly_over_estimated_risk Monthly return / risk
Median Monthly / Risk median_monthly_over_estimated_risk Median monthly / risk
Mean Annual / Risk mean_annual_over_estimated_risk Annual return / risk
Median Annual / Risk median_annual_over_estimated_risk Median annual / risk
Sharpe Overall sharpe_overall Overall Sharpe ratio
Sortino Overall sortino_overall Overall Sortino ratio
Sharpe by Year sharpe_by_year Sharpe ratio per calendar year
Sortino by Year sortino_by_year Sortino ratio per calendar year
Mean Calendar Annual / Risk mean_annual_calendar_over_estimated_risk Mean of calendar-year returns / risk
Median Calendar Annual / Risk median_annual_calendar_over_estimated_risk Median of calendar-year returns / risk

Drawdown Metrics (22)

Metric Field Description
Worst Daily Loss worst_daily_loss Single worst day
Worst Daily Loss Date worst_daily_loss_date Date of worst day
Worst 7d Loss worst_7d_loss Worst rolling 7-day sum
Worst 1m Loss (30d) worst_1m_30d_loss Worst rolling 30-day sum
Worst 3m Loss (90d) worst_3m_90d_loss Worst rolling 90-day sum
Worst 6m Loss (180d) worst_6m_180d_loss Worst rolling 180-day sum
Worst 12m Loss (365d) worst_12m_365d_loss Worst rolling 365-day sum
Max Drawdown max_drawdown Worst peak-to-trough decline
Each period also includes *_start_date, *_end_date Start and end dates
Risk-adjusted variants *_over_estimated_risk Loss divided by estimated risk

Streak Metrics (12)

Metric Field Description
Worst Any Period Start worst_any_period_start_date Start of worst drawdown period
Worst Any Period End worst_any_period_end_date End of worst drawdown period
Worst Any Period Length worst_any_period_length_days Duration in days
Longest Loss Period Start longest_loss_period_start_date Start of longest consecutive loss
Longest Loss Period End longest_loss_period_end_date End of longest consecutive loss
Longest Loss Period Length longest_loss_period_length_days Duration in days
Longest Period Loss longest_period_loss Total loss during longest loss period
Win Rate win_rate_pct Percentage of profitable days
% 1m Periods with Loss pct_1m_periods_with_loss Fraction of 30-day windows with net loss
% 3m Periods with Loss pct_3m_periods_with_loss Fraction of 90-day windows with net loss
% 6m Periods with Loss pct_6m_periods_with_loss Fraction of 180-day windows with net loss
% 12m Periods with Loss pct_12m_periods_with_loss Fraction of 365-day windows with net loss

Visualization Access

The engine provides a plots property that returns a MetricsPlots instance for individual Plotly charts:

plots = engine.plots

fig = plots.performance_chart()          # 3-pane cumulative/drawdown/daily
fig = plots.gains_histogram("daily")     # Distribution histogram
fig = plots.cumulative_gains_by_year()   # Yearly overlay
fig = plots.daily_gains_bar_chart(30)    # Last N days bar chart
fig = plots.daily_gains_heatmap(2026)    # GitHub-style heatmap
fig = plots.quantile_box_plots()         # Period distribution box plots

fig.show()  # Opens in browser

See Dashboards for the full Streamlit dashboard and HTML export.