Token导航 LogoToken导航TokenDH.com
待分类权限需确认github未标认证来源可访问许可证需确认审计通过

portfolio-analytics投资组合分析

Agent Skill

用于辅助数据整理、表格处理、CSV/Excel 分析、指标计算和图表准备。它适合让 Agent 清洗字段、汇总数据、发现异常、生成统计口径或把分析结果转成可读说明。使用时需要确认数据来源、字段含义和时间范围,避免把样本数据当全量事实;涉及敏感数据、导出文件或批量写回时,应先确认权限和脱敏边界。

总安装

382

周安装

34

GitHub Stars

16

下载量

280
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:portfolio-analytics(投资组合分析)
来源仓库:https://github.com/agiprolabs/claude-trading-skills
仓库路径:skills/portfolio-analytics
安装命令:
npx skills add https://github.com/agiprolabs/claude-trading-skills --skill portfolio-analytics
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/agiprolabs/claude-trading-skills --skill portfolio-analytics

简介

用于辅助数据整理、表格处理和指标计算,适合 CSV/Excel 分析和图表准备。

  • 可清洗字段、汇总数据、发现异常并生成统计口径。
  • 使用时需确认数据来源和时间范围,避免将样本数据当作全量事实。
  • 涉及敏感数据或批量写回时应先确认权限和脱敏边界。
  • portfolio-analytics 属于待分类类 Skill,可作为该场景下的辅助能力补充。

SKILL.md

Portfolio Analytics

Compute portfolio-level performance metrics from equity curves and trade logs. Covers return metrics, risk metrics, risk-adjusted ratios, drawdown analysis, rolling windows, benchmark comparison, trade-level statistics, and automated HTML report generation via quantstats.

When to Use This Skill

  • After backtesting a strategy (e.g., from vectorbt or strategy-framework)
  • Comparing multiple strategies or parameter sets side-by-side
  • Generating investor-ready performance reports
  • Evaluating live trading performance against benchmarks
  • Assessing risk-adjusted returns for portfolio allocation decisions

Prerequisites

uv pip install pandas numpy quantstats

Input Format

All analytics start from an equity curve — a time-indexed Series of portfolio values:

import pandas as pd
import numpy as np

# From a backtest
equity = pd.Series(
    [10000, 10150, 10080, 10320, 10510, 10440, 10680],
    index=pd.date_range("2025-01-01", periods=7, freq="D"),
    name="strategy_equity"
)

# Convert to returns
returns = equity.pct_change().dropna()

Return Metrics

Total Return

total_return = (equity.iloc[-1] / equity.iloc[0]) - 1

CAGR (Compound Annual Growth Rate)

days = (equity.index[-1] - equity.index[0]).days
cagr = (equity.iloc[-1] / equity.iloc[0]) ** (365.25 / days) - 1

Daily Mean Return

daily_mean = returns.mean()
annualized_mean = daily_mean * 252  # trading days

Cumulative Returns

cumulative = (1 + returns).cumprod() - 1

Risk Metrics

Annualized Volatility

daily_vol = returns.std()
annual_vol = daily_vol * np.sqrt(252)

Value at Risk (VaR)

Historical VaR at a given confidence level:

def historical_var(returns: pd.Series, confidence: float = 0.95) -> float:
    """Compute historical VaR.

    Args:
        returns: Daily return series.
        confidence: Confidence level (e.g., 0.95 for 95%).

    Returns:
        VaR as a positive number representing potential loss.
    """
    return -np.percentile(returns, (1 - confidence) * 100)

Conditional VaR (CVaR / Expected Shortfall)

def historical_cvar(returns: pd.Series, confidence: float = 0.95) -> float:
    """Mean of returns below the VaR threshold."""
    var = historical_var(returns, confidence)
    return -returns[returns <= -var].mean()

Maximum Drawdown

def max_drawdown(equity: pd.Series) -> float:
    """Maximum peak-to-trough decline."""
    peak = equity.cummax()
    drawdown = (equity - peak) / peak
    return drawdown.min()  # negative number

def drawdown_series(equity: pd.Series) -> pd.Series:
    """Full drawdown time series."""
    peak = equity.cummax()
    return (equity - peak) / peak

Time Underwater

def time_underwater(equity: pd.Series) -> int:
    """Longest consecutive period below previous peak (in days)."""
    dd = drawdown_series(equity)
    is_underwater = dd < 0
    groups = (~is_underwater).cumsum()
    underwater_periods = is_underwater.groupby(groups).sum()
    return int(underwater_periods.max()) if len(underwater_periods) > 0 else 0

Risk-Adjusted Ratios

Sharpe Ratio

def sharpe_ratio(
    returns: pd.Series,
    rf: float = 0.0,
    periods_per_year: int = 252
) -> float:
    """Annualized Sharpe ratio.

    Args:
        returns: Period returns.
        rf: Risk-free rate per period.
        periods_per_year: Annualization factor.

    Returns:
        Annualized Sharpe ratio.
    """
    excess = returns - rf
    if excess.std() == 0:
        return 0.0
    return (excess.mean() / excess.std()) * np.sqrt(periods_per_year)

Sortino Ratio

def sortino_ratio(
    returns: pd.Series,
    rf: float = 0.0,
    periods_per_year: int = 252
) -> float:
    """Annualized Sortino ratio (penalizes only downside vol)."""
    excess = returns - rf
    downside = excess[excess < 0]
    if len(downside) == 0 or downside.std() == 0:
        return float("inf") if excess.mean() > 0 else 0.0
    return (excess.mean() / downside.std()) * np.sqrt(periods_per_year)

Calmar Ratio

def calmar_ratio(equity: pd.Series, periods_per_year: int = 252) -> float:
    """CAGR divided by max drawdown (absolute value)."""
    returns = equity.pct_change().dropna()
    days = (equity.index[-1] - equity.index[0]).days
    cagr = (equity.iloc[-1] / equity.iloc[0]) ** (365.25 / days) - 1
    mdd = abs(max_drawdown(equity))
    if mdd == 0:
        return float("inf") if cagr > 0 else 0.0
    return cagr / mdd

Omega Ratio

def omega_ratio(
    returns: pd.Series,
    threshold: float = 0.0
) -> float:
    """Ratio of probability-weighted gains to losses."""
    excess = returns - threshold
    gains = excess[excess > 0].sum()
    losses = abs(excess[excess <= 0].sum())
    if losses == 0:
        return float("inf") if gains > 0 else 1.0
    return gains / losses

Information Ratio

def information_ratio(
    returns: pd.Series,
    benchmark_returns: pd.Series,
    periods_per_year: int = 252
) -> float:
    """Excess return per unit of tracking error."""
    active = returns - benchmark_returns
    if active.std() == 0:
        return 0.0
    return (active.mean() / active.std()) * np.sqrt(periods_per_year)

Rolling Analysis

Rolling Sharpe

def rolling_sharpe(
    returns: pd.Series,
    window: int = 63,
    rf: float = 0.0,
    periods_per_year: int = 252
) -> pd.Series:
    """Rolling annualized Sharpe ratio."""
    excess = returns - rf
    roll_mean = excess.rolling(window).mean()
    roll_std = excess.rolling(window).std()
    return (roll_mean / roll_std) * np.sqrt(periods_per_year)

Rolling Max Drawdown

def rolling_max_drawdown(equity: pd.Series, window: int = 252) -> pd.Series:
    """Rolling max drawdown over a fixed window."""
    result = pd.Series(index=equity.index, dtype=float)
    for i in range(window, len(equity)):
        window_eq = equity.iloc[i - window:i + 1]
        peak = window_eq.cummax()
        dd = (window_eq - peak) / peak
        result.iloc[i] = dd.min()
    return result

Trade-Level Analysis

When you have individual trade records:

def trade_statistics(pnl: pd.Series) -> dict:
    """Compute trade-level statistics from a series of trade PnL values.

    Args:
        pnl: Series where each value is the PnL of one trade.

    Returns:
        Dictionary of trade statistics.
    """
    wins = pnl[pnl > 0]
    losses = pnl[pnl < 0]
    total = len(pnl)

    win_rate = len(wins) / total if total > 0 else 0.0
    avg_win = wins.mean() if len(wins) > 0 else 0.0
    avg_loss = losses.mean() if len(losses) > 0 else 0.0
    largest_win = wins.max() if len(wins) > 0 else 0.0
    largest_loss = losses.min() if len(losses) > 0 else 0.0

    gross_profit = wins.sum() if len(wins) > 0 else 0.0
    gross_loss = abs(losses.sum()) if len(losses) > 0 else 0.0
    profit_factor = gross_profit / gross_loss if gross_loss > 0 else float("inf")

    expectancy = pnl.mean() if total > 0 else 0.0

    return {
        "total_trades": total,
        "win_rate": win_rate,
        "avg_win": avg_win,
        "avg_loss": avg_loss,
        "largest_win": largest_win,
        "largest_loss": largest_loss,
        "profit_factor": profit_factor,
        "expectancy": expectancy,
        "gross_profit": gross_profit,
        "gross_loss": gross_loss,
    }

Monthly / Yearly Return Tables

def monthly_returns_table(returns: pd.Series) -> pd.DataFrame:
    """Pivot returns into a month-by-year table.

    Returns:
        DataFrame with years as rows, months (1-12) as columns,
        and an Annual column.
    """
    monthly = returns.resample("ME").apply(lambda x: (1 + x).prod() - 1)
    table = monthly.groupby([monthly.index.year, monthly.index.month]).first()
    table = table.unstack(level=1)
    table.columns = [
        "Jan", "Feb", "Mar", "Apr", "May", "Jun",
        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
    ]
    # Annual column
    annual = returns.resample("YE").apply(lambda x: (1 + x).prod() - 1)
    table["Annual"] = annual.values[:len(table)]
    return table

Benchmark Comparison

def benchmark_comparison(
    strategy_returns: pd.Series,
    benchmark_returns: pd.Series,
    rf: float = 0.0
) -> dict:
    """Compare strategy to benchmark across key metrics."""
    strat_eq = (1 + strategy_returns).cumprod()
    bench_eq = (1 + benchmark_returns).cumprod()

    return {
        "strategy_total_return": strat_eq.iloc[-1] - 1,
        "benchmark_total_return": bench_eq.iloc[-1] - 1,
        "strategy_sharpe": sharpe_ratio(strategy_returns, rf),
        "benchmark_sharpe": sharpe_ratio(benchmark_returns, rf),
        "strategy_max_dd": max_drawdown(strat_eq),
        "benchmark_max_dd": max_drawdown(bench_eq),
        "information_ratio": information_ratio(strategy_returns, benchmark_returns),
        "correlation": strategy_returns.corr(benchmark_returns),
        "beta": (
            strategy_returns.cov(benchmark_returns)
            / benchmark_returns.var()
        ),
        "alpha": (
            strategy_returns.mean()
            - (strategy_returns.cov(benchmark_returns) / benchmark_returns.var())
            * benchmark_returns.mean()
        ) * 252,
    }

Quantstats HTML Reports

Generate investor-ready HTML reports with one function call:

import quantstats as qs

# From returns Series
qs.reports.html(
    returns,
    benchmark=benchmark_returns,  # optional
    output="report.html",
    title="My Strategy",
    rf=0.0,
    periods_per_year=252
)

# Individual metrics
print(f"Sharpe: {qs.stats.sharpe(returns):.2f}")
print(f"Sortino: {qs.stats.sortino(returns):.2f}")
print(f"Max DD: {qs.stats.max_drawdown(returns):.2%}")
print(f"Calmar: {qs.stats.calmar(returns):.2f}")

# Console tearsheet
qs.reports.full(returns)

See references/quantstats_guide.md for full API reference and customization.

Integration with Vectorbt

import vectorbt as vbt

# After running a vectorbt backtest
portfolio = vbt.Portfolio.from_signals(close, entries, exits, init_cash=10000)

# Extract equity curve
equity = portfolio.value()
returns = portfolio.returns()

# Use quantstats
qs.reports.html(returns, output="backtest_report.html")

Files

FileDescription
references/metrics_guide.mdFormulas, derivations, annualization factors, interpretation benchmarks
references/quantstats_guide.mdQuantstats library API, customization, integration patterns
scripts/analyze_portfolio.pySingle portfolio analysis with all metrics, rolling stats, monthly table
scripts/compare_strategies.pyMulti-strategy comparison with ranking by risk-adjusted metrics

Related Skills

  • vectorbt — Backtesting engine that produces equity curves for analysis
  • risk-management — Portfolio-level risk guardrails and allocation
  • position-sizing — Optimal position sizing using portfolio metrics
  • kelly-criterion — Optimal growth rate sizing from win rate and payoff
  • trading-visualization — Chart generation for equity curves and drawdowns

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

33.79%
按下载量换算95

Claude

30.28%
按下载量换算85

Cursor

17.03%
按下载量换算48

Gemini CLI

9.59%
按下载量换算27

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

权限需确认

当前来源未能明确判断权限范围,默认进入异常复核队列。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills