CCStockWorkEnv is a Claude Code environment for multi-market stock research & financial analysis, accessed via Claude Telegram Bot (ctb). Supports US, Taiwan, and China markets.
Launch: ctb /Users/wanghsuanchung/Projects/CCStockWorkEnv
- Fail-fast — No overprotective try-except. Let errors propagate with clear messages.
- Direct dict access — Use
dict["key"]by default. Only.get()for truly optional fields. - No workarounds — Investigate root causes, don't mask bugs with fallbacks.
- No over-engineering — Only build what's needed now.
When the user's request involves multi-stock analysis, financial data, company research, industry overview, or any response that would exceed ~10 lines of text, Claude MUST generate an HTML report on the web server instead of sending a long Telegram message.
- Always reply in the original chat — Respond directly in the conversation. The ctb bot delivers your reply to the original Telegram chat/group automatically. Do NOT call
send_message.pyorsend_mail.pyunless the user explicitly asks to send a message/email, or in scheduled tasks that run without a conversation. Never use email or Telegram send tools as a substitute for replying. - Web report over Telegram text — If the answer contains tables, financial data, multiple stocks, or detailed analysis, always produce an HTML report in
output/and reply with the web server URL. Never dump walls of text into Telegram. - Embed interactive charts — For any stock mentioned in the report, embed a K-line chart via iframe. MUST use relative path (starts with
/), NEVER usehttp://localhost:8800or any absolute URL — mobile users cannot reach localhost.Full tag:✅ <iframe src="/charts/TICKER/?market=XX&period=1y&embed=1" ...> ❌ <iframe src="http://localhost:8800/charts/TICKER/..." ...> ❌ <iframe src="http://<EXTERNAL_IP>/charts/TICKER/..." ...>
<iframe src="/charts/TICKER/?market=XX&period=1y&embed=1" style="width:100%;height:420px;border:1px solid #e0e0e0;border-radius:6px;" loading="lazy"></iframe> - Provide evidence — Back up claims with data. Include: stock price charts (embedded iframes), key financial ratios in tables, data source attribution, and relevant metrics (P/E, P/B, ROE, etc.).
- Telegram reply should be short — Only reply with a brief summary (2-3 sentences) + the report URL.
Report URL construction:
- Read
config.json→web_server.fixed_ipandweb_server.external_port - If
fixed_ipis set (non-empty, not null): usehttp://<fixed_ip>:<external_port>/reports/SLUG/ - If
fixed_ipis missing/empty: fallback tohttp://localhost:<internal_port>/reports/SLUG/and warn the user that the URL is only accessible on the local network, not from mobile devices outside the LAN - Example:
📊 騰訊 vs 阿里巴巴個股分析報告已產生,包含財務數據、K線圖及關鍵指標。\n🔗 http://<FIXED_IP>:<EXTERNAL_PORT>/reports/SLUG/
- Read
- Short answers stay in Telegram — Simple queries like "台積電現在股價多少?" can be answered directly in Telegram without generating a report.
- "幫我查詢騰訊和阿里巴巴的個股資訊及財務數據" → HTML report with company info tables, financial data, embedded K-line charts
- "找美國上市的光通訊公司,檢查去年財務狀況" → HTML report with screened companies, financial health analysis, charts
- "分析半導體產業趨勢" → HTML report with industry overview, key players, charts
- "比較 AAPL 和 MSFT" → HTML report with side-by-side comparison, charts
- "台積電現在多少錢?" → Short price quote in Telegram
- "MU 的 P/E 是多少?" → One-liner answer in Telegram
- Code & docs: English
- Claude 回應: 繁體中文 (Traditional Chinese) — Claude MUST respond in Traditional Chinese at all times
- User-facing output: 繁體中文 (Traditional Chinese)
- Commands 描述: 繁體中文 (shown in Telegram)
CCStockWorkEnv/
├── CLAUDE.md # This file
├── .claude/commands/ # CTB slash commands (14)
├── .claude/skills/ # Domain knowledge (8)
├── .claude/agents/ # Specialized agents (2)
├── tool_scripts/
│ ├── send_telegram/ # Telegram messaging
│ ├── send_mail/ # Email via Mailgun
│ ├── market_data/ # Market data API abstraction
│ ├── financial_calc/ # Z-Score, F-Score, screener
│ ├── db_ops/ # SQLite operations
│ ├── report_gen/ # Report & chart generation
│ └── web_server/ # Django report viewer (RWD)
├── schedules/ # Scheduled task scripts
├── data/ # SQLite DB, exports, charts, logs (gitignored)
├── output/ # Timestamped reports (gitignored)
└── prompts/ # YYYYMMDD_N_description.md
All tool_scripts run via uv:
cd tool_scripts/<subfolder> && uv run python <script>.py [args]Format: YYYYMMDD_N_description.md where N is sequential for the day.
SQLite at data/ccstockworkenv.db. WAL mode enabled. Schema managed by db_ops/db_manager.py.
fetcher_base.py → MarketDataFetcher (ABC)
fetcher_us.py → USFetcher [yfinance]
fetcher_tw.py → TWFetcher [twstock/FinMind]
fetcher_cn.py → CNFetcher [AKShare]
fetcher_factory.py → get_fetcher(market) → MarketDataFetcher
Market codes: US, TW, CN
| Metric | Module | Purpose |
|---|---|---|
| Altman Z-Score | zscore.py |
Bankruptcy risk (>2.99 safe, <1.81 distress) |
| Piotroski F-Score | fscore.py |
Financial strength (0-9, ≥7 strong) |
| Opportunity Score | opportunity_score.py |
Weighted composite score |
| Key Ratios | ratios.py |
P/E, P/B, ROE, ROA, D/E, margins |
When the user asks about a company's 財務狀況 (financial status/health), Claude MUST perform deep financial research — not just surface-level revenue or earnings. Use fetcher_factory.py to pull actual financial data (financials, metrics, quote) and present them in structured tables.
1. 盈利能力 (Profitability)
| Metric | 中文 | How to interpret |
|---|---|---|
| Gross Margin | 毛利率 | >40% excellent, <20% weak |
| Operating Margin | 營業利潤率 | Industry-dependent |
| Net Margin | 淨利率 | Higher = stronger pricing power |
| ROE | 股東權益報酬率 | >15% strong, <5% weak |
| ROA | 資產報酬率 | >5% good for capital-intensive |
| EPS & EPS Growth | 每股盈餘及年增率 | Trend matters more than absolute |
2. 財務健康 (Solvency & Liquidity)
| Metric | 中文 | How to interpret |
|---|---|---|
| Debt/Equity | 負債比率 | <1.0 conservative, >2.0 high leverage |
| Current Ratio | 流動比率 | >1.5 safe, <1.0 liquidity risk |
| Quick Ratio | 速動比率 | >1.0 safe |
| Interest Coverage | 利息保障倍數 | >3x safe, <1.5x danger |
| Altman Z-Score | Z分數 | >2.99 safe, <1.81 distress |
3. 現金流 (Cash Flow) — 最重要的維度
| Metric | 中文 | How to interpret |
|---|---|---|
| Operating Cash Flow | 營業現金流 | Must be positive and growing |
| Free Cash Flow | 自由現金流 | OCF minus CapEx, must be positive |
| OCF / Net Income | 現金流/淨利 | >1.0 = high quality earnings |
| CapEx | 資本支出 | Context-dependent (growth vs maintenance) |
4. 成長性 (Growth)
| Metric | 中文 | How to interpret |
|---|---|---|
| Revenue Growth YoY | 營收年增率 | Trend over 3-5 years |
| Net Income Growth YoY | 淨利年增率 | Should track or exceed revenue growth |
| Book Value Growth | 每股淨值成長 | Steady growth = compounding |
5. 估值 (Valuation)
| Metric | 中文 | How to interpret |
|---|---|---|
| P/E | 本益比 | Compare to industry peers |
| P/B | 股價淨值比 | <1.0 may be undervalued |
| P/S | 股價營收比 | Useful for unprofitable companies |
| EV/EBITDA | 企業價值倍數 | <10 may be cheap |
| Dividend Yield | 股息殖利率 | >3% attractive for income |
6. 財務品質 (Quality Scores)
| Metric | 中文 | How to interpret |
|---|---|---|
| Piotroski F-Score | F分數 | ≥7 strong, ≤3 weak |
| Receivable Turnover Days | 應收帳款周轉天數 | Lower = faster collection |
| Inventory Turnover Days | 存貨周轉天數 | Lower = better efficiency |
Each company in the report MUST include:
- Company overview table — name, ticker, market, sector, market cap
- Key metrics summary — a single table with all ratios above (color-coded: green=good, yellow=watch, red=danger)
- Financial statements extract — revenue, net income, total assets, total liabilities, OCF for the last 3-5 periods in a trend table
- K-line chart iframe — embedded interactive chart
- Assessment — brief text interpretation of the numbers
Use fetcher_factory.py CLI to get real data:
# Financial statements (income, balance sheet, cash flow)
cd tool_scripts/market_data && uv run python fetcher_factory.py financials TICKER --market XX --period annual
# Key metrics (P/E, P/B, ROE, margins, etc.)
cd tool_scripts/market_data && uv run python fetcher_factory.py metrics TICKER --market XX
# Current quote (price, market cap, volume)
cd tool_scripts/market_data && uv run python fetcher_factory.py quote TICKER --market XX| Tool | Path | Usage |
|---|---|---|
| Send Telegram | tool_scripts/send_telegram/send_message.py |
--message "text" / --send-file path |
| Send Email | tool_scripts/send_mail/send_mail.py |
--subject "s" --body "b" |
| DB Manager | tool_scripts/db_ops/db_manager.py |
--init / --migrate |
| Stock Ops | tool_scripts/db_ops/stock_ops.py |
CRUD for stock universe |
| Price Ops | tool_scripts/db_ops/price_ops.py |
Daily price CRUD + bulk upsert |
| Financial Ops | tool_scripts/db_ops/financial_ops.py |
Financial + health scores CRUD |
| Research Cache | tool_scripts/db_ops/research_cache_ops.py |
Cache freshness check + mark |
| Screening Ops | tool_scripts/db_ops/screening_ops.py |
Screening results CRUD |
| Watchlist Ops | tool_scripts/db_ops/watchlist_ops.py |
Watchlist & notes CRUD |
| Fetcher Factory | tool_scripts/market_data/fetcher_factory.py |
get_fetcher("US") |
| Z-Score | tool_scripts/financial_calc/zscore.py |
calculate_zscore(data) |
| F-Score | tool_scripts/financial_calc/fscore.py |
calculate_fscore(data) |
| Ratios | tool_scripts/financial_calc/ratios.py |
calculate_ratios(data) |
| Screener | tool_scripts/financial_calc/screener.py |
screen(criteria, market) |
| Report Gen | tool_scripts/report_gen/markdown_report.py |
Generate markdown reports |
| Chart Gen | tool_scripts/report_gen/chart_gen.py |
matplotlib charts |
| Web Server | tool_scripts/web_server/ |
Django report viewer (port 8800) |
All reports MUST follow .claude/skills/report_generation_guide.md. Key requirements:
- Mobile-first HTML — viewport meta, RWD CSS, tables in
.table-container - Publish via URL — send web server link to Telegram, not raw HTML files
- Web server — Django at
http://localhost:8800, auto-reads fromoutput/ - Report naming — timestamp FIRST — Both files and directories MUST use
YYYYMMDD_HHMMSS_<type>format. The Django scanner regex requires^\d{8}_\d{4,6}_at the start. Reversed naming like<type>_YYYYMMDDwill silently 404.✅ output/20260301_104212_cn_hbm_companies/index.html ❌ output/cn_hbm_companies_20260301_104212/index.html - Pre-publish verification (MANDATORY) — NEVER send a report URL to the user before verification passes. Follow this exact sequence:
If Step B returns 404, DO NOT send the URL. Debug the naming issue first. If Step C screenshots show layout issues, fix the HTML before sending.
# Step A: Ensure web server is running curl -s -o /dev/null -w "%{http_code}" http://localhost:8800 | grep -q 200 || \ (cd tool_scripts/web_server && bash start_server.sh) # Step B: Verify the report URL returns HTTP 200 (NOT 404) curl -s -o /dev/null -w "%{http_code}" http://localhost:8800/reports/<SLUG>/ # If 404 → naming is wrong. Fix the directory name before proceeding. # Step C: Browser verification via Playwright (MANDATORY) # Mobile screenshot (375x812) npx playwright screenshot --browser chromium --viewport-size "375,812" --full-page --wait-for-timeout 3000 \ "http://localhost:8800/reports/<SLUG>/" /tmp/report_mobile.png # Desktop screenshot (1280x800) npx playwright screenshot --browser chromium --viewport-size "1280,800" --full-page --wait-for-timeout 3000 \ "http://localhost:8800/reports/<SLUG>/" /tmp/report_desktop.png # Read both screenshots to verify: # - Title displays fully, no truncation # - Tables are inside .table-container (horizontally scrollable) # - Cards and badges render correctly # - Text is readable (>= 13px) # - No horizontal overflow on body # - Color coding correct (green/orange/red) # Step D: Only after Step B AND Step C pass, reply with URL
When performing financial research, Claude MUST use the cache-first pattern to avoid redundant API calls. Fetched data is stored in the DB; subsequent requests read from cache.
| data_type | Max age | Rationale |
|---|---|---|
financials |
90 days | Quarterly filing cycle |
metrics |
24 hours | Price-dependent ratios |
company_info |
180 days | Rarely changes |
health_scores has no independent freshness — it is recomputed whenever financials are updated.
Any prompt involving 財務狀況 / 財報 / 健康 / 比較 / 篩選 triggers the cache-first workflow. Simple price queries do NOT trigger storage.
# 1. Check cache freshness
cd tool_scripts/db_ops && uv run python research_cache_ops.py --is-fresh AAPL US financials
# 2. If stale/missing → fetch + save + compute
cd tool_scripts/market_data && uv run python fetcher_factory.py financials AAPL --market US --period annual
cd tool_scripts/db_ops && uv run python financial_ops.py --bulk-upsert --json '<json_data>'
cd tool_scripts/db_ops && uv run python financial_ops.py --compute-health AAPL --market US
cd tool_scripts/db_ops && uv run python research_cache_ops.py --mark AAPL US financials
# 3. If fresh → read directly from DB
cd tool_scripts/db_ops && uv run python financial_ops.py --get AAPL --market US --period annual
cd tool_scripts/db_ops && uv run python financial_ops.py --get-health AAPL --market US
# 4. Also check metrics cache (24h freshness)
cd tool_scripts/db_ops && uv run python research_cache_ops.py --is-fresh AAPL US metrics
# If stale: fetch metrics → mark cache with --data '<json>'For multi-stock requests, run the single-stock workflow for each ticker. Companies with fresh cache skip API calls entirely.
API fetch (financials) → financials table → compute → health_scores table
API fetch (metrics) → research_cache.data_json
When users ask for recurring/scheduled tasks, follow .claude/skills/scheduler_sop.md. Key points:
- Script →
schedules/<task_name>.sh(shell script with Telegram notifications) - Plist →
~/Library/LaunchAgents/com.ccstockworkenv.<task_name>.plist(launchd scheduler) - Logs →
data/logs/<task_name>.log - launchctl limitation —
launchctl loadcannot run from Claude Code. Create the files, then tell the user to runlaunchctl loadin Terminal.app. - Always notify — Both success and failure must send Telegram messages.
All analysis is for research and educational purposes only. Not investment advice. Always do your own due diligence before making investment decisions.