Signals
Twenty-five scored dimensions per article across industry, business register and risk context. Filter on ranges, stack them, and stop trying to express "energy coverage that sounds worried" as a keyword query.
What is the difference between a signal and sentiment?
Sentiment collapses an article to a single axis, which forces unrelated things to compete. A piece about a record harvest that also warns about export tariffs averages out to roughly neutral, and the average describes nothing that is in it.
Signals keep the dimensions separate. The same article can score high on agriculture, high on trade, and high on financial uncertainty simultaneously — which is a description you can filter on and act on.
Industry
How strongly an article reads as being about a sector, rather than merely mentioning one.
| Signal | Reads as |
|---|---|
economy | General economic activity |
finance | Banking, markets, capital |
energy | Power, fuel, utilities |
agriculture | Farming, food production, commodities |
healthcare | Care delivery, providers, public health |
labor | Employment, unions, workforce |
transportation | Freight, aviation, shipping, rail |
real_estate | Property, construction, housing |
defense | Military, procurement, security industry |
science_technology | Research, computing, engineering |
trade | Imports, exports, tariffs |
environment | Ecology, pollution, conservation |
public_sector | Government administration and services |
Business language
The register a piece is written in — how markets and lawyers talk, rather than the subject.
| Signal | Reads as |
|---|---|
financial_positive | Optimistic financial framing |
financial_negative | Pessimistic financial framing |
financial_uncertainty | Hedging, unresolved outcomes |
financial_stability_stress | Systemic stress language |
legal_litigation | Proceedings, claims, regulatory action |
Risk context
Operational and systemic risk language, for monitoring rather than sentiment.
| Signal | Reads as |
|---|---|
supply_disruption | Interrupted production, logistics, shortage |
cyber_incident | Breach, intrusion, outage |
conflict | Armed conflict and hostilities |
climate | Climate and extreme weather |
health_security | Outbreaks and health emergencies |
governance_risk | Corruption, institutional failure |
anxiety | Public alarm and apprehension |
Filtering on them
Each signal is a number. Filter on a range, not a value, and stack constraints when you need more than one to hold at once.
# one signal, lower bound
GET /search?signal=cyber_incident&signal_min=0.25
# several at once — every constraint must match
GET /search?signals=energy:0.3,financial_stability_stress:0.2
# combine with any other filter
GET /stories?country=GM&signal=supply_disruption&signal_min=0.2
# the top 5% of the match set on one signal
GET /search?signal=cyber_incident&signal_percentile_min=95
A fixed signal_min admits more in a busy week than in a quiet one.
signal_percentile_min keeps a share instead: 95 keeps the articles at or
above the 95th percentile of that signal over the articles the rest of the query matched. The
response reports the cutoff it used as signal_threshold, so you can see
where the slice fell, compare it across runs or pin it as a fixed bound.
Every article response also carries its full signal map, so you can retrieve broadly and threshold client-side while you work out where the useful cut is.
Why does a signal filter return nothing at all?
Because the range excludes everything the other constraints matched, or the window is
empty. A wrong name is no longer one of the causes. The engine stores
signal_finance and accepts finance, and any other name is
refused with unknown_signal and the nearest valid name, so a leading
signal_ is an error you see rather than an empty set you misread. If a
signal filter returns zero results where you expected some, loosen the range before
changing anything else.
Choosing a threshold
There is no universal cutoff. Scores are relative to how strongly the language
appears, and the useful threshold depends on how noisy your query already is.
Retrieve a week without a signal filter, look at the distribution for the signal
you care about, and cut where the results stop being about what you meant.
GET /aggregate?interval=week&metrics=signal:cyber_incident reports the
mean and the 50th and 95th percentiles for each week without retrieving the articles.
Re-check it quarterly. Editorial vocabulary shifts, and a threshold tuned during a quiet period will admit far too much during an active one. A percentile cut moves with the volume on its own, and in exchange it returns the same share of the match set whether or not anything unusual happened.
What they are not
Signals measure language, not events. A high risk score means an article is written in the register of that risk — it does not confirm the risk is real, material, or new. Treat them as a retrieval and triage tool, not as a finding.
They are also versioned. Responses carry the version that produced them, so a series computed across a version change is comparing two different things and should be recomputed rather than concatenated.
Reference
Every parameter, what it accepts and how it behaves against the index: the signal taxonomy.