Skill: x-rayluan/openclaw-equity-research (version 0.1.4)

--- name: openclaw-equity-research description: Use when Codex needs to create, review, or update equity research work products such as earnings previews, post-earnings notes, model updates, initiating coverage, sector overviews, catalyst calendars, thesis trackers, idea generation screens, valuation memos, investor briefs, or public-company research that requires filings, transcripts, market data, spreadsheet work, citations, and analyst-review guardrails. --- # OpenClaw Equity Research ## Operating Rule Draft analyst work product for human review. Do not present output as investment, legal, tax, accounting, or trading advice. Do not claim a buy/sell/hold recommendation unless the user explicitly asks for a draft recommendation and the assumptions, valuation, risks, and sources support it. ## First Pass 1. Identify the deliverable type and audience: earnings note, preview, model update, initiation, sector overview, catalyst calendar, thesis tracker, idea screen, or one-off memo. 2. Confirm the ticker/company, exchange, coverage universe, geography, currency, date range, and output format if missing. 3. Use fresh sources for public-company facts, estimates, prices, filings, calendar events, executives, market data, and news. Browse when the answer depends on current information. 4. Prefer primary sources: SEC/company filings, investor relations releases, earnings call transcripts, presentations, exchange notices, and company websites. Use reputable secondary sources only to triangulate or fill gaps. 5. Separate fact, calculation, inference, and opinion. Label uncertainty and stale data. 6. Produce a concise decision-ready artifact with citations, assumptions, and next checks. ## Workflow Selection - **Earnings analysis**: read release, presentation, transcript, and filing; compare actuals to consensus or prior guide; isolate beats/misses, guide changes, margin drivers, cash flow, balance sheet, segment trends, and management tone. - **Earnings preview**: map consensus, guidance, buyside debate, expected KPIs, risks, and likely stock-moving questions before the print. - **Model update**: update actuals, guidance, forecast assumptions, valuation outputs, and sensitivity tables. Use spreadsheet tools for `.xlsx` work. - **Initiating coverage**: build company profile, industry structure, competitive position, financial history, forecast drivers, valuation, risks, catalysts, and final note/deck. - **Morning note**: summarize overnight news, earnings, rating changes, macro/sector moves, and actionable implications. - **Sector overview**: define market map, demand/supply drivers, unit economics, regulation, competitive landscape, valuation dispersion, and stock implications. - **Thesis tracker**: maintain thesis, evidence, catalysts, counterevidence, kill criteria, conviction, and unresolved questions. - **Catalyst calendar**: list dated events, expected impact, source, confidence, and prep needed. - **Idea generation**: screen for dislocations, estimate revisions, quality, momentum, valuation gaps, catalysts, short-interest, ownership changes, or variant perception. For detailed checklists, read `references/workflows.md`. For quality gates and output patterns, read `references/research-standard.md`. ## Deliverable Standard Every research output should include: - **Snapshot**: company/ticker, date, source freshness, base currency, market data timestamp if used. - **Key takeaways**: 3-5 bullets that answer what changed, why it matters, and what to watch. - **Evidence table**: metric/event, source, reported value, comparison point, implication. - **Model/valuation assumptions**: explicit drivers, ranges, sensitivities, and what would change the conclusion. - **Risks and counterarguments**: strongest opposing case, missing data, and near-term failure modes. - **Source list**: links or file names for every material claim. - **Human review note**: checks needed before external distribution. ## Tooling - Use web search/browsing for current filings, releases, transcripts, market data, calendars, and news. - Use `spreadsheets` when creating or editing financial models, forecast tables, comps, sensitivities, or `.xlsx` files. - Use `documents` when producing professional `.docx` notes or redlines. - Use `presentations` when producing an investment committee deck, company overview, or sector deck. - If a user provides local files, inspect them first and preserve source provenance in the final artifact. ## Guardrails - Do not invent consensus estimates, market prices, valuation multiples, or filing data. - Do not use a single unsourced chart or article as the basis for a financial conclusion. - Do not hide assumptions inside prose; put them in a table or bullet list. - Do not mix currencies, fiscal years, adjusted/non-GAAP metrics, or segment definitions without labeling them. - Do not externalize or post research without explicit user approval.

path: README-clawhub.md
content: # OpenClaw Equity Research

> Analyst-grade public-equity research workflows for OpenClaw: earnings previews, post-earnings notes, model update summaries, initiating coverage drafts, sector overviews, catalyst calendars, thesis trackers, idea screens, and ticker memos.

[Feature Matrix](#feature-matrix) · [Quick Start](#quick-start) · [Example Output](#example-output) · [Disclaimer](#disclaimer)

---

## Feature Matrix

| Capability | Coverage |
| --- | --- |
| Earnings preview | Consensus setup, prior guidance, buyside debate, key KPIs, stock-moving questions |
| Post-earnings analysis | Actuals versus expectations, guide changes, margin drivers, segments, management tone |
| Model update notes | Forecast changes, valuation assumptions, sensitivity updates |
| Initiating coverage | Company profile, industry map, financial drivers, valuation, risks, catalysts |
| Sector overview | Value chain, demand/supply drivers, peer comparison, valuation dispersion |
| Catalyst calendar | Dated events, expected impact, source, confidence, prep notes |
| Thesis tracker | Thesis, evidence, counterevidence, confidence, kill criteria |
| Idea generation | Dislocations, revisions, quality/valuation gaps, catalysts, ownership changes |

---

## Quick Start

Use it in OpenClaw:

```text
Use OpenClaw Equity Research to write an earnings preview for NVDA.
```

```text
Prepare a post-earnings update for TSLA with evidence, model changes, risks, and review notes.
```

```text
Build a catalyst calendar for AMD and NVDA for the next two quarters.
```

---

## Example Output

The skill pushes OpenClaw to produce:

- **Snapshot**: company, ticker, date, source freshness, currency, market data timestamp
- **Key takeaways**: what changed, why it matters, what to watch
- **Evidence table**: metric/event, source, reported value, comparison point, implication
- **Model and valuation assumptions**: drivers, ranges, sensitivities
- **Risks and counterarguments**: strongest opposing case and missing diligence
- **Source list**: links or local files for material claims
- **Human review note**: checks needed before external use

---

## Source And Tooling Coverage

| Type | Preferred Sources or Tools |
| --- | --- |
| Primary company evidence | Filings, investor relations releases, presentations, transcripts, official websites |
| Market and calendar facts | Fresh web/data-provider checks, exchange or regulator sources |
| Research context | Reputable financial press, analyst summaries, user notes |
| Spreadsheet work | Forecast tables, comps, sensitivities, `.xlsx` files |
| Final artifacts | Research notes, redlines, decks, IC materials |

---

## Included Files

- `SKILL.md`
- `scripts/equity_research.py`
- `references/workflows.md`
- `references/research-standard.md`
- `references/research-framework.md`
- `references/data-sources.md`
- `references/report-rubric.md`

---

## Disclaimer

This is research assistance, not financial advice. It helps draft and organize analyst work product for review; it does not replace due diligence, compliance review, licensed market data, or professional investment judgment.


path: README.md
content: # OpenClaw Equity Research [![GitHub stars](https://img.shields.io/github/stars/X-RayLuan/openclaw-equity-research?style=social)](https://github.com/X-RayLuan/openclaw-equity-research/stargazers) [![OpenClaw Skill](https://img.shields.io/badge/OpenClaw-Skill-111827)](https://github.com/X-RayLuan/openclaw-equity-research) [![ClawHub](https://img.shields.io/badge/ClawHub-openclaw--equity--research-2563EB)](https://github.com/X-RayLuan/openclaw-equity-research)

> Analyst-grade public-equity research workflows for OpenClaw: earnings previews, post-earnings notes, model update summaries, initiating coverage drafts, sector overviews, catalyst calendars, thesis trackers, idea screens, and ticker memos.

[Feature Matrix](#feature-matrix) · [Research Workflow](#research-workflow) · [Quick Start](#quick-start) · [Example Output](#example-output) · [Files](#files) · [Disclaimer](#disclaimer)

---

## Product Preview

OpenClaw Equity Research is a skillpack that gives OpenClaw a structured analyst workflow instead of a generic stock prompt.

It helps the agent:

- choose the right research workflow for the request
- gather fresh primary sources when facts are current or market-sensitive
- separate facts, calculations, assumptions, inference, and judgment
- produce notes with evidence tables, risks, counterarguments, and review gates
- route spreadsheet, document, presentation, and web-research work to the right tools

The skill is built for research assistance and analyst workflow automation. It is not financial, legal, tax, accounting, or trading advice.

---

## Feature Matrix

| Capability | Coverage |
| --- | --- |
| Earnings preview | Consensus setup, prior guidance, buyside debate, key KPIs, likely stock-moving questions, bull/base/bear print scenarios |
| Post-earnings analysis | Release, presentation, transcript, filing, actuals versus expectations, guide changes, margin drivers, segment trends, management tone |
| Model update notes | Actuals import, forecast driver changes, valuation and sensitivity updates, changed-assumption log |
| Initiating coverage | Investment summary, business model, industry map, competitive position, financial drivers, valuation, risks, catalysts |
| Sector overview | Value chain, demand/supply drivers, regulation, technology, pricing, peer comparison, valuation dispersion |
| Catalyst calendar | Dated events, expected impact, source, confidence, prep notes |
| Thesis tracker | Thesis, evidence, counterevidence, catalyst, confidence, kill criteria, next action |
| Idea generation | Dislocations, estimate revisions, quality versus valuation, margin inflection, refinancing risk, short interest, ownership changes |

### Source and Tooling Coverage

| Type | Preferred Sources or Tools |
| --- | --- |
| Primary company evidence | SEC/company filings, investor relations releases, earnings presentations, transcripts, official company websites |
| Market and calendar facts | Fresh web/data-provider checks, exchange or regulator sources, user-provided vendor exports |
| Research context | Reputable financial press, analyst summaries, user notes, company presentations |
| Spreadsheet work | Forecast tables, comps, sensitivities, model update logs, `.xlsx` files |
| Document output | Professional notes, redlines, `.docx` artifacts |
| Presentation output | Company overview decks, sector decks, investment committee materials |

---

## Quick Start

### Option 1: Use in OpenClaw Webchat

Install or sync the skill into OpenClaw's active workspace skills path:

```text
/Users/m1/.openclaw/workspace/skills/openclaw-equity-research/
```

Then ask:

```text
Use OpenClaw Equity Research to write an earnings preview for NVDA.
```

```text
Prepare a post-earnings update for TSLA with reported KPIs, guidance changes, model implications, and risks.
```

```text
Build a catalyst calendar for AMD and NVDA for the next two quarters.
```

```text
Draft an initiating coverage outline for RKLB with valuation framework and thesis kill criteria.
```

```text
Compare PLTR and SNOW as an idea-generation screen with evidence, counterarguments, and next diligence.
```

### Option 2: Use the Lightweight Script

From this skill directory:

```bash
python3 scripts/equity_research.py AAPL --out reports
python3 scripts/equity_research.py TSLA NVDA RKLB --mode watchlist --out reports
python3 scripts/equity_research.py --template AAPL --out reports
```

The script writes:

- `{ticker}-equity-research.md`
- `{ticker}-equity-research.json`
- `watchlist-equity-research.md`

The script is intentionally lightweight. It is a fast starting point; the skill instructions tell OpenClaw to add fresh sourcing, assumptions, judgment, and human-review gates when the user needs a research-quality output.

---

## Research Workflow

### 1. Classify the deliverable

OpenClaw first identifies whether the task is an earnings note, preview, model update, initiation, sector overview, catalyst calendar, thesis tracker, idea screen, or one-off memo.

### 2. Gather current evidence

For current company facts, market prices, estimates, filings, executives, calendar events, and news, OpenClaw should browse or use connected data sources. It should prefer primary sources and disclose stale or missing data.

### 3. Build the research artifact

The default research artifact includes:

- **Snapshot**: company, ticker, date, source freshness, currency, market data timestamp
- **Key takeaways**: what changed, why it matters, what to watch
- **Evidence table**: metric/event, source, reported value, comparison point, implication
- **Model and valuation assumptions**: drivers, ranges, sensitivities, conclusion-changing inputs
- **Risks and counterarguments**: strongest opposing case and missing diligence
- **Source list**: links or local files for material claims
- **Human review note**: checks needed before external use

### 4. Review before external use

The skill explicitly blocks unsupported recommendation language, invented market data, hidden valuation assumptions, mixed currencies or fiscal periods, and external publishing without user approval.

---

## Example Output

```markdown
# NVDA Earnings Preview

## Snapshot
- Company / ticker:
- Event date:
- Source freshness:
- Currency:
- Market data timestamp:

## Key Takeaways
- ...

## Evidence Table
| Metric / event | Source | Reported value | Comparison point | Implication |
| --- | --- | --- | --- | --- |

## Debate Into The Print
- Bull case:
- Base case:
- Bear case:

## Model And Valuation Assumptions
- Revenue driver:
- Margin driver:
- Capex / cash flow:
- Sensitivity:

## Risks And Counterarguments
- ...

## What To Watch Next
- ...

## Human Review Notes
- Verify source timestamps and consensus inputs before external distribution.
```

---

## Configuration Notes

This skill does not require a fixed data vendor. Quality depends on the sources available in the active OpenClaw session.

Recommended setup:

| Need | Recommended Setup |
| --- | --- |
| Fresh filings and releases | Web access or SEC/company IR links |
| Earnings transcripts | User-provided transcript, official IR transcript, or trusted transcript source |
| Consensus and estimates | User-provided model/vendor export when available |
| Market data | Connected data provider or fresh web check with timestamp |
| Model update work | Spreadsheet-capable environment and source model |
| Final memo or deck | Document or presentation tool access |

---

## Files

| File | Purpose |
| --- | --- |
| `SKILL.md` | Agent-facing routing, output contract, tool guidance, and guardrails |
| `references/workflows.md` | Detailed workflow checklists |
| `references/research-standard.md` | Source, evidence, calculation, writing, and review standards |
| `references/research-framework.md` | Original memo framework |
| `references/data-sources.md` | Data-provider guidance and caveats |
| `references/report-rubric.md` | Quality rubric for review-ready output |
| `scripts/equity_research.py` | Lightweight ticker memo generator |
| `agents/openai.yaml` | OpenAI/OpenClaw UI metadata |

---

## Related Workflows

This skill pairs well with:

| Workflow | How it helps |
| --- | --- |
| OpenClaw webchat | Interactive research, follow-up questions, and artifact drafting |
| Spreadsheet workflows | Forecast updates, comps, valuation tables, sensitivities |
| Document workflows | Professional research notes and redlined drafts |
| Presentation workflows | Company overview, sector overview, or IC-style decks |

---

## Disclaimer

This project is for research assistance and workflow automation only. It does not provide investment advice, trading instructions, legal advice, tax advice, accounting advice, compliance approval, or audited financial models. Users are responsible for verifying all data, assumptions, citations, and conclusions before making decisions or sharing outputs externally.


path: agents/openai.yaml
content: display_name: OpenClaw Equity Research
short_description: Equity research workflows for analyst notes.
default_prompt: Use OpenClaw Equity Research to prepare an earnings update for a public company with cited sources, assumptions, risks, and human-review notes.


path: references/data-sources.md
content: # Data Sources

## Preferred Source Order
Use the freshest reliable source available for the user's task.

### Market Data
- OpenBB Platform when installed and configured.
- yfinance for lightweight public price/profile/news data.
- Exchange or broker data if the user provides access.
- Web search for quotes only if API data is unavailable, and cite timestamp.

### Fundamentals
- SEC filings: 10-K, 10-Q, 8-K for US companies.
- Company investor relations: earnings releases, presentations, transcripts.
- OpenBB/yfinance public fundamentals for fast first pass.
- Paid sources only if the user has configured access.

### News And Catalysts
- Company press releases and IR calendar.
- Reputable financial news.
- Exchange filings and regulatory notices.
- Avoid unsourced social rumors unless the user asks for sentiment monitoring; label them as unverified.

### Estimates And Analyst Data
- Use only if sourced. Do not invent consensus estimates.
- If unavailable, write "not available in current data pull" and use scenario ranges instead.

## Provider Caveats
- yfinance fields can be incomplete, delayed, or inconsistent across tickers.
- OpenBB provider availability depends on local installation and provider credentials.
- News APIs may return headlines without full text.
- Current price data can be delayed; include timestamp.

## Browsing Rule
For current market price, news, legal/regulatory issues, earnings dates, analyst actions, or anything likely to have changed recently, browse or use an API unless the user explicitly says not to.



path: references/report-rubric.md
content: # Equity Research Report Rubric

## Pass Criteria
- Clear ticker, company, market, and timestamp.
- Thesis is understandable without reading the whole memo.
- Evidence table includes sources or data provenance.
- At least one bull case, base case, and bear case.
- Risks are specific and tied to the thesis.
- Monitoring points are observable.
- Research limits and stale/missing data are disclosed.
- No personalized financial advice.

## Strong Report Criteria
- Identifies variant perception: what the market may be missing.
- Separates short-term setup from long-term fundamentals.
- Includes both quantitative and qualitative evidence.
- Uses valuation assumptions instead of unsupported target prices.
- Highlights what would change the analyst's mind.
- Can be handed to an analyst or PM as a first-pass research packet.

## Common Failures
- Treating technical indicators as a full investment thesis.
- Giving BUY/SELL labels without risk and falsification.
- Using stale news as if current.
- Mixing company facts with model guesses.
- Hiding data gaps.
- Over-precise targets from weak inputs.


path: references/research-framework.md
content: # OpenClaw Equity Research Framework

## Source Patterns
This skill borrows two architecture patterns:

1. OpenBB-style platform base:
   - Treat market data, fundamentals, filings, news, and analyst data as provider modules.
   - Produce artifacts that can feed a terminal, workspace, dashboard, MCP server, or agent.
   - Keep data collection separate from synthesis.

2. Agentic stock research workflow:
   - Stock finder or triage agent identifies why a ticker matters.
   - Market data agent gathers price, volume, trend, technicals.
   - News analyst agent gathers catalysts, sentiment, calendar events.
   - Recommendation/synthesis agent turns evidence into a research view with risks.

## Stage Contract
Each stage should return facts plus interpretation, not free-form unsupported opinion.

### 1. Triage
Answer:
- Why is this ticker being researched now?
- Is it liquid enough for the user's likely use case?
- What is the main controversy or variant perception?

### 2. Market Data
Minimum:
- latest close or current price with timestamp
- 1 day, 1 month, 3 month, and 6 month return when available
- volume vs recent average
- 20/50/200 day moving averages
- RSI or momentum regime
- obvious support/resistance if a trading note is requested

### 3. Business/Fundamentals
Minimum:
- business description
- segment or revenue drivers when available
- market cap and enterprise value if available
- revenue growth, margins, cash generation, balance sheet risk when available
- peer group if the user asks for valuation

### 4. News/Catalysts
Minimum:
- recent headlines with dates
- earnings date or known event calendar when available
- product/regulatory/macro catalysts
- classify each as positive, negative, neutral, or uncertain

### 5. Valuation Frame
Use one or more:
- multiples: P/E, EV/Sales, EV/EBITDA, P/S, FCF yield
- scenario: base, bull, bear assumptions
- sum-of-the-parts for multi-segment companies
- DCF only when assumptions are explicit

Never give a precise target without showing assumptions.

### 6. Synthesis
Use:
- `Research View: Bullish`, `Neutral`, or `Bearish`
- `Confidence: Low`, `Medium`, or `High`
- `Time Horizon`
- monitoring checklist
- falsification triggers

Avoid personalized language like "you should buy." Prefer "A bullish case depends on..." or "The evidence supports a neutral research view until..."

## Quality Bar
A useful memo should:
- make the thesis clear in the first 10 lines
- include evidence that could change the conclusion
- separate near-term trading setup from long-term investment quality
- name data limitations
- be concise enough for a portfolio manager to scan


path: references/research-standard.md
content: # Research Standard

## Source Hierarchy

Prefer:
1. Company filings, investor relations releases, presentations, transcripts, and official websites.
2. Exchange/regulator sources.
3. Data vendors or user-provided models/files.
4. Reputable financial press and analyst summaries for context only.

Use secondary sources to triangulate, not to replace primary evidence.

## Evidence Rules

- Cite every material claim about revenue, margins, guidance, market size, price reaction, estimates, management changes, litigation, regulation, and capital allocation.
- Include source date and publication timestamp when timing matters.
- Flag when consensus, price, or estimates are missing, stale, or user-provided.
- Keep quoted text short and only when exact wording matters.

## Calculation Rules

- Show formula or driver logic for non-trivial calculations.
- State fiscal calendar, currency, units, and adjusted/GAAP basis.
- Reconcile period comparisons: QoQ, YoY, sequential, trailing twelve months, or fiscal year.
- Avoid false precision. Use ranges when source data is uncertain.

## Writing Standard

Use the analyst note pattern:
- "What happened"
- "Why it matters"
- "What changed in the model"
- "What the market may debate"
- "What to watch next"

Keep claims falsifiable. Prefer concrete drivers over generic adjectives.

## Review Gate

Before finalizing, check:
- All tickers, company names, dates, currencies, and fiscal periods are consistent.
- No unsupported recommendation language slipped in.
- No stale market data is presented as current.
- Risk/counterargument section is real, not boilerplate.
- Final artifact says it is a draft for review when used outside internal analysis.


path: references/workflows.md
content: # Equity Research Workflows

## Earnings Analysis

Required inputs:
- Press release, investor presentation, prepared remarks/transcript, 10-Q/10-K or 6-K/20-F when applicable.
- Prior guidance, current guidance, consensus or user-provided estimates, prior model if available.
- Price reaction and timestamp only if sourced fresh.

Output structure:
- Headline: what changed versus expectations.
- KPI table: actual, consensus/prior guide, prior period, delta, implication.
- Guidance bridge: old guide, new guide, consensus, key drivers.
- Segment commentary: growth, margin, backlog, bookings, churn, ARPU, unit economics, or sector-specific KPIs.
- Management tone: quote sparingly; summarize themes and repeated signals.
- Model actions: exact forecast lines to update and why.

## Earnings Preview

Build:
- Setup: stock move, valuation, estimate revisions, positioning, sentiment.
- Debate: 3-5 buyside questions likely to drive reaction.
- Watchlist: KPIs, guideposts, margin items, capital allocation, product/regulatory updates.
- Scenario table: bull/base/bear print and likely debate after each.

## Model Update

Checklist:
- Roll forward periods and import actuals.
- Tie reported revenue, gross margin, EBIT/operating income, EPS, cash flow, debt, and share count.
- Reconcile adjusted metrics to GAAP where possible.
- Update drivers before outputs; avoid hardcoding plugs.
- Rebuild valuation and sensitivity tables after forecasts change.
- Document every changed assumption.

## Initiating Coverage

Minimum sections:
- Investment summary and variant perception.
- Company and business model.
- Market size, growth, cyclicality, regulation, and competitive map.
- Financial history and forecast drivers.
- Valuation framework with at least one primary method and one cross-check.
- Catalysts, risks, and what would change the thesis.
- Appendix with source list and model assumptions.

## Sector Overview

Cover:
- Value chain and profit pools.
- Demand, supply, regulation, technology, and pricing drivers.
- Peer set with revenue mix, margin profile, growth, leverage, and valuation.
- Winners/losers under key scenarios.
- Open questions requiring further diligence.

## Thesis Tracker

Maintain rows for:
- Thesis claim.
- Evidence for and against.
- Catalyst or monitor.
- Source and date.
- Confidence.
- Kill criterion.
- Next action.

## Catalyst Calendar

Required columns:
- Date or expected window.
- Company/ticker.
- Event.
- Why it matters.
- Expected metric/question.
- Source.
- Confidence.
- Prep owner or next step.

## Idea Generation

Useful screens:
- Estimate revision divergence.
- Valuation versus growth/quality.
- Margin mean reversion.
- Free cash flow inflection.
- Balance sheet stress or refinancing wall.
- Short interest plus catalyst.
- Insider/ownership change.
- Product cycle, regulation, or litigation event.

Always convert screens into a shortlist with testable theses, not a pile of tickers.


path: scripts/equity_research.py
content: #!/usr/bin/env python3
from __future__ import annotations

import argparse
import json
import math
from dataclasses import dataclass, asdict
from datetime import datetime, timezone
from pathlib import Path
from typing import Any


DISCLAIMER = (
    "This is research assistance, not financial advice. Verify data and consult a qualified "
    "professional before making investment decisions."
)


@dataclass
class PriceSnapshot:
    ticker: str
    generated_at: str
    company_name: str | None
    sector: str | None
    industry: str | None
    currency: str | None
    market_cap: float | None
    last_close: float | None
    previous_close: float | None
    day_change_pct: float | None
    return_1m_pct: float | None
    return_3m_pct: float | None
    return_6m_pct: float | None
    avg_volume_20d: float | None
    latest_volume: float | None
    sma20: float | None
    sma50: float | None
    sma200: float | None
    rsi14: float | None
    technical_view: str
    data_warnings: list[str]


def now_utc() -> str:
    return datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC")


def safe_float(value: Any) -> float | None:
    try:
        if value is None:
            return None
        out = float(value)
        if math.isnan(out) or math.isinf(out):
            return None
        return out
    except Exception:
        return None


def pct_change(new: float | None, old: float | None) -> float | None:
    if new is None or old in (None, 0):
        return None
    return (new - old) / old * 100


def fmt(value: Any, digits: int = 2) -> str:
    num = safe_float(value)
    if num is None:
        return "n/a"
    return f"{num:,.{digits}f}"


def fmt_pct(value: Any) -> str:
    num = safe_float(value)
    if num is None:
        return "n/a"
    return f"{num:+.2f}%"


def simple_rsi(closes: list[float], period: int = 14) -> float | None:
    if len(closes) <= period:
        return None
    gains: list[float] = []
    losses: list[float] = []
    for prev, cur in zip(closes[-period - 1 : -1], closes[-period:]):
        delta = cur - prev
        gains.append(max(delta, 0))
        losses.append(abs(min(delta, 0)))
    avg_gain = sum(gains) / period
    avg_loss = sum(losses) / period
    if avg_loss == 0:
        return 100.0
    rs = avg_gain / avg_loss
    return 100 - (100 / (1 + rs))


def moving_average(values: list[float], window: int) -> float | None:
    if len(values) < window:
        return None
    return sum(values[-window:]) / window


def technical_view(last: float | None, sma20: float | None, sma50: float | None, sma200: float | None, rsi: float | None) -> str:
    score = 0
    if last is not None and sma20 is not None and last > sma20:
        score += 1
    if sma20 is not None and sma50 is not None and sma20 > sma50:
        score += 1
    if sma50 is not None and sma200 is not None and sma50 > sma200:
        score += 1
    if rsi is not None and 45 <= rsi <= 70:
        score += 1
    if rsi is not None and rsi > 75:
        score -= 1
    if score >= 3:
        return "Constructive"
    if score <= 1:
        return "Weak or mixed"
    return "Neutral"


def load_yfinance_snapshot(ticker: str) -> tuple[PriceSnapshot, list[dict[str, str]]]:
    warnings: list[str] = []
    try:
        import yfinance as yf  # type: ignore
    except Exception as exc:
        raise SystemExit(f"yfinance is required for live mode: {exc}")

    obj = yf.Ticker(ticker)
    try:
        hist = obj.history(period="1y", interval="1d", auto_adjust=False).reset_index()
    except Exception as exc:
        raise SystemExit(f"failed to fetch history for {ticker}: {exc}")

    if hist.empty:
        raise SystemExit(f"no price history returned for {ticker}")

    info: dict[str, Any] = {}
    try:
        info = obj.get_info() or {}
    except Exception as exc:
        warnings.append(f"company profile unavailable: {exc}")

    closes = [safe_float(x) for x in hist["Close"].tolist()]
    closes = [x for x in closes if x is not None]
    volumes = [safe_float(x) for x in hist["Volume"].tolist()]
    volumes = [x for x in volumes if x is not None]

    last = closes[-1] if closes else None
    prev = closes[-2] if len(closes) > 1 else None
    sma20 = moving_average(closes, 20)
    sma50 = moving_average(closes, 50)
    sma200 = moving_average(closes, 200)
    rsi = simple_rsi(closes)

    def trailing_return(days: int) -> float | None:
        if len(closes) <= days:
            return None
        return pct_change(closes[-1], closes[-days - 1])

    news: list[dict[str, str]] = []
    try:
        for item in (obj.news or [])[:8]:
            content = item.get("content") or {}
            title = content.get("title") or item.get("title") or ""
            summary = content.get("summary") or item.get("summary") or ""
            url = (
                (content.get("canonicalUrl") or {}).get("url")
                or (content.get("clickThroughUrl") or {}).get("url")
                or item.get("link")
                or ""
            )
            if title:
                news.append({"title": title, "summary": summary, "url": url})
    except Exception as exc:
        warnings.append(f"news unavailable: {exc}")

    snapshot = PriceSnapshot(
        ticker=ticker.upper(),
        generated_at=now_utc(),
        company_name=info.get("longName") or info.get("shortName"),
        sector=info.get("sector"),
        industry=info.get("industry"),
        currency=info.get("currency"),
        market_cap=safe_float(info.get("marketCap")),
        last_close=last,
        previous_close=prev,
        day_change_pct=pct_change(last, prev),
        return_1m_pct=trailing_return(21),
        return_3m_pct=trailing_return(63),
        return_6m_pct=trailing_return(126),
        avg_volume_20d=(sum(volumes[-20:]) / min(len(volumes), 20)) if volumes else None,
        latest_volume=volumes[-1] if volumes else None,
        sma20=sma20,
        sma50=sma50,
        sma200=sma200,
        rsi14=rsi,
        technical_view=technical_view(last, sma20, sma50, sma200, rsi),
        data_warnings=warnings,
    )
    return snapshot, news


def template_snapshot(ticker: str) -> tuple[PriceSnapshot, list[dict[str, str]]]:
    return PriceSnapshot(
        ticker=ticker.upper(),
        generated_at=now_utc(),
        company_name=None,
        sector=None,
        industry=None,
        currency=None,
        market_cap=None,
        last_close=None,
        previous_close=None,
        day_change_pct=None,
        return_1m_pct=None,
        return_3m_pct=None,
        return_6m_pct=None,
        avg_volume_20d=None,
        latest_volume=None,
        sma20=None,
        sma50=None,
        sma200=None,
        rsi14=None,
        technical_view="Not assessed",
        data_warnings=["template mode: no market data fetched"],
    ), []


def render_memo(snapshot: PriceSnapshot, news: list[dict[str, str]]) -> str:
    title_name = snapshot.company_name or snapshot.ticker
    news_block = "\n".join(
        f"- {item['title']}" + (f" ({item['url']})" if item.get("url") else "")
        for item in news[:5]
    ) or "- No recent headlines fetched in this run."
    warnings = "\n".join(f"- {item}" for item in snapshot.data_warnings) or "- None from the data pull."

    return f"""# {snapshot.ticker} Equity Research Memo

## Snapshot
- Company: {title_name}
- Sector / industry: {snapshot.sector or "n/a"} / {snapshot.industry or "n/a"}
- Data timestamp: {snapshot.generated_at}
- Currency: {snapshot.currency or "n/a"}
- Last close: {fmt(snapshot.last_close)}
- Market cap: {fmt(snapshot.market_cap, 0)}
- 1D / 1M / 3M / 6M return: {fmt_pct(snapshot.day_change_pct)} / {fmt_pct(snapshot.return_1m_pct)} / {fmt_pct(snapshot.return_3m_pct)} / {fmt_pct(snapshot.return_6m_pct)}
- Technical view: {snapshot.technical_view}

## Thesis
Write the research thesis here. Separate what is known from what must be verified.

## Evidence
| Area | Evidence | Source / timestamp | Interpretation |
|---|---|---|---|
| Price | Last close {fmt(snapshot.last_close)}, 1M return {fmt_pct(snapshot.return_1m_pct)} | yfinance pull at {snapshot.generated_at} | Establishes recent market posture. |
| Trend | SMA20 {fmt(snapshot.sma20)}, SMA50 {fmt(snapshot.sma50)}, SMA200 {fmt(snapshot.sma200)}, RSI14 {fmt(snapshot.rsi14)} | computed from daily history | Technical posture: {snapshot.technical_view}. |
| Volume | Latest volume {fmt(snapshot.latest_volume, 0)}, 20D avg {fmt(snapshot.avg_volume_20d, 0)} | computed from daily history | Check liquidity and abnormal activity. |

## Market Data And Technical Setup
- Last close: {fmt(snapshot.last_close)}
- Previous close: {fmt(snapshot.previous_close)}
- Day change: {fmt_pct(snapshot.day_change_pct)}
- SMA20 / SMA50 / SMA200: {fmt(snapshot.sma20)} / {fmt(snapshot.sma50)} / {fmt(snapshot.sma200)}
- RSI14: {fmt(snapshot.rsi14)}
- Volume vs 20D average: {fmt(snapshot.latest_volume, 0)} vs {fmt(snapshot.avg_volume_20d, 0)}

## Company And Fundamentals
- Business description: add from filings, investor relations, or reliable company profile.
- Revenue drivers: add segment/product/geography context.
- Financial quality: add growth, margins, free cash flow, balance sheet, dilution, and capital allocation.

## Catalysts
Recent headlines from this run:
{news_block}

Add earnings date, product launches, regulatory events, macro drivers, and analyst estimate changes when available.

## Valuation Frame
- Base case: define revenue/margin/multiple or cash-flow assumptions.
- Bull case: define what has to go right.
- Bear case: define downside assumptions.
- Key assumptions: list the variables that matter most.

## Risks And Falsification
- What would make the thesis wrong?
- What operating metric should be monitored first?
- What price/technical level would invalidate the setup?
- What balance sheet or liquidity risk matters?

## Monitoring Checklist
- Next earnings date and guideposts.
- Revenue growth, gross margin, operating margin, free cash flow.
- Estimate revisions and management commentary.
- Sector/macro indicators.
- Price trend, volume, SMA50/SMA200, RSI regime.

## Research Limits
{warnings}

{DISCLAIMER}
"""


def write_artifacts(snapshot: PriceSnapshot, news: list[dict[str, str]], out_dir: Path) -> Path:
    out_dir.mkdir(parents=True, exist_ok=True)
    stem = snapshot.ticker.lower().replace(".", "-")
    md_path = out_dir / f"{stem}-equity-research.md"
    json_path = out_dir / f"{stem}-equity-research.json"
    md_path.write_text(render_memo(snapshot, news), encoding="utf-8")
    json_path.write_text(
        json.dumps({"snapshot": asdict(snapshot), "news": news, "disclaimer": DISCLAIMER}, indent=2),
        encoding="utf-8",
    )
    return md_path


def render_watchlist(snapshots: list[PriceSnapshot]) -> str:
    rows = []
    for item in snapshots:
        rows.append(
            f"| {item.ticker} | {item.company_name or 'n/a'} | {fmt_pct(item.return_1m_pct)} | "
            f"{fmt_pct(item.return_3m_pct)} | {fmt(item.rsi14)} | {item.technical_view} | {item.sector or 'n/a'} |"
        )
    return f"""# Equity Watchlist Triage

- Generated: {now_utc()}
- Tickers: {", ".join(item.ticker for item in snapshots)}

| Ticker | Company | 1M return | 3M return | RSI14 | Technical view | Sector |
|---|---|---:|---:|---:|---|---|
{chr(10).join(rows)}

## Next Research Steps
- Pick the names with the clearest variant perception.
- Add fundamentals, valuation, catalysts, and risk checks before forming a research view.
- Verify current data and news before any decision.

{DISCLAIMER}
"""


def main() -> int:
    parser = argparse.ArgumentParser(description="Generate lightweight equity research artifacts.")
    parser.add_argument("tickers", nargs="*", help="Ticker symbols, e.g. AAPL TSLA NVDA")
    parser.add_argument("--out", default="reports", help="Output directory")
    parser.add_argument("--mode", choices=["memo", "watchlist"], default="memo")
    parser.add_argument("--template", action="store_true", help="Generate offline template(s) without fetching data")
    args = parser.parse_args()

    tickers = [item.upper() for item in args.tickers if item.strip()]
    if not tickers:
        tickers = ["TICKER"] if args.template else []
    if not tickers:
        parser.error("provide at least one ticker or use --template")

    out_dir = Path(args.out)
    snapshots: list[PriceSnapshot] = []
    for ticker in tickers:
        snapshot, news = template_snapshot(ticker) if args.template else load_yfinance_snapshot(ticker)
        snapshots.append(snapshot)
        if args.mode == "memo":
            path = write_artifacts(snapshot, news, out_dir)
            print(path)

    if args.mode == "watchlist":
        out_dir.mkdir(parents=True, exist_ok=True)
        path = out_dir / "watchlist-equity-research.md"
        path.write_text(render_watchlist(snapshots), encoding="utf-8")
        print(path)

    return 0


if __name__ == "__main__":
    raise SystemExit(main())


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)