mirror of
https://github.com/TauricResearch/TradingAgents.git
synced 2026-09-19 11:15:24 +03:00
fix(dataflows): trim global news to the window before the limit (#1356)
- out-of-window articles no longer spend the article budget or cut the remaining searches short
This commit is contained in:
@@ -213,3 +213,47 @@ def test_coverage_gap_future_window_is_unavailable():
|
|||||||
today = datetime.now(timezone.utc).date()
|
today = datetime.now(timezone.utc).date()
|
||||||
out = coverage_gap([], str(today), str(today + timedelta(days=3)), "Feed", "items")
|
out = coverage_gap([], str(today), str(today + timedelta(days=3)), "Feed", "items")
|
||||||
assert out is not None and "past today" in out
|
assert out is not None and "past today" in out
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_out_of_window_articles_do_not_consume_the_article_budget(monkeypatch):
|
||||||
|
"""The limit counts articles the run may see, not candidates fetched (#1356).
|
||||||
|
|
||||||
|
Out-of-window items were counted first, so they filled the budget, stopped
|
||||||
|
the remaining searches, and the in-window news was reported as absent.
|
||||||
|
"""
|
||||||
|
stale = [{"title": f"OLD {i}", "publisher": "P", "link": "l",
|
||||||
|
"providerPublishTime": _epoch("2025-01-01")} for i in range(2)]
|
||||||
|
wanted = {"title": "IN WINDOW", "publisher": "P", "link": "l",
|
||||||
|
"providerPublishTime": _epoch("2025-05-08")}
|
||||||
|
pages = [stale, [wanted]]
|
||||||
|
|
||||||
|
class FakeSearch:
|
||||||
|
def __init__(self, *a, **k):
|
||||||
|
self.news = pages.pop(0) if pages else []
|
||||||
|
|
||||||
|
monkeypatch.setattr(ynews.yf, "Search", FakeSearch)
|
||||||
|
monkeypatch.setattr(ynews, "get_config", lambda: {
|
||||||
|
"global_news_lookback_days": 7, "global_news_article_limit": 2,
|
||||||
|
"global_news_queries": ["markets", "economy"],
|
||||||
|
})
|
||||||
|
|
||||||
|
out = ynews.get_global_news_yfinance("2025-05-09")
|
||||||
|
|
||||||
|
assert "IN WINDOW" in out
|
||||||
|
assert "OLD 0" not in out
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_the_article_limit_still_caps_what_is_returned(monkeypatch):
|
||||||
|
articles = [{"title": f"NEWS {i}", "publisher": "P", "link": "l",
|
||||||
|
"providerPublishTime": _epoch("2025-05-08")} for i in range(5)]
|
||||||
|
|
||||||
|
class FakeSearch:
|
||||||
|
def __init__(self, *a, **k):
|
||||||
|
self.news = articles
|
||||||
|
|
||||||
|
monkeypatch.setattr(ynews.yf, "Search", FakeSearch)
|
||||||
|
out = ynews.get_global_news_yfinance("2025-05-09", look_back_days=7, limit=3)
|
||||||
|
|
||||||
|
assert out.count("### ") == 3
|
||||||
|
|||||||
@@ -146,7 +146,11 @@ def get_global_news_yfinance(
|
|||||||
limit = config["global_news_article_limit"]
|
limit = config["global_news_article_limit"]
|
||||||
search_queries = config["global_news_queries"]
|
search_queries = config["global_news_queries"]
|
||||||
|
|
||||||
all_news = []
|
curr_dt = datetime.strptime(curr_date, "%Y-%m-%d")
|
||||||
|
start_dt = curr_dt - relativedelta(days=look_back_days)
|
||||||
|
start_date = start_dt.strftime("%Y-%m-%d")
|
||||||
|
|
||||||
|
in_window_news = []
|
||||||
seen_titles = set()
|
seen_titles = set()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -157,47 +161,33 @@ def get_global_news_yfinance(
|
|||||||
enable_fuzzy_query=True,
|
enable_fuzzy_query=True,
|
||||||
))
|
))
|
||||||
|
|
||||||
if search.news:
|
for article in search.news or []:
|
||||||
for article in search.news:
|
# Window first: the limit counts what the run may read, so an
|
||||||
# Handle both flat and nested structures
|
# out-of-window article must not spend the budget or cut the
|
||||||
if "content" in article:
|
# remaining searches short (#1356). Flat articles are filtered
|
||||||
data = _extract_article_data(article)
|
# on the same rule, so none can leak future news (#1007).
|
||||||
title = data["title"]
|
|
||||||
else:
|
|
||||||
title = article.get("title", "")
|
|
||||||
|
|
||||||
# Deduplicate by title
|
|
||||||
if title and title not in seen_titles:
|
|
||||||
seen_titles.add(title)
|
|
||||||
all_news.append(article)
|
|
||||||
|
|
||||||
if len(all_news) >= limit:
|
|
||||||
break
|
|
||||||
|
|
||||||
# Calculate date range
|
|
||||||
curr_dt = datetime.strptime(curr_date, "%Y-%m-%d")
|
|
||||||
start_dt = curr_dt - relativedelta(days=look_back_days)
|
|
||||||
start_date = start_dt.strftime("%Y-%m-%d")
|
|
||||||
|
|
||||||
news_str = ""
|
|
||||||
kept = 0
|
|
||||||
for article in all_news[:limit]:
|
|
||||||
# Extract uniformly (flat + nested) and apply the same look-ahead-safe
|
|
||||||
# window filter, so flat articles can't leak future news (#1007).
|
|
||||||
data = _extract_article_data(article)
|
data = _extract_article_data(article)
|
||||||
if not in_window(data["pub_date"], start_dt, curr_dt):
|
if not in_window(data["pub_date"], start_dt, curr_dt):
|
||||||
continue
|
continue
|
||||||
|
if data["title"] and data["title"] not in seen_titles:
|
||||||
|
seen_titles.add(data["title"])
|
||||||
|
in_window_news.append(data)
|
||||||
|
|
||||||
|
if len(in_window_news) >= limit:
|
||||||
|
break
|
||||||
|
|
||||||
|
news_str = ""
|
||||||
|
for data in in_window_news[:limit]:
|
||||||
news_str += f"### {data['title']} (source: {data['publisher']})\n"
|
news_str += f"### {data['title']} (source: {data['publisher']})\n"
|
||||||
if data["summary"]:
|
if data["summary"]:
|
||||||
news_str += f"{data['summary']}\n"
|
news_str += f"{data['summary']}\n"
|
||||||
if data["link"]:
|
if data["link"]:
|
||||||
news_str += f"Link: {data['link']}\n"
|
news_str += f"Link: {data['link']}\n"
|
||||||
news_str += "\n"
|
news_str += "\n"
|
||||||
kept += 1
|
|
||||||
|
|
||||||
# All candidates fell outside the window -> say so rather than return an
|
# Nothing fell inside the window -> say so rather than return an
|
||||||
# empty-bodied report (#993).
|
# empty-bodied report (#993).
|
||||||
if kept == 0:
|
if not news_str:
|
||||||
# Results merge several fuzzy searches, so their timestamps prove no
|
# Results merge several fuzzy searches, so their timestamps prove no
|
||||||
# continuous coverage; judge the window against the present only.
|
# continuous coverage; judge the window against the present only.
|
||||||
gap = coverage_gap((), start_date, curr_date, "Yahoo Finance global news", "market news")
|
gap = coverage_gap((), start_date, curr_date, "Yahoo Finance global news", "market news")
|
||||||
|
|||||||
Reference in New Issue
Block a user