
Daisy Financial Research
- 3 installs
- 27 repo stars
- Updated May 17, 2026
- agents365-ai/daisy-financial-research
daisy-financial-research is a Claude Code skill that autonomously researches stocks, companies, and sectors, runs DCF valuation, and produces a sourced report.
About
A Claude Code skill that runs an autonomous financial-research workflow: plan, gather data via Tushare and web search, validate numbers, and produce a sourced report. It supports DCF valuation, financial comparison, catalyst analysis, and stock screening across A-share, Hong Kong, and US markets. An analyst uses it for stock or sector deep-dives. It never presents investment advice as certainty.
- Autonomous stock, company, and sector research producing a sourced report
- DCF valuation with sensitivity analysis plus screening across A-share, HK, and US markets
- Iterative agent loop with scratchpad, numerical validation, and Tushare data
Daisy Financial Research by the numbers
- 3 all-time installs (skills.sh)
- Ranked #847 of 1,106 Finance & Trading skills by installs in the Skillselion catalog
- Data as of Jul 31, 2026 (Skillselion catalog sync)
daisy-financial-research capabilities & compatibility
Requires a TUSHARE_TOKEN for any Tushare call; core analysis needs no other paid CLI.
- Capabilities
- financial research · stock valuation · stock screening · report generation
- Use cases
- research · data analysis · trading
- Platforms
- macOS · Linux · Windows
- Pricing
- Bring your own API key
What daisy-financial-research says it does
Autonomous stock / company / sector research workflow — plan, gather data, validate numbers, produce a sourced report.
Output concise, sourced analysis with caveats; never present investment advice as certainty.
npx skills add https://github.com/agents365-ai/daisy-financial-research --skill daisy-financial-researchAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 3 |
|---|---|
| repo stars | ★ 27 |
| Last updated | May 17, 2026 |
| Repository | agents365-ai/daisy-financial-research ↗ |
What it does
Run autonomous stock, sector, or DCF valuation research and produce a sourced financial report.
Who is it for?
Stock, company, and sector deep-dives and DCF valuation across A-share, HK, and US markets.
Skip if: Buy/sell order execution or personalized portfolio advice without risk and timeline context.
When should I use this skill?
The user asks for stock/company/sector deep-dive research, DCF or valuation, or stock screening.
What you get
A sourced financial report with DCF valuation, sensitivity analysis, and caveats.
- Sourced financial research report
- DCF valuation
- Screening results
By the numbers
- Encodes 8 Dexter-inspired research principles
- Scripts use documented exit codes 0-5
Files
Daisy Financial Research
Autonomous stock / company / sector research workflow — plan, gather data, validate numbers, produce a sourced report. Inspired by the virattt/dexter design patterns (iterative agent loop, scratchpad, soft loop limits, numerical validation), packaged as a multi-platform skill.
Dexter’s key ideas:
1. Treat financial research as an iterative agent loop, not a one-shot answer. 2. First create a compact research plan, then execute data-gathering steps. 3. Use a scratchpad as the single source of truth for tool calls, results, assumptions, and partial conclusions. 4. Prefer high-level meta-queries to finance data tools, but fall back to specific interfaces when needed. 5. Use soft loop limits and repeat-query detection to avoid runaway tool use. 6. Validate numerical answers before finalizing. 7. For valuation, use an explicit DCF workflow with sensitivity analysis and sanity checks. 8. Output concise, sourced analysis with caveats; never present investment advice as certainty.
Python interpreter convention
Command examples in this skill use a bare python. Substitute it with whichever interpreter in the caller's environment has tushare, pandas, and requests installed — for example python3, ~/.hermes/venv/bin/python, ~\.hermes\venv\Scripts\python.exe, a conda env, uv run python, or a pyenv-managed version. The skill does not assume any specific install location and works on macOS, Linux, and Windows.
Agent-native CLI conventions
All scripts under scripts/ follow a uniform agent-native contract so an LLM agent can call them without parsing prose:
- Output format auto-detection. When stdout is not a TTY (e.g. captured by
subprocess.run), scripts emit a single JSON envelope on stdout. When stdout is a TTY, scripts emit the legacy human table. Override with--format json|table. - Stable success envelope:
{"ok": true, "data": {...}, "meta": {"schema_version", "request_id", "latency_ms"}}. - Stable error envelope:
{"ok": false, "error": {"code", "message", "retryable", "context"}, "meta": {...}}. Error messages stay on stderr in table mode. - Schema introspection.
python <this-skill-dir>/scripts/<name>.py --schemareturns parameter types, preset registries, upstream interfaces, and error codes as JSON. Agents should prefer--schemaover parsing--help. - Dry-run preview.
--dry-runechoes the request shape (would_call, would_write, filters, search_window) without making upstream API calls or writing files. Available on all mutating scripts. - Documented exit codes:
0ok ·1runtime ·2auth ·3validation ·4no_data ·5dependency. - Long-running progress.
screen_hk_connect.py --with-momentumandfinancial_report.pyemit NDJSON progress events on stderr (one JSON per line) so agents can detect liveness during multi-second runs. - Idempotency. Output files are date-stamped (
YYYYMMDD_*orYYYYMMDD-HHMMSS_*); re-runs are deterministic and overwrite the same path.
Agents calling these scripts should:
1. Run --schema once per script to learn parameters/presets, instead of parsing --help. 2. Capture stdout as JSON (auto-detected when piped) and branch on data.ok. 3. Read error.code (not error.message) to decide retry vs. escalate. retryable: true + a no_data code typically means "loosen filters or extend --lookback-days".
Trigger conditions
Use this skill for:
- “研究一下 / analyze / deep dive” on a stock, company, ETF, index, sector, or market theme
- “DCF / intrinsic value / fair value / undervalued / overvalued / price target”
- Financial statement trend analysis, valuation comparison, earnings/catalyst analysis
- “Compare A vs B” for companies/sectors
- “Why did this stock move?” or “what changed recently?”
- Chinese A-share/HK/ETF queries where Tushare can provide data
- Stock screening / watchlist construction across A-share or Hong Kong markets, including dividend, quality, valuation, growth, momentum, and risk filters
Do not use for:
- Direct buy/sell/order execution
- Personalized portfolio advice without explicit risk/timeline context
- Unverifiable rumors
- Questions that can be answered from stable definitions without external data
Mandatory workflow
Step 0. Update check (notify, don't pull) — first use per conversation
Throttle to one check per 24 hours per installation; never mutate the skill directory without explicit user consent.
1. If <this-skill-dir>/.last_update exists and is less than 24 hours old, skip this step entirely.
2. Otherwise, fetch the latest tag from upstream:
git -C <this-skill-dir> ls-remote --tags origin 'v*' 2>/dev/null \
| awk '{print $2}' | sed 's|refs/tags/||' \
| sort -V | tail -13. Compare with this skill's metadata.version from the frontmatter. If the upstream tag is strictly newer (semver), tell the user one line and ask:
"A newer version of this skill is available: vX.Y.Z → vA.B.C. Want me to git pull?"If they say yes, run git -C <this-skill-dir> pull --ff-only. Refresh .last_update either way so the prompt doesn't repeat for 24 hours.
4. If upstream is the same or older, refresh .last_update silently and continue.
5. On any failure (offline, not a git checkout — e.g. ClawHub-installed copy, read-only path, no permission), swallow the error silently and continue with the user's task. Do not mention the failure.
0. State scope and assumptions briefly
Infer obvious defaults instead of asking:
- “recent” = last 60 calendar days / ~40 trading days
- “financial trend” = last 8 quarters or last 5 annual periods when available
- “valuation” = DCF + multiple sanity check
- “A股” = Tushare first; US stocks = web/search or available market APIs first
Ask only if ambiguity changes the analysis materially.
1. Create a research scratchpad
For any non-trivial finance task, keep a local scratchpad file under:
./financial-research/scratchpad/
Use the helper script in this skill when useful:
python <this-skill-dir>/scripts/dexter_scratchpad.py init "original query"
python <this-skill-dir>/scripts/dexter_scratchpad.py add /path/to/file.jsonl tool_result tool_name='tushare.daily' args='...' result='...'If not using the helper, still preserve internally:
- original query
- plan
- each data source/tool/interface used
- parameters/date ranges
- raw key data and transformed metrics
- errors/empty results/permission issues
- assumptions and interim conclusions
1b. Pull cross-session decision memory (optional but recommended)
The scratchpad is per-task. For learning across sessions and tickers, use the decision-log helper to read past calls before the plan step and to record the new call after the final answer:
# At plan step: pull recent same-ticker analyses + cross-ticker lessons
python <this-skill-dir>/scripts/dexter_memory_log.py context --ticker 600519.SH
# After final answer: record a pending decision
python <this-skill-dir>/scripts/dexter_memory_log.py record \
--ticker 600519.SH --rating Buy --date 20260502 \
--decision "Thesis: PE22, ROE30, dividend stable, demand resilient. Plan: re-check at next earnings."
# Later, when realized returns are known: resolve the pending entry.
# Recommended path — let daisy fetch close prices and benchmark automatically:
python <this-skill-dir>/scripts/dexter_memory_log.py auto-resolve \
--ticker 600519.SH --date 20260502 \
--reflection "Held 17d, raw +4.8% vs CSI300 +3.6%, alpha +1.2%. Dividend+ROE thesis worked."
# Or if you've already computed the numbers yourself:
python <this-skill-dir>/scripts/dexter_memory_log.py resolve \
--ticker 600519.SH --date 20260502 \
--raw-return 4.8 --alpha-return 1.2 --holding-days 17 \
--reflection "..."auto-resolve is the recommended path. It fetches close[decision_date] and close[as_of_date] for the ticker, walks forward / backward to the nearest trading day, fetches the right benchmark by ticker suffix (CSI 300 for *.SH/SZ/BJ, HSI for *.HK, SPY for US tickers), computes raw + alpha + holding days, then runs the same atomic-rewrite resolve logic as the manual path. For HK names, when Tushare's HK index endpoints aren't available in the user's plan, the helper falls through to AKShare's stock_hk_index_daily_sina for HSI (requires pip install akshare).
Use dexter_memory_log.py compute-returns to inspect the numbers without persisting:
python <this-skill-dir>/scripts/dexter_memory_log.py compute-returns \
--ticker 600519.SH --date 20260415
# → JSON envelope with raw_return_pct / alpha_return_pct / benchmark_return_pct / holding_daysTo audit your own track record across many resolved entries, run backtest:
# Auto-derived window covering every resolved entry
python <this-skill-dir>/scripts/dexter_memory_log.py backtest
# Explicit window, Buy ratings only
python <this-skill-dir>/scripts/dexter_memory_log.py backtest \
--from 20260101 --to 20260430 --rating BuyReturns per-rating count / mean alpha / alpha_hit_rate / alpha_t_stat / annualized_alpha_pct, plus an overall block with the cumulative-alpha drawdown. The metric names make explicit that this is decision-level — daisy logs decisions, not a continuous portfolio NAV, so a textbook Sharpe ratio doesn't apply.
When writing the --reflection text, follow the standard 2–4-sentence shape in references/reflection-prompt.md so lessons stay short enough to be re-injected on future runs.
Storage: a single Markdown file at ./financial-research/memory/decision-log.md. Entries are separated by the HTML comment <!-- ENTRY_END -->. Tag lines start as [YYYY-MM-DD | ticker | rating | pending] and become [YYYY-MM-DD | ticker | rating | +X.X% | +Y.Y% | Nd] on resolve. record is idempotent on (date, ticker) — re-running with the same key skips silently. Ratings are constrained to Buy / Overweight / Hold / Underweight / Sell (see references/decision-schema.md for the full rating vocabulary and report markdown contract). Use dexter_memory_log.py stats for a hit-rate / mean-alpha summary.
2. Plan before tools
Write a 3–7 item plan. Keep it tactical:
- identify company/ticker/universe
- collect price/market data
- collect financials/ratios/estimates/filings/news as relevant
- compute metrics or valuation
- validate numbers and sources
- synthesize concise answer
3. Tool/data routing policy
The canonical per-market routing reference (A-share / HK / US, primary + documented fallback chain for each data type) lives at references/data-source-routing.md. Read it before the plan step; the rest of this section is the agent-facing summary.
For Chinese market / Tushare-accessible data:
- Load/use the
tushareskill if not already loaded. - Use
TUSHARE_TOKENfrom environment. - Prefer Tushare for: A-share daily prices, stock_basic, daily_basic, income, balancesheet, cashflow, fina_indicator, forecast/express, moneyflow, margin, concept/index/ETF/fund/macro data.
- Use date format
YYYYMMDDand stock code format like000001.SZ,600000.SH.
For Hong Kong stocks:
- Use Tushare HK interfaces when available before falling back to web quote sites.
pro.hk_basic(ts_code='00005.HK', ...)andpro.hk_daily(ts_code='00005.HK', start_date='YYYYMMDD', end_date='YYYYMMDD')are known-good for HK tickers such as HSBC00005.HK.- For the user's Hong Kong Stock Connect universe (港股通) preference when explicitly requested, use
pro.hk_hold(trade_date='YYYYMMDD')as a first-pass universe identifier. It returns Southbound Stock Connect holdings with fields such ascode,trade_date,ts_code,name,vol,ratio,exchange. - Use the bundled helper to export the latest 港股通 universe:
python <this-skill-dir>/scripts/hk_connect_universe.py --date YYYYMMDD --top 20- The helper searches backward when the requested date has no data and writes a CSV under
./financial-research/universes/YYYYMMDD_hk-connect-universe.csv. - For 港股通 flow/capital attention, optionally use
pro.ggt_top10(...),pro.ggt_daily(...), andpro.moneyflow_hsgt(...). - Do not assume every advertised HK interface works in the installed Tushare version; in this environment
pro.hk_daily_basic(...)returned请指定正确的接口名, so treat it as unavailable unless re-tested. Fallback: when an HK valuation/fundamentals call fails on Tushare, use the bundled AKShare helper (no Tushare token, no auth):
# PE-TTM / PB / PS / PCF snapshot + Stock Connect eligibility
python <this-skill-dir>/scripts/akshare_hk_valuation.py valuation --ts-code 00005.HK
# Annual or quarterly fundamentals: ROE_YEARLY, EPS_TTM, BPS, ROA, leverage
python <this-skill-dir>/scripts/akshare_hk_valuation.py fundamentals --ts-code 00005.HK --period 年度 --limit 8
# Local-dict-only Chinese name lookup (no API call) — covers ~30 HK majors
python <this-skill-dir>/scripts/akshare_hk_valuation.py name --ts-code 00700.HKSources: AKShare stock_hk_valuation_comparison_em + stock_hk_security_profile_em for valuation; stock_financial_hk_analysis_indicator_em for fundamentals. Optional pip install akshare; the helper emits dependency_missing (exit=5) with a clear install hint if the package is absent.
- For banks, DCF is usually the wrong primary valuation frame. Prefer RoTE/ROE, CET1, dividend payout/yield, NIM/NII guidance, credit cost, P/B or P/E, buyback capacity, and analyst target sanity checks.
- Maintain the user's preferred finance-search stack: Tushare for structured market/financial data; Brave MCP as primary web search; Bailian WebSearch MCP as Chinese/China-market supplement; Python for calculations; browser only for dynamic/interactive pages. Do not include Asta/Semantic Scholar as a default route for finance evidence.
- Session detail: see
references/hsbc-hk-bank-research-test-20260429.mdfor the HSBC test workflow and pitfalls.
For web/current context:
- Prefer Brave MCP search (
brave_web_search/brave_local_searchwhen available) for current news, filings, company pages, market context, source discovery, and broad English/global web coverage. - Use Bailian WebSearch MCP (
bailian_web_search) as an optional/secondary search channel, especially for Chinese-language queries, China-market news, general encyclopedia-style facts, weather/news/current info, or when Brave results are sparse. - Cross-check important claims with at least two independent sources when the answer depends on recent news, market rumors, policy, regulation, or company events.
- Use browser only when interaction, dynamic pages, paywall/login behavior, or visual inspection is needed.
- Use terminal Python for calculations and tabulation.
- If a dedicated finance API/tool is unavailable, be explicit about source limits.
Routing heuristics adapted from Dexter:
- Price / market movement / news / insider activity → market data or web search.
- Income statement / balance sheet / cash flow / ratios / estimates → financials.
- SEC filing details → filings/web sources.
- Broad market or macro news → web search.
- Screening by financial criteria → Tushare screening script or Python filtering.
- DCF / fair value → follow the DCF checklist below.
- Revenue breakdown by product / region / segment → A-share has a structured source (
scripts/segments.py→ AKSharestock_zygc_em); for HK / US there is no free segment API, so read the annual report's "Segment Information" note via filings / Brave search.
When the user asks "why is the market down today" / "今天大盘为什么跌" / "what's moving the Hang Seng" — no specific ticker — go straight to broad web search (Brave MCP for English / global, Bailian MCP for Chinese-language sources) with a market-wide query like 美股下跌 原因 YYYY-MM-DD or S&P 500 selloff YYYY-MM-DD. Do not pick one large-cap ticker and search its news as a proxy; the intent is macro / sector-rotation / rates / geopolitical catalysts, not a company event.
4. Soft loop limits
Avoid repetitive tool calls:
- Suggested max per tool/interface: 3 attempts per query.
- If a query/interface fails twice, change strategy: different endpoint, broader/narrower date range, web fallback, or explain limitation.
- Do not keep calling the same endpoint with near-identical parameters.
- If data is incomplete, proceed with caveated analysis rather than fabricating.
When the scratchpad helper is active, you can ask it to flag both failure modes before a tool call:
python <this-skill-dir>/scripts/dexter_scratchpad.py can-call \
<scratchpad.jsonl> tushare.daily 'ts_code=600519.SH start=20240101 end=20240630'
# → {allowed: true, warning: null|string, current_count: int, similar_to: [...]}allowed is always true (this is a soft warning, not a block). React to a non-null warning: if current_count >= max_calls, change endpoint; if similar_to is non-empty, the tool is about to repeat a recent call — adjust the query or skip.
5. Numerical validation checklist
Before final answer, verify:
- Date ranges and units are stated.
- Currency/unit scale is consistent: yuan vs USD, CNY vs HKD, millions/billions.
- Growth rates use comparable periods.
- Per-share metrics use correct shares if computed manually.
- Market cap / EV / price are from a stated date.
- Any ranking/screening has universe and filters stated.
- If data is missing or permission-limited, say so.
6. Final answer format
Use this concise structure:
1. Scope/Data: tickers, period, sources/interfaces used. 2. Key Findings: 3–6 bullets with numbers. 3. Evidence Table: compact table when comparative/numerical. 4. Interpretation: what the data suggests, not overclaimed. 5. Risks / Missing Data / Caveats. 6. If exported: file path.
Always include: “Data analysis only, not investment advice.” when discussing securities.
7. Report export policy
For substantial research tasks, generate a durable report under:
./financial-research/reports/
Preferred report stack:
1. Markdown source (.md) as the canonical editable record. 2. HTML report (.html) as the primary polished output. 3. PDF (.pdf) only when the user asks for a printable/shareable file, or when HTML-to-PDF tooling is available and stable.
Default behavior:
- For quick answers: reply in chat only, optionally with scratchpad path.
- For medium/deep research: create both
.mdand.html. - For formal deliverables: create
.md,.html, and.pdfif possible.
Hermes back-compat note. Hermes installations that want to keep the legacy archive layout (~/.hermes/reports/financial-research/) can pass --out-dir ~/.hermes/reports/financial-research to any script — the script appends the matching subdir (reports/, watchlists/, universes/, scratchpad/) automatically.
Use the bundled report generator:
# medium/deep research: Markdown + HTML
python <this-skill-dir>/scripts/financial_report.py report.md --title "Company Research Report" --slug company-research
# formal deliverable: Markdown + HTML + PDF
python <this-skill-dir>/scripts/financial_report.py report.md --title "Company Research Report" --slug company-research --pdfThe generator copies the Markdown source and renders the report to:
./financial-research/reports/YYYYMMDD-HHMMSS_slug.{md,html,pdf}
Why HTML first:
- Easier to render tables, charts, color-coded risks, source links, and sensitivity matrices.
- More reliable than PDF generation in CLI environments.
- Can be opened directly in a browser and later printed/exported to PDF.
PDF guidance:
- Use PDF for sharing, archiving, printing, or sending to non-technical readers.
- Prefer generating PDF from the HTML report using browser print, Playwright/Chromium, or another available HTML-to-PDF tool.
- If PDF generation fails, keep the HTML and state the limitation rather than blocking the analysis.
Recommended report sections:
1. Executive summary / investment view. 2. Company and ticker scope. 3. Data sources and dates. 4. Price and valuation snapshot. When the report needs a technical-analysis layer, pick up to 8 complementary indicators from references/technical-indicator-cheatsheet.md and compute them via scripts/technical_indicators.py --ts-code <code> (auto-routes A-share/HK/US, applies a strict look-ahead-bias guard at --as-of). Skip TA entirely for banks / insurers — RoTE / CET1 / NIM are the right frame for those. 5. Financial performance and key drivers. For A-share names, pulling a revenue-by-segment / 主营构成 breakdown often surfaces concentration risk (one product line / one region) that the headline P&L hides:
# All classifications (按产品 / 按地区 / 按行业), 4 most recent reports
python <this-skill-dir>/scripts/segments.py --ts-code 600519.SH
# Filter to one axis
python <this-skill-dir>/scripts/segments.py --ts-code 000001.SZ --classification 按地区HK / US names: no free segment API — read the latest annual report's "Segment Information" note (10-K for US, annual report "Operating Segments" section for HK) via the filings tool or Brave search. 6. News/catalyst review. For A-share / 港股 names, pull China-market context (涨跌停 risk, 北向资金, 板块 rotation, 监管 backdrop) using the system prompt in references/cn-market-analyst-prompts.md. 7. Bull/base/bear scenarios. For balanced single-company research, run the three-prompt debate template in references/debate-prompts.md (Bull → Bear → Synthesis) instead of writing scenarios free-form. The synthesis output's 5-tier rating maps directly onto dexter_memory_log.py record --rating. For position-sizing follow-up after the directional rating is set, optionally run references/risk-debate-prompts.md (Aggressive → Conservative → Neutral → Portfolio Manager). All synthesis outputs use the markdown shape and rating vocabulary documented in references/decision-schema.md. Either loop can be driven mechanically by scripts/debate_runner.py (subcommands init / next / synthesize, --type research|risk) — the script enforces the rotation rules and exit conditions so the agent only has to write each speaker's argument; full usage in the "Programmatic loop driver" sections of the two prompt files. 8. Risks and what would change the view. 9. Evidence tables and calculations. 10. Disclaimer: data analysis only, not investment advice.
8. Stock screening and watchlist workflow
Use this when the user asks “怎么选股”, “筛一批股票”, “A股/港股有什么值得关注”, or wants a watchlist rather than a single-company report.
Reusable files:
- Presets/reference:
references/stock-screening-presets.md - Screening report template:
templates/screening_report.md - A-share screener:
scripts/screen_a_share.py - Hong Kong Stock Connect screener:
scripts/screen_hk_connect.py(only when 港股通 is explicitly requested) - Report generator:
scripts/financial_report.py
Common commands:
# A-share dividend/quality watchlist + Markdown report source
python <this-skill-dir>/scripts/screen_a_share.py --preset a_dividend_quality --top 50 --report
# A-share value watchlist
python <this-skill-dir>/scripts/screen_a_share.py --preset a_value --top 50 --report
# 港股通 watchlist only when explicitly requested
python <this-skill-dir>/scripts/screen_hk_connect.py --top 50 --with-momentum
# Turn generated Markdown into the three-layer report stack
python <this-skill-dir>/scripts/financial_report.py report.md --title "Watchlist Report" --slug watchlist --pdfWatchlist outputs go under:
./financial-research/watchlists/
Do not try to predict winners directly. Build a funnel:
1. Define universe
- A-share: all listed stocks, index constituents, industry, market-cap band, dividend universe, or user-defined list.
- Hong Kong: HK main board / H-share / Hang Seng indexes / Hong Kong Stock Connect (港股通, when explicitly requested) / user-defined HK tickers.
- Exclude suspended, ST/*ST, newly listed names, illiquid names, or missing-data names unless the user explicitly wants them.
2. Choose screening style
- Dividend/income: dividend yield, payout sustainability, ROE/ROTE, cash flow, debt, earnings stability.
- Quality compounder: ROE/ROIC, gross/net margin, revenue/profit CAGR, low leverage, stable cash flow.
- Value: low PE/PB/EV metrics, but require profitability and no obvious balance-sheet trap.
- Growth: revenue/profit growth, margin trend, industry tailwind, valuation sanity.
- Turnaround/event: earnings inflection, policy catalyst, restructuring, buyback, sector cycle.
- Momentum: 1/3/6/12-month returns, relative strength, drawdown, volume confirmation.
3. Apply hard filters first
- Liquidity: daily turnover or volume threshold.
- Size: market cap threshold.
- Financial health: positive earnings or operating cash flow, leverage not extreme.
- Valuation: remove obvious extreme outliers unless justified.
- Data completeness: remove rows with missing critical fields.
4. Score candidates
- Build 4–6 factor scores rather than one magic metric.
- Suggested default weights: quality 30%, valuation 25%, growth 20%, shareholder return 15%, momentum 10%.
- For bank/insurance stocks, replace generic DCF/gross-margin metrics with ROE/ROTE, CET1/solvency, NIM/NII, credit cost, PB/PE, dividend and buyback capacity.
5. Produce a shortlist
- Output 10–30 names for broad screens, then 3–8 names for deep-dive priority.
- Include “why selected”, key metrics, red flags, and next verification step.
- Never present the screen as a buy list; call it a research watchlist.
6. Deep-dive the finalists
- For each finalist, run the single-company research workflow: data, news/catalysts, valuation, risks, scenario view.
- Generate Markdown + HTML reports for substantial screens; add PDF for formal deliverables.
Suggested output tables:
- Universe and filters table.
- Top candidates table with ticker, name, industry, market cap, PE/PB, ROE/ROTE, dividend yield, growth, momentum, score, red flag.
- Priority deep-dive list: top 3–8 names and why they deserve follow-up.
- Exclusion notes: important names removed and why.
Screening caveats:
- Tushare/HK data availability varies by interface and user permissions; document missing fields.
- A cheap stock can be a value trap; require at least one quality or catalyst confirmation.
- A high dividend can be unsafe; check payout ratio, earnings stability, balance sheet and cash flow.
- Momentum screens need risk controls; do not confuse recent price strength with intrinsic value.
DCF valuation workflow
Use when the user asks for intrinsic/fair value, DCF, price target, undervalued/overvalued.
Progress checklist:
- [ ] Gather financial data
- [ ] Calculate FCF base and historical FCF growth
- [ ] Estimate discount rate / WACC
- [ ] Project FCF for years 1–5 + terminal value
- [ ] Discount to present value and compute fair value per share
- [ ] Run sensitivity analysis
- [ ] Validate result
- [ ] Present assumptions and caveats
Data to gather
- 5 years annual cash flow: operating cash flow, capex, free cash flow
- latest balance sheet: cash, investments, total debt, shares outstanding
- financial metrics: market cap, enterprise value, margins, ROE/ROIC, debt/equity, revenue growth
- analyst estimates if available
- latest price
- sector/industry for WACC sanity
Assumptions
- FCF = operating cash flow - capex if not directly available
- Growth: use 5-year FCF CAGR if stable, haircut by 10–20%; cap sustained base growth at 15% unless justified
- For volatile FCF, triangulate with revenue growth, EPS estimates, and margin trend
- WACC: default 8–10% for mature companies; higher for cyclicals/small caps/high leverage; lower for stable defensives
- Terminal growth: default 2.5%; sensitivity 2.0%, 2.5%, 3.0%
- Years 1–5 growth decay: base growth × 1.00, 0.95, 0.90, 0.85, 0.80
DCF validation
- Terminal value should usually be 50–80% of EV for mature companies; >90% is fragile.
- Calculated EV should be directionally plausible vs market EV; if >30–50% away, explain drivers.
- Cross-check fair value against FCF/share × 15–25 or sector multiple.
- Include a 3×3 sensitivity matrix: WACC base ±1% vs terminal growth 2.0/2.5/3.0%.
A-share quick-start patterns with Tushare
Environment check:
import os, tushare as ts
assert os.getenv('TUSHARE_TOKEN') or ts.get_token(), 'Missing TUSHARE_TOKEN'
pro = ts.pro_api(os.getenv('TUSHARE_TOKEN') or ts.get_token())Common interfaces:
# Stock list
pro.stock_basic(list_status='L', fields='ts_code,symbol,name,area,industry,list_date')
# Daily price
pro.daily(ts_code='000001.SZ', start_date='20240101', end_date='20241231')
# Daily valuation/market metrics
pro.daily_basic(ts_code='000001.SZ', start_date='20240101', end_date='20241231', fields='ts_code,trade_date,close,pe,pb,total_mv,circ_mv,turnover_rate,volume_ratio')
# Financial indicators
pro.fina_indicator(ts_code='000001.SZ', period='20231231')
# Income / balance sheet / cash flow
pro.income(ts_code='000001.SZ', period='20231231')
pro.balancesheet(ts_code='000001.SZ', period='20231231')
pro.cashflow(ts_code='000001.SZ', period='20231231')Quality bar
A good Dexter-style answer should be:
- grounded: every important number has a source/interface/date
- multi-step: shows it planned, gathered, computed, validated
- honest: says what is missing or permission-limited
- compact: useful to a finance reader, not a data dump
- reproducible: scratchpad/export path if the analysis used substantial data
CLAUDE.md
.claude/settings.local.json
__pycache__/
*.pyc
.DS_Store
.worktrees/
docs/superpowers/
# uv-managed local environment (pyproject.toml + uv.lock are tracked; .venv is not)
.venv/
.python-version
# Test suite kept local-only (not part of the published skill artifact)
tests/
.pytest_cache/
# Diagram source SVGs kept local-only; the rendered JPGs in assets/ are tracked
assets/*.svg
# Local-only research / migration notes (not part of the published skill)
docs/migration-research-*.md
dexter/
1777689909
interface:
display_name: "Daisy Financial Research"
short_description: "Autonomous stock / company / sector research with Tushare data, DCF valuation, screening, and sourced reports."
brand_color: "#2E7D5B"
policy:
allow_implicit_invocation: true
capabilities:
- Plan-then-execute research loop with scratchpad, soft loop limits, and numerical validation
- DCF workflow with sensitivity analysis and sanity checks
- Bank/financial-sector valuation (RoTE, CET1, NIM, P/B) instead of DCF
- A-share and Hong Kong Stock Connect screening with named presets
- Markdown + HTML (+ optional PDF) sourced reports under ./financial-research/
- Brave MCP / Bailian WebSearch MCP integration for web context
- Multi-agent debate (Bull/Bear and risk perspectives) for substantial single-name research, driven by debate_runner.py
prerequisites:
- Python 3.9+ with tushare, pandas, requests
- TUSHARE_TOKEN environment variable for any Tushare call
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
What this repo is
A multi-platform agent skill (daisy-financial-research) for stock / company / sector research, DCF valuation, and stock screening. The local working directory is dexter-financial-research/ (legacy name retained); the published name and the GitHub repo are daisy-financial-research. Loaded by Claude Code, Opencode, OpenClaw / ClawHub, Hermes, OpenAI Codex, and SkillsMP.
The canonical user-facing contract is SKILL.md; everything else (scripts/, references/, templates/, agents/openai.yaml) supports it. When changing behavior the user will see, update SKILL.md in lockstep with the scripts — they reference each other by exact path and CLI flag.
Runtime layout
By default, every script writes under the user's current working directory:
./financial-research/reports/— final reports (md/html/pdf)../financial-research/watchlists/— screener outputs../financial-research/scratchpad/— per-task JSONL scratchpads../financial-research/universes/— HK Connect universe exports.
Each script accepts --out-dir <root> to redirect the root; subdirs are appended automatically. Hermes users who want the legacy ~/.hermes/reports/financial-research/<subdir>/ layout pass --out-dir ~/.hermes/reports/financial-research.
TUSHARE_TOKEN env var is required for any screen_*, hk_connect_universe.py, or other Tushare-backed call.
SKILL.md examples use a bare python placeholder; the caller substitutes whichever interpreter has tushare, pandas, requests installed (system python3, ~/.hermes/venv/bin/python, ~\.hermes\venv\Scripts\python.exe, conda, uv run python, pyenv...). Do not reintroduce hardcoded interpreter paths — the convention is documented in SKILL.md under "Python interpreter convention".
SKILL.md references scripts by the placeholder <this-skill-dir>/scripts/X.py (the same placeholder convention drawio-skill uses). The agent runtime substitutes the actual install dir. Do not reintroduce hardcoded ~/.<platform>/skills/.../scripts/ paths in SKILL.md.
Renaming a script is a breaking change to the skill contract.
Scripts
scripts/dexter_scratchpad.py—init/add/show/can-callsubcommands; appends JSONL records of tool calls and results.can-call <path> <tool> <query>is a soft loop-limit guard ported fromvirattt/dexter:src/agent/scratchpad.ts: always returnsallowed=True, but emits a warning when (a) the tool has been called >=--max-callstimes in the pad (default 3, matching SKILL.md §4), or (b) the new query is textually similar (difflibratio >=--similarity-threshold, default 0.7) to a prior call's args. Stdlib-only — no embedding deps. Default output:./financial-research/scratchpad/. Per-task only.scripts/dexter_memory_log.py— cross-session, cross-ticker decision log. Subcommands:record(append pending entry, idempotent on (date, ticker)),resolve(replace pending tag with realized returns + append REFLECTION via atomic rewrite),list/context/stats,backtest(risk-adjusted decision-level metrics across a--from/--towindow: per-rating mean / hit-rate /alpha_t_stat/annualized_alpha_pct/annualized_alpha_sortino_likeplus a cumulative-alpha curve and its max drawdown — explicitly not a portfolio Sharpe ratio since daisy logs decisions, not a continuous NAV), pluscompute-returns(fetch close[decision]/close[as_of]/benchmark, compute raw + alpha; no log mutation) andauto-resolve(compute + resolve in one call — closes the resolve loop; also batch modeauto-resolve --due --min-pending-days Nsweeps every pending entry past N days, computes each one's returns, and writes an auto-templated[no manual reflection]REFLECTION via single batched atomic rewrite). Benchmark routing by ticker suffix:*.SH/SZ/BJ→ 000300.SH (CSI 300) viapro.index_daily;*.HK→ HSI viapro.index_daily/pro.index_global/pro.hk_dailyfallback chain, with AKSharestock_hk_index_daily_sinaas final fallback (lazy-imported); US tickers → SPY viayfinance(lazy-imported). Single Markdown file at./financial-research/memory/decision-log.md. Format ported fromTradingAgents/tradingagents/agents/utils/memory.py:<!-- ENTRY_END -->separator, tag-line[date | ticker | rating | …],DECISION:/REFLECTION:body sections. Rating enum: Buy / Overweight / Hold / Underweight / Sell.scripts/financial_report.py— copies a Markdown source to the reports dir and renders HTML;--pdfadds PDF (best-effort, may no-op if no HTML→PDF tool is available). Default output:./financial-research/reports/.scripts/hk_connect_universe.py—pro.hk_hold(...)based HK Stock Connect (港股通) universe export. Searches backward when the requested date has no data. Default output:./financial-research/universes/.scripts/screen_a_share.py— A-share screener with named presets (seereferences/stock-screening-presets.md);--reportemits a Markdown source thatfinancial_report.pycan render. Default outputs:./financial-research/watchlists/(csv/json) and./financial-research/reports/(when--report).scripts/screen_hk_connect.py— HK Stock Connect screener; only used when 港股通 is explicitly requested. Default output:./financial-research/watchlists/.scripts/akshare_hk_valuation.py— HK valuation + fundamentals fallback via AKShare. Subcommandsvaluation(PE/PB/PS snapshot + Stock Connect eligibility viastock_hk_valuation_comparison_em+stock_hk_security_profile_em),fundamentals(ROE/EPS/BPS/leverage time series viastock_financial_hk_analysis_indicator_em), andname(local-dict-only Chinese-name lookup, no API call). No Tushare token. Closes the documentedpro.hk_daily_basicgap. AKShare is lazy-imported, so--help/--schema/--dry-runwork without the optional dep installed; live calls returndependency_missing(exit=5) when akshare is absent. Thevaluationsubcommand falls throughakshare row.简称 → references/hk-ticker-name.json → ''for the Chinese name and reports the winning leg asname_source.scripts/technical_indicators.py— point-in-time technical-indicator calculator (SMA/EMA/MACD/RSI/Bollinger/ATR/VWMA viastockstats). Auto-routes by ts_code suffix:*.SH/SZ/BJ→pro.daily,*.HK→pro.hk_daily, bare →yfinance.download. Look-ahead-bias guard filters rows byDate <= --as-ofbefore stockstats runs, so backtests cannot see future bars. Default indicators are the 8 fromreferences/technical-indicator-cheatsheet.md"Worked picking example".tushare/yfinance/stockstatsare all lazy-imported;--help/--schema/--dry-runwork without any of them. Read-only (no file output, no--out-dir). Design ported fromTradingAgents/tradingagents/dataflows/stockstats_utils.py, refactored for batch indicator output and multi-market routing.scripts/segments.py— operating-segment / 主营构成 breakdown. Ports virattt/dexter'sgetFinancialSegmentstool (/financials/segments/) to a free-tier path: A-share (*.SH/SZ/BJ) → AKSharestock_zygc_em(rows tagged with分类类型按产品 / 按地区 / 按行业 plus revenue / cost / profit / share / gross margin). HK and US emitno_data(exit=4) with ahintpointing at the annual report's "Segment Information" note via filings / web — no free structured source exists for those markets. ts_code routing via the same suffix grammar astechnical_indicators.py. AKShare is lazy-imported, so--help/--schema/--dry-runwork without the dep installed. Read-only (no--out-dir). Default output: 4 most-recent report dates, all classification axes.scripts/debate_runner.py— multi-agent debate orchestrator (Bull/Bear/Synthesis + Aggressive/Conservative/Neutral/PortfolioManager) ported from the TradingAgents debate / risk_mgmt loops. State-machine referee + template renderer; never calls an LLM, never fetches data. Subcommandsinit/next/synthesize;--type research|riskselects the rotation;--context-fileinjects all placeholder values via a single JSON object; state derives entirely fromdebate_init+debate_turnrecords the script appends to the agent-supplied--pad. Embedded prompt strings stay byte-identical toreferences/{debate,risk-debate}-prompts.md(enforced bytests/test_debate_prompts_match_references.py). Hash-drift in--context-fileor--prior-synthesis-fileafter init surfaces as non-fatalmeta.warnings+debate_warningpad records. No--out-dir(read-only outside the pad).
All mutating scripts accept --out-dir <root>; subdirs are appended automatically. technical_indicators.py, debate_runner.py, and segments.py are read-only outside their input/pad files and have no --out-dir.
Agent-native CLI contract
Every script under scripts/ shares a uniform contract enforced via scripts/_envelope.py:
--format json|table— auto-JSON when stdout is not a TTY, else table (legacy prose). SetDAISY_FORCE_JSON=1to force JSON regardless of TTY.--schema— emits the script's full parameter/output/error schema as a JSON envelope. Add new params and updateSCHEMAin the same script in lockstep —--schemais the agent's primary discovery surface, not--help.--dry-run— preview the request shape; never call upstream APIs or write files. Implemented on every mutating script.- Exit codes:
0ok ·1runtime ·2auth ·3validation ·4no_data ·5dependency. Documented in each--helpepilog. - Success envelope:
{"ok": true, "data": ..., "meta": {schema_version, request_id, latency_ms}}. - Error envelope:
{"ok": false, "error": {code, message, retryable, context}, "meta": {...}}. _envelope.emit_progress(event, **fields)writes one NDJSON line to stderr — used by long-running operations (screen_hk_connect.py --with-momentum,financial_report.py) so agents can detect liveness.
When adding a new script: import from _envelope, define a SCHEMA dict, call add_common_args(parser), and route success/error through emit_success / emit_failure. Keep the human table render as a table_render callback so --format table users see no regression.
scripts/_envelope.py::SCHEMA_VERSION is the contract version exposed to agents in every meta block. Bump it (semver) when the envelope shape changes in a way that breaks downstream parsers.
Tests (local-only, not in the published artifact)
tests/ is gitignored — the contract suite lives on disk for local development but is not part of the published skill that users git clone into their .claude/skills/ (or equivalent) directory. To run it locally:
uv sync --all-extras
uv run pytest tests/ # 131 tests, ~12 s, no Tushare token, no networkCoverage: --help / --schema / --dry-run invariants across all 8 scripts, validation/no_data error envelopes, DAISY_FORCE_JSON override, full memory-log lifecycle (record idempotency → resolve atomic rewrite → list/context/stats/backtest), on-disk format wire-compatibility with TradingAgents memory.py, plus compute-returns / auto-resolve dry-run + validation paths, technical_indicators market-routing dry-run, akshare_hk_valuation name local-dict lookup, backtest aggregate math (mean alpha, hit rate, t-stat, annualized alpha, cumulative-alpha drawdown, window/rating filters), and record --rating tolerant extraction (canonical word, markdown bold, lowercase, full synthesis paragraph, plus rejection of input with no 5-tier word). See tests/README.md. Run before committing any change to scripts/.
Tushare gotchas (verified in this env)
pro.hk_daily_basic(...)returns请指定正确的接口名— treat as unavailable. Fallback:scripts/akshare_hk_valuation.py valuation --ts-code <code>covers PE/PB/PS snapshot;... fundamentals --ts-code <code>covers ROE/EPS/BPS time series.pro.hk_basic,pro.hk_daily,pro.hk_hold,pro.ggt_top10,pro.ggt_daily,pro.moneyflow_hsgtare known-working.- Date format is
YYYYMMDDstrings (notYYYY-MM-DD), ts_codes are000001.SZ/600000.SH/00005.HK.
The full per-market routing table (A-share / HK / US, primary + documented fallback chain for each data type) lives at references/data-source-routing.md. New scripts that route across markets should reference it from their --help epilog or SCHEMA["data_sources"] block instead of duplicating the routing rules.
Search routing (do not change without user sign-off)
The skill commits to a specific finance-search stack: Tushare for structured data, Brave MCP as primary web search, Bailian WebSearch MCP as Chinese/China-market supplement, Python for math, browser only for dynamic pages. Asta/Semantic Scholar is explicitly not part of the finance route.
Bank/financial-sector valuation
For banks (HSBC etc.), DCF is the wrong primary frame. Use RoTE/ROE, CET1, payout/yield, NIM/NII, credit cost, P/B or P/E, buyback capacity. The HSBC test workflow and pitfalls are recorded in references/hsbc-hk-bank-research-test-20260429.md — consult before changing bank-related logic.
# Runtime manifest for daisy-financial-research.
#
# This project is a multi-platform agent SKILL — the user-facing artifact is
# scripts/, references/, and SKILL.md, not a Python package. The pyproject
# exists so contributors can reproduce the runtime environment with uv:
#
# uv sync # install runtime deps (tushare/pandas/numpy)
# uv sync --extra akshare # also install AKShare HK fallback
# uv sync --extra us # also install yfinance for US tickers
# uv sync --extra ta # also install stockstats for technical_indicators.py
# uv sync --all-extras # everything
[project]
name = "daisy-financial-research"
version = "2.8.0"
description = "Multi-platform agent skill for stock / company / sector research"
readme = "README.md"
requires-python = ">=3.11"
license = { text = "MIT" }
authors = [{ name = "Agents365-ai", url = "https://space.bilibili.com/1107534197" }]
# Runtime requirements that every script in scripts/ assumes are present.
dependencies = [
"pandas>=2.0",
"numpy>=1.24",
"tushare>=1.4",
"requests>=2.31",
]
[project.optional-dependencies]
# AKShare HK valuation/fundamentals fallback (akshare_hk_valuation.py)
# and the HSI benchmark fallback for dexter_memory_log auto-resolve.
akshare = ["akshare>=1.18"]
# US tickers in dexter_memory_log compute-returns / auto-resolve.
us = ["yfinance>=0.2"]
# Technical indicators (technical_indicators.py); pandas-only computation.
ta = ["stockstats>=0.6"]
[project.urls]
Homepage = "https://github.com/Agents365-ai/daisy-financial-research"
Repository = "https://github.com/Agents365-ai/daisy-financial-research"
Daisy 金融研究
给 AI agent 用的股票研究 skill。计划 → 取数 → 校验 → 出报告。覆盖 A 股、港股、美股。
!Daisy 投研智能体工作流
---
你最终拿到什么
把一只股票、一个板块或一个主题丢给 agent,daisy 把它变成一个有结构的分析师工作流,并且留下一份你随时能回头查的审计轨迹。
落到磁盘上的产出 (默认全部在当前目录的 ./financial-research/ 下):
- `reports/<时间戳>_<slug>.{md,html,pdf}` —— 带来源引用的研究报告 (Markdown 源 + 浏览器即开即看的 HTML + 可选 PDF,CSS 已处理中英文字体回退)
- `watchlists/<时间戳>_<preset>.{csv,json}` —— 多因子筛选输出:股息质量、价值、成长、动量、港股通等
- `scratchpad/<时间戳>.jsonl` —— 这次任务里 agent 调用的每一个工具、参数、原始结果、假设。可重复
- `memory/decision-log.md` —— 跨会话的追加式决策日志,每一笔 Buy / Overweight / Hold / Underweight / Sell 评级都有
pending → resolved生命周期,每个 closed 条目带 2-4 句话的反思 - `universes/<日期>_hk-connect-universe.csv` —— 南向港股通 universe 快照
工作流是怎么跑的
当用户说"深度研究茅台"/"给汇丰做个 DCF"/"筛一批 A 股股息质量股"时:
1. 拉记忆。 dexter_memory_log.py context --ticker <X> 把同一只票过去的研究记录 (含已 resolve 的实际超额收益) 注入到 plan 步骤。让过去的错误指导这次决策。 2. 写计划。 Agent 在动任何数据源之前,先把 3-7 步的研究计划写进每任务一份的 JSONL scratchpad。 3. 路由取数。 按代码后缀分流:*.SH/SZ/BJ → Tushare pro.daily / pro.daily_basic / pro.fina_indicator / pro.income 等;*.HK → Tushare pro.hk_daily 加上 AKShare 兜底 (因为 pro.hk_daily_basic 在本环境返回 请指定正确的接口名);裸代码 → yfinance。完整的主源 + 兜底链表见 references/data-source-routing.md。 4. 软循环上限。 每次工具调用前,dexter_scratchpad.py can-call <tool> <query> 会预警两种典型故障:(a) 同一个工具本任务已经被调用 ≥ N 次;(b) 这次的查询和之前某次很像。这是软警告不是硬阻断——agent 自己决定怎么应对。 5. 算估值。 普通公司:DCF + 敏感性矩阵。银行/保险/金融板块:daisy 自动跳过 DCF,改用 RoTE / CET1 / NIM / P/B / 派息率——DCF 对金融股是错的口径,但原生 agent 经常在这上面翻车。技术指标 (SMA / MACD / RSI / Bollinger / ATR) 通过 scripts/technical_indicators.py,内置 look-ahead-bias 守卫:Date > --as-of 的行在 stockstats 拿到数据之前就被裁掉了,回测看不到未来。 6. 数值校验。 硬性 checklist:单位、币种、期间口径、每股分母、市值日期、排名 universe。不通过就显式标出来,不会偷偷糊弄。 7. 多空辩论 (可选,给单股深度报告用)。 三段式 prompt 模板 + 明确的轮次状态机;synthesis 输出用规范的 5 档评级,能直接落进决策日志。 8. 出报告。 scripts/financial_report.py 把 Markdown 源渲染成 HTML,可选再到 PDF。规整的章节结构:scope → data → price/valuation → financial drivers → news/catalysts → bull/base/bear → risks → evidence tables → 免责声明。 9. 写决策日志。 最终评级以 pending 状态记入决策日志。日后 dexter_memory_log.py auto-resolve 自动取 decision_date 和 as_of_date 的收盘价、按市场选对应基准 (A 股 → CSI 300、港股 → HSI 经 AKShare Sina 兜底、美股 → SPY)、算实际 alpha + 持仓天数,并把 entry 改写为 resolved + 写入反思。 10. 战绩审计。 dexter_memory_log.py backtest 在指定窗口聚合所有已 resolve 条目:每 rating 桶的均值 alpha、命中率、alpha_t_stat、年化 alpha、Sortino-flavored 比率,加上累计 alpha 曲线和它的最大回撤。故意不叫 Sharpe——日志记的是离散决策不是连续 NAV,命名上就把这点说清楚。
它和你自己组合工具的差别在哪
| 关注点 | 自己组合 | 用 daisy |
|---|---|---|
| 取数前先写计划 | 经常跳过 | 永远先写——JSONL scratchpad 落盘 |
| 同一个端点被调 5 次只换一点点参数 | 经典翻车 | can-call 在调用之前就警告 (difflib,无 embedding 依赖) |
| 同一只票上次研究的结论 | 跨会话忘个干净 | memory_log context 在 plan 时自动注入 |
| 银行用 DCF 估值 (口径错) | 看运气 | 自动改用 RoTE / CET1 / NIM / P/B |
pro.hk_daily_basic 接口"消失" | 突发故障 | 已记录的 gap,AKShare 兜底已接好 |
| 技术指标里的 look-ahead bias | 容易悄无声息地引入 | Date > --as-of 的行在 stockstats 拿到之前就裁掉 |
LLM 输出 **Rating**: Buy 而不是规范 Buy | 静默落到 Hold 默认值 | 容错抽取;完全没 5 档评级词时显式拒绝 |
| 50 笔历史决策的命中率 | 手工 Excel | memory_log backtest (alpha t-stat、命中率、最大回撤) |
| 带来源的研报排版 (中英文字体、表格、敏感性矩阵、注脚) | 每次手工调 | 一行命令出三层产物 |
| Agent 集成 (JSON envelope、schema 内省、dry-run) | 自己写一堆 subprocess 胶水 | 每个脚本内置——基于 error.code 分支,无需解析 prose |
快速开始
export TUSHARE_TOKEN=... # 任何 A 股/港股 Tushare 调用都需要
# A 股股息质量 watchlist + 渲染成报告
python <skill-dir>/scripts/screen_a_share.py --preset a_dividend_quality --top 50 --report
python <skill-dir>/scripts/financial_report.py ./financial-research/reports/<latest>.md \
--title "A 股股息 watchlist" --slug a-div --pdf
# 时点安全的技术指标 (look-ahead-bias 守卫)
python <skill-dir>/scripts/technical_indicators.py \
--ts-code 600519.SH --as-of 20260415 --indicators rsi,macd,boll
# 港股 ticker → 中文名零 API 本地查询
python <skill-dir>/scripts/akshare_hk_valuation.py name --ts-code 00700.HK
# 审计自己的决策战绩
python <skill-dir>/scripts/dexter_memory_log.py backtest任何脚本都接受 --out-dir <root> 来覆盖默认的 ./financial-research/ 目录。
安装
| 平台 | 全局 | 项目级 |
|---|---|---|
| Claude Code | git clone https://github.com/Agents365-ai/daisy-financial-research.git ~/.claude/skills/daisy-financial-research | git clone ... .claude/skills/daisy-financial-research |
| Opencode | git clone ... ~/.config/opencode/skills/daisy-financial-research | git clone ... .opencode/skills/daisy-financial-research |
| OpenClaw / ClawHub | clawhub install daisy-financial-research | git clone ... skills/daisy-financial-research |
| Hermes | git clone ... ~/.hermes/skills/research/daisy-financial-research | 通过 ~/.hermes/config.yaml 的 external_dirs |
| OpenAI Codex | git clone ... ~/.agents/skills/daisy-financial-research | git clone ... .agents/skills/daisy-financial-research |
| SkillsMP | skills install daisy-financial-research | — |
# 核心
pip install tushare pandas requests
# 可选 extras
pip install akshare # 港股 PE/PB/PS + ROE/EPS 兜底 (无需 Tushare token)
pip install yfinance # 美股 ticker (technical_indicators / auto-resolve)
pip install stockstats # technical_indicators.py
# PDF 输出
brew install pandoc && brew install --cask basictex或者直接 uv sync --all-extras。
脚本一览
| 脚本 | 作用 |
|---|---|
dexter_scratchpad.py | 单任务 JSONL,记录每次工具调用。can-call 子命令在调用前预警重复 |
dexter_memory_log.py | 跨会话决策日志:record / resolve / list / context / stats / backtest / compute-returns / auto-resolve |
screen_a_share.py | A 股多因子筛选 (预设驱动:股息、价值、质量、动量) |
screen_hk_connect.py | 港股通筛选 (仅在用户明确要求 港股通 时使用) |
hk_connect_universe.py | 南向港股通 universe 导出,自带日期回填 |
akshare_hk_valuation.py | 港股 PE/PB/PS + ROE/EPS/BPS via AKShare;name 子命令做零 API 本地字典查询 |
technical_indicators.py | 时点安全的 SMA/EMA/MACD/RSI/Bollinger/ATR/VWMA,含 look-ahead-bias 守卫 |
financial_report.py | Markdown → HTML → 可选 PDF 报告渲染,CSS 已含中英文字体回退 |
Agent-native CLI 契约——每个脚本都支持:
--schema——给 agent 内省的 JSON 参数规格 (优先于解析--help)--dry-run——预演请求形状,不调用上游 API、不写文件--format json|table——stdout 不是 TTY 时自动 JSON;DAISY_FORCE_JSON=1强制- 结构化退出码:
0成功 ·1运行时 ·2认证 ·3参数 ·4无数据 ·5依赖 - 稳定的成功/错误 envelope:
{ok, data, meta}/{ok: false, error: {code, message, retryable, context}, meta}
参考文档
Agent 在工作流需要的时候按需读取 references/ 下的这些文档:
data-source-routing.md——三市场数据源路由表 (主源 + 兜底链)hk-ticker-name.json——港股 ticker → 中文名字典stock-screening-presets.md——筛选预设注册表technical-indicator-cheatsheet.md——11 个指标的选用指南debate-prompts.md/risk-debate-prompts.md——多空/综合 + 激进/保守/中立辩论模板,含明确的轮次 Loop specdecision-schema.md——5 档评级词表 + Markdown 输出契约reflection-prompt.md——固定形状的反思 promptcn-market-analyst-prompts.md——A 股/港股市场分析框架 (涨跌停 / 北向资金 / 板块轮动)position-sizing.md、hsbc-hk-bank-research-test-20260429.md——仓位推荐配方 + 银行估值实战
自动更新
技能在每次会话首次调用时检查 <skill-dir>/.last_update,超过 24 小时则静默 git pull --ff-only。失败 (离线 / 冲突 / 非 git checkout) 不打断流程。
免责声明
本技能仅产出数据分析和研究记录,不构成投资建议。所有结论需结合最新公开信息独立判断。
支持作者
如果这个 skill 对你有帮助,欢迎支持作者:
<table> <tr> <td align="center"> <img src="https://raw.githubusercontent.com/Agents365-ai/images_payment/main/qrcode/wechat-pay.png" width="180" alt="微信支付"> <br> <b>微信支付</b> </td> <td align="center"> <img src="https://raw.githubusercontent.com/Agents365-ai/images_payment/main/qrcode/alipay.png" width="180" alt="支付宝"> <br> <b>支付宝</b> </td> <td align="center"> <img src="https://raw.githubusercontent.com/Agents365-ai/images_payment/main/qrcode/buymeacoffee.png" width="180" alt="Buy Me a Coffee"> <br> <b>Buy Me a Coffee</b> </td> <td align="center"> <img src="https://raw.githubusercontent.com/Agents365-ai/images_payment/main/awarding/award.gif" width="180" alt="打赏"> <br> <b>打赏</b> </td> </tr> </table>
作者
- Bilibili: https://space.bilibili.com/1107534197
- GitHub: https://github.com/Agents365-ai
Daisy Financial Research
Stock research skill for AI agents. Plan → fetch → validate → report. A-share, HK, US.
!Daisy investment research workflow
---
What you get
Point an agent at a stock, sector, or theme. Daisy turns it into the structured workflow of an analyst — and leaves an audit trail you can come back to.
Tangible deliverables, all written under ./financial-research/ in your cwd:
- `reports/<ts>_<slug>.{md,html,pdf}` — the sourced research note (markdown source + browser-ready HTML + optional PDF, CN/EN fonts handled).
- `watchlists/<ts>_<preset>.{csv,json}` — multi-factor screener output: dividend quality, value, growth, momentum, HK Stock Connect, and so on.
- `scratchpad/<ts>.jsonl` — every tool call, parameter, raw result, and assumption the agent made on this task. Replayable.
- `memory/decision-log.md` — append-only Markdown log of every Buy / Overweight / Hold / Underweight / Sell call across sessions, with
pending → resolvedlifecycle and a 2-4 sentence reflection on each closed call. - `universes/<date>_hk-connect-universe.csv` — Southbound Stock Connect (港股通) universe snapshot.
How the workflow runs
When a user asks "deep-dive on Mao Tai" / "DCF for HSBC" / "find me A-share dividend names with quality":
1. Memory pull. dexter_memory_log.py context --ticker <X> injects past calls on the same ticker (with realized alpha) into the plan step. Past mistakes inform the current call. 2. Plan. The agent writes a 3–7 step plan into a per-task JSONL scratchpad before touching any data source. 3. Data routing. Suffix-based: *.SH/SZ/BJ → Tushare pro.daily / pro.daily_basic / pro.fina_indicator / pro.income / etc.; *.HK → Tushare pro.hk_daily plus AKShare for the documented pro.hk_daily_basic gap; bare US ticker → yfinance. The full primary + fallback table lives in references/data-source-routing.md. 4. Soft loop limits. Before each tool call, dexter_scratchpad.py can-call <tool> <query> flags two failure modes: (a) the same tool already called ≥ N times this task, (b) a textually similar query was already issued. It's a warning, not a block — the agent decides what to do. 5. Computation. DCF with sensitivity matrix for normal companies. For banks / insurers / financial-sector names, daisy automatically skips DCF and uses RoTE / CET1 / NIM / P/B / payout instead — DCF is the wrong frame for them, but most native agents miss this. Technical indicators (SMA / MACD / RSI / Bollinger / ATR) via scripts/technical_indicators.py, with a look-ahead-bias guard built in: rows newer than --as-of are dropped before the indicator engine sees the data, so backtests cannot peek at the future. 6. Numerical validation. Hard checklist: units, currency, period, per-share denominators, market-cap date, ranking universe. Failures are surfaced, not silently fudged. 7. Bull / Bear / Synthesis (optional, for balanced single-name research). Three-prompt template with an explicit round-counter loop spec; the synthesis output uses the canonical 5-tier rating that drops straight into the memory log. 8. Report. scripts/financial_report.py renders the Markdown source to HTML, then optionally to PDF. Structured sections: scope → data → price/valuation → financial drivers → news/catalysts → bull/base/bear → risks → evidence tables → disclaimer. 9. Decision log. The final call is recorded as a pending entry. Later, dexter_memory_log.py auto-resolve fetches close prices on decision_date and as_of_date, picks the right benchmark (CSI 300 for A-share, HSI for HK with AKShare Sina fallback, SPY for US), computes realized alpha + holding days, and closes the entry with the agent's reflection. 10. Track-record audit. dexter_memory_log.py backtest aggregates resolved entries over a window: per-rating mean alpha, hit rate, alpha t-stat, annualized alpha, Sortino-flavored ratio, plus the cumulative-alpha curve and its max drawdown. Honestly not called Sharpe — daisy logs decisions, not a continuous NAV.
What's actually different
| Concern | Rolling your own | With daisy |
|---|---|---|
| Plan written down before data calls | Often skipped | Always — JSONL scratchpad on disk |
| Same endpoint hit 5× with similar args | Common failure mode | can-call warns before it happens (difflib-based, no embeddings) |
| Past calls on the same ticker | Forgotten across sessions | memory_log context injects them at plan time |
| Bank valued via DCF (wrong frame) | Hit-or-miss | Auto-override to RoTE / CET1 / NIM / P/B |
pro.hk_daily_basic returns 请指定正确的接口名 | Surprise outage | Documented gap with AKShare fallback wired |
| Look-ahead bias in technical indicators | Easy to introduce silently | Rows newer than --as-of filtered before stockstats sees them |
LLM emits **Rating**: Buy instead of Buy | Silent default to Hold | Tolerant extraction; loud rejection if no 5-tier word found |
| Hit rate across 50 prior calls | Manual spreadsheet | memory_log backtest (alpha t-stat, hit rate, max-DD) |
| Sourced report (CN+EN fonts, tables, sensitivity matrix, footnotes) | Manual every time | One command, three layers |
| Agent integration (JSON envelope, schema introspection, dry-run) | Manual subprocess plumbing | Built into every script — branch on error.code, not parsed prose |
Quick start
export TUSHARE_TOKEN=... # required for any A-share/HK Tushare call
# A-share dividend-quality watchlist + a rendered report
python <skill-dir>/scripts/screen_a_share.py --preset a_dividend_quality --top 50 --report
python <skill-dir>/scripts/financial_report.py ./financial-research/reports/<latest>.md \
--title "A-share dividend watchlist" --slug a-div --pdf
# Point-in-time technical indicators (look-ahead-bias guarded)
python <skill-dir>/scripts/technical_indicators.py \
--ts-code 600519.SH --as-of 20260415 --indicators rsi,macd,boll
# Zero-API HK ticker → Chinese name lookup
python <skill-dir>/scripts/akshare_hk_valuation.py name --ts-code 00700.HK
# Audit your decision track record
python <skill-dir>/scripts/dexter_memory_log.py backtestEvery script accepts --out-dir <root> to override the default ./financial-research/ location.
Installation
| Platform | Global | Project |
|---|---|---|
| Claude Code | git clone https://github.com/Agents365-ai/daisy-financial-research.git ~/.claude/skills/daisy-financial-research | git clone ... .claude/skills/daisy-financial-research |
| Opencode | git clone ... ~/.config/opencode/skills/daisy-financial-research | git clone ... .opencode/skills/daisy-financial-research |
| OpenClaw / ClawHub | clawhub install daisy-financial-research | git clone ... skills/daisy-financial-research |
| Hermes | git clone ... ~/.hermes/skills/research/daisy-financial-research | via external_dirs in ~/.hermes/config.yaml |
| OpenAI Codex | git clone ... ~/.agents/skills/daisy-financial-research | git clone ... .agents/skills/daisy-financial-research |
| SkillsMP | skills install daisy-financial-research | — |
# Core
pip install tushare pandas requests
# Optional extras
pip install akshare # HK PE/PB/PS + ROE/EPS fallback (no Tushare token)
pip install yfinance # US tickers (technical_indicators / auto-resolve)
pip install stockstats # technical_indicators.py
# PDF rendering
brew install pandoc && brew install --cask basictexOr with uv: uv sync --all-extras.
Scripts at a glance
| Script | Purpose |
|---|---|
dexter_scratchpad.py | Per-task JSONL of every tool call. can-call warns before repeat calls |
dexter_memory_log.py | Cross-session decision log: record / resolve / list / context / stats / backtest / compute-returns / auto-resolve |
screen_a_share.py | A-share multi-factor screener with named presets (dividend, value, quality, momentum) |
screen_hk_connect.py | HK Stock Connect screener (only when 港股通 is explicitly requested) |
hk_connect_universe.py | Southbound Stock Connect universe export with date back-fill |
akshare_hk_valuation.py | HK PE/PB/PS + ROE/EPS/BPS via AKShare; name for zero-API local-dict lookup |
technical_indicators.py | Point-in-time SMA/EMA/MACD/RSI/Bollinger/ATR/VWMA, look-ahead-bias guarded |
financial_report.py | Markdown → HTML → optional PDF report renderer with CN/EN font fallback |
Agent-native CLI contract — every script supports:
--schema— JSON parameter spec for agent introspection (preferred over--help)--dry-run— preview the request shape without any upstream call or file write--format json|table— auto-JSON when stdout is not a TTY;DAISY_FORCE_JSON=1to override- Structured exit codes:
0ok ·1runtime ·2auth ·3validation ·4no_data ·5dependency - Stable success / error envelopes:
{ok, data, meta}/{ok: false, error: {code, message, retryable, context}, meta}
Reference docs
The agent reads these from references/ when the workflow needs them:
data-source-routing.md— canonical (market × data type) routing tablehk-ticker-name.json— curated HK ticker → Chinese-name dictstock-screening-presets.md— registry of screening presetstechnical-indicator-cheatsheet.md— 11-indicator selection guidedebate-prompts.md/risk-debate-prompts.md— Bull/Bear/Synthesis + Aggressive/Conservative/Neutral templates with explicit loop specsdecision-schema.md— 5-tier rating vocabulary + markdown render contractreflection-prompt.md— fixed-shape reflection prompt for memory-log resolvecn-market-analyst-prompts.md— China-market analyst framing (涨跌停 / 北向资金 / 板块轮动)position-sizing.md,hsbc-hk-bank-research-test-20260429.md— sizing recipe + bank valuation worked example
Auto-update
The skill checks <skill-dir>/.last_update once per conversation. If the file is missing or older than 24 hours, daisy silently runs git pull --ff-only. Failures (offline, conflict, not a git checkout) are ignored without interrupting the workflow.
Disclaimer
Data analysis and research records, not investment advice. All conclusions require independent judgement against the latest public information.
Support
If this skill helps you, consider supporting the author:
<table> <tr> <td align="center"> <img src="https://raw.githubusercontent.com/Agents365-ai/images_payment/main/qrcode/wechat-pay.png" width="180" alt="WeChat Pay"> <br> <b>WeChat Pay</b> </td> <td align="center"> <img src="https://raw.githubusercontent.com/Agents365-ai/images_payment/main/qrcode/alipay.png" width="180" alt="Alipay"> <br> <b>Alipay</b> </td> <td align="center"> <img src="https://raw.githubusercontent.com/Agents365-ai/images_payment/main/qrcode/buymeacoffee.png" width="180" alt="Buy Me a Coffee"> <br> <b>Buy Me a Coffee</b> </td> <td align="center"> <img src="https://raw.githubusercontent.com/Agents365-ai/images_payment/main/awarding/award.gif" width="180" alt="Give a Reward"> <br> <b>Give a Reward</b> </td> </tr> </table>
Author
- Bilibili: https://space.bilibili.com/1107534197
- GitHub: https://github.com/Agents365-ai
China Market Analyst Prompts
Two prompts for adding A-share / 港股 framing to a research report. Adapted from TradingAgents-CN/tradingagents/agents/analysts/china_market_analyst.py (system message and screener prompt). The TA-CN data-loading and tool-binding code is dropped — daisy uses Tushare directly via its own scripts. Only the prompt text is borrowed; it's the part that's actually portable.
When to use
- Single-name research on an A-share or 港股 ticker where the report needs proper local-market context (涨跌停 risk, ST flag, 北向资金 stance, 板块 rotation, 监管 backdrop).
- Stock screening for the Chinese market where you want a structured prompt rather than free-form criteria.
Skip when
- US / non-Chinese tickers — these prompts assume A-share and HK market mechanics.
- Pure quant screens — daisy's own preset screeners (
screen_a_share.py,screen_hk_connect.py) already cover the mechanical filtering. Use this prompt only for the interpretation layer on top.
Prompt 1 — China Market Analyst (single-name framing)
Use this prompt to draft the "Sector / market context" portion of the §6 report or as the first sub-turn in a multi-step analysis. Output is Chinese prose by design — it's what the local-market reader expects.
您是一位专业的中国股市分析师,专门分析A股、港股等中国资本市场。您具备深厚的中国股市知识和丰富的本土投资经验。
您的专业领域包括:
1. **A股市场分析**:深度理解A股的独特性,包括涨跌停制度、T+1交易、融资融券等
2. **中国经济政策**:熟悉货币政策、财政政策对股市的影响机制
3. **行业板块轮动**:掌握中国特色的板块轮动规律和热点切换
4. **监管环境**:了解证监会政策、退市制度、注册制等监管变化
5. **市场情绪**:理解中国投资者的行为特征和情绪波动
分析重点:
- **技术面分析**:使用结构化数据进行精确的技术指标分析
- **基本面分析**:结合中国会计准则和财报特点进行分析
- **政策面分析**:评估政策变化对个股和板块的影响
- **资金面分析**:分析北向资金、融资融券、大宗交易等资金流向
- **市场风格**:判断当前是成长风格还是价值风格占优
中国股市特色考虑:
- 涨跌停板限制对交易策略的影响
- ST股票的特殊风险和机会
- 科创板、创业板的差异化分析
- 国企改革、混改等主题投资机会
- 中美关系、地缘政治对中概股的影响
当前分析日期:{trade_date},分析标的:{ticker}({name})。
可用数据:
- Tushare 行情与基本面:{market_data}
- 公司公告 / 新闻:{news}
- 行业板块上下文:{sector_context}
- 历史决策(来自跨会话记忆库):{past_context}
请基于上述数据,结合中国股市的特殊性,撰写专业的中文分析报告。
确保在报告末尾附上 Markdown 表格,总结关键发现和投资建议。Prompt 2 — China Stock Screener (interpretation layer)
Use this prompt to take the output of screen_a_share.py (CSV / JSON of candidates) and turn it into a narrative shortlist with rationale per pick. Pairs with the existing templates/screening_report.md.
您是一位专业的中国股票筛选专家,负责从给定的候选池中筛选出具有投资价值的股票。
筛选维度包括:
1. **基本面筛选**:
- 财务指标:ROE、ROA、净利润增长率、营收增长率
- 估值指标:PE、PB、PEG、PS 比率
- 财务健康:资产负债率、流动比率、速动比率
2. **技术面筛选**:
- 趋势指标:均线系统、MACD、KDJ
- 动量指标:RSI、威廉指标、CCI
- 成交量指标:量价关系、换手率
3. **市场面筛选**:
- 资金流向:主力资金净流入、北向资金偏好
- 机构持仓:基金重仓、社保持仓、QFII 持仓
- 市场热度:概念板块活跃度、题材炒作程度
4. **政策面筛选**:
- 政策受益:国家政策扶持行业
- 改革红利:国企改革、混改标的
- 监管影响:监管政策变化的影响
筛选策略(任选其一作为主轴):
- **价值投资**:低估值、高分红、稳定增长
- **成长投资**:高增长、新兴行业、技术创新
- **主题投资**:政策驱动、事件催化、概念炒作
- **周期投资**:经济周期、行业周期、季节性
输入:
- 候选池(来自 daisy 筛选脚本):{candidate_list}
- 当前日期与市场环境:{trade_date}
- 用户偏好的策略主轴:{strategy_axis}
请基于当前市场环境和政策背景,从候选池中挑选 3–8 只重点关注的标的,并对每只给出:
1. 选中理由(具体到 2–3 个最关键指标 / 政策 / 资金面信号)
2. 红旗 (red flags) 和需要进一步核验的点
3. 跟踪建议(下次什么事件 / 数据点会改变看法)
最后用 Markdown 表格汇总选中标的的核心指标。Integration
- Hook into SKILL.md §3 ("Tool/data routing policy") as the interpretation layer that runs after Tushare data fetch.
- The single-name prompt (#1) feeds into report §6 ("News/catalyst review") as the China-context block.
- The screener prompt (#2) consumes
screen_a_share.pyoutput and produces the narrative fortemplates/screening_report.md.
Why borrow only the prompts
TA-CN's china_market_analyst.py is ~250 lines, but the load-bearing parts for an LLM-driven workflow are these two ~30-line system messages. The remaining 190 lines are LangGraph state plumbing, Google-tool-call handling, logging, and tool-name extraction — none of which apply to daisy. The prompts themselves are clean, tightly scoped, and translate intact.
Source
TradingAgents-CN/tradingagents/agents/analysts/china_market_analyst.pylines 113–138 (analyst system message) and 222–252 (screener system message).
Data-source routing reference
The canonical "where do I look first?" table for daisy-financial-research, indexed by (market × data type). When two sources can both answer a question, this doc fixes the order; when the primary source has a known gap, the fallback is documented inline.
This is the formal version of the routing logic that already lives in SKILL.md §3 and CLAUDE.md "Tushare gotchas" / "Search routing" — consolidated so an agent doesn't have to cross-reference both.
Adapted from hsliuping/TradingAgents-CN:tradingagents/dataflows/providers/{china,hk,us}/. The MongoDB cache, BaoStock provider, Finnhub adapter, and database-driven priority configs are intentionally not ported — they belong to a hosted product, not a portable skill.
Hard rules
1. Search-routing stack is committed. Tushare for structured data, Brave MCP as primary web search, Bailian WebSearch MCP as Chinese / China-market supplement, Python for math, browser only for dynamic pages. Asta / Semantic Scholar is explicitly not on the finance route. Do not change without user sign-off. 2. Tushare ts_codes use `YYYYMMDD` strings, not YYYY-MM-DD. Tickers are 000001.SZ / 600000.SH / 00005.HK. 3. Look-ahead bias is a correctness bug. Any historical / backtest call must filter rows where Date > as_of before downstream computation. scripts/technical_indicators.py already does this; new scripts must too. 4. Lazy-import optional deps so --help / --schema / --dry-run work without the upstream dep installed. Surface missing deps as dependency_missing (exit 5), not runtime_error.
A-share (*.SH / *.SZ / *.BJ)
| Data type | Primary | Fallback | Notes |
|---|---|---|---|
| Daily OHLCV | tushare.pro.daily | (no fallback today; AKShare stock_zh_a_hist is a candidate but unimplemented) | Reliable. daily(ts_code, start_date, end_date). |
| Daily valuation (PE/PB/total_mv) | tushare.pro.daily_basic | — | Use fields= to limit payload. |
| Income / balance / cash flow | tushare.pro.income / pro.balancesheet / pro.cashflow | — | Period as YYYYMMDD quarter-end. |
| Financial ratios | tushare.pro.fina_indicator | — | ROE / ROA / margin / leverage / growth. |
| Forecast / express | tushare.pro.forecast / pro.express | — | Earnings surprise driver. |
| Northbound / Stock Connect flow | tushare.pro.moneyflow_hsgt | — | Aggregate inflow per day. |
| Stock universe | tushare.pro.stock_basic(list_status='L') | — | Filter by exchange / industry / market cap. |
| Index daily | tushare.pro.index_daily (000300.SH) | — | Used as benchmark for dexter_memory_log auto-resolve. |
| Concept / ETF | tushare.pro.concept / pro.fund_basic | — | |
| Technical indicators | scripts/technical_indicators.py (auto-routes via pro.daily) | — | SMA / MACD / RSI / Bollinger / ATR / VWMA via stockstats. |
| Operating segments (主营构成) | scripts/segments.py (AKShare stock_zygc_em) | — | Rows by 分类类型: 按产品 / 按地区 / 按行业. Free, no token. |
| Screening | scripts/screen_a_share.py | — | Preset registry in references/stock-screening-presets.md. |
| Web context | Brave MCP | Bailian WebSearch MCP | Use Bailian for China-only news / regulation. |
Auth: TUSHARE_TOKEN env var. Missing token → auth_missing (exit 2).
Hong Kong (*.HK)
| Data type | Primary | Fallback | Notes |
|---|---|---|---|
| Daily OHLCV | tushare.pro.hk_daily | — | hk_daily(ts_code='00005.HK', start_date, end_date). |
| Stock basic / list | tushare.pro.hk_basic | — | |
| Daily valuation (PE/PB/PS) | ⚠️ tushare.pro.hk_daily_basic returns 请指定正确的接口名 in the test env — treat as unavailable | scripts/akshare_hk_valuation.py valuation --ts-code <code> (AKShare stock_hk_valuation_comparison_em + stock_hk_security_profile_em) | The fallback is the documented primary today. No Tushare token. |
| Fundamentals (ROE/EPS/BPS/leverage time series) | scripts/akshare_hk_valuation.py fundamentals (AKShare stock_financial_hk_analysis_indicator_em) | — | Period: 年度 / 中报 / 季报. |
| Chinese short name | AKShare row 简称 | references/hk-ticker-name.json (~30 majors) → '' | akshare_hk_valuation.py automatically falls through this chain; emits `name_source: akshare \ |
| Stock Connect (港股通) universe | tushare.pro.hk_hold(trade_date='YYYYMMDD') | — | Search backward for trading days; scripts/hk_connect_universe.py automates this. |
| Stock Connect flow | tushare.pro.ggt_top10 / pro.ggt_daily / pro.moneyflow_hsgt | — | |
| HSI benchmark | tushare.pro.index_daily | tushare.pro.index_global → tushare.pro.hk_daily → AKShare stock_hk_index_daily_sina | dexter_memory_log auto-resolve walks this chain automatically. |
| Stock Connect screening | scripts/screen_hk_connect.py (only when 港股通 is explicitly requested) | — | |
| Technical indicators | scripts/technical_indicators.py (auto-routes via pro.hk_daily) | — | |
| Operating segments | (no free structured API) | read_filings / Brave MCP on the annual report's "Segment Information" note | scripts/segments.py --ts-code <code>.HK short-circuits to no_data (exit 4) with a hint pointing here. |
| Web context | Brave MCP | Bailian WebSearch MCP |
Auth: TUSHARE_TOKEN for Tushare endpoints; AKShare needs no token. Missing token → auth_missing (exit 2) when the call is Tushare-only.
Bank-specific note. For HSBC / Standard Chartered / mainland banks listed in HK, prefer the AKShare fundamentals path for RoTE / RoE / leverage; DCF is the wrong primary frame. See SKILL.md §10 and references/hsbc-hk-bank-research-test-20260429.md.
US (bare ticker, no suffix)
| Data type | Primary | Fallback | Notes |
|---|---|---|---|
| Daily OHLCV | yfinance.download (lazy-imported, optional us extra) | — | Used by scripts/technical_indicators.py and scripts/dexter_memory_log.py compute-returns for US tickers. |
| SPY benchmark | yfinance.download('SPY') | — | dexter_memory_log auto-resolve benchmark. |
| Fundamentals | yfinance Ticker(...).financials (not currently used by daisy scripts) | — | If you need it, prefer pulling structured data from filings via Brave MCP search → SEC links. |
| Technical indicators | scripts/technical_indicators.py | — | |
| Operating segments | (no free structured API) | read_filings / Brave MCP on the latest 10-K Note "Segment Reporting" | scripts/segments.py --ts-code AAPL short-circuits to no_data (exit 4) with a hint pointing here. |
| Web context | Brave MCP | (Bailian is China-tilted, less useful for US news) |
Auth: none for yfinance. yfinance is rate-limited; reuse cached data when possible.
Long tail / out-of-scope
These are explicitly not routed today; flag the gap and ask the user before reaching for them:
- Crypto — out of scope.
- Options / futures / derivatives — out of scope.
- Intraday tick data — out of scope; daisy is research-grade, not execution-grade.
- Alternative data (satellite / shipping / sentiment proper) — out of scope.
- OpenBB SDK — would conflict with the committed Tushare + Brave + Bailian stack; do not add without user sign-off.
Failure modes and what they mean
| Symptom | Likely cause | Action |
|---|---|---|
请指定正确的接口名 from pro.hk_daily_basic | Tushare plan does not include the HK valuation interface | Use akshare_hk_valuation.py valuation instead. |
auth_missing (exit 2) on any Tushare call | TUSHARE_TOKEN env var unset or expired | Surface to user; do not retry. |
no_data (exit 4) with retryable: true | Empty result after filters / lookback exhausted | Loosen filters, extend --lookback-days, check ticker. |
dependency_missing (exit 5) | Optional dep not installed (akshare / yfinance / stockstats) | Run `uv sync --extra <ta\ |
Empty name after AKShare valuation call | Network glitch or unusual ticker | Local dict at references/hk-ticker-name.json covers the ~30 majors automatically; name_source on the response tells you which path won. |
How agents should use this doc
1. Read before the plan step, not during. The agent's plan should already reference the right primary call by the time it gets to data-gathering. 2. When a primary fails, do not retry the same endpoint; jump to the documented fallback. This is the soft-loop-limit pattern in SKILL.md §4. 3. If a market × data-type cell is empty, that's the documented gap. Either propose an alternative analysis or escalate to the user — don't fabricate.
Source
Routing pattern adapted from hsliuping/TradingAgents-CN:tradingagents/dataflows/providers/{china,hk,us}/ (Apache-2.0 portion). HK ticker→name dict ported from the same repo's providers/hk/improved_hk.py. Daisy's adaptation strips the MongoDB cache layer, BaoStock provider, and database-driven priority configs — those assume hosted infrastructure that a portable skill should not require.
Bull / Bear / Synthesis Debate Prompts
Three prompt templates for generating the Bull / Base / Bear scenarios section that SKILL.md §7 calls for. Adapted from TradingAgents/tradingagents/agents/researchers/{bull,bear}_researcher.py and agents/managers/research_manager.py — minus the LangGraph state machine, since daisy lets the agent (Claude / etc.) drive the loop directly.
When to use
- Substantial single-company research where the user wants a balanced view, not just a directional pitch.
- Before finalizing a report's "Bull / Base / Bear scenarios" section.
- When the agent's first-pass conclusion feels one-sided and you want to stress-test it before committing.
Skip when
- Quick factual lookups ("what's HSBC's PE?")
- Pure stock screening (no per-name thesis to argue)
- The user has explicitly asked for a directional take
How daisy uses these prompts
The agent runs three internal sub-turns in sequence inside the same conversation:
1. Bull turn — fill the Bull prompt with current evidence, generate the bull case. 2. Bear turn — fill the Bear prompt with the same evidence + the bull case, generate the bear case. 3. Synthesis turn — fill the Synthesis prompt with both arguments, commit to one of the five ratings, write a 1-paragraph investment plan.
Record the synthesis output in the cross-session memory log (scripts/dexter_memory_log.py record) so the call can be reflected on later when realized returns are known.
The five-rating scale (Buy / Overweight / Hold / Underweight / Sell) matches the memory log's --rating enum on purpose, so the synthesis output drops straight in.
Loop spec
Ported from TauricResearch/TradingAgents:tradingagents/graph/conditional_logic.py::should_continue_debate. The agent drives the loop directly (no LangGraph), so this section is the contract.
- Parameter:
max_debate_rounds(default1). One round = one Bull turn + one Bear turn, somax_debate_rounds = 1produces a 2-turn debate,= 2produces 4 turns, etc. - Turn counter: start at
0. Increment by1after each speaker turn (whether Bull or Bear). - Exit condition: when
count >= 2 * max_debate_rounds, stop the debate and run the Synthesis prompt. - Speaker rotation: if the previous turn was Bull, the next speaker is Bear; otherwise Bull. The very first turn is Bull. Each later Bull/Bear turn must reference the immediately preceding counter-argument by its first sentence — this is what forces engagement instead of parallel monologues.
- Synthesis is not counted. It runs exactly once, after the loop exits.
- Default escalation: raise
max_debate_roundsto2when the first round produced two strong, evidence-balanced cases that did not engage with each other (i.e. when the bull/bear arguments are about different things). Don't escalate past3— beyond that, returns diminish andHoldbecomes the path of least resistance, which is exactly what we're trying to avoid.
Auditing the loop (optional but recommended for substantial reports): log each turn to the per-task scratchpad with the debate_turn entry type so the round shape can be replayed later.
python <skill-dir>/scripts/dexter_scratchpad.py add <scratchpad.jsonl> debate_turn \
speaker=Bull round=1 turn=1 argument="<paragraph>"
python <skill-dir>/scripts/dexter_scratchpad.py add <scratchpad.jsonl> debate_turn \
speaker=Bear round=1 turn=2 argument="<paragraph>"
# loop exits because count (2) >= 2 * max_debate_rounds (1) → run Synthesis promptProgrammatic loop driver
Instead of hand-tracking the rotation, use scripts/debate_runner.py to enforce the state machine. The script never calls an LLM — it renders the right prompt next, records each turn into the scratchpad, and signals when to switch to synthesis.
# Start the debate; the script returns the rendered Bull prompt
python <skill-dir>/scripts/debate_runner.py init \
--type research --ticker 600519.SH \
--pad <scratchpad.jsonl> \
--context-file <ctx.json> \
--max-rounds 1
# → {"data": {"debate_id": "dbg_...", "next_action": "speak", "speaker": "Bull", "prompt": "..."}}
# Bull spoke; record the bull turn (the script returns the next-step Bear prompt
# pre-filled with the just-recorded bull argument)
python <skill-dir>/scripts/debate_runner.py next \
--pad <scratchpad.jsonl> --debate-id dbg_... \
--argument-file <bull-argument.txt>
# → {"data": {"next_action": "speak", "speaker": "Bear", "prompt": "..."}}
# Bear spoke; record the bear turn. With max_rounds=1 the bound is reached.
python <skill-dir>/scripts/debate_runner.py next \
--pad <scratchpad.jsonl> --debate-id dbg_... \
--argument-file <bear-argument.txt>
# → {"data": {"next_action": "synthesize", "speaker": null, "prompt": ""}}
python <skill-dir>/scripts/debate_runner.py synthesize \
--pad <scratchpad.jsonl> --debate-id dbg_...
# → {"data": {"next_action": "done", "speaker": "ResearchManager", "prompt": "..."}}--context-file is a JSON object whose keys are the placeholder names (market_data, fundamentals, news, sector_context, past_context); missing keys are filled with _(not provided)_. The agent still drives the loop directly — the script is the referee, not the driver.
---
Prompt 1 — Bull Analyst
You are a Bull Analyst advocating for investing in {ticker}. Build a strong, evidence-based case emphasizing growth potential, competitive advantages, and positive market indicators. Use the supplied research and data to address concerns and counter likely bear arguments preemptively.
Focus on:
- Growth potential: market opportunity, revenue trajectory, scalability, addressable market.
- Competitive advantages: moats — unique products, brand, distribution, regulation, network effects, cost position, dominant share.
- Positive indicators: financial health, industry tailwind, recent positive catalysts, capital return (dividend / buyback) where relevant.
- Anticipated bear counterpoints: name the strongest two or three bear arguments and rebut each with specific data.
- For banks / insurers / financial-sector names: lead with RoTE / RoE, CET1, payout ratio + buyback capacity, NIM trend, credit cost, P/B vs cost-of-equity. Do not lead with DCF.
Engagement style: a tight, conversational paragraph or two, not a bullet dump. Cite specific numbers (date, source) for every claim that drives the conclusion.
Resources you have:
- Market / price data: {market_data}
- Financials and ratios: {fundamentals}
- News and catalysts: {news}
- Sector context: {sector_context}
- Past calls on this ticker (from the cross-session memory log): {past_context}
- Most recent bear argument to engage with (empty on the very first turn): {bear_argument}
Deliver the bull argument now.Prompt 2 — Bear Analyst
You are a Bear Analyst making the case against investing in {ticker}. Present a well-reasoned argument emphasizing risks, structural challenges, and negative indicators. Use the supplied research to highlight downside and to expose weaknesses in the bull case.
Focus on:
- Risks and headwinds: market saturation, financial fragility, leverage, regulatory exposure, macro sensitivity, currency / commodity drag.
- Competitive weaknesses: weakening market position, declining innovation, share loss to peers or substitutes, governance issues.
- Negative indicators: deteriorating margins, cash-flow weakness, accruals quality, unfavorable insider activity, earnings-quality issues, recent adverse news.
- Bull counterpoints: name the bull's strongest two or three claims and challenge each with specific data — flag over-optimistic assumptions, mark-to-model risk, or one-off items inflating the trend.
- For banks / insurers: NPL formation and coverage, RWA density, capital adequacy under stress, dividend coverage by core earnings, exposure to property / sovereign / FX shocks. Do not rely on DCF.
Engagement style: tight, conversational paragraph or two; cite specific numbers (date, source) for every claim.
Resources you have:
- Market / price data: {market_data}
- Financials and ratios: {fundamentals}
- News and catalysts: {news}
- Sector context: {sector_context}
- Past calls on this ticker (from the cross-session memory log): {past_context}
- Bull case to rebut: {bull_argument}
Deliver the bear argument now.Prompt 3 — Synthesis (Research Manager)
You are the Research Manager. Critically evaluate the bull / bear debate above and deliver a clear, actionable investment plan.
Rating scale (use exactly one):
- Buy — Strong conviction in the bull thesis; recommend taking or growing the position
- Overweight — Constructive view; recommend gradually increasing exposure
- Hold — Balanced view; recommend maintaining the current position
- Underweight — Cautious view; recommend trimming exposure
- Sell — Strong conviction in the bear thesis; recommend exiting or avoiding the position
Commit to a clear stance whenever the strongest arguments warrant one; reserve Hold for situations where the evidence on both sides is genuinely balanced. Do not hedge for politeness.
Output format (exactly these sections, in this order):
**Rating:** <one of Buy / Overweight / Hold / Underweight / Sell>
**Thesis (2-4 sentences):** the single strongest reason for the rating, anchored on the most decisive piece of evidence.
**What would change the view:** the two or three observable conditions that would move the rating up or down a notch (specific metric thresholds, dates, catalysts).
**Risks acknowledged:** the strongest one or two bear points the rating cannot fully neutralize, named honestly.
**Holding period and re-check trigger:** target horizon (e.g. "next 2 quarters / next earnings"), and the precise event or metric that should trigger the next look.
Debate to evaluate:
{bull_argument}
{bear_argument}
Past calls on this ticker (from the cross-session memory log):
{past_context}
Deliver the rating and plan now.---
Recording the call
After the synthesis turn produces a rating, persist it to the cross-session memory log so future runs can pull it back in via dexter_memory_log.py context --ticker <ts_code>:
python <skill-dir>/scripts/dexter_memory_log.py record \
--ticker 600519.SH --rating Buy --date 20260502 \
--decision "Thesis: ... What would change view: ... Risks: ... Holding period: ..."When realized returns are known later (next earnings, next quarter, when the holding-period trigger fires), resolve the entry with realized raw return + alpha vs benchmark + reflection — the memory log will surface that reflection on the next call to context for this ticker.
Why three prompts, not one
Forcing the bull and bear into separate prompts prevents the agent from collapsing into a polite middle-ground answer that doesn't commit. The synthesis prompt then demands a commitment ("reserve Hold for genuinely balanced evidence; do not hedge for politeness"). This pattern is from the TradingAgents research_manager source — the explicit anti-hedging language is doing real work and is preserved verbatim.
Decision Schema — Rating Vocabulary and Markdown Render Contract
A docs-only port of the Pydantic schemas in TradingAgents/tradingagents/agents/schemas.py. Daisy does not run the runtime validation (no Pydantic dependency); instead, the prompts in references/debate-prompts.md and references/risk-debate-prompts.md ask the LLM to emit exactly the markdown shape documented here, and dexter_memory_log.py record --rating enforces the rating enum at the CLI boundary.
The shape here is load-bearing: report writers (scripts/financial_report.py), the memory log file format, and any external code that greps the saved reports all read this exact set of section headers.
---
Rating vocabulary
5-tier directional rating (Research Manager / Portfolio Manager)
The same vocabulary dexter_memory_log.py enforces via --rating.
| Rating | Meaning |
|---|---|
| Buy | Strong conviction in the bull thesis; recommend taking or growing the position |
| Overweight | Constructive view; recommend gradually increasing exposure |
| Hold | Balanced view; recommend maintaining the current position |
| Underweight | Cautious view; recommend trimming exposure |
| Sell | Strong conviction in the bear thesis; recommend exiting or avoiding the position |
Picking rule: reserve Hold for cases where the evidence on both sides is genuinely balanced. Otherwise commit to the side with the stronger argument. Do not hedge for politeness.
3-tier transaction action (Trader)
For workflows that produce an actionable transaction proposal on top of the directional view:
| Action | Meaning |
|---|---|
| Buy | Execute a buy this round |
| Hold | Do nothing this round |
| Sell | Execute a sell / exit this round |
The 5-tier rating expresses position vs target; the 3-tier action expresses what the desk does this round. They can diverge — e.g. an Overweight rating with a Hold action means "favorable view but the entry has already been made and we're not adding more today."
---
Markdown render contracts
A. Research Plan (output of Bull/Bear synthesis)
Used by references/debate-prompts.md Prompt 3.
**Rating**: <one of the 5-tier values>
**Thesis (2-4 sentences)**: <single strongest reason for the rating, anchored on the most decisive evidence>
**What would change the view**: <2-3 observable conditions that would move the rating up or down a notch (specific metric thresholds, dates, catalysts)>
**Risks acknowledged**: <strongest 1-2 bear points the rating cannot fully neutralize>
**Holding period and re-check trigger**: <target horizon (e.g. "next 2 quarters / next earnings"), and the precise event or metric that should trigger the next look>B. Trader Proposal (optional — for trade-execution workflows)
**Action**: <Buy / Hold / Sell>
**Reasoning**: <2-4 sentences anchored in the analysts' reports and the research plan>
**Entry Price**: <optional numeric target in quote currency>
**Stop Loss**: <optional numeric level in quote currency>
**Position Sizing**: <optional sizing guidance, e.g. "5% of portfolio">
FINAL TRANSACTION PROPOSAL: **BUY** | **HOLD** | **SELL**The trailing FINAL TRANSACTION PROPOSAL: **...** line is the canonical sentinel. External tooling that scans for end-of-debate markers reads it; preserve it verbatim.
C. Portfolio Decision (output of Risk Debate synthesis)
Used by references/risk-debate-prompts.md Prompt 4.
**Rating**: <one of the 5-tier values>
**Executive Summary**: <2-4 sentences covering entry strategy, position sizing, key risk levels, time horizon>
**Investment Thesis**: <detailed reasoning anchored in specific evidence from the risk debate; incorporate prior lessons if present in the prompt context>
**Price Target**: <optional numeric target in quote currency, or omit the line entirely>
**Time Horizon**: <optional, e.g. "3-6 months", or omit the line entirely>---
JSON Schema (for agents that want to self-validate)
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "PortfolioDecision",
"type": "object",
"required": ["rating", "executive_summary", "investment_thesis"],
"properties": {
"rating": {
"type": "string",
"enum": ["Buy", "Overweight", "Hold", "Underweight", "Sell"]
},
"executive_summary": {
"type": "string",
"description": "2-4 sentences covering entry strategy, sizing, risk levels, time horizon"
},
"investment_thesis": {
"type": "string",
"description": "Detailed reasoning anchored in specific evidence"
},
"price_target": {
"type": ["number", "null"],
"description": "Optional target price in the instrument's quote currency"
},
"time_horizon": {
"type": ["string", "null"],
"description": "Optional recommended holding period"
}
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "ResearchPlan",
"type": "object",
"required": ["rating", "thesis", "what_would_change_the_view", "risks_acknowledged", "holding_period_and_recheck"],
"properties": {
"rating": {
"type": "string",
"enum": ["Buy", "Overweight", "Hold", "Underweight", "Sell"]
},
"thesis": {"type": "string", "description": "2-4 sentences"},
"what_would_change_the_view": {"type": "string"},
"risks_acknowledged": {"type": "string"},
"holding_period_and_recheck": {"type": "string"}
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "TraderProposal",
"type": "object",
"required": ["action", "reasoning"],
"properties": {
"action": {"type": "string", "enum": ["Buy", "Hold", "Sell"]},
"reasoning": {"type": "string", "description": "2-4 sentences"},
"entry_price": {"type": ["number", "null"]},
"stop_loss": {"type": ["number", "null"]},
"position_sizing": {"type": ["string", "null"]}
}
}---
Why no runtime validation
A skill is consumed by an external LLM-driven agent harness. The agent's own structured-output mode (json_schema for OpenAI/xAI, response_schema for Gemini, tool-use for Anthropic) does the runtime validation against the JSON Schemas above. Re-implementing validation inside daisy would either duplicate effort, force a Pydantic dependency on a deliberately-light skill, or both. The CLI does enforce the 5-tier --rating enum at dexter_memory_log.py record time, which is the single integration point where invalid output would corrupt durable state.
Source
TradingAgents/tradingagents/agents/schemas.py (PortfolioRating, TraderAction, ResearchPlan, TraderProposal, PortfolioDecision, plus the render_* helpers). The schema field descriptions are condensed; the markdown shape is preserved verbatim.
Analysis of virattt/dexter key points
Source repo inspected: https://github.com/virattt/dexter.git Commit inspected: beb5f36 (Add LangSearch as web search provider; HEAD on 2026-05-13) Prior pin was 0e5d805 (Update earnings tool).
Delta since the prior pin — what was imported
- `segments.py` — ports the idea of dexter's
getFinancialSegmentstool
(commit 8819ad7) to a free-tier path: A-share via AKShare stock_zygc_em, HK/US emit no_data with a pointer to filings. Different geography than dexter (which is US-only via the paid Financial Datasets API), but the segment-level analytical frame is now available to the agent.
- Broad-market news routing — dexter's commit
745687etaught the
get_company_news tool to take a ticker? (omit for macro). Equivalent guidance is now in SKILL.md §3 as a Brave / Bailian MCP routing rule for no-ticker macro queries; no script needed because the MCPs handle the query directly.
Explicitly not imported (and why)
- LangSearch web-search provider (commit
beb5f36) — the skill commits
to Brave + Bailian MCPs (SKILL.md §3, "Search routing"). LangSearch is redundant. Documented in routing notes only.
- Memory keyword-search fallback (commit
a4c511a) — daisy's memory log
is a single Markdown file with no embeddings, so there's no degradation path to add.
- Research-rules prompt fix (commit
aadaef1) — daisy has no
.dexter/RULES.md concept; not applicable.
- CLI/UI commits (cron z.union, model selector, flicker, duration
counter, disclaimer, approval flow) — runtime UX, not relevant to a Python skill.
- DCF simplification — drop analyst-estimates step (commit
4c355d4) —
the current SKILL.md DCF section does not lean on analyst EPS estimates as a hard input, so no edit was needed.
What Dexter is
Dexter is a TypeScript/Bun CLI financial research agent using LangChain, Ink UI, Financial Datasets API, search tools, browser scraping, skills, memory, cron, WhatsApp gateway, and evaluation datasets.
Main architectural ideas worth porting into Hermes
1. Agentic finance loop
- Decompose a financial question into research steps.
- Iterate with tool calls until sufficient evidence exists.
- Stop at max iterations instead of running forever.
2. Scratchpad as single source of truth
- Each run writes JSONL entries containing init, tool_result, thinking.
- Useful for auditability, debugging, and continuing long analyses.
3. Meta-tools for finance routing
- Dexter exposes high-level tools such as get_financials and get_market_data.
- Internally, those tools call an LLM router to choose specific sub-tools.
- For Hermes, this is best approximated by skill instructions + Tushare/web/Python routing, unless a full plugin is built.
4. Rich tool-use policy
- Explicit “when to use / when not to use”.
- Prefer one complete natural-language finance query to many fragmented calls when using a meta-tool.
- Avoid breaking comparisons into unnecessary repeated calls.
5. Concurrent read-only tools
- Dexter safely batches read-only tool calls in parallel.
- Hermes already has parallel/delegation patterns, so the skill tells the agent to gather independent evidence efficiently.
6. Soft loop limits and retry warnings
- Dexter tracks call counts and similar query repetition.
- It warns but does not hard-block repeated calls.
- The skill ports this as a max-three-attempt heuristic.
7. Context management and compaction
- Dexter caps large tool results, persists overflow to files, and compacts long sessions while preserving key numbers.
- Hermes already has persisted tool outputs and file tools; the skill emphasizes scratchpad and numeric preservation.
8. DCF skill
- Dexter includes a DCF valuation skill with a clear checklist, growth/WACC assumptions, terminal value, sensitivity matrix, and sanity checks.
- This is directly ported into the Hermes skill.
9. Evaluation mindset
- Dexter has a finance QA eval dataset and LangSmith judge workflow.
- Future Hermes plugin could add a finance-eval runner, but the skill focuses on operational workflow.
Skill vs plugin decision
A Hermes skill is the right first implementation because:
- The core Dexter advantage is workflow/prompt/tool policy, not a unique UI.
- Hermes already has memory, tools, browser, terminal, cron, and skills.
- The user already has Tushare configured, which is better for Chinese-market data than Dexter’s US-focused Financial Datasets API.
- A plugin would only be necessary if we want native new tool functions like get_financials(query) or get_market_data(query) backed by Financial Datasets API.
Future plugin option
If building a Hermes plugin later, implement:
- financial_research(query): high-level router
- tushare_query(interface, params): structured Tushare wrapper
- dcf_valuation(ticker, market): calculation wrapper
- finance_scratchpad(action, path, payload): JSONL run log
- finance_eval(dataset_path): batch evaluation runner
Keep the skill as the policy layer even if plugin tools are added.
{
"_comment": "HK ticker (5-digit canonical) -> Chinese short name. Used as a free, no-API-call fallback when AKShare returns empty name. Keys are normalized via akshare_hk_valuation.normalize_hk_code (zero-padded to 5 digits). Ported from hsliuping/TradingAgents-CN:tradingagents/dataflows/providers/hk/improved_hk.py (Apache-2.0). Extend by adding new (code, name) pairs and re-running tests.",
"00700": "腾讯控股",
"00941": "中国移动",
"00762": "中国联通",
"00728": "中国电信",
"00939": "建设银行",
"01398": "工商银行",
"03988": "中国银行",
"00005": "汇丰控股",
"01299": "友邦保险",
"02318": "中国平安",
"02628": "中国人寿",
"00857": "中国石油",
"00386": "中国石化",
"01109": "华润置地",
"01997": "九龙仓置业",
"09988": "阿里巴巴",
"03690": "美团",
"01024": "快手",
"09618": "京东集团",
"01876": "百威亚太",
"00291": "华润啤酒",
"01093": "石药集团",
"00867": "康师傅",
"02238": "广汽集团",
"01211": "比亚迪",
"00753": "中国国航",
"00670": "中国东航",
"00347": "鞍钢股份",
"00902": "华能国际",
"00991": "大唐发电"
}
HSBC HK bank research test — 2026-04-29
Why this reference exists
Session-tested details for the dexter-financial-research skill when researching a Hong Kong-listed bank using Tushare + MCP search.
User preference captured
For finance research/search workflows, do not include Asta/Semantic Scholar as a default evidence route. Use:
1. Tushare for structured market/financial data where available. 2. Brave MCP as the primary web/current search tool. 3. Bailian WebSearch MCP as a Chinese-language / China-market supplemental search tool. 4. Python for calculations and tabulation. 5. Browser only when pages need interaction, dynamic rendering, login/paywall handling, or visual inspection.
Known-good HK Tushare probes
Environment used: python (whichever interpreter has tushare, pandas, requests installed; see SKILL.md "Python interpreter convention"), Tushare 1.4.29, token from TUSHARE_TOKEN.
import os, tushare as ts
pro = ts.pro_api(os.getenv('TUSHARE_TOKEN') or ts.get_token())
pro.hk_basic(ts_code='00005.HK', fields='ts_code,name,list_status,list_date,delist_date')
pro.hk_daily(ts_code='00005.HK', start_date='20250101', end_date='20260429')Observed for HSBC 00005.HK:
hk_basicreturned one listed row:00005.HK 汇丰控股, list date19800102.hk_dailyreturned daily HK price rows; latest available in the test was 2026-04-28 close 140.6 HKD.hk_daily_basicfailed with:请指定正确的接口名. Do not rely on it without re-testing.
Useful calculation pattern
Sort hk_daily ascending by trade_date, then compute:
- latest close/date
- YTD return from first trading day >= Jan 1
- 1M/3M/6M/1Y returns from approximate trading-day offsets
- 52-week high/low from last 252 rows
- dividend yield from official USD dividend translated at approximate HKD/USD peg when no structured dividend API is available
- target-price upside from sourced analyst targets
HSBC test facts captured
From HSBC official 2025 results pages and search cross-checks:
- 2025 revenue: 68.3B USD.
- Reported profit before tax: 29.9B USD.
- Profit before tax excluding notable items: 36.6B USD.
- RoTE: 13.3%; RoTE excluding notable items: 17.2%.
- CET1: 14.9%.
- 2025 total dividend: 0.75 USD/share.
- 2025 buybacks completed: 6B USD.
- 2026 guidance: banking NII at least 45B USD.
- 2026–2028 target: RoTE excluding notable items >=17%; target payout ratio basis 50%.
- HSBC stated further buybacks would wait until CET1 returns to/above target range after Hang Seng Bank privatization capital impact.
Tushare-derived in the session:
- Latest
hk_dailyclose: 140.6 HKD on 2026-04-28. - YTD return: about +13.1%.
- 1Y return: about +76.7%.
- 52-week high/low: about 148.0 / 80.05 HKD.
- Estimated trailing dividend yield using 0.75 USD * 7.8 / 140.6: about 4.16%.
Bailian/Brave search snippets surfaced market targets:
- Futu aggregated average target around 164.63 HKD, lowest around 143.08, highest around 180.00.
- Goldman target around 160 HKD, with caution that buyback may be more likely after Q2 than immediately after Q1.
Bank valuation pitfall
Do not default to DCF for banks. For banks, the first-pass framework should be:
- profitability: RoTE/ROE, NIM/NII trend, fee/wealth income
- capital: CET1, target capital range, buyback capacity
- distribution: dividend per share, payout ratio, dividend yield, buyback restart timing
- risk: credit costs/ECL, loan growth, commercial real estate/China/geopolitical exposure
- valuation: P/B and P/E where sourced, plus target-price sanity checks
- catalyst: next earnings release, management guidance, buyback resumption, rate path
Scratchpad example
The test created:
./financial-research/scratchpad/20260429-162217_b2d5c91bf629.jsonl
Use only as an example pattern; do not treat that file as canonical data in future sessions.
Reflection Prompt for Memory Log Resolve
A standardized prompt for the lesson the agent passes to dexter_memory_log.py resolve --reflection. The system-message body is verbatim from TradingAgents/tradingagents/graph/reflection.py::Reflector._get_log_reflection_prompt. The inputs block is intentionally extended beyond TA's version: TA hardcodes SPY as the benchmark and omits holding-days; daisy routes benchmarks per market (CSI 300 / HSI / SPY) and surfaces holding-days, so the lesson can reference both.
When to use
- Whenever an agent calls
dexter_memory_log.py resolveand needs to write the--reflectionlesson. - Before persisting a resolution: have the LLM write the lesson using this prompt, then pass the resulting text as
--reflection.
Why a fixed prompt
Without one, lesson lengths drift across runs (some 1-line, some 5-paragraph), formatting drifts (some bulleted, some prose), and content drifts (some pure narrative, some pure numbers). Stable shape matters because:
- Lessons are re-injected into future agent prompts via
dexter_memory_log.py context. A bloated lesson burns context tokens on every subsequent call for that ticker. - The cross-ticker
contextblock lists ~3 recent lessons; if any of them is a 200-word paragraph, the block becomes too noisy to read. - The
statsaggregation reads tag lines, not lessons — but a human auditing the log expects a consistent shape.
The TA prompt's "exactly 2-4 sentences of plain prose" constraint is doing real work; preserve it.
The prompt
You are a trading analyst reviewing your own past decision now that the outcome is known.
Write exactly 2-4 sentences of plain prose (no bullets, no headers, no markdown).
Cover in order:
1. Was the directional call correct? (cite the alpha figure)
2. Which part of the investment thesis held or failed?
3. One concrete lesson to apply to the next similar analysis.
Be specific and terse. Your output will be stored verbatim in a decision log and re-read by future analysts, so every word must earn its place.
---
Inputs (daisy-extended — TA's version only had raw + alpha vs SPY):
Raw return: {raw_return:+.1%}
Alpha vs benchmark: {alpha_return:+.1%}
Holding days: {holding_days}d
Benchmark used: {benchmark}
Prior decision text (the thesis being reviewed):
{decision_text}
Write the reflection now.Workflow
# 1. Compute raw_return and alpha (manually, or via the auto-resolve helper when shipped)
RAW=4.8
ALPHA=1.2
DAYS=17
# 2. Have the LLM write the lesson using the prompt above. Capture as $REFL.
# 3. Persist
python <skill-dir>/scripts/dexter_memory_log.py resolve \
--ticker 600519.SH --date 20260415 \
--raw-return "$RAW" --alpha-return "$ALPHA" --holding-days "$DAYS" \
--reflection "$REFL"Anti-pattern
Don't paste the entire research report or the synthesis output as the reflection — that's what --decision is for at record time. The reflection is only the post-hoc lesson, written after the outcome is known. If the reflection ends up longer than the original decision, it's wrong.
Risk-Debate Prompts (Aggressive / Conservative / Neutral + Portfolio Manager)
A second debate layer that runs after the Bull/Bear/Synthesis debate in references/debate-prompts.md produces a directional rating. While Bull/Bear argues direction, this layer argues position sizing and risk posture — how much to actually commit, where to stop out, and what time horizon to hold.
Adapted from TradingAgents/tradingagents/agents/risk_mgmt/{aggressive,conservative,neutral}_debator.py and agents/managers/portfolio_manager.py. Pure prompt text — the LangGraph state plumbing has been replaced with explicit placeholders the agent fills in.
When to use
- After the Bull/Bear synthesis has produced a 5-tier rating, and the user wants the report's executive summary / position-sizing / stop-loss language to reflect a balanced risk view.
- When a single-stance recommendation feels mechanically optimistic or mechanically defensive and you want to stress-test it against the opposite risk posture.
Skip when
- The user only asked for a directional research view (no position sizing).
- The Bull/Bear synthesis already landed on
Hold— risk debate adds little value when the directional call is "do nothing". - Quick screens or watchlists — risk debate is per-name only.
How daisy uses these prompts
Run three internal sub-turns, then a synthesis:
1. Aggressive turn — champion the high-reward case for the rating, push for larger position / wider stop / longer horizon. 2. Conservative turn — counter with downside protection, smaller size, tighter stop, shorter horizon, mark-to-model risks. 3. Neutral turn — challenge both extremes, propose the moderate sizing the report should actually adopt. 4. Portfolio Manager synthesis — commit to one final position plan with rating, executive summary, investment thesis, optional price target, optional time horizon.
The synthesis output uses the exact markdown render contract documented in references/decision-schema.md, so it drops straight into dexter_memory_log.py record --decision.
Loop spec
Ported from TauricResearch/TradingAgents:tradingagents/graph/conditional_logic.py::should_continue_risk_analysis. The agent drives the loop directly (no LangGraph), so this section is the contract.
- Parameter:
max_risk_discuss_rounds(default1). One round = three speaker turns (Aggressive + Conservative + Neutral), somax_risk_discuss_rounds = 1produces a 3-turn debate,= 2produces 6 turns, etc. - Turn counter: start at
0. Increment by1after each speaker turn. - Exit condition: when
count >= 3 * max_risk_discuss_rounds, stop and run the Portfolio Manager prompt. - Speaker rotation (strict): Aggressive → Conservative → Neutral → Aggressive → … . The very first turn is Aggressive. Each later turn must respond directly to the most recent argument from the other two analysts — this is what makes the three-vs-one structure produce judgment rather than three parallel monologues.
- Synthesis is not counted. The Portfolio Manager runs exactly once, after the loop exits.
- Default escalation: raise
max_risk_discuss_roundsto2only when the first round leaves Aggressive vs Conservative deadlocked on a binary stop-loss / sizing question that Neutral did not break. Don't escalate past2for risk debate — sizing converges fast, and a third round usually just rephrases the second.
Auditing the loop (optional but recommended for substantial reports): log each turn to the per-task scratchpad with the debate_turn entry type.
python <skill-dir>/scripts/dexter_scratchpad.py add <scratchpad.jsonl> debate_turn \
speaker=Aggressive round=1 turn=1 argument="<paragraph>"
python <skill-dir>/scripts/dexter_scratchpad.py add <scratchpad.jsonl> debate_turn \
speaker=Conservative round=1 turn=2 argument="<paragraph>"
python <skill-dir>/scripts/dexter_scratchpad.py add <scratchpad.jsonl> debate_turn \
speaker=Neutral round=1 turn=3 argument="<paragraph>"
# loop exits because count (3) >= 3 * max_risk_discuss_rounds (1) → run Portfolio ManagerProgrammatic loop driver
Use scripts/debate_runner.py --type risk to enforce the three-speaker rotation and exit condition. The risk layer needs the prior research synthesis as input — pass its text via --prior-synthesis-file.
python <skill-dir>/scripts/debate_runner.py init \
--type risk --ticker 600519.SH \
--pad <scratchpad.jsonl> \
--context-file <ctx.json> \
--prior-synthesis-file <prior_synth.txt> \
--max-rounds 1
# → first prompt is Aggressive
python <skill-dir>/scripts/debate_runner.py next \
--pad <scratchpad.jsonl> --debate-id dbg_... \
--argument-file <aggressive-argument.txt>
# → next prompt is Conservative (with aggressive argument inlined)
python <skill-dir>/scripts/debate_runner.py next \
--pad <scratchpad.jsonl> --debate-id dbg_... \
--argument-file <conservative-argument.txt>
# → next prompt is Neutral (sees both aggressive + conservative arguments)
python <skill-dir>/scripts/debate_runner.py next \
--pad <scratchpad.jsonl> --debate-id dbg_... \
--argument-file <neutral-argument.txt>
# → next_action: synthesize
python <skill-dir>/scripts/debate_runner.py synthesize \
--pad <scratchpad.jsonl> --debate-id dbg_...
# → Portfolio Manager prompt with the full debate inlined--prior-synthesis-file is mandatory for --type risk (every risk-layer prompt has the {prior_synthesis} placeholder). The agent still drives the loop directly — the script is the referee, not the driver.
---
Prompt 1 — Aggressive Risk Analyst
As the Aggressive Risk Analyst, your role is to actively champion high-reward, high-risk opportunities, emphasizing bold strategies and competitive advantages. When evaluating the prior recommendation and plan, focus intently on the potential upside, growth potential, and innovative benefits — even when these come with elevated risk. Use the supplied research to strengthen your arguments and challenge opposing views.
Specifically, respond directly to each point made by the conservative and neutral analysts (if any), countering with data-driven rebuttals and persuasive reasoning. Highlight where their caution might miss critical opportunities or where their assumptions may be overly conservative.
Prior recommendation to push aggressively on:
{prior_synthesis}
Research and data:
- Market / price data: {market_data}
- Financials and ratios: {fundamentals}
- News and catalysts: {news}
- Sector context: {sector_context}
Existing arguments in the debate (may be empty on first turn):
- Last conservative argument: {conservative_response}
- Last neutral argument: {neutral_response}
Engage actively. Address specific concerns raised, refute weaknesses in their logic, and assert the benefits of risk-taking. Maintain a focus on debating and persuading, not just presenting data. Output conversationally, no headers or bullets.Prompt 2 — Conservative Risk Analyst
As the Conservative Risk Analyst, your primary objective is to protect capital, minimize volatility, and ensure steady, reliable growth. You prioritize stability, security, and risk mitigation — assess potential losses, downturns, and adverse scenarios carefully. When evaluating the prior recommendation, critically examine high-risk elements and point out where the plan may expose the position to undue risk and where more cautious alternatives could secure long-term gains.
Prior recommendation to push back on:
{prior_synthesis}
Research and data:
- Market / price data: {market_data}
- Financials and ratios: {fundamentals}
- News and catalysts: {news}
- Sector context: {sector_context}
Existing arguments in the debate:
- Last aggressive argument: {aggressive_response}
- Last neutral argument: {neutral_response}
Question their optimism. Emphasize potential downsides they may have overlooked. Address each of their counterpoints to showcase why a conservative stance is the safest path. Focus on debating and critiquing, not just presenting data. Output conversationally, no headers or bullets.Prompt 3 — Neutral Risk Analyst
As the Neutral Risk Analyst, provide a balanced perspective, weighing both upside and risk in the prior recommendation. Prioritize a well-rounded approach: factor in broader market trends, potential economic shifts, and diversification.
Prior recommendation to balance:
{prior_synthesis}
Research and data:
- Market / price data: {market_data}
- Financials and ratios: {fundamentals}
- News and catalysts: {news}
- Sector context: {sector_context}
Existing arguments in the debate:
- Last aggressive argument: {aggressive_response}
- Last conservative argument: {conservative_response}
Challenge both sides — point out where the aggressive view is overly optimistic and where the conservative view is overly cautious. Propose a moderate sizing / stop / horizon that captures the upside while protecting against the most likely downside scenarios. Output conversationally, no headers or bullets.Prompt 4 — Portfolio Manager Synthesis
As the Portfolio Manager, synthesize the risk analysts' debate above and deliver the final position plan.
Rating Scale (use exactly one):
- Buy — Strong conviction to enter or add to position
- Overweight — Favorable outlook; gradually increase exposure
- Hold — Maintain current position; no action needed
- Underweight — Reduce exposure; take partial profits
- Sell — Exit position or avoid entry
Context:
- Prior research synthesis (directional view): {prior_synthesis}
- Past calls on this ticker (from the cross-session memory log): {past_context}
Risk Analysts Debate History:
{aggressive_argument}
{conservative_argument}
{neutral_argument}
---
Output exactly the section headers below, in this order (the rest of daisy's
report writers, memory log, and any external parsers depend on this shape):
**Rating**: <Buy / Overweight / Hold / Underweight / Sell>
**Executive Summary**: <Two to four sentences covering entry strategy, position sizing, key risk levels, and time horizon.>
**Investment Thesis**: <Detailed reasoning anchored in specific evidence from the analysts' debate. If past lessons are referenced above, incorporate them; otherwise rely solely on the current analysis.>
**Price Target**: <Optional. Numeric target in the instrument's quote currency, or omit the line entirely.>
**Time Horizon**: <Optional. e.g. "3-6 months", or omit the line entirely.>
Be decisive. Reserve Hold for situations where the evidence on both sides is genuinely balanced. Ground every claim in specific evidence from the analysts.---
Recording the call
After the Portfolio Manager synthesis, persist to the memory log:
python <skill-dir>/scripts/dexter_memory_log.py record \
--ticker 600519.SH --rating Buy --date 20260502 \
--decision "<paste the synthesis output verbatim>"The synthesis output already follows references/decision-schema.md, so the stored decision will roundtrip cleanly through dexter_memory_log.py context on future runs.
Why three risk perspectives, not two
Two-sided debates collapse to compromise; three-sided debates force an actual judgment call. The Aggressive analyst pushes upside, the Conservative analyst pushes downside protection, and the Neutral analyst is structurally biased against both — its job is to call out what each extreme misses. The Portfolio Manager then has to pick a position, not just split the difference. This three-vs-one structure is the whole reason the TA risk_mgmt module exists and is preserved verbatim from the source.
{{ title }}
Date: {{ date }} Universe: {{ universe }} Preset: {{ preset }} Data sources: {{ data_sources }}
Executive summary
- This is a research watchlist, not a buy list.
- Screening style: {{ preset }}.
- Candidates retained: {{ candidate_count }}.
- Next step: deep-dive the top 3–8 names before any decision.
Universe and filters
| Item | Setting |
|---|---|
| Market | {{ market }} |
| Universe | {{ universe }} |
| Trade date | {{ trade_date }} |
| Exclusions | ST/suspended/newly listed/illiquid/missing critical data where possible |
| Main factors | valuation, shareholder return, size/liquidity, optional momentum |
Top candidates
{{ top_candidates_table }}
Priority deep-dive list
{{ priority_list }}
Red flags to verify
- Dividend sustainability: payout ratio, earnings stability, cash flow.
- Value trap risk: low PE/PB caused by deteriorating fundamentals.
- Liquidity risk: especially for small-cap HK stocks.
- Sector concentration: avoid accidentally building a one-sector portfolio.
- Recent price overheating: screen momentum is not the same as margin of safety.
Suggested next checks
1. Pull latest annual/interim report and earnings announcement. 2. Verify dividend/buyback policy. 3. Check 3–5 year earnings and cash-flow trend. 4. Check sector-specific risks. 5. Run single-company deep-dive report for finalists.
Disclaimer
Data analysis only, not investment advice.
Related skills
FAQ
What data source does it use?
Tushare (requires TUSHARE_TOKEN) plus web search, with numerical validation before finalizing.
Does it give investment advice?
No; it outputs sourced analysis with caveats and never presents investment advice as certainty.