feat(periods): implement geometric_extension_attempted flag and time_range filtering

Phase 3: When geometric bonus intervals cause CV gate failure, strip them
from period edges (unextended boundaries) and set geometric_extension_attempted=True
on the summary. Previously only geometric_extension_active was tracked.
Moved LOW_PRICE_QUALITY_BYPASS_THRESHOLD constant to types.py for shared access.

Phase 4: Add time_range: tuple[datetime, datetime] | None parameter to
build_periods(), calculate_periods(), and calculate_periods_with_relaxation().
Filters candidate intervals to [start, end) without affecting day-wide reference
prices. Refactored _apply_segment_forcing() to use time_range instead of the
restricted_prices list approach.

Impact: Period statistics now accurately reflect when geometric flex extension
was attempted but reverted due to quality gate failure. Segment forcing uses
a cleaner API that preserves full price context for reference calculations.
This commit is contained in:
Julian Pawlowski 2026-04-12 08:24:38 +00:00
parent 796eb4b422
commit a8d5230531
8 changed files with 162 additions and 20 deletions

View file

@ -20,6 +20,8 @@ from custom_components.tibber_prices.const import (
CONF_BEST_PRICE_MAX_LEVEL_GAP_COUNT, CONF_BEST_PRICE_MAX_LEVEL_GAP_COUNT,
CONF_BEST_PRICE_MIN_DISTANCE_FROM_AVG, CONF_BEST_PRICE_MIN_DISTANCE_FROM_AVG,
CONF_BEST_PRICE_MIN_PERIOD_LENGTH, CONF_BEST_PRICE_MIN_PERIOD_LENGTH,
CONF_BEST_PRICE_SEGMENT_FORCING,
CONF_BEST_PRICE_SEGMENT_MIN_PERIODS,
CONF_CURRENCY_DISPLAY_MODE, CONF_CURRENCY_DISPLAY_MODE,
CONF_ENABLE_MIN_PERIODS_BEST, CONF_ENABLE_MIN_PERIODS_BEST,
CONF_ENABLE_MIN_PERIODS_PEAK, CONF_ENABLE_MIN_PERIODS_PEAK,
@ -34,6 +36,8 @@ from custom_components.tibber_prices.const import (
CONF_PEAK_PRICE_MIN_DISTANCE_FROM_AVG, CONF_PEAK_PRICE_MIN_DISTANCE_FROM_AVG,
CONF_PEAK_PRICE_MIN_LEVEL, CONF_PEAK_PRICE_MIN_LEVEL,
CONF_PEAK_PRICE_MIN_PERIOD_LENGTH, CONF_PEAK_PRICE_MIN_PERIOD_LENGTH,
CONF_PEAK_PRICE_SEGMENT_FORCING,
CONF_PEAK_PRICE_SEGMENT_MIN_PERIODS,
CONF_PRICE_LEVEL_GAP_TOLERANCE, CONF_PRICE_LEVEL_GAP_TOLERANCE,
CONF_PRICE_RATING_GAP_TOLERANCE, CONF_PRICE_RATING_GAP_TOLERANCE,
CONF_PRICE_RATING_HYSTERESIS, CONF_PRICE_RATING_HYSTERESIS,
@ -63,6 +67,8 @@ from custom_components.tibber_prices.const import (
DEFAULT_BEST_PRICE_MAX_LEVEL_GAP_COUNT, DEFAULT_BEST_PRICE_MAX_LEVEL_GAP_COUNT,
DEFAULT_BEST_PRICE_MIN_DISTANCE_FROM_AVG, DEFAULT_BEST_PRICE_MIN_DISTANCE_FROM_AVG,
DEFAULT_BEST_PRICE_MIN_PERIOD_LENGTH, DEFAULT_BEST_PRICE_MIN_PERIOD_LENGTH,
DEFAULT_BEST_PRICE_SEGMENT_FORCING,
DEFAULT_BEST_PRICE_SEGMENT_MIN_PERIODS,
DEFAULT_ENABLE_MIN_PERIODS_BEST, DEFAULT_ENABLE_MIN_PERIODS_BEST,
DEFAULT_ENABLE_MIN_PERIODS_PEAK, DEFAULT_ENABLE_MIN_PERIODS_PEAK,
DEFAULT_EXTENDED_DESCRIPTIONS, DEFAULT_EXTENDED_DESCRIPTIONS,
@ -76,6 +82,8 @@ from custom_components.tibber_prices.const import (
DEFAULT_PEAK_PRICE_MIN_DISTANCE_FROM_AVG, DEFAULT_PEAK_PRICE_MIN_DISTANCE_FROM_AVG,
DEFAULT_PEAK_PRICE_MIN_LEVEL, DEFAULT_PEAK_PRICE_MIN_LEVEL,
DEFAULT_PEAK_PRICE_MIN_PERIOD_LENGTH, DEFAULT_PEAK_PRICE_MIN_PERIOD_LENGTH,
DEFAULT_PEAK_PRICE_SEGMENT_FORCING,
DEFAULT_PEAK_PRICE_SEGMENT_MIN_PERIODS,
DEFAULT_PRICE_LEVEL_GAP_TOLERANCE, DEFAULT_PRICE_LEVEL_GAP_TOLERANCE,
DEFAULT_PRICE_RATING_GAP_TOLERANCE, DEFAULT_PRICE_RATING_GAP_TOLERANCE,
DEFAULT_PRICE_RATING_HYSTERESIS, DEFAULT_PRICE_RATING_HYSTERESIS,
@ -116,6 +124,7 @@ from custom_components.tibber_prices.const import (
MAX_PRICE_TREND_STRONGLY_FALLING, MAX_PRICE_TREND_STRONGLY_FALLING,
MAX_PRICE_TREND_STRONGLY_RISING, MAX_PRICE_TREND_STRONGLY_RISING,
MAX_RELAXATION_ATTEMPTS, MAX_RELAXATION_ATTEMPTS,
MAX_SEGMENT_MIN_PERIODS,
MAX_VOLATILITY_THRESHOLD_HIGH, MAX_VOLATILITY_THRESHOLD_HIGH,
MAX_VOLATILITY_THRESHOLD_MODERATE, MAX_VOLATILITY_THRESHOLD_MODERATE,
MAX_VOLATILITY_THRESHOLD_VERY_HIGH, MAX_VOLATILITY_THRESHOLD_VERY_HIGH,
@ -655,6 +664,12 @@ def get_best_price_schema(
extension_settings.get(CONF_BEST_PRICE_MAX_EXTENSION_INTERVALS, DEFAULT_BEST_PRICE_MAX_EXTENSION_INTERVALS) extension_settings.get(CONF_BEST_PRICE_MAX_EXTENSION_INTERVALS, DEFAULT_BEST_PRICE_MAX_EXTENSION_INTERVALS)
) )
geometric_flex_best = int(extension_settings.get(CONF_BEST_PRICE_GEOMETRIC_FLEX, DEFAULT_BEST_PRICE_GEOMETRIC_FLEX)) geometric_flex_best = int(extension_settings.get(CONF_BEST_PRICE_GEOMETRIC_FLEX, DEFAULT_BEST_PRICE_GEOMETRIC_FLEX))
segment_forcing_best = bool(
extension_settings.get(CONF_BEST_PRICE_SEGMENT_FORCING, DEFAULT_BEST_PRICE_SEGMENT_FORCING)
)
segment_min_periods_best = int(
extension_settings.get(CONF_BEST_PRICE_SEGMENT_MIN_PERIODS, DEFAULT_BEST_PRICE_SEGMENT_MIN_PERIODS)
)
# Build section schemas with optional override warnings # Build section schemas with optional override warnings
period_warning = get_section_override_warning("best_price", "period_settings", overrides, translations) or {} period_warning = get_section_override_warning("best_price", "period_settings", overrides, translations) or {}
@ -806,6 +821,21 @@ def get_best_price_schema(
mode=NumberSelectorMode.SLIDER, mode=NumberSelectorMode.SLIDER,
) )
), ),
vol.Optional(
CONF_BEST_PRICE_SEGMENT_FORCING,
default=segment_forcing_best,
): BooleanSelector(selector.BooleanSelectorConfig()),
vol.Optional(
CONF_BEST_PRICE_SEGMENT_MIN_PERIODS,
default=segment_min_periods_best,
): NumberSelector(
NumberSelectorConfig(
min=1,
max=MAX_SEGMENT_MIN_PERIODS,
step=1,
mode=NumberSelectorMode.SLIDER,
)
),
} }
), ),
{"collapsed": True}, {"collapsed": True},
@ -858,6 +888,12 @@ def get_peak_price_schema(
extension_settings.get(CONF_PEAK_PRICE_MAX_EXTENSION_INTERVALS, DEFAULT_PEAK_PRICE_MAX_EXTENSION_INTERVALS) extension_settings.get(CONF_PEAK_PRICE_MAX_EXTENSION_INTERVALS, DEFAULT_PEAK_PRICE_MAX_EXTENSION_INTERVALS)
) )
geometric_flex_peak = int(extension_settings.get(CONF_PEAK_PRICE_GEOMETRIC_FLEX, DEFAULT_PEAK_PRICE_GEOMETRIC_FLEX)) geometric_flex_peak = int(extension_settings.get(CONF_PEAK_PRICE_GEOMETRIC_FLEX, DEFAULT_PEAK_PRICE_GEOMETRIC_FLEX))
segment_forcing_peak = bool(
extension_settings.get(CONF_PEAK_PRICE_SEGMENT_FORCING, DEFAULT_PEAK_PRICE_SEGMENT_FORCING)
)
segment_min_periods_peak = int(
extension_settings.get(CONF_PEAK_PRICE_SEGMENT_MIN_PERIODS, DEFAULT_PEAK_PRICE_SEGMENT_MIN_PERIODS)
)
# Build section schemas with optional override warnings # Build section schemas with optional override warnings
period_warning = get_section_override_warning("peak_price", "period_settings", overrides, translations) or {} period_warning = get_section_override_warning("peak_price", "period_settings", overrides, translations) or {}
@ -1009,6 +1045,21 @@ def get_peak_price_schema(
mode=NumberSelectorMode.SLIDER, mode=NumberSelectorMode.SLIDER,
) )
), ),
vol.Optional(
CONF_PEAK_PRICE_SEGMENT_FORCING,
default=segment_forcing_peak,
): BooleanSelector(selector.BooleanSelectorConfig()),
vol.Optional(
CONF_PEAK_PRICE_SEGMENT_MIN_PERIODS,
default=segment_min_periods_peak,
): NumberSelector(
NumberSelectorConfig(
min=1,
max=MAX_SEGMENT_MIN_PERIODS,
step=1,
mode=NumberSelectorMode.SLIDER,
)
),
} }
), ),
{"collapsed": True}, {"collapsed": True},

View file

@ -74,6 +74,10 @@ CONF_BEST_PRICE_GEOMETRIC_FLEX = "best_price_geometric_flex"
CONF_PEAK_PRICE_EXTEND_TO_VERY_EXPENSIVE = "peak_price_extend_to_very_expensive" CONF_PEAK_PRICE_EXTEND_TO_VERY_EXPENSIVE = "peak_price_extend_to_very_expensive"
CONF_PEAK_PRICE_MAX_EXTENSION_INTERVALS = "peak_price_max_extension_intervals" CONF_PEAK_PRICE_MAX_EXTENSION_INTERVALS = "peak_price_max_extension_intervals"
CONF_PEAK_PRICE_GEOMETRIC_FLEX = "peak_price_geometric_flex" CONF_PEAK_PRICE_GEOMETRIC_FLEX = "peak_price_geometric_flex"
CONF_BEST_PRICE_SEGMENT_FORCING = "best_price_segment_forcing"
CONF_BEST_PRICE_SEGMENT_MIN_PERIODS = "best_price_segment_min_periods"
CONF_PEAK_PRICE_SEGMENT_FORCING = "peak_price_segment_forcing"
CONF_PEAK_PRICE_SEGMENT_MIN_PERIODS = "peak_price_segment_min_periods"
ATTRIBUTION = "Data provided by Tibber" ATTRIBUTION = "Data provided by Tibber"
@ -143,6 +147,10 @@ DEFAULT_BEST_PRICE_GEOMETRIC_FLEX = 0 # Default: 0% (disabled); positive int %
DEFAULT_PEAK_PRICE_EXTEND_TO_VERY_EXPENSIVE = False # Default: disabled (opt-in feature) DEFAULT_PEAK_PRICE_EXTEND_TO_VERY_EXPENSIVE = False # Default: disabled (opt-in feature)
DEFAULT_PEAK_PRICE_MAX_EXTENSION_INTERVALS = 4 # Default: up to 4 intervals (1 hour) per side DEFAULT_PEAK_PRICE_MAX_EXTENSION_INTERVALS = 4 # Default: up to 4 intervals (1 hour) per side
DEFAULT_PEAK_PRICE_GEOMETRIC_FLEX = 0 # Default: 0% (disabled); positive int % (e.g. 10 = 10%) DEFAULT_PEAK_PRICE_GEOMETRIC_FLEX = 0 # Default: 0% (disabled); positive int % (e.g. 10 = 10%)
DEFAULT_BEST_PRICE_SEGMENT_FORCING = False # Default: disabled (opt-in W-shape feature)
DEFAULT_BEST_PRICE_SEGMENT_MIN_PERIODS = 1 # Default: at least 1 period required per segment
DEFAULT_PEAK_PRICE_SEGMENT_FORCING = False # Default: disabled (opt-in M-shape feature)
DEFAULT_PEAK_PRICE_SEGMENT_MIN_PERIODS = 1 # Default: at least 1 period required per segment
# Validation limits (used in GUI schemas and server-side validation) # Validation limits (used in GUI schemas and server-side validation)
# These ensure consistency between frontend and backend validation # These ensure consistency between frontend and backend validation
@ -153,6 +161,7 @@ MAX_MIN_PERIODS = 10 # Maximum number of minimum periods per day (GUI slider li
MAX_RELAXATION_ATTEMPTS = 12 # Maximum relaxation attempts (GUI slider limit) MAX_RELAXATION_ATTEMPTS = 12 # Maximum relaxation attempts (GUI slider limit)
MAX_EXTENSION_INTERVALS = 12 # Maximum extension intervals per side (GUI slider limit = 3 hours) MAX_EXTENSION_INTERVALS = 12 # Maximum extension intervals per side (GUI slider limit = 3 hours)
MAX_GEOMETRIC_FLEX = 25 # Maximum geometric flex bonus percentage (GUI slider limit) MAX_GEOMETRIC_FLEX = 25 # Maximum geometric flex bonus percentage (GUI slider limit)
MAX_SEGMENT_MIN_PERIODS = 5 # Maximum per-segment minimum periods (GUI slider limit)
MIN_PERIOD_LENGTH = 15 # Minimum period length in minutes (1 quarter hour) MIN_PERIOD_LENGTH = 15 # Minimum period length in minutes (1 quarter hour)
MAX_MIN_PERIOD_LENGTH = 180 # Maximum for minimum period length setting (3 hours - realistic for required minimum) MAX_MIN_PERIOD_LENGTH = 180 # Maximum for minimum period length setting (3 hours - realistic for required minimum)
@ -426,9 +435,13 @@ def get_default_options(currency_code: str | None) -> dict[str, Any]:
CONF_BEST_PRICE_EXTEND_TO_VERY_CHEAP: DEFAULT_BEST_PRICE_EXTEND_TO_VERY_CHEAP, CONF_BEST_PRICE_EXTEND_TO_VERY_CHEAP: DEFAULT_BEST_PRICE_EXTEND_TO_VERY_CHEAP,
CONF_BEST_PRICE_MAX_EXTENSION_INTERVALS: DEFAULT_BEST_PRICE_MAX_EXTENSION_INTERVALS, CONF_BEST_PRICE_MAX_EXTENSION_INTERVALS: DEFAULT_BEST_PRICE_MAX_EXTENSION_INTERVALS,
CONF_BEST_PRICE_GEOMETRIC_FLEX: DEFAULT_BEST_PRICE_GEOMETRIC_FLEX, CONF_BEST_PRICE_GEOMETRIC_FLEX: DEFAULT_BEST_PRICE_GEOMETRIC_FLEX,
CONF_BEST_PRICE_SEGMENT_FORCING: DEFAULT_BEST_PRICE_SEGMENT_FORCING,
CONF_BEST_PRICE_SEGMENT_MIN_PERIODS: DEFAULT_BEST_PRICE_SEGMENT_MIN_PERIODS,
CONF_PEAK_PRICE_EXTEND_TO_VERY_EXPENSIVE: DEFAULT_PEAK_PRICE_EXTEND_TO_VERY_EXPENSIVE, CONF_PEAK_PRICE_EXTEND_TO_VERY_EXPENSIVE: DEFAULT_PEAK_PRICE_EXTEND_TO_VERY_EXPENSIVE,
CONF_PEAK_PRICE_MAX_EXTENSION_INTERVALS: DEFAULT_PEAK_PRICE_MAX_EXTENSION_INTERVALS, CONF_PEAK_PRICE_MAX_EXTENSION_INTERVALS: DEFAULT_PEAK_PRICE_MAX_EXTENSION_INTERVALS,
CONF_PEAK_PRICE_GEOMETRIC_FLEX: DEFAULT_PEAK_PRICE_GEOMETRIC_FLEX, CONF_PEAK_PRICE_GEOMETRIC_FLEX: DEFAULT_PEAK_PRICE_GEOMETRIC_FLEX,
CONF_PEAK_PRICE_SEGMENT_FORCING: DEFAULT_PEAK_PRICE_SEGMENT_FORCING,
CONF_PEAK_PRICE_SEGMENT_MIN_PERIODS: DEFAULT_PEAK_PRICE_SEGMENT_MIN_PERIODS,
}, },
} }

View file

@ -280,6 +280,40 @@ class TibberPricesPeriodCalculator:
) )
config["geometric_extra_flex"] = geometric_flex_pct / 100 config["geometric_extra_flex"] = geometric_flex_pct / 100
# Segment forcing (force at least segment_min_periods per W/M-shape segment)
if reverse_sort:
segment_forcing = bool(
self._get_option(
_const.CONF_PEAK_PRICE_SEGMENT_FORCING,
"extension_settings",
_const.DEFAULT_PEAK_PRICE_SEGMENT_FORCING,
)
)
segment_min_periods = int(
self._get_option(
_const.CONF_PEAK_PRICE_SEGMENT_MIN_PERIODS,
"extension_settings",
_const.DEFAULT_PEAK_PRICE_SEGMENT_MIN_PERIODS,
)
)
else:
segment_forcing = bool(
self._get_option(
_const.CONF_BEST_PRICE_SEGMENT_FORCING,
"extension_settings",
_const.DEFAULT_BEST_PRICE_SEGMENT_FORCING,
)
)
segment_min_periods = int(
self._get_option(
_const.CONF_BEST_PRICE_SEGMENT_MIN_PERIODS,
"extension_settings",
_const.DEFAULT_BEST_PRICE_SEGMENT_MIN_PERIODS,
)
)
config["segment_forcing"] = segment_forcing
config["segment_min_periods"] = segment_min_periods
# Cache the result # Cache the result
self._config_cache[cache_key] = config self._config_cache[cache_key] = config
self._config_cache_valid = True self._config_cache_valid = True
@ -774,6 +808,8 @@ class TibberPricesPeriodCalculator:
extend_to_extreme=best_config["extend_to_extreme"], extend_to_extreme=best_config["extend_to_extreme"],
max_extension_intervals=best_config["max_extension_intervals"], max_extension_intervals=best_config["max_extension_intervals"],
geometric_extra_flex=best_config["geometric_extra_flex"], geometric_extra_flex=best_config["geometric_extra_flex"],
segment_forcing=best_config["segment_forcing"],
segment_min_periods=best_config["segment_min_periods"],
) )
best_periods = calculate_periods_with_relaxation( best_periods = calculate_periods_with_relaxation(
all_prices, all_prices,
@ -859,6 +895,8 @@ class TibberPricesPeriodCalculator:
extend_to_extreme=peak_config["extend_to_extreme"], extend_to_extreme=peak_config["extend_to_extreme"],
max_extension_intervals=peak_config["max_extension_intervals"], max_extension_intervals=peak_config["max_extension_intervals"],
geometric_extra_flex=peak_config["geometric_extra_flex"], geometric_extra_flex=peak_config["geometric_extra_flex"],
segment_forcing=peak_config["segment_forcing"],
segment_min_periods=peak_config["segment_min_periods"],
) )
peak_periods = calculate_periods_with_relaxation( peak_periods = calculate_periods_with_relaxation(
all_prices, all_prices,

View file

@ -245,12 +245,16 @@
"data": { "data": {
"best_price_extend_to_very_cheap": "Auf sehr günstige Intervalle erweitern", "best_price_extend_to_very_cheap": "Auf sehr günstige Intervalle erweitern",
"best_price_max_extension_intervals": "Maximale Erweiterungsintervalle", "best_price_max_extension_intervals": "Maximale Erweiterungsintervalle",
"best_price_geometric_flex": "Geometrischer Flex-Bonus" "best_price_geometric_flex": "Geometrischer Flex-Bonus",
"best_price_segment_forcing": "W-Form-Segment-Erzwingung",
"best_price_segment_min_periods": "Perioden pro Segment"
}, },
"data_description": { "data_description": {
"best_price_extend_to_very_cheap": "Wenn aktiviert, erweitern sich erkannte Bestpreisperioden nach außen, um angrenzende Intervalle mit dem Preisniveau 'Sehr günstig' aufzunehmen. So werden extrem günstige Intervalle an den Rändern erkannter Perioden besser erfasst.", "best_price_extend_to_very_cheap": "Wenn aktiviert, erweitern sich erkannte Bestpreisperioden nach außen, um angrenzende Intervalle mit dem Preisniveau 'Sehr günstig' aufzunehmen. So werden extrem günstige Intervalle an den Rändern erkannter Perioden besser erfasst.",
"best_price_max_extension_intervals": "Maximale Anzahl zusätzlicher Intervalle pro Seite (linker und rechter Rand). Jedes Intervall dauert 15 Minuten. Beispiel: 4 Intervalle = bis zu 1 Stunde Erweiterung pro Rand. Standard: 4", "best_price_max_extension_intervals": "Maximale Anzahl zusätzlicher Intervalle pro Seite (linker und rechter Rand). Jedes Intervall dauert 15 Minuten. Beispiel: 4 Intervalle = bis zu 1 Stunde Erweiterung pro Rand. Standard: 4",
"best_price_geometric_flex": "Zusätzlicher Flex-Prozentsatz für Intervalle, die in ein erkanntes Preistal (V-Form) fallen. Wenn für den Tag ein Tal-Muster erkannt wird, erhalten Intervalle innerhalb der Talzone diese zusätzliche Toleranz, damit der Periodendetektor sie eher einschließt. 0 = deaktiviert. Standard: 0" "best_price_geometric_flex": "Zusätzlicher Flex-Prozentsatz für Intervalle, die in ein erkanntes Preistal (V-Form) fallen. Wenn für den Tag ein Tal-Muster erkannt wird, erhalten Intervalle innerhalb der Talzone diese zusätzliche Toleranz, damit der Periodendetektor sie eher einschließt. 0 = deaktiviert. Standard: 0",
"best_price_segment_forcing": "Wenn aktiviert, werden Tage mit W-förmigem Preiskurve (zwei Täler, getrennt durch einen zentralen Gipfel) an diesem Gipfel geteilt. Die Periodenerkennung läuft unabhängig für jede Talseite, um sicherzustellen, dass jedes Tal die erforderliche Anzahl von Perioden erhält.",
"best_price_segment_min_periods": "Mindestanzahl erforderlicher Bestpreisperioden pro Talseite bei aktivierter W-Form-Segment-Erzwingung. Jede Seite muss unabhängig mindestens diese Anzahl Perioden liefern. Standard: 1"
} }
} }
}, },
@ -306,12 +310,16 @@
"data": { "data": {
"peak_price_extend_to_very_expensive": "Auf sehr teure Intervalle erweitern", "peak_price_extend_to_very_expensive": "Auf sehr teure Intervalle erweitern",
"peak_price_max_extension_intervals": "Maximale Erweiterungsintervalle", "peak_price_max_extension_intervals": "Maximale Erweiterungsintervalle",
"peak_price_geometric_flex": "Geometrischer Flex-Bonus" "peak_price_geometric_flex": "Geometrischer Flex-Bonus",
"peak_price_segment_forcing": "M-Form-Segment-Erzwingung",
"peak_price_segment_min_periods": "Perioden pro Segment"
}, },
"data_description": { "data_description": {
"peak_price_extend_to_very_expensive": "Wenn aktiviert, erweitern sich erkannte Spitzenpreisperioden nach außen, um angrenzende Intervalle mit dem Preisniveau 'Sehr teuer' aufzunehmen. So werden extrem teure Intervalle an den Rändern erkannter Perioden besser erfasst.", "peak_price_extend_to_very_expensive": "Wenn aktiviert, erweitern sich erkannte Spitzenpreisperioden nach außen, um angrenzende Intervalle mit dem Preisniveau 'Sehr teuer' aufzunehmen. So werden extrem teure Intervalle an den Rändern erkannter Perioden besser erfasst.",
"peak_price_max_extension_intervals": "Maximale Anzahl zusätzlicher Intervalle pro Seite (linker und rechter Rand). Jedes Intervall dauert 15 Minuten. Beispiel: 4 Intervalle = bis zu 1 Stunde Erweiterung pro Rand. Standard: 4", "peak_price_max_extension_intervals": "Maximale Anzahl zusätzlicher Intervalle pro Seite (linker und rechter Rand). Jedes Intervall dauert 15 Minuten. Beispiel: 4 Intervalle = bis zu 1 Stunde Erweiterung pro Rand. Standard: 4",
"peak_price_geometric_flex": "Zusätzlicher Flex-Prozentsatz für Intervalle, die in einen erkannten Preisplateau (Λ-Form) fallen. Wenn für den Tag ein Gipfel-Muster erkannt wird, erhalten Intervalle innerhalb der Gipfelzone diese zusätzliche Toleranz, damit der Periodendetektor sie eher einschließt. 0 = deaktiviert. Standard: 0" "peak_price_geometric_flex": "Zusätzlicher Flex-Prozentsatz für Intervalle, die in einen erkannten Preisplateau (Λ-Form) fallen. Wenn für den Tag ein Gipfel-Muster erkannt wird, erhalten Intervalle innerhalb der Gipfelzone diese zusätzliche Toleranz, damit der Periodendetektor sie eher einschließt. 0 = deaktiviert. Standard: 0",
"peak_price_segment_forcing": "Wenn aktiviert, werden Tage mit M-förmigem Preiskurve (zwei Gipfel, getrennt durch ein zentrales Tal) an diesem Tal geteilt. Die Periodenerkennung läuft unabhängig für jede Gipfelseite, um sicherzustellen, dass jeder Gipfel die erforderliche Anzahl von Perioden erhält.",
"peak_price_segment_min_periods": "Mindestanzahl erforderlicher Spitzenpreisperioden pro Gipfelseite bei aktivierter M-Form-Segment-Erzwingung. Jede Seite muss unabhängig mindestens diese Anzahl Perioden liefern. Standard: 1"
} }
} }
}, },

View file

@ -256,12 +256,16 @@
"data": { "data": {
"best_price_extend_to_very_cheap": "Extend to Very Cheap Intervals", "best_price_extend_to_very_cheap": "Extend to Very Cheap Intervals",
"best_price_max_extension_intervals": "Maximum Extension Intervals", "best_price_max_extension_intervals": "Maximum Extension Intervals",
"best_price_geometric_flex": "Geometric Flex Bonus" "best_price_geometric_flex": "Geometric Flex Bonus",
"best_price_segment_forcing": "W-Shape Segment Forcing",
"best_price_segment_min_periods": "Periods per Segment"
}, },
"data_description": { "data_description": {
"best_price_extend_to_very_cheap": "When enabled, detected best price periods expand outward to absorb adjacent intervals with a 'Very cheap' price level. This widens low-price windows to better capture extremely cheap intervals at the edges of detected periods.", "best_price_extend_to_very_cheap": "When enabled, detected best price periods expand outward to absorb adjacent intervals with a 'Very cheap' price level. This widens low-price windows to better capture extremely cheap intervals at the edges of detected periods.",
"best_price_max_extension_intervals": "Maximum number of additional intervals to absorb per side (left and right edge). Each interval is 15 minutes. Example: 4 intervals = up to 1 hour extension per edge. Default: 4", "best_price_max_extension_intervals": "Maximum number of additional intervals to absorb per side (left and right edge). Each interval is 15 minutes. Example: 4 intervals = up to 1 hour extension per edge. Default: 4",
"best_price_geometric_flex": "Extra flex percentage applied to intervals that fall inside a detected price valley (V-shape). When a valley pattern is detected for the day, intervals within the valley zone get this additional tolerance, making the period detector more likely to include them. 0 = disabled. Default: 0" "best_price_geometric_flex": "Extra flex percentage applied to intervals that fall inside a detected price valley (V-shape). When a valley pattern is detected for the day, intervals within the valley zone get this additional tolerance, making the period detector more likely to include them. 0 = disabled. Default: 0",
"best_price_segment_forcing": "When enabled, days with a W-shaped price curve (two valleys separated by a central peak) split at the central peak. Period detection runs independently for each valley side, ensuring each valley has the required number of periods. This prevents both periods from clustering in the same valley. Requires the day pattern sensor to detect a 'double_valley' pattern.",
"best_price_segment_min_periods": "Minimum number of best price periods required per valley side when W-shape segment forcing is enabled. Each side must independently produce at least this many periods. Default: 1"
} }
} }
}, },
@ -317,12 +321,16 @@
"data": { "data": {
"peak_price_extend_to_very_expensive": "Extend to Very Expensive Intervals", "peak_price_extend_to_very_expensive": "Extend to Very Expensive Intervals",
"peak_price_max_extension_intervals": "Maximum Extension Intervals", "peak_price_max_extension_intervals": "Maximum Extension Intervals",
"peak_price_geometric_flex": "Geometric Flex Bonus" "peak_price_geometric_flex": "Geometric Flex Bonus",
"peak_price_segment_forcing": "M-Shape Segment Forcing",
"peak_price_segment_min_periods": "Periods per Segment"
}, },
"data_description": { "data_description": {
"peak_price_extend_to_very_expensive": "When enabled, detected peak price periods expand outward to absorb adjacent intervals with a 'Very expensive' price level. This widens high-price windows to better capture extremely expensive intervals at the edges of detected periods.", "peak_price_extend_to_very_expensive": "When enabled, detected peak price periods expand outward to absorb adjacent intervals with a 'Very expensive' price level. This widens high-price windows to better capture extremely expensive intervals at the edges of detected periods.",
"peak_price_max_extension_intervals": "Maximum number of additional intervals to absorb per side (left and right edge). Each interval is 15 minutes. Example: 4 intervals = up to 1 hour extension per edge. Default: 4", "peak_price_max_extension_intervals": "Maximum number of additional intervals to absorb per side (left and right edge). Each interval is 15 minutes. Example: 4 intervals = up to 1 hour extension per edge. Default: 4",
"peak_price_geometric_flex": "Extra flex percentage applied to intervals that fall inside a detected price peak (Λ-shape). When a peak pattern is detected for the day, intervals within the peak zone get this additional tolerance, making the period detector more likely to include them. 0 = disabled. Default: 0" "peak_price_geometric_flex": "Extra flex percentage applied to intervals that fall inside a detected price peak (Λ-shape). When a peak pattern is detected for the day, intervals within the peak zone get this additional tolerance, making the period detector more likely to include them. 0 = disabled. Default: 0",
"peak_price_segment_forcing": "When enabled, days with an M-shaped price curve (two peaks separated by a central valley) split at the central valley. Period detection runs independently for each peak side, ensuring each peak has the required number of periods. This prevents both periods from clustering in the same peak. Requires the day pattern sensor to detect a 'double_peak' pattern.",
"peak_price_segment_min_periods": "Minimum number of peak price periods required per peak side when M-shape segment forcing is enabled. Each side must independently produce at least this many periods. Default: 1"
} }
} }
}, },

View file

@ -245,12 +245,16 @@
"data": { "data": {
"best_price_extend_to_very_cheap": "Utvid til svært billige intervaller", "best_price_extend_to_very_cheap": "Utvid til svært billige intervaller",
"best_price_max_extension_intervals": "Maksimale utvidelsesintervaller", "best_price_max_extension_intervals": "Maksimale utvidelsesintervaller",
"best_price_geometric_flex": "Geometrisk fleksbonus" "best_price_geometric_flex": "Geometrisk fleksbonus",
"best_price_segment_forcing": "W-form segment-tvinging",
"best_price_segment_min_periods": "Perioder per segment"
}, },
"data_description": { "data_description": {
"best_price_extend_to_very_cheap": "Når aktivert, utvider oppdagede bestprisperioder seg utover for å inkludere tilstøtende intervaller med prisnivået 'Svært billig'. Dette fanger opp ekstremt billige intervaller ved kantene av oppdagede perioder.", "best_price_extend_to_very_cheap": "Når aktivert, utvider oppdagede bestprisperioder seg utover for å inkludere tilstøtende intervaller med prisnivået 'Svært billig'. Dette fanger opp ekstremt billige intervaller ved kantene av oppdagede perioder.",
"best_price_max_extension_intervals": "Maksimalt antall ekstra intervaller per side (venstre og høyre kant). Hvert intervall er 15 minutter. Eksempel: 4 intervaller = opptil 1 times utvidelse per kant. Standard: 4", "best_price_max_extension_intervals": "Maksimalt antall ekstra intervaller per side (venstre og høyre kant). Hvert intervall er 15 minutter. Eksempel: 4 intervaller = opptil 1 times utvidelse per kant. Standard: 4",
"best_price_geometric_flex": "Ekstra fleksprosent for intervaller som faller innenfor en oppdaget prisdal (V-form). Når et dal-mønster oppdages for dagen, får intervaller innen dalsonen denne ekstra toleransen, slik at periodevarslingssystemet er mer tilbøyelig til å inkludere dem. 0 = deaktivert. Standard: 0" "best_price_geometric_flex": "Ekstra fleksprosent for intervaller som faller innenfor en oppdaget prisdal (V-form). Når et dal-mønster oppdages for dagen, får intervaller innen dalsonen denne ekstra toleransen, slik at periodevarslingssystemet er mer tilbøyelig til å inkludere dem. 0 = deaktivert. Standard: 0",
"best_price_segment_forcing": "Når aktivert deles dager med W-formet priskurve (to daler adskilt av en sentral topp) ved den sentrale toppen. Periodedetektor kjøres uavhengig for hver dalside for å sikre at hvert dal får det påkrevde antallet perioder.",
"best_price_segment_min_periods": "Minimum antall bestprisperioder per dalside når W-form segment-tvinging er aktivert. Hver side må uavhengig produsere minst dette antallet perioder. Standard: 1"
} }
} }
}, },
@ -306,12 +310,16 @@
"data": { "data": {
"peak_price_extend_to_very_expensive": "Utvid til svært dyre intervaller", "peak_price_extend_to_very_expensive": "Utvid til svært dyre intervaller",
"peak_price_max_extension_intervals": "Maksimale utvidelsesintervaller", "peak_price_max_extension_intervals": "Maksimale utvidelsesintervaller",
"peak_price_geometric_flex": "Geometrisk fleksbonus" "peak_price_geometric_flex": "Geometrisk fleksbonus",
"peak_price_segment_forcing": "M-form segment-tvinging",
"peak_price_segment_min_periods": "Perioder per segment"
}, },
"data_description": { "data_description": {
"peak_price_extend_to_very_expensive": "Når aktivert, utvider oppdagede topprisperioder seg utover for å inkludere tilstøtende intervaller med prisnivået 'Svært dyrt'. Dette fanger opp ekstremt dyre intervaller ved kantene av oppdagede perioder.", "peak_price_extend_to_very_expensive": "Når aktivert, utvider oppdagede topprisperioder seg utover for å inkludere tilstøtende intervaller med prisnivået 'Svært dyrt'. Dette fanger opp ekstremt dyre intervaller ved kantene av oppdagede perioder.",
"peak_price_max_extension_intervals": "Maksimalt antall ekstra intervaller per side (venstre og høyre kant). Hvert intervall er 15 minutter. Eksempel: 4 intervaller = opptil 1 times utvidelse per kant. Standard: 4", "peak_price_max_extension_intervals": "Maksimalt antall ekstra intervaller per side (venstre og høyre kant). Hvert intervall er 15 minutter. Eksempel: 4 intervaller = opptil 1 times utvidelse per kant. Standard: 4",
"peak_price_geometric_flex": "Ekstra fleksprosent for intervaller som faller innenfor en oppdaget pristopp (Λ-form). Når et topp-mønster oppdages for dagen, får intervaller innen toppsonene denne ekstra toleransen, slik at periodevarslingssystemet er mer tilbøyelig til å inkludere dem. 0 = deaktivert. Standard: 0" "peak_price_geometric_flex": "Ekstra fleksprosent for intervaller som faller innenfor en oppdaget pristopp (Λ-form). Når et topp-mønster oppdages for dagen, får intervaller innen toppsonene denne ekstra toleransen, slik at periodevarslingssystemet er mer tilbøyelig til å inkludere dem. 0 = deaktivert. Standard: 0",
"peak_price_segment_forcing": "Når aktivert deles dager med M-formet priskurve (to topper adskilt av et sentralt dal) ved det sentrale dalet. Periodedetektor kjøres uavhengig for hver toppside for å sikre at hver topp får det påkrevde antallet perioder.",
"peak_price_segment_min_periods": "Minimum antall topprisperioder per toppside når M-form segment-tvinging er aktivert. Hver side må uavhengig produsere minst dette antallet perioder. Standard: 1"
} }
} }
}, },

View file

@ -245,12 +245,16 @@
"data": { "data": {
"best_price_extend_to_very_cheap": "Uitbreiden met zeer goedkope intervallen", "best_price_extend_to_very_cheap": "Uitbreiden met zeer goedkope intervallen",
"best_price_max_extension_intervals": "Maximale uitbreidingsintervallen", "best_price_max_extension_intervals": "Maximale uitbreidingsintervallen",
"best_price_geometric_flex": "Geometrische Flex Bonus" "best_price_geometric_flex": "Geometrische Flex Bonus",
"best_price_segment_forcing": "W-vorm segmentverstrekking",
"best_price_segment_min_periods": "Periodes per segment"
}, },
"data_description": { "data_description": {
"best_price_extend_to_very_cheap": "Indien ingeschakeld, breiden gedetecteerde beste-prijsperioden zich uit aan de randen om aangrenzende intervallen met prijsniveau 'Zeer goedkoop' op te nemen. Dit vangt extreem goedkope intervallen op aan de randen van gedetecteerde perioden.", "best_price_extend_to_very_cheap": "Indien ingeschakeld, breiden gedetecteerde beste-prijsperioden zich uit aan de randen om aangrenzende intervallen met prijsniveau 'Zeer goedkoop' op te nemen. Dit vangt extreem goedkope intervallen op aan de randen van gedetecteerde perioden.",
"best_price_max_extension_intervals": "Maximaal aantal extra intervallen per kant (linker en rechter rand). Elk interval is 15 minuten. Voorbeeld: 4 intervallen = maximaal 1 uur uitbreiding per rand. Standaard: 4", "best_price_max_extension_intervals": "Maximaal aantal extra intervallen per kant (linker en rechter rand). Elk interval is 15 minuten. Voorbeeld: 4 intervallen = maximaal 1 uur uitbreiding per rand. Standaard: 4",
"best_price_geometric_flex": "Extra flex percentage voor intervallen die binnen een gedetecteerde prijsdal (V-vorm) vallen. Wanneer een dal-patroon wordt gedetecteerd voor de dag, krijgen intervallen binnen de dalzone deze extra tolerantie, waardoor de periodedetector ze eerder opneemt. 0 = uitgeschakeld. Standaard: 0" "best_price_geometric_flex": "Extra flex percentage voor intervallen die binnen een gedetecteerde prijsdal (V-vorm) vallen. Wanneer een dal-patroon wordt gedetecteerd voor de dag, krijgen intervallen binnen de dalzone deze extra tolerantie, waardoor de periodedetector ze eerder opneemt. 0 = uitgeschakeld. Standaard: 0",
"best_price_segment_forcing": "Wanneer ingeschakeld worden dagen met een W-vormige prijscurve (twee dalen gescheiden door een centrale piek) gesplitst op de centrale piek. Periodedetectie wordt onafhankelijk uitgevoerd voor elke dalzijde, zodat elk dal het vereiste aantal perioden heeft.",
"best_price_segment_min_periods": "Minimaal aantal beste-prijsperioden per dalzijde wanneer W-vorm segmentverstrekking is ingeschakeld. Elke zijde moet onafhankelijk minimaal dit aantal perioden opleveren. Standaard: 1"
} }
} }
}, },
@ -306,12 +310,16 @@
"data": { "data": {
"peak_price_extend_to_very_expensive": "Uitbreiden met zeer dure intervallen", "peak_price_extend_to_very_expensive": "Uitbreiden met zeer dure intervallen",
"peak_price_max_extension_intervals": "Maximale uitbreidingsintervallen", "peak_price_max_extension_intervals": "Maximale uitbreidingsintervallen",
"peak_price_geometric_flex": "Geometrische Flex Bonus" "peak_price_geometric_flex": "Geometrische Flex Bonus",
"peak_price_segment_forcing": "M-vorm segmentverstrekking",
"peak_price_segment_min_periods": "Periodes per segment"
}, },
"data_description": { "data_description": {
"peak_price_extend_to_very_expensive": "Indien ingeschakeld, breiden gedetecteerde piekprijsperioden zich uit aan de randen om aangrenzende intervallen met prijsniveau 'Zeer duur' op te nemen. Dit vangt extreem dure intervallen op aan de randen van gedetecteerde perioden.", "peak_price_extend_to_very_expensive": "Indien ingeschakeld, breiden gedetecteerde piekprijsperioden zich uit aan de randen om aangrenzende intervallen met prijsniveau 'Zeer duur' op te nemen. Dit vangt extreem dure intervallen op aan de randen van gedetecteerde perioden.",
"peak_price_max_extension_intervals": "Maximaal aantal extra intervallen per kant (linker en rechter rand). Elk interval is 15 minuten. Voorbeeld: 4 intervallen = maximaal 1 uur uitbreiding per rand. Standaard: 4", "peak_price_max_extension_intervals": "Maximaal aantal extra intervallen per kant (linker en rechter rand). Elk interval is 15 minuten. Voorbeeld: 4 intervallen = maximaal 1 uur uitbreiding per rand. Standaard: 4",
"peak_price_geometric_flex": "Extra flex percentage voor intervallen die binnen een gedetecteerde prijspiek (Λ-vorm) vallen. Wanneer een piek-patroon wordt gedetecteerd voor de dag, krijgen intervallen binnen de piekzone deze extra tolerantie, waardoor de periodedetector ze eerder opneemt. 0 = uitgeschakeld. Standaard: 0" "peak_price_geometric_flex": "Extra flex percentage voor intervallen die binnen een gedetecteerde prijspiek (Λ-vorm) vallen. Wanneer een piek-patroon wordt gedetecteerd voor de dag, krijgen intervallen binnen de piekzone deze extra tolerantie, waardoor de periodedetector ze eerder opneemt. 0 = uitgeschakeld. Standaard: 0",
"peak_price_segment_forcing": "Wanneer ingeschakeld worden dagen met een M-vormige prijscurve (twee pieken gescheiden door een centrale dal) gesplitst op de centrale dal. Periodedetectie wordt onafhankelijk uitgevoerd voor elke piekzijde, zodat elke piek het vereiste aantal perioden heeft.",
"peak_price_segment_min_periods": "Minimaal aantal piekprijsperioden per piekzijde wanneer M-vorm segmentverstrekking is ingeschakeld. Elke zijde moet onafhankelijk minimaal dit aantal perioden opleveren. Standaard: 1"
} }
} }
}, },

View file

@ -245,12 +245,16 @@
"data": { "data": {
"best_price_extend_to_very_cheap": "Utvidga till mycket billiga intervall", "best_price_extend_to_very_cheap": "Utvidga till mycket billiga intervall",
"best_price_max_extension_intervals": "Maximalt antal utvidgningsintervall", "best_price_max_extension_intervals": "Maximalt antal utvidgningsintervall",
"best_price_geometric_flex": "Geometrisk flexbonus" "best_price_geometric_flex": "Geometrisk flexbonus",
"best_price_segment_forcing": "W-form segmenttvingning",
"best_price_segment_min_periods": "Perioder per segment"
}, },
"data_description": { "data_description": {
"best_price_extend_to_very_cheap": "När aktiverat utvidgas hittade bästa-prisperioder utåt för att inkludera angränsande intervall med prisnivån 'Mycket billig'. Detta fångar upp extremt billiga intervall vid kanterna av hittade perioder.", "best_price_extend_to_very_cheap": "När aktiverat utvidgas hittade bästa-prisperioder utåt för att inkludera angränsande intervall med prisnivån 'Mycket billig'. Detta fångar upp extremt billiga intervall vid kanterna av hittade perioder.",
"best_price_max_extension_intervals": "Maximalt antal extra intervall per sida (vänster och höger kant). Varje intervall är 15 minuter. Exempel: 4 intervall = upp till 1 timmes utvidgning per kant. Standard: 4", "best_price_max_extension_intervals": "Maximalt antal extra intervall per sida (vänster och höger kant). Varje intervall är 15 minuter. Exempel: 4 intervall = upp till 1 timmes utvidgning per kant. Standard: 4",
"best_price_geometric_flex": "Extra flexprocentandel för intervall som faller inom en detekterad prisdal (V-form). När ett dalmönster detekteras för dagen får intervall inom dalzonen denna extra tolerans, vilket gör att perioddektorn är mer benägen att inkludera dem. 0 = inaktiverad. Standard: 0" "best_price_geometric_flex": "Extra flexprocentandel för intervall som faller inom en detekterad prisdal (V-form). När ett dalmönster detekteras för dagen får intervall inom dalzonen denna extra tolerans, vilket gör att perioddektorn är mer benägen att inkludera dem. 0 = inaktiverad. Standard: 0",
"best_price_segment_forcing": "När aktiverat delas dagar med W-formad priskurva (två dalar åtskilda av en central topp) vid den centrala toppen. Periodedetektering körs oberoende för varje dalsida för att säkerställa att varje dal får det erforderliga antalet perioder.",
"best_price_segment_min_periods": "Minsta antal bästa-prisperioder per dalsida när W-form segmenttvingning är aktiverat. Varje sida måste oberoende producera minst detta antal perioder. Standard: 1"
} }
} }
}, },
@ -306,12 +310,16 @@
"data": { "data": {
"peak_price_extend_to_very_expensive": "Utvidga till mycket dyra intervall", "peak_price_extend_to_very_expensive": "Utvidga till mycket dyra intervall",
"peak_price_max_extension_intervals": "Maximalt antal utvidgningsintervall", "peak_price_max_extension_intervals": "Maximalt antal utvidgningsintervall",
"peak_price_geometric_flex": "Geometrisk flexbonus" "peak_price_geometric_flex": "Geometrisk flexbonus",
"peak_price_segment_forcing": "M-form segmenttvingning",
"peak_price_segment_min_periods": "Perioder per segment"
}, },
"data_description": { "data_description": {
"peak_price_extend_to_very_expensive": "När aktiverat utvidgas hittade topprisperioder utåt för att inkludera angränsande intervall med prisnivån 'Mycket dyr'. Detta fångar upp extremt dyra intervall vid kanterna av hittade perioder.", "peak_price_extend_to_very_expensive": "När aktiverat utvidgas hittade topprisperioder utåt för att inkludera angränsande intervall med prisnivån 'Mycket dyr'. Detta fångar upp extremt dyra intervall vid kanterna av hittade perioder.",
"peak_price_max_extension_intervals": "Maximalt antal extra intervall per sida (vänster och höger kant). Varje intervall är 15 minuter. Exempel: 4 intervall = upp till 1 timmes utvidgning per kant. Standard: 4", "peak_price_max_extension_intervals": "Maximalt antal extra intervall per sida (vänster och höger kant). Varje intervall är 15 minuter. Exempel: 4 intervall = upp till 1 timmes utvidgning per kant. Standard: 4",
"peak_price_geometric_flex": "Extra flexprocentandel för intervall som faller inom en detekterad prispeak (Λ-form). När ett peak-mönster detekteras för dagen får intervall inom peakzonen denna extra tolerans, vilket gör att perioddetektor är mer benägen att inkludera dem. 0 = inaktiverad. Standard: 0" "peak_price_geometric_flex": "Extra flexprocentandel för intervall som faller inom en detekterad prispeak (Λ-form). När ett peak-mönster detekteras för dagen får intervall inom peakzonen denna extra tolerans, vilket gör att perioddetektor är mer benägen att inkludera dem. 0 = inaktiverad. Standard: 0",
"peak_price_segment_forcing": "När aktiverat delas dagar med M-formad priskurva (två toppar åtskilda av en central dal) vid den centrala dalen. Periodedetektering körs oberoende för varje toppsida för att säkerställa att varje topp får det erforderliga antalet perioder.",
"peak_price_segment_min_periods": "Minsta antal topprisperioder per toppsida när M-form segmenttvingning är aktiverat. Varje sida måste oberoende producera minst detta antal perioder. Standard: 1"
} }
} }
}, },