* fix: prefetch portfolio snapshot quotes
* fix: keep portfolio quote prefetch concurrent
* fix: stop sharing fetcher manager across portfolio quote workers
Previously _prefetch_realtime_position_prices reused a single
DataFetcherManager for all workers when prefetch_realtime_quotes
returned a full count. That count only signals a successful bulk
fetch on the underlying fetcher's module-level cache; it does not
guarantee every per-symbol get_realtime_quote() is a cache hit
(mixed-market portfolios, cache misses, or providers that report
counts loosely). With a shared manager, manager-owned per-fetcher
locks (_call_fetcher_method) re-serialize the cache-miss reads
and re-trigger the #1803 30s timeout the prefetch was meant to
avoid.
Discard the prefetch manager after warming the module-level
fetcher cache and let each worker own its DataFetcherManager.
Independent locks restore true concurrency, while cache hits
still work because the realtime cache is fetcher-module-level.
Add a regression test that exercises len>=5, full-count bulk
prefetch, and a per-symbol fetch path that still blocks; it
verifies a fresh manager per worker and max_active>=2.
Follow-up to the #1773 data-layer MVP (Taiwan suffix-only detection + routing,
merged in 2086e3c). That MVP deferred the service/API/frontend layers, leaving a
live defect: tw was absent from the DecisionSignal/Portfolio service VALID_MARKETS,
so _normalize_market("tw") raised ValueError on the decision-signal write path.
The analysis pipeline auto-extracts a DecisionSignal after history save
(_extract_decision_signal_after_history_save), so every tw analysis silently
failed to persist a signal while jp/kr succeeded -- tw was the only
yfinance-supported market that could be analyzed but never produced a signal.
Converge the tw market contract for DecisionSignal + Portfolio + Intelligence in
one pass (mirroring jp/kr #1720), per the human review on #1801 asking not to
land it piecemeal:
Backend service + API:
- src/services/{portfolio,intelligence}_service.py: VALID_MARKETS /
_ALLOWED_MARKETS + _normalize_market error strings accept tw
- src/services/decision_signal_service.py: _normalize_market error string
(VALID_MARKETS is imported from portfolio_service, so the set change propagates)
- src/services/decision_signal_extractor.py: drop the now-stale "(e.g. tw)" guard
comment (tw is supported; the guard still protects genuinely-unsupported markets)
- api/v1/schemas/{decision_signals,intelligence,portfolio}.py: Pydantic Literals + tw
- api/v1/endpoints/decision_signals.py + docs/architecture/api_spec.json: market
filter description + DecisionSignalMarket enum gain tw; test_api_schema_pydantic
exact-match vs create_app().openapi() passes (api_spec kept CRLF)
Frontend (DecisionSignal + Portfolio typed consumers only; tsc + vitest pass):
- apps/dsa-web/src/types/{decisionSignals,portfolio}.ts + pages/{DecisionSignalsPage,
PortfolioPage}.tsx + utils/{decisionSignalLabels,stockCode}.ts + i18n/uiText.ts:
add tw to the DecisionSignalMarket / portfolio market unions, the market filter
options, the tw display label, and .TW/.TWO stock-code normalization
- the alert Market-Light surface (types/alerts.ts MarketRegion, featureText
ALERT_MARKET_REGION_*) is intentionally LEFT OUT: the backend market_light_service
is cn/hk/us only, so exposing tw there would be a front/back mismatch
Tests:
- flip the two #1773 graceful-skip regressions to first-class assertions and add
test_extract_and_persist_writes_tw_signal (end-to-end persist guard)
- frontend: PortfolioPage + stockCode vitest gain tw cases
Docs (reconcile the tw contract so changelog/topic docs/code state one fact):
- docs/CHANGELOG.md: rewrite the #1772 [Unreleased] entries so they no longer say
"service/API deferred" + "tw gracefully skipped" alongside "tw now supported"
- docs/market-support.md, docs/decision-signals.md, docs/intelligence-sources.md:
sync the tw market enum / filter / examples; keep the boundary note
Still deferred (separate follow-ups): the Taiwan stock-index/seed + Web autocomplete,
and the alert (大盘红绿灯) Market-Light tw support (needs a market_light backend change).
Refs #1772
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
* fix(issue-1739): [bug]-docker重新部署时,.env设置里的通知变量$content_j
* fix(review-feedback-1741): escape/unescape the braced forms too, or reject them before persisting
* fix(review-feedback-1741): preserve braced templates during env import
* fix(review-feedback-1741): 澄清外部模型/API 配置风险提示是否命中真实 diff
* fix(review-feedback-1741): limit raw parsing to CUSTOM WEBHOOK BODY TEMPLATE or re-apply the
* fix: backfill report decision signals
* fix(review-feedback-1719): 确认并修复“不明确建议”可能被默认回填成 hold 信号的正确性风险 and persist the report date in
* fix(review-feedback-1719): 修复 legacy decision type 被当作明确 action 导致误回填的问题
* fix(review-feedback-1719): Prevent stale backfills from staying active
* fix(review-feedback-1719): anchor the same metadata-based TTL that default expires at uses to the
* fix(review-feedback-1719): 修复或明确处理懒回填时间锚点的时区/时间域不一致风险
* feat: add intelligence source ingestion baseline
* feat: feed local intelligence into analysis contexts
* fix(review-feedback-1708): Reject DNS names that resolve privately and Validate redirect targets
* fix(review-feedback-1709): 处理大盘复盘本地资讯可能被搜索结果截断掉的问题,并澄清结构化检测到的外部模型/API 或运行时配置风险是否为真实变更
* fix(review-feedback-1708): Pin DNS resolution before fetching and Stream feeds before enforcing
* fix(review-feedback-1709): filter by published at for analysis evidence, or keep missing publish
* fix(review-feedback-1708): Sanitize fetch errors before returning them and Normalize nullable
* fix(review-feedback-1709): Normalize symbol scope before lookup
* fix(review-feedback-1708): Avoid rolling back prior item inserts on duplicate races and Reject
* fix(review-feedback-1709): Use the effective news window for local evidence and add the plain
* fix(review-feedback-1708): 落地可配置 RSS/Atom 情报源、存储、查询、retention 和基础安全边界
* fix(review-feedback-1709): 补充“revert 本 PR 或移除本地资讯接入入口/清退本地资讯源配置数据”级别说明即可
* fix(review-feedback-1708): 落地 RSS/Atom 情报源的存储、拉取、查询、retention 和基础安全边界
* fix(review-feedback-1709): 补 Refs 1707
* fix(review-feedback-1709): 解决冲突后再合入
* fix(review-feedback-1709): src/services/intelligence service.py 回退了资讯源 URL 安全防护:移除了 hostname DNS
* fix: harden intelligence source ingestion
* fix(review-feedback-1709): Sanitize upstream fetch errors before returning them
* feat: add NewsNow intelligence sources
* fix(review-feedback-1709): 确认并修复
* fix(review-feedback-1709): 补一个未命中敏感规则的异常回归测试
* fix(review-feedback-1709): 处理
* docs: enhance NEWSNOW_BASE_URL compatibility guidance with official links
- Add official NewsNow GitHub deployment guide link to .env.example
- Include curl-based API contract verification example for production validation
- Update CHANGELOG.md with official repository reference and deployment recommendation
- Clarify risk of public example instance and necessity for self-hosted in production
- All HTTP 500 responses already use sanitize_diagnostic_text for privacy
Ref #1707
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(review-feedback-1709): 补齐官方 NewsNow 实例链接文档或 API 契约确认,明确指出公开实例风险(需结合当前部署指南文档,更新 .env.example
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat: add intelligence source ingestion baseline
* fix(review-feedback-1708): Reject DNS names that resolve privately and Validate redirect targets
* fix(review-feedback-1708): Pin DNS resolution before fetching and Stream feeds before enforcing
* fix(review-feedback-1708): Sanitize fetch errors before returning them and Normalize nullable
* fix(review-feedback-1708): Avoid rolling back prior item inserts on duplicate races and Reject
* fix(review-feedback-1708): 落地可配置 RSS/Atom 情报源、存储、查询、retention 和基础安全边界
* fix(review-feedback-1708): 落地 RSS/Atom 情报源的存储、拉取、查询、retention 和基础安全边界
* fix: harden alphasift hotspot fallback
* fix(review-feedback-1691): either copy directories recursively or enumerate the cache files and
* fix: address alphasift hotspot review feedback
* fix(review-feedback-1691): route the migration through the new recursive helper or explicitly and
* fix(review-feedback-1691): only use the new friendly refresh-failure message here, or localize
* 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
* feat: adapt AlphaSift hotspot topics
* fix(review-feedback-1635): preserve cached hotspots when refresh returns no rows and Avoid
* fix(review-feedback-1635): Reload detail when refreshing the selected hotspot and Fall back when
* fix(review-feedback-1635): update by the requested topic or a request token
* fix(review-feedback-1635): preserve analysis when optional serve startup fails and Fall back when
* fix(review-feedback-1635): 当前 CI 状态为 failure,且 backend-gate 为 cancelled、docker-build 为 skipped
* fix(review-feedback-1635): 让 backend-gate / docker-build 在当前 head 上完成并通过,或给出可接受的维护者级豁免说明
* fix(review-feedback-1635): 当前 CI 仍未闭环:结构化事实显示 backend-gate 被取消、docker-build skipped
* fix(review-feedback-1635): 修复 hotspot detail 的上游 schema 容错问题,并让当前 head 的阻断 CI 重新跑通
* fix(review-feedback-1635): docs/CHANGELOG.md:当前 diff 不是在 [Unreleased] 扁平格式下追加本 PR
* fix(review-feedback-1635): CI 闭环未完成:结构化事实显示当前 CI 状态为 failure,backend-gate cancelled、docker-build
* fix(review-feedback-1635): 让当前 head 的阻断型 CI 完整通过,并修正文档中的 AlphaSift commit hash 不一致
* fix(review-feedback-1635): CI 仍未在当前 head 完整通过:结构化事实显示
* fix(review-feedback-1635): 修复 docs/CHANGELOG.md 对主线既有 [Unreleased] 条目的删除,并补齐 AlphaSift
* fix(review-feedback-1635): Accept slash-containing hotspot topics
* fix(review-feedback-1635): Try industry constituents for industry hotspots
* fix(review-feedback-1635): Clear old hotspot details when loading a new topic and preserve the
* test: isolate main schedule environment
* chore: clean AlphaSift PR scope
* fix: respect AlphaSift hotspot fallbacks
* feat: add live run-flow updates and diagnostics
* feat: update task status filter to include 'cancel_requested'
* feat: enhance run-flow diagnostics and task management with cancel-requested status handling
* fix: stabilize run flow live updates
* fix: align live run-flow review contracts
* fix: sanitize live run-flow events