Commit Graph

107 Commits

Author SHA1 Message Date
zhulinsen
294210883c fix: 分析后无报告生成 (#2201) (#2211)
* fix(issue-2201): [bug]-分析后无报告生成

* fix(review-feedback-2211): Keep the TTM cutoff anchored to the as-of date and Propagate report

* fix(review-feedback-2211): Move the empty-watchlist check before the trading-day return

* fix(review-feedback-2211): preserve the failure status, but defer returning until after the and

* fix(review-feedback-2211): preserve the failure result, but defer returning until after the

* fix(review-feedback-2211): Handle failures in the market-review-only CLI branch
2026-08-14 22:08:51 +08:00
Nicholas-Xiong
7fa29c7e3e fix(longbridge): 修正 history_candlesticks_by_offset 位置参数顺序,导致 volume_ratio 静默失败 (fixes #2100) (#2109)
* fix(longbridge): 修正 history_candlesticks_by_offset 位置参数顺序 (fixes #2100)

_compute_volume_ratio 调用 ctx.history_candlesticks_by_offset 时把
time(datetime, 第 5 个位置参数)与 count(int, 第 6 个位置参数)值传反——time 槽位
被传入 6, count 槽位被传入 datetime.now()。PyO3 在 PyDateTime 转换层抛
'argument "time": "int" object cannot be converted to "PyDateTime"',
异常被 try/except 吞到 logger.debug, 上层只看到 '未获取到 HK01109 的数据'
DataFetchError, 量比字段恒为 None, 港股实时行情链路上整体退化。

实测发现 Windows 下因 SDK 行为宽容差异未必必复现, 但参数顺序 error 是
SDK 签名 (symbol, period, adjust_type, forward, time, count) 与官方 .pyi
annotation 直接违反的硬 bug——Docker/Linux 下用户长期命中。

修复:
1. 按正确顺序传入 datetime.now() 作为 time、6 作为 count, 在源码注释里写
   清参数语义与旧 bug 模式, 避免未来再次翻位传反。
2. 新增 test_volume_ratio_history_candlesticks_by_offset_arg_order 回归测试,
   对 call_args.args[4]/[5] 做 isinstance + 值断言, 任何回退都会立即 CI 失败。
   旧测试 test_realtime_quote_with_volume_ratio 用 MagicMock 不约束参数顺序,
   是 bug 一直逃过测试的根本原因。

* feat: history_candlesticks_by_offset keyword args cross-SDK compat

- Switch from positional args to keyword args (symbol=, period=,
  adjust_type=, forward=, time=, count=) in _compute_volume_ratio,
  immune to positional signature drift between longbridge 0.2.74
  (forward, time, count) and 4.x (forward, count, time).
- Update test_volume_ratio_history_candlesticks_by_offset_arg_order
  to assert keyword args instead of positional slots.
- Add test_volume_ratio_keyword_args_cross_sdk_compat covering
  4.x positional signature with keyword args.
- Narrow CHANGELOG: keyword args compat + cross-version regression tests.

* docs(changelog): move #2100 fix entry to [Unreleased] + drop 8 stray 3.28.0 entries

[Unreleased] 段此前为空,而 #2100 的修复 entry 被错误地放进已发布的
[3.28.0] - 2026-07-26 段下的 ### 文档 子段中,且伴随 8 条与 #2100 无关
的旧 issue 条目(#2026/#1985/#2051/#2070/#2091 等)也被一并错塞。

按仓库规则修复:
- 把 #2100 的 [修复] 条目(描述:LongbridgeFetcher._compute_volume_ratio
  调用 history_candlesticks_by_offset 位置参数 time/count 传反,导致港股/
  美股实时量比字段恒为 None;改用 adaptive keyword args 兼容 0.2.74 与
  4.x,并补回归测试)作为单条扁平条目写入 [Unreleased] 段;
- 删除 [3.28.0] ### 文档 段下错塞的 9 条无关条目(8 条历史 issue + #2100
  自身),恢复 3.28.0 已发布段原貌;
- 保留 3.28.0 ### 文档 段原有的「修复文档中的失效相对链接」一条。

PR #2109 review feedback from @ZhuLinsen.

---------

Co-authored-by: xxiaoxiong <xxiaoxiong@users.noreply.github.com>
Co-authored-by: xxiaoxiong <xxiaoxiong@nicholasxiong.cn>
2026-07-31 19:59:31 +08:00
zhulinsen
90f62349af fix: 将 Tencent 日 K 保持为最终兜底 (#2107)
* fix: keep Tencent daily data as final fallback

* fix(review-feedback-2107): 补一个覆盖 python scripts/check env.py --fetch 或“先 import data provider 再读取
2026-07-26 20:32:12 +08:00
zhulinsen
20c399e793 fix: 避免港股实时行情重复拉取全市场数据 (#2104)
* fix: cache HK realtime market data

* fix(review-feedback-2104): Coalesce failed cold-cache refreshes

* fix(review-feedback-2104): Reject empty market snapshots before caching success
2026-07-26 16:33:28 +08:00
Nicholas-Xiong
02717771a1 fix: 修复 YfinanceFetcher 4-5 位裸港股码路由到 .SZ 的 bug(fixes #2091) (#2097)
* fix: route 4-5 digit bare HK codes to .HK in YfinanceFetcher (fixes #2091)

Issue #2091: 5-digit HK listings without an explicit 'HK' prefix (e.g.
02513 for Zhipu) fell through to the 'cannot determine market, default
to .SZ' tail branch in YfinanceFetcher._convert_stock_code(), producing
'02513.SZ' which Yahoo Finance rejects with 404, breaking the daily OHLC
chain and leaving LLM-driven stop-loss / MA levels unreliable.

Fix: insert a new branch ahead of the .SZ fallback that routes 4-5 digit
pure-numeric codes to '.HK' using the same zero-padding logic as the
existing HK-prefix branch (lstrip leading zeros, zfill to 4).

Safety:
  - A-share codes are always 6 digits, so no A-share rule is shadowed.
  - BSE codes are 6 digits (4xxxxx / 8xxxxx / 920xxx) and routed earlier
    via is_bse_code(); the new branch only sees 4-5 digit inputs.
  - ETF branches (15xx/16xx/18xx/51xx/52xx/56xx/58xx) all expect 6-digit
    codes and run earlier; no overlap.
  - JP/KR/TW/US suffix codes are routed earlier; unaffected.
  - Codes already carrying .SS/.SZ/.HK/.BJ pass through verbatim earlier.
  - 1-3 digit numerics continue to fall through to the .SZ default to
    preserve prior behaviour; the fix is intentionally scoped to 4-5
    digits per maintainer note in issue #2091, avoiding speculative
    expansion of the HK rule to inputs users never fetch in practice.

Tests: tests/test_yfinance_hk_bare_code.py covers:
  - HK-prefix still routes to .HK (4 cases, regression guard)
  - Bare 4-5 digit numeric -> .HK with correct zero padding (5 cases)
  - A-share 600/601/603/688 (.SS) and 000/002/300 (.SZ) unchanged (7 cases)
  - BSE 4xxxxx / 8xxxxx / 920xxx routes to .BJ unchanged (3 cases)
  - ETF (510300.SS / 159915.SZ), JP/KR/US suffix, and pre-suffixed codes
    pass through unchanged (7 cases)
All 23 new tests pass; 71 yfinance / convert_stock_code tests total
across the wider related test set pass with no regression.

* fix: 修复 _is_hk_market 4位裸港股码路由 + DataFetcherManager 回归测试

修复 maintainer 在 PR #2097 review 中指出的 OR-COR-bfddfd66 blocker:
_DataFetcherManager.get_daily_data() 仍按 A 股链路路由 4 位裸港股码。

根因:data_provider/base.py::_is_hk_market() 只把 5 位裸数字识别
为港股,4 位裸数字 (0001 长和 / 0941 中国移动) 被路由到 cn 分支,
导致 AkshareFetcher 走 stock_zh_a_hist、BaostockFetcher 兜成 sz.0001、
TushareFetcher 转成 0001.SZ——issue #2091 在主调用路径上未真正关闭。

修复:_is_hk_market 的裸数字分支从 len(normalized) == 5 改为
4 <= len(normalized) <= 5,与 YfinanceFetcher._convert_stock_code
的 4-5 位分支保持一致。A 股 (6 位) / BSE (6 位) / ETF (51/15 开头)
不受影响,因为它们走不同前缀或位数判定。

回归测试:新增 tests/test_data_fetcher_manager_hk_bare_code.py
- _is_hk_market('0001'/'0941'/'0078') -> True
- 4位裸港股码经 DataFetcherManager 只路由到 HK-capable fetcher
  (YfinanceFetcher/AkshareFetcher/TushareFetcher),Efinance/Tencent/
  TickFlow/Pytdx/Baostock 完全不被调用
- 5位裸港股码行为不变 (00700 -> YfinanceFetcher)
- 6位 A 股仍路由到 CN-only fetcher,YfinanceFetcher 不被调用

跨 7 个相关测试文件 186 passed,全量 135 passed,无回归。

* fix(#2091): 同步 akshare_fetcher._is_hk_code 到 4-5 位裸港股码

Review blocker OR-COR-ea09dfe8 (#2097): manager 层 _is_hk_market()
已在前序 commit 放开到 4-5 位裸数字,但 AkshareFetcher._is_hk_code()
仍只接受 5 位。DataFetcherManager 默认优先级下 AkshareFetcher 优先
于 YfinanceFetcher 执行,0001 在 manager 被判为 HK 后于
AkshareFetcher 内部因 _is_hk_code('0001')==False 落到 _fetch_stock_data
A股链路,两套市场契约冲突。

同步放宽 _is_hk_code:
- 无前缀裸数字从 len==5 改为 4<=len<=5
- docstring 补充 OR-COR-ea09dfe8 说明

新增两类 provider-level regression tests:
1. TestAkshareFetcherIsHkCodeContract: 直接断言 _is_hk_code 对 4/5
   位裸码、6位裸码(排除)、前缀后缀的解码结果
2. TestAkshareFetcherRoutingCallsHkBranch: patch _fetch_hk_data /
   _fetch_stock_data,驱动 _fetch_raw_data('0001') 验证真实分流到
   HK 分支而非 A股分支,防止 _is_hk_code 被重收紧后静默回归

* fix(#2091): 同步 longbridge_fetcher._is_hk_code 到 4-5 位裸港股码

OR-COR-ea09dfe8 关闭:DataFetcherManager 路由层 (data_provider/base.py)
已放宽到 4-5 位裸港股码,把 LongbridgeFetcher 视为 HK-capable provider
保留进港股链路;但 longbridge_fetcher._is_hk_code 仍只接受 5 位裸数字,
导致 4 位裸港股 (0001 长和 / 0941 中国移动) 在配置了 Longbridge 的真实
日线/实时链路上 _to_longbridge_symbol 返回 None,日线 fallback 抛
ValueError、实时兜底被静默跳过。

修复与 base._is_hk_market 的市场契约对齐:
- _is_hk_code: 4-5 位裸数字判定为港股 (与 base._is_hk_market 一致);
  .HK 后缀严格校验后缀 base 部分为 1-5 位数字 (之前无条件 True,会把
  类似 "XXX.HK" 也误判,本次顺带收紧);HK 前缀分支保持不变。
- _to_longbridge_symbol: 在 _is_hk_code 通过后的 .HK 后缀输入仍走
  原路径直接 return upper,行为不变;4 位裸码现在能正确 zfill(4) 到
  "0001.HK" / "0941.HK"。

回归测试:
- tests/test_longbridge_fetcher.py 新增 TestSymbolConversion
  .test_hk_stock_4digit_bare_code_issue_2091: 显式 assert 0001/0941
  _is_hk_code=True + _to_longbridge_symbol="0001.HK"/"0941.HK"。
- 现有 TestSymbolConversion 7 测试 + 全文件 30 测试 全通过。
- tests/test_yfinance_hk_bare_code.py + test_data_fetcher_manager_hk_bare_code.py
  共 39 测试 全通过。
- 总计新相关回归 69/69 pass。

本地确定性复现 (reviewer 报告的 head 行为):
- 修复前: _is_hk_code("0001") == False, _to_longbridge_symbol("0001") == None
- 修复后: _is_hk_code("0001") == True,  _to_longbridge_symbol("0001") == "0001.HK"
- _is_hk_code("0941") == True,  _to_longbridge_symbol("0941") == "0941.HK"
- _is_hk_code("00700") == True, _to_longbridge_symbol("00700") == "0700.HK"
- _is_hk_code("00700.HK") == True, _to_longbridge_symbol("00700.HK") == "00700.HK"
- _is_hk_code("HK00700") == True, _to_longbridge_symbol("HK00700") == "0700.HK"
- _is_hk_code("600690") == False (6 位 A 股不误判), _to_longbridge_symbol("600690") == None
- _is_hk_code("AAPL") == False,    _to_longbridge_symbol("AAPL") == "AAPL.US"

注:本轮只关闭 OR-COR-ea09dfe8 阻断;非阻断建议 (CHANGELOG #2063 条目
移出、PR 描述 sync、docstring 中 review blocker 编号清理) 将在下一条
commit 单独处理。

* docs(#2097): 清理评审叙事 + 收敛 CHANGELOG 范围

回应 reviewer 非阻断建议 (上一条 commit a251e4f8 提到的"下一 commit 处理"):

1. docs/CHANGELOG.md
   - 移除本 PR 主题 (#2091 yfinance/HK bare-code 路由) 之外混入的
     "parse_analysis_target() / #2063 Phase 1" 条目;该 feat 属于
     commit 0313dd3c (issue #2063 Phase 1) 单独 PR 的范围,不应在本 PR
     中夹带。
   - 把 #2091 条目改写为反映本 PR 实际范围:三处 _is_hk_code 同步
     (YfinanceFetcher 在 4e915a4f、AkshareFetcher 在 e2bf3a88、
     LongbridgeFetcher 在 a251e4f8) + DataFetcherManager 港股路由
     回归测试 (9c473787),统一描述为"DataFetcherManager 港股路由"
     而非只提 yfinance 单侧,与当前累计 diff 一致。

2. data_provider/base.py:_is_hk_market / akshare_fetcher.py:_is_hk_code
   docstring 清理:移除"Review blocker OR-COR-... (PR #2097 / issue
   #2091)"评审叙事、PR 编号、blocker 编号、过程性"之前只接受..."。
   改为稳定的市场识别规则说明:支持哪种形式 (.HK 后缀 / HK 前缀 /
   4-5 位裸数字)、与 provider 内 _is_hk_code 的位数契约对齐即可。
   长期保留的产品代码不应写入评审过程叙事。

行为无变化:仅 docstring + CHANGELOG 文本修改,无代码路径修改。
回归:tests/test_longbridge_fetcher.py + tests/test_yfinance_hk_bare_code.py
+ tests/test_data_fetcher_manager_hk_bare_code.py 共 69/69 pass。
2026-07-26 11:20:12 +08:00
Nicholas-Xiong
f2bfa0210f feat: 支持 TUSHARE_HTTP_URL 自定义 Tushare Pro 接入地址 (#2048)
* feat: support custom Tushare Pro endpoint via TUSHARE_HTTP_URL

Add TUSHARE_HTTP_URL so the Tushare data source can point at a self-hosted
or third-party compatible endpoint when the official api.tushare.pro is not
reachable. Defaults to the official host when unset, so behavior is
unchanged for existing users.

- data_provider/tushare_fetcher.py: add _resolve_tushare_http_url() helper
  (strip + http(s):// schema validation) and forward the resolved URL into
  _TushareHttpClient, with an info log when a custom endpoint is in use
- .env.example + .github/workflows/00-daily-analysis.yml: document and map
  TUSHARE_HTTP_URL so the new option is wired into the daily job without
  leaving a half-configured state
- tests: cover env parsing (empty/whitespace/http/https/missing schema),
  fetcher fall-through to the official host, and end-to-end POST target
- docs/CHANGELOG.md: flat [Unreleased] entries

Fixes #1985

* refactor: drop unnecessary string-literal type hint in _build_api_client

TushareHttpClient is already defined above TushareFetcher in the module
scope, so a string-literal type hint is not needed for forward reference.
Restore the bare type to match the surrounding code style and reduce the
diff against main.

* docs(tushare): 补 TUSHARE_HTTP_URL 在 full-guide 中英版本的用途/默认行为/workflow 映射说明

按 PR #2048 review 反馈补齐:
- 表格内新增 TUSHARE_HTTP_URL 行(中英文版本同步),明确默认 https://api.tushare.pro 与 http(s):// 前缀要求
- 在 GitHub Actions 段落后补充 TUSHARE_HTTP_URL 的 vars/Secrets 优先级与每日 workflow 0映射说明,与现有非敏感配置(TICKFLOW_PRIORITY)一致
- 完整环境变量列表(中英文版本)补 TUSHARE_HTTP_URL 行,默认值列填 https://api.tushare.pro
- 与代码实现一致:00-daily-analysis.yml 已用 vars.TUSHARE_HTTP_URL || secrets.TUSHARE_HTTP_URL 映射

* docs(tushare): align TUSHARE_HTTP_URL default with runtime and clarify vars/secrets precedence

Per review feedback on PR #2048: the per-repo config contract must stay
consistent across runtime, .env.example, workflow priority, tests and
both zh/en guides. Two fixes applied as a single contract update:

1. Default endpoint alignment. data_provider/tushare_fetcher.py:100,
   .env.example, and tests/test_tushare_fetcher_http_client.py all keep
   the existing official endpoint http://api.tushare.pro, but the zh/en
   full-guide rows had drifted to https://api.tushare.pro. Switching the
   documented default to HTTPS would silently change the runtime contract
   that the feat commit explicitly preserved. Revert both zh and en
   guide rows to http://api.tushare.pro so docs match runtime, .env.example
   and the test assertions.

2. vars/secrets precedence wording. The workflow uses
   'vars.TUSHARE_HTTP_URL || secrets.TUSHARE_HTTP_URL', which means a
   non-empty vars entry always wins and Secrets cannot override it. The
   zh/en notes previously suggested 'Secrets as a tamper fallback' which
   is incorrect under this precedence and can mislead users into thinking
   Secrets has override power. Replace with explicit description of the
   real semantics: vars wins when non-empty; Secrets is only selected
   when the Variable is empty; for a tamper-resistant deployment put the
   value only in Secrets and leave Variables empty.

Both zh and en guides are updated together; the same 6 contract surfaces
(runtime / .env.example / workflow priority / tests / zh guide / en guide)
now describe one consistent contract.

* docs(tushare): remove false Secret-as-tamper-guard claim, document real vars/secrets write-permission model
2026-07-22 20:54:40 +08:00
oguz
748dba5074 fix(yfinance): populate pe_ratio/pb_ratio for US realtime quotes (#2023)
get_realtime_quote hardcoded pe_ratio and pb_ratio to None for US stocks,
so the valuation block was always empty and every US quote reported
data_quality="partial" with pe_ratio/pb_ratio in missing_fields.

yfinance already returns both via Ticker.info (trailingPE / priceToBook),
and info is fetched a few lines above for the stock name, so this reads
them from the existing dict with no extra request.

The US index path (_get_us_index_realtime_quote) is intentionally left
alone: indices have no PE/PB.

Verified: AAPL now returns pe_ratio=39.67, pb_ratio=45.08 and
missing_fields shrinks from ['amount','pe_ratio','pb_ratio'] to ['amount'].
2026-07-19 18:01:22 +08:00
zhulinsen
e8a9ca7742 fix: harden release packages and report market context (#1994)
* fix: bundle orjson in release packages

* fix: remove duplicate decision signal excerpts

* feat: add TickFlow sector ranking fallback
2026-07-12 18:56:21 +08:00
zhulinsen
d70af7eafb fix(issue-1894): [bug]-回测时获取数据异常 (#1895) 2026-07-03 19:53:31 +08:00
Wenyu Chiou
0f57e4e2e5 fix(data): parse yfinance dividends from the single-column DataFrame yfinance 1.2.x returns (#1867)
yfinance 1.2.x returns Ticker.dividends as a single-column DataFrame instead of a
Series. YfinanceFundamentalAdapter iterated it with `.items()`, which for a DataFrame
yields (column_name, Series) — so `_safe_float(Series)` returned None, every dividend
event was dropped, and the TTM cash/count silently fell back to
`info.trailingAnnualDividendRate` (e.g. 24.0 with "0 次") instead of the true TTM sum.
Coerce to a Series before iterating.

Affects every yfinance-backed market (US/HK/JP/KR/TW); surfaced on a live TW report
(2330.TW showed 24.0 / 0 payouts vs the real ~22 across 4). + a regression test that
feeds a single-column DataFrame and asserts the events + TTM sum are parsed.

Co-authored-by: zhulinsen <42829555+ZhuLinsen@users.noreply.github.com>
2026-07-02 18:52:08 +08:00
Wenyu Chiou
7230465da5 feat(market): v2.1 — surface tw 三大法人 in report + LLM prompt, fix TWD currency (Refs #1777) (#1866)
* feat(market): v2.1 — consume tw 三大法人 in report + LLM prompt, fix TWD currency label

The merged 三大法人 (institutional flows) data (#1829/#1841/#1855/#1863) reached
get_fundamental_context but was never surfaced: the report renderer and the LLM
prompt-builder never read the institution block, so a real analysis run showed a tw
report with no 三大法人 figures. This wires the last mile (tw-only, additive,
fail-open) and fixes a TWD currency mislabel found in the same run:

- report render (src/notification.py): _append_institutional_flow renders a 三大法人
  net-buy/sell table (外资/投信/自营/合计 in 万股/亿股) when the institution block is
  status='ok'; _get_fundamental_blocks now extracts it. + zh/en/ko labels.
- LLM prompt (src/analyzer.py): _format_prompt injects a 三大法人 section (raw net
  figures + a qualitative 台股筹码过滤器 note, mirroring the capital_flow section)
  when status='ok' and all four core nets are present.
- fetch availability (data_provider/base.py): the tw institution fetch (a ~4-5s
  whole-market download) now uses the remaining stage budget instead of the ~3s
  per-symbol fetch cap that starved it and made the first/only stock of a run
  coin-flip to not_supported. Still bounded by the stage deadline (fail-open).
- currency (src/notification.py): _CURRENCY_SUFFIX gains TWD -> 新台币 so TWD amounts
  (revenue/profit/dividend) no longer render as the A-share default 元 (reads as RMB).

Verified end-to-end via a real analysis run: the tw report now shows the 三大法人
table and 新台币-labelled financials, and institution is reliably 'ok' on a
single-stock run. Strictly additive — cn/hk/us/jp/kr byte-identical (gated on
market=='tw' / status=='ok'); no capital_flow_signal / scoring / schema. + 11 tests.

Dual review (code-reviewer APPROVE + silent-failure-hunter): the hunter caught a
prompt-gate asymmetry (only total_net checked vs all four in the render gate);
tightened to require all four core nets, matching the render/base.py gate.

* fix(market): honour FUNDAMENTAL_FETCH_TIMEOUT_SECONDS=0 for the tw institution fetch

Review on #1866: the v2.1 institution-timeout change (use the remaining stage budget
instead of the ~3s per-fetch cap) inadvertently bypassed the existing
FUNDAMENTAL_FETCH_TIMEOUT_SECONDS=0 semantic — with fetch_timeout=0, valuation and
bundle are disabled (they gate on fetch_timeout) but the institution fetch still ran.

Gate the institution fetch on `fetch_timeout > 0` as well, so fetch_timeout=0 disables
it like the other fundamental fetches; when enabled it still uses the remaining stage
budget (the whole-market download needs more than the per-symbol cap). + a regression
test asserting fetch_timeout=0 -> institution not_supported and the fetcher is not called.

---------

Co-authored-by: zhulinsen <42829555+ZhuLinsen@users.noreply.github.com>
2026-07-01 19:11:27 +08:00
Wenyu Chiou
b889523396 feat(market): harden tw institutional fetcher — circuit breaker + TPEx date guard (#1864)
Two tw-only, strictly-additive, fail-open hardening changes to TwInstitutionalFetcher
(benchmarked against docs/data-source-stability.md):

- C1 circuit breaker: reuse data_provider.realtime_types.CircuitBreaker (the same class
  DataFetcherManager uses), keyed per market (twse/tpex), 3-fail / ~5-min cooldown /
  half-open probe. When an endpoint is unreachable the fetch is skipped fast and fails
  open, instead of paying timeout + throttle on every stock during an outage. The breaker
  tracks REACHABILITY: a hard network/HTTP error trips it; an empty / stat!=OK body still
  means the endpoint responded, so it counts as success (resets the failure streak and
  closes the breaker during recovery -- a no-data day mid-recovery can never strand it open).
- C2 TPEx date guard: TPEx OpenAPI serves only the latest trading day; an explicit date
  that does not match the served record now fails open (None) instead of a wrong-day record.

Tests: +6 (breaker opens after 3 failures & skips the 4th fetch; recovers after cooldown;
3 empties don't trip it; TPEx date match / mismatch / no-date). + CHANGELOG.

Dual review (code-reviewer APPROVE + silent-failure-hunter): the hunter flagged a
HALF_OPEN-recovery edge in the shared CircuitBreaker; resolved tw-side by treating an
empty response as success (reachability), without modifying the shared class.
2026-07-01 19:10:45 +08:00
Wenyu Chiou
b939f7579f feat(market): surface tw 三大法人 in the tw report institution block (Refs #1777) (#1863)
* feat(market): surface tw 三大法人 (institutional flows) in the tw report institution block

Wire the merged TwInstitutionalFetcher (#1829/#1841/#1855) into the tw analysis
report: in _build_offshore_fundamental_context, for market == "tw" only, the
institution block now carries the raw 三大法人 net buy/sell figures (foreign /
trust / dealer / total, unit=shares) from TWSE T86 / TPEx instead of the hardcoded
not_supported. v2 scope confirmed by the maintainer on issue #1777.

- tw-only + strictly additive: cn/hk/us/jp/kr offshore flows are byte-identical
  (the not_supported loop just excludes institution; non-tw still gets the same
  not_supported block). Pinned by tests for us/hk/jp/kr.
- fail-open + default-on: any error or no-data -> not_supported, never interrupts
  the analysis. Wiring (import/construct) failures log at error (visible) but still
  fail open; fetch failures log at warning.
- status "ok" only when all four core nets are present (a genuine 0 is kept).
- raw figures only: no capital_flow_signal / scoring / weight / schema / Web.

Tests: tests/test_tw_institution_report_wiring.py (9) covers data->ok, genuine-zero,
None/raise/init-raise fail-open, missing-core-net->not_supported, us/hk/jp/kr
byte-identical + fetcher-unused, and no-derived-key. + CHANGELOG.

Dual review (code-reviewer + silent-failure-hunter): the hunter caught the combined
try swallowing wiring (import/construct) errors silently at info -> split into
loud-but-fail-open (error log + still not_supported, honoring #1777's
never-interrupt rule) and guarded "ok" against a null core net.

* fix(market): address Codex review on tw institution wiring (#1863) — stage timeout, overall status, docs

- P1: run the tw institution fetch under the fundamental stage/fetch budget via
  _run_with_retry (like the other offshore blocks), so a slow / rate-limited TWSE/TPEx
  call fails open at the deadline instead of pushing the analysis past it.
- P2: count a present tw institution toward the OVERALL fundamental status so a report
  with only 三大法人 data still surfaces fundamentals (consumers key off the top-level
  status). missing_fields keeps the original three blocks, so offshore markets without
  institution data stay byte-identical.
- P2 (docs): sync docs/market-support.md (drop the now-false "data-layer only / not
  wired into report" clause) + add a tw institution note to docs/full-guide.md and
  docs/full-guide_EN.md (AGENTS.md requires user-visible report changes to update guides).

Tests: + stage-timeout (a 2s fetch is abandoned at the ~0.3s budget, fail-open) +
overall-status surfacing assertion. 10 tw-wiring tests + offshore/tw/jp-kr suites green.
2026-06-30 22:31:56 +08:00
Wenyu Chiou
2a65384654 perf(market): coalesce concurrent tw-institutional fetches (cache-stampede guard) (#1841)
Hardens the merged #1777 TwInstitutionalFetcher. Under concurrent callers for the
same (market, date) the previous check-then-fetch could issue N duplicate upstream
requests -- which for the TWSE T86 RWD endpoint risks tripping its informal
~3 req/5s ban. A per-(market,date) in-flight lock now coalesces same-key callers
into a single fetch (double-checked against the cache); different keys still fetch
in parallel, and the master lock is never held across network I/O.

- data_provider/tw_institutional_fetcher.py: per-key in-flight lock + _read_cache /
  _key_lock helpers. Behavior is otherwise unchanged -- still fail-open, still
  caches only non-empty results, tw-only, no data_provider/base.py change.
- tests: + concurrent-same-key-coalesces-to-single-fetch (8 threads -> 1 request),
  different-keys-not-coalesced, and HTTP-error (429) fail-open
- docs/CHANGELOG.md: [改进] entry

Refs #1777

Co-authored-by: zhulinsen <42829555+ZhuLinsen@users.noreply.github.com>
2026-06-30 19:01:38 +08:00
Wenyu Chiou
7715896fe4 feat(market): tw institutional-flows (三大法人) data-layer fetcher (#1829)
Implements the #1777 maintainer-greenlit Phase-2 data layer: a self-contained,
tw-only fetcher for Taiwan per-stock institutional (外資/投信/自營商) net
buy/sell. Strictly additive -- no change to the existing cn/hk/us/jp/kr flows in
data_provider/base.py, and not yet wired into the report/Web/scoring path (a
deliberate follow-up per the maintainer's scope).

- data_provider/tw_institutional_fetcher.py (NEW): TwInstitutionalFetcher
  - 上市 .TW  -> TWSE T86 legacy rwd JSON endpoint (西元 date, comma values)
  - 上櫃 .TWO -> TPEx OpenAPI tpex_3insti_daily_trading (民國 date, plain ints)
  - T86 columns are read by NAME (validated against the payload `fields` header),
    so a TWSE column rename / reorder fails open instead of silently shipping
    misaligned numbers under stale indices
  - foreign_net = 外陸資 (ex 外資自營商, T86) / dealer-excluded foreign (TPEx) so
    the breakdown matches the official 三大法人 total; total_net is the official
    figure; unit = shares, signs preserved
  - whole-market single-day cache keyed by (market, date), filtered per stock;
    ~3 req/5s throttle (own lock) for the T86 endpoint
  - fail-open: any network/rate-limit/empty/unknown-stock returns None; a missing
    or renamed column drops the row (never a fabricated 0); a row whose trading
    date cannot be attributed (TPEx 民國 unconvertible) is dropped; empty/failed
    fetches are not cached (no TTL-long blackout)
- tests/test_tw_institutional_fetcher.py (NEW): 20 offline tests with fixtures
  trimmed from real T86 (2330) / TPEx (3105) responses; pins the net breakdown +
  sign, 民國->西元 conversion, routing, caching, and fail-open -- including
  column reorder (parsed by name), column rename / missing header (fail-open),
  the missing-column-vs-genuine-zero distinction, and unconvertible TPEx dates
- docs/market-support.md + docs/CHANGELOG.md: data-source capability boundary +
  OGDL v1 license note; no new config (.env.example untouched)

Addresses the #1829 review (read T86 by field name; drop TPEx rows with an
unconvertible date; foreign_net excludes foreign-dealers).

Sources are 政府開放資料 under 政府資料開放授權條款第 1 版 (OGDL v1, commercial-safe).

Refs #1777
2026-06-28 18:49:09 +08:00
zariba
94cd2a4d96 feat(data): 接入 TickFlow 核心 A 股数据源 (#1790)
* feat(data): integrate TickFlow core A-share provider

* fix(data): harden TickFlow daily kline contract

* fix(web): localize TickFlow settings and run-flow coverage

* fix(web): clarify TickFlow priority settings

* chore(deps): require verified TickFlow SDK

---------

Co-authored-by: timeance <timeance@users.noreply.github.com>
2026-06-28 17:09:28 +08:00
zhulinsen
ade3b4cb6e feat: add JP KR market review support (#1822)
* feat: add JP KR market review support

* fix(review-feedback-1822): update the Market Light schema/service support before accepting these

* fix(review-feedback-1822): add JP/KR to daily market context before accepting them and update

* fix(review-feedback-1822): add English JP/KR strategy text or make the renderer language-aware

* fix(review-feedback-1822): update the prompt role/shell alongside the new accepted regions

* fix(review-feedback-1822): 修正 PR 描述中的过期验证结论,并补充/澄清 JP/KR Yahoo Finance 指数兼容性证据或在线验证边界

* fix(review-feedback-1822): 收敛 Market Light 告警契约与用户文档同步问题

* fix(review-feedback-1822): 修正后再合入

* fix(review-feedback-1822): 补齐外部 Yahoo Finance 指数接入与运行时配置变更的兼容性/迁移证据,并修正 PR 描述中与当前 CI 状态不一致的内容

* fix(review-feedback-1822): 当前 CI 状态为 failure,且阻断型 backend-gate 失败

* fix(review-feedback-1822): 修复 MARKET REVIEW REGION 逗号值在交易日过滤与配置 schema 中的契约漂移,并补充对应回归测试

* fix(review-feedback-1822): 收敛 PR 描述中的验证状态与用户可见 Web 改动证据

* fix(review-feedback-1822): 修正 PR 描述中的过期验证结论,并补充 Web 设置变更的截图或无法截图时的替代可视证据说明

* fix(review-feedback-1822): 收敛 PR 描述中的验证状态,并补齐 Web 设置变更的截图或替代可视证据

* fix(review-feedback-1822): 收敛 PR 描述后再合入

* fix(review-feedback-1822): 收敛 PR 描述中的验证状态,并补充 Web 设置变更截图或无法截图时的替代可视证据说明

* fix(review-feedback-1822): 收敛验证状态和 Web UI 可视证据,避免合入记录与实际 head 不一致

* fix(review-feedback-1822): 收敛 PR 描述中的验证状态与 Web 可视证据

* fix(review-feedback-1822): 收敛 PR 描述中的验证状态,并补齐 Web 设置变更的截图或替代可视证据说明

* fix(review-feedback-1822): 修正 PR 描述与当前 CI 事实不一致的问题,补充 Web 设置可视证据,并收敛或拆出 PR 模板改动

* fix(review-feedback-1822): 收敛 PR 描述与证据

* fix(review-feedback-1822): 修正 PR 描述与证据,使验证状态、用户可见变更证据、模板改动范围和当前 head 保持一致

* fix(review-feedback-1822): 收敛 PR 描述、补齐可视证据,并澄清/补充外部模型/API 与运行时配置迁移相关兼容性证据

* fix(review-feedback-1822): 收敛 Web/文档契约不一致、同步残留测试,并更新 PR 描述与可视证据
2026-06-28 17:03:15 +08:00
zhulinsen
03fe7a00a9 feat: 补齐大盘报告与 Web 个股页的概念板块展示 (#1764) (#1778)
* feat(issue-1764): feat:-补齐大盘报告与-web-个股页的概念板块展示

* fix(review-feedback-1778): 确认历史报告 fallback 路径是否完整透传 conceptRankings,否则会出现新字段在部分历史详情/API 场景中静默丢失

* fix: render market review industry and concept rankings side-by-side

* fix: refine related board display and concept ranking cache
2026-06-27 23:07:18 +08:00
zhulinsen
ef9371003c feat: harden JP/KR suffix market support (#1817)
* feat: harden JP/KR suffix market support

* fix: define yfinance index quote metadata

* fix(review-feedback-1817): Handle JP 4-digit bare-code lookup in analysis paths

* fix(review-feedback-1817): Route 4-digit JP bases through API normalization

* fix(review-feedback-1817): 补齐外部 API/provider/运行时配置风险说明或证明该检测为误报
2026-06-27 22:10:08 +08:00
Wenyu Chiou
2086e3cca4 feat(market): Taiwan (台股) suffix-only market detection + routing (#1773)
Per the maintainer review on #1772/#1773, scope this PR to market detection
and data routing only — no Taiwan stock-index / seed / pool-resolution, so a
bare code can never be silently rewritten to a .TW suffix (the index path is a
deferred follow-up with the jp/kr-style honest doc + entry-point tests).

- data_provider/base.py: _is_tw_market / _is_tw_suffix_stock, _market_tag,
  normalize_stock_code, get_daily_data + get_realtime_quote routing,
  _DAILY_MARKET_FETCHER_SUPPORT (YfinanceFetcher only), offshore fundamental path
- data_provider/yfinance_fetcher.py: _get_tw_main_indices (^TWII / ^TWOII) +
  .TW/.TWO suffix passthrough
- src/core/trading_calendar.py: XTAI / Asia/Taipei + get_market_for_stock
- src/market_context.py: detect_market + Taiwan prompt guidelines
- src/{market_phase_summary, services/stock_code_utils, core/pipeline}: tw plumbing
  (stock_code_utils only recognizes .TW/.TWO codes; no bare->suffix rewrite)
- src/services/decision_signal_extractor.py: gracefully skip tw (recognized by
  the data layer but not yet supported by the signal service) — no swallowed
  ValueError + traceback
- tests: test_tw_market_support.py + test_decision_signal_extractor.py
  (test_build_payload_skips_tw_market_gracefully — real tw skip regression)
- docs/market-support.md + CHANGELOG

Deferred follow-ups: stock index/seed + autocomplete + frontend, API market
enums + api_spec, and the service-layer market enums.

Bare numeric codes keep A-share semantics; only an explicit .TW/.TWO suffix
opts into Taiwan.

Refs #1772
2026-06-25 22:55:08 +08:00
mumu
e49301a79f feat: 支持日本/韩国 suffix-only 个股分析 (#1720)
* feat: support JP/KR suffix stock analysis

* fix(review-feedback-1720): 解决冲突后再合入
2026-06-19 11:59:59 +08:00
mumu
3b5ef16144 feat: harden AlphaSift data source routing (#1680)
* feat: harden alphasift data source routing

* fix(review-feedback-1680): Keep the sample AlphaSift pin in sync and Avoid circuit-breaking

* fix(review-feedback-1680): Defer half-open health checks until a source is attempted and Treat

* fix(review-feedback-1680): fix only bypasses this branch for fetchers that opt into allow empty

* fix(review-feedback-1680): Avoid accepting truncated Tencent histories

* fix(review-feedback-1680): 修正 .env.example 的 AlphaSift 快照源默认说明,并避免在 docs/CHANGELOG.md

* fix(review-feedback-1680): src/config.py、src/services/alphasift service.py

* fix(review-feedback-1680): requirements.txt、src/config.py、.env.example、src/services/alphasift

* fix(review-feedback-1680): 修复 TencentFetcher 对显式历史窗口的处理问题

* fix(review-feedback-1680): Check the cap was hit before rejecting Tencent history

* fix(review-feedback-1680): Use Tencent's hyphenated date format and Convert Tencent daily volume

* fix(review-feedback-1680): Accept the executor timeout argument and preserve capped windows with

* fix: limit empty daily data opt-in behavior

* docs: restore AlphaSift 3.22 changelog entry
2026-06-14 18:47:41 +08:00
LouisHong
145ee6b5a5 fix: [issue #1652] stabilize run-flow live states and layout polish (#1682)
* fix: polish run-flow topology layout and i18n

* fix: prevent task panel layout squeeze

* test: update run flow frontend coverage

* fix: isolate market context run flow

* fix: handle completed task flow refresh

* feat: show running run-flow nodes

* fix: show chip provider runs in flow

* fix: stabilize stock run flow live updates

* fix: refine run-flow history lookup and spacing

* fix: update run-flow edge states and layout gaps

* fix: polish run-flow task panel text
2026-06-14 17:57:16 +08:00
LouisHong
4f536026f8 fix: clarify runtime logging context (#1643)
* fix: clarify market review logging context

* docs: update changelog for logging context
2026-06-09 01:08:55 +08:00
Alfred
9f14850265 feat: add intraday realtime quality metadata (#1538)
Refs #1386

Refs #1389
2026-05-31 21:05:12 +08:00
Alfred
b22d7e0277 fix: correct ETF secid routing for efinance (#1535)
Co-authored-by: mumu <42829555+ZhuLinsen@users.noreply.github.com>
2026-05-31 09:27:54 +08:00
mumu
647b9d24e5 fix: support Longbridge OAuth token cache (#1490)
* fix: support Longbridge OAuth token cache

* fix(review-feedback-1490): bump the runtime dependency minimum or fail with a clear upgrade

* fix(review-feedback-1490): 补充对应回归测试

* fix(review-feedback-1490): Point OAuth cache at the dsa home in Docker

* fix(review-feedback-1490): 修复或给出明确的 CI 重跑通过证据

* fix(review-feedback-1490): 处理 Docker/Actions 持久化 token cache 已损坏时无法被新的 LONGBRIDGE OAUTH TOKEN

* fix: tighten Longbridge OAuth compatibility

* fix: refresh stale Longbridge OAuth cache

* fix: guard Longbridge OAuth SDK availability
2026-05-29 23:08:16 +08:00
mumu
a13863e50e fix: 为 Akshare 历史兜底接口增加超时保护 (#1465)
* fix(issue-1464): [bug]-scheduled-analysis-hangs-when-stoc

* fix(review-feedback-1465): Avoid leaking daemon threads on timed-out Akshare calls

* fix(review-feedback-1465): fix can reintroduce hangs/crashes in the exact scheduled pipeline it

* fix(review-feedback-1465): Use a safe start method for timeout subprocesses

* fix(review-feedback-1465): Call freeze support before spawning timeout workers
2026-05-27 21:18:23 +08:00
mumu
e0e5fa6870 feat: add #1391 Phase 1 run diagnostics trace plumbing (#1441)
* fix(issue-1412): [bug]-stock_list格式问题

* fix(review-feedback-1413): preserve exchange hint for dotted A-share inputs

* fix(review-feedback-1413): Keep normalized A-share codes usable by market routing and preserve

* fix(review-feedback-1413): Limit raw dotted codes to fetchers that can parse them

* fix(review-feedback-1413): Keep Tushare daily input normalized for ETF detection

* fix(review-feedback-1413): 澄清结构化检测中的外部模型/API 与运行时配置迁移风险

* fix(review-feedback-1413): 处理或明确确认该失败与本 PR 无关且已有维护者豁免依据

* fix: keep stock list input as bare codes

* docs: add phase-0 run diagnostics contract

* fix(review-feedback-1435): 修正描述并澄清/补齐运行时代码变更的验证证据

* fix(review-feedback-1435): 补齐前缀提示识别,并增加对应回归测试

* fix(review-feedback-1435): 修正

* fix(review-feedback-1435): 解决冲突并更新描述/验证记录后再合入

* fix(review-feedback-1435): 解决冲突,并在最终 head 上重新确认 python -m pytest tests/test a share fetcher code

* fix(review-feedback-1435): 修复并补齐回归覆盖后再复核最终 head

* fix(review-feedback-1435): data provider/baostock fetcher.py 的 convert stock code 只从 .SH/.SS/.SZ

* fix: preserve A-share exchange hints

* fix(review-feedback-1435): 修正 docs/run-diagnostics-p0.md 对 Tushare 本轮范围的矛盾描述

* feat: add phase 1 run diagnostics trace plumbing

* fix(review-feedback-1441): 打通 trace id 与数据源运行快照,改动目标明确
2026-05-25 21:01:40 +08:00
mumu
09afbf566f fix: add #1391 Phase 0 diagnostics and A-share code guards (#1435)
* fix(issue-1412): [bug]-stock_list格式问题

* fix(review-feedback-1413): preserve exchange hint for dotted A-share inputs

* fix(review-feedback-1413): Keep normalized A-share codes usable by market routing and preserve

* fix(review-feedback-1413): Limit raw dotted codes to fetchers that can parse them

* fix(review-feedback-1413): Keep Tushare daily input normalized for ETF detection

* fix(review-feedback-1413): 澄清结构化检测中的外部模型/API 与运行时配置迁移风险

* fix(review-feedback-1413): 处理或明确确认该失败与本 PR 无关且已有维护者豁免依据

* fix: keep stock list input as bare codes

* docs: add phase-0 run diagnostics contract

* fix(review-feedback-1435): 修正描述并澄清/补齐运行时代码变更的验证证据

* fix(review-feedback-1435): 补齐前缀提示识别,并增加对应回归测试

* fix(review-feedback-1435): 修正

* fix(review-feedback-1435): 解决冲突并更新描述/验证记录后再合入

* fix(review-feedback-1435): 解决冲突,并在最终 head 上重新确认 python -m pytest tests/test a share fetcher code

* fix(review-feedback-1435): 修复并补齐回归覆盖后再复核最终 head

* fix(review-feedback-1435): data provider/baostock fetcher.py 的 convert stock code 只从 .SH/.SS/.SZ

* fix: preserve A-share exchange hints

* fix(review-feedback-1435): 修正 docs/run-diagnostics-p0.md 对 Tushare 本轮范围的矛盾描述
2026-05-25 20:38:22 +08:00
mumu
8777439f1c fix: stock_list格式问题 (#1412) (#1413) 2026-05-23 19:15:23 +08:00
vinvcn
2c26203c24 fix: normalize Tencent realtime volume (#1409)
* fix: normalize Tencent realtime volume

* fix: harden Tencent realtime normalization

* docs: localize Tencent realtime comments

---------

Co-authored-by: mumu <42829555+ZhuLinsen@users.noreply.github.com>
2026-05-22 22:42:05 +08:00
TianleZhang
bb814ff89d feat: 邮件报告补充 财务摘要 / 股东回报 / 关联板块,并接入 HK/US 基本面 (#1369)
* feat: enrich email report with financial / dividend / related-boards blocks for CN/HK/US

Render three structured sections in single-stock and dashboard markdown reports
(used by email and other static channels) sourced from fundamental_context:

- 财务摘要: report date, revenue, parent net profit, operating cash flow, ROE,
  YoY growth, gross margin
- 股东回报: TTM cash dividend per share (pre-tax), TTM event count, TTM yield,
  latest ex-dividend date
- 关联板块: belong_boards with sector-leading/lagging signal & change_pct when
  available; auto-collapses to a 2-col (name / type) layout when no sector
  ranking data exists (typical for HK/US)

To unlock the offshore (HK/US) data path:

- Add YfinanceFundamentalAdapter producing the same bundle shape as the
  AkShare adapter, with growth, financial_report (incl. currency),
  TTM dividend events, and sector+industry as belong_boards
- Route get_fundamental_context for hk/us through a new
  _build_offshore_fundamental_context helper instead of short-circuiting
  to not_supported; keep capital_flow / dragon_tiger / boards as
  not_supported (no equivalent feed)
- Stop overwriting belong_boards in the pipeline for non-CN markets so the
  adapter-provided sector/industry data survives

Notification renderer:

- Attach fundamental_context onto AnalysisResult so the notification layer
  can reach it without an extra DB round-trip
- Currency-aware formatting: USD -> 亿/万美元, HKD -> 亿/万港元,
  CNY/other -> 亿/万元; per-share amounts get the same suffix
- Right-align numeric columns and center the report-date column in the
  financial summary and shareholder return tables for cleaner rendering

Tests:

- Offline coverage for the new yfinance adapter (symbol conversion, bundle
  shape, refusal to use QoQ as YoY, graceful degradation when yfinance is
  unavailable)
- Offshore market path through DataFetcherManager.get_fundamental_context
- Adaptive related-boards rendering (2-col when no sector signal,
  4-col when at least one row carries data)
- Currency-aware amount formatting for AAPL (USD) and HK09988 (CNY)

* fix: split financial vs dividend currency and recompute TTM yield for HK/US

HK ADRs typically report financialCurrency=CNY but pay dividends in HKD
(live yfinance confirms: 9988.HK / 0700.HK both have financialCurrency=CNY
and currency=HKD; BABA US ADR has financialCurrency=CNY and currency=USD).
Previously the adapter wrote a single currency field shared by both the
financial report and the dividend block, which rendered HK dividends as
yuan in the email report.

This change writes earnings.financial_report.currency from
info.financialCurrency and earnings.dividend.currency from info.currency
independently, and the notification renderer reads each block's own
currency (falling back to financial_report.currency when dividend.currency
is absent, which keeps AkShare A-share rendering unchanged).

TTM dividend yield was sourced from info.dividendYield, mixing the
numerator (TTM cash window) with a denominator-defined yield from a
different yfinance computation. The yield is now recomputed as
ttm_cash_dividend_per_share / latest_price * 100 with both sides in the
trading currency (latest_price falls through info.currentPrice ->
regularMarketPrice -> previousClose). info.dividendYield is now a
last-resort passthrough (no *100, since current yfinance already returns
percent), behind info.trailingAnnualDividendYield (decimal).

Docs (full-guide / full-guide_EN) updated: HK/US now returns
valuation/growth/earnings/belong_boards rather than not_supported, with
the dual-currency split and the TTM yield formula explicitly documented.
CHANGELOG entries rewritten to reflect the corrected behavior.

Verified live against 9988.HK / 0700.HK / AAPL / 600519: the rendered
email shows financial summary in 亿元 for HK ADRs (CNY statements) while
the shareholder return block shows 港元 (HKD dividends), and the
recomputed yields match ttm_cash / latest_price in the same currency.

---------

Co-authored-by: suan baby <suanai@tianle.me>
2026-05-21 20:04:32 +08:00
mumu
b7d449783c fix: 资金流数据缺失 (#1367) (#1368)
* fix(issue-1367): [bug]-资金流数据缺失

* fix(review-feedback-1368): preserve agent-provided chip data when prefetch is unavailable and

* fix(review-feedback-1368): 补充具体受影响文件/字段、是否会清理或迁移用户已有配置、用户可见恢复方式,以及对应回归测试或确认不涉及运行时行为的证据

* fix(review-feedback-1368): 结构化检测显示本 PR 涉及模型名、provider、Base URL、废弃日期、LiteLLM 或 LLM

* docs: trim issue 1367 compatibility notes

* docs: keep issue 1367 note in changelog only
2026-05-20 23:32:14 +08:00
wqlC
5029605efa fix: clear AlphaVantage fetcher index name to avoid 'date' ambiguity (#1351)
_fetch_raw_data 中 `df.index = pd.to_datetime(df['date'])` 会让
DatetimeIndex 继承源 Series 的 name='date',随后 _normalize_data
重新添加 'date' 列时与索引 level 同名,导致 pandas 在
sort_values('date') 处抛出 ValueError。

复现:load_history_df('AAPL') 走 AlphaVantage 通道时返回 0 行,
报错 "'date' is both an index level and a column label"。

修复:assign 索引后立刻清空 index.name,下游可无歧义引用 'date' 列。
影响范围仅 data_provider/alphavantage_fetcher.py 一行,
其他数据源链路未触及。

Co-authored-by: 贞元 <zhenyuan.wql@alibaba-inc.com>
2026-05-19 22:00:12 +08:00
DragonL641
51e7337882 feat: add Finnhub & AlphaVantage US market data source adapters (#1313)
* feat: add Finnhub and AlphaVantage API key config fields

* feat: add FinnhubFetcher for US market OHLCV and realtime quotes

* feat: add AlphaVantageFetcher for US market OHLCV and realtime quotes

* feat: register FinnhubFetcher and AlphaVantageFetcher in DataFetcherManager

* fix: address PR review feedback on US fetcher routing and AlphaVantage pct_chg

- Fix AlphaVantage pct_chg calculation: sort by date ascending before
  computing percentage change to handle newest-first API response
- Extend US daily routing source_order to include FinnhubFetcher and
  AlphaVantageFetcher in the failover chain
- Add newest-first pct_chg regression test for AlphaVantage
- Add US routing fallback order test
- Update CHANGELOG.md with new feature and fix entries
- Add docs/specs/ to .gitignore to prevent accidental commits

Verified: 1956 tests passed, ci_gate.sh clean

* fix: address third round PR review - routing gaps, index isolation, CHANGELOG cleanup

- Isolate US index routing: YFinance-first, skip Finnhub/AlphaVantage for index codes
- Add Finnhub/AlphaVantage to US realtime_quote and get_stock_name routing
- Clean CHANGELOG.md: only our 3 Finnhub/AlphaVantage entries in [Unreleased]
2026-05-17 21:24:34 +08:00
mumu
0fb6c9c5b8 feat: add market review data sources (#1278)
* feat: add market review data sources
2026-05-15 20:51:22 +08:00
zbl-96
72da793233 fix(data-provider): skip unavailable optional fetchers and back off longbridge reconnects (#1249)
* fix(data-provider): skip unavailable optional fetchers and back off longbridge reconnects

* fix(data-provider): back off unavailable longbridge routes

* fix(data-provider): back off noisy pytdx name lookups

* docs: align PR 1249 changelog and compatibility notes

* fix(config): preserve legacy schedule override precedence
2026-05-10 23:28:30 +08:00
mumu
595908c81a chore: reorganize root files (#1257)
* chore: reorganize root files

* fix: mark moved docs assets as binary
2026-05-10 19:50:22 +08:00
mumu
7bb7dc99dd Fix HK historical data provider routing (#1232) 2026-05-09 19:57:42 +08:00
Jonty Wang
07b4fe5795 feat: Add Hong Kong market support (#1068)
* fix: update report title from 'A股智能分析报告' to '股票智能分析报告'

* feat: add support for Hong Kong market in market review and analysis features

* fix: update daily_analysis workflow name and adjust region check in market analyzer

* fix: enhance market region handling in run_market_review and compute_effective_region functions

* test: add Hong Kong market support in MarketReviewLocalizationTestCase

* fix: update market review region handling to include Hong Kong support in documentation and logic

* fix: update daily_analysis workflow name to Chinese and adjust Hong Kong index symbols in yfinance_fetcher

* fix: refactor market review execution to use run_market_review for improved handling

* fix: implement trading day filtering in MarketCommand to skip reviews on market holidays

* fix: update changelog and README to reflect market command enhancements and support for multiple regions

* fix: update YfinanceFetcher to include HK index symbol mappings for improved data retrieval

Co-authored-by: Jonty <jonty.wang@hk.com.sg>
Co-authored-by: mumu <42829555+ZhuLinsen@users.noreply.github.com>
2026-04-28 22:25:29 +08:00
mumu
efd2bce694 fix: handle efinance index open column fallback (#1051)
* fix: handle efinance index open column fallback
2026-04-19 22:27:25 +08:00
SophiaJiaLi
0e8728fdcf fix: return latest trade date on non-trade days for chip distribution and sector rankings(Fix #1009) (#1067)
Problem Statement:
On non-trade dats, get_trade_time() returned the second-to-last trade instead of the latest one.

Root Cause:
_pick_trade_date(trade_dates,use_today=False) skips trade_dates[0], but on non-trade days trade_dates[0] is already the most recent trade date

Solution:
for non-trade days, put use_today=Ture
2026-04-17 22:18:59 +08:00
mumu
edab25d880 feat: 让后端优先复用现有 stocks.index.json 解析股票名称映射,在缺失时保持现有回退链路… (#1027) (#1033)
* feat(issue-1027): [feature]-后端复用前端-stocks.index.json-作为全量股
2026-04-10 15:40:55 +08:00
jiangmin168168
bfa7b8cd89 Feat/tushare增加港股查询支持 (#994) 2026-04-08 23:03:14 +08:00
mumu
44d3b65a40 fix: 缺失值判断逻辑不应静默吞掉非预期异常 (#996) (#997)
* fix(issue-996): [bug]-缺失值判断逻辑不应静默吞掉非预期异常
2026-04-04 10:21:01 +08:00
mumu
314de7fb58 fix: akshare港股增强 (#940) (#964) 2026-04-03 22:49:15 +08:00
Rules
dd89d06a7d feat:修复美股数据丢失,新增长桥数据源 (#981)
* fix:新增长桥数据源,补充美港股数据

* ci: Actions 注入 LONGBRIDGE_* 环境变量并增加数据源检查;full-guide 补充 CI 配置说明

Made-with: Cursor

* fix:长桥参数初始化时机调整

* fix:添加长桥初始化调试日志

* fix:调整数据源优先级

* Update tests/test_search_searxng.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update data_provider/base.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update data_provider/longbridge_fetcher.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update docs/README_CHT.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update docs/README_EN.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update README.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update docs/README_EN.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update docs/README_CHT.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix(longbridge): 使用 get_config() 替代不存在的 config 导入(_is_available)

* fix:CHANGELOG、联调脚本重命名、繁中 README 英文链接、PRINT_QUOTE_PACKAGES 默认值已对齐。

* fix:调整审核中的问题

---------

Co-authored-by: xuxiong <xiong.xu@xsgames.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-02 23:05:08 +08:00
mumu
066a597028 fix: 减少实时行情链路的误报和重复调用,让系统只有在全部数据源都不可用时才对用户提示“实时行情获取失败” (#792) (#905)
* fix(issue-792): 总是提示实时行情获取失败是为啥啊?
2026-04-01 10:19:12 +08:00
mumu
c89fd9cf20 fix: 修复并发执行时共享状态缺少统一加锁的问题 (#881) (#928)
* fix(issue-881): [bug]-修复并发执行时共享状态缺少统一加锁的问题
2026-03-31 22:53:56 +08:00