返回 Skills
tradermonty/claude-trading-skills· MIT 内容可用

kanchi-dividend-us-tax-accounting

Provide US dividend tax and account-location workflow for Kanchi-style income portfolios. Use when users ask about qualified vs ordinary dividends, 1099-DIV interpretation, REIT/BDC distribution treatment, holding-period checks, or taxable-vs-IRA account placement decisions for dividend assets.

安装

与 skills.sh 相同的 Command / Prompt 安装方式


name: kanchi-dividend-us-tax-accounting description: Provide US dividend tax and account-location workflow for Kanchi-style income portfolios. Use when users ask about qualified vs ordinary dividends, 1099-DIV interpretation, REIT/BDC distribution treatment, holding-period checks, or taxable-vs-IRA account placement decisions for dividend assets.

Kanchi Dividend Us Tax Accounting

Overview

Apply a practical US-tax workflow for dividend investors while keeping decisions auditable. Focus on account placement and classification, not legal/tax advice replacement.

When to Use

Use this skill when the user needs:

  • US dividend tax classification planning (qualified vs ordinary assumptions).
  • Holding-period checks before year-end tax planning.
  • Account-location decisions for stock/REIT/BDC/MLP income holdings.
  • A standardized annual dividend tax memo format.

Prerequisites

Prepare holding-level inputs:

  • ticker
  • instrument_type
  • account_type
  • hold_days_in_window (if available)

Expected JSON Input Format

{
  "holdings": [
    {
      "ticker": "JNJ",
      "instrument_type": "stock",
      "account_type": "taxable",
      "security_type": "common",
      "hold_days_in_window": 75
    },
    {
      "ticker": "O",
      "instrument_type": "reit",
      "account_type": "ira",
      "hold_days_in_window": 100
    }
  ]
}

For deterministic output artifacts, provide JSON input and run:

python3 skills/kanchi-dividend-us-tax-accounting/scripts/build_tax_planning_sheet.py \
  --input /path/to/tax_input.json \
  --output-dir reports/

Guardrails

Always state this clearly: tax outcomes depend on individual facts and jurisdiction. Treat this skill as planning support, then escalate final filing decisions to a tax professional.

Workflow

1) Classify each distribution stream

For each holding, classify expected cash flow into:

  • Potential qualified dividend.
  • Ordinary dividend/non-qualified distribution.
  • REIT/BDC-specific distribution components where applicable.

Use references/qualified-dividend-checklist.md for holding-period and classification checks.

2) Validate holding-period eligibility assumptions

For potential qualified treatment:

  • Check ex-dividend date windows.
  • Check required minimum holding days in the measurement window.
  • Flag positions at risk of failing holding-period requirement.

If data is incomplete, mark status as ASSUMPTION-REQUIRED.

3) Map to reporting fields

Map planning assumptions to expected tax-form buckets:

  • Ordinary dividend total.
  • Qualified dividend subset.
  • REIT-related components when reported separately.

Use form terminology consistently so year-end reconciliation is straightforward.

4) Build account-location recommendation

Use references/account-location-matrix.md to place assets by tax profile:

  • Taxable account for holdings likely to remain qualified-focused.
  • Tax-advantaged account for higher ordinary-income style distributions.

When constraints conflict (liquidity, strategy, concentration), explain the tradeoff explicitly.

5) Produce annual planning memo

Use references/annual-tax-memo-template.md and include:

  • Assumptions used.
  • Distribution classification summary.
  • Placement actions taken.
  • Open items for CPA/tax-advisor review.

Output

Always output:

  1. Holding-level distribution classification table.
  2. Account-location recommendation table with rationale.
  3. Open-risk checklist for unresolved tax assumptions.
  4. Optional generated artifacts from skills/kanchi-dividend-us-tax-accounting/scripts/build_tax_planning_sheet.py.

Cadence

Use this minimum rhythm:

  • Annually (60 min): full tax planning memo with account-location review.
  • Quarterly (15 min): refresh holding-period status for recent acquisitions.
  • Ad-hoc: rerun after material position changes, REIT/BDC additions, or triggered reviews from kanchi-dividend-review-monitor.

Multi-Skill Handoff

  • Receive candidate and holding list from kanchi-dividend-sop.
  • Receive risk-event context (WARN/REVIEW) from kanchi-dividend-review-monitor.
  • Return account-location constraints back to kanchi-dividend-sop before new entries.

Resources

  • skills/kanchi-dividend-us-tax-accounting/scripts/build_tax_planning_sheet.py: tax planning sheet generator.
  • skills/kanchi-dividend-us-tax-accounting/scripts/tests/test_build_tax_planning_sheet.py: tests for tax planning outputs.
  • references/qualified-dividend-checklist.md: classification and holding-period checks.
  • references/account-location-matrix.md: placement matrix by account type and instrument.
  • references/annual-tax-memo-template.md: reusable memo structure.

附带文件

agents/openai.yaml
interface:
  display_name: "Kanchi Dividend Us Tax Accounting"
  short_description: "Help with Kanchi Dividend Us Tax Accounting tasks"
references/account-location-matrix.md
# Account Location Matrix

Use this matrix to propose placement between taxable and tax-advantaged accounts.

## Baseline Placement Logic

| Instrument profile | Taxable account | Tax-advantaged account | Rationale |
|---|---|---|---|
| Qualified-dividend-heavy US equity | Usually preferred | Optional | Potentially better ongoing tax efficiency in taxable account |
| REIT-heavy income holdings | Less preferred | Often preferred | Distribution may include higher ordinary-income components |
| BDC/high-distribution structures | Less preferred | Often preferred | Tax treatment can be less favorable in taxable account |
| MLP (partnership units) | Case-by-case | Caution in tax-advantaged accounts | UBTI and K-1 complexity can make placement non-trivial |
| Broad index ETF with low turnover | Often acceptable | Also acceptable | Depends on overall asset-location design |

If MLP is outside mandate, mark it explicitly as `OUT-OF-SCOPE` and exclude from default allocation logic.

## Conflict Resolution Rules

When account-location recommendation conflicts with other needs:
1. Respect concentration and risk controls first.
2. Respect liquidity/withdrawal constraints second.
3. Optimize taxes third.

## Output Format

Return one line per holding:

```text
[Ticker] -> [Recommended Account] | Why: [one sentence]
```
references/annual-tax-memo-template.md
# Annual Dividend Tax Planning Memo Template

```markdown
# Annual Dividend Tax Planning Memo ([Year])

## Filing Timeline
- Tax year:
- Filing deadline:
- Extension status (filed/planned/not needed):
- Expected filing date:

## Scope
- Accounts included:
- Holdings covered:
- Data sources used:

## Assumptions
- Rule set version/date:
- Missing data assumptions:

## Distribution Classification Summary
| Ticker | Account | Ordinary (est.) | Qualified (est.) | REIT/other components | Confidence |
|---|---|---:|---:|---|---|
| ... | ... | ... | ... | ... | High/Med/Low |

## Account-Location Actions
| Ticker | Current location | Proposed location | Reason |
|---|---|---|---|
| ... | ... | ... | ... |

## Open Risks / Follow-Ups
1. ...
2. ...
3. ...

## Advisor Questions
1. ...
2. ...
```

Keep the memo concise and assumption-driven.
references/qualified-dividend-checklist.md
# Qualified Dividend Checklist (US)

Use this checklist for planning assumptions.

## Classification Pass

For each holding, verify:
1. Distribution is potentially eligible for qualified treatment by instrument/type.
2. Shares meet required holding-period test around the ex-dividend date window.
3. No known disqualifying condition in the current fact pattern.

If any item is uncertain, mark as `ASSUMPTION-REQUIRED`.

## Holding-Period Rules (US Federal, Common Planning Baseline)

- Common stock baseline: hold shares for **more than 60 days** during the **121-day period** that starts **60 days before** the ex-dividend date.
- Preferred stock (certain long-period dividends): often uses **more than 90 days** during a **181-day period** starting **90 days before** ex-dividend date.

Use current IRS guidance as source of truth:
- IRS Publication 550.
- IRS Form 1099-DIV instructions.

## Practical Data Fields

Track these fields for each position:
- Ticker
- Account type
- Ex-dividend date
- Purchase date(s)
- Disposal date(s), if any
- Days held in required window
- Preliminary classification (`qualified-likely`, `ordinary-likely`, `unknown`)

## Common Pitfalls

- Assuming all common-stock dividends will be qualified without holding-period verification.
- Ignoring short holding periods caused by frequent tactical trading.
- Treating REIT/BDC distributions as identical to standard qualified-dividend flows.
- **Modeling special / variable dividends as steady qualified income**
  (WS-8 hand-off from `kanchi-dividend-sop`). When the upstream
  `dividend_basis` carries `special_dividend_flag` or
  `variable_policy_flag` (e.g. ORI annual specials, CALM variable policy),
  treat that cash as **lumpy and non-recurring** for account-location and
  cash-flow planning: budget the *regular* run-rate as base income and the
  special/variable component separately (timing unpredictable; may still
  be qualified but must not be annualized as if recurring).

## Recommended Source Hierarchy

1. Broker tax documents and distribution breakdowns.
2. Official IRS publications/instructions for current-year rules (Publication 550, 1099-DIV instructions).
3. Issuer or fund notices when classification is revised.
scripts/build_tax_planning_sheet.py
#!/usr/bin/env python3
"""Generate a deterministic US dividend tax planning sheet."""

from __future__ import annotations

import argparse
import csv
import json
from dataclasses import dataclass
from datetime import date
from pathlib import Path
from typing import Any


@dataclass
class PlanningRow:
    ticker: str
    instrument_type: str
    account_type: str
    hold_days_in_window: int | None
    classification: str
    location_hint: str
    note: str


def required_days(security_type: str | None) -> int:
    if str(security_type or "").strip().lower() == "preferred":
        return 91
    return 61


def classify_holding(holding: dict[str, Any]) -> PlanningRow:
    ticker = str(holding.get("ticker", "")).strip().upper() or "UNKNOWN"
    instrument_type = str(holding.get("instrument_type", "stock")).strip().lower()
    account_type = str(holding.get("account_type", "unknown")).strip().lower()
    security_type = str(holding.get("security_type", "common")).strip().lower()

    hold_days_raw = holding.get("hold_days_in_window")
    hold_days: int | None
    if hold_days_raw is None:
        hold_days = None
    else:
        hold_days = int(hold_days_raw)

    if instrument_type in {"reit", "bdc"}:
        return PlanningRow(
            ticker=ticker,
            instrument_type=instrument_type,
            account_type=account_type,
            hold_days_in_window=hold_days,
            classification="ordinary_likely",
            location_hint="tax_advantaged_preferred",
            note="distribution may include ordinary-income style components",
        )

    if instrument_type == "mlp":
        return PlanningRow(
            ticker=ticker,
            instrument_type=instrument_type,
            account_type=account_type,
            hold_days_in_window=hold_days,
            classification="out_of_scope_mlp",
            location_hint="case_by_case",
            note="check K-1 and UBTI implications before placement",
        )

    if hold_days is None:
        return PlanningRow(
            ticker=ticker,
            instrument_type=instrument_type,
            account_type=account_type,
            hold_days_in_window=hold_days,
            classification="assumption_required",
            location_hint="taxable_preferred",
            note="hold_days_in_window missing; cannot verify qualified treatment",
        )

    threshold = required_days(security_type)
    if hold_days >= threshold:
        classification = "qualified_likely"
        note = f"hold_days_in_window >= {threshold}"
    else:
        classification = "ordinary_likely"
        note = f"hold_days_in_window < {threshold}"

    return PlanningRow(
        ticker=ticker,
        instrument_type=instrument_type,
        account_type=account_type,
        hold_days_in_window=hold_days,
        classification=classification,
        location_hint="taxable_preferred",
        note=note,
    )


def render_markdown(rows: list[PlanningRow], as_of: str) -> str:
    lines = [
        "# US Dividend Tax Planning Sheet",
        "",
        f"- as_of: `{as_of}`",
        f"- holding_count: `{len(rows)}`",
        "",
        "| Ticker | Instrument | Account | Hold Days | Classification | Location Hint | Note |",
        "|---|---|---|---:|---|---|---|",
    ]
    for row in rows:
        hold_days = "" if row.hold_days_in_window is None else str(row.hold_days_in_window)
        lines.append(
            f"| {row.ticker} | {row.instrument_type} | {row.account_type} | {hold_days} | "
            f"{row.classification} | {row.location_hint} | {row.note} |"
        )

    lines.extend(
        [
            "",
            "## Open Items",
            "",
            "- Confirm final classification against broker 1099-DIV and IRS current-year guidance.",
            "- Escalate unresolved assumptions to CPA/tax advisor.",
            "",
        ]
    )
    return "\n".join(lines)


def write_csv(path: Path, rows: list[PlanningRow]) -> None:
    with path.open("w", encoding="utf-8", newline="") as f:
        writer = csv.writer(f)
        writer.writerow(
            [
                "ticker",
                "instrument_type",
                "account_type",
                "hold_days_in_window",
                "classification",
                "location_hint",
                "note",
            ]
        )
        for row in rows:
            writer.writerow(
                [
                    row.ticker,
                    row.instrument_type,
                    row.account_type,
                    row.hold_days_in_window if row.hold_days_in_window is not None else "",
                    row.classification,
                    row.location_hint,
                    row.note,
                ]
            )


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(description="Build US dividend tax planning artifacts.")
    parser.add_argument("--input", required=True, help="Path to JSON input with holdings list.")
    parser.add_argument("--output-dir", default="reports", help="Output directory.")
    parser.add_argument("--as-of", default=date.today().isoformat(), help="As-of date.")
    return parser.parse_args()


def main() -> int:
    args = parse_args()
    payload = json.loads(Path(args.input).read_text())
    holdings = payload.get("holdings", [])
    if not isinstance(holdings, list) or not holdings:
        raise SystemExit("Input JSON must include a non-empty holdings list.")

    rows = [classify_holding(item) for item in holdings]
    output_dir = Path(args.output_dir)
    output_dir.mkdir(parents=True, exist_ok=True)

    markdown_path = output_dir / f"tax_planning_sheet_{args.as_of}.md"
    csv_path = output_dir / f"tax_planning_sheet_{args.as_of}.csv"
    markdown_path.write_text(render_markdown(rows, args.as_of) + "\n", encoding="utf-8")
    write_csv(csv_path, rows)

    print(f"Wrote markdown: {markdown_path}")
    print(f"Wrote csv: {csv_path}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
scripts/tests/conftest.py
"""Shared fixtures for Kanchi US tax accounting script tests."""

import os
import sys

sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
scripts/tests/test_build_tax_planning_sheet.py
"""Tests for build_tax_planning_sheet.py."""

import csv
from pathlib import Path

from build_tax_planning_sheet import classify_holding, render_markdown, write_csv


def test_classify_stock_with_sufficient_days_is_qualified_likely() -> None:
    row = classify_holding(
        {
            "ticker": "jnj",
            "instrument_type": "stock",
            "account_type": "taxable",
            "hold_days_in_window": 75,
            "security_type": "common",
        }
    )
    assert row.classification == "qualified_likely"


def test_classify_missing_days_is_assumption_required() -> None:
    row = classify_holding(
        {
            "ticker": "pg",
            "instrument_type": "stock",
            "account_type": "taxable",
        }
    )
    assert row.classification == "assumption_required"


def test_classify_mlp_is_out_of_scope() -> None:
    row = classify_holding(
        {
            "ticker": "et",
            "instrument_type": "mlp",
            "account_type": "ira",
            "hold_days_in_window": 200,
        }
    )
    assert row.classification == "out_of_scope_mlp"
    assert row.location_hint == "case_by_case"


def test_markdown_and_csv_generation(tmp_path: Path) -> None:
    rows = [
        classify_holding(
            {
                "ticker": "o",
                "instrument_type": "reit",
                "account_type": "ira",
                "hold_days_in_window": 100,
            }
        ),
        classify_holding(
            {
                "ticker": "jnj",
                "instrument_type": "stock",
                "account_type": "taxable",
                "hold_days_in_window": 80,
            }
        ),
    ]
    markdown = render_markdown(rows, "2026-02-22")
    assert "# US Dividend Tax Planning Sheet" in markdown
    assert "| O | reit | ira | 100 | ordinary_likely |" in markdown

    csv_path = tmp_path / "sheet.csv"
    write_csv(csv_path, rows)
    with csv_path.open() as f:
        reader = list(csv.reader(f))
    assert reader[0][0] == "ticker"
    assert reader[1][0] == "O"
    assert reader[2][0] == "JNJ"