PyAlgoEngine 0.8.0a10__tar.gz → 0.8.0a12__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 (57) hide show
  1. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/PKG-INFO +2 -6
  2. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/PyAlgoEngine.egg-info/PKG-INFO +2 -6
  3. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/__init__.py +1 -1
  4. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/backtest/replay.py +46 -28
  5. pyalgoengine-0.8.0a12/algo_engine/backtest/sim_match.py +505 -0
  6. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/base/__init__.py +4 -4
  7. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/base/candlestick.pyi +4 -0
  8. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/base/market_data.pyi +4 -0
  9. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/base/market_data_buffer.pyi +5 -2
  10. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/base/tick.pyi +17 -0
  11. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/base/trade_utils.pyi +15 -10
  12. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/base/transaction.pyi +35 -20
  13. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/engine/market_engine.py +36 -55
  14. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/monitor/advanced_data_interface.py +134 -39
  15. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/setup.py +7 -5
  16. pyalgoengine-0.8.0a10/algo_engine/backtest/sim_match.py +0 -333
  17. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/LICENSE +0 -0
  18. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/PyAlgoEngine.egg-info/SOURCES.txt +0 -0
  19. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/PyAlgoEngine.egg-info/dependency_links.txt +0 -0
  20. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/PyAlgoEngine.egg-info/requires.txt +0 -0
  21. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/PyAlgoEngine.egg-info/top_level.txt +0 -0
  22. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/README.md +0 -0
  23. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/apps/__init__.py +0 -0
  24. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/apps/backtest/__init__.py +0 -0
  25. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/apps/backtest/doc_server.py +0 -0
  26. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/apps/backtest/tester.py +0 -0
  27. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/apps/backtest/web_app.py +0 -0
  28. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/apps/bokeh_server.py +0 -0
  29. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/apps/demo/__init__.py +0 -0
  30. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/apps/demo/test.py +0 -0
  31. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/apps/sim_input/__init__.py +0 -0
  32. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/apps/sim_input/client.py +0 -0
  33. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/apps/sim_input/sim_keyboard.py +0 -0
  34. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/apps/sim_input/sim_mouse.py +0 -0
  35. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/apps/sim_input/window.py +0 -0
  36. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/backtest/__init__.py +0 -0
  37. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/backtest/__main__.py +0 -0
  38. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/backtest/metrics.py +0 -0
  39. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/base/console_utils.py +0 -0
  40. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/base/finance_decimal.py +0 -0
  41. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/base/market_utils_nt.py +0 -0
  42. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/base/market_utils_posix.py +0 -0
  43. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/base/technical_analysis.py +0 -0
  44. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/base/telemetrics.py +0 -0
  45. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/engine/__init__.py +0 -0
  46. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/engine/algo_engine.py +0 -0
  47. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/engine/event_engine.py +0 -0
  48. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/engine/trade_engine.py +0 -0
  49. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/monitor/__init__.py +0 -0
  50. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/profile/__init__.py +0 -0
  51. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/profile/cn.py +0 -0
  52. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/strategy/__init__.py +0 -0
  53. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/strategy/strategy_engine.py +0 -0
  54. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/utils/__init__.py +0 -0
  55. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/utils/commit_regularizer.py +0 -0
  56. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/utils/data_utils.py +0 -0
  57. {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/setup.cfg +0 -0
@@ -1,17 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyAlgoEngine
3
- Version: 0.8.0a10
3
+ Version: 0.8.0a12
4
4
  Summary: Basic algo engine
5
5
  Home-page: https://github.com/BolunHan/PyAlgoEngine
6
6
  Author: Bolun.Han
7
7
  Author-email: Bolun.Han@outlook.com
8
- Classifier: Programming Language :: Python :: 3.8
9
- Classifier: Programming Language :: Python :: 3.9
10
- Classifier: Programming Language :: Python :: 3.10
11
- Classifier: Programming Language :: Python :: 3.11
12
8
  Classifier: Programming Language :: Python :: 3.12
13
9
  Classifier: Operating System :: OS Independent
14
- Requires-Python: >=3.1
10
+ Requires-Python: >=3.12
15
11
  Description-Content-Type: text/markdown
16
12
  License-File: LICENSE
17
13
  Requires-Dist: numpy
@@ -1,17 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyAlgoEngine
3
- Version: 0.8.0a10
3
+ Version: 0.8.0a12
4
4
  Summary: Basic algo engine
5
5
  Home-page: https://github.com/BolunHan/PyAlgoEngine
6
6
  Author: Bolun.Han
7
7
  Author-email: Bolun.Han@outlook.com
8
- Classifier: Programming Language :: Python :: 3.8
9
- Classifier: Programming Language :: Python :: 3.9
10
- Classifier: Programming Language :: Python :: 3.10
11
- Classifier: Programming Language :: Python :: 3.11
12
8
  Classifier: Programming Language :: Python :: 3.12
13
9
  Classifier: Operating System :: OS Independent
14
- Requires-Python: >=3.1
10
+ Requires-Python: >=3.12
15
11
  Description-Content-Type: text/markdown
16
12
  License-File: LICENSE
17
13
  Requires-Dist: numpy
@@ -1,4 +1,4 @@
1
- __version__ = "0.8.0.alpha10"
1
+ __version__ = "0.8.0.alpha12"
2
2
 
3
3
  import logging
4
4
  import os
@@ -2,10 +2,11 @@ import abc
2
2
  import datetime
3
3
  import inspect
4
4
  import operator
5
- from typing import Iterable
5
+ from collections.abc import Mapping, Sequence, Iterator
6
+ from typing import Iterable, Protocol
6
7
 
7
8
  from . import LOGGER
8
- from ..base import Progress, TickData, TransactionData, TradeData, OrderBook, MarketData
9
+ from ..base import Progress, TickData, TransactionData, TradeData, OrderData, MarketData, MarketDataBuffer
9
10
 
10
11
  LOGGER = LOGGER.getChild('Replay')
11
12
 
@@ -86,6 +87,11 @@ class SimpleReplay(Replay):
86
87
  return self
87
88
 
88
89
 
90
+ class DataLoader(Protocol):
91
+ def __call__(self, market_date: datetime.date, ticker: str, dtype: str) -> Mapping[float, MarketData] | Sequence[MarketData] | MarketDataBuffer:
92
+ ...
93
+
94
+
89
95
  class ProgressiveReplay(Replay):
90
96
  """
91
97
  progressively loading and replaying market data
@@ -103,7 +109,7 @@ class ProgressiveReplay(Replay):
103
109
 
104
110
  def __init__(
105
111
  self,
106
- loader,
112
+ loader: DataLoader,
107
113
  **kwargs
108
114
  ):
109
115
  self.loader = loader
@@ -117,7 +123,8 @@ class ProgressiveReplay(Replay):
117
123
 
118
124
  self.replay_subscription = {}
119
125
  self.replay_calendar = []
120
- self.replay_task = []
126
+ self.replay_task: Iterator | None = None
127
+ self.replay_task_length: int = 0
121
128
  self.replay_status = {}
122
129
 
123
130
  self.date_progress = 0
@@ -125,7 +132,7 @@ class ProgressiveReplay(Replay):
125
132
  self.progress = Progress(tasks=1, **kwargs)
126
133
 
127
134
  tickers: list[str] = kwargs.pop('ticker', kwargs.pop('tickers', []))
128
- dtypes: list[str | type] = kwargs.pop('dtype', kwargs.pop('dtypes', [TradeData, TransactionData, OrderBook, TickData]))
135
+ dtypes: list[str | type] = kwargs.pop('dtype', kwargs.pop('dtypes', [TradeData, TransactionData, OrderData, TickData]))
129
136
 
130
137
  if not all([arg_name in inspect.getfullargspec(loader).args for arg_name in ['market_date', 'ticker', 'dtype']]):
131
138
  raise TypeError('loader function has 3 requires args, market_date, ticker and dtype.')
@@ -193,6 +200,8 @@ class ProgressiveReplay(Replay):
193
200
  self.replay_status = {market_date: 'skipped' if market_date < self.market_date else 'idle' for market_date in self.replay_calendar}
194
201
 
195
202
  self.task_progress = 0
203
+ self.replay_task_length = 0
204
+ self.replay_task = None
196
205
  self.date_progress = sum([1 for _ in self.replay_calendar if _ < self.market_date])
197
206
  self.progress.reset()
198
207
 
@@ -200,36 +209,45 @@ class ProgressiveReplay(Replay):
200
209
  self.progress.done_tasks = self.date_progress / len(self.replay_calendar)
201
210
 
202
211
  def next_trade_day(self):
203
- if self.date_progress < len(self.replay_calendar):
204
- market_date = self.market_date = self.replay_calendar[self.date_progress]
205
- self.replay_status[market_date] = 'started'
206
- self.progress.prompt = f'Replay {market_date:%Y-%m-%d} ({self.date_progress + 1} / {len(self.replay_calendar)}):'
207
- for topic in self.replay_subscription:
208
- ticker, dtype = self.replay_subscription[topic]
209
- LOGGER.info(f'{self} loading {market_date} {ticker} {dtype}')
210
- data = self.loader(market_date=market_date, ticker=ticker, dtype=dtype)
211
- if isinstance(data, dict):
212
- self.replay_task.extend(list(data.values()))
213
- elif isinstance(data, (list, tuple)):
214
- self.replay_task.extend(data)
215
-
216
- LOGGER.info(f'{market_date} data loaded! {len(self.replay_task):,} entries.')
217
- self.date_progress += 1
218
- else:
212
+ if self.date_progress >= len(self.replay_calendar):
219
213
  raise StopIteration()
220
214
 
221
- self.replay_task.sort(key=operator.attrgetter('timestamp', 'ticker', '__class__.__name__'))
215
+ self.market_date = market_date = self.replay_calendar[self.date_progress]
216
+ self.replay_status[market_date] = 'started'
217
+ self.progress.prompt = f'Replay {market_date:%Y-%m-%d} ({self.date_progress + 1} / {len(self.replay_calendar)}):'
218
+
219
+ for topic in self.replay_subscription:
220
+ ticker, dtype = self.replay_subscription[topic]
221
+ LOGGER.info(f'{self} loading {market_date} {ticker} {dtype}...')
222
+ data = self.loader(market_date=market_date, ticker=ticker, dtype=dtype)
223
+ if isinstance(data, Mapping):
224
+ data = [data[ts] for ts in sorted(data)] # expect to be a mapping of ts and data
225
+ self.replay_task = iter(data)
226
+ self.replay_task_length = len(data)
227
+ elif isinstance(data, Sequence):
228
+ data = sorted(data, key=operator.attrgetter('timestamp', 'ticker', '__class__.__name__'))
229
+ self.replay_task = iter(data)
230
+ self.replay_task_length = len(data)
231
+ elif isinstance(data, MarketDataBuffer):
232
+ data.sort()
233
+ self.replay_task = iter(data)
234
+ self.replay_task_length = len(data)
235
+ else:
236
+ raise TypeError(f'Invalid return type of dataloader, expect list, tuple, dict or MarketDataBuffer, got {type(data)}.')
237
+
238
+ LOGGER.info(f'{market_date} data loaded! {self.replay_task_length:,} entries.')
239
+ self.date_progress += 1
222
240
 
223
241
  def next_task(self):
224
- if self.task_progress < len(self.replay_task):
225
- data = self.replay_task[self.task_progress]
242
+ try:
243
+ data = next(self.replay_task)
226
244
  self.task_progress += 1
227
- else:
245
+ except StopIteration:
228
246
  if self.eod is not None and self.replay_status[self.market_date] == 'started':
229
247
  self.eod(market_date=self.market_date, replay=self)
230
248
  self.replay_status[self.market_date] = 'done'
231
249
 
232
- self.replay_task.clear()
250
+ self.replay_task = None
233
251
  self.task_progress = 0
234
252
 
235
253
  if self.bod is not None and self.date_progress < len(self.replay_calendar):
@@ -242,8 +260,8 @@ class ProgressiveReplay(Replay):
242
260
 
243
261
  data = self.next_task()
244
262
 
245
- if self.replay_task and self.replay_calendar:
246
- current_progress = (self.date_progress - 1 + (self.task_progress / len(self.replay_task))) / len(self.replay_calendar)
263
+ if self.replay_task_length and self.replay_calendar:
264
+ current_progress = (self.date_progress - 1 + (self.task_progress / self.replay_task_length)) / len(self.replay_calendar)
247
265
  self.progress.done_tasks = current_progress
248
266
  else:
249
267
  self.progress.done_tasks = 1