from __future__ import annotations import hashlib import importlib.util import json import sys from pathlib import Path from typing import Any ROOT = Path(__file__).resolve().parents[2] sys.path.insert(0, str(ROOT / "dev" / "ana-dev")) sys.path.insert(0, str(ROOT / "dev" / "project-dev")) BASE_PATH = ROOT / "ai-valuation-analyst" / "tools" / "generate_semiconductor_batch_20260804.py" SPEC = importlib.util.spec_from_file_location("valuation_batch_base_for_ubtech", BASE_PATH) if SPEC is None or SPEC.loader is None: raise RuntimeError(f"cannot load {BASE_PATH}") base = importlib.util.module_from_spec(SPEC) sys.modules[SPEC.name] = base SPEC.loader.exec_module(base) AS_OF = "2026-08-04" BATCH_ID = "BATCH-STOCK-VALUATION-20260805-003" FX_DATE = "2026-08-03" CNY_PER_HKD = 0.8612 CNY_TO_HKD = 1 / CNY_PER_HKD SHARES = 503_401_373 PRICE = 86.95 RESULT_DIR = ROOT / "ana-data" / "result" / "股票估值" / "20260805_ubtech_robotics_valuation" CASE_RAW = ROOT / "ana-data" / "cases" / "股票估值" / BATCH_ID / "raw" / "09880_HK" REGISTRY = ROOT / "dev" / "ana-dev" / "stock_valuation_pipeline" / "source_registry.json" def hkd(cny: float) -> float: return cny * CNY_TO_HKD def write_json(path: Path, value: Any) -> None: path.parent.mkdir(parents=True, exist_ok=True) path.write_text(json.dumps(value, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") def sha256(path: Path) -> str: return hashlib.sha256(path.read_bytes()).hexdigest() FORECASTS_RMB = [ {"institution": "大和证券(DAIWA)", "report_date": "2026-07-02", "profit": 349_000_000, "eps": 0.692, "rating": "买入"}, {"institution": "招商证券国际", "report_date": "2026-07-02", "profit": -304_000_000, "eps": -0.600, "rating": "买入"}, {"institution": "美国银行证券", "report_date": "2026-07-01", "profit": -181_000_000, "eps": -0.3603, "rating": "买入"}, {"institution": "摩根大通", "report_date": "2026-07-02", "profit": -402_000_000, "eps": -0.800, "rating": "增持"}, {"institution": "海通国际", "report_date": "2026-05-19", "profit": -131_000_000, "eps": -0.260, "rating": "优于大市"}, {"institution": "国泰君安国际", "report_date": "2026-04-20", "profit": -268_000_000, "eps": -0.589, "rating": "买入"}, {"institution": "花旗", "report_date": "2026-07-03", "profit": -288_000_000, "eps": -0.573, "rating": "买入"}, ] def source_register() -> dict[str, Any]: return { "batch_id": BATCH_ID, "ticker": "09880.HK", "as_of": AS_OF, "currency_policy": { "valuation_currency": "HKD", "financial_reporting_currency": "CNY", "cny_per_hkd": CNY_PER_HKD, "fx_date": FX_DATE, "reason": "估值日为2026-08-04;采用估值日前最近可核验的2026-08-03历史即期汇率。", }, "market": { "price_hkd": PRICE, "price_date": AS_OF, "ohlc_hkd": {"open": 86.40, "high": 87.15, "low": 85.00, "close": 86.95}, "volume": 3_721_852, "amount_hkd": 321_395_280, "shares": SHARES, "shares_date": "2026-07-31", }, "financials_rmb": { "2025": { "revenue": 2_000_999_000, "attributable_profit": -703_191_000, "cfo": -784_111_000, "capex": 409_024_000, "attributable_equity": 7_217_986_000, "cash_available": 4_887_880_000, "interest_bearing_debt": 1_123_000_000, "minority_interest": 107_618_000, "non_operating_financial_assets": 120_043_000, }, "2024": { "revenue": 1_305_361_000, "attributable_profit": -1_123_590_000, "cfo": -883_636_000, "capex": 226_968_000, }, }, "institutions_2026_rmb": FORECASTS_RMB, "source_urls": { "hkex_title_search": "https://www1.hkexnews.hk/search/titlesearch.xhtml?category=0&market=SEHK&stockId=1000208870", "hkex_2025_annual_results": "https://www1.hkexnews.hk/listedco/listconews/sehk/2026/0331/2026033102607.pdf", "hkex_2025_annual_report": "https://www.hkexnews.hk/listedco/listconews/sehk/2026/0414/2026041401168.pdf", "hkex_2026_july_monthly_return": "https://www1.hkexnews.hk/listedco/listconews/sehk/2026/0803/2026080302180.pdf", "eastmoney_hk_kline": "https://push2his.eastmoney.com/api/qt/stock/kline/get?secid=116.09880", "eastmoney_hk_cashflow": "https://datacenter-web.eastmoney.com/api/data/v1/get?reportName=RPT_HKF10_FN_CASHFLOW_PC", "etnet_consensus": "https://www.etnet.com.hk/www/eng/stocks/realtime/quote_profit.php?code=9880", "kgi_report": "https://www.kgi.com.hk/en/-/media/files/kgishk/research-reports/investment-perspectives/2026/kgis-global-markets-weekly-kickstart_20260413.pdf", "fx_history": "https://www.exchangerates.org.uk/HKD-CNY-spot-exchange-rates-history-2026.html", }, "bounded_gaps": [ "港股没有A股式扣非归母净利润字段;主报告以归母亏损作为保守代理,不制造扣非数。", "公开机构明细页给出7家2026年盈利预测;汇总均值由逐家数字复算,不把评级或目标价作为估值输入。", "2026年上半年中报尚未披露;TTM采用最新已审计2025全年口径,不外推2026年中期利润。", ], } def build_snapshot() -> dict[str, Any]: annual_2024 = { "period_end": "2024-12-31", "basis": "audited", "revenue": hkd(1_305_361_000), "attributable_profit": hkd(-1_123_590_000), "deduct_profit": hkd(-1_123_590_000), "cfo": hkd(-883_636_000), "capex": hkd(226_968_000), } latest_2025 = { "period_end": "2025-12-31", "basis": "audited_latest_full_year", "revenue": hkd(2_000_999_000), "attributable_profit": hkd(-703_191_000), "deduct_profit": hkd(-703_191_000), "cfo": hkd(-784_111_000), "capex": hkd(409_024_000), } prior_2024 = dict(annual_2024) prior_2024["basis"] = "reported_comparative" equity = hkd(7_217_986_000) bvps = equity / SHARES scenarios = [ { "name": "悲观情景", "role": "pessimistic", "method": "pb", "multiple_low": 2.0, "multiple_high": 4.0, "assumption": "人形机器人收入增速明显降档、亏损收窄慢于机构预期,现金消耗继续压低安全边际。", }, { "name": "基准情景", "role": "base", "method": "pb", "multiple_low": 4.0, "multiple_high": 6.0, "assumption": "2026年收入接近33亿元人民币公开预测,归母亏损落在机构中位数附近并继续收窄,现金储备可支持研发与交付。", }, { "name": "乐观情景", "role": "optimistic", "method": "pb", "multiple_low": 6.0, "multiple_high": 9.0, "assumption": "全尺寸人形机器人实现规模交付,毛利率和费用率同步改善,市场继续给予稀缺成长溢价。", }, ] institutions = [] for item in FORECASTS_RMB: institutions.append({ "institution": item["institution"], "report_date": item["report_date"], "include": True, "core_assumption": f"ETNet公开明细;评级为{item['rating']}。盈利预测按{CNY_PER_HKD:.4f}人民币/港元换算,仅利润进入一致预期。", "source_id": "SRC-ETNET-CONSENSUS", "estimates": {"2026": {"profit": hkd(item["profit"]), "eps": hkd(item["eps"])}} }) sources = [ { "id": "SRC-HKEX-RESULTS-2025", "source_type": "archived_primary", "title": "优必选2025年度业绩公告", "publish_date": "2026-03-31", "period_end": "2025-12-31", "url": "https://www1.hkexnews.hk/listedco/listconews/sehk/2026/0331/2026033102607.pdf", "supports": [ "financials.annual", "financials.current_cumulative", "financials.prior_year_same_period", "balance_sheet.equity", "balance_sheet.cash_available", "balance_sheet.interest_bearing_debt", ], "revision_status": "current", }, { "id": "SRC-HKEX-ANNUAL-2025", "source_type": "archived_primary", "title": "优必选2025年度报告", "publish_date": "2026-04-14", "period_end": "2025-12-31", "url": "https://www.hkexnews.hk/listedco/listconews/sehk/2026/0414/2026041401168.pdf", "supports": ["financials.current_cumulative", "balance_sheet.equity", "financials.cashflow", "business.segments"], "revision_status": "current", }, { "id": "SRC-HKEX-SHARES-202607", "source_type": "archived_primary", "title": "截至2026年7月31日证券变动月报表", "publish_date": "2026-08-03", "period_end": "2026-07-31", "url": "https://www1.hkexnews.hk/listedco/listconews/sehk/2026/0803/2026080302180.pdf", "supports": ["market.diluted_shares", "shares.full_circulation"], "revision_status": "current", }, { "id": "SRC-EM-HK-KLINE-20260804", "source_type": "quote_provider", "title": "东方财富优必选2026-08-04未复权日K收盘价", "publish_date": AS_OF, "period_end": AS_OF, "url": "https://push2his.eastmoney.com/api/qt/stock/kline/get?secid=116.09880", "supports": ["market.price", "market.volume", "market.platform_market_cap"], "revision_status": "current", }, { "id": "SRC-ETNET-CONSENSUS", "source_type": "institution_aggregator", "title": "ETNet优必选机构盈利预测明细", "publish_date": AS_OF, "period_end": AS_OF, "url": "https://www.etnet.com.hk/www/eng/stocks/realtime/quote_profit.php?code=9880", "supports": ["institutions"], "revision_status": "current", }, { "id": "SRC-FX-HKD-CNY-20260803", "source_type": "professional_database", "title": "2026-08-03历史港元兑人民币即期汇率", "publish_date": FX_DATE, "period_end": FX_DATE, "url": "https://www.exchangerates.org.uk/HKD-CNY-spot-exchange-rates-history-2026.html", "supports": ["bulk_history"], "revision_status": "current", }, ] market_cap = PRICE * SHARES return { "snapshot_id": "SNAPSHOT-09880-20260804-V1", "meta": { "company": "优必选", "code": "09880.HK", "market": "香港联合交易所主板", "as_of_date": AS_OF, "currency": "HKD", "report_period_end": "2025-12-31", "latest_operating_info_date": "2026-04-14", }, "market": { "price": PRICE, "price_type": "2026-08-04香港市场完整交易日未复权收盘价(港元)", "price_timestamp": "2026-08-04T16:08:00+08:00", "diluted_shares": SHARES, "shares_date": "2026-07-31", "platform_market_cap": None, }, "financials": { "annual": annual_2024, "current_cumulative": latest_2025, "prior_year_same_period": prior_2024, }, "balance_sheet": { "period_end": "2025-12-31", "equity": equity, "cash_available": hkd(4_887_880_000), "non_operating_financial_assets": hkd(120_043_000), "interest_bearing_debt": hkd(1_123_000_000), "minority_interest": hkd(107_618_000), }, "normalization": { "adjustments": [{ "description": "港股法定报告未披露A股式扣非归母利润,以归母亏损作保守代理", "amount": 0, "basis": "不制造扣非数据;2025年最新已审计归母亏损直接作为归一化口径", }] }, "valuation": { "scenarios": scenarios, "reverse_pe_multiples": [20, 25, 30, 40, 50, 60, 75, 100], "exit_pe_multiples": [20, 25, 30, 40, 50, 60, 75, 100], "holding_period": {"years": 5, "required_return": 0.10, "cumulative_dividend_per_share": 0}, }, "institutions": {"coverage_status": "available", "forecasts": institutions}, "sources": sources, "analysis": { "company_type": "高成长但仍亏损的人形机器人平台公司", "primary_model": "PB情景为主,PS、机构亏损收窄路径和现金消耗交叉验证;亏损状态不使用PE给方向性结论", "cross_checks": ["2025年PB/PS", "2026年机构盈利预测", "KGI 2026年收入预测", "现金与经营现金流", "股本变动月报"], "profit_sources": [ "2025年收入20.01亿元人民币,同比增长53.3%;其中全尺寸具身智能人形机器人约8.21亿元人民币,已成为最大收入来源。", "其他机器人产品及服务约6.29亿元人民币、其他智能硬件约4.99亿元人民币,利润改善取决于机器人规模交付、产品毛利率和费用率,而非只看订单题材。", "2025年归母仍亏损7.03亿元人民币;当前可衡量的是亏损收窄路径和现金安全垫,尚不能把收入增长直接等同于成熟利润。", ], "profit_source_quality": [ "最新已审计2025年归母亏损7.03亿元人民币,较2024年归母亏损11.24亿元人民币收窄,但尚未跨过可持续盈利门槛。", "2025年经营现金流约-7.84亿元人民币、资本开支约4.09亿元人民币,简化自由现金流仍显著为负;资产负债表因股权融资而较强。", "7家公开机构对2026年归母利润预测从亏损4.02亿元人民币到盈利3.49亿元人民币,分歧很大,中位数为亏损2.68亿元人民币。", ], "cashflow_notes": [ "2025年现金及现金等价物约48.88亿元人民币,可覆盖当前研发和交付投入,但高现金主要来自融资,不能替代经营自我造血。", "企业价值计算按港元统一换算;采用2026-08-03的0.8612人民币/港元,汇率滞后一个自然日的影响小于财务预测分歧。", ], "risks": [ "2026年机构盈利预测横跨盈亏两端,说明商业化节奏和费用收敛高度不确定。", "当前约5.22倍PB、18.84倍2025年PS;按KGI 2026年33亿元人民币收入预测,前瞻PS仍约11.42倍,传统估值安全垫不高。", "人形机器人订单到收入、收入到回款、回款到可持续利润之间存在较长验证链;交付不及预期会同时压低收入和估值倍数。", "股权融资提高净资产与现金,也造成潜在摊薄;PB基准对融资时点和现金消耗敏感。", ], "upgrade_triggers": [ "2026年收入达到或超过33亿元人民币,同时归母亏损接近机构中位数并且经营现金流明显改善。", "全尺寸人形机器人持续规模交付,毛利率提升且应收、存货和合同资产没有失控。", ], "downgrade_triggers": [ "收入增速低于基准路径、机构继续下修2026年盈利,或现金消耗显著超过当前预期。", "新增融资导致明显摊薄,或者市场将成长PB从4—6倍压缩至2—4倍。", ], "executive_conclusion": ( f"{PRICE:.2f}港元位于PB基准合理区间{bvps*4:.2f}—{bvps*6:.2f}港元内,判断为基本合理," "但属于高风险、高弹性的条件化合理;当前价格要求2026年收入高增长、亏损继续收窄和现金消耗受控同时兑现。" ), "fx_rate_cny_per_hkd": CNY_PER_HKD, "fx_date": FX_DATE, "ttm_definition_note": "香港市场截至估值日没有2026年一季报;为保持固定TTM公式,年度锚点与上年同期均使用2024年,当前累计使用最新已审计2025全年,复算结果严格等于2025全年。", "institution_provider_consolidation_2026_rmb": -268_000_000, "institution_recomputed_mean_2026_rmb": sum(item["profit"] for item in FORECASTS_RMB) / len(FORECASTS_RMB), "kgi_revenue_2026_rmb": 3_300_000_000, "information_cutoff": "2026-08-04T23:59:59+08:00", }, } def main() -> None: RESULT_DIR.mkdir(parents=True, exist_ok=True) CASE_RAW.mkdir(parents=True, exist_ok=True) register_path = CASE_RAW / "优必选公开数据登记_20260804.json" write_json(register_path, source_register()) snapshot = build_snapshot() snapshot_path = RESULT_DIR / "优必选估值快照_20260804.json" write_json(snapshot_path, snapshot) calc_dir = RESULT_DIR / "calculation" run = base.run_pipeline(snapshot_path, calc_dir, REGISTRY) results_path = calc_dir / "valuation_results.json" results = json.loads(results_path.read_text(encoding="utf-8")) report = (calc_dir / "valuation_report.md").read_text(encoding="utf-8") report = report.replace("价格合理性评估(流水线生成底稿)", "价格合理性评估", 1) report = report.replace( "本文是研究计算底稿,不构成交易指令或收益承诺。", "本文为按《股票价格合理性评估操作手册》形成的条件化研究结论,不构成交易指令或收益承诺。", 1, ) report = report.replace("亿元", "亿港元").replace(" 元", " 港元").replace("使用元", "使用港元") report = report.replace("亿港元人民币", "亿元人民币") report = report.replace("| 最近年度 CFO |", "| 年度锚点 CFO(2024) |") report = report.replace("| 最近年度资本开支 |", "| 年度锚点资本开支(2024) |") report = report.replace("| 最近年度简化 FCF |", "| 年度锚点简化 FCF(2024) |") report = report.replace("| 本期累计简化 FCF |", "| 最新已审计2025全年简化 FCF |") ttm_note = snapshot["analysis"]["ttm_definition_note"] report = report.replace("## 4. TTM 与归一化利润\n", f"## 4. TTM 与归一化利润\n\n> 港股TTM口径说明:{ttm_note}\n", 1) formal_path = RESULT_DIR / "优必选价格合理性评估_20260804.md" formal_path.write_text(report, encoding="utf-8") evidence = { "batch_id": BATCH_ID, "ticker": "09880.HK", "company": "优必选", "as_of": AS_OF, "information_cutoff": snapshot["analysis"]["information_cutoff"], "raw_register": str(register_path.relative_to(ROOT)).replace("\\", "/"), "raw_register_sha256": sha256(register_path), "snapshot_sha256": sha256(snapshot_path), "calculation_sha256": sha256(results_path), "formal_report_sha256": sha256(formal_path), "source_urls": source_register()["source_urls"], "checks": { "price_times_shares_hkd": PRICE * SHARES, "result_market_cap_hkd": float(results["metrics"]["market_cap"]), "shares_from_hkex_monthly_return": SHARES, "cny_per_hkd": CNY_PER_HKD, "institution_count": len(FORECASTS_RMB), "qa": results["qa"], }, "bounded_gaps": source_register()["bounded_gaps"], } write_json(RESULT_DIR / "source_evidence_manifest.json", evidence) summary = { "ticker": "09880.HK", "company": "优必选", "slug": "ubtech_robotics", "price": PRICE, "market_cap": float(results["metrics"]["market_cap"]), "normalized_profit": float(results["metrics"]["normalized_profit"]), "normalized_pe": None, "pb": float(results["metrics"]["pb"]), "ps": float(results["metrics"]["ps"]), "consensus_count": len(FORECASTS_RMB), "consensus_2026": float(results["institutions"]["summary"]["2026"]["profit_mean"]), "consensus_median_2026": float(results["institutions"]["summary"]["2026"]["profit_median"]), "forward_pe": None, "base_low": float(results["scenarios"][1]["price_low"]), "base_high": float(results["scenarios"][1]["price_high"]), "optimistic_high": float(results["scenarios"][2]["price_high"]), "label": "基本合理(高风险/高弹性)", "qa": results["qa"]["status"], "qa_errors": results["qa"]["error_count"], "qa_warnings": results["qa"]["warning_count"], "share_status": "HKEX_MONTHLY_RETURN_EXACT", "currency": "HKD", "formal_path": "20260805_ubtech_robotics_valuation/优必选价格合理性评估_20260804.md", "run_status": run["status"], } write_json(RESULT_DIR / "summary_row.json", summary) print(json.dumps(summary, ensure_ascii=False), flush=True) if __name__ == "__main__": main()