11from dataclasses import dataclass
22import math
33from statistics import median
4- from typing import List , Optional
4+ from typing import List , Optional , Tuple , Union
55
66
77@dataclass
@@ -190,6 +190,35 @@ def filter_upcoming_blocks(
190190 return [block for block in blocks if block .end_offset_minutes > now_offset_minutes ]
191191
192192
193+ def resolve_active_and_next_blocks (
194+ blocks : Optional [Union [PriceBlock , List [PriceBlock ]]],
195+ now_offset_minutes : float ,
196+ ) -> Tuple [Optional [PriceBlock ], Optional [PriceBlock ], List [PriceBlock ]]:
197+ """Resolve active and next blocks from a block or list of blocks."""
198+ if blocks is None :
199+ return None , None , []
200+ block_list = blocks if isinstance (blocks , list ) else [blocks ]
201+ active_block = next (
202+ (
203+ block
204+ for block in block_list
205+ if block .start_offset_minutes <= now_offset_minutes < block .end_offset_minutes
206+ ),
207+ None ,
208+ )
209+ upcoming_blocks = [
210+ block
211+ for block in block_list
212+ if block .start_offset_minutes > now_offset_minutes
213+ ]
214+ next_block = (
215+ min (upcoming_blocks , key = lambda block : block .start_offset_minutes )
216+ if upcoming_blocks
217+ else None
218+ )
219+ return active_block , next_block , block_list
220+
221+
193222def compute_brake_level (
194223 block : Optional [PriceBlock ],
195224 amplitude : float ,
@@ -252,23 +281,10 @@ def compute_price_brake(
252281 )
253282 upcoming_blocks = filter_upcoming_blocks (blocks , now_offset_minutes )
254283 selected_blocks = select_price_blocks (upcoming_blocks , max_blocks = 2 )
255- active_block = None
256- for block in selected_blocks :
257- if block .start_offset_minutes <= now_offset_minutes < block .end_offset_minutes :
258- active_block = block
259- break
260- upcoming_block = None
261- if active_block is None :
262- upcoming_candidates = [
263- block
264- for block in selected_blocks
265- if block .start_offset_minutes > now_offset_minutes
266- ]
267- if upcoming_candidates :
268- upcoming_block = min (
269- upcoming_candidates , key = lambda block : block .start_offset_minutes
270- )
271- primary_block = active_block or upcoming_block
284+ active_block , next_block , _ = resolve_active_and_next_blocks (
285+ upcoming_blocks , now_offset_minutes
286+ )
287+ primary_block = active_block or next_block
272288 area = primary_block .area if primary_block else 0.0
273289 safe_area_scale = area_scale if area_scale > 0 else 1.0
274290 amplitude = clamp (area / safe_area_scale , 0.0 , 1.0 ) if primary_block else 0.0
@@ -279,6 +295,11 @@ def compute_price_brake(
279295 post_release_minutes ,
280296 now_offset_minutes ,
281297 )
298+ blocks_for_status = list (selected_blocks )
299+ for candidate in (active_block , next_block ):
300+ if candidate and candidate not in blocks_for_status :
301+ blocks_for_status .append (candidate )
302+ blocks_for_status .sort (key = lambda block : block .start_index )
282303
283304 return {
284305 "baseline" : baseline ,
@@ -287,5 +308,7 @@ def compute_price_brake(
287308 "amplitude" : amplitude ,
288309 "brake_level" : brake_level ,
289310 "block" : primary_block ,
290- "blocks" : selected_blocks ,
311+ "blocks" : blocks_for_status ,
312+ "active_block" : active_block ,
313+ "next_block" : next_block ,
291314 }
0 commit comments