Back to Playbooks
Quant Traderintermediate
weathercommoditiesearningsalternative datacross-source

I Connected Weather Forecasts to Earnings Calls and Found Signals Nobody Else Was Watching

Trawl Team·

Weather Creates Earnings Surprises the Market Is Slow to Price

Every hurricane season, the same thing happens: a storm hits the Gulf Coast, refineries shut down, and energy stocks move — but only after the quarterly earnings miss. Analysts rarely update models mid-quarter for weather events, even when crop damage, refinery shutdowns, or insurance claims are already visible in real-time NWS data.

I built a pipeline that connects live weather alerts to the stocks most exposed to them, scores the exposure, and cross-references with earnings calls, news, and SEC filings to identify tradeable signals before the market catches up.

The Thesis

Weather impacts at least four major equity sectors, and the market systematically underreacts to it:

SectorMechanismExample
AgricultureCrop yields, equipment demandDrought in Iowa crushes Deere guidance
EnergyRefinery shutdowns, demand spikesGulf hurricane takes 20% of refining offline
RetailFoot traffic, seasonal demandBlizzard kills Q4 same-store sales
InsuranceCatastrophe losses, reserve chargesHurricane season drives Allstate earnings miss

The information advantage is simple: NWS alerts are free, real-time, and public. Earnings revisions are slow. The gap between "storm hits" and "analyst downgrades" is where the alpha lives.

Step 1: Pull Severe Weather Alerts

Start with what's happening right now. NWS alerts include severity, affected areas, and event type.

Fetch severe weather alerts
curl "https://api.gettrawl.com/api/weather/alerts?severity=Severe"

Step 2: Check the Forecast

For specific weather-sensitive locations, pull the 7-day forecast. This example uses coordinates for the Iowa farm belt.

Fetch weather forecast
curl "https://api.gettrawl.com/api/weather/forecast?lat=41.88&lon=-93.10"

Step 3: Cross-Reference Earnings

Has Deere mentioned weather on recent earnings calls? Companies that acknowledge weather risk in transcripts are historically more exposed.

Search earnings calls
curl "https://api.gettrawl.com/api/earnings/search?ticker=DE"

Step 4: Check News Signals

Is there already weather-related coverage for the affected stocks?

Search weather-related news
curl "https://api.gettrawl.com/api/news/search?q=Deere+weather+drought"

The Sector-to-Ticker Mapping

The pipeline tracks 25+ weather-sensitive tickers across five sectors, each with HQ coordinates for localized weather lookups:

Agriculture: DE (Deere), ADM (Archer-Daniels-Midland), BG (Bunge), CORN (Teucrium Corn Fund), MOS (Mosaic), CF (CF Industries)

Energy: XOM (Exxon), CVX (Chevron), COP (ConocoPhillips), DVN (Devon Energy), HAL (Halliburton), SLB (Schlumberger)

Utilities: NEE (NextEra), DUK (Duke Energy), SO (Southern Company), AEP (American Electric Power)

Retail: WMT (Walmart), HD (Home Depot), LOW (Lowe's)

Insurance: ALL (Allstate), TRV (Travelers), PGR (Progressive), CB (Chubb)

Alert-to-sector matching uses geographic rules: corn belt states (Iowa, Illinois, Indiana, etc.) map to agriculture; Gulf Coast states (Texas, Louisiana, Florida) map to energy; major metros map to retail. Catastrophe-class events (hurricanes, tornadoes, wildfires) always map to insurance.

The Scoring Model

Each ticker gets a deterministic exposure score (0-100) from four equal-weight components:

Alert Severity (0-25): Based on NWS severity classification. Extreme = 25, Severe = 19, Moderate = 12, Minor = 6.

Historical Sensitivity (0-25): Count of weather-related keywords (drought, hurricane, freeze, etc.) found in earnings transcripts. More mentions = the company acknowledges weather risk more frequently = more historically exposed.

News Signal (0-25): Volume of recent weather-related news articles for the ticker. More coverage = the market is starting to pay attention.

Sector Base Risk (0-25): Inherent weather sensitivity by sector. Insurance (22) and agriculture (20) score highest. Retail (10) scores lowest.

Exposure labels: CRITICAL (75+), HIGH (50+), MODERATE (30+), LOW (under 30).

The Full Pipeline

from trawl import TrawlClient

client = TrawlClient()

WEATHER_KEYWORDS = [
    "weather", "storm", "hurricane", "drought", "flood",
    "freeze", "wildfire", "tornado", "blizzard", "heat wave",
]

SECTOR_TICKERS = {
    "agriculture": ["DE", "ADM", "BG", "MOS", "CF"],
    "energy": ["XOM", "CVX", "COP", "DVN", "HAL"],
    "utilities": ["NEE", "DUK", "SO", "AEP"],
    "retail": ["WMT", "HD", "LOW"],
    "insurance": ["ALL", "TRV", "PGR", "CB"],
}

SECTOR_BASE_RISK = {
    "agriculture": 20, "energy": 18, "insurance": 22,
    "utilities": 15, "retail": 10,
}

def scan_weather_exposure():
    """Scan active weather alerts and map to exposed tickers."""

    # 1. Fetch severe weather alerts
    alerts = client.weather.alerts(severity="Severe")

    for alert in alerts.results:
        print(f"\nALERT: {alert.event} ({alert.severity})")
        print(f"  Area: {alert.area_desc}")

        # 2. Map alert to affected sectors
        affected_sectors = match_sectors(alert)

        for sector in affected_sectors:
            for ticker in SECTOR_TICKERS[sector]:
                # 3. Score exposure
                score = score_exposure(ticker, sector, alert)
                label = classify(score)
                print(f"  {ticker:5s} [{sector:12s}] Score: {score}/100 ({label})")

def score_exposure(ticker, sector, alert):
    """Compute weather exposure score for a ticker."""
    score = 0

    # Alert severity (0-25)
    severity_map = {"Extreme": 25, "Severe": 19, "Moderate": 12, "Minor": 6}
    score += severity_map.get(alert.severity, 5)

    # Earnings weather mentions (0-25)
    earnings = client.earnings.search(ticker=ticker)
    weather_mentions = 0
    for call in (earnings.results or []):
        text = (call.text or "").lower()
        weather_mentions += sum(1 for kw in WEATHER_KEYWORDS if kw in text)
    score += min(weather_mentions * 5, 25)

    # News signal (0-25)
    news = client.news.search(q=f"{ticker} weather")
    score += min(len(news.results or []) * 5, 25)

    # Sector base risk (0-25)
    score += SECTOR_BASE_RISK.get(sector, 8)

    return min(score, 100)

def match_sectors(alert):
    """Map an alert's affected area to weather-sensitive sectors."""
    area = (alert.area_desc or "").lower()
    event = (alert.event or "").lower()
    sectors = []

    ag_states = ["iowa", "illinois", "indiana", "ohio", "minnesota", "nebraska", "kansas"]
    if any(state in area for state in ag_states):
        sectors.append("agriculture")

    gulf_states = ["texas", "louisiana", "florida", "mississippi", "alabama"]
    if any(state in area for state in gulf_states):
        sectors.append("energy")

    if any(kw in event for kw in ["hurricane", "tornado", "wildfire", "flood"]):
        sectors.append("insurance")

    if any(kw in event for kw in ["hurricane", "tornado", "freeze", "heat"]):
        sectors.append("utilities")

    return sectors

def classify(score):
    if score >= 75: return "CRITICAL"
    if score >= 50: return "HIGH"
    if score >= 30: return "MODERATE"
    return "LOW"

# Run the scan
scan_weather_exposure()

Why This Matters

Weather data is one of the most underused alternative data sources in finance. NWS alerts are free, real-time, and high-quality — yet most quantitative models don't incorporate them. The lag between "severe weather event" and "analyst earnings revision" is consistently days to weeks. This pipeline closes that gap.

The approach is deliberately simple: no ML models, no proprietary weather data, no satellite imagery subscriptions. Just public NWS alerts cross-referenced with public earnings data through one API. The alpha comes from connecting data that already exists, not from better data.

Non-Code Options

Trawl's MCP server handles this naturally:

"Check current severe weather alerts and tell me which stocks in agriculture, energy, and insurance are most exposed. Cross-reference with their recent earnings calls — have any of them mentioned weather as a risk factor?"

Claude pulls the alerts, maps them to sectors, checks earnings transcripts for weather keywords, and ranks the exposure. No code required.

Source Code

The full implementation with alert-to-sector mapping, dual-mode CLI (alerts + ticker profile), and comprehensive scoring:

github.com/trawlhq/examples/tree/master/weather-alpha