- Go 71.9%
- HTML 28.1%
| data | ||
| examples | ||
| static | ||
| .gitignore | ||
| CLAUDE.md | ||
| composite.go | ||
| db.go | ||
| go.mod | ||
| ingest.go | ||
| LICENSE | ||
| main.go | ||
| README.md | ||
| serve.go | ||
| station.go | ||
| TODO.md | ||
| volatility.go | ||
weather-vix
Notice: This is a fun project that might be informative for retrospect, but I would guess not great for forecasting. Further, it likely has zero business or investment significance.
Overview
Roald suggested that weather fluctuations have become more common lately. Earlier hot days and later cold days. Sudden changes within days of each other.
To deduce such volatility would need a mechanic of calculating this fluctuation — similar to the stock market ^VIX Volatility Index.
Approach
Weather stations open and close over the decades, each at a slightly different location and elevation. Naively comparing measurements across time mixes stations, introducing geographic noise. This tool addresses that by:
- Finding all NOAA GHCN-D stations within a radius of a target lat/long (defaults to zip 27560, Raleigh NC, 50 km)
- Storing all historical daily observations (TMAX, TMIN, PRCP, SNOW, SNWD) in a local DuckDB database
- Computing a spatially-weighted composite temperature series — each station's daily reading weighted by 1/distance² to the target — so the series is stable even as stations appear and disappear
- Computing a rolling standard deviation of day-over-day TMAX changes (30/90/365-day windows), annualized
Current results: 343 stations, 3M+ observations from 1891 to present, producing ~49k composite dates and a full-length volatility index.
Usage
go build ./...
# Download NOAA data for all stations within 50 km of 27560 (default target; cached in data/cache/)
./weather-vix ingest
# Compute spatially-weighted composite TMAX/TMIN series
./weather-vix composite
# Compute rolling volatility index (stores 30/90/365-day windows)
./weather-vix vix
# Query results directly
duckdb weather.db "SELECT date, value FROM volatility WHERE window_days=30 ORDER BY date DESC LIMIT 10;"
# Export to CSV for plotting
duckdb weather.db "COPY volatility TO 'volatility.csv' (HEADER, DELIMITER ',');"
# Or just look at it: local dashboard with composite temps + volatility overlay
./weather-vix serve
# → open http://localhost:8080
Graphs
In the local dashboard the current views are:
Composite Temperature (°C)
This view has tight cycles becuase it includes each year's full seasons.
Volatility Index (annualized σ of day-over-day ΔTMAX)
You can see the general trend of the volatility index of the cycles does not particularly trend over the 100+ years.
Same Calendar Date, Year Over Year
To normalize the seasonal frequency, this view let's you choose a particular date of the year, and plot the volatility of that date over the timeline. Say whether December 1st (+/- 1 day) has become more volatile over the years.
Compare Years (seasonal overlay)
This view normalizes to see seasonal volatility over a sample of the timeline, all overlayed to a 12 month period. So if the color of the newer years floats to the top as generally being more volatile weather changes than previous years.
Running this for your own location
The whole pipeline targets one point in space. To swap it for anywhere else:
-
Get lat/long for your area. NOAA's own GHCN-D station list has no zip field — only lat/long — so that's the real search key here. A zip code is just a convenience other tools geocode to lat/long before ever talking to NOAA. Look yours up with any map (e.g. right-click a spot on Google Maps and copy the coordinates, or search " lat long").
-
Run the pipeline with
--lat/--long/--radius-km, using a dedicated--dbfor that location (composite/vix each work off a single DB's worth of stations, so don't mix locations into one file):./weather-vix ingest --lat 40.7128 --long -74.0060 --radius-km 50 --db nyc.db ./weather-vix composite --db nyc.db ./weather-vix vix --db nyc.db duckdb nyc.db "SELECT date, value FROM volatility WHERE window_days=30 ORDER BY date DESC LIMIT 10;" # Or view it in the dashboard instead of querying directly: ./weather-vix serve --db nyc.db --addr :8081All three flags are optional and independently overridable — omit
--radius-kmto keep the 50 km default, etc.ingestis idempotent and caches downloaded CSVs under--cache, so re-running it (e.g. to pull in fresh days) only fetches what's missing.
Research Notes
- Volatility definition: rolling std-dev of day-over-day TMAX delta, annualized (
σ * sqrt(365)) - Spatial weighting: inverse-distance-squared; stations at zero distance use an epsilon of 0.01 km²
- Station coverage: USW/USC prefix stations (ASOS/COOP network) have century-long TMAX/TMIN records; US1NC* stations (CoCoRaHS citizen science) are denser but precipitation-only and recent
Open questions / future work
- Seasonal decomposition to distinguish anomalous volatility from expected seasonal swings
- Including PRCP as an additional volatility signal
- Normalizing the VIX scale to a baseline period for interpretability
Data Sources
- NOAA Global Historical Climatology Network Daily (GHCN-D)
- Station list:
ghcnd-stations.txt/ghcnd-stations.csv - Per-station daily CSVs:
.../access/<STATION_ID>.csv
- Station list:



