posun dovybijejiciho okna tesne pred zapornou cenu
This commit is contained in:
@@ -9,7 +9,9 @@ from types import SimpleNamespace
|
||||
from services.planning_engine import (
|
||||
PlanningSlot,
|
||||
_dynamic_arb_floor_wh_series,
|
||||
_prewindow_deferral_slots,
|
||||
_slots_until_buy_le_threshold,
|
||||
_slots_until_sell_lt,
|
||||
_soc_panel_min_wh_series,
|
||||
solve_dispatch,
|
||||
)
|
||||
@@ -60,6 +62,68 @@ def _battery(
|
||||
)
|
||||
|
||||
|
||||
class SlotsUntilSellNegativeTests(unittest.TestCase):
|
||||
def test_slots_until_first_negative_sell(self) -> None:
|
||||
base = datetime(2026, 4, 3, 0, 0, tzinfo=timezone.utc)
|
||||
slots: list[PlanningSlot] = []
|
||||
for i in range(10):
|
||||
slots.append(
|
||||
PlanningSlot(
|
||||
interval_start=base + timedelta(minutes=15 * i),
|
||||
buy_price=1.0,
|
||||
sell_price=2.0 if i < 4 else -0.5,
|
||||
pv_a_forecast_w=0,
|
||||
pv_b_forecast_w=0,
|
||||
load_baseline_w=500,
|
||||
ev1_connected=False,
|
||||
ev2_connected=False,
|
||||
)
|
||||
)
|
||||
dist = _slots_until_sell_lt(slots, 0.0)
|
||||
self.assertEqual(dist[0], 4)
|
||||
self.assertEqual(dist[3], 1)
|
||||
self.assertEqual(dist[4], 0)
|
||||
|
||||
def test_prewindow_deferral_prefers_sell_anchor(self) -> None:
|
||||
"""Když existuje záporný prodej, kotva je vzdálenost k němu, ne k extrémnímu buy."""
|
||||
base = datetime(2026, 4, 3, 0, 0, tzinfo=timezone.utc)
|
||||
slots: list[PlanningSlot] = []
|
||||
for i in range(8):
|
||||
slots.append(
|
||||
PlanningSlot(
|
||||
interval_start=base + timedelta(minutes=15 * i),
|
||||
buy_price=-50.0,
|
||||
sell_price=1.0 if i < 2 else -0.1,
|
||||
pv_a_forecast_w=0,
|
||||
pv_b_forecast_w=0,
|
||||
load_baseline_w=500,
|
||||
ev1_connected=False,
|
||||
ev2_connected=False,
|
||||
)
|
||||
)
|
||||
adv = _prewindow_deferral_slots(slots, -2.0)
|
||||
self.assertEqual(adv[0], 2)
|
||||
|
||||
def test_prewindow_deferral_falls_back_to_buy_when_no_negative_sell(self) -> None:
|
||||
base = datetime(2026, 4, 3, 0, 0, tzinfo=timezone.utc)
|
||||
slots: list[PlanningSlot] = []
|
||||
for i in range(10):
|
||||
slots.append(
|
||||
PlanningSlot(
|
||||
interval_start=base + timedelta(minutes=15 * i),
|
||||
buy_price=3.0 if i < 7 else -10.0,
|
||||
sell_price=2.0,
|
||||
pv_a_forecast_w=0,
|
||||
pv_b_forecast_w=0,
|
||||
load_baseline_w=500,
|
||||
ev1_connected=False,
|
||||
ev2_connected=False,
|
||||
)
|
||||
)
|
||||
adv = _prewindow_deferral_slots(slots, -2.0)
|
||||
self.assertEqual(adv[0], 7)
|
||||
|
||||
|
||||
class SlotsUntilBuyExtremeTests(unittest.TestCase):
|
||||
def test_slots_until_first_extreme(self) -> None:
|
||||
base = datetime(2026, 4, 3, 0, 0, tzinfo=timezone.utc)
|
||||
@@ -94,7 +158,7 @@ class SlotsUntilBuyExtremeTests(unittest.TestCase):
|
||||
|
||||
def test_prewindow_clamps_relaxed_floor_until_close(self) -> None:
|
||||
sm = [5000.0] * 10
|
||||
dist = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
|
||||
dist = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] # obecná kotva (sell nebo buy)
|
||||
panel = _soc_panel_min_wh_series(sm, dist, 10_000.0, 20_000.0, 2)
|
||||
self.assertEqual(panel[0], 20_000.0)
|
||||
self.assertEqual(panel[6], 20_000.0)
|
||||
|
||||
Reference in New Issue
Block a user