dalsi
This commit is contained in:
@@ -12,6 +12,7 @@ from services.planning_engine import (
|
||||
_dynamic_arb_floor_wh_series,
|
||||
_dispatch_result_comparison,
|
||||
_pre_neg_peak_sell_idx,
|
||||
_prague_hour,
|
||||
_prewindow_deferral_slots,
|
||||
_slots_until_buy_le_threshold,
|
||||
_slots_until_sell_lt,
|
||||
@@ -620,8 +621,8 @@ class PlanningDispatchMilpTests(unittest.TestCase):
|
||||
if results[0].grid_setpoint_w < 0:
|
||||
self.assertLess(
|
||||
results[0].battery_soc_target,
|
||||
19.0,
|
||||
msg="with relaxed soc_min, first-slot export should be able to finish below reserve %",
|
||||
22.0,
|
||||
msg="with relaxed soc_min, morning export should finish below reserve %",
|
||||
)
|
||||
|
||||
def test_negative_sell_forbids_battery_export_arbitrage(self) -> None:
|
||||
@@ -785,11 +786,12 @@ class PlanningDispatchMilpTests(unittest.TestCase):
|
||||
tuv_delta_stats=None,
|
||||
operating_mode="AUTO",
|
||||
)
|
||||
# Slot index 1 je poslední před prvním sell<0 (index 2).
|
||||
first_neg = 2
|
||||
pre_neg_soc = [results[i].battery_soc_target for i in range(first_neg)]
|
||||
self.assertLessEqual(
|
||||
results[1].battery_soc_target,
|
||||
min(pre_neg_soc),
|
||||
6.0,
|
||||
msg="anchor should drive SoC close to planner floor before first negative sell",
|
||||
msg="anchor at morning peak should drive SoC near planner floor before first negative sell",
|
||||
)
|
||||
|
||||
def test_anchor_uses_planner_floor_even_without_extreme_buy(self) -> None:
|
||||
@@ -802,7 +804,7 @@ class PlanningDispatchMilpTests(unittest.TestCase):
|
||||
PlanningSlot(
|
||||
interval_start=base,
|
||||
buy_price=3.0,
|
||||
sell_price=1.0,
|
||||
sell_price=3.06,
|
||||
pv_a_forecast_w=0,
|
||||
pv_b_forecast_w=0,
|
||||
load_baseline_w=0,
|
||||
@@ -814,7 +816,7 @@ class PlanningDispatchMilpTests(unittest.TestCase):
|
||||
PlanningSlot(
|
||||
interval_start=base + timedelta(minutes=15),
|
||||
buy_price=3.0,
|
||||
sell_price=0.5,
|
||||
sell_price=2.0,
|
||||
pv_a_forecast_w=0,
|
||||
pv_b_forecast_w=0,
|
||||
load_baseline_w=0,
|
||||
@@ -860,8 +862,11 @@ class PlanningDispatchMilpTests(unittest.TestCase):
|
||||
tuv_delta_stats=None,
|
||||
operating_mode="AUTO",
|
||||
)
|
||||
# Slot index 1 je poslední před prvním sell<0 (index 2).
|
||||
self.assertLessEqual(results[1].battery_soc_target, 6.0)
|
||||
self.assertLess(
|
||||
results[0].grid_setpoint_w,
|
||||
-1_000,
|
||||
msg="morning peak slot should export before first negative sell",
|
||||
)
|
||||
|
||||
def test_grid_import_soft_cap_penalizes_breaker_overdraw(self) -> None:
|
||||
"""
|
||||
@@ -1562,7 +1567,7 @@ class ChargeAcquisitionArbitrageTests(unittest.TestCase):
|
||||
charge_acquisition_cutoff_at=base + timedelta(minutes=30),
|
||||
)
|
||||
)
|
||||
battery = _battery(uc_wh=64_000.0, terminal_soc_value_factor=0.5)
|
||||
battery = _battery(uc_wh=64_000.0, terminal_soc_value_factor=0.0)
|
||||
battery.max_charge_power_w = 17_000
|
||||
battery.max_discharge_power_w = 17_000
|
||||
hp = SimpleNamespace(rated_heating_power_w=0, tuv_min_temp_c=45.0, tuv_target_temp_c=55.0)
|
||||
@@ -2236,6 +2241,39 @@ class PlannerArbitrageImprovementsTests(unittest.TestCase):
|
||||
first_neg = 2
|
||||
self.assertEqual(_pre_neg_peak_sell_idx(slots, first_neg), 1)
|
||||
|
||||
def test_pre_neg_peak_ignores_midnight_on_same_day(self) -> None:
|
||||
"""Půlnoc může mít vyšší sell než ráno — peak musí být v pásmu 5–11, ne 00:00."""
|
||||
base = datetime(2026, 5, 22, 22, 0, tzinfo=timezone.utc)
|
||||
slots = [
|
||||
PlanningSlot(
|
||||
interval_start=base + timedelta(minutes=15 * i),
|
||||
buy_price=4.0,
|
||||
sell_price=3.72 if i == 0 else (3.06 if i == 28 else 2.0),
|
||||
pv_a_forecast_w=0,
|
||||
pv_b_forecast_w=0,
|
||||
load_baseline_w=1000,
|
||||
ev1_connected=False,
|
||||
ev2_connected=False,
|
||||
)
|
||||
for i in range(36)
|
||||
] + [
|
||||
PlanningSlot(
|
||||
interval_start=base + timedelta(minutes=15 * 36),
|
||||
buy_price=0.5,
|
||||
sell_price=-0.1,
|
||||
pv_a_forecast_w=0,
|
||||
pv_b_forecast_w=0,
|
||||
load_baseline_w=1000,
|
||||
ev1_connected=False,
|
||||
ev2_connected=False,
|
||||
),
|
||||
]
|
||||
first_neg = 36
|
||||
peak_idx = _pre_neg_peak_sell_idx(slots, first_neg)
|
||||
self.assertIsNotNone(peak_idx)
|
||||
self.assertGreater(_prague_hour(slots[peak_idx]), 4)
|
||||
self.assertLess(_prague_hour(slots[peak_idx]), 12)
|
||||
|
||||
def test_pre_neg_peak_idx_is_highest_positive_sell(self) -> None:
|
||||
base = datetime(2026, 5, 23, 4, 0, tzinfo=timezone.utc)
|
||||
slots = [
|
||||
|
||||
Reference in New Issue
Block a user