PyAlgoEngine 0.5.4.post7__tar.gz → 0.5.4.post10__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 (45) hide show
  1. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/PKG-INFO +1 -1
  2. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/PyAlgoEngine.egg-info/PKG-INFO +1 -1
  3. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/__init__.py +1 -1
  4. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/base/market_utils.py +89 -8
  5. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/profile/__init__.py +3 -0
  6. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/utils/data_utils.py +4 -3
  7. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/LICENSE +0 -0
  8. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/PyAlgoEngine.egg-info/SOURCES.txt +0 -0
  9. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/PyAlgoEngine.egg-info/dependency_links.txt +0 -0
  10. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/PyAlgoEngine.egg-info/requires.txt +0 -0
  11. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/PyAlgoEngine.egg-info/top_level.txt +0 -0
  12. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/README.md +0 -0
  13. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/apps/__init__.py +0 -0
  14. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/apps/backtest/__init__.py +0 -0
  15. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/apps/backtest/doc_server.py +0 -0
  16. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/apps/backtest/tester.py +0 -0
  17. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/apps/backtest/web_app.py +0 -0
  18. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/apps/bokeh_server.py +0 -0
  19. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/apps/demo/__init__.py +0 -0
  20. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/apps/demo/test.py +0 -0
  21. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/backtest/__init__.py +0 -0
  22. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/backtest/__main__.py +0 -0
  23. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/backtest/metrics.py +0 -0
  24. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/backtest/replay.py +0 -0
  25. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/backtest/sim_match.py +0 -0
  26. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/base/__init__.py +0 -0
  27. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/base/console_utils.py +0 -0
  28. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/base/finance_decimal.py +0 -0
  29. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/base/technical_analysis.py +0 -0
  30. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/base/telemetrics.py +0 -0
  31. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/base/trade_utils.py +0 -0
  32. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/engine/__init__.py +0 -0
  33. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/engine/algo_engine.py +0 -0
  34. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/engine/event_engine.py +0 -0
  35. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/engine/market_engine.py +0 -0
  36. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/engine/trade_engine.py +0 -0
  37. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/monitor/__init__.py +0 -0
  38. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/monitor/advanced_data_interface.py +0 -0
  39. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/profile/cn.py +0 -0
  40. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/strategy/__init__.py +0 -0
  41. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/strategy/strategy_engine.py +0 -0
  42. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/utils/__init__.py +0 -0
  43. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/algo_engine/utils/commit_regularizer.py +0 -0
  44. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/setup.cfg +0 -0
  45. {pyalgoengine-0.5.4.post7 → pyalgoengine-0.5.4.post10}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyAlgoEngine
3
- Version: 0.5.4.post7
3
+ Version: 0.5.4.post10
4
4
  Summary: Basic algo engine
5
5
  Home-page: https://github.com/BolunHan/PyAlgoEngine
6
6
  Author: Bolun.Han
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyAlgoEngine
3
- Version: 0.5.4.post7
3
+ Version: 0.5.4.post10
4
4
  Summary: Basic algo engine
5
5
  Home-page: https://github.com/BolunHan/PyAlgoEngine
6
6
  Author: Bolun.Han
@@ -1,4 +1,4 @@
1
- __version__ = "0.5.4.post7"
1
+ __version__ = "0.5.4.post10"
2
2
 
3
3
  import logging
4
4
  import os
@@ -11,6 +11,7 @@ from ctypes import c_ulong, c_double, c_wchar, c_int, c_longlong
11
11
  from multiprocessing import RawValue, RawArray
12
12
  from typing import overload, Literal
13
13
 
14
+ import ciso8601
14
15
  import numpy as np
15
16
 
16
17
  from . import LOGGER, PROFILE
@@ -475,6 +476,22 @@ class OrderBook(MarketData):
475
476
  return [entry[1] for entry in self._book]
476
477
 
477
478
  def __init__(self, *, ticker: str, timestamp: float, bid: list[list[float | int]] = None, ask: list[list[float | int]] = None, **kwargs):
479
+ """
480
+ Initialize an OrderBook instance with market data.
481
+
482
+ Args:
483
+ ticker (str): The ticker symbol of the financial instrument.
484
+ timestamp (float): The timestamp of the market data.
485
+ bid (list[list[float | int]], optional): A list of bid data, where each sublist contains price, volume, and optionally, order details. Defaults to None.
486
+ ask (list[list[float | int]], optional): A list of ask data.
487
+ **kwargs: Additional key-value pairs for parsing extra data fields.
488
+
489
+ Attributes:
490
+ bid (OrderBook.Book): An instance of the Book class representing the bid side of the order book.
491
+ ask (OrderBook.Book): An instance of the Book class representing the ask side of the order book.
492
+
493
+ The `OrderBook` class extends `MarketData` and represents an order book with bids and asks. The `__init__` method initializes the order book with the provided bid and ask data and parses any additional keyword arguments to populate other attributes.
494
+ """
478
495
  super().__init__(ticker=ticker, timestamp=timestamp)
479
496
  self.update(
480
497
  bid=[] if bid is None else bid,
@@ -889,7 +906,7 @@ class DailyBar(BarData):
889
906
  def __init__(
890
907
  self, *,
891
908
  ticker: str,
892
- market_date: datetime.date, # The market date of the bar, if with 1D data, or the END date of the bar.
909
+ market_date: datetime.date | str, # The market date of the bar, if with 1D data, or the END date of the bar.
893
910
  timestamp: float = None,
894
911
  start_date: datetime.date = None,
895
912
  bar_span: datetime.timedelta | int = None, # expect to be a timedelta for several days, or the number of days
@@ -902,6 +919,9 @@ class DailyBar(BarData):
902
919
  trade_count: int = 0,
903
920
  **kwargs
904
921
  ):
922
+ if isinstance(market_date, str):
923
+ market_date = datetime.date.fromisoformat(market_date)
924
+
905
925
  if bar_span is None and start_date is None:
906
926
  raise ValueError('Must assign ether datetime.date or bar_span or both.')
907
927
  elif start_date is None:
@@ -941,6 +961,54 @@ class DailyBar(BarData):
941
961
  def __repr__(self):
942
962
  return f'<{self.__class__.__name__}>([{self.market_time:%Y-%m-%d}] {self.ticker}, open={self.open_price}, close={self.close_price}, high={self.high_price}, low={self.low_price})'
943
963
 
964
+ def to_json(self, fmt='str', **kwargs) -> str | dict:
965
+ data_dict = super().to_json(fmt='dict', **kwargs)
966
+
967
+ data_dict['market_date'] = self.market_date.isoformat()
968
+
969
+ if fmt == 'dict':
970
+ return data_dict
971
+ elif fmt == 'str':
972
+ return json.dumps(data_dict, **kwargs)
973
+ else:
974
+ raise ValueError(f'Invalid format {fmt}, except "dict" or "str".')
975
+
976
+ def to_list(self) -> list[float | int | str | bool]:
977
+ return [self.__class__.__name__,
978
+ self.ticker,
979
+ self.market_date.isoformat(),
980
+ self.timestamp,
981
+ self.high_price,
982
+ self.low_price,
983
+ self.open_price,
984
+ self.close_price,
985
+ self['bar_span'],
986
+ self.volume,
987
+ self.notional,
988
+ self.trade_count]
989
+
990
+ @classmethod
991
+ def from_list(cls, data_list: list[float | int | str | bool]) -> BarData:
992
+ (dtype, ticker, market_date, timestamp, high_price, low_price, open_price, close_price,
993
+ bar_span, volume, notional, trade_count) = data_list
994
+
995
+ if dtype != cls.__name__:
996
+ raise TypeError(f'dtype mismatch, expect {cls.__name__}, got {dtype}.')
997
+
998
+ return cls(
999
+ ticker=ticker,
1000
+ market_date=market_date,
1001
+ timestamp=timestamp,
1002
+ high_price=high_price,
1003
+ low_price=low_price,
1004
+ open_price=open_price,
1005
+ close_price=close_price,
1006
+ bar_span=datetime.timedelta(bar_span) if bar_span else None,
1007
+ volume=volume,
1008
+ notional=notional,
1009
+ trade_count=trade_count
1010
+ )
1011
+
944
1012
  @property
945
1013
  def bar_span(self) -> datetime.timedelta:
946
1014
  return datetime.timedelta(days=self['bar_span'])
@@ -982,6 +1050,8 @@ class TickData(MarketData):
982
1050
  ask_price: float = None,
983
1051
  ask_volume: float = None,
984
1052
  order_book: OrderBook = None,
1053
+ bid: list[list[float | int]] = None,
1054
+ ask: list[list[float | int]] = None,
985
1055
  total_traded_volume: float = 0.,
986
1056
  total_traded_notional: float = 0.,
987
1057
  total_trade_count: int = 0,
@@ -996,6 +1066,23 @@ class TickData(MarketData):
996
1066
  total_trade_count=total_trade_count,
997
1067
  )
998
1068
 
1069
+ if order_book is not None:
1070
+ self['order_book'] = {'bid': order_book['bid'], 'ask': order_book['ask']}
1071
+ elif bid and ask:
1072
+ self['order_book'] = {
1073
+ 'bid': sorted(bid, key=lambda _: _[0], reverse=True),
1074
+ 'ask': sorted(ask, key=lambda _: _[0], reverse=False)
1075
+ }
1076
+
1077
+ if bid_price is None:
1078
+ bid_price, _, *_ = bid[0]
1079
+ if bid_volume is None:
1080
+ _, bid_volume, *_ = bid[0]
1081
+ if ask_price is None:
1082
+ ask_price, _, *_ = ask[0]
1083
+ if ask_volume is None:
1084
+ _, ask_volume, *_ = ask[0]
1085
+
999
1086
  if bid_price is not None and math.isfinite(bid_price):
1000
1087
  self['bid_price'] = bid_price
1001
1088
 
@@ -1008,16 +1095,10 @@ class TickData(MarketData):
1008
1095
  if ask_volume is not None and math.isfinite(ask_volume):
1009
1096
  self['ask_volume'] = ask_volume
1010
1097
 
1011
- if order_book is not None:
1012
- self['order_book'] = order_book
1013
-
1014
- if kwargs:
1015
- self['additional'] = dict(kwargs)
1016
-
1017
1098
  @property
1018
1099
  def level_2(self) -> OrderBook | None:
1019
1100
  if 'order_book' in self:
1020
- return OrderBook(**self['order_book'])
1101
+ return OrderBook(ticker=self.ticker, timestamp=self.timestamp, **self['order_book'])
1021
1102
  else:
1022
1103
  return None
1023
1104
 
@@ -18,6 +18,9 @@ class Profile(object, metaclass=abc.ABCMeta):
18
18
 
19
19
  self.time_zone = None
20
20
 
21
+ def __repr__(self):
22
+ return f'<Profile {self.profile_id}>({id(self)})'
23
+
21
24
  def override_profile(self, profile: Self = None) -> Self:
22
25
  if profile is None:
23
26
  profile = PROFILE
@@ -11,12 +11,12 @@ from ..profile import Profile, PROFILE
11
11
 
12
12
 
13
13
  @overload
14
- def ts_indices(market_date, interval, session_start, session_end, session_break, time_zone, ts_mode, ts_format='timestamp') -> list[float]:
14
+ def ts_indices(market_date, interval, session_start, session_end, session_break, time_zone, ts_mode: Literal['start', 'end', 'both'], ts_format='timestamp') -> list[float]:
15
15
  ...
16
16
 
17
17
 
18
18
  @overload
19
- def ts_indices(market_date, interval, session_start, session_end, session_break, time_zone, ts_mode, ts_format='datetime') -> list[datetime.datetime]:
19
+ def ts_indices(market_date, interval, session_start, session_end, session_break, time_zone, ts_mode: Literal['start', 'end', 'both'], ts_format='datetime') -> list[datetime.datetime]:
20
20
  ...
21
21
 
22
22
 
@@ -158,7 +158,8 @@ def fake_data(
158
158
  session_start=session_start,
159
159
  session_end=session_end,
160
160
  session_break=session_break,
161
- time_zone=time_zone
161
+ time_zone=time_zone,
162
+ ts_mode='end',
162
163
  )
163
164
 
164
165
  ttl_days = kwargs.get('ttl_days', 252)