Agent skill

Performance Attribution

by HKUDS in HKUDS/Vibe-Trading

Explains why a portfolio beat or lagged its benchmark with Brinson sector attribution, factor alpha and beta decomposition, timing evaluation and benchmark comparison.

MITAuto-check passedBusiness, Finance & HR

Install Performance Attribution

skills CLI
$ npx skills add HKUDS/Vibe-Trading --skill performance-attribution -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install HKUDS/Vibe-Trading performance-attribution --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/HKUDS/Vibe-Trading.git skills-src && mkdir -p .claude/skills && cp -r skills-src/agent/src/skills/performance-attribution .claude/skills/performance-attribution && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
performance-attribution
GitHub stars
35k
Token cost
~3.1k tokens
SKILL.md length
798 words
Files
1
Skills in repo
89
Repo updated
First seen
Licence
MIT

At a glance

Explains why a portfolio beat or lagged its benchmark with Brinson sector attribution, factor alpha and beta decomposition, timing evaluation and benchmark comparison.

  • Works in 4 steps: Aggregate Analysis → Attribution Decomposition → Style Analysis → …
  • Decomposing a portfolio's excess return by sector allocation and stock selection
  • SKILL.md covers Overview, Brinson Attribution Model, Factor Attribution and Market-Timing Evaluation, plus 4 more sections
  • Instructions only: no scripts, shell commands, URLs or credentials in SKILL.md

What it does

The skill splits excess return into allocation, selection, factor exposure and timing, so you see why a strategy made or lost money and not only how much. For Brinson-Fachler attribution the agent is told to import the tested brinson_fachler function from src/quantlib/attribution.py instead of retyping formulas. The three effects sum exactly to portfolio minus benchmark return, and the function raises an error if the weights do not carry the same total.

It separates a residual inside the decomposition, which signals an arithmetic or weight-convention error, from a normal residual between the decomposition and the reported fund return, caused by intra-period trading, cash drag, corporate actions or FX, which should be quantified and attributed. A benchmark sector you did not own shows zero selection and interaction, so the whole effect falls in allocation. Multi-period linked attribution and an example table are included.

When your agent uses it

  • Decomposing a portfolio's excess return by sector allocation and stock selection
  • Reconciling an attribution result with the reported fund return
  • Separating factor alpha from beta exposure
  • Evaluating the market timing contribution against a benchmark

Example prompts

  • “Run a Brinson-Fachler attribution of my portfolio against the CSI 300 by sector.”
  • “Why doesn't my attribution tie to the reported fund return?”
  • “Explain why a sector I did not own shows zero selection.”
  • “Link the monthly Brinson results into one multi-period attribution.”

Requirements

  • The brinson_fachler function in src/quantlib/attribution.py

Workflow steps

4 steps, taken from the step headings in SKILL.md.

  1. Aggregate Analysis
  2. Attribution Decomposition
  3. Style Analysis
  4. Conclusions and Recommendations

What it can do on your machine

Read from SKILL.md and the folder at commit 7f6908b. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    No scripts in the folder and no shell commands in SKILL.md (its code samples are markdown and python).

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Performance Attribution loads about 3.1k tokens when it runs. Until then it costs about 49 tokens; SKILL.md has 798 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~49
When it runs · the whole SKILL.md, loaded when a task matches
~3.1k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from HKUDS/Vibe-Trading at commit 7f6908b, republished under its MIT licence (© HKUDS). 798 words, ~3,084 tokens.

Download SKILL.mdSave it as .claude/skills/performance-attribution/SKILL.md (or your agent's skills folder).
name
performance-attribution
description
Performance attribution analysis — Brinson sector/stock-selection attribution, factor alpha/beta decomposition, market-timing evaluation, and benchmark comparison framework.
category
analysis

Performance Attribution Analysis

Overview

Decompose portfolio excess returns into explainable sources: sector allocation, stock selection, factor exposure, timing contribution, and more. This helps explain why a strategy made or lost money, rather than only how much it made or lost.

Brinson Attribution Model

Do not retype these formulas into throwaway Python. They are implemented and tested in src/quantlib/attribution.py; import them.

Single-Period Brinson-Fachler Model
Let w_p,i = portfolio weight of sector i
    w_b,i = benchmark weight of sector i
    r_p,i = portfolio return of sector i
    r_b,i = benchmark return of sector i
    R_b   = total benchmark return

Allocation_i  = (w_p,i - w_b,i) × (r_b,i - R_b)
Selection_i   =  w_b,i          × (r_p,i - r_b,i)
Interaction_i = (w_p,i - w_b,i) × (r_p,i - r_b,i)

Total active return = Σ(Allocation_i) + Σ(Selection_i) + Σ(Interaction_i)

The decomposition itself has no residual term. The three effects sum to R_p - R_b identically, for any sector returns whatsoever, provided the portfolio and benchmark weights carry the same total. brinson_fachler enforces the weight-sum precondition and raises rather than returning a decomposition that does not tie out.

A residual is therefore never a property of the algebra — but it is a real and expected property of a reported attribution, because the inputs are a snapshot. Intra-period trading, cash drag, corporate actions and FX translation all move the actual portfolio return away from the one these weights and sector returns imply. So:

  • residual inside the decomposition, given the inputs → impossible; if you see one, the arithmetic or the weight convention is wrong;
  • residual between the decomposition and the reported fund return → normal; quantify it and attribute it to its source rather than absorbing it silently into selection. This is what the /attrib reconciliation gate asks for.
python
from src.quantlib.attribution import brinson_fachler

result = brinson_fachler(
    portfolio_weights={"Tech": 0.40, "Financials": 0.10, "Energy": 0.30, "Health": 0.20},
    benchmark_weights={"Tech": 0.25, "Financials": 0.30, "Energy": 0.25, "Health": 0.20},
    portfolio_returns={"Tech": 0.12, "Financials": 0.04, "Energy": -0.02, "Health": 0.07},
    benchmark_returns={"Tech": 0.10, "Financials": 0.05, "Energy": -0.01, "Health": 0.06},
)

result.portfolio_return   # 0.0600
result.benchmark_return   # 0.0495
result.active_return      # 0.0105
result.allocation         # 0.0045
result.selection          # 0.0015
result.interaction        # 0.0045
# 0.0045 + 0.0015 + 0.0045 == 0.0105 exactly (residual ~3e-18, machine epsilon)

for effect in result.sectors:
    print(effect.sector, effect.allocation, effect.selection, effect.interaction, effect.total)

A sector return may be omitted only where the matching weight is zero. A benchmark sector you did not own therefore shows zero selection and zero interaction, and the whole effect lands in allocation — you cannot demonstrate stock-picking skill in something you never held.

Example Brinson Attribution

Rendered from the call above, so every figure below is reproducible:

markdown
### Brinson Sector Attribution

| Sector | Portfolio Weight | Benchmark Weight | Portfolio Return | Benchmark Return | Allocation | Selection | Interaction |
|------|---------|---------|---------|---------|---------|---------|---------|
| Tech | 40% | 25% | 12% | 10% | +0.7575% | +0.50% | +0.30% |
| Financials | 10% | 30% | 4% | 5% | -0.0100% | -0.30% | +0.20% |
| Energy | 30% | 25% | -2% | -1% | -0.2975% | -0.25% | -0.05% |
| Health | 20% | 20% | 7% | 6% | +0.0000% | +0.20% | +0.00% |
| **Total** | 100% | 100% | 6.00% | 4.95% | **+0.45%** | **+0.15%** | **+0.45%** |

Active return 1.05% = allocation 0.45% + selection 0.15% + interaction 0.45%. No residual.
Multi-Period Attribution (Linked Brinson)

Single-period effects add, but returns compound, so simply summing each period's effects does not reproduce the compounded active return. Take the four-sector period above and two more like it (the exact three are the _three_periods fixture in tests/quantlib/test_attribution.py, so you can run them): summing the three active returns gives 2.8500%, while the compounded active return is 3.0318% — an 18.2bp error that grows with the horizon and the return level.

Use Carino logarithmic linking, implemented as carino_link. It is residual-free, and its per-period scaling factor depends only on that period's total portfolio and benchmark return — never on the effects being linked — so linking is deterministic and cannot be steered by how sectors were bucketed. (Menchero linking is also residual-free but distributes a correction term derived from the effects themselves; Carino needs less machinery for the same guarantee.)

k   = (ln(1 + R_P) - ln(1 + R_B)) / (R_P - R_B)        # over the whole horizon
k_t = (ln(1 + R_p,t) - ln(1 + R_b,t)) / (R_p,t - R_b,t)  # for period t

linked effect = Σ_t (k_t / k) × effect_{i,t}
python
from src.quantlib.attribution import brinson_fachler, carino_link

periods = [brinson_fachler(**month) for month in monthly_inputs]
linked = carino_link(periods)

linked.active_return   # compounded, not summed
linked.allocation, linked.selection, linked.interaction
linked.scaling_factors  # one k_t / k per period, exposed so a report can be audited

for sector in linked.sectors:
    print(sector.sector, sector.total)
# allocation + selection + interaction == linked.active_return exactly

Arithmetic linking is acceptable only when you explicitly report the residual. Since carino_link costs one function call and leaves none, prefer it.

Factor Attribution

Alpha-Beta Decomposition
R_p = α + β × R_m + ε

α (alpha): excess return, manager skill
β (beta): market exposure, systematic risk
ε (epsilon): residual, idiosyncratic risk

Regression method: OLS regression, with at least 60 data points
Multi-Factor Attribution (Fama-French Extension)
R_p - R_f = α + β_mkt × (R_m - R_f) + β_smb × SMB + β_hml × HML + β_mom × MOM + ε

| Factor | Meaning | China A-share Proxy |
|------|------|--------|
| MKT | Market | CSI 300 return |
| SMB | Small-cap premium | CSI 500 - CSI 300 |
| HML | Value premium | high-PB group - low-PB group |
| MOM | Momentum | top past-12M winners - bottom group |
Factor Exposure Analysis Template
markdown
### Factor Exposure Analysis

| Factor | Beta | t-stat | Significance | Interpretation |
|------|------|---------|--------|------|
| Market (MKT) | 0.85 | 12.3 | *** | Below 1, defensive profile |
| Small-cap (SMB) | 0.25 | 3.2 | ** | Small-cap tilt |
| Value (HML) | -0.15 | -1.8 | * | Growth tilt |
| Momentum (MOM) | 0.30 | 4.1 | *** | Significant momentum exposure |
| **Alpha** | **0.8% / month** | **2.5** | ** | **Significant alpha** |

R² = 0.72 → factors explain 72% of return variation
Alpha = 0.8% / month = 10% / year, significant

Market-Timing Evaluation

Treynor-Mazuy Model
R_p - R_f = α + β × (R_m - R_f) + γ × (R_m - R_f)² + ε

γ > 0 and significant → timing ability exists (adds risk in bull markets, cuts risk in bear markets)
γ ≤ 0 → no timing ability
Henriksson-Merton Model
R_p - R_f = α + β × (R_m - R_f) + γ × max(R_m - R_f, 0) + ε

γ > 0 → portfolio beta is higher in bull markets (successful timing)
Show full SKILL.md (342 more words)Show less
Practical Timing Metrics
MetricCalculationMeaning
Bull capture ratioportfolio return in bull markets / benchmark return>100% = outperforming
Bear capture ratioportfolio return in bear markets / benchmark return<100% = better downside defense
Timing hit rateproportion of months where market direction was called correctly>55% = shows skill
Correlation between position changes and marketcorr(position_change, future_return)>0 = timing is correct

Benchmark Comparison Framework

Benchmark Selection
Strategy TypeRecommended BenchmarkChina A-share Code
China A-share large capCSI 300000300.SH
China A-share small capCSI 500 / CSI 1000000905.SH
China A-share broad marketCSI All Share000985.SH
Hong Kong equitiesHang Seng IndexHSI
US equitiesS&P 500SPX
CryptoBTCBTC-USDT
Multi-asset60/40 portfolioself-constructed
Risk-Adjusted Performance Metrics
MetricFormulaExcellentGoodAverage
Sharpe(R_p - R_f) / σ_p>1.51.0-1.50.5-1.0
Sortino(R_p - R_f) / σ_down>2.01.5-2.01.0-1.5
CalmarR_p / MaxDD>1.00.5-1.00.2-0.5
Information Ratio(R_p - R_b) / TE>1.00.5-1.00.2-0.5
Treynor(R_p - R_f) / βused comparatively
Rolling Analysis
Use rolling windows (such as 12 months) to analyze:
- Rolling Sharpe: strategy stability
- Rolling alpha: whether alpha persists
- Rolling beta: whether market exposure is stable
- Rolling information ratio: persistence of benchmark outperformance

Suggested windows: 252 days for daily data, 12-36 months for monthly data

Analysis Framework

Step 1: Aggregate Analysis
1. Cumulative return vs benchmark
2. Excess-return decomposition (annual / monthly)
3. Summary risk metrics (volatility / max drawdown / Sharpe)
Step 2: Attribution Decomposition
1. Brinson attribution (if sector information is available)
2. Factor attribution (alpha / beta / factor exposure)
3. Timing attribution (TM / HM models)
Step 3: Style Analysis
1. Large cap vs small cap exposure
2. Growth vs value exposure
3. Style drift detection (rolling style analysis)
Step 4: Conclusions and Recommendations
1. Main sources of excess return
2. Whether risk exposure is reasonable
3. Suggested improvement directions

Output Format

markdown
## Performance Attribution Report

### Performance Overview
| Metric | Strategy | Benchmark | Excess |
|------|------|------|------|
| Cumulative return | +85.2% | +32.1% | +53.1% |
| Annualized return | 12.5% | 5.8% | +6.7% |
| Annualized volatility | 18.2% | 20.5% | - |
| Sharpe | 0.69 | 0.28 | - |
| Information Ratio | 0.82 | - | - |

### Attribution Breakdown
| Source | Contribution (annualized) | Share |
|------|-----------|------|
| Sector allocation | +2.1% | 31% |
| Stock selection | +3.8% | 57% |
| Timing | +0.8% | 12% |

### Factor Exposure
[factor exposure table]

### Conclusion
Excess return mainly comes from stock selection (57% contribution), followed by sector allocation.
Alpha is significant (`t=2.5`), indicating real stock-picking ability.
Watch the risk of excessive small-cap exposure (`SMB beta=0.25`).

Notes

  1. Attribution ≠ prediction: attribution explains the past; it does not guarantee persistence in the future
  2. Benchmark selection affects attribution: switch the benchmark and alpha may disappear, so benchmark choice must be appropriate
  3. Data frequency: daily attribution is noisy, monthly attribution is more stable but has fewer samples; recommended workflow is daily computation with monthly reporting
  4. Survivorship bias: delisted stocks may be excluded in backtests, creating false alpha
  5. Multiple-testing problem: if you test 100 strategies, about 5 may appear significant by chance (p=0.05); use multiple-comparison correction
  6. Factor data requirement: factor attribution requires factor return data, which can be obtained from tushare or self-constructed
  7. Attribution in backtest reports: metrics.csv already provides basic metrics after a backtest; this skill adds deeper attribution analysis
  8. Brinson is implemented, not improvised: src/quantlib/attribution.py holds the tested single-period and Carino-linked decomposition. Import it. Hand-written attribution code that reports a single-period residual is a bug in that code, not a property of the model

© HKUDS, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in agent/src/skills/performance-attribution of HKUDS/Vibe-Trading.

Open the folder on GitHubat commit 7f6908b

Compare with similar skills

Performance Attribution next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Performance Attribution compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Performance Attribution this skillHKUDS/Vibe-Trading35k—~3.1kAutomated safety check: PassMIT
Stock Deep Analysis Workflowwbh604/UZI-Skill7.1k—~9.1kAutomated safety check: NotesMIT
Three-Statement Model Builderginlix-ai/LangAlpha1.8k—~5.4kAutomated safety check: PassApache-2.0
Supply Chain Bottleneck Hunterxbtlin/ai-berkshire17k—~2.6kAutomated safety check: PassMIT
A-Share Daily Reviewqusong0627/QuantMind1.7k—~1.9kAutomated safety check: PassAGPL-3.0
Futu OpenAPI Market and Trading Assistantqusong0627/QuantMind1.7k—~3.3kAutomated safety check: NotesAGPL-3.0

Similar skills

  • Runs a staged deep analysis of a single stock on China A-share, Hong Kong and US markets, ending in an HTML report with valuation models and investor-panel scores.

    7.1k GitHub stars~9.1k tokensUpdated 1 mo ago
    Business, Finance & HRAuto-check: notes
  • Builds or repairs an integrated income statement, balance sheet and cash flow model in Excel with live formulas, supporting schedules, scenarios and a Checks sheet.

    1.8k GitHub stars~5.4k tokensUpdated today
    Business, Finance & HRAuto-check passed
  • Scans a long-running industry trend for supply chain chokepoints, aiming to find second- and third-layer suppliers that the market has not yet priced in.

    17k GitHub stars~2.6k tokensUpdated today
    Business, Finance & HRAuto-check passed
  • A-Share Daily Review

    qusong0627/QuantMind

    Produces a post-market review report for the China A-share market from local QuantDB data, news sentiment and model signals, ending in a next-day direction call.

    1.7k GitHub stars~1.9k tokensUpdated today
    Business, Finance & HRAuto-check passed
  • Queries Futu quotes, options, fundamentals and accounts and places orders through the Futu OpenAPI Python SDK, defaulting to simulated trading.

    1.7k GitHub stars~3.3k tokensUpdated today
    Business, Finance & HRAuto-check: notes
  • China Market Open Data Search

    OpenSenseNova/SenseNova-Skills

    Researches Chinese market, macro, trade, procurement, listed-company and regulatory information from free official sources that need no sign-up or API key.

    5.7k GitHub stars~954 tokensUpdated 19 days ago
    Business, Finance & HRAuto-check: notes

More from HKUDS/Vibe-Trading

All 89 skills in this repo
  • Eastmoney Market Data

    HKUDS/Vibe-Trading

    Index of Eastmoney's free, no-token market data interfaces for China A-shares and Hong Kong stocks: fund flows, dragon-tiger lists, margin trading, reports and news.

    35k GitHub stars~1k tokensUpdated yesterday
    Auto-check passed
  • OKX Market Data

    HKUDS/Vibe-Trading

    Retrieves public OKX cryptocurrency market data such as spot prices, candlesticks, funding rates and open interest through the OKX V5 REST API, with no authentication.

    35k GitHub stars~1.3k tokensUpdated yesterday
    Auto-check passed
  • SEC EDGAR Filings Fetcher

    HKUDS/Vibe-Trading

    Fetches U.S. SEC EDGAR data: resolves tickers to CIK numbers, lists recent 10-K, 10-Q and 8-K filings with document URLs, and pulls XBRL financial series.

    35k GitHub stars~1.4k tokensUpdated yesterday
    Auto-check passed
  • A-Share ST Risk Screener

    HKUDS/Vibe-Trading

    Predicts whether a mainland China A-share company risks an ST or *ST warning after its next annual report, using financial thresholds and Sina penalty records.

    35k GitHub stars~4.9k tokensUpdated yesterday
    Auto-check passed
  • Breaks a structural trend such as AI infrastructure into its physical supply chain and ranks lesser-known listed companies sitting on each bottleneck.

    35k GitHub stars~2.7k tokensUpdated yesterday
    Auto-check passed
  • Plans and drafts an eight-part, roughly 120k-word investigative series on one company, built around a strict fact-check pass rather than fast drafting.

    35k GitHub stars~2.4k tokensUpdated yesterday
    Auto-check passed

Works with

Questions about Performance Attribution

What does Performance Attribution do?

Explains why a portfolio beat or lagged its benchmark with Brinson sector attribution, factor alpha and beta decomposition, timing evaluation and benchmark comparison. The skill splits excess return into allocation, selection, factor exposure and timing, so you see why a strategy made or lost money and not only how much.py instead of retyping formulas.

When should I use Performance Attribution?

Performance Attribution fits situations like: decomposing a portfolio's excess return by sector allocation and stock selection; reconciling an attribution result with the reported fund return; separating factor alpha from beta exposure; evaluating the market timing contribution against a benchmark.

How do I install Performance Attribution in Claude Code?

Run `npx skills add HKUDS/Vibe-Trading --skill performance-attribution -a claude-code`. Or copy the skill folder (agent/src/skills/performance-attribution in HKUDS/Vibe-Trading) into .claude/skills/performance-attribution in your project. Claude Code loads it when a task matches its description.

How do I install Performance Attribution in Codex?

Run `npx skills add HKUDS/Vibe-Trading --skill performance-attribution -a codex`. Or copy the skill folder (agent/src/skills/performance-attribution in HKUDS/Vibe-Trading) into .agents/skills/performance-attribution in your project. Codex loads it when a task matches its description.

Can I use Performance Attribution in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add HKUDS/Vibe-Trading --skill performance-attribution -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/performance-attribution, .gemini/skills/performance-attribution, .github/skills/performance-attribution and .opencode/skills/performance-attribution in your project.

What does Performance Attribution need to run?

SKILL.md names no scripts, command-line tools or credentials: Performance Attribution is instructions for the agent only. Our summary lists: The brinson_fachler function in src/quantlib/attribution.py.

Does Performance Attribution access the network?

SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.

Is Performance Attribution safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Performance Attribution use?

Performance Attribution is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Performance Attribution use?

About 3.1k tokens (SKILL.md is roughly 12k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Performance Attribution?

Skills that share tags, products or a category with Performance Attribution: Stock Deep Analysis Workflow (wbh604/UZI-Skill, 7.1k stars), Three-Statement Model Builder (ginlix-ai/LangAlpha, 1.8k stars), Supply Chain Bottleneck Hunter (xbtlin/ai-berkshire, 17k stars) and A-Share Daily Review (qusong0627/QuantMind, 1.7k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Performance Attribution?

HKUDS (a GitHub organization) maintains it in HKUDS/Vibe-Trading, which has 34,884 GitHub stars. The repository holds 89 skills in this directory. The repository was last updated on October 6, 2026.

Source: HKUDS/Vibe-Trading on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.