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.
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/PKG-INFO +2 -6
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/PyAlgoEngine.egg-info/PKG-INFO +2 -6
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/__init__.py +1 -1
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/backtest/replay.py +46 -28
- pyalgoengine-0.8.0a12/algo_engine/backtest/sim_match.py +505 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/base/__init__.py +4 -4
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/base/candlestick.pyi +4 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/base/market_data.pyi +4 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/base/market_data_buffer.pyi +5 -2
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/base/tick.pyi +17 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/base/trade_utils.pyi +15 -10
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/base/transaction.pyi +35 -20
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/engine/market_engine.py +36 -55
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/monitor/advanced_data_interface.py +134 -39
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/setup.py +7 -5
- pyalgoengine-0.8.0a10/algo_engine/backtest/sim_match.py +0 -333
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/LICENSE +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/PyAlgoEngine.egg-info/SOURCES.txt +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/PyAlgoEngine.egg-info/dependency_links.txt +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/PyAlgoEngine.egg-info/requires.txt +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/PyAlgoEngine.egg-info/top_level.txt +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/README.md +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/apps/__init__.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/apps/backtest/__init__.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/apps/backtest/doc_server.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/apps/backtest/tester.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/apps/backtest/web_app.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/apps/bokeh_server.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/apps/demo/__init__.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/apps/demo/test.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/apps/sim_input/__init__.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/apps/sim_input/client.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/apps/sim_input/sim_keyboard.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/apps/sim_input/sim_mouse.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/apps/sim_input/window.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/backtest/__init__.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/backtest/__main__.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/backtest/metrics.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/base/console_utils.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/base/finance_decimal.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/base/market_utils_nt.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/base/market_utils_posix.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/base/technical_analysis.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/base/telemetrics.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/engine/__init__.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/engine/algo_engine.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/engine/event_engine.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/engine/trade_engine.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/monitor/__init__.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/profile/__init__.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/profile/cn.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/strategy/__init__.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/strategy/strategy_engine.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/utils/__init__.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/utils/commit_regularizer.py +0 -0
- {pyalgoengine-0.8.0a10 → pyalgoengine-0.8.0a12}/algo_engine/utils/data_utils.py +0 -0
- {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.
|
|
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.
|
|
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.
|
|
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.
|
|
10
|
+
Requires-Python: >=3.12
|
|
15
11
|
Description-Content-Type: text/markdown
|
|
16
12
|
License-File: LICENSE
|
|
17
13
|
Requires-Dist: numpy
|
|
@@ -2,10 +2,11 @@ import abc
|
|
|
2
2
|
import datetime
|
|
3
3
|
import inspect
|
|
4
4
|
import operator
|
|
5
|
-
from
|
|
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,
|
|
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,
|
|
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
|
|
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.
|
|
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
|
-
|
|
225
|
-
data = self.replay_task
|
|
242
|
+
try:
|
|
243
|
+
data = next(self.replay_task)
|
|
226
244
|
self.task_progress += 1
|
|
227
|
-
|
|
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
|
|
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.
|
|
246
|
-
current_progress = (self.date_progress - 1 + (self.task_progress /
|
|
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
|