NEM Market Briefs
Overview
Section titled “Overview”The NEM Daily Brief is an AI-generated market summary produced automatically every morning at 05:30 AEST by Claude Sonnet 4.5 via Databricks FMAPI. The brief provides a structured narrative covering the previous day’s market conditions, key price events, generation highlights, and a forward outlook based on the weather forecast.
Brief Structure
Section titled “Brief Structure”Each Daily Brief follows a consistent structure optimised for market participants:
# NEM Daily Market Brief — [Date]Generated: [timestamp]
## Overnight Price Summary[Regional price performance, direction, and notable movements]
## Generation Highlights[Key fuel-type developments: wind ramp events, solar contributions,coal unit outages, BESS activity]
## FCAS Market[Ancillary service price conditions, contingency events]
## Price Event Analysis[Deep-dive on any spikes, troughs, or sustained high-price periods]
## Gas Market Context[STTM prices and any gas-electricity nexus developments]
## Forward Outlook[Weather forecast and implied demand/price risk for next 24-48 hours]Generation Pipeline
Section titled “Generation Pipeline”The Market Brief is generated by pipelines/06_market_summary.py, which runs as a scheduled serverless job:
# 06_market_summary.py — simplified excerptimport anthropic
SYSTEM_PROMPT = """You are an expert Australian energy market analyst. Generate a concise dailymarket brief for NEM participants.
IMPORTANT: Do not provide trading advice or investment recommendations.Focus on factual market observations and data-driven analysis."""
def generate_daily_brief(market_data: dict) -> str: client = anthropic.Anthropic( base_url=f"{DATABRICKS_HOST}/serving-endpoints", api_key=DATABRICKS_TOKEN, )
response = client.messages.create( model="databricks-claude-sonnet-4-5", max_tokens=2048, system=SYSTEM_PROMPT, messages=[{ "role": "user", "content": f"Generate a daily NEM market brief for {market_data['date']}. Data: {json.dumps(market_data)}" }] )
return response.content[0].textThe pipeline fetches the previous day’s market data from Gold tables, constructs a structured data payload, and passes it to Claude for narrative generation.
Anchor Time Logic
Section titled “Anchor Time Logic”The brief uses an “anchor timestamp” to define which data window to summarise. This ensures consistent coverage even if the pipeline runs slightly late (network delays, warehouse cold start):
# Anchor timestamp calculationfrom datetime import datetime, timedeltaimport pytz
AEST = pytz.timezone('Australia/Sydney')
def get_brief_window(): """ Returns (start_dt, end_dt) for the brief data window. Window: previous day 04:00 AEST → current day 04:00 AEST Anchor: current day 05:30 AEST (scheduled job time) """ now_aest = datetime.now(AEST) anchor_today_0400 = now_aest.replace(hour=4, minute=0, second=0, microsecond=0) anchor_yesterday_0400 = anchor_today_0400 - timedelta(days=1) return anchor_yesterday_0400, anchor_today_0400The 04:00 AEST window boundary is chosen because:
- AEMO’s settlement process calculates 30-minute prices with a 30-minute delay
- By 05:30 AEST, all data from the previous day’s 04:00 window is fully settled
- The window captures a full NEM trading day including peak morning demand
Brief Storage and Retrieval
Section titled “Brief Storage and Retrieval”Generated briefs are stored in:
gold.market_briefs— Delta table with brief text, metadata, and quality scoregold.daily_market_summary— structured summary for API consumption
-- Retrieve the latest market briefSELECT brief_date, generated_at, brief_text, word_count, quality_scoreFROM energy_copilot.gold.market_briefsORDER BY brief_date DESCLIMIT 1;Quality Score
Section titled “Quality Score”Each brief is automatically scored on four dimensions:
- Completeness (0–25): Did the brief cover all required sections?
- Data accuracy (0–25): Do the numbers mentioned match the source data?
- Brevity (0–25): Is the brief within the target word count (400–800 words)?
- Tone compliance (0–25): Did the brief avoid trading advice language?
Briefs scoring below 70/100 are flagged for review and a fallback template brief is served instead.
API Endpoints
Section titled “API Endpoints”# Latest market briefGET /api/market-brief/latest
# Brief for specific dateGET /api/market-brief/2025-03-21
# Brief history (last 30 days)GET /api/market-brief/history?days=30
# Response structure:{ "brief_date": "2025-03-21", "generated_at": "2025-03-21T05:32:14+10:00", "brief_text": "## NEM Daily Market Brief — 21 March 2025\n...", "word_count": 547, "quality_score": 88}Dashboard Page
Section titled “Dashboard Page”The Market Briefs dashboard (/front-office/market-briefs) shows:
- Today’s brief as a formatted card with expandable sections
- Brief history — last 30 days of briefs in a scrollable timeline
- Key statistics extracted from each brief: max price, min price, dominant fuel, notable events
- Brief search — full-text search across historical briefs