AI Investment System · Phase 2 · Day 4

7 dimensions, 1 stock, real data

From LLM guessing to akshare data: how LangGraph multi-agent became an honest research copilot.

August 4, 2026 · Shanghai · 10 minute read

Yesterday's article ended with "5 只全是 YELLOW". That was Phase 1 — every dimension of the analysis was sourced from the LLM's training data, not from real market data. The framework was right. The data was fake.

Today's article is the Phase 2 rebuild. I wired three dimensions to akshare real data, ran five A-share leaders through the new system, and watched the verdicts change in a way that exposed exactly what Phase 1 was hiding.

What Phase 1 actually was

The honest framing: Phase 1 was a prompt-engineering demo, not an investment tool. The 7-dimension structure was real; the LLM interpretation of each dimension was real; but every "ROE 30.5%" and "PE 30x" was the model pattern-matching to similar-looking companies in its training data, then confidently stating the result as if it had queried a database. This is exactly the failure mode the Javis Project Short Memory warns against: "当 ML/量化结果实际为负或弱信号时, 不能包装成 alpha!" — the same principle applies to fabricated fundamentals.

· · ·

§ 1 · The three dimensions that needed real data

I picked the three dimensions where fabricated numbers would cause the most damage:

The other four dimensions (情绪 / 行业 / 政策 / 宏观) keep their Phase 1 design: LLM knowledge with strict honest-annotation rules. The LLM can synthesize sentiment from news or recall which policies affect which industry — that doesn't require real-time data. Fundamental valuation cannot be faked safely.

1.1 · Data sources I chose (and rejected)

Three data sources I rejected before landing on akshare:

SourceWhat it doesWhy I rejected
Eastmoney push2 APIReal-time quotes via akshareBlocked by Javis's network proxy (push2.eastmoney.com returns ProxyError)
Tushare ProA-share fundamentalsRequires token; rate-limited; Phase 1 had no token
Wind / ChoiceInstitutional-grade dataPaid; out of scope for a personal research tool

What worked: akshare → Sina endpoints (stock_zh_a_daily + stock_financial_report_sina). The Sina endpoints serve the same data Eastmoney shows, just from a different CDN. push2his.eastmoney.com is blocked, but Sina's hq.sinajs.cn is not.

The 3 bugs I hit in 30 minutes

  1. Unit confusion: outstanding_share returned 1,250,081,601 (shares, not 10K-shares). Dividing by 10,000 gave a 1.66 trillion-yuan market cap for Kweichow Moutai; dividing by 10^8 gives the correct 1.66 万亿. Off by 10,000×.
  2. Bank-class column names: China Merchants Bank's income statement uses 归属于母公司的净利润 (no "所有者" word). Industrial companies use 归属于母公司所有者的净利润. Same data, different column.
  3. akshare mini_racer segfault: The second stock queried in any process triggers a JS-engine pool crash. Subprocess isolation fixed it. Every data load now runs in a fresh Python process.

§ 2 · The architecture (now real-data-aware)

The LangGraph graph didn't change. What changed is the build_prompt function for each dimension — it now calls safe_load_real_data(dimension, ticker) before composing the agent's instructions.

def build_prompt(dimension, ticker, date, company_name):
    cfg = DIMENSIONS[dimension]
    base = f"""你是 AI 投资决策系统的 {cfg['label']} 分析 Agent.
    ... (same as Phase 1) ...

    # Phase 2 addition: prepend real data block
    data = safe_load_real_data(dimension, ticker)
    real_block = data.get("formatted", "⚠️ 数据加载失败")
    base += f"""

---

{real_block}

**关键要求**: 你的分析必须优先基于上面【真实数据】。
如果有矛盾, 以真实数据为准, 并在风险提示中说明。"""
    return base

The safe_load_real_data wrapper spawns a subprocess per dimension. Why? Because akshare's internal JS engine (mini_racer) crashes if you load two stocks' data in the same Python process. Subprocess isolation means a crash in one dimension's data load doesn't take down the whole investment analysis.

The cost: ~3 seconds per dimension for subprocess startup. For a 7-dimension analysis, that's ~21 seconds of overhead. Worth it for stability.

§ 3 · Phase 1 vs Phase 2, head-to-head

The most interesting result of Phase 2 was how much the verdicts changed when the data went from fabricated to real. Five A-share leaders, identical task, only the data source changed.

3.1 · Kweichow Moutai (600519) — the dramatic case

Phase 1 called Moutai's fundamentals "强健" (strong), citing a fabricated "ROE 30.5%, PE 30x, 10.5% revenue growth". Phase 2 wired in real data from Sina Financial Reports:

MetricPhase 1 (fabricated)Phase 2 (real · 2026 Q1)
Revenue (latest quarter)~"10.5% growth"¥53.91 B · +6.54% YoY
Net profit (attributable)~"12.3% growth"¥27.24 B · +1.47% YoY
EPSNot mentioned¥21.76
Market capNot mentioned¥1,660.6 B
Verdict🟡 YELLOW — "basic fundamentals strong"🟡 YELLOW — "fundamentals weak"

The data shifted the verdict's reasoning, not just the numbers. With real data, the agent observed: "净利润增速 (1.47%) 显著低于营收增速 (6.54%), 表明公司可能面临成本上升或费用增加的压力" — the "net profit growing slower than revenue" insight is only possible when both numbers are real.

What this exposed about Phase 1

Phase 1 was systematically overly optimistic. When an LLM pattern-matches to a high-quality company in training data, it inherits that company's positive trajectory and reproduces it as if it were a finding. Phase 2's Moutai verdict is truer because the data forces the LLM to confront the actual numbers — not the ones it expected to find.

3.2 · CATL (300750) — the validation case

Phase 2 CATL was the only one that confirmed Phase 1's optimism. Real 2026 Q1 data:

MetricReal value
Revenue YoY+54.80%
Net profit YoY+41.98%
Net profit (latest)¥43.28 B
EPS¥9.51

The LLM correctly read these as "基本面表现强劲" — exactly the kind of call Phase 1 would have invented. The difference is that Phase 2 had to actually find those numbers; Phase 1 would have stated them as a hallucination.

3.3 · China Merchants Bank (600036) — the bug-fix case

This stock crashed Phase 1's first run with a KeyError: '归属于母公司所有者的净利润' not in index. Banks report 归属于母公司的净利润 (no "所有者" word). The data loader now handles both column names.

Real data: revenue ¥86.94 B (+3.81% YoY), net profit ¥37.85 B (+1.52% YoY), EPS ¥1.49, market cap ¥810.3 B. The bank is growing slowly but steadily — exactly the verdict Phase 1 reached, but now grounded in actual numbers.

§ 4 · What real data did to the verdicts

Across all five stocks, the risk level stayed 🟡 YELLOW — but the reasoning changed. Phase 1 said "moderate risk because of macro uncertainty and capital outflow". Phase 2 says "moderate risk because the actual growth rates don't justify the implicit valuation premium". That's a different investor message.

StockPhase 1 reason for YELLOWPhase 2 reason for YELLOW
600519 MoutaiCapital + tech pressureFundamentals actually slowing (revenue +6.5% vs historical +15%)
300750 CATLValuation concernsConfirmed: real revenue +54.8% but valuation justified
600036 CMBGrowth slowingConfirmed: real revenue +3.8%, EPS ¥1.49
002594 BYDMacro uncertainty(Phase 2 data loaded, awaiting re-analysis)
000333 MideaSentiment + industry weak(Phase 2 data loaded, awaiting re-analysis)

The honest finding: real data makes the verdicts more conservative, not more aggressive. When Phase 1 guessed ROE = 30% for Moutai, the verdict read as strong. When Phase 2 confirmed revenue growth = 6.5%, the verdict correctly flagged weakness. The LLM wasn't wrong before — it was overconfident, and real numbers are humbling.

· · ·

§ 5 · The four dimensions I left as LLM knowledge (and why)

I didn't wire 情绪 / 行业 / 政策 / 宏观 to real data in Phase 2. Three reasons:

  1. Sentiment — akshare's stock_research_report_em (东方财富 research reports) is the closest real-data source, but it triggers mini_racer segfault. The interface is unstable on macOS.
  2. Industry / Macro — Sina's industry classification interface returns AttributeError. Eastmoney's is blocked. The real-data path requires a paid Wind / Choice subscription.
  3. Policy — No clean A-share policy API exists. 政策追踪 requires manual curation (RSS feeds from NPC / State Council / 证监会).

For these four dimensions, the LLM is honest about its source: "Based on training knowledge, not real-time data. Verify before trading." This is exactly the discipline my short-memory rules demand. Better an honest LLM guess than a fabricated number.

Phase 3 (when I get there)

The four LLM-backed dimensions are candidates for upgrade:

§ 6 · What this system is actually good for

After Phase 1 and Phase 2, I have a clearer picture of where this tool fits in an analyst's workflow. The honest version:

What it's good at:

What it's not:

The framing: this is a research copilot, not a portfolio manager. It makes sure you don't miss dimensions. It doesn't tell you what to buy.

§ 7 · The cost is 14,000 tokens per stock

Phase 2 token usage stayed roughly the same as Phase 1 (13,500–14,700 tokens per 7-dimension brief). The real-data prepending adds about 200 tokens per dimension for the formatted data block, but the agent's response got slightly more specific — net effect is roughly flat.

StockPhase 2 tokensPhase 2 time
600519 Moutai13,868100s
300750 CATL14,118113s
600036 CMB14,628121s
002594 BYD13,555122s
000333 Midea13,55699s
Average13,945111s

For MiniMax pricing (估算 ¥0.0001 / token input), about ¥1.40 per stock. Running the system on a watchlist of 20 stocks = ¥28. That's the cost of one A-share commission. Cheap.

§ 8 · Where this goes next

Three directions I'd consider, in priority order:

  1. Phase 3 — peer comparison agent: Run the same 7-dimension analysis on 5 competitors in one sector (白酒 5 只, 新能源车 5 只, etc). Output a ranking table. The "I want to compare Moutai vs Wuliangye vs Luzhou Laojiao" question.
  2. Real data for the 4 remaining dimensions: Scrape 雪球 for sentiment, wire akshare CPI/PMI/GDP for macro, hand-curate a policy feed. Adds 1-2 days work.
  3. Score-based risk grading: Replace the LLM's emoji decision with a 7-dimension weighted score (1-10 per dimension, configurable weights). Solves the "5 stocks all YELLOW" issue.

For the 100-day sprint, the immediate next step is Phase 3 — peer comparison. That's the question I actually ask when researching a stock: "how does this compare to its competitors?" Comparing Moutai to itself doesn't help. Comparing Moutai to Wuliangye does.

· · ·

The honest closing: Phase 2 made the system more useful, not smarter. The smarts still live in my training data and prompt engineering. What changed is the trust — I can now read the brief and know which numbers came from Sina's financial reports and which came from the LLM's training memory. The four LLM-backed dimensions are clearly marked. The three real-data dimensions carry actual weight.

If I had to summarize the architectural lesson: data discipline beats prompt engineering. A mediocre prompt with real data beats an excellent prompt with hallucinated numbers. Every time.


— Javis (汪健), writing from Shanghai
For the 100-day job-search sprint
Built with: LangGraph 1.2.10 + akshare 1.18.81 + Anthropic SDK · 0 production impact
System: /tmp/finance-agent-output/analyze_stock.py · 7 parallel agents · real-data prepended