PyAlgoEngine 0.7.7.post1__tar.gz → 0.8.0a2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/PKG-INFO +3 -2
  2. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/PyAlgoEngine.egg-info/PKG-INFO +3 -2
  3. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/PyAlgoEngine.egg-info/SOURCES.txt +1 -0
  4. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/__init__.py +1 -1
  5. pyalgoengine-0.8.0a2/algo_engine/base/market_data_wrapper.py +483 -0
  6. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/LICENSE +0 -0
  7. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/PyAlgoEngine.egg-info/dependency_links.txt +0 -0
  8. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/PyAlgoEngine.egg-info/requires.txt +0 -0
  9. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/PyAlgoEngine.egg-info/top_level.txt +0 -0
  10. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/README.md +0 -0
  11. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/apps/__init__.py +0 -0
  12. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/apps/backtest/__init__.py +0 -0
  13. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/apps/backtest/doc_server.py +0 -0
  14. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/apps/backtest/tester.py +0 -0
  15. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/apps/backtest/web_app.py +0 -0
  16. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/apps/bokeh_server.py +0 -0
  17. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/apps/demo/__init__.py +0 -0
  18. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/apps/demo/test.py +0 -0
  19. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/apps/sim_input/__init__.py +0 -0
  20. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/apps/sim_input/client.py +0 -0
  21. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/apps/sim_input/sim_keyboard.py +0 -0
  22. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/apps/sim_input/sim_mouse.py +0 -0
  23. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/apps/sim_input/window.py +0 -0
  24. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/backtest/__init__.py +0 -0
  25. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/backtest/__main__.py +0 -0
  26. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/backtest/metrics.py +0 -0
  27. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/backtest/replay.py +0 -0
  28. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/backtest/sim_match.py +0 -0
  29. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/base/__init__.py +0 -0
  30. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/base/console_utils.py +0 -0
  31. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/base/finance_decimal.py +0 -0
  32. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/base/market_buffer.py +0 -0
  33. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/base/market_utils.py +0 -0
  34. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/base/market_utils_nt.py +0 -0
  35. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/base/market_utils_posix.py +0 -0
  36. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/base/technical_analysis.py +0 -0
  37. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/base/telemetrics.py +0 -0
  38. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/base/trade_utils.py +0 -0
  39. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/engine/__init__.py +0 -0
  40. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/engine/algo_engine.py +0 -0
  41. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/engine/event_engine.py +0 -0
  42. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/engine/market_engine.py +0 -0
  43. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/engine/trade_engine.py +0 -0
  44. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/monitor/__init__.py +0 -0
  45. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/monitor/advanced_data_interface.py +0 -0
  46. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/profile/__init__.py +0 -0
  47. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/profile/cn.py +0 -0
  48. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/strategy/__init__.py +0 -0
  49. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/strategy/strategy_engine.py +0 -0
  50. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/utils/__init__.py +0 -0
  51. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/utils/commit_regularizer.py +0 -0
  52. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/utils/data_utils.py +0 -0
  53. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/setup.cfg +0 -0
  54. {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: PyAlgoEngine
3
- Version: 0.7.7.post1
3
+ Version: 0.8.0a2
4
4
  Summary: Basic algo engine
5
5
  Home-page: https://github.com/BolunHan/PyAlgoEngine
6
6
  Author: Bolun.Han
@@ -28,6 +28,7 @@ Dynamic: classifier
28
28
  Dynamic: description
29
29
  Dynamic: description-content-type
30
30
  Dynamic: home-page
31
+ Dynamic: license-file
31
32
  Dynamic: provides-extra
32
33
  Dynamic: requires-dist
33
34
  Dynamic: requires-python
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: PyAlgoEngine
3
- Version: 0.7.7.post1
3
+ Version: 0.8.0a2
4
4
  Summary: Basic algo engine
5
5
  Home-page: https://github.com/BolunHan/PyAlgoEngine
6
6
  Author: Bolun.Han
@@ -28,6 +28,7 @@ Dynamic: classifier
28
28
  Dynamic: description
29
29
  Dynamic: description-content-type
30
30
  Dynamic: home-page
31
+ Dynamic: license-file
31
32
  Dynamic: provides-extra
32
33
  Dynamic: requires-dist
33
34
  Dynamic: requires-python
@@ -29,6 +29,7 @@ algo_engine/base/__init__.py
29
29
  algo_engine/base/console_utils.py
30
30
  algo_engine/base/finance_decimal.py
31
31
  algo_engine/base/market_buffer.py
32
+ algo_engine/base/market_data_wrapper.py
32
33
  algo_engine/base/market_utils.py
33
34
  algo_engine/base/market_utils_nt.py
34
35
  algo_engine/base/market_utils_posix.py
@@ -1,4 +1,4 @@
1
- __version__ = "0.7.7.post1"
1
+ __version__ = "0.8.0.alpha2"
2
2
 
3
3
  import logging
4
4
  import os
@@ -0,0 +1,483 @@
1
+ import abc
2
+ import datetime
3
+ import enum
4
+ import logging
5
+ from typing import Self, Literal
6
+
7
+ import candlestick as candlestick_cython
8
+ import market_data as market_data_cython
9
+ import transaction as transaction_cython
10
+
11
+ LOGGER = logging.getLogger(__name__)
12
+ from algo_engine.profile import PROFILE
13
+
14
+
15
+ class Direction(enum.IntEnum):
16
+ DIRECTION_UNKNOWN = 1
17
+ DIRECTION_SHORT = 0
18
+ DIRECTION_LONG = 2
19
+
20
+
21
+ class Offset(enum.IntEnum):
22
+ OFFSET_CANCEL = 0
23
+ OFFSET_ORDER = 4
24
+ OFFSET_OPEN = 8
25
+ OFFSET_CLOSE = 16
26
+
27
+
28
+ class TransactionSide(enum.IntEnum):
29
+ # Long Side
30
+ SIDE_LONG_OPEN = Direction.DIRECTION_LONG + Offset.OFFSET_OPEN # 2 + 8 = 10
31
+ SIDE_LONG_CLOSE = Direction.DIRECTION_LONG + Offset.OFFSET_CLOSE # 2 + 16 = 18
32
+ SIDE_LONG_CANCEL = Direction.DIRECTION_LONG + Offset.OFFSET_CANCEL # 2 + 0 = 2
33
+
34
+ # Short Side
35
+ SIDE_SHORT_OPEN = Direction.DIRECTION_SHORT + Offset.OFFSET_OPEN # 0 + 8 = 8
36
+ SIDE_SHORT_CLOSE = Direction.DIRECTION_SHORT + Offset.OFFSET_CLOSE # 0 + 16 = 16
37
+ SIDE_SHORT_CANCEL = Direction.DIRECTION_SHORT + Offset.OFFSET_CANCEL # 0 + 0 = 0
38
+
39
+ # Order
40
+ SIDE_BID = Direction.DIRECTION_LONG + Offset.OFFSET_ORDER # 2 + 4 = 6
41
+ SIDE_ASK = Direction.DIRECTION_SHORT + Offset.OFFSET_ORDER # 0 + 4 = 4
42
+
43
+ # Generic Cancel
44
+ SIDE_CANCEL = Direction.DIRECTION_UNKNOWN + Offset.OFFSET_CANCEL # 1 + 0 = 1
45
+
46
+ # Alias
47
+ SIDE_UNKNOWN = SIDE_CANCEL # 1
48
+ SIDE_LONG = SIDE_LONG_OPEN # 10
49
+ SIDE_SHORT = SIDE_SHORT_OPEN # 8
50
+
51
+ # Backward compatibility
52
+ ShortOrder = AskOrder = Ask = SIDE_ASK
53
+ LongOrder = BidOrder = Bid = SIDE_BID
54
+
55
+ ShortFilled = Unwind = Sell = SIDE_SHORT_CLOSE
56
+ LongFilled = LongOpen = Buy = SIDE_LONG_OPEN
57
+
58
+ ShortOpen = Short = SIDE_SHORT_OPEN
59
+ Cover = SIDE_LONG_CLOSE
60
+
61
+ UNKNOWN = CANCEL = SIDE_CANCEL
62
+
63
+ def __neg__(self) -> Self:
64
+ return self.__class__([market_data_cython.TransactionHelper.pyget_opposite(self.value)])
65
+
66
+ @classmethod
67
+ def _missing_(cls, value: str | int):
68
+ """
69
+ Handle missing values in the enumeration.
70
+
71
+ Args:
72
+ value (str | int): The value to resolve.
73
+
74
+ Returns:
75
+ TransactionSide: The resolved transaction side, or UNKNOWN if not recognized.
76
+ """
77
+ side_str = str(value).lower()
78
+
79
+ match side_str:
80
+ case 'long' | 'buy' | 'b':
81
+ trade_side = cls.SIDE_LONG_OPEN
82
+ case 'short' | 'sell' | 's':
83
+ trade_side = cls.SIDE_SHORT_CLOSE
84
+ case 'short' | 'ss':
85
+ trade_side = cls.SIDE_SHORT_OPEN
86
+ case 'cover' | 'bc':
87
+ trade_side = cls.SIDE_LONG_CLOSE
88
+ case 'ask':
89
+ trade_side = cls.SIDE_ASK
90
+ case 'bid':
91
+ trade_side = cls.SIDE_BID
92
+ case _:
93
+ try:
94
+ trade_side = cls.__getitem__(value)
95
+ except Exception as _:
96
+ trade_side = cls.SIDE_UNKNOWN
97
+ LOGGER.warning('{} is not recognized, return TransactionSide.UNKNOWN'.format(value))
98
+
99
+ return trade_side
100
+
101
+ @property
102
+ def sign(self) -> int:
103
+ return market_data_cython.TransactionHelper.pyget_sign(self.value)
104
+
105
+ @property
106
+ def offset(self) -> Offset:
107
+ """
108
+ Get the offset of the transaction side.
109
+
110
+ Returns:
111
+ int: The offset value, equivalent to the sign.
112
+ """
113
+ return Offset(market_data_cython.TransactionHelper.pyget_offset(self.value))
114
+
115
+ @property
116
+ def direction(self) -> Direction:
117
+ """
118
+ Get the offset of the transaction side.
119
+
120
+ Returns:
121
+ int: The offset value, equivalent to the sign.
122
+ """
123
+ return Direction(market_data_cython.TransactionHelper.pyget_direction(self.value))
124
+
125
+ @property
126
+ def side_name(self) -> str:
127
+ return market_data_cython.TransactionHelper.pyget_side_name(self.value)
128
+
129
+ @property
130
+ def offset_name(self) -> str:
131
+ return market_data_cython.TransactionHelper.pyget_offset_name(self.value)
132
+
133
+ @property
134
+ def direction_name(self) -> str:
135
+ return market_data_cython.TransactionHelper.pyget_direction_name(self.value)
136
+
137
+
138
+ class OrderType(enum.IntEnum):
139
+ UNKNOWN = -20
140
+ CancelOrder = -10
141
+ Generic = 0
142
+ LimitOrder = 10
143
+ LimitMarketMaking = 11
144
+ MarketOrder = 2
145
+ FOK = 21
146
+ FAK = 22
147
+ IOC = 23
148
+
149
+
150
+ class MarketData(market_data_cython.MarketData, metaclass=abc.ABCMeta):
151
+ """
152
+ Python wrapper for MarketData Cython class.
153
+ Provides pickle serialization support.
154
+ """
155
+
156
+ @classmethod
157
+ def from_buffer(cls, buffer, **kwargs):
158
+ self = super().from_buffer(buffer)
159
+ self.__dict__.update(kwargs)
160
+ return self
161
+
162
+ @abc.abstractmethod
163
+ def __reduce__(self):
164
+ ...
165
+
166
+ def __copy__(self):
167
+ return self.__class__.from_buffer(memoryview(self), **self.__dict__)
168
+
169
+ @property
170
+ def topic(self) -> str:
171
+ return f'{self.ticker}.{self.__class__.__name__}'
172
+
173
+ @property
174
+ def market_time(self) -> datetime.datetime | datetime.date:
175
+ return datetime.datetime.fromtimestamp(self.timestamp, tz=PROFILE.time_zone)
176
+
177
+
178
+ class TransactionData(transaction_cython.TransactionData, MarketData):
179
+ def __init__(
180
+ self,
181
+ ticker: str,
182
+ timestamp: float,
183
+ price: float,
184
+ volume: float,
185
+ side: int | TransactionSide,
186
+ multiplier: float = 1.0,
187
+ notional: float = 0.0,
188
+ transaction_id: int | str | bytes = None,
189
+ buy_id: int | str | bytes = None,
190
+ sell_id: int | str | bytes = None,
191
+ **kwargs
192
+ ):
193
+ super().__init__(ticker=ticker, timestamp=timestamp, price=price, volume=volume, side=side, multiplier=multiplier, notional=notional, transaction_id=transaction_id, buy_id=buy_id, sell_id=sell_id)
194
+ self.__dict__.update(kwargs)
195
+
196
+ def __reduce__(self):
197
+ """Support for pickle serialization"""
198
+ return self.__class__.from_bytes, (self.to_bytes(),), self.__dict__
199
+
200
+ def __setstate__(self, state):
201
+ """Restore state from pickle"""
202
+ self.__dict__.update(state)
203
+
204
+ @property
205
+ def side_int(self) -> int:
206
+ return super().side
207
+
208
+ @property
209
+ def side(self) -> TransactionSide:
210
+ return TransactionSide(super().side)
211
+
212
+
213
+ class BarData(candlestick_cython.BarData, MarketData):
214
+ def __init__(
215
+ self,
216
+ ticker: str,
217
+ timestamp: float,
218
+ high_price: float,
219
+ low_price: float,
220
+ open_price: float,
221
+ close_price: float,
222
+ volume: float = 0.0,
223
+ notional: float = 0.0,
224
+ trade_count: int = 0,
225
+ start_timestamp: float = None,
226
+ bar_span: float = None,
227
+ **kwargs: object
228
+ ) -> None:
229
+ if bar_span is None:
230
+ if start_timestamp is None:
231
+ raise ValueError('Must assign either start_timestamp or bar_span or both.')
232
+ else:
233
+ bar_span = timestamp - start_timestamp
234
+ else:
235
+ if isinstance(bar_span, datetime.timedelta):
236
+ bar_span = bar_span.total_seconds()
237
+ else:
238
+ bar_span = float(bar_span)
239
+
240
+ super().__init__(ticker=ticker, timestamp=timestamp, high_price=high_price, low_price=low_price, open_price=open_price, close_price=close_price, volume=volume, notional=notional, trade_count=trade_count, bar_span=bar_span)
241
+ self.__dict__.update(kwargs)
242
+
243
+ def __reduce__(self):
244
+ """Support for pickle serialization"""
245
+ return self.__class__.from_bytes, (self.to_bytes(),), self.__dict__
246
+
247
+ def __setstate__(self, state):
248
+ """Restore state from pickle"""
249
+ self.__dict__.update(state)
250
+
251
+ @property
252
+ def bar_type(self) -> Literal['Hourly-Plus', 'Hourly', 'Minute-Plus', 'Minute', 'Sub-Minute']:
253
+ """
254
+ Determines the type of the bar based on its span.
255
+
256
+ Returns:
257
+ Literal['Hourly-Plus', 'Hourly', 'Minute-Plus', 'Minute', 'Sub-Minute']: The type of the bar.
258
+ """
259
+ bar_span = super().bar_span
260
+
261
+ if bar_span > 3600:
262
+ return 'Hourly-Plus'
263
+ elif bar_span == 3600:
264
+ return 'Hourly'
265
+ elif bar_span > 60:
266
+ return 'Minute-Plus'
267
+ elif bar_span == 60:
268
+ return 'Minute'
269
+ else:
270
+ return 'Sub-Minute'
271
+
272
+ @property
273
+ def bar_end_time(self) -> datetime.datetime | datetime.date:
274
+ """
275
+ The end time of the bar.
276
+
277
+ Returns:
278
+ datetime.datetime | datetime.date: The end time of the bar.
279
+ """
280
+ return self.market_time
281
+
282
+ @property
283
+ def bar_start_time(self) -> datetime.datetime:
284
+ """
285
+ The start time of the bar.
286
+
287
+ Returns:
288
+ datetime.datetime: The start time of the bar.
289
+ """
290
+ return datetime.datetime.fromtimestamp(super().start_timestamp, tz=PROFILE.time_zone)
291
+
292
+ @property
293
+ def bar_span(self) -> datetime.timedelta:
294
+ return datetime.timedelta(seconds=super().bar_span)
295
+
296
+
297
+ class DailyBar(candlestick_cython.BarData):
298
+ def __init__(
299
+ self,
300
+ ticker: str,
301
+ market_date: datetime.date, # The market date of the bar, if with 1D data, or the END date of the bar.
302
+ high_price: float,
303
+ low_price: float,
304
+ open_price: float,
305
+ close_price: float,
306
+ volume: float = 0.0,
307
+ notional: float = 0.0,
308
+ trade_count: int = 0,
309
+ bar_span: datetime.timedelta | int = None, # Expect to be a timedelta for several days, or the number of days
310
+ start_date: datetime.date = None,
311
+ **kwargs
312
+ ):
313
+ if bar_span is None:
314
+ if start_date is None:
315
+ bar_span = 1
316
+ else:
317
+ bar_span = (market_date - start_date).days
318
+ else:
319
+ if isinstance(bar_span, datetime.timedelta):
320
+ bar_span = bar_span.days
321
+ else:
322
+ bar_span = float(bar_span)
323
+
324
+ timestamp = 10000 * market_date.year + 100 * market_date.month + market_date.day
325
+
326
+ super().__init__(ticker=ticker, timestamp=timestamp, high_price=high_price, low_price=low_price, open_price=open_price, close_price=close_price, volume=volume, notional=notional, trade_count=trade_count, bar_span=bar_span)
327
+ self.__dict__.update(kwargs)
328
+
329
+ def __repr__(self) -> str:
330
+ if (bar_span := super().bar_span) == 1:
331
+ return f"DailyBar(ticker='{self.ticker}', date={self.market_date}, open={self.open_price}, high={self.high_price}, low={self.low_price}, close={self.close_price}, volume={self.volume})"
332
+ else:
333
+ return f"DailyBar(ticker='{self.ticker}', date={self.market_date}, span={bar_span}d, open={self.open_price}, high={self.high_price}, low={self.low_price}, close={self.close_price}, volume={self.volume})"
334
+
335
+ def __reduce__(self):
336
+ """Support for pickle serialization"""
337
+ return self.__class__.from_bytes, (self.to_bytes(),), self.__dict__
338
+
339
+ def __setstate__(self, state):
340
+ """Restore state from pickle"""
341
+ self.__dict__.update(state)
342
+
343
+ @property
344
+ def market_date(self) -> datetime.date:
345
+ """
346
+ The market date of the bar.
347
+
348
+ Returns:
349
+ datetime.date: The market date of the bar.
350
+ """
351
+
352
+ int_date = int(self.timestamp)
353
+ y, _m = divmod(int_date, 10000)
354
+ m, d = divmod(_m, 100)
355
+
356
+ return datetime.date(year=y, month=m, day=d)
357
+
358
+ @property
359
+ def market_time(self) -> datetime.date:
360
+ """
361
+ The market date of the bar (same as `market_date`).
362
+
363
+ Returns:
364
+ datetime.date: The market date of the bar.
365
+ """
366
+ return self.market_date
367
+
368
+ @property
369
+ def bar_start_time(self) -> datetime.date:
370
+ """
371
+ The start date of the bar period.
372
+
373
+ Returns:
374
+ datetime.date: The start date of the bar.
375
+ """
376
+ return self.market_date - self.bar_span
377
+
378
+ @property
379
+ def bar_end_time(self) -> datetime.date:
380
+ """
381
+ The end date of the bar period.
382
+
383
+ Returns:
384
+ datetime.date: The end date of the bar.
385
+ """
386
+ return self.market_date
387
+
388
+ @property
389
+ def bar_span(self) -> datetime.timedelta:
390
+ return datetime.timedelta(days=super().bar_span)
391
+
392
+ @property
393
+ def bar_type(self) -> Literal['Daily', 'Daily-Plus']:
394
+ """
395
+ Determines the type of the bar based on its span.
396
+
397
+ Returns:
398
+ Literal['Daily', 'Daily-Plus']: The type of the bar.
399
+
400
+ Raises:
401
+ ValueError: If `bar_span` is not valid for a daily bar.
402
+ """
403
+ if super().bar_span == 1:
404
+ return 'Daily'
405
+ elif super().bar_span > 1:
406
+ return 'Daily-Plus'
407
+ else:
408
+ raise ValueError(f'Invalid bar_span for {self.__class__.__name__}! Expect an int greater or equal to 1, got {super().bar_span}.')
409
+
410
+
411
+ class TickDataLite(market_data_cython.TickDataLite, MarketData):
412
+ """
413
+ Python wrapper for TickDataLite Cython class.
414
+ Represents tick data for a specific ticker without the order_book field.
415
+ """
416
+
417
+ def __init__(
418
+ self,
419
+ ticker: str,
420
+ timestamp: float,
421
+ last_price: float,
422
+ bid_price: float = float('nan'),
423
+ bid_volume: float = float('nan'),
424
+ ask_price: float = float('nan'),
425
+ ask_volume: float = float('nan'),
426
+ total_traded_volume: float = 0.0,
427
+ total_traded_notional: float = 0.0,
428
+ total_trade_count: int = 0,
429
+ **kwargs
430
+ ):
431
+ """
432
+ Initialize a new instance of TickDataLite.
433
+
434
+ Args:
435
+ ticker (str): The ticker symbol for the market data.
436
+ timestamp (float): The timestamp of the tick data.
437
+ last_price (float): The last traded price.
438
+ bid_price (float, optional): The bid price. Defaults to None.
439
+ bid_volume (float, optional): The bid volume. Defaults to None.
440
+ ask_price (float, optional): The ask price. Defaults to None.
441
+ ask_volume (float, optional): The ask volume. Defaults to None.
442
+ total_traded_volume (float, optional): The total traded volume. Defaults to 0.0.
443
+ total_traded_notional (float, optional): The total traded notional value. Defaults to 0.0.
444
+ total_trade_count (int, optional): The total number of trades. Defaults to 0.
445
+ **kwargs: Additional keyword arguments.
446
+ """
447
+ # Create the Cython object
448
+ super().__init__(
449
+ ticker=ticker,
450
+ timestamp=timestamp,
451
+ last_price=last_price,
452
+ bid_price=bid_price,
453
+ bid_volume=bid_volume,
454
+ ask_price=ask_price,
455
+ ask_volume=ask_volume,
456
+ total_traded_volume=total_traded_volume,
457
+ total_traded_notional=total_traded_notional,
458
+ total_trade_count=total_trade_count
459
+ )
460
+ self.__dict__.update(kwargs)
461
+
462
+ def __reduce__(self):
463
+ """Support for pickle serialization"""
464
+ return self.__class__.from_bytes, (self.to_bytes(),), self.__dict__
465
+
466
+ def __setstate__(self, state):
467
+ """Restore state from pickle"""
468
+ self.__dict__.update(state)
469
+
470
+ def __repr__(self) -> str:
471
+ """
472
+ Returns a string representation of the TickDataLite instance.
473
+
474
+ Returns:
475
+ str: A string representation of the TickDataLite instance.
476
+ """
477
+ return f'<TickDataLite>([{self.market_time:%Y-%m-%d %H:%M:%S}] {self.ticker}, bid={self.bid_price}, ask={self.ask_price})'
478
+
479
+ @classmethod
480
+ def from_buffer(cls, buffer, **kwargs):
481
+ self = super().from_buffer(buffer)
482
+ self.__dict__.update(kwargs)
483
+ return self