Education· Last updated April 6, 2026

Build a Natural Gas Price Monitoring Dashboard with Python and the Energy Volatility API

Learn how to build a real-time natural gas price monitoring dashboard using the Energy Volatility API. Track Henry Hub spot prices, volatility indices, and storage data.

Build a Natural Gas Price Monitoring Dashboard with Python and the Energy Volatility API

Natural gas markets are among the most volatile commodity markets in the world. Henry Hub spot prices can swing 20–30% in a single week on weather forecasts, storage inventory reports, or LNG export disruptions. For energy procurement teams, utilities, and industrial consumers, missing these moves is expensive.

A real-time natural gas monitoring dashboard gives you the edge: continuous price tracking, volatility signals, storage trend analysis, and automated alerts when conditions cross your risk thresholds.

This guide shows you how to build one using the Energy Volatility API and Python.

What the Dashboard Tracks

A production-grade natural gas monitoring dashboard covers four data layers:

  1. Spot prices — Henry Hub cash price, day-ahead forward, and regional basis differentials
  2. Volatility indices — 30-day rolling volatility, annualized implied volatility, ATR (Average True Range)
  3. Storage data — EIA weekly injection/withdrawal vs. five-year average
  4. Seasonal spread — Winter vs. summer price differential to track injection season dynamics

Step 1: Environment Setup

pip install requests pandas plotly dash python-dotenv
import os
import requests
import pandas as pd
from datetime import datetime, timedelta
from dotenv import load_dotenv
 
load_dotenv()
 
ENERGY_API_KEY = os.getenv("YOUR_API_KEY")
ENERGY_API_BASE = "https://apivult.com/api/energy-volatility"
 
HEADERS = {
    "X-RapidAPI-Key": ENERGY_API_KEY,
    "Content-Type": "application/json"
}

Step 2: Fetch Real-Time Henry Hub Data

def get_henry_hub_snapshot() -> dict:
    """Fetch current Henry Hub spot price and volatility metrics."""
    resp = requests.get(
        f"{ENERGY_API_BASE}/spot",
        params={
            "commodity": "natural_gas",
            "hub": "henry_hub",
            "metrics": "price,volatility_30d,atr_14d,change_pct"
        },
        headers=HEADERS
    )
    resp.raise_for_status()
    return resp.json()["data"]
 
 
def get_price_history(days: int = 90) -> pd.DataFrame:
    """Fetch historical price series for trend analysis."""
    end_date = datetime.utcnow().date()
    start_date = end_date - timedelta(days=days)
 
    resp = requests.get(
        f"{ENERGY_API_BASE}/history",
        params={
            "commodity": "natural_gas",
            "hub": "henry_hub",
            "start": start_date.isoformat(),
            "end": end_date.isoformat(),
            "interval": "daily"
        },
        headers=HEADERS
    )
    resp.raise_for_status()
    records = resp.json()["data"]["prices"]
    df = pd.DataFrame(records)
    df["date"] = pd.to_datetime(df["date"])
    df.set_index("date", inplace=True)
    return df

Step 3: Pull EIA Storage Data

Weekly EIA storage reports are among the most market-moving data points for natural gas. The Energy Volatility API aggregates these into a clean, structured feed.

def get_storage_data(weeks: int = 52) -> pd.DataFrame:
    """Fetch EIA natural gas storage injection/withdrawal history."""
    resp = requests.get(
        f"{ENERGY_API_BASE}/storage",
        params={
            "commodity": "natural_gas",
            "region": "lower_48",
            "weeks": weeks
        },
        headers=HEADERS
    )
    resp.raise_for_status()
    records = resp.json()["data"]["storage"]
    df = pd.DataFrame(records)
    df["report_date"] = pd.to_datetime(df["report_date"])
    df.set_index("report_date", inplace=True)
    return df
 
 
def compute_storage_deficit(storage_df: pd.DataFrame) -> pd.DataFrame:
    """Calculate storage vs. five-year average deficit."""
    storage_df["deficit_bcf"] = (
        storage_df["five_year_avg_bcf"] - storage_df["current_inventory_bcf"]
    )
    storage_df["deficit_pct"] = (
        storage_df["deficit_bcf"] / storage_df["five_year_avg_bcf"] * 100
    )
    return storage_df

Step 4: Calculate Volatility Signals

def compute_volatility_signals(price_df: pd.DataFrame) -> pd.DataFrame:
    """Add rolling volatility and signal columns to price history."""
    df = price_df.copy()
 
    # Log returns
    df["log_return"] = (df["close_price"] / df["close_price"].shift(1)).apply(
        lambda x: pd.NA if pd.isna(x) else __import__("math").log(x)
    )
 
    # 30-day rolling annualized volatility
    df["vol_30d"] = df["log_return"].rolling(30).std() * (252 ** 0.5) * 100
 
    # 7-day simple moving average
    df["sma_7d"] = df["close_price"].rolling(7).mean()
 
    # Bollinger Bands (20-day, 2 std)
    rolling_20 = df["close_price"].rolling(20)
    df["bb_upper"] = rolling_20.mean() + (rolling_20.std() * 2)
    df["bb_lower"] = rolling_20.mean() - (rolling_20.std() * 2)
 
    # Price vs. Bollinger Band position
    df["bb_position"] = (df["close_price"] - df["bb_lower"]) / (
        df["bb_upper"] - df["bb_lower"]
    )
 
    return df

Step 5: Set Up Threshold Alerts

def evaluate_alerts(snapshot: dict, price_df: pd.DataFrame) -> list[dict]:
    """Check current conditions against user-defined thresholds."""
    alerts = []
    current_price = snapshot["price_usd_mmbtu"]
    current_vol = snapshot["volatility_30d_pct"]
 
    # Price spike alert
    if snapshot["change_pct_24h"] > 5.0:
        alerts.append({
            "severity": "HIGH",
            "type": "PRICE_SPIKE",
            "message": f"Henry Hub up {snapshot['change_pct_24h']:.1f}% in 24h — "
                       f"current: ${current_price:.3f}/MMBtu"
        })
 
    # Volatility regime alert
    if current_vol > 60.0:
        alerts.append({
            "severity": "HIGH",
            "type": "VOLATILITY_ELEVATED",
            "message": f"30-day volatility at {current_vol:.1f}% — "
                       "hedging review recommended"
        })
 
    # Storage deficit alert
    latest_row = price_df.iloc[-1]
    if latest_row.get("deficit_pct", 0) > 10:
        alerts.append({
            "severity": "MEDIUM",
            "type": "STORAGE_DEFICIT",
            "message": f"Storage {latest_row['deficit_pct']:.1f}% below 5yr avg — "
                       "bullish price pressure likely"
        })
 
    return alerts

Step 6: Build the Plotly Dash Dashboard

import plotly.graph_objects as go
from plotly.subplots import make_subplots
import dash
from dash import dcc, html, callback, Output, Input
 
 
def build_price_chart(price_df: pd.DataFrame) -> go.Figure:
    """Build a candlestick + volatility subplot."""
    fig = make_subplots(
        rows=2, cols=1,
        row_heights=[0.7, 0.3],
        shared_xaxes=True,
        subplot_titles=("Henry Hub Natural Gas ($/MMBtu)", "30-Day Volatility (%)")
    )
 
    # Candlestick
    fig.add_trace(go.Scatter(
        x=price_df.index,
        y=price_df["close_price"],
        name="Price",
        line=dict(color="#3b82f6", width=2)
    ), row=1, col=1)
 
    # Bollinger Bands
    fig.add_trace(go.Scatter(
        x=price_df.index,
        y=price_df["bb_upper"],
        name="BB Upper",
        line=dict(color="#94a3b8", dash="dash", width=1),
        showlegend=False
    ), row=1, col=1)
 
    fig.add_trace(go.Scatter(
        x=price_df.index,
        y=price_df["bb_lower"],
        name="BB Lower",
        line=dict(color="#94a3b8", dash="dash", width=1),
        fill="tonexty",
        fillcolor="rgba(148,163,184,0.1)",
        showlegend=False
    ), row=1, col=1)
 
    # Volatility
    fig.add_trace(go.Scatter(
        x=price_df.index,
        y=price_df["vol_30d"],
        name="Volatility",
        line=dict(color="#f97316", width=2),
        fill="tozeroy",
        fillcolor="rgba(249,115,22,0.1)"
    ), row=2, col=1)
 
    fig.add_hline(y=60, line_dash="dot", line_color="red",
                  annotation_text="High Vol Threshold", row=2, col=1)
 
    fig.update_layout(
        height=600,
        template="plotly_dark",
        title="Natural Gas Price & Volatility Monitor",
        hovermode="x unified"
    )
    return fig
 
 
# Dash app
app = dash.Dash(__name__)
 
app.layout = html.Div([
    html.H1("Natural Gas Market Dashboard", style={"fontFamily": "sans-serif"}),
    html.Div(id="alert-panel"),
    dcc.Graph(id="price-chart"),
    dcc.Graph(id="storage-chart"),
    dcc.Interval(id="refresh", interval=5 * 60 * 1000, n_intervals=0)  # 5-min refresh
])
 
 
@callback(
    [Output("price-chart", "figure"),
     Output("alert-panel", "children")],
    Input("refresh", "n_intervals")
)
def update_dashboard(n):
    snapshot = get_henry_hub_snapshot()
    price_df = compute_volatility_signals(get_price_history(90))
    alerts = evaluate_alerts(snapshot, price_df)
 
    alert_divs = [
        html.Div(
            f"⚠ [{a['severity']}] {a['message']}",
            style={"color": "red" if a["severity"] == "HIGH" else "orange",
                   "fontFamily": "monospace", "padding": "4px"}
        )
        for a in alerts
    ]
 
    return build_price_chart(price_df), alert_divs
 
 
if __name__ == "__main__":
    app.run(debug=True, port=8050)

Running the Dashboard

python dashboard.py
# Open http://localhost:8050 in your browser

The dashboard auto-refreshes every 5 minutes. For production, deploy it behind nginx with gunicorn:

gunicorn dashboard:server -b 0.0.0.0:8050 --workers 2

What You Get

SignalUse Case
Real-time spot priceTrigger procurement decisions at target levels
30-day rolling volatilitySize hedges proportionally to market risk
Bollinger Band positionIdentify mean-reversion entry points
Storage deficit alertsAnticipate winter price spikes early
24h price change alertsReact to EIA reports and weather events

Expected Business Impact

Natural gas buyers who monitor volatility in real time typically:

  • Reduce hedge timing costs by 12–18% by avoiding peak volatility windows
  • Improve budget accuracy for energy-intensive operations
  • Catch injection season trends 4–6 weeks earlier than monthly reporting cycles

Start monitoring natural gas markets in real time. Get your Energy Volatility API key and deploy this dashboard today.