JPstock-agent: Thematic Equity Research Workflow
2026-04-22 19:16 · 28.7 KB
{#
================================================================================
ANALYSIS WORKFLOW TEMPLATE
This is NOT a prompt template — it documents the step-by-step methodology
for conducting thematic equity research using JPstock-agent.
Use this as a checklist for future analysis runs.
Last updated: 2026-04-14 (Claude-only analysis + J-Quants + site publishing)
================================================================================
#}
JPstock-agent: Thematic Equity Research Workflow
Overview
This template documents the end-to-end process for producing a validated
equity research report on a thematic investment idea (e.g., nuclear restart,
defense spending, AI/semiconductors).
Total time: ~30-60 minutes depending on stock count and LLM availability.
Analysis Architecture (Claude-only, J-Quants data)
| Step | What | How |
|---|---|---|
| 0. Data | Fetch fundamentals from J-Quants (paid plan, 107 columns) | Python function: fetch_jquants_with_valuation(ticker) |
| 1. Full DD | 14-section DD + dimension scores + peer comparison | Claude writes, submits via PUT /api/stocks/{ticker}/deep-dd |
| 2. Stock Card | Per-stock WHY/RISK/TRIGGER with peer context | Claude writes, included in deep-dd submission |
| 3. Research | Web search, macro, cross-validation, supply chain | Claude performs web searches, validates metrics |
| 4. Portfolio | Rank, weight, construct, write report | Claude builds final report with sourced evidence |
No Qwen/LLM involved. Claude does all analysis. J-Quants provides the data.
Why no Qwen:
- Claude has web search — validates metrics, finds macro context, sources evidence
- Claude has judgment — conviction calls, weight decisions, risk assessment
- Claude produces higher quality narratives than Qwen's JSON-constrained output
- J-Quants provides official JPX data (107 columns) — no need for yfinance's unreliable ratios
Data sources (use the RIGHT source for each metric):
| What | Primary Source | Why | Avoid |
|---|---|---|---|
| PE, PB, valuation ratios | StockAnalysis.com | Handles splits correctly, split-adjusted | J-Quants (split bugs), yfinance (known errors) |
| Raw financials (revenue, NI, margins) | J-Quants | Official JPX data, 107 columns | yfinance (unreliable for JP stocks) |
| Company forecasts (fwd EPS, guidance) | J-Quants | Direct from company filings | StockAnalysis (analyst consensus, not company) |
| Cash flow (CFO, CFI, FCF) | J-Quants | Official filing data | yfinance (can be wrong, see Yamato Kogyo) |
| Cross-validation | Yahoo Finance | Good secondary check | — |
J-Quants PE CAVEATS (verified from actual filings 2026-04-17):
J-Quants raw data (EPS, NI, shares from 決算短信) is ACCURATE. But the PE our code calculates has 2 issues:
1. SPLIT BUG: After a stock split, J-Quants filing EPS is pre-split but price is post-split.
Must divide EPS by split ratio. Known: KHI 5:1, IHI 7:1, SBI 2:1.
Fix: check ShOutFY changes between filings to detect splits.
2. TIMELINESS: J-Quants may have older filings than StockAnalysis (e.g., Nidec had 2Q
while StockAnalysis had 3Q data). This is a data lag, not a bug.
3. METHODOLOGY: J-Quants annualizes cumulative period EPS (e.g., 3Q×4/3). StockAnalysis
uses rolling TTM (sum of last 4 quarters). Both are valid but can differ 10-20%.
Best practice:
- Use StockAnalysis PE as the display value (split-adjusted, rolling TTM, widely understood)
- Use J-Quants raw EPS/NI for fundamental analysis (official filing data, most accurate)
- If PE gap >20% between sources, investigate: is it a split? stale filing? different methodology?
J-Quants IS reliable for:
- Financial summaries: revenue, OP, NI, EPS, BPS, total assets, equity, CFO/CFI/CFF
- Company forecasts: forward revenue, forward EPS, forward OP (company guidance)
- Prices: official TSE OHLCV
- Universe: 3,800+ TSE stocks with sector/market classification
- Derived metrics calculated locally: PE, PB, ROE, margins, D/E, FCF, dividend yield
- Function:
from src.data.jquants_fundamentals import fetch_jquants_with_valuation
Pipeline order: J-Quants data fetch → Claude research + analysis → Claude submits deep-dd → HTML report
PHASE 1: Theme Identification & Stock Universe (10 min)
1a. Identify core stocks
Two approaches — use BOTH, not just one:
Approach A: Direct search (good for Japan-specific themes)
Query existing stocks, search for Japanese companies in this sector.
Good for: Nuclear, Tourism, Governance, VTuber, Defense — themes where Japan IS the market.
Approach B: Global-first value chain tracing (the "T-glass method")
Use when the theme is driven by a GLOBAL industry (AI, EV, semiconductors, etc.):
1. Identify the global leaders (any country) — e.g., Nvidia, TSMC, Tesla, BYD
2. Trace their value chain backwards — from end product to raw materials
3. At each layer ask: "Who is the dominant supplier? Is it Japanese? Is the position irreplaceable?"
4. Search in English, Japanese (日本語), AND Chinese (中文) — Taiwan/China industry sources (DigiTimes, TrendForce) often have the best supply chain intelligence
Why this matters: The T-glass discovery proved that the most valuable Japanese companies are often INVISIBLE upstream suppliers — not the obvious names. Nittobo (90% T-glass monopoly) was missing from our AI coverage because we started by looking for "AI companies in Japan" instead of tracing "what does an Nvidia GPU need to be manufactured?"
1b. Value chain mapping
Every theme has a value chain — the number of layers varies by industry:
| Theme Type | Typical Layers | Example |
|---|---|---|
| Semiconductor/AI | 10-13 layers | Materials → Equipment → Fab → Packaging → Components → System |
| EV/Automotive | 5-8 layers | Materials → Battery → Drivetrain → Assembly → Infrastructure |
| Nuclear/Energy | 4-6 layers | Fuel → Equipment → Operator → Grid → Services |
| Consumer/Tourism | 3-5 layers | Infrastructure → Services → Distribution → Experience |
Map the value chain for YOUR theme, then check Japan's position at each layer.
1c. Moat rating
Rate each Japanese company's structural advantage:
| Rating | Criteria | Example |
|---|---|---|
| ★★★★★ MONOPOLY | >80% share, no alternative, impossible to replicate in <10yr | TEL coater/developer, Nittobo T-glass |
| ★★★★ DOMINANT | >50% share or duopoly, very difficult to replicate | Shin-Etsu wafers, Hoya mask blanks |
| ★★★ STRONG | 20-50% share, defensible but competitors exist | Murata MLCC, Ibiden substrates |
| ★★ COMPETITIVE | <20% share, multiple competitors | Renesas MCU, Hirose connectors |
| ★ WEAK | No structural advantage vs global peers | Japan PCB mfg vs Taiwan |
Prioritize ★★★★★ and ★★★★ for investment. Include ★★★ for diversification. Skip ★★/★ unless valuation is extreme.
1d. Competitive context
For key positions, briefly compare Japan vs the closest global competitor.
This prevents overestimating Japan's edge (e.g., Asahi Kasei Q Glass looked like a monopoly until we found Glotech and Feilihua competing).
1e. Gap check
Read existing theme JSONs. Ask:
- Which value chain layers have NO Japanese stock coverage?
- Are we missing upstream material suppliers? (These are where monopolies hide)
- Have we only covered the "obvious" names and missed the Nittobo-type hidden chokepoints?
1f. Select stocks for deep DD
Focus on 5-10 stocks:
- At least 1 from each major value chain layer
- Prioritize positions with structural moats (★★★+)
- Mix of pure-plays and diversified exposure
PHASE 2: Data Gathering (10 min)
2a. Pull fundamentals from database
`sql
SELECT f.ticker, s.name, f.metric_name, f.metric_value, f.fiscal_date, f.fetched_at
FROM fundamentals f
JOIN stocks s ON f.ticker = s.ticker
WHERE f.ticker IN ({{ core_tickers | join(', ') }})
AND f.fiscal_date = (SELECT MAX(fiscal_date) FROM fundamentals WHERE ticker = f.ticker)
ORDER BY f.ticker, f.metric_name
`
Key metrics to extract:
- Valuation: pe_ratio, forward_pe, pb_ratio, ev_ebitda, dividend_yield
- Quality: roe, roa, operating_margin, net_margin, ebitda_margin
- Growth: revenue_growth, earnings_growth
- Health: debt_to_equity, current_ratio
- Other: beta, market_cap, free_cashflow, payout_ratio
2b. Check data freshness
CRITICAL: Note the fetched_at timestamp. If data is >7 days old, consider
re-fetching fundamentals before analysis:
`
make fetch-fundamentals TICKER=XXXX.T
`
2c. Pull relationships and peer data
`sql
SELECT source_ticker, target_ticker, relationship_type, description, confidence
FROM company_relationships
WHERE source_ticker IN ({{ core_tickers }}) OR target_ticker IN ({{ core_tickers }})
AND confidence >= 0.6
`
2d. Gather macro context via web research
Search for:
1. Current policy/regulatory status for the theme
2. Recent government announcements or legislation
3. Demand drivers and catalysts (e.g., data center demand for nuclear)
4. Any recent corporate news for the core stocks
5. Consensus analyst views and target prices
Sources to check:
- NRA Japan (nuclear), MOD Japan (defense), METI (energy policy)
- Nikkei, Reuters Japan, Bloomberg
- World Nuclear Association, EIA, IEA (for energy themes)
- Kabutan, Yahoo Finance JP, Minkabu (for stock-specific)
PHASE 3: Build Comparison Table (10 min)
3a. Construct the peer comparison table
Organize all core stocks in a single table with these columns:
| Category | Metrics |
|---|---|
| Identity | Ticker, name, role, nuclear/theme exposure description |
| Valuation | PE (TTM), Forward PE, P/B, EV/EBITDA, dividend yield |
| Quality | ROE, operating margin, EBITDA margin |
| Health | D/E ratio, current ratio |
| Growth | Revenue growth, EPS growth |
| Technical | Beta, 52-week range, distance from high |
| Size | Market cap |
3b. Normalize units
IMPORTANT — these are the normalized scales after our data pipeline fixes:
- Ratios (ROE, margins, yields, D/E): stored as decimals (0.12 = 12%). Display as %.
- Currency (market_cap, EV, FCF): stored in raw JPY. Display as ¥B (divide by 1e9).
- Multiples (PE, PB, EV/EBITDA): stored as raw numbers. Display as-is with 'x'.
3c. Flag obvious data anomalies
Before proceeding, scan for:
- [ ] Any dividend yield > 10% → likely data error
- [ ] Any PE < 0 → loss-making, use forward PE instead
- [ ] Forward PE wildly different from trailing → check for stock splits, one-time charges
- [ ] D/E > 300% → flag as high leverage, verify it's real
- [ ] ROE negative → flag as distressed
- [ ] Any metric that seems 10x off from peers → investigate
LESSON LEARNED (IHI 7013.T, April 2026): yfinance can produce incorrect
forward EPS after stock splits. IHI did a 7-for-1 split in Oct 2025, and
yfinance mixed pre-split forward EPS (¥578) with post-split share count,
producing a fake forward PE of 6.1x (actual: ~26.7x). ALWAYS cross-check
forward PE against at least one web source when the gap vs trailing PE is >50%.
PHASE 4: Web Validation (15 min)
4a. Cross-check every number against independent sources
For each core stock, validate against at least ONE of:
- StockAnalysis.com (https://stockanalysis.com/quote/tyo/XXXX/)
- Yahoo Finance JP (https://finance.yahoo.co.jp/quote/XXXX.T)
- Kabutan (https://kabutan.jp/stock/?code=XXXX)
- Buffett Code (https://www.buffett-code.com/company/XXXX)
Metrics to validate:
- [ ] PE ratio (trailing and forward)
- [ ] P/B ratio
- [ ] ROE
- [ ] Dividend yield
- [ ] Market cap
- [ ] Any metric used in a recommendation
4b. Validate macro claims
Every macro/policy claim must have a named source:
- [ ] Government policy documents (cite specific plan/date)
- [ ] Reactor count (cite NRA or World Nuclear Association)
- [ ] Demand forecasts (cite research firm and report date)
- [ ] Corporate events (cite press release or news article)
4c. Document discrepancies
Create an errata table:
| Original Value | Validated Value | Source | Impact on Thesis |
|---|
If a correction changes the ranking, update the ranking.
PHASE 5: Analysis & Ranking (10 min)
5a. Score each stock across dimensions
Use the Condensed Equity Peer Comparison framework:
| Category | Weight | What to evaluate |
|---|---|---|
| Business quality & moat | 25% | Market position, pricing power, barriers |
| Growth | 15% | Revenue/EPS trajectory, capacity expansion |
| Profitability | 15% | Margins, ROE vs TSE 8% target |
| Cash flow & balance sheet | 15% | FCF, leverage, liquidity |
| Valuation | 15% | Relative and absolute, premium/discount |
| Catalysts & timing | 10% | Near-term (0-6M) and medium-term (6-24M) |
| Risk | 5% | Downside scenarios, governance |
Japan-specific factors to always consider:
- Corporate governance reform (JPX 2023: PBR > 1.0 push)
- BOJ monetary policy and yen dynamics
- Sector policy tailwinds (nuclear, defense, semiconductors)
- Shareholder return trends (payout ratio expansion)
5b. Produce ranking with conviction levels
| Conviction | Criteria |
|---|---|
| HIGH | Valuation compelling + quality confirmed + clear catalyst + manageable risk |
| MEDIUM | Good but missing one element (expensive, or catalyst uncertain) |
| LOW | Speculative or overvalued — only with position sizing caveat |
5c. Generate Stock Cards (Layer 2 — Comparison)
For each core stock, generate a stock card that adds peer comparison context.
This bridges the gap between per-stock DD (Layer 1) and portfolio ranking (Layer 3).
`
GET /api/stocks/{ticker}/stock-card
`
Each stock card produces:
| Field | Content |
|---|---|
| thesis | WHY this stock — 2-3 sentences with specific catalysts |
| weight_pct | Suggested weight (integer %) |
| weight_rationale | WHY this weight — comparison to peers |
| risk | What could go wrong — 2-3 specific risks with metrics |
| monitoring_trigger | When to add/trim — If [metric/event], then [action] |
| dimension_scores | Quality, growth, valuation, health, catalyst, moat (1-10) |
| vs_peers | Best at, worst at, unique edge vs peer group |
| scenario | Bull/base/bear with probabilities |
5d. Write scenario analysis for top picks
For each HIGH conviction stock:
- Bull case: What goes right + approximate upside % + key assumption
- Base case: Status quo + expected return (yield + modest re-rating)
- Bear case: What goes wrong + approximate downside % + key risk
5e. Construct recommended portfolio
Use stock card suggested weights as starting point, then adjust:
- HIGH conviction: 15-30% each (stock card may suggest higher)
- MEDIUM conviction: 5-15% each
- LOW/speculative: 3-5% max
- AVOID: 0% with explanation
- Total must sum to 100%
- Verify no single stock exceeds 35% (concentration limit)
PHASE 5.5: Claude Deep DD (All Analysis)
Claude performs ALL analysis. J-Quants provides the raw data.
No Qwen/LLM intermediary — Claude writes everything directly.
5.5a. Per-stock deep research (Claude)
For each stock, Claude performs ANALYST-LEVEL research (not just metric checking):
1. Financial filings & IR — the primary source of truth
- Search: '{company name} IR 決算 2026', '{company} investor relations presentation'
- Read: latest earnings presentation (決算説明資料), medium-term plans, segment data
- Extract: revenue by segment, margin trajectory, guidance revisions, capex plans, return policy
2. Press releases & corporate news
- Search: '{company name} press release 2026', '{会社名} プレスリリース 2026'
- Look for: new contracts, M&A, partnerships, product launches, management changes
- Grade each finding by materiality: HIGH (thesis-changing) / MEDIUM / LOW
3. Global/macro impact analysis
- Trade policy: tariffs, export controls affecting this company
- Commodity prices: steel, oil, copper, rare earth (if relevant to this stock)
- FX: yen strength/weakness impact on this company's earnings
- Industry regulation: new laws, standards, subsidies
- Competitor moves: global competitors entering/exiting Japan
4. Supply chain evidence — MANDATORY, every stock must have 2-3+ entries
- Grade: CONFIRMED (press release/filing) / PROBABLE (industry standard) / INFERRED (logical)
- Date every evidence link for freshness
- Check for supply chain CHANGES (new wins, losses, disruptions)
- Each entry: {customer, product, evidence, detail, source_url, date}
- source_url is REQUIRED for 'confirmed' evidence — link to actual press release, filing, or article
- date when the relationship was confirmed or last verified
- Search for: key customers, key suppliers, JV partners, government contracts
- NO stock should have empty supply_chain[] — if missing, fill during evolve/explore rounds
- Future validation: source_url enables re-checking if the relationship still holds
5. Key dates refresh — keep catalyst timeline current
- Remove past dates that have occurred
- Update earnings dates from company IR calendar
- Add NEW catalyst dates discovered during research
- Format: {date, type: earnings|catalyst|monitoring|rebalance, description}
6. Thesis validation — the most important step
- Does the original WHY still hold? What's strengthened/weakened?
- Are monitoring triggers being hit?
- Has conviction changed? (HOLD / UPGRADED / DOWNGRADED / EXIT)
- New risks discovered?
7. Per-stock deep card — Claude writes/updates the WHY/RISK/TRIGGER card:
`
WHY this stock: [2-3 sentences with specific catalysts, sourced]
WHY XX%: [weight rationale comparing to peers, with numbers]
What could go wrong: [2-3 risks with specific metrics/thresholds]
Monitoring trigger: [If metric/event, then add/trim. Specific dates.]
`
8. Metric cross-validation — verify data against independent sources
- StockAnalysis.com, Kabutan, Yahoo Finance JP
- Flag and correct any errors (stock splits, stale data)
- Build errata table: Original | Validated | Source | Impact
5.5b. Data + Analysis flow
For each stock:
1. fetch_jquants_with_valuation(ticker) → raw fundamentals + derived metrics
2. Claude web research → macro context, policy data, supply chain evidence
3. Claude cross-validates J-Quants metrics against web sources → errata
4. Claude writes 14-section DD + stock card + dimension scores
5. PUT /api/stocks/{ticker}/deep-dd → stores everything in DB
6. GET /api/stocks/{ticker}/deep-dd → retrieves for report generation
5.5c. Task breakdown
| Task | How | Notes |
|---|---|---|
| Fetch fundamentals | fetch_jquants_with_valuation(ticker) | J-Quants paid plan, 107 columns, official JPX data |
| Calculate derived metrics | Same function (automatic) | PE, PB, ROE, margins, D/E, FCF — all from raw J-Quants data |
| Compute Piotroski F-Score | _compute_piotroski(ticker, metrics) | Formula-based, called in dd_report.py |
| Web search for macro/policy | Claude web search | Government sources, industry reports, news |
| Cross-validate metrics | Claude checks StockAnalysis/Kabutan | Flag yfinance/J-Quants discrepancies |
| Write 14-section DD | Claude writes | Executive summary through verdict |
| Score dimensions (1-10) | Claude scores | Quality, growth, valuation, health, catalyst, moat |
| Write stock card (WHY/RISK/TRIGGER) | Claude writes | Thesis, weight rationale, risks, monitoring triggers |
| Peer comparison | Claude compares | Best at, worst at, unique edge vs peers |
| Rank stocks | Claude ranks | Conviction, tier (Core/Satellite/Tactical) |
| Set portfolio weights | Claude decides | Based on conviction, quality, diversification |
| Write HTML report | Claude writes | Full narrative with sourced evidence |
| Submit to DB | PUT /api/stocks/{ticker}/deep-dd | All analysis stored for report generation |
Qwen/Ollama is NOT used. All analysis is Claude. Data is J-Quants.
PHASE 6: Report Generation & Publishing
6a. Generate theme JSON data files
For each theme, create reports-site/data/{theme}_companies.json:
`python
from src.storage.database import init_db, get_dd_report
import json
init_db()
dd = get_dd_report(ticker, 'deep_dd')
c = json.loads(dd['raw_llm_response'])
vm = c.get('validated_metrics', {})
company = {
'ticker': t, 'name': c.get('name', t),
'weight': c.get('weight_pct', 0) / 100.0,
'conviction': c.get('conviction', ''),
'layer': c.get('layer', ''),
'thesis': c.get('thesis', ''),
'risk': c.get('risk', ''),
'monitoring_trigger': c.get('monitoring_trigger', ''),
'pe': vm.get('pe_ratio', 'N/A'),
'forward_pe': vm.get('forward_pe', 'N/A'),
'pb': vm.get('pb_ratio', 'N/A'),
'roe': fmt_pct(vm.get('roe')), # "12.2%"
'op_margin': fmt_pct(vm.get('operating_margin')),
'de': fmt_pct(vm.get('debt_to_equity')),
'div_yield': fmt_pct(vm.get('dividend_yield')),
'fcf': '+¥28B' or '-¥11B' or 'N/A', # formatted with +/- prefix
'market_cap': '¥165B', # formatted
'segments': [],
'supply_chain': [{'customer': '...', 'product': '...', 'evidence': 'confirmed', 'detail': '...'}],
'themes': ['Theme Name'],
'data_date': '2026-04-14',
'sources': ['https://...'],
'key_dates': [{'date': '2026-05', 'event': 'FY2026 earnings'}],
}
output = {
'theme_title': 'Theme Name',
'macro_context': 'Sourced macro text...',
'companies': [company, ...],
'data_date': '2026-04-14',
}
Write to reports-site/data/{theme}_companies.json
`
6b. Generate portfolio JSON (for cross-theme reports)
Create reports-site/data/portfolio_{name}.json with additional fields:
`python
{
'title': 'Report Title',
'subtitle': 'Description',
'date': '2026-04-14',
'method': 'Analysis methodology description',
'companies': [
# Same as theme JSON but add:
'theme': 'Crypto', # for cross-theme color coding
'weight_rationale': 'WHY X%...',
'what_market_misses': '...',
'dimension_scores': {'quality': 8, 'growth': 6, ...},
'vs_peers': {'best_at': '...', 'worst_at': '...', 'unique_edge': '...'},
'scenario': {'bull': {...}, 'base': {...}, 'bear': {...}},
],
'errata': [{'ticker': '...', 'metric': '...', 'original': '...', 'actual': '...', 'source': '...', 'impact': '...'}],
}
`
6c. Register new themes on the site
Add to THEME_CONFIG in reports-site/lib/data.ts:
`typescript
{theme_slug}: { color: "#hex", file: "{theme}_companies.json" },
`
6d. Map portfolio reports to JSON rendering
Add to `REPORT_JSON_MAP` in TWO places:
1. reports-site/app/reports/[slug]/page.tsx — for Classic site rendering
2. scripts/generate_kairos_data.py — for Kairos site rendering
`typescript
"report_slug_name": "portfolio_{name}.json",
`
This makes reports render with company cards (dimension scores, scenarios, errata)
instead of plain markdown. Reports NOT listed render as markdown-only.
6e. Also create markdown export
Write reports-site/exports/{slug}.md so the report appears on /reports listing page.
The markdown is a fallback — the JSON rendering takes priority when mapped.
6f. Update History (CRITICAL — do this for EVERY data change)
When modifying any company data in theme JSONs, ALWAYS add tracking:
Per-company — append to company.update_history[]:
`json
{
"date": "2026-04-14",
"type": "evolve", // or "explore" or "new_narrative"
"report": "nuclear_dd_evolution_2026-04-14", // slug matching exports/ filename
"summary": "Dividend raised ¥64→¥78, yield 1.1%→1.4%",
"changes": [
{"field": "div_yield", "old": "1.1%", "new": "1.4%", "source": "J-Quants"}
],
"thesis_status": "HOLD" // or "UPGRADED" / "DOWNGRADED" / "EXIT"
}
`
Per-theme — append to theme.recent_updates[]:
`json
{
"date": "2026-04-14",
"type": "evolve",
"report": "nuclear_dd_evolution_2026-04-14",
"summary": "Updated 3 stocks: JSW, Yokogawa, Organo",
"stocks_updated": ["5631.T", "6841.T", "6368.T"]
}
`
The report slug creates clickable links: stock page → /reports/{slug} → evolve report.
6g. Regenerate Kairos data files
After updating ANY theme JSON, portfolio JSON, or markdown export, ALWAYS regenerate:
`bash
python3 scripts/generate_kairos_data.py
`
This reads all data files and produces 3 JS files for the Kairos homepage:
reports-site/public/kairos/data.js— stocks, themes, links, prices, newsreports-site/public/kairos/data_meta.js— thesis, conviction, metrics, scenariosreports-site/public/kairos/data_report.js— reports with full markdown + portfolio data
If you skip this step, the Kairos site at `/` will show stale data.
6h. Deploy
NEVER deploy manually with wrangler. Always:
`bash
python3 scripts/generate_kairos_data.py # Regenerate Kairos data FIRST
git add reports-site/
git commit -m "data: {type} DD — {theme}"
git push origin main
`
GitHub Actions (deploy-reports.yml) handles: npm run build → wrangler pages deploy out
Site: reports.ddbancho.com
Pages rendered at:
/→ Kairos SPA (default homepage — force-directed graph, theme explorer, reports)/kairos/→ same Kairos SPA/home→ Classic homepage (rankings, themes grid)/themes/{slug}— Classic theme page/reports/{slug}— Classic report page (PortfolioReport or markdown)/portfolio— Classic portfolio view
6g. Rendering components
PortfolioReport(reports-site/components/PortfolioReport.tsx):
Shared component for portfolio-style rendering. Used by /reports/[slug] and /portfolio.
Renders: comparison table, conviction breakdown, stock cards (WHY/RISK/TRIGGER),
dimension scores, vs_peers, scenarios, errata table, methodology, sources.
- Theme pages (
reports-site/app/themes/[theme]/page.tsx):
Comparison table + company cards. Click through to individual stock pages.
- Stock pages (
reports-site/app/themes/[theme]/[ticker]/page.tsx):
Price chart (TradingView), thesis, risk, trigger, metrics grid, segments,
supply chain evidence, sources, peer comparison table.
PHASE 7: Quality Checklist (Before Publishing)
Data integrity
- [ ] All financial metrics cross-checked against at least one web source
- [ ] Forward PE validated (especially after stock splits or one-time charges)
- [ ] Data freshness noted (fetched_at date visible in report)
- [ ] Fiscal period noted (which quarter the data covers)
Analysis quality
- [ ] No internal contradictions (e.g., "Buy" in text but "Avoid" in table)
- [ ] Numbers cited in narrative match the comparison table
- [ ] Peer comparisons are apples-to-apples (same metric definitions)
- [ ] Scenario analysis has specific assumptions, not just "things go well"
Macro claims
- [ ] Every policy/regulatory claim has a named source with date
- [ ] Demand forecasts cite the research firm and report date
- [ ] Reactor counts / status verified against official sources
- [ ] Corporate events verified against press releases
Report formatting
- [ ] Errata section clearly shows what was corrected and why
- [ ] Disclaimer present
- [ ] All sources listed with dates
- [ ] Report date prominent
Known Data Pipeline Issues (Update as Found)
| Issue | Date Found | Affected Stocks | Workaround |
|---|---|---|---|
| yfinance PE wrong after stock splits | 2026-04-14 | SBI (2:1 Nov 2025), KHI (5:1 Mar 2026), IHI (7:1 Oct 2025) | Use J-Quants or StockAnalysis. Always cross-check when gap vs trailing >50% |
| J-Quants FCF can be massively wrong | 2026-04-14 | Yamato Kogyo (showed -¥27.7B, actual +¥39.9B) | Always cross-validate FCF against StockAnalysis |
| J-Quants D/E uses total liabilities (not interest-bearing debt) | 2026-04-14 | ShinMaywa (1.42x vs 0.54x), Monex (5.04x vs 0.85x) | StockAnalysis uses interest-bearing debt only. Note which definition. |
| yfinance operating margin can be wrong | 2026-04-14 | Nihon Kohden (showed 4.3%, actual 9.2%) | Always verify against company filings |
| yfinance PB wrong after stock splits | 2026-04-14 | SBI (showed 1.14, actual 0.85 — below book value) | Use J-Quants or StockAnalysis |
| yfinance dividendYield returns % not ratio for JP stocks | 2026-03-29 | All .T stocks | Fixed in pipeline (divide by 100 at ingestion) |
| yfinance debtToEquity returns % not ratio | 2026-03-29 | All .T stocks | Fixed in pipeline (divide by 100 at ingestion) |
| Piotroski F-Score requires 2 periods (usually only 1 available) | 2026-03-29 | All stocks | 5/9 criteria default to 0; flag as incomplete |
| J-Quants price data may be stale (not real-time) | 2026-04-14 | Tokyo Keiki (showed ¥4,770, actual ¥7,690) | Check multiple recent dates, verify against live sources |
| wrangler manual deploy overwrites Next.js static export | 2026-04-14 | Site deployment | NEVER use wrangler manually. Always git push → GitHub Actions |