Introduction
Since October 2025, electric markets in Finland and Nordpool has moved to
15-minute electricity pricing data. This granular data reveals significant
price spikes lasting just 15-45 minutes - opportunities that hourly averaging
completely misses.
About This Series
While this series uses HVAC heating as the primary example, the optimization
principles apply to any controllable electrical load:
- Heat pumps and air conditioning
- Water heaters and boilers
- EV charging systems
- Pool pumps and spa heaters
- Industrial equipment with thermal/energy storage
The key requirement: Your load must tolerate short interruptions or have some
form of energy storage capacity (thermal mass, battery, water tank, etc.).
Traditional HVAC energy-saving approaches:
Turn off heating during the most expensive hour
Ignore building thermal mass
Result in temperature drops and comfort loss
This series presents a smarter approach: A Python-based optimization system that:
Exploits 15-minute price peaks
Respects building thermodynamics
Maintains comfort while saving 10-20% on peak costs
v2.0: Now works with ANY Nordpool sensor configuration
v2.0: Automatic handling of mixed 15/60 min data
My heating system: Two air-source heat pumps (ILPs) as the primary heat source, with hydronic radiator heating set very low as backup. This hybrid approach provides flexibility for intelligent optimization.
The Core Problem
Traditional Approach
17:00 - Expensive hour detected
17:00 - Turn off HVAC completely
18:00 - Turn on HVAC
18:00 - Building is cold, recovery takes hours
Result: Discomfort, slower recovery, suboptimal actual savings.
My Approach: Intelligent Load Reduction
13:00 - PREHEAT: Warm building above target (store thermal energy)
17:00 - REDUCED HEATING: HVAC fan-only + radiators minimum
21:00 - RECOVERY: Gentle return to normal operation
01:00 - System back to baseline
Result: Minimal temperature drop, even heat distribution, optimal energy use, maximum savings.
The Three Phases Explained
Phase 1: Preheat (Pre-heating)
Duration: Same as cutoff period
HVAC Mode: Active heating (+1Β°C above target)
Radiators: Elevated temperature (target + offset)
Fan: High speed
Purpose: Store thermal energy in building mass
Your building is a thermal battery. By heating it 1-2Β°C above target before an expensive period, you store energy in walls, floors, and air that can sustain comfort during the reduced heating phase.
Cost multiplier: 1.5Γ (or weather-adaptive in v2.0)
Why? Heating above target requires more energy due to increased heat loss at higher ΞT.
v2.0 Enhancement: Weather-adaptive multipliers automatically adjust based on outdoor temperature:
- Extreme cold (<-10Β°C): 1.45Γ preheat, 1.20Γ recovery
- Cold (-10Β°C to -3Β°C): 1.34Γ preheat, 1.15Γ recovery
- Cool (-3Β°C to 2Β°C): 1.24Γ preheat, 1.12Γ recovery
- Mild (2Β°C to 7Β°C): 1.14Γ preheat, 1.08Γ recovery
- Warm (>7Β°C): 1.08Γ preheat, 1.04Γ recovery
This ensures optimal comfort while adapting to real-time weather conditions.
Phase 2: Reduced Heating (Cutoff)
Duration: 1-4 hours (optimized dynamically)
HVAC Mode: Fan-only (circulates air without heating)
Radiators: Minimum temperature setting
Fan: High speed (maintains air circulation)
Purpose: Minimize electricity consumption during peak prices
The heat pump switches to fan-only mode to maintain air circulation and even temperature distribution throughout the building, while radiators are set to minimum. This allows the buildingβs thermal mass to sustain comfort with minimal active heating.
Why not completely off? Fan circulation prevents cold spots and maintains even temperature distribution using stored thermal energy. The fan draws minimal power (~50W) compared to active heating (2000-5000W).
Weather-adaptive duration:
- In extreme cold (<0Β°C) or heat (>24Β°C), the system automatically reduces max cutoff to 3 hours to maintain comfort
- In mild conditions (0-24Β°C), full 4-hour cutoffs are allowed
Phase 3: Recovery
Duration: Same as cutoff period
HVAC Mode: Active heating
Fan: Medium to high speed
Radiators: Return to normal offset
Purpose: Return to baseline without shock-loading
Gentle ramp-up to normal operation. Slightly elevated consumption to restore baseline quickly without temperature overshoot.
Cost multiplier: 1.2Γ (or weather-adaptive in v2.0)
Why? Youβre catching up from a slightly lower temperature, requiring brief elevated output.
The Optimization Algorithm
Dynamic Price Difference Scaling
The Problem: How do you compare a 1-hour cutoff vs a 4-hour cutoff?
A 1-hour cutoff targets a sharp price spike:
- 17:30-18:30: Peak price 36.6 c/kWh
- Average of 1h: ~30 c/kWh
- Requires high price difference: 3.5 c/kWh
A 4-hour cutoff spans multiple price levels:
- 16:00-20:00: Mix of 5-35 c/kWh
- Average of 4h: ~15 c/kWh
- If we required 3.5Γ 4 = 14 c/kWh difference, weβd never find 4h cutoffs
Solution: Dynamic scaling
required_price_diff = base_requirement Γ (1h / cutoff_duration)
1h cutoff: 3.5 Γ (1/1) = 3.5 c/kWh
2h cutoff: 3.5 Γ (1/2) = 1.75 c/kWh
4h cutoff: 3.5 Γ (1/4) = 0.88 c/kWh
Calibrating the base requirement (3.5 c/kWh):
This value isnβt arbitrary - I calculated it from my real heating data and Nordpool price history. By analyzing which cutoff periods actually delivered 10%+ savings over several months, I found that 3.5 c/kWh price difference for 1-hour cutoffs consistently met this threshold for my specific building.
Your building will likely need different values depending on:
- Thermal mass (concrete, wood, insulation)
- Heat pump efficiency (SCOP values)
- Backup heating system (radiators, resistive)
- Building size and layout
Part 3 of this series will show you how to calibrate this value for your own home using historical data analysis.
This allows the algorithm to find both sharp peaks and broader elevated periods while ensuring each cutoff delivers meaningful savings for your specific building characteristics.
15-Minute Precision
The system tests:
- Start time: Every 15 minutes (16:00, 16:15, 16:30β¦)
- Duration: Every 15 minutes (1.0h, 1.25h, 1.5h, 1.75h, 2.0hβ¦)
- Total candidates: ~100-200 per day
Why not test every possible combination?
Testing every start Γ every duration would create 1000+ candidates β memory overflow in Home Assistant templates.
Python script solution: Handles this easily, selecting the top ~100 candidates that meet criteria.
v2.0 Enhancement: Now handles mixed 15-minute and 60-minute data automatically! If your Nordpool sensor provides hourly data (24 slots) or mixed today (96Γ15min) + tomorrow (24Γ60min), the script normalizes everything to 15-minute intervals for optimal precision.
Real-World Example
October 1, 2025 - Extreme Price Day
Peak price: 36.6 c/kWh at 17:45
Minimum price: 0.3 c/kWh at 03:00
Average: 6.2 c/kWh
Optimized schedule:
- 13:00-17:00: Preheat
- 17:00-21:00: Reduced heating (4h)
- 21:00-01:00: Recovery
Costs:
- Without optimization: 33.82 kWh-value
- With optimization: 21.67 kWh-value
- Savings: 12.15 (35.9%)
Price difference: 2.70 c/kWh (average cutoff vs average preheat+recovery)
Scaled requirement: 0.88 c/kWh (for 4h cutoff) ![]()
October 3, 2025 - Moderate Price Day
Peak price: 7.66 c/kWh at 17:45
Minimum price: 0.25 c/kWh at 00:45
Average: 2.0 c/kWh
Optimized schedule:
- 12:15-16:15: Preheat
- 16:15-20:15: Reduced heating (4h) β 15-min optimization!
- 20:15-00:15: Recovery
Why 16:15 instead of 16:00?
The 16:00-16:15 slot is relatively cheap (2.45 c/kWh). By starting at 16:15, the system:
- Avoids cutoff during that cheaper period
- Includes the 20:00-20:15 slot in cutoff (2.90 c/kWh)
- Result: +4.1% additional savings vs hourly optimization
Key Insights
- Building thermal mass is a battery: Store energy when cheap, use when expensive
- Symmetry is critical: Preheat and recovery durations must match cutoff
- Dynamic scaling is necessary: Different cutoff lengths need different thresholds
- 15-minute precision matters: 4% additional savings over hourly precision
- Fan-only maintains comfort: Air circulation uses minimal energy while distributing stored heat
- Weather-adaptive optimization: Cutoff duration and multipliers adjust automatically based on outdoor temperature
- v2.0: Universal Nordpool compatibility: Works with any Nordpool sensor configuration - no script editing required
- v2.0: Mixed resolution support: Handles both 15-minute and hourly data seamlessly
Whatβs Your Experience?
This approach has saved me 10-20% on peak electricity costs while maintaining perfect comfort levels. The building never drops temperature significantly during cutoffs, and the system runs completely automatically.
But I want to hear from you:
- Are you using the optimizer already? Share your results!
- What challenges have you faced with configuration?
- What features would you like to see in future versions?
v2.0.0 Now Available!
Major update just released! The complete implementation is production-ready and includes significant improvements:
GitHub Repository: nordpool-spot-cutoff-optimizer
Whatβs new in v2.0.0:
Key Features
-
Configurable Nordpool Sensor - Works with ANY Nordpool integration without editing code- Three-tier resolution: service data β input_text helper β automatic fallbacks
- Supports different sensor naming conventions across regions
- Telemetry shows which sensor is being used
-
Mixed Resolution Support - Automatic handling of 15-minute + hourly data- Today: 96Γ15min, Tomorrow: 24Γ60min? No problem!
- Automatic normalization to 15-minute precision
- Diagnostics show data resolution and slot durations
-
Weather-Adaptive Multipliers - Dynamic cost adjustment based on outdoor temperature- 5 temperature bands with optimal multipliers
- Hard failsafe for missing/invalid temperature sensors
- Telemetry exposes current multipliers and source
-
Enhanced Telemetry - Comprehensive diagnostics for troubleshootingnordpool_entity_used,nordpool_source- Which sensor was selecteddata_resolution,today_slot_minutes,tomorrow_slot_minutes- Data qualitypreheat_mult,recovery_mult,multiplier_source,outdoor_temp_c- Cost modelcandidates_scanned,results_found,total_cost_saving- Optimization stats
-
Bug Fixes - Template compatibility and timestamp handling- Fixed midnight crossovers and date seam issues
- Corrected attribute names (
shutdown_endβrecovery_start) - Robust error handling with graceful fallbacks
Documentation
Part 2: Python Implementation - Complete algorithm with v2.0 updates
Part 3: Integration Examples - HVAC & water heater setups updated for v2.0
Ready-to-use Python script with copy-paste configurations
Migration guide from v1.x to v2.0
Troubleshooting section for v2.0-specific issues
Breaking Changes
If upgrading from v1.x:
- Update template sensors:
shutdown_endβrecovery_start - Update attribute references in dashboards (see migration guide)
- Python script must be replaced (not compatible with v1.x)
Now production-ready and deployed in real-world installations!
Have fun optimizing your electric/HVAC systems! ![]()
![]()
(And save some money while youβre at it) ![]()
P.S. If you break your heating system, thatβs between you and your thermostat. We just provide the math! ![]()
Questions about the theory, v2.0 features, or calculations? Discussion starts below! ![]()