* fix: compact aggregate report market status
* fix(review-feedback-1612): preserve input order when selecting market status
* chore: rerun review after clarifying PR scope
* fix(api): return json from root health endpoint
* test(web): stabilize vitest runtime setup
* chore(security): warn on public web exposure without auth
* refactor(api): centralize error response helpers
* chore(db): record baseline schema version
* refactor(web): extract portfolio formatting helpers
* fix(db): make schema baseline initialization idempotent
* docs: document health check and schema baseline changes
* feat(issue-1580): feat:-完善大盘复盘历史聚合、查看与删除能力
* fix(review-feedback-1598): resetting the paged window or deduping the merged list before storing it
* fix(review-feedback-1598): 补齐 AGENTS.md 要求的用户可见能力文档同步
* feat: upgrade MiniMax default model to M3
- Add MiniMax-M3 to model list and set as default
- Keep MiniMax-M2.7 and MiniMax-M2.7-highspeed
- Remove older models (M2.5)
- Update related tests
* docs: clarify MiniMax-M3 conservative <=512K pricing tier and restore M2.5 legacy entry
Address review feedback on MiniMax model upgrade:
- M3 official docs report up to 1M input tokens with a separate >512K input
pricing tier; expand inline comments in src/agent/llm_adapter.py and in
docs to make clear that this PR conservatively registers only the
<=512K input bucket (context_window=512000, max_tokens=128000,
$0.6/M input, $2.4/M output), and that long-context cost estimates
using this entry should be treated as a floor.
- Restore MiniMax-M2.5 as a legacy entry under _CUSTOM_MODEL_PRICING so
existing user configs that still reference M2.5 continue to report
accurate cost estimates instead of silently falling back to the zero-
cost generic pricing. M2.5 is still listed as a Legacy Model on the
official pricing page; the comment block notes the removal precondition.
- Update docs/CHANGELOG.md and docs/llm-providers.md to mention the 1M
upstream limit, the conservative <=512K registration, and that M2.5 is
retained as legacy pricing.
No runtime behavior change beyond the M2.5 pricing restoration; existing
M3 / M2.7 pricing values are unchanged.
---------
Co-authored-by: octo-patch <octo-patch@github.com>
* feat: add stock bar sidebar and watchlist management
- Replace history list sidebar with stock bar that deduplicates stocks
and places market review at top, ordered by latest analysis time
- Add GET /api/v1/history/stocks endpoint for distinct stock list
- Add code normalization to merge code variants (e.g. 002460 / 002460.SZ)
- Add GET/POST /api/v1/stocks/watchlist CRUD endpoints
- Add watchlist card to report detail right sidebar
- Add watchlist buttons to chat/ask page with auto stock code detection
- Add useWatchlist hook for shared watchlist state management
- Update CHANGELOG with [Unreleased] flat entries
Refs #1530
* fix: use canonical normalize_stock_code for stock code dedup
Replace ad-hoc suffix stripping with the existing normalize_stock_code
in data_provider/base.py, which handles all supported variants:
SH600519 / 600519.SH / HK00700 / 00700.HK / BJ920748 / 920748.BJ.
Addresses Codex P2 suggestion on PR #1540.
* fix: address PR review feedback on #1540
1. Fix extractStockCodeFromMessage to reject exchange prefixes
(SH/SZ/BJ/HK/US/SS) as standalone tickers; add explicit patterns
for SH600519/SZ000001/BJ920748 prefix formats.
Move function to utils/chatStockCode.ts to satisfy react-refresh.
2. Add stock code validation and normalization in watchlist API
(add/remove endpoints now validate format and use
normalize_stock_code before persisting to STOCK_LIST).
3. Over-fetch distinct stocks (limit*3) before normalization dedup
to avoid losing results when code variants shrink after grouping.
4. Update HomePage tests: remove batch-delete test scenario (sidebar
replaced by StockBar), add getStockBarList/getWatchlist mocks,
adjust assertions to match StockBarItem accessible names.
Refs #1540
* fix: normalize watchlist codes on read/write and strengthen validation
1. _read_watchlist_codes now normalizes and deduplicates all existing
STOCK_LIST entries via normalize_stock_code, so hk00700/HK00700/00700.HK
are treated as one canonical entry.
2. _write_watchlist_codes deduplicates before persisting.
3. _validate_and_normalize_stock_code uses regex matching against
all supported stock code formats (aligned with frontend validateStockCode).
4. Restore accidentally deleted CHANGELOG entry for same-stock history trend.
5. Fix CHANGELOG/PR description API paths to match actual routes.
Refs #1540
* fix: normalize stock codes in frontend watchlist comparison
Add normalizeStockCode utility mirroring backend normalize_stock_code,
and use it in isInWatchlist so SH600519/600519.SH/HK00700/00700.HK
variants are all recognized as the same stock. Add 10 regression tests.
Refs #1540
* fix: normalize stock code variants in watchlist comparison and extraction
1. ChatPage.tsx: stockInWatchlist now normalizes input via normalizeStockCode
before comparing against watchlistCodes, so SH600519/600519.SH variants
are correctly recognized as already being in the watchlist.
2. chatStockCode.ts: add \d{1,5}\.HK suffix pattern (e.g. 00700.HK → HK00700),
and return normalizeStockCode(normalized) for all extracted codes.
3. stocks.py: add bare 5-digit HK code (\d{5}) to _STOCK_CODE_RE regex.
4. ChatPage tests: add mockGetWatchlist/AddToWatchlist/RemoveFromWatchlist mocks,
add 2 regression tests for watchlist button with code variants
(600519.SH → 600519 and 1810.HK → HK01810).
Refs #1540
* fix: keep STOCK_LIST as-is, normalize only for comparison
Revert read/write normalization in _read_watchlist_codes and
_write_watchlist_codes. STOCK_LIST is now stored exactly as the user
input, without auto-normalization. Add/remove endpoints normalize
only for duplicate/matching comparison, preserving raw codes.
Refs #1540
* feat: restore batch delete in StockBar and add delete-by-code API
1. Add DELETE /api/v1/history/by-code/{stock_code} endpoint to delete
all history records for a given stock code in one call.
2. Add deleteByCode function to frontend historyApi.
3. StockBar: restore select-all checkbox and batch delete button,
matching the original HistoryList UX.
4. StockBarItem: add per-item delete button (trash icon, visible on hover).
5. HomePage: wire handleDeleteStock to StockBar, refresh stock bar and
history after deletion.
6. Update CHANGELOG to reflect restored batch delete capability.
Refs #1540
Introduce a zero-fetch builder that assembles internal AnalysisContextPack from existing pipeline artifacts, with tests and P2 contract docs for #1389.
* 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>
Improve stock-news relevance scoring and ranking.
Fix lower-case US ticker suffix matching, preserve Chinese preference for A/HK weakly-related news, and cover the addressed review edge cases.