Interactive Streamlit dashboard demonstrating how improved renewable energy forecasting increases battery storage revenue in ERCOT markets. Features a modular OOP architecture with Supabase database integration for scalable historical data analysis.
-
Clone the repository
-
Install dependencies:
pip install -r requirements.txt
-
Run the dashboard:
streamlit run Home.py
If you want to use the Supabase database integration:
# Copy environment template
cp .env.example .env
# Edit .env and add your credentials:
# - Supabase URL and API key
# - ERCOT API credentials (optional)streamlit run Home.pyThe dashboard will open automatically at http://localhost:8501
The dashboard provides 7 specialized analysis pages:
- 🏠 Overview - Strategy performance comparison and key revenue metrics
- 📈 Price Analysis - Price dynamics, forecast errors, and extreme events
- 🔋 Operations - State of charge tracking and dispatch action distribution
- 💰 Revenue - Cumulative revenue tracking and pricing analysis
- 🏗️ Asset Design - Hybrid asset configuration
- 📈 Strategy Analysis - Sensitivity analysis & strategy comparison
- 📅 Timeline - Gantt-style dispatch visualization showing charge/discharge patterns
- ⚙️ Optimization - Deep-dive into strategy decision-making logic
- Settlement Point Selection: Analyze different ERCOT nodes
- Battery Specifications: Adjust capacity (10-600 MWh), power (5-300 MW), efficiency
- EIA-860 Presets: Use real Texas battery system configurations (Small/Medium/Large)
- Strategy Selection:
- Threshold-Based (percentile-driven charge/discharge)
- Rolling Window Optimization (lookahead planning)
- Forecast Improvement: Simulate 0-100% accuracy improvements
Every analysis compares:
- Baseline: Day-ahead forecast only (current capability)
- Improved: With Zentus forecast enhancement (+X%)
- Optimal: Perfect foresight (theoretical maximum revenue)
The dashboard supports two data loading modes:
- Location:
data/da_prices.csv,data/rt_prices.csv - Use case: Local development, offline analysis
- Data: Single-day snapshot (July 20, 2025)
- Nodes: 5 wind farm settlement points
- Location: Cloud PostgreSQL database
- Use case: Production, historical analysis, team collaboration
- Data: Years of ERCOT market data
- Nodes: All ERCOT settlement points
- Features: Date range selection, automatic updates, query optimization
The system automatically detects Supabase credentials and uses the database if configured, otherwise falls back to CSV.
ERCOT-Dashboard/
├── Home.py # Main Entry Point (Streamlit App)
├── pages/ # Analysis Pages
│ ├── 0_📊_Overview.py # Strategy performance comparison
│ ├── 1_🗺️_Nodal_Analysis.py # Nodal analysis
│ ├── 2_📈_Price_Analysis.py # Price dynamics and forecast errors
│ ├── 3_🔋_Operations.py # SOC and dispatch analysis
│ ├── 4_💰_Revenue.py # Revenue tracking over time
│ ├── 5_🏗️_Asset_Design.py # Hybrid asset design
│ ├── 6_📈_Strategy_Analysis.py # Sensitivity analysis & strategy comparison
│ ├── 7_📅_Timeline.py # Gantt-style dispatch visualization
│ └── 8_⚙️_Optimization.py # Strategy deep-dive
│
├── core/ # Business logic (OOP design)
│ ├── battery/
│ │ ├── battery.py # BatterySpecs & Battery classes
│ │ ├── simulator.py # BatterySimulator orchestration
│ │ └── strategies.py # DispatchStrategy implementations
│ ├── data/
│ │ └── loaders.py # DataLoader & SupabaseDataLoader
│ └── analytics/ # Analysis utilities
│
├── ui/ # User interface components
│ ├── components/
│ │ ├── sidebar.py # Shared configuration sidebar
│ │ └── header.py # Page header with branding
│ └── styles/
│ └── custom_css.py # Custom CSS styling
│
├── config/ # Configuration
│ ├── settings.py # Constants, colors, database config
│ └── page_config.py # Streamlit page settings
│
├── utils/ # Utilities
│ └── state.py # Session state management (AppState)
│
├── scripts/ # Database & data management
│ ├── setup_supabase_schema.sql # Database DDL (tables, indexes, views)
│ ├── create_supabase_tables.py # Schema setup helper
│ ├── fetch_ercot_data.py # Fetch new data from ERCOT API
│ ├── migrate_existing_data.py # One-time CSV → DB migration
│ └── test_database_connection.py # Database connectivity test
│
├── data/ # CSV data (fallback/local dev)
│ ├── da_prices.csv # Day-ahead market prices
│ ├── rt_prices.csv # Real-time market prices
│ └── processed/
│ └── ercot_batteries.parquet # EIA-860 battery market data
│
├── .env.example # Environment variable template
├── .gitignore # Git ignore rules (includes .env)
├── requirements.txt # Python dependencies
└── README.md # This file
- Object-Oriented Design: Dataclasses for immutable state, Strategy pattern for dispatch algorithms
- Separation of Concerns: Business logic (core/) separate from UI (ui/)
- Caching: Streamlit
@st.cache_datafor expensive operations - Modularity: 1,919-line monolith refactored into ~10 focused modules
- Database Abstraction: Unified
load_data()function supports multiple sources
To use the Supabase database integration for historical data:
- Create account at https://supabase.com
- Create new project
- Copy Project URL and API Key (anon public)
cp .env.example .env
# Edit .env with your credentials- Open Supabase SQL Editor in your project dashboard
- Copy contents of
scripts/setup_supabase_schema.sql - Run the SQL to create tables, indexes, and views
Alternatively:
python scripts/create_supabase_tables.py
# Follow the instructions to manually execute SQL# Import dashboard CSV data
python scripts/migrate_existing_data.py
# Optionally include live data from ../ERCOT-Live-Data/
python scripts/migrate_existing_data.py --include-live-datapython scripts/test_database_connection.pyTo populate with fresh ERCOT market data:
# Requires ERCOT API credentials in .env
# Fetch last 7 days
python scripts/fetch_ercot_data.py
# Fetch specific date range
python scripts/fetch_ercot_data.py --start 2025-01-01 --end 2025-01-31 --refresh-viewThe dashboard will automatically use the database when credentials are configured!
- Date: July 20, 2025 (24 hours)
- Nodes: 5 wind farm settlement points
- BUFF_GAP_ALL
- BAIRDWND_ALL
- CEDROHI_CHW1
- SWTWN4_WND45
- WH_WIND_ALL
- Records: 768 hourly price points
- Extreme Events: 192 hours with >$10/MWh spreads (25%)
- Negative Prices: 40 hours with negative RT prices
- Date Range: Configurable (depends on data ingestion)
- Nodes: All ERCOT resource nodes
- Records: Potentially millions (DAM hourly + RTM 15-min)
- Update Frequency: As configured (manual or scheduled)
Revenue Opportunity = Optimal Strategy Revenue - Baseline Strategy Revenue
This metric quantifies the value of improved forecasting:
- Baseline: What's possible with DA forecasts alone
- Improved: What Zentus's enhanced forecasting enables
- Optimal: Theoretical maximum (perfect foresight benchmark)
Price spreads >$10/MWh between DA and RT markets. These high-volatility periods create the biggest arbitrage opportunities and account for most of the revenue opportunity.
0% = Use only day-ahead forecasts (baseline) 50% = Correct 50% of the forecast error 100% = Perfect real-time price knowledge (optimal)
The dashboard shows how revenue scales with forecast accuracy improvements.
Threshold-Based:
- Charge when price < 25th percentile
- Discharge when price > 75th percentile
- Fast, simple, works well with better forecasts
Rolling Window Optimization:
- Looks ahead N hours
- Solves for optimal charge/discharge pattern
- More sophisticated, benefits more from accuracy
This module allows users to design a hybrid Solar + Storage asset and optimize the battery size to capture "clipped" energy.
1. Data Fetching (Solar Potential):
- The system fetches Regional Solar Potential (Resource Availability) from ERCOT via the
ercot_generationtable. - This data represents the theoretical maximum power the sun provides in that specific region before any grid limits are applied.
- The profile is normalized (0-1) and scales dynamically with the user's Solar Capacity (MW) input.
2. Simulation Logic (The "Value of Clipping"):
- Clipped Energy (Green Area): Power that would normally be wasted because it exceeds the user-defined Interconnection Limit (MW) (POI limit).
- The simulation treats this clipped energy as "free" fuel for the battery.
- It runs a full optimization sweep (simulating every battery size from 0 MW up to 1.5x the solar capacity in 1 MW increments).
- For each size, it calculates the revenue gained by capturing that free energy and discharging it during the daily price peaks (using 15-minute Real-Time Market prices).
3. Optimization Result:
- The system identifies the Optimal Battery Size that maximizes total revenue (Base Solar Revenue + Battery Arbitrage Revenue).
- It quantifies the Lost Revenue of the current asset compared to the optimal configuration.
- Price Charts: Interactive Plotly charts with zoom/pan
- State of Charge: Real-time SOC tracking across scenarios
- Revenue Curves: Cumulative revenue comparison
- Sensitivity Analysis: Revenue vs forecast improvement curves
- Dispatch Timeline: Gantt-style charge/discharge visualization
- Distribution Analysis: Histogram of price forecasts vs actuals
All charts use Zentus brand colors and support dark/light mode.
The BatterySimulator class orchestrates:
- Price data ingestion (CSV or database)
- Strategy selection (Threshold or Rolling Window)
- Timestep-by-timestep dispatch decisions
- SOC tracking with efficiency losses
- Revenue calculation (actual RT prices)
- Result aggregation
improved_price = da_price + (improvement_factor * forecast_error)
# where forecast_error = rt_price - da_priceThis simulates having a better forecast that captures X% of the DA-to-RT price movement.
- ercot_prices: Raw price data (timestamp, location, market, price)
- ercot_prices_merged: Materialized view (pre-joined DAM + RTM with metrics)
- eia_batteries: Texas battery market reference data
- Indexes: Optimized for location + market + timestamp queries
# Test database connection
python scripts/test_database_connection.py
# Run dashboard in test mode
streamlit run Home.py
# Check dependencies
pip checkZentus - Intelligent Forecasting for Renewables
- Juan Boullosa - Dashboard development & OOP architecture
- N - Data processing (ERCOT-shadow-monitor)
- Aoife Henry - Strategy and submission
- Rafa Mudafort - Database infrastructure
- Ishaan Sood - Market context
Zentus Stanford Doerr School of Sustainability Accelerator Fellow
Email: jmboullosa@zentus.io Website: https://zentus.io
- Source: Electric Reliability Council of Texas (ERCOT)
- Access: Public API via gridstatus library
- Markets: Day-Ahead Market (DAM), Real-Time Market (RTM)
- License: ERCOT data usage subject to ERCOT protocols
- Source: U.S. Energy Information Administration (EIA-860)
- Dataset: Form EIA-860 Energy Storage Inventory (2024)
- Scope: 136 operational battery systems in Texas (ERCOT)
- License: Public domain
- Author: Zentus
- Purpose: Engie Urja AI Challenge 2025 submission
- Framework: Streamlit 1.28+
- Database: Supabase (PostgreSQL)
Potential additions:
- Real-time data streaming: WebSocket connection to ERCOT API
- Advanced strategies: Machine learning-based dispatch
- Multi-market optimization: Combine arbitrage + ancillary services
- Portfolio analysis: Multi-battery fleet optimization
- Weather integration: Renewable generation forecasts
- Cost modeling: Include degradation, O&M, demand charges
- Export functionality: CSV/PDF report generation
- Supabase Setup: See
scripts/create_supabase_tables.pydocstrings - Data Schema: See
scripts/setup_supabase_schema.sqlcomments - API Usage: See
scripts/fetch_ercot_data.pyfor ERCOT API examples - Strategy Details: See
core/battery/strategies.pydocstrings
Built with ❤️ by Zentus - Making renewable energy more predictable and profitable