mirror of
https://github.com/jpawlowski/hass.tibber_prices.git
synced 2026-03-29 21:03:40 +00:00
feat(periods): add gap tolerance for price level filters with intelligent period splitting
Implemented configurable gap tolerance (0-8 intervals) for best price and peak price level filters to prevent periods from being split by occasional level deviations. Key features: - Gap tolerance only applies to periods ≥ MIN_INTERVALS_FOR_GAP_TOLERANCE (1.5h) - Short periods (< 1.5h) use strict filtering (zero tolerance) - Dynamic minimum distance between gaps: max(2, (interval_count // max_gap_count) // 2) - 25% maximum cap on total gaps to prevent excessive outliers in long periods - Intelligent period splitting at gap clusters (2+ consecutive non-qualifying intervals) - Each sub-period independently validated with same gap tolerance rules Technical implementation: - Added CONF_BEST_PRICE_MAX_LEVEL_GAP_COUNT and CONF_PEAK_PRICE_MAX_LEVEL_GAP_COUNT constants - Added MIN_INTERVALS_FOR_GAP_TOLERANCE = 6 (1.5h minimum for gap tolerance) - Implemented _split_at_gap_clusters() for period recovery - Implemented _check_short_period_strict() for strict short-period filtering - Implemented _check_level_filter_with_gaps() with fallback splitting logic - Extracted _check_sequence_with_gap_tolerance() for reusable core validation - Enhanced _check_level_filter() to use gap-tolerant validation Configuration UI: - Added NumberSelector (0-8, slider mode) for gap count in config flow - Added translations for all 5 languages (de, en, nb, nl, sv) - Default: 0 (strict filtering, backwards compatible) Impact: Users can now configure how many occasional level deviations are acceptable within qualifying price periods. This reduces period fragmentation while maintaining meaningful price-based filtering. Long periods are protected by the 25% cap, and gap clusters trigger intelligent splitting to recover usable sub-periods.
This commit is contained in:
parent
40a335dabe
commit
817658f230
8 changed files with 346 additions and 18 deletions
|
|
@ -42,6 +42,7 @@ from .const import (
|
|||
BEST_PRICE_MAX_LEVEL_OPTIONS,
|
||||
CONF_BEST_PRICE_FLEX,
|
||||
CONF_BEST_PRICE_MAX_LEVEL,
|
||||
CONF_BEST_PRICE_MAX_LEVEL_GAP_COUNT,
|
||||
CONF_BEST_PRICE_MIN_DISTANCE_FROM_AVG,
|
||||
CONF_BEST_PRICE_MIN_PERIOD_LENGTH,
|
||||
CONF_BEST_PRICE_MIN_VOLATILITY,
|
||||
|
|
@ -51,6 +52,7 @@ from .const import (
|
|||
CONF_MIN_PERIODS_BEST,
|
||||
CONF_MIN_PERIODS_PEAK,
|
||||
CONF_PEAK_PRICE_FLEX,
|
||||
CONF_PEAK_PRICE_MAX_LEVEL_GAP_COUNT,
|
||||
CONF_PEAK_PRICE_MIN_DISTANCE_FROM_AVG,
|
||||
CONF_PEAK_PRICE_MIN_LEVEL,
|
||||
CONF_PEAK_PRICE_MIN_PERIOD_LENGTH,
|
||||
|
|
@ -66,6 +68,7 @@ from .const import (
|
|||
CONF_VOLATILITY_THRESHOLD_VERY_HIGH,
|
||||
DEFAULT_BEST_PRICE_FLEX,
|
||||
DEFAULT_BEST_PRICE_MAX_LEVEL,
|
||||
DEFAULT_BEST_PRICE_MAX_LEVEL_GAP_COUNT,
|
||||
DEFAULT_BEST_PRICE_MIN_DISTANCE_FROM_AVG,
|
||||
DEFAULT_BEST_PRICE_MIN_PERIOD_LENGTH,
|
||||
DEFAULT_BEST_PRICE_MIN_VOLATILITY,
|
||||
|
|
@ -75,6 +78,7 @@ from .const import (
|
|||
DEFAULT_MIN_PERIODS_BEST,
|
||||
DEFAULT_MIN_PERIODS_PEAK,
|
||||
DEFAULT_PEAK_PRICE_FLEX,
|
||||
DEFAULT_PEAK_PRICE_MAX_LEVEL_GAP_COUNT,
|
||||
DEFAULT_PEAK_PRICE_MIN_DISTANCE_FROM_AVG,
|
||||
DEFAULT_PEAK_PRICE_MIN_LEVEL,
|
||||
DEFAULT_PEAK_PRICE_MIN_PERIOD_LENGTH,
|
||||
|
|
@ -679,6 +683,22 @@ class TibberPricesOptionsFlowHandler(OptionsFlow):
|
|||
translation_key="price_level",
|
||||
),
|
||||
),
|
||||
vol.Optional(
|
||||
CONF_BEST_PRICE_MAX_LEVEL_GAP_COUNT,
|
||||
default=int(
|
||||
self.config_entry.options.get(
|
||||
CONF_BEST_PRICE_MAX_LEVEL_GAP_COUNT,
|
||||
DEFAULT_BEST_PRICE_MAX_LEVEL_GAP_COUNT,
|
||||
)
|
||||
),
|
||||
): NumberSelector(
|
||||
NumberSelectorConfig(
|
||||
min=0,
|
||||
max=8,
|
||||
step=1,
|
||||
mode=NumberSelectorMode.SLIDER,
|
||||
),
|
||||
),
|
||||
vol.Optional(
|
||||
CONF_ENABLE_MIN_PERIODS_BEST,
|
||||
default=self.config_entry.options.get(
|
||||
|
|
@ -811,6 +831,22 @@ class TibberPricesOptionsFlowHandler(OptionsFlow):
|
|||
translation_key="price_level",
|
||||
),
|
||||
),
|
||||
vol.Optional(
|
||||
CONF_PEAK_PRICE_MAX_LEVEL_GAP_COUNT,
|
||||
default=int(
|
||||
self.config_entry.options.get(
|
||||
CONF_PEAK_PRICE_MAX_LEVEL_GAP_COUNT,
|
||||
DEFAULT_PEAK_PRICE_MAX_LEVEL_GAP_COUNT,
|
||||
)
|
||||
),
|
||||
): NumberSelector(
|
||||
NumberSelectorConfig(
|
||||
min=0,
|
||||
max=8,
|
||||
step=1,
|
||||
mode=NumberSelectorMode.SLIDER,
|
||||
),
|
||||
),
|
||||
vol.Optional(
|
||||
CONF_ENABLE_MIN_PERIODS_PEAK,
|
||||
default=self.config_entry.options.get(
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@ CONF_BEST_PRICE_MIN_VOLATILITY = "best_price_min_volatility"
|
|||
CONF_PEAK_PRICE_MIN_VOLATILITY = "peak_price_min_volatility"
|
||||
CONF_BEST_PRICE_MAX_LEVEL = "best_price_max_level"
|
||||
CONF_PEAK_PRICE_MIN_LEVEL = "peak_price_min_level"
|
||||
CONF_BEST_PRICE_MAX_LEVEL_GAP_COUNT = "best_price_max_level_gap_count"
|
||||
CONF_PEAK_PRICE_MAX_LEVEL_GAP_COUNT = "peak_price_max_level_gap_count"
|
||||
CONF_ENABLE_MIN_PERIODS_BEST = "enable_min_periods_best"
|
||||
CONF_MIN_PERIODS_BEST = "min_periods_best"
|
||||
CONF_RELAXATION_STEP_BEST = "relaxation_step_best"
|
||||
|
|
@ -64,6 +66,9 @@ DEFAULT_BEST_PRICE_MIN_VOLATILITY = "low" # Show best price at any volatility (
|
|||
DEFAULT_PEAK_PRICE_MIN_VOLATILITY = "low" # Always show peak price (warning relevant even at low spreads)
|
||||
DEFAULT_BEST_PRICE_MAX_LEVEL = "any" # Default: show best price periods regardless of price level
|
||||
DEFAULT_PEAK_PRICE_MIN_LEVEL = "any" # Default: show peak price periods regardless of price level
|
||||
DEFAULT_BEST_PRICE_MAX_LEVEL_GAP_COUNT = 0 # Default: no tolerance for level gaps (strict filtering)
|
||||
DEFAULT_PEAK_PRICE_MAX_LEVEL_GAP_COUNT = 0 # Default: no tolerance for level gaps (strict filtering)
|
||||
MIN_INTERVALS_FOR_GAP_TOLERANCE = 6 # Minimum period length (in 15-min intervals = 1.5h) required for gap tolerance
|
||||
DEFAULT_ENABLE_MIN_PERIODS_BEST = False # Default: minimum periods feature disabled for best price
|
||||
DEFAULT_MIN_PERIODS_BEST = 2 # Default: require at least 2 best price periods (when enabled)
|
||||
DEFAULT_RELAXATION_STEP_BEST = 25 # Default: 25% of original threshold per relaxation step for best price
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ from .api import (
|
|||
from .const import (
|
||||
CONF_BEST_PRICE_FLEX,
|
||||
CONF_BEST_PRICE_MAX_LEVEL,
|
||||
CONF_BEST_PRICE_MAX_LEVEL_GAP_COUNT,
|
||||
CONF_BEST_PRICE_MIN_DISTANCE_FROM_AVG,
|
||||
CONF_BEST_PRICE_MIN_PERIOD_LENGTH,
|
||||
CONF_BEST_PRICE_MIN_VOLATILITY,
|
||||
|
|
@ -35,6 +36,7 @@ from .const import (
|
|||
CONF_MIN_PERIODS_BEST,
|
||||
CONF_MIN_PERIODS_PEAK,
|
||||
CONF_PEAK_PRICE_FLEX,
|
||||
CONF_PEAK_PRICE_MAX_LEVEL_GAP_COUNT,
|
||||
CONF_PEAK_PRICE_MIN_DISTANCE_FROM_AVG,
|
||||
CONF_PEAK_PRICE_MIN_LEVEL,
|
||||
CONF_PEAK_PRICE_MIN_PERIOD_LENGTH,
|
||||
|
|
@ -48,6 +50,7 @@ from .const import (
|
|||
CONF_VOLATILITY_THRESHOLD_VERY_HIGH,
|
||||
DEFAULT_BEST_PRICE_FLEX,
|
||||
DEFAULT_BEST_PRICE_MAX_LEVEL,
|
||||
DEFAULT_BEST_PRICE_MAX_LEVEL_GAP_COUNT,
|
||||
DEFAULT_BEST_PRICE_MIN_DISTANCE_FROM_AVG,
|
||||
DEFAULT_BEST_PRICE_MIN_PERIOD_LENGTH,
|
||||
DEFAULT_BEST_PRICE_MIN_VOLATILITY,
|
||||
|
|
@ -56,6 +59,7 @@ from .const import (
|
|||
DEFAULT_MIN_PERIODS_BEST,
|
||||
DEFAULT_MIN_PERIODS_PEAK,
|
||||
DEFAULT_PEAK_PRICE_FLEX,
|
||||
DEFAULT_PEAK_PRICE_MAX_LEVEL_GAP_COUNT,
|
||||
DEFAULT_PEAK_PRICE_MIN_DISTANCE_FROM_AVG,
|
||||
DEFAULT_PEAK_PRICE_MIN_LEVEL,
|
||||
DEFAULT_PEAK_PRICE_MIN_PERIOD_LENGTH,
|
||||
|
|
@ -68,6 +72,7 @@ from .const import (
|
|||
DEFAULT_VOLATILITY_THRESHOLD_MODERATE,
|
||||
DEFAULT_VOLATILITY_THRESHOLD_VERY_HIGH,
|
||||
DOMAIN,
|
||||
MIN_INTERVALS_FOR_GAP_TOLERANCE,
|
||||
PRICE_LEVEL_MAPPING,
|
||||
)
|
||||
from .period_utils import (
|
||||
|
|
@ -778,6 +783,242 @@ class TibberPricesDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
|||
override=level_override,
|
||||
)
|
||||
|
||||
def _split_at_gap_clusters(
|
||||
self,
|
||||
today_intervals: list[dict[str, Any]],
|
||||
level_order: int,
|
||||
min_period_length: int,
|
||||
*,
|
||||
reverse_sort: bool,
|
||||
) -> list[list[dict[str, Any]]]:
|
||||
"""
|
||||
Split intervals into sub-sequences at gap clusters.
|
||||
|
||||
A gap cluster is 2+ consecutive intervals that don't meet the level requirement.
|
||||
This allows recovering usable periods from sequences that would otherwise be rejected.
|
||||
|
||||
Args:
|
||||
today_intervals: List of price intervals for today
|
||||
level_order: Required level order from PRICE_LEVEL_MAPPING
|
||||
min_period_length: Minimum number of intervals required for a valid sub-sequence
|
||||
reverse_sort: True for peak price, False for best price
|
||||
|
||||
Returns:
|
||||
List of sub-sequences, each at least min_period_length long.
|
||||
|
||||
"""
|
||||
sub_sequences = []
|
||||
current_sequence = []
|
||||
consecutive_non_qualifying = 0
|
||||
|
||||
for interval in today_intervals:
|
||||
interval_level = PRICE_LEVEL_MAPPING.get(interval.get("level", "NORMAL"), 0)
|
||||
meets_requirement = interval_level >= level_order if reverse_sort else interval_level <= level_order
|
||||
|
||||
if meets_requirement:
|
||||
# Qualifying interval - add to current sequence
|
||||
current_sequence.append(interval)
|
||||
consecutive_non_qualifying = 0
|
||||
elif consecutive_non_qualifying == 0:
|
||||
# First non-qualifying interval (single gap) - add to current sequence
|
||||
current_sequence.append(interval)
|
||||
consecutive_non_qualifying = 1
|
||||
else:
|
||||
# Second+ consecutive non-qualifying interval = gap cluster starts
|
||||
# Save current sequence if long enough (excluding the first gap we just added)
|
||||
if len(current_sequence) - 1 >= min_period_length:
|
||||
sub_sequences.append(current_sequence[:-1]) # Exclude the first gap
|
||||
current_sequence = []
|
||||
consecutive_non_qualifying = 0
|
||||
|
||||
# Don't forget last sequence
|
||||
if len(current_sequence) >= min_period_length:
|
||||
sub_sequences.append(current_sequence)
|
||||
|
||||
return sub_sequences
|
||||
|
||||
def _check_short_period_strict(
|
||||
self,
|
||||
today_intervals: list[dict[str, Any]],
|
||||
level_order: int,
|
||||
*,
|
||||
reverse_sort: bool,
|
||||
) -> bool:
|
||||
"""
|
||||
Strict filtering for short periods (< 1.5h) without gap tolerance.
|
||||
|
||||
All intervals must meet the requirement perfectly, or at least one does
|
||||
and all others are exact matches.
|
||||
|
||||
Args:
|
||||
today_intervals: List of price intervals for today
|
||||
level_order: Required level order from PRICE_LEVEL_MAPPING
|
||||
reverse_sort: True for peak price, False for best price
|
||||
|
||||
Returns:
|
||||
True if all intervals meet requirement (with at least one qualifying), False otherwise.
|
||||
|
||||
"""
|
||||
has_qualifying = False
|
||||
for interval in today_intervals:
|
||||
interval_level = PRICE_LEVEL_MAPPING.get(interval.get("level", "NORMAL"), 0)
|
||||
meets_requirement = interval_level >= level_order if reverse_sort else interval_level <= level_order
|
||||
if meets_requirement:
|
||||
has_qualifying = True
|
||||
elif interval_level != level_order:
|
||||
# Any deviation in short periods disqualifies the entire sequence
|
||||
return False
|
||||
return has_qualifying
|
||||
|
||||
def _check_level_filter_with_gaps(
|
||||
self,
|
||||
today_intervals: list[dict[str, Any]],
|
||||
level_order: int,
|
||||
max_gap_count: int,
|
||||
*,
|
||||
reverse_sort: bool,
|
||||
) -> bool:
|
||||
"""
|
||||
Check if intervals meet level requirements with gap tolerance and minimum distance.
|
||||
|
||||
A "gap" is an interval that deviates by exactly 1 level step.
|
||||
For best price: CHEAP allows NORMAL as gap (but not EXPENSIVE).
|
||||
For peak price: EXPENSIVE allows NORMAL as gap (but not CHEAP).
|
||||
|
||||
Gap tolerance is only applied to periods with at least MIN_INTERVALS_FOR_GAP_TOLERANCE
|
||||
intervals (1.5h). Shorter periods use strict filtering (zero tolerance).
|
||||
|
||||
Between gaps, there must be a minimum number of "good" intervals to prevent
|
||||
periods that are mostly interrupted by gaps.
|
||||
|
||||
Args:
|
||||
today_intervals: List of price intervals for today
|
||||
level_order: Required level order from PRICE_LEVEL_MAPPING
|
||||
max_gap_count: Maximum total gaps allowed
|
||||
reverse_sort: True for peak price, False for best price
|
||||
|
||||
Returns:
|
||||
True if any qualifying sequence exists, False otherwise.
|
||||
|
||||
"""
|
||||
if not today_intervals:
|
||||
return False
|
||||
|
||||
interval_count = len(today_intervals)
|
||||
|
||||
# Periods shorter than MIN_INTERVALS_FOR_GAP_TOLERANCE (1.5h) use strict filtering
|
||||
if interval_count < MIN_INTERVALS_FOR_GAP_TOLERANCE:
|
||||
return self._check_short_period_strict(today_intervals, level_order, reverse_sort=reverse_sort)
|
||||
|
||||
# Try normal gap tolerance check first
|
||||
if self._check_sequence_with_gap_tolerance(
|
||||
today_intervals, level_order, max_gap_count, reverse_sort=reverse_sort
|
||||
):
|
||||
return True
|
||||
|
||||
# Normal check failed - try splitting at gap clusters as fallback
|
||||
# Get minimum period length from config (convert minutes to intervals)
|
||||
if reverse_sort:
|
||||
min_period_minutes = self.config_entry.options.get(
|
||||
CONF_PEAK_PRICE_MIN_PERIOD_LENGTH,
|
||||
DEFAULT_PEAK_PRICE_MIN_PERIOD_LENGTH,
|
||||
)
|
||||
else:
|
||||
min_period_minutes = self.config_entry.options.get(
|
||||
CONF_BEST_PRICE_MIN_PERIOD_LENGTH,
|
||||
DEFAULT_BEST_PRICE_MIN_PERIOD_LENGTH,
|
||||
)
|
||||
|
||||
min_period_intervals = min_period_minutes // 15
|
||||
|
||||
sub_sequences = self._split_at_gap_clusters(
|
||||
today_intervals,
|
||||
level_order,
|
||||
min_period_intervals,
|
||||
reverse_sort=reverse_sort,
|
||||
)
|
||||
|
||||
# Check if ANY sub-sequence passes gap tolerance
|
||||
for sub_seq in sub_sequences:
|
||||
if self._check_sequence_with_gap_tolerance(sub_seq, level_order, max_gap_count, reverse_sort=reverse_sort):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _check_sequence_with_gap_tolerance(
|
||||
self,
|
||||
intervals: list[dict[str, Any]],
|
||||
level_order: int,
|
||||
max_gap_count: int,
|
||||
*,
|
||||
reverse_sort: bool,
|
||||
) -> bool:
|
||||
"""
|
||||
Check if a single interval sequence passes gap tolerance requirements.
|
||||
|
||||
This is the core gap tolerance logic extracted for reuse with sub-sequences.
|
||||
|
||||
Args:
|
||||
intervals: List of price intervals to check
|
||||
level_order: Required level order from PRICE_LEVEL_MAPPING
|
||||
max_gap_count: Maximum total gaps allowed
|
||||
reverse_sort: True for peak price, False for best price
|
||||
|
||||
Returns:
|
||||
True if sequence meets all gap tolerance requirements, False otherwise.
|
||||
|
||||
"""
|
||||
if not intervals:
|
||||
return False
|
||||
|
||||
interval_count = len(intervals)
|
||||
|
||||
# Calculate minimum distance between gaps dynamically.
|
||||
# Shorter periods require relatively larger distances.
|
||||
# Longer periods allow gaps closer together.
|
||||
# Distance is never less than 2 intervals between gaps.
|
||||
min_distance_between_gaps = max(2, (interval_count // max_gap_count) // 2)
|
||||
|
||||
# Limit total gaps to max 25% of period length to prevent too many outliers.
|
||||
# This ensures periods remain predominantly "good" even when long.
|
||||
effective_max_gaps = min(max_gap_count, interval_count // 4)
|
||||
|
||||
gap_count = 0
|
||||
consecutive_good_count = 0
|
||||
has_qualifying_interval = False
|
||||
|
||||
for interval in intervals:
|
||||
interval_level = PRICE_LEVEL_MAPPING.get(interval.get("level", "NORMAL"), 0)
|
||||
|
||||
# Check if interval meets the strict requirement
|
||||
meets_requirement = interval_level >= level_order if reverse_sort else interval_level <= level_order
|
||||
|
||||
if meets_requirement:
|
||||
has_qualifying_interval = True
|
||||
consecutive_good_count += 1
|
||||
continue
|
||||
|
||||
# Check if this is a tolerable gap (exactly 1 step deviation)
|
||||
is_tolerable_gap = interval_level == level_order - 1 if reverse_sort else interval_level == level_order + 1
|
||||
|
||||
if is_tolerable_gap:
|
||||
# If we already had gaps, check minimum distance
|
||||
if gap_count > 0 and consecutive_good_count < min_distance_between_gaps:
|
||||
# Not enough "good" intervals between gaps
|
||||
return False
|
||||
|
||||
gap_count += 1
|
||||
if gap_count > effective_max_gaps:
|
||||
return False
|
||||
|
||||
# Reset counter for next gap
|
||||
consecutive_good_count = 0
|
||||
else:
|
||||
# Too far from required level (more than 1 step deviation)
|
||||
return False
|
||||
|
||||
return has_qualifying_interval
|
||||
|
||||
def _check_level_filter(
|
||||
self,
|
||||
price_info: dict[str, Any],
|
||||
|
|
@ -786,7 +1027,10 @@ class TibberPricesDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
|||
override: str | None = None,
|
||||
) -> bool:
|
||||
"""
|
||||
Check if today has any intervals that meet the level requirement.
|
||||
Check if today has any intervals that meet the level requirement with gap tolerance.
|
||||
|
||||
Gap tolerance allows a configurable number of intervals within a qualifying sequence
|
||||
to deviate by one level step (e.g., CHEAP allows NORMAL, but not EXPENSIVE).
|
||||
|
||||
Args:
|
||||
price_info: Price information dict with today data
|
||||
|
|
@ -795,7 +1039,8 @@ class TibberPricesDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
|||
override: Optional override value (e.g., "any" to disable filter)
|
||||
|
||||
Returns:
|
||||
True if ANY interval meets the level requirement, False otherwise.
|
||||
True if ANY sequence of intervals meets the level requirement
|
||||
(considering gap tolerance), False otherwise.
|
||||
|
||||
"""
|
||||
# Use override if provided
|
||||
|
|
@ -825,10 +1070,23 @@ class TibberPricesDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
|||
if not today_intervals:
|
||||
return True # If no data, don't filter
|
||||
|
||||
# Check if ANY interval today meets the level requirement
|
||||
# Get gap tolerance configuration
|
||||
if reverse_sort:
|
||||
max_gap_count = self.config_entry.options.get(
|
||||
CONF_PEAK_PRICE_MAX_LEVEL_GAP_COUNT,
|
||||
DEFAULT_PEAK_PRICE_MAX_LEVEL_GAP_COUNT,
|
||||
)
|
||||
else:
|
||||
max_gap_count = self.config_entry.options.get(
|
||||
CONF_BEST_PRICE_MAX_LEVEL_GAP_COUNT,
|
||||
DEFAULT_BEST_PRICE_MAX_LEVEL_GAP_COUNT,
|
||||
)
|
||||
|
||||
# Note: level_config is lowercase from selector, but PRICE_LEVEL_MAPPING uses uppercase
|
||||
level_order = PRICE_LEVEL_MAPPING.get(level_config.upper(), 0)
|
||||
|
||||
# If gap tolerance is 0, use simple ANY check (backwards compatible)
|
||||
if max_gap_count == 0:
|
||||
if reverse_sort:
|
||||
# Peak price: level >= min_level (show if ANY interval is expensive enough)
|
||||
return any(
|
||||
|
|
@ -837,7 +1095,16 @@ class TibberPricesDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
|||
)
|
||||
# Best price: level <= max_level (show if ANY interval is cheap enough)
|
||||
return any(
|
||||
PRICE_LEVEL_MAPPING.get(interval.get("level", "NORMAL"), 0) <= level_order for interval in today_intervals
|
||||
PRICE_LEVEL_MAPPING.get(interval.get("level", "NORMAL"), 0) <= level_order
|
||||
for interval in today_intervals
|
||||
)
|
||||
|
||||
# Use gap-tolerant check
|
||||
return self._check_level_filter_with_gaps(
|
||||
today_intervals,
|
||||
level_order,
|
||||
max_gap_count,
|
||||
reverse_sort=reverse_sort,
|
||||
)
|
||||
|
||||
def _calculate_periods_for_price_info(self, price_info: dict[str, Any]) -> dict[str, Any]:
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@
|
|||
"best_price_min_distance_from_avg": "Mindestabstand: Erforderlich unter dem Tagesdurchschnitt",
|
||||
"best_price_min_volatility": "Mindest-Volatilitätsfilter",
|
||||
"best_price_max_level": "Preisniveau-Filter (Optional)",
|
||||
"best_price_max_level_gap_count": "Lückentoleranz für Niveaufilter",
|
||||
"enable_min_periods_best": "Mindestanzahl Perioden anstreben",
|
||||
"min_periods_best": "Mindestanzahl Perioden",
|
||||
"relaxation_step_best": "Lockerungsschritt"
|
||||
|
|
@ -113,6 +114,7 @@
|
|||
"data_description": {
|
||||
"best_price_min_volatility": "Zeigt Bestpreis-Perioden nur an, wenn die interne Preisvolatilität der Periode (Preisspanne innerhalb der Periode) mindestens diesem Level entspricht. Standard: 'Niedrig' (zeigt Perioden mit beliebigem Volatilitätslevel) - ermöglicht das Finden günstiger Perioden auch wenn die Preise stabil sind. Wähle 'Moderat'/'Hoch' um nur Perioden mit signifikanten Preisschwankungen anzuzeigen, was auf dynamischere Preismöglichkeiten hinweisen kann.",
|
||||
"best_price_max_level": "Zeigt Bestpreis-Perioden nur an, wenn sie Intervalle mit Preisniveaus ≤ dem gewählten Wert enthalten. Beispiel: Wahl von 'Günstig' bedeutet, dass die Periode mindestens ein 'SEHR_GÜNSTIG' oder 'GÜNSTIG' Intervall haben muss. Dies stellt sicher, dass 'Bestpreis'-Perioden nicht nur relativ günstig für den Tag sind, sondern tatsächlich günstig in absoluten Zahlen. Wähle 'Beliebig' um Bestpreise unabhängig vom absoluten Preisniveau anzuzeigen.",
|
||||
"best_price_max_level_gap_count": "Maximale Anzahl aufeinanderfolgender Intervalle, die exakt um eine Niveaustufe vom geforderten Level abweichen dürfen. Beispiel: Bei Filter 'Günstig' und Lückentoleranz 1 wird die Sequenz 'GÜNSTIG, GÜNSTIG, NORMAL, GÜNSTIG' akzeptiert (NORMAL ist eine Stufe über GÜNSTIG). Dies verhindert, dass Perioden durch gelegentliche Niveau-Abweichungen aufgespalten werden. Standard: 0 (strenge Filterung, keine Toleranz).",
|
||||
"enable_min_periods_best": "Wenn aktiviert, werden Filter schrittweise gelockert, falls nicht genug Perioden gefunden wurden. Dies versucht die gewünschte Mindestanzahl zu erreichen, was dazu führen kann, dass auch weniger optimale Zeiträume als Bestpreis-Perioden markiert werden.",
|
||||
"min_periods_best": "Mindestanzahl an Bestpreis-Perioden, die pro Tag angestrebt werden. Filter werden schrittweise gelockert, um diese Anzahl zu erreichen. Nur aktiv, wenn 'Mindestanzahl Perioden anstreben' aktiviert ist. Standard: 1",
|
||||
"relaxation_step_best": "Prozentsatz des ursprünglichen Flexibilitätsschwellwerts, der pro Lockerungsschritt addiert wird. Beispiel: Bei 15% Flexibilität und 25% Schrittgröße werden 15%, 18,75%, 22,5% usw. versucht. Höhere Werte bedeuten schnellere Lockerung, aber geringere Präzision."
|
||||
|
|
@ -128,6 +130,7 @@
|
|||
"peak_price_min_distance_from_avg": "Mindestabstand: Erforderlich über dem Tagesdurchschnitt",
|
||||
"peak_price_min_volatility": "Mindest-Volatilitätsfilter",
|
||||
"peak_price_min_level": "Preisniveau-Filter (Optional)",
|
||||
"peak_price_max_level_gap_count": "Lückentoleranz für Niveaufilter",
|
||||
"enable_min_periods_peak": "Mindestanzahl Perioden anstreben",
|
||||
"min_periods_peak": "Mindestanzahl Perioden",
|
||||
"relaxation_step_peak": "Lockerungsschritt"
|
||||
|
|
@ -135,6 +138,7 @@
|
|||
"data_description": {
|
||||
"peak_price_min_volatility": "Zeigt Spitzenpreis-Perioden nur an, wenn die interne Preisvolatilität der Periode (Preisspanne innerhalb der Periode) mindestens diesem Level entspricht. Standard: 'Niedrig' (zeigt Perioden mit beliebigem Volatilitätslevel) - ermöglicht das Identifizieren teurer Perioden auch wenn die Preise stabil sind. Wähle 'Moderat'/'Hoch' um nur Perioden mit signifikanten Preisschwankungen anzuzeigen, was auf dringenderen Bedarf hinweisen kann, diese Zeiten zu vermeiden.",
|
||||
"peak_price_min_level": "Zeigt Spitzenpreis-Perioden nur an, wenn sie Intervalle mit Preisniveaus ≥ dem gewählten Wert enthalten. Beispiel: Wahl von 'Teuer' bedeutet, dass die Periode mindestens ein 'TEUER' oder 'SEHR_TEUER' Intervall haben muss. Dies stellt sicher, dass 'Spitzenpreis'-Perioden nicht nur relativ teuer für den Tag sind, sondern tatsächlich teuer in absoluten Zahlen. Wähle 'Beliebig' um Spitzenpreise unabhängig vom absoluten Preisniveau anzuzeigen.",
|
||||
"peak_price_max_level_gap_count": "Maximale Anzahl aufeinanderfolgender Intervalle, die exakt um eine Niveaustufe vom geforderten Level abweichen dürfen. Beispiel: Bei Filter 'Teuer' und Lückentoleranz 2 wird die Sequenz 'TEUER, NORMAL, NORMAL, TEUER' akzeptiert (NORMAL ist eine Stufe unter TEUER). Dies verhindert, dass Perioden durch gelegentliche Niveau-Abweichungen aufgespalten werden. Standard: 0 (strenge Filterung, keine Toleranz).",
|
||||
"enable_min_periods_peak": "Wenn aktiviert, werden Filter schrittweise gelockert, falls nicht genug Perioden gefunden wurden. Dies versucht die gewünschte Mindestanzahl zu erreichen, um sicherzustellen, dass du auch an Tagen mit ungewöhnlichen Preismustern vor teuren Perioden gewarnt wirst.",
|
||||
"min_periods_peak": "Mindestanzahl an Spitzenpreis-Perioden, die pro Tag angestrebt werden. Filter werden schrittweise gelockert, um diese Anzahl zu erreichen. Nur aktiv, wenn 'Mindestanzahl Perioden anstreben' aktiviert ist. Standard: 1",
|
||||
"relaxation_step_peak": "Prozentsatz des ursprünglichen Flexibilitätsschwellwerts, der pro Lockerungsschritt addiert wird. Beispiel: Bei -15% Flexibilität und 25% Schrittgröße werden -15%, -18,75%, -22,5% usw. versucht. Höhere Werte bedeuten schnellere Lockerung, aber geringere Präzision."
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@
|
|||
"best_price_min_distance_from_avg": "Minimum Distance: Required below daily average",
|
||||
"best_price_min_volatility": "Minimum Volatility Filter",
|
||||
"best_price_max_level": "Price Level Filter (Optional)",
|
||||
"best_price_max_level_gap_count": "Level Filter Gap Tolerance",
|
||||
"enable_min_periods_best": "Try to Achieve Minimum Period Count",
|
||||
"min_periods_best": "Minimum Periods Required",
|
||||
"relaxation_step_best": "Filter Relaxation Step Size"
|
||||
|
|
@ -113,6 +114,7 @@
|
|||
"data_description": {
|
||||
"best_price_min_volatility": "Only show best price periods when the period's internal price volatility (spread within the period) meets or exceeds this level. Default: 'Low' (show periods with any volatility level) - allows finding cheap periods even if prices are stable. Select 'Moderate'/'High' to only show periods with significant price variations, which may indicate more dynamic pricing opportunities.",
|
||||
"best_price_max_level": "Only show best price periods if they contain intervals with price levels ≤ selected value. For example, selecting 'Cheap' means the period must have at least one 'VERY_CHEAP' or 'CHEAP' interval. This ensures 'best price' periods are not just relatively cheap for the day, but actually cheap in absolute terms. Select 'Any' to show best prices regardless of their absolute price level.",
|
||||
"best_price_max_level_gap_count": "Maximum number of consecutive intervals allowed that deviate by exactly one level step from the required level. For example: with 'Cheap' filter and gap count 1, a sequence 'CHEAP, CHEAP, NORMAL, CHEAP' is accepted (NORMAL is one step above CHEAP). This prevents periods from being split by occasional level deviations. Default: 0 (strict filtering, no tolerance).",
|
||||
"enable_min_periods_best": "When enabled, filters will be gradually relaxed if not enough periods are found. This attempts to reach the desired minimum number of periods, which may include less optimal time windows as best-price periods.",
|
||||
"min_periods_best": "Minimum number of best price periods to aim for per day. Filters will be relaxed step-by-step to try achieving this count. Only active when 'Try to Achieve Minimum Period Count' is enabled. Default: 1",
|
||||
"relaxation_step_best": "Percentage of the original flexibility threshold to add per relaxation step. For example: with 15% flexibility and 25% step size, filters will try 15%, 18.75%, 22.5%, etc. Higher values mean faster relaxation but less precision."
|
||||
|
|
@ -128,6 +130,7 @@
|
|||
"peak_price_min_distance_from_avg": "Minimum Distance: Required above daily average",
|
||||
"peak_price_min_volatility": "Minimum Volatility Filter",
|
||||
"peak_price_min_level": "Price Level Filter (Optional)",
|
||||
"peak_price_max_level_gap_count": "Level Filter Gap Tolerance",
|
||||
"enable_min_periods_peak": "Try to Achieve Minimum Period Count",
|
||||
"min_periods_peak": "Minimum Periods Required",
|
||||
"relaxation_step_peak": "Filter Relaxation Step Size"
|
||||
|
|
@ -135,6 +138,7 @@
|
|||
"data_description": {
|
||||
"peak_price_min_volatility": "Only show peak price periods when the period's internal price volatility (spread within the period) meets or exceeds this level. Default: 'Low' (show periods with any volatility level) - allows identifying expensive periods even if prices are stable. Select 'Moderate'/'High' to only show periods with significant price variations, which may indicate more urgent need to avoid these times.",
|
||||
"peak_price_min_level": "Only show peak price periods if they contain intervals with price levels ≥ selected value. For example, selecting 'Expensive' means the period must have at least one 'EXPENSIVE' or 'VERY_EXPENSIVE' interval. This ensures 'peak price' periods are not just relatively expensive for the day, but actually expensive in absolute terms. Select 'Any' to show peak prices regardless of their absolute price level.",
|
||||
"peak_price_max_level_gap_count": "Maximum number of consecutive intervals allowed that deviate by exactly one level step from the required level. For example: with 'Expensive' filter and gap count 2, a sequence 'EXPENSIVE, NORMAL, NORMAL, EXPENSIVE' is accepted (NORMAL is one step below EXPENSIVE). This prevents periods from being split by occasional level deviations. Default: 0 (strict filtering, no tolerance).",
|
||||
"enable_min_periods_peak": "When enabled, filters will be gradually relaxed if not enough periods are found. This attempts to reach the desired minimum number of periods to ensure you're warned about expensive periods even on days with unusual price patterns.",
|
||||
"min_periods_peak": "Minimum number of peak price periods to aim for per day. Filters will be relaxed step-by-step to try achieving this count. Only active when 'Try to Achieve Minimum Period Count' is enabled. Default: 1",
|
||||
"relaxation_step_peak": "Percentage of the original flexibility threshold to add per relaxation step. For example: with -15% flexibility and 25% step size, filters will try -15%, -18.75%, -22.5%, etc. Higher values mean faster relaxation but less precision."
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@
|
|||
"best_price_min_distance_from_avg": "Minimumsavstand: Påkrevd % under daglig gjennomsnitt",
|
||||
"best_price_min_volatility": "Minimum volatilitetsfilter",
|
||||
"best_price_max_level": "Prisnivåfilter (valgfritt)",
|
||||
"best_price_max_level_gap_count": "Gaptoleranse for nivåfilter",
|
||||
"enable_min_periods_best": "Prøv å oppnå minimum antall perioder",
|
||||
"min_periods_best": "Minimum antall perioder",
|
||||
"relaxation_step_best": "Avslappingstrinn"
|
||||
|
|
@ -115,7 +116,8 @@
|
|||
"best_price_max_level": "Vis kun beste prisperioder hvis de inneholder intervaller med prisnivåer ≤ valgt verdi. For eksempel: å velge 'Billig' betyr at perioden må ha minst ett 'VELDIG_BILLIG' eller 'BILLIG' intervall. Dette sikrer at 'beste pris'-perioder ikke bare er relativt billige for dagen, men faktisk billige i absolutte tall. Velg 'Alle' for å vise beste priser uavhengig av deres absolutte prisnivå.",
|
||||
"enable_min_periods_best": "Når aktivert vil filtrene gradvis bli lempeligere hvis det ikke blir funnet nok perioder. Dette forsøker å nå ønsket minimum antall perioder, noe som kan føre til at mindre optimale tidsrom blir markert som beste-pris-perioder.",
|
||||
"min_periods_best": "Minimum antall beste-pris-perioder å sikte mot per dag. Filtre vil bli lempet trinn for trinn for å prøve å oppnå dette antallet. Kun aktiv når 'Prøv å oppnå minimum antall perioder' er aktivert. Standard: 1",
|
||||
"relaxation_step_best": "Prosentandel av den opprinnelige fleksibilitetsterskealen som legges til per avslappingstrinn. For eksempel: med 15% fleksibilitet og 25% trinnstørrelse vil filtrene prøve 15%, 18,75%, 22,5%, osv. Høyere verdier betyr raskere avslapping men mindre presisjon."
|
||||
"relaxation_step_best": "Prosentandel av den opprinnelige fleksibilitetsterskealen som legges til per avslappingstrinn. For eksempel: med 15% fleksibilitet og 25% trinnstørrelse vil filtrene prøve 15%, 18,75%, 22,5%, osv. Høyere verdier betyr raskere avslapping men mindre presisjon.",
|
||||
"best_price_max_level_gap_count": "Maksimalt antall påfølgende intervaller som kan avvike med nøyaktig ett nivåtrinn fra det nødvendige nivået. For eksempel: med 'Billig' filter og gapantall 1, aksepteres sekvensen 'BILLIG, BILLIG, NORMAL, BILLIG' (NORMAL er ett trinn over BILLIG). Dette forhindrer at perioder blir delt opp av tilfeldige nivåavvik. Standard: 0 (streng filtrering, ingen toleranse)."
|
||||
},
|
||||
"submit": "Neste til steg 5"
|
||||
},
|
||||
|
|
@ -128,6 +130,7 @@
|
|||
"peak_price_min_distance_from_avg": "Minimumsavstand: Påkrevd % over daglig gjennomsnitt",
|
||||
"peak_price_min_volatility": "Minimum volatilitetsfilter",
|
||||
"peak_price_min_level": "Prisnivåfilter (valgfritt)",
|
||||
"peak_price_max_level_gap_count": "Gaptoleranse for nivåfilter",
|
||||
"enable_min_periods_peak": "Prøv å oppnå minimum antall perioder",
|
||||
"min_periods_peak": "Minimum antall perioder",
|
||||
"relaxation_step_peak": "Avslappingstrinn"
|
||||
|
|
@ -137,7 +140,8 @@
|
|||
"peak_price_min_level": "Vis kun topprisperioder hvis de inneholder intervaller med prisnivåer ≥ valgt verdi. For eksempel: å velge 'Dyr' betyr at perioden må ha minst ett 'DYR' eller 'VELDIG_DYR' intervall. Dette sikrer at 'topppris'-perioder ikke bare er relativt dyre for dagen, men faktisk dyre i absolutte tall. Velg 'Alle' for å vise topppriser uavhengig av deres absolutte prisnivå.",
|
||||
"enable_min_periods_peak": "Når aktivert vil filtrene gradvis bli lempeligere hvis det ikke blir funnet nok perioder. Dette forsøker å nå ønsket minimum antall perioder for å sikre at du blir advart om dyre perioder selv på dager med uvanlige prismønstre.",
|
||||
"min_periods_peak": "Minimum antall topp-pris-perioder å sikte mot per dag. Filtre vil bli lempet trinn for trinn for å prøve å oppnå dette antallet. Kun aktiv når 'Prøv å oppnå minimum antall perioder' er aktivert. Standard: 1",
|
||||
"relaxation_step_peak": "Prosentandel av den opprinnelige fleksibilitetsterskealen som legges til per avslappingstrinn. For eksempel: med -15% fleksibilitet og 25% trinnstørrelse vil filtrene prøve -15%, -18,75%, -22,5%, osv. Høyere verdier betyr raskere avslapping men mindre presisjon."
|
||||
"relaxation_step_peak": "Prosentandel av den opprinnelige fleksibilitetsterskealen som legges til per avslappingstrinn. For eksempel: med -15% fleksibilitet og 25% trinnstørrelse vil filtrene prøve -15%, -18,75%, -22,5%, osv. Høyere verdier betyr raskere avslapping men mindre presisjon.",
|
||||
"peak_price_max_level_gap_count": "Maksimalt antall påfølgende intervaller som kan avvike med nøyaktig ett nivåtrinn fra det nødvendige nivået. For eksempel: med 'Dyr' filter og gapantall 2, aksepteres sekvensen 'DYR, NORMAL, NORMAL, DYR' (NORMAL er ett trinn under DYR). Dette forhindrer at perioder blir delt opp av tilfeldige nivåavvik. Standard: 0 (streng filtrering, ingen toleranse)."
|
||||
},
|
||||
"submit": "Neste til steg 6"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@
|
|||
"best_price_min_distance_from_avg": "Minimale afstand: Vereist % onder dagelijks gemiddelde",
|
||||
"best_price_min_volatility": "Minimum volatiliteitsfilter",
|
||||
"best_price_max_level": "Prijsniveaufilter (Optioneel)",
|
||||
"best_price_max_level_gap_count": "Gaptolerantie voor niveaufilter",
|
||||
"enable_min_periods_best": "Probeer minimum aantal periodes te bereiken",
|
||||
"min_periods_best": "Minimum aantal periodes",
|
||||
"relaxation_step_best": "Ontspanningsstap"
|
||||
|
|
@ -115,7 +116,8 @@
|
|||
"best_price_max_level": "Toon alleen beste prijsperiodes als ze intervallen bevatten met prijsniveaus ≤ geselecteerde waarde. Bijvoorbeeld: selecteren van 'Goedkoop' betekent dat de periode minstens één 'ZEER_GOEDKOOP' of 'GOEDKOOP' interval moet hebben. Dit zorgt ervoor dat 'beste prijs'-periodes niet alleen relatief goedkoop zijn voor de dag, maar daadwerkelijk goedkoop in absolute termen. Selecteer 'Alle' om beste prijzen te tonen ongeacht hun absolute prijsniveau.",
|
||||
"enable_min_periods_best": "Wanneer ingeschakeld worden filters geleidelijk versoepeld als er niet genoeg periodes worden gevonden. Dit probeert het gewenste minimum aantal periodes te bereiken om ervoor te zorgen dat je kansen hebt om van lage prijzen te profiteren, zelfs op dagen met ongebruikelijke prijspatronen.",
|
||||
"min_periods_best": "Minimum aantal beste prijsperiodes om naar te streven per dag. Filters worden stap voor stap versoepeld om dit aantal te proberen bereiken. Alleen actief wanneer 'Probeer minimum aantal periodes te bereiken' is ingeschakeld. Standaard: 1",
|
||||
"relaxation_step_best": "Percentage van de oorspronkelijke flexibiliteitsdrempel om toe te voegen per ontspanningsstap. Bijvoorbeeld: met 15% flexibiliteit en 25% stapgrootte zullen de filters 15%, 18,75%, 22,5%, enz. proberen. Hogere waarden betekenen snellere ontspanning maar minder precisie."
|
||||
"relaxation_step_best": "Percentage van de oorspronkelijke flexibiliteitsdrempel om toe te voegen per ontspanningsstap. Bijvoorbeeld: met 15% flexibiliteit en 25% stapgrootte zullen de filters 15%, 18,75%, 22,5%, enz. proberen. Hogere waarden betekenen snellere ontspanning maar minder precisie.",
|
||||
"best_price_max_level_gap_count": "Maximum aantal opeenvolgende intervallen dat precies één niveaustap mag afwijken van het vereiste niveau. Bijvoorbeeld: met 'Goedkoop' filter en gaptelling 1 wordt de reeks 'GOEDKOOP, GOEDKOOP, NORMAAL, GOEDKOOP' geaccepteerd (NORMAAL is één stap boven GOEDKOOP). Dit voorkomt dat periodes worden opgesplitst door incidentele niveauafwijkingen. Standaard: 0 (strikte filtering, geen tolerantie)."
|
||||
},
|
||||
"submit": "Volgende naar stap 5"
|
||||
},
|
||||
|
|
@ -128,6 +130,7 @@
|
|||
"peak_price_min_distance_from_avg": "Minimale afstand: Vereist % boven dagelijks gemiddelde",
|
||||
"peak_price_min_volatility": "Minimum volatiliteitsfilter",
|
||||
"peak_price_min_level": "Prijsniveaufilter (Optioneel)",
|
||||
"peak_price_max_level_gap_count": "Gaptolerantie voor niveaufilter",
|
||||
"enable_min_periods_peak": "Probeer minimum aantal periodes te bereiken",
|
||||
"min_periods_peak": "Minimum aantal periodes",
|
||||
"relaxation_step_peak": "Ontspanningsstap"
|
||||
|
|
@ -137,7 +140,8 @@
|
|||
"peak_price_min_level": "Toon alleen piekprijsperiodes als ze intervallen bevatten met prijsniveaus ≥ geselecteerde waarde. Bijvoorbeeld: selecteren van 'Duur' betekent dat de periode minstens één 'DUUR' of 'ZEER_DUUR' interval moet hebben. Dit zorgt ervoor dat 'piekprijs'-periodes niet alleen relatief duur zijn voor de dag, maar daadwerkelijk duur in absolute termen. Selecteer 'Alle' om piekprijzen te tonen ongeacht hun absolute prijsniveau.",
|
||||
"enable_min_periods_peak": "Wanneer ingeschakeld worden filters geleidelijk versoepeld als er niet genoeg periodes worden gevonden. Dit probeert het gewenste minimum aantal periodes te bereiken om ervoor te zorgen dat je wordt gewaarschuwd voor dure periodes, zelfs op dagen met ongebruikelijke prijspatronen.",
|
||||
"min_periods_peak": "Minimum aantal piekprijsperiodes om naar te streven per dag. Filters worden stap voor stap versoepeld om dit aantal te proberen bereiken. Alleen actief wanneer 'Probeer minimum aantal periodes te bereiken' is ingeschakeld. Standaard: 1",
|
||||
"relaxation_step_peak": "Percentage van de oorspronkelijke flexibiliteitsdrempel om toe te voegen per ontspanningsstap. Bijvoorbeeld: met -15% flexibiliteit en 25% stapgrootte zullen de filters -15%, -18,75%, -22,5%, enz. proberen. Hogere waarden betekenen snellere ontspanning maar minder precisie."
|
||||
"relaxation_step_peak": "Percentage van de oorspronkelijke flexibiliteitsdrempel om toe te voegen per ontspanningsstap. Bijvoorbeeld: met -15% flexibiliteit en 25% stapgrootte zullen de filters -15%, -18,75%, -22,5%, enz. proberen. Hogere waarden betekenen snellere ontspanning maar minder precisie.",
|
||||
"peak_price_max_level_gap_count": "Maximum aantal opeenvolgende intervallen dat precies één niveaustap mag afwijken van het vereiste niveau. Bijvoorbeeld: met 'Duur' filter en gaptelling 2 wordt de reeks 'DUUR, NORMAAL, NORMAAL, DUUR' geaccepteerd (NORMAAL is één stap onder DUUR). Dit voorkomt dat periodes worden opgesplitst door incidentele niveauafwijkingen. Standaard: 0 (strikte filtering, geen tolerantie)."
|
||||
},
|
||||
"submit": "Volgende naar stap 6"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@
|
|||
"best_price_min_distance_from_avg": "Minimiavstånd: Krävd % under dagligt genomsnitt",
|
||||
"best_price_min_volatility": "Minimum volatilitetsfilter",
|
||||
"best_price_max_level": "Prisnivåfilter (Valfritt)",
|
||||
"best_price_max_level_gap_count": "Gaptolerens för nivåfilter",
|
||||
"enable_min_periods_best": "Försök uppnå minsta antal perioder",
|
||||
"min_periods_best": "Minsta antal perioder",
|
||||
"relaxation_step_best": "Avslappningssteg"
|
||||
|
|
@ -115,7 +116,8 @@
|
|||
"best_price_max_level": "Visa endast bästa prisperioder om de innehåller intervall med prisnivåer ≤ valt värde. Till exempel: att välja 'Billigt' betyder att perioden måste ha minst ett 'MYCKET_BILLIGT' eller 'BILLIGT' intervall. Detta säkerställer att 'bästa pris'-perioder inte bara är relativt billiga för dagen, utan faktiskt billiga i absoluta tal. Välj 'Alla' för att visa bästa priser oavsett deras absoluta prisnivå.",
|
||||
"enable_min_periods_best": "När aktiverad kommer filtren att gradvis luckras upp om inte tillräckligt många perioder hittas. Detta försöker uppnå det önskade minsta antalet perioder för att säkerställa att du har möjligheter att dra nytta av låga priser även på dagar med ovanliga prismönster.",
|
||||
"min_periods_best": "Minsta antal bästa prisperioder att sträva efter per dag. Filtren kommer att luckras upp steg för steg för att försöka uppnå detta antal. Endast aktiv när 'Försök uppnå minsta antal perioder' är aktiverad. Standard: 1",
|
||||
"relaxation_step_best": "Procentandel av den ursprungliga flexibilitetströskeln att lägga till per avslappningssteg. Till exempel: med 15% flexibilitet och 25% stegstorlek kommer filtren att prova 15%, 18,75%, 22,5%, osv. Högre värden innebär snabbare avslappning men mindre precision."
|
||||
"relaxation_step_best": "Procentandel av den ursprungliga flexibilitetströskeln att lägga till per avslappningssteg. Till exempel: med 15% flexibilitet och 25% stegstorlek kommer filtren att prova 15%, 18,75%, 22,5%, osv. Högre värden innebär snabbare avslappning men mindre precision.",
|
||||
"best_price_max_level_gap_count": "Maximalt antal på varandra följande intervaller som får avvika med exakt ett nivåsteg från det erforderliga nivået. Till exempel: med 'Billigt' filter och gapantal 1 accepteras sekvensen 'BILLIGT, BILLIGT, NORMALT, BILLIGT' (NORMALT är ett steg över BILLIGT). Detta förhindrar att perioder delas upp av tillfälliga nivåavvikelser. Standard: 0 (strikt filtrering, ingen tolerans)."
|
||||
},
|
||||
"submit": "Nästa till steg 5"
|
||||
},
|
||||
|
|
@ -128,6 +130,7 @@
|
|||
"peak_price_min_distance_from_avg": "Minimiavstånd: Krävd % över dagligt genomsnitt",
|
||||
"peak_price_min_volatility": "Minimum volatilitetsfilter",
|
||||
"peak_price_min_level": "Prisnivåfilter (Valfritt)",
|
||||
"peak_price_max_level_gap_count": "Gaptolerens för nivåfilter",
|
||||
"enable_min_periods_peak": "Försök uppnå minsta antal perioder",
|
||||
"min_periods_peak": "Minsta antal perioder",
|
||||
"relaxation_step_peak": "Avslappningssteg"
|
||||
|
|
@ -137,7 +140,8 @@
|
|||
"peak_price_min_level": "Visa endast topprisperioder om de innehåller intervall med prisnivåer ≥ valt värde. Till exempel måste perioden om du väljer 'Dyr' ha minst ett 'DYR' eller 'MYCKET_DYR' intervall. Detta säkerställer att 'toppris'-perioder inte bara är relativt dyra för dagen, utan faktiskt dyra i absoluta termer (inte bara 'lite dyrare än genomsnittet på en billig dag').",
|
||||
"enable_min_periods_peak": "När aktiverad kommer filtren att gradvis luckras upp om inte tillräckligt många perioder hittas. Detta försöker uppnå det önskade minsta antalet perioder för att säkerställa att du blir varnad för dyra perioder även på dagar med ovanliga prismönster.",
|
||||
"min_periods_peak": "Minsta antal topprisperioder att sträva efter per dag. Filtren kommer att luckras upp steg för steg för att försöka uppnå detta antal. Endast aktiv när 'Försök uppnå minsta antal perioder' är aktiverad. Standard: 1",
|
||||
"relaxation_step_peak": "Procentandel av den ursprungliga flexibilitetströskeln att lägga till per avslappningssteg. Till exempel: med -15% flexibilitet och 25% stegstorlek kommer filtren att prova -15%, -18,75%, -22,5%, osv. Högre värden innebär snabbare avslappning men mindre precision."
|
||||
"relaxation_step_peak": "Procentandel av den ursprungliga flexibilitetströskeln att lägga till per avslappningssteg. Till exempel: med -15% flexibilitet och 25% stegstorlek kommer filtren att prova -15%, -18,75%, -22,5%, osv. Högre värden innebär snabbare avslappning men mindre precision.",
|
||||
"peak_price_max_level_gap_count": "Maximalt antal på varandra följande intervaller som får avvika med exakt ett nivåsteg från det erforderliga nivået. Till exempel: med 'Dyrt' filter och gapantal 2 accepteras sekvensen 'DYRT, NORMALT, NORMALT, DYRT' (NORMALT är ett steg under DYRT). Detta förhindrar att perioder delas upp av tillfälliga nivåavvikelser. Standard: 0 (strikt filtrering, ingen tolerans)."
|
||||
},
|
||||
"submit": "Nästa till steg 6"
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue