Skip to main content

The Lookback Window Problem in Concept-to-Stock Mention Rankings: Getting the days Parameter Right

Updated 7 min readMarkdown

"Which stocks are carrying the discussion around this theme right now?" is one of the most common questions people ask a heat monitoring dataset. In Lava, the answer comes from the concept member ranking: GET /concepts/stocks, exposed to agents as the MCP tool get_concept_stock_heat. It returns the stocks most mentioned by institutions and influencers under a given concept, along with how many distinct sources mentioned each one.

The endpoint has one innocent-looking parameter, days, which defaults to 30 and accepts 1 to 365. Combined with a hard limit, a roughly ten-minute refresh cycle, and exact-match concept names, it is responsible for most "why don't our two queries agree?" conversations. This article explains the mechanics and the fixes. Concept names like "Concept Alpha" and all numbers are illustrative.

Where the Concept Ranking Fits

Lava exposes three read-only paths around concepts. They look similar but are parameterized differently:

MCP tool HTTP path Time parameter Rows Refresh
get_market_hot_concepts /hot/concepts window: 1h, 4h, 18h, 24h, 3d (default 24h, no 7d) default 50, max 200 about 2 min
get_concept_stock_heat /concepts/stocks days: default 30, range 1–365 default 50, max 100 about 10 min
get_stock_concept_heat /concepts/by-stock days: default 90, range 1–365 default 30, max 200 per response

concept is required on /concepts/stocks, matched exactly, and limited to 30 characters.

It Is a Mention Ranking, Not an Index Constituent List

The most frequent misunderstanding is treating the output as an official membership list. It is a ranking of stocks that were mentioned in connection with the concept during the lookback. A stock commonly classified under the theme but not discussed in the window simply will not appear.

Name the column accordingly in your research tables, for example concept_mention_members_d30, and source official constituents from your own reference data.

How days Shapes the Answer

Unstated lookbacks create phantom disagreements

One analyst says the top name under Concept Alpha is A; another gets B. Usually one used the default days=30 and the other passed days=7. A single burst early in the month can dominate the 30-day ranking while the 7-day ranking reflects this week. Always cite the lookback: "Concept Alpha, days=7, rank 1".

Long lookbacks freeze old events at the top

At days=90 or longer, the leader can stay unchanged for weeks. A stock that collected 300 mentions from one event sixty days ago and single digits since can still rank first. Long lookbacks answer "who has carried this theme over time"; short ones answer "who is carrying it lately". To see change, read several lookbacks together.

Short lookbacks are thin

At days=1, the ranking reshuffles on every refresh because positions 5 and 30 may differ by one or two mentions. Read only the top few and always look at the counts. For intraday structure on a specific name, /heat/windows gives six rolling windows plus hourly_24h.

days is not window

Comparing /hot/concepts?window=3d with /concepts/stocks?days=3 produces numbers that do not reconcile, because one is a rolling hourly window over the whole concept and the other is a day-based lookback per member stock. Reconcile within one endpoint; across endpoints compare direction and coverage only.

Operational Limits That Bite

Row cap. limit defaults to 50 and tops out at 100, with no documented offset parameter. If your definition is "every mentioned member", label the table "top 100" instead. For very broad themes, shorten days or work from the stock side with /concepts/by-stock.

Refresh cadence. The ranking updates about every ten minutes. Polling every minute mostly returns duplicates while each successful call is still billed. Poll at ten-minute intervals or slower and hash the payload, storing a snapshot only when the content changes. Four hours at one-minute intervals is 240 calls; at ten minutes it is 24.

Exact names. "Concept Alpha" may resolve while "Concept Alpha theme" or a variant with different spacing or full-width characters returns nothing. Copy canonical names from /hot/concepts or /concepts/by-stock responses and keep a local dictionary. Record an empty result as "no match for these parameters" rather than retrying with near-synonyms.

Moving snapshots. Two days=30 pulls taken in the morning and afternoon are not the same cross-section: the window slid and the data refreshed in between. Save meta.server_time and meta.request_id with every snapshot and fix a daily capture time if you track membership changes.

Do Not Backtest on a Lookback

The endpoint has no as_of_date; the lookback always ends at request time. A days=30 ranking pulled today includes discussion that happened after the date you might want to study. For history:

  • /panel/daily (get_stock_heat_panel) with type=stock and up to 20 codes, or type=concept with names, for calendar-day series of up to 92 days;
  • /hot/weighted with as_of_date (messages through 15:00 that day) or /agencies/pit for point-in-time, score-weighted cross-sections.

A Three-Rung Lookback Ladder

The practical fix for most of the above is to pull several lookbacks at once and compare membership:

# concept_days_ladder.py - illustrative; base URL and token are placeholders
import hashlib
import json
import os
import requests

BASE = os.environ.get("LAVA_BASE", "<YOUR_LAVA_BASE>/subscribe/v1")
HEADERS = {"Authorization": "Bearer " + os.environ.get("LAVA_API_KEY", "<YOUR_TOKEN>")}
CONCEPT = "Concept Alpha"   # copy the canonical name from /hot/concepts

def members(days):
    resp = requests.get(
        BASE + "/concepts/stocks",
        params={"concept": CONCEPT, "days": days, "limit": 100},
        headers=HEADERS, timeout=60,
    ).json()
    if not resp.get("ok"):
        return None
    data = resp["data"]
    rows = data if isinstance(data, list) else data.get("list", [])
    digest = hashlib.sha256(json.dumps(rows, sort_keys=True).encode()).hexdigest()[:12]
    # Identifier column names should be checked against the OpenAPI schema.
    codes = {r.get("ts_code") or r.get("stock_name") for r in rows}
    return {"codes": codes, "server_time": resp["meta"]["server_time"], "hash": digest}

ladder = {d: members(d) for d in (7, 30, 90)}
s7, s30, s90 = (ladder[d]["codes"] if ladder[d] else set() for d in (7, 30, 90))
print("in all three:", len(s7 & s30 & s90))
print("7-day only:", len(s7 - s30 - s90))
print("90-day only:", len(s90 - s30 - s7))

Read the output as structure, not as a signal. Names present in all three rungs are the long-term carriers of the theme. Names that appear only at seven days were pulled into the conversation recently. Names only at ninety days are fading from it.

Cross-Checking from the Stock Side

Concept-to-stock and stock-to-concept are two directions of one relationship, but their defaults differ: 30 days versus 90. If you do not align them, you get the false contradiction "the stock ranks high under the concept, but the concept ranks low for the stock".

A clean cross-check: pull the top ten members with days=30, call /concepts/by-stock for each with an explicit days=30, and note where Concept Alpha ranks among each stock's concepts. A low position means that stock's discussion is mostly carried by other themes. That is eleven calls in total, so it is cheap to schedule daily.

FAQ

Does /concepts/stocks accept min_score? Check the OpenAPI schema. For a high-score source view, apply min_score on /hot/concepts or inspect members individually through /heat/windows.

/hot/concepts has no 7d. How do I get a weekly view? Use /concepts/stocks with days=7, or /panel/daily with type=concept for a daily series. Do not pass unlisted window values.

Is days=365 useful? As background, yes: it shows which names have carried a theme over a year. It barely reflects recent change and the 100-row cap trims the tail, so read it alongside the 7- and 30-day rungs.

Summary

get_concept_stock_heat is a mention ranking whose meaning depends almost entirely on days. Remember the defaults (30, range 1–365), the 100-row cap, the ten-minute refresh, exact concept names, and the missing as_of_date. Pull a 7/30/90 ladder, store parameters and server time with every snapshot, and most inconsistencies disappear.

To explore concept members interactively before writing code, open the concept stocks tool.


中文简介:熔岩概念成分提及排行(get_concept_stock_heat)的读数高度依赖 days 回看参数,长回看会把旧事件「冻」在榜首,短回看样本又太薄。本文梳理 limit 上限、10 分钟更新、概念名精确匹配与不可回测等坑,并给出 7/30/90 三档并列的 Python 示例。熔岩是量化交易必备的机构/大V 热度监测工具。

No comments yet. Be the first to comment.