@@ -312,6 +312,34 @@ class DeliveryDuration(enum.Enum):
312312 MINUTES_60 = delivery_duration_pb2 .DeliveryDuration .DELIVERY_DURATION_60
313313 """1-hour contract duration."""
314314
315+ @classmethod
316+ def from_timedelta (cls , duration : timedelta ) -> Self :
317+ """Convert a timedelta to a DeliveryDuration enum.
318+
319+ Args:
320+ duration: The duration as a timedelta.
321+
322+ Returns:
323+ The corresponding DeliveryDuration enum value.
324+
325+ Raises:
326+ ValueError: If the duration is not one of the supported values.
327+ """
328+ minutes = duration .total_seconds () / 60
329+ match minutes :
330+ case 5 :
331+ return DeliveryDuration .MINUTES_5
332+ case 15 :
333+ return DeliveryDuration .MINUTES_15
334+ case 30 :
335+ return DeliveryDuration .MINUTES_30
336+ case 60 :
337+ return DeliveryDuration .MINUTES_60
338+ case _:
339+ raise ValueError (
340+ "Invalid duration value. Duration must be 5, 15, 30, or 60 minutes."
341+ )
342+
315343 @classmethod
316344 @from_pb
317345 def from_pb (
@@ -343,6 +371,7 @@ def to_pb(self) -> delivery_duration_pb2.DeliveryDuration.ValueType:
343371 return delivery_duration_pb2 .DeliveryDuration .ValueType (self .value )
344372
345373
374+ @dataclass (frozen = True )
346375class DeliveryPeriod :
347376 """
348377 Time period during which the contract is delivered.
@@ -358,54 +387,10 @@ class DeliveryPeriod:
358387 duration : DeliveryDuration
359388 """The length of the delivery period."""
360389
361- def __init__ (
362- self ,
363- start : datetime ,
364- duration : timedelta ,
365- ) -> None :
366- """
367- Initialize the DeliveryPeriod object.
368-
369- Args:
370- start: Start UTC timestamp represents the beginning of the delivery period.
371- duration: The length of the delivery period.
372-
373- Raises:
374- ValueError: If the start timestamp does not have a timezone.
375- or if the duration is not 5, 15, 30, or 60 minutes.
376- """
377- if start .tzinfo is None :
378- raise ValueError ("Start timestamp must have a timezone." )
379- if start .tzinfo != timezone .utc :
380- _logger .warning (
381- "Start timestamp is not in UTC timezone. Converting to UTC."
382- )
383- start = start .astimezone (timezone .utc )
384- self .start = start
385-
386- minutes = duration .total_seconds () / 60
387- match minutes :
388- case 5 :
389- self .duration = DeliveryDuration .MINUTES_5
390- case 15 :
391- self .duration = DeliveryDuration .MINUTES_15
392- case 30 :
393- self .duration = DeliveryDuration .MINUTES_30
394- case 60 :
395- self .duration = DeliveryDuration .MINUTES_60
396- case _:
397- raise ValueError (
398- "Invalid duration value. Duration must be 5, 15, 30, or 60 minutes."
399- )
400-
401- def __hash__ (self ) -> int :
402- """
403- Create hash of the DeliveryPeriod object.
404-
405- Returns:
406- Hash of the DeliveryPeriod object.
407- """
408- return hash ((self .start , self .duration ))
390+ def __post_init__ (self ) -> None :
391+ """Validate that the start timestamp is in UTC."""
392+ if self .start .tzinfo is None or self .start .tzinfo != timezone .utc :
393+ raise ValueError ("Start timestamp must be in UTC timezone." )
409394
410395 def __eq__ (
411396 self ,
@@ -453,27 +438,10 @@ def from_pb(cls, delivery_period: delivery_duration_pb2.DeliveryPeriod) -> Self:
453438
454439 Returns:
455440 DeliveryPeriod object corresponding to the protobuf message.
456-
457- Raises:
458- ValueError: If the duration is not 5, 15, 30, or 60 minutes.
459441 """
460442 start = delivery_period .start .ToDatetime (tzinfo = timezone .utc )
461443 delivery_duration_enum = DeliveryDuration .from_pb (delivery_period .duration )
462-
463- match delivery_duration_enum :
464- case DeliveryDuration .MINUTES_5 :
465- duration = timedelta (minutes = 5 )
466- case DeliveryDuration .MINUTES_15 :
467- duration = timedelta (minutes = 15 )
468- case DeliveryDuration .MINUTES_30 :
469- duration = timedelta (minutes = 30 )
470- case DeliveryDuration .MINUTES_60 :
471- duration = timedelta (minutes = 60 )
472- case _:
473- raise ValueError (
474- "Invalid duration value. Duration must be 5, 15, 30, or 60 minutes."
475- )
476- return cls (start = start , duration = duration )
444+ return cls (start = start , duration = delivery_duration_enum )
477445
478446 def to_pb (self ) -> delivery_duration_pb2 .DeliveryPeriod :
479447 """Convert a DeliveryPeriod object to protobuf DeliveryPeriod.
@@ -1025,7 +993,7 @@ def to_pb(
1025993 return self .value
1026994
1027995
1028- @dataclass ()
996+ @dataclass (frozen = True )
1029997class Order : # pylint: disable=too-many-instance-attributes
1030998 """Represents an order in the electricity market."""
1031999
@@ -1074,11 +1042,11 @@ class Order: # pylint: disable=too-many-instance-attributes
10741042 def __post_init__ (self ) -> None :
10751043 """Post initialization checks to ensure that all datetimes are UTC."""
10761044 if self .valid_until is not None :
1077- if self .valid_until .tzinfo is None :
1045+ if (
1046+ self .valid_until .tzinfo is None
1047+ or self .valid_until .tzinfo != timezone .utc
1048+ ):
10781049 raise ValueError ("Valid until must be a UTC datetime." )
1079- if self .valid_until .tzinfo != timezone .utc :
1080- _logger .warning ("Valid until is not a UTC datetime. Converting to UTC." )
1081- self .valid_until = self .valid_until .astimezone (timezone .utc )
10821050
10831051 @classmethod
10841052 @from_pb
@@ -1213,7 +1181,7 @@ def __eq__(self, other: object) -> bool:
12131181 )
12141182
12151183
1216- @dataclass ()
1184+ @dataclass (frozen = True )
12171185class Trade : # pylint: disable=too-many-instance-attributes
12181186 """Represents a private trade in the electricity market."""
12191187
@@ -1247,11 +1215,11 @@ class Trade: # pylint: disable=too-many-instance-attributes
12471215
12481216 def __post_init__ (self ) -> None :
12491217 """Post initialization checks to ensure that all datetimes are UTC."""
1250- if self . execution_time . tzinfo is None :
1251- raise ValueError ( "Execution time must have timezone information" )
1252- if self .execution_time .tzinfo != timezone .utc :
1253- _logger . warning ( "Execution timenis not in UTC timezone. Converting to UTC." )
1254- self . execution_time = self . execution_time . astimezone ( timezone . utc )
1218+ if (
1219+ self . execution_time . tzinfo is None
1220+ or self .execution_time .tzinfo != timezone .utc
1221+ ):
1222+ raise ValueError ( "Execution time must be a UTC datetime." )
12551223
12561224 @classmethod
12571225 @from_pb
@@ -1347,7 +1315,7 @@ def to_pb(self) -> electricity_trading_pb2.OrderDetail.StateDetail:
13471315 )
13481316
13491317
1350- @dataclass ()
1318+ @dataclass (frozen = True )
13511319class OrderDetail :
13521320 """
13531321 Represents an order with full details, including its ID, state, and associated UTC timestamps.
@@ -1378,19 +1346,14 @@ def __post_init__(self) -> None:
13781346 ValueError: If create_time or modification_time do not have timezone information.
13791347
13801348 """
1381- if self .create_time .tzinfo is None :
1382- raise ValueError ("Create time must have timezone information" )
1383- if self .create_time .tzinfo != timezone .utc :
1384- _logger .warning ("Create time is not in UTC timezone. Converting to UTC." )
1385- self .create_time = self .create_time .astimezone (timezone .utc )
1349+ if self .create_time .tzinfo is None or self .create_time .tzinfo != timezone .utc :
1350+ raise ValueError ("Create time must be a UTC datetime." )
13861351
1387- if self .modification_time .tzinfo is None :
1388- raise ValueError ("Modification time must have timezone information" )
1389- if self .modification_time .tzinfo != timezone .utc :
1390- _logger .warning (
1391- "Modification time is not in UTC timezone. Converting to UTC."
1392- )
1393- self .modification_time = self .modification_time .astimezone (timezone .utc )
1352+ if (
1353+ self .modification_time .tzinfo is None
1354+ or self .modification_time .tzinfo != timezone .utc
1355+ ):
1356+ raise ValueError ("Modification time must be a UTC datetime." )
13941357
13951358 @classmethod
13961359 @from_pb
@@ -1439,7 +1402,7 @@ def to_pb(self) -> electricity_trading_pb2.OrderDetail:
14391402 )
14401403
14411404
1442- @dataclass ()
1405+ @dataclass (frozen = True )
14431406class PublicTrade : # pylint: disable=too-many-instance-attributes
14441407 """Represents a public order in the market."""
14451408
@@ -1469,11 +1432,11 @@ class PublicTrade: # pylint: disable=too-many-instance-attributes
14691432
14701433 def __post_init__ (self ) -> None :
14711434 """Post initialization checks to ensure that all datetimes are UTC."""
1472- if self . execution_time . tzinfo is None :
1473- raise ValueError ( "Execution time must have timezone information" )
1474- if self .execution_time .tzinfo != timezone .utc :
1475- _logger . warning ( "Execution time is not in UTC timezone. Converting to UTC." )
1476- self . execution_time = self . execution_time . astimezone ( timezone . utc )
1435+ if (
1436+ self . execution_time . tzinfo is None
1437+ or self .execution_time .tzinfo != timezone .utc
1438+ ):
1439+ raise ValueError ( "Execution time must be a UTC datetime." )
14771440
14781441 @classmethod
14791442 @from_pb
@@ -1967,7 +1930,7 @@ def to_pb(self) -> electricity_trading_pb2.PublicTradeFilter:
19671930 )
19681931
19691932
1970- @dataclass ()
1933+ @dataclass (frozen = True )
19711934class UpdateOrder : # pylint: disable=too-many-instance-attributes
19721935 """
19731936 Represents the order properties that can be updated after an order has been placed.
@@ -2010,12 +1973,10 @@ class UpdateOrder: # pylint: disable=too-many-instance-attributes
20101973
20111974 def __post_init__ (self ) -> None :
20121975 """Post initialization checks to ensure that all datetimes are UTC."""
2013- if self .valid_until is not None :
2014- if self .valid_until .tzinfo is None :
2015- raise ValueError ("Valid until must be a UTC datetime." )
2016- if self .valid_until .tzinfo != timezone .utc :
2017- _logger .warning ("Valid until is not a UTC datetime. Converting to UTC." )
2018- self .valid_until = self .valid_until .astimezone (timezone .utc )
1976+ if self .valid_until is not None and (
1977+ self .valid_until .tzinfo is None or self .valid_until .tzinfo != timezone .utc
1978+ ):
1979+ raise ValueError ("Valid until must be a UTC datetime." )
20191980
20201981 @classmethod
20211982 @from_pb
@@ -2109,7 +2070,7 @@ def to_pb(self) -> electricity_trading_pb2.UpdateGridpoolOrderRequest.UpdateOrde
21092070 )
21102071
21112072
2112- @dataclass ()
2073+ @dataclass (frozen = True )
21132074class PublicOrder : # pylint: disable=too-many-instance-attributes
21142075 """Represents a public order in the market."""
21152076
@@ -2143,18 +2104,6 @@ class PublicOrder: # pylint: disable=too-many-instance-attributes
21432104 update_time : datetime
21442105 """UTC Timestamp of the last update to the order."""
21452106
2146- def __post_init__ (self ) -> None :
2147- """Post initialization checks to ensure that all datetimes are UTC."""
2148- if self .delivery_period .start .tzinfo is None :
2149- raise ValueError ("Delivery period start must have timezone information" )
2150- if self .delivery_period .start .tzinfo != timezone .utc :
2151- _logger .warning (
2152- "Delivery period start is not in UTC timezone. Converting to UTC."
2153- )
2154- self .delivery_period .start = self .delivery_period .start .astimezone (
2155- timezone .utc
2156- )
2157-
21582107 @classmethod
21592108 @from_pb
21602109 def from_pb (
@@ -2222,7 +2171,7 @@ def to_pb(self) -> electricity_trading_pb2.PublicOrderBookRecord:
22222171 )
22232172
22242173
2225- @dataclass ()
2174+ @dataclass (frozen = True )
22262175class PublicOrderBookFilter :
22272176 """Parameters for filtering the public orders in the market."""
22282177
0 commit comments