Skip to content

Commit efc9267

Browse files
author
AdvancingTitans
committed
fix: tighten dated stages and news freshness
1 parent f0c08f0 commit efc9267

8 files changed

Lines changed: 231 additions & 37 deletions

File tree

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.1.8] - 2026-06-01
9+
10+
### Fixed
11+
- Focused commands now include clearer market/date context, including `YYYY-MM-DD + stage` labels such as `2026-05-29 交易日盘后`.
12+
- A-share activity fallback no longer pretends the requested date is the data date when the online source has no explicit trading date.
13+
14+
### Changed
15+
- All news displays now filter to the requested trading date only, show at most five items, show fewer when fewer valid items exist, and print a clear empty-state message when no effective news is found.
16+
- `young a` now includes an A-share news section by default and supports `--no-news`.
17+
- News heat ranking now omits zero-news symbols instead of filling Top 5 with inactive tickers.
18+
819
## [0.1.7] - 2026-06-01
920

1021
### Added

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Requires Python 3.10+.
2727

2828
```bash
2929
young a # A-share after-hours dashboard (the main thing)
30+
young a --no-news # A-share dashboard without news links
3031
young hk # Hong Kong indices snapshot
3132
young us # US indices snapshot
3233
young global # A + HK + US in one view
@@ -75,6 +76,7 @@ It is also a foundation for analysis pipelines: every subcommand maps to a Pytho
7576
- **Rich terminal tables** — readable on dark and light terminals.
7677
- **Verified A-share fund flow**`young flow` shows the latest Eastmoney A-share / Shanghai Composite fund-flow record and its trading date explicitly, including a warning when it differs from the requested report date; if realtime fund-flow sources are temporarily unavailable, it tries online market indicators from Sina/Tencent and clearly labels them as activity references before falling back to the most recent locally cached good fund-flow record.
7778
- **News heat ranking** — HK/US focus stocks can be ranked by filtered news heat from multiple no-login sources: Futu, Sina Finance, and Eastmoney fast news. Xueqiu/THS are intentionally not hardwired unless a stable no-login interface is available.
79+
- **Same-day news discipline** — news sections only show items published on the requested trading date, up to five items, with a clear empty-state message when nothing valid is available.
7880
- **Sector boards via browser fallback** — when Eastmoney's board API rate-limits, falls back to rendering the public web page (optional, requires a local browser engine).
7981
- **Local updater**`young update` runs `python -m pip install --upgrade young-stock-cli` with the same interpreter that launched the CLI.
8082

@@ -120,6 +122,7 @@ A 股盘后行情命令行工具。免登录、免 API key、免反爬技巧 —
120122
```bash
121123
pip install young-stock-cli
122124
young a # A 股盘后总览(主命令)
125+
young a --no-news # 只看 A 股行情与情绪,跳过新闻
123126
young stock 600519 # 单只股票速览(A股 / 港股 / 美股)
124127
young news 3690.HK # 单只股票消息面,多源新闻与链接
125128
young hk --no-news # 只看行情,跳过新闻链接

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "young-stock-cli"
7-
version = "0.1.7"
7+
version = "0.1.8"
88
description = "A-share (China stock market) after-hours CLI — no login, no scraping tricks, just data."
99
readme = "README.md"
1010
requires-python = ">=3.10"

src/young_stock/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""young-stock-cli: A-share after-hours CLI."""
22

3-
__version__ = "0.1.7"
3+
__version__ = "0.1.8"

src/young_stock/_core.py

Lines changed: 129 additions & 30 deletions
Large diffs are not rendered by default.

src/young_stock/cli.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def _run(market: str, date: str | None, refresh: bool, include_news: bool = True
2424
_core.cache_clear_old(days=7)
2525
date_str = date or _core.nearest_trade_date()
2626
if market == "a":
27-
_core.run_a_share(date_str)
27+
_core.run_a_share(date_str, include_news=include_news)
2828
elif market == "hk":
2929
_core.run_hk_market(date_str, include_news=include_news)
3030
elif market == "us":
@@ -43,8 +43,9 @@ def _run(market: str, date: str | None, refresh: bool, include_news: bool = True
4343
@cli.command(help="A-share dashboard: indices, ZT/DT pool, verified A-share fund flow, boards.")
4444
@_date_opt
4545
@_refresh_opt
46-
def a(date: str | None, refresh: bool) -> None:
47-
_run("a", date, refresh)
46+
@click.option("--no-news", is_flag=True, help="Only show market data, skip news lookup.")
47+
def a(date: str | None, refresh: bool, no_news: bool) -> None:
48+
_run("a", date, refresh, include_news=not no_news)
4849

4950

5051
@cli.command(help="Hong Kong market after-hours snapshot.")
@@ -137,15 +138,16 @@ def stock(symbol: str, date: str | None, refresh: bool, no_news: bool) -> None:
137138

138139

139140
@cli.command(help="Show multi-source news for one stock, e.g. 600519, 0700.HK, AAPL.")
140-
@click.argument("symbol")
141+
@click.argument("parts", nargs=-1, required=True)
141142
@_date_opt
142143
@_refresh_opt
143144
@click.option("--limit", default=8, show_default=True, help="Maximum news items to show.")
144-
def news(symbol: str, date: str | None, refresh: bool, limit: int) -> None:
145+
def news(parts: tuple[str, ...], date: str | None, refresh: bool, limit: int) -> None:
145146
if refresh:
146147
_core.NO_CACHE = True
147148
_core.cache_clear_old(days=7)
148149
date_str = date or _core.nearest_trade_date()
150+
symbol = parts[-1] if len(parts) > 1 and parts[0].lower() == "stock" else parts[0]
149151
_core.run_stock_news(symbol, date_str, size=limit)
150152

151153

tests/test_cli.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,21 @@ def fake_run_stock(symbol, date_str, include_news):
5353
assert calls == [("600519", "20260529", False)]
5454

5555

56+
def test_cli_a_no_news(monkeypatch):
57+
from click.testing import CliRunner
58+
59+
calls = []
60+
monkeypatch.setattr(cli_module._core, "nearest_trade_date", lambda: "20260529")
61+
monkeypatch.setattr(cli_module._core, "cache_clear_old", lambda days: None)
62+
monkeypatch.setattr(cli_module._core, "run_a_share", lambda date_str, include_news=True: calls.append((date_str, include_news)))
63+
64+
runner = CliRunner()
65+
result = runner.invoke(cli, ["a", "--no-news"])
66+
67+
assert result.exit_code == 0
68+
assert calls == [("20260529", False)]
69+
70+
5671
def test_cli_us_no_news(monkeypatch):
5772
from click.testing import CliRunner
5873

@@ -82,6 +97,12 @@ def test_cli_news_runs_stock_news(monkeypatch):
8297
assert result.exit_code == 0
8398
assert calls == [("0700.HK", "20260529", 6)]
8499

100+
calls.clear()
101+
result = runner.invoke(cli, ["news", "stock", "0700.HK", "--limit", "6"])
102+
103+
assert result.exit_code == 0
104+
assert calls == [("0700.HK", "20260529", 6)]
105+
85106

86107
def test_cli_hk_no_news(monkeypatch):
87108
from click.testing import CliRunner

tests/test_core.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,20 @@ def test_fund_flow_uses_sina_activity_before_cache(monkeypatch):
327327
assert "_cache_note" not in flow
328328

329329

330+
def test_market_activity_uses_latest_trade_date_when_source_has_no_date(monkeypatch):
331+
monkeypatch.setattr(_core, "nearest_trade_date", lambda: "20260529")
332+
333+
flow = _core._market_activity_snapshot(
334+
[{"f12": "000001", "f14": "上证指数", "f2": "3000", "f3": "1.0", "f6": "1000"}],
335+
"新浪财经A股指数行情",
336+
"20260601",
337+
)
338+
339+
assert flow["date"] == "2026-05-29"
340+
assert flow["_requested_date"] == "2026-06-01"
341+
assert flow["_date_note"] == "latest_available"
342+
343+
330344
def test_print_fund_flow_labels_a_share_and_stale_notice(capsys):
331345
_core.print_fund_flow(
332346
{
@@ -339,6 +353,7 @@ def test_print_fund_flow_labels_a_share_and_stale_notice(capsys):
339353

340354
output = capsys.readouterr().out
341355
assert "A股资金流向" in output
356+
assert "阶段: 2026-05-29 交易日盘后" in output
342357
assert "当前展示来源最新可用数据" in output
343358
assert "主力净流入" in output
344359
assert "暂不展示" not in output
@@ -451,6 +466,41 @@ def test_news_chain_falls_back_to_eastmoney(monkeypatch):
451466
assert news["data"][0]["title"].startswith("腾讯")
452467

453468

469+
def test_combined_news_filters_to_requested_date(monkeypatch):
470+
monkeypatch.setattr(
471+
_core,
472+
"futu_news_search",
473+
lambda *args, **kwargs: {
474+
"source": "futu_news",
475+
"data": [
476+
{"title": "腾讯今日新闻", "url": "https://today.example", "publish_time": 1780300000},
477+
{"title": "腾讯旧新闻", "url": "https://old.example", "publish_time": 1780210000},
478+
],
479+
},
480+
)
481+
monkeypatch.setattr(_core, "futu_stock_feed", lambda *args, **kwargs: {"data": []})
482+
monkeypatch.setattr(_core, "sina_roll_news", lambda *args, **kwargs: {"source": "sina_roll", "data": []})
483+
monkeypatch.setattr(_core, "eastmoney_fast_news", lambda *args, **kwargs: {"source": "eastmoney_fast", "data": []})
484+
485+
news = _core.combined_news_search("腾讯", size=5, date_str="20260601")
486+
487+
assert [item["title"] for item in news["data"]] == ["腾讯今日新闻"]
488+
489+
490+
def test_rank_symbols_only_returns_positive_news(monkeypatch):
491+
def fake_combined(keyword, size=5, lang="zh-CN", aliases=None, date_str=None):
492+
if keyword == "AAA":
493+
return {"data": [{"title": "AAA today", "publish_time": 1780300000}], "all_count": 1, "source_counts": {"futu_news": 1}}
494+
return {"data": [], "all_count": 0, "source_counts": {}}
495+
496+
monkeypatch.setattr(_core, "combined_news_search", fake_combined)
497+
498+
ranked, heat = _core.rank_symbols_by_news_heat(["AAA", "BBB"], date_str="20260601")
499+
500+
assert ranked == ["AAA"]
501+
assert heat["BBB"]["score"] == 0
502+
503+
454504
def test_combined_news_preserves_per_item_source_and_url(monkeypatch):
455505
monkeypatch.setattr(
456506
_core,
@@ -497,8 +547,16 @@ def test_print_news_shows_source_and_link_status(capsys):
497547
assert "来源: 新浪财经 | 链接: 暂无公开链接" in output
498548

499549

550+
def test_print_news_empty_is_clear(capsys):
551+
_core.print_futu_news({"source": "none", "data": []}, "A股市场")
552+
553+
output = capsys.readouterr().out
554+
assert "目前暂未获取到有效新闻信息" in output
555+
556+
500557
def test_session_stage_labels_date_mismatch_as_after_hours():
501558
assert _core.session_stage_label(data_date="2026-05-29", requested_date="20260601") == "交易日盘后"
559+
assert _core.dated_stage_label(data_date="2026-05-29", requested_date="20260601") == "2026-05-29 交易日盘后"
502560

503561

504562
def test_report_footer_is_user_friendly(capsys):

0 commit comments

Comments
 (0)