PyAlgoEngine 0.8.0a16__tar.gz → 0.8.0a17__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.0a16 → pyalgoengine-0.8.0a17}/PKG-INFO +1 -1
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/PyAlgoEngine.egg-info/PKG-INFO +1 -1
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/__init__.py +1 -1
- pyalgoengine-0.8.0a17/algo_engine/backtest/__init__.py +19 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/backtest/replay.py +134 -47
- pyalgoengine-0.8.0a16/algo_engine/backtest/__init__.py +0 -19
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/LICENSE +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/PyAlgoEngine.egg-info/SOURCES.txt +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/PyAlgoEngine.egg-info/dependency_links.txt +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/PyAlgoEngine.egg-info/requires.txt +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/PyAlgoEngine.egg-info/top_level.txt +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/README.md +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/apps/__init__.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/apps/backtest/__init__.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/apps/backtest/doc_server.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/apps/backtest/tester.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/apps/backtest/web_app.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/apps/bokeh_server.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/apps/demo/__init__.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/apps/demo/test.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/apps/sim_input/__init__.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/apps/sim_input/client.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/apps/sim_input/sim_keyboard.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/apps/sim_input/sim_mouse.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/apps/sim_input/window.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/backtest/__main__.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/backtest/metrics.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/backtest/sim_match.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/base/__init__.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/base/candlestick.pyi +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/base/console_utils.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/base/finance_decimal.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/base/market_data.pyi +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/base/market_data_buffer.pyi +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/base/market_utils_nt.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/base/market_utils_posix.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/base/technical_analysis.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/base/telemetrics.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/base/tick.pyi +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/base/trade_utils.pyi +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/base/transaction.pyi +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/engine/__init__.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/engine/algo_engine.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/engine/event_engine.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/engine/market_engine.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/engine/trade_engine.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/monitor/__init__.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/monitor/advanced_data_interface.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/profile/__init__.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/profile/cn.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/strategy/__init__.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/strategy/strategy_engine.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/utils/__init__.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/utils/commit_regularizer.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/utils/data_utils.py +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/setup.cfg +0 -0
- {pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/setup.py +0 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from .. import LOGGER
|
|
4
|
+
|
|
5
|
+
LOGGER = LOGGER.getChild('BackTest')
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def set_logger(logger: logging.Logger):
|
|
9
|
+
global LOGGER
|
|
10
|
+
LOGGER = logger
|
|
11
|
+
|
|
12
|
+
replay.LOGGER = LOGGER.getChild('Replay')
|
|
13
|
+
sim_match.LOGGER = LOGGER.getChild('SimMatch')
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
from .replay import PyDataScope, MarketDateCallable, MarketDataLoader, MarketDataBulkLoader, Replay, SimpleReplay, ProgressReplay, ProgressiveReplay
|
|
17
|
+
from .sim_match import SimMatch
|
|
18
|
+
|
|
19
|
+
__all__ = ['PyDataScope', 'MarketDateCallable', 'MarketDataLoader', 'MarketDataBulkLoader', 'Replay', 'SimpleReplay', 'ProgressReplay', 'ProgressiveReplay', 'SimMatch']
|
|
@@ -1,16 +1,96 @@
|
|
|
1
1
|
import abc
|
|
2
2
|
import datetime
|
|
3
|
+
import enum
|
|
3
4
|
import inspect
|
|
4
5
|
import operator
|
|
5
6
|
import warnings
|
|
6
7
|
from collections.abc import Sequence, Mapping, Iterable, Callable
|
|
7
|
-
from typing import Literal, Protocol, runtime_checkable, get_type_hints
|
|
8
|
+
from typing import Literal, Protocol, runtime_checkable, get_type_hints, Self
|
|
8
9
|
|
|
9
10
|
from . import LOGGER
|
|
10
11
|
from ..base import MarketData, DataType, MarketDataBuffer
|
|
11
12
|
|
|
12
13
|
LOGGER = LOGGER.getChild('Replay')
|
|
13
|
-
__all__ = ['MarketDateCallable', 'MarketDataLoader', 'MarketDataBulkLoader', 'Replay', 'SimpleReplay', 'ProgressReplay', 'ProgressiveReplay']
|
|
14
|
+
__all__ = ['PyDataScope', 'MarketDateCallable', 'MarketDataLoader', 'MarketDataBulkLoader', 'Replay', 'SimpleReplay', 'ProgressReplay', 'ProgressiveReplay']
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class PyDataScope(enum.Flag):
|
|
18
|
+
SCOPE_TRANSACTION = enum.auto()
|
|
19
|
+
SCOPE_ORDER = enum.auto()
|
|
20
|
+
SCOPE_TICK = enum.auto()
|
|
21
|
+
SCOPE_TICK_LITE = enum.auto()
|
|
22
|
+
|
|
23
|
+
SCOPE_ALL = SCOPE_TRANSACTION | SCOPE_ORDER | SCOPE_TICK
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
def _missing_(cls, value: Literal['TickData', 'TickDataLite', 'OrderData', 'TransactionData']):
|
|
27
|
+
if isinstance(value, int):
|
|
28
|
+
return super()._missing_(value)
|
|
29
|
+
|
|
30
|
+
if isinstance(value, str):
|
|
31
|
+
dtypes = value.split(',')
|
|
32
|
+
elif isinstance(value, Iterable):
|
|
33
|
+
dtypes = value
|
|
34
|
+
else:
|
|
35
|
+
raise TypeError(value)
|
|
36
|
+
|
|
37
|
+
_ = PyDataScope(0)
|
|
38
|
+
for dtype in dtypes:
|
|
39
|
+
_ = _.from_str(dtype)
|
|
40
|
+
return _
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def get_dtype(cls, dtype: DataType | str) -> str | Literal['TickData', 'TickDataLite', 'OrderData', 'TransactionData']:
|
|
44
|
+
match dtype:
|
|
45
|
+
case 'TickData' | 'TickDataLite' | 'OrderData' | 'TransactionData':
|
|
46
|
+
return str(dtype)
|
|
47
|
+
case 'TradeData': # handle the alias
|
|
48
|
+
return 'TransactionData'
|
|
49
|
+
case DataType.DTYPE_TICK | DataType.DTYPE_ORDER | DataType.DTYPE_TRANSACTION:
|
|
50
|
+
return DataType(dtype).name.removeprefix('DTYPE_').capitalize() + 'Data'
|
|
51
|
+
case DataType.DTYPE_TICK_LITE:
|
|
52
|
+
return 'Data'.join(_.capitalize() for _ in DataType(dtype).name.removeprefix('DTYPE_').split('_'))
|
|
53
|
+
case _:
|
|
54
|
+
raise ValueError(f'Invalid dtype {dtype}, expect str or int.')
|
|
55
|
+
|
|
56
|
+
def __iter__(self):
|
|
57
|
+
return iter(self.to_dtype())
|
|
58
|
+
|
|
59
|
+
def to_dtype(self) -> list[DataType]:
|
|
60
|
+
scope = list(super().__iter__())
|
|
61
|
+
scope_dtype = set()
|
|
62
|
+
|
|
63
|
+
for dtype in scope:
|
|
64
|
+
|
|
65
|
+
if dtype is PyDataScope.SCOPE_TRANSACTION:
|
|
66
|
+
scope_dtype.add(DataType.DTYPE_TRANSACTION)
|
|
67
|
+
elif dtype is PyDataScope.SCOPE_ORDER:
|
|
68
|
+
scope_dtype.add(DataType.DTYPE_ORDER)
|
|
69
|
+
elif dtype is PyDataScope.SCOPE_TICK_LITE:
|
|
70
|
+
scope_dtype.add(DataType.DTYPE_TICK_LITE)
|
|
71
|
+
elif dtype is PyDataScope.SCOPE_TICK:
|
|
72
|
+
scope_dtype.add(DataType.DTYPE_TICK)
|
|
73
|
+
|
|
74
|
+
return list(scope_dtype)
|
|
75
|
+
|
|
76
|
+
def to_int(self) -> list[int]:
|
|
77
|
+
return [int(_) for _ in self.to_dtype()]
|
|
78
|
+
|
|
79
|
+
def to_str(self) -> list[str]:
|
|
80
|
+
return [self.get_dtype(_) for _ in self.to_dtype()]
|
|
81
|
+
|
|
82
|
+
def from_str(self, dtype: Literal['TickData', 'TickDataLite', 'OrderData', 'TransactionData']) -> Self:
|
|
83
|
+
match dtype:
|
|
84
|
+
case 'TickData':
|
|
85
|
+
return self | self.SCOPE_TICK
|
|
86
|
+
case 'TickDataLite':
|
|
87
|
+
return self | self.SCOPE_TICK_LITE
|
|
88
|
+
case 'OrderData':
|
|
89
|
+
return self | self.SCOPE_ORDER
|
|
90
|
+
case 'TransactionData' | 'TradeData':
|
|
91
|
+
return self | self.SCOPE_TRANSACTION
|
|
92
|
+
case _:
|
|
93
|
+
raise ValueError(f'Invalid str {dtype}.')
|
|
14
94
|
|
|
15
95
|
|
|
16
96
|
@runtime_checkable
|
|
@@ -27,7 +107,7 @@ class MarketDataLoader(Protocol):
|
|
|
27
107
|
|
|
28
108
|
@runtime_checkable
|
|
29
109
|
class MarketDataBulkLoader(Protocol):
|
|
30
|
-
def __call__(self, market_date: datetime.date, tickers: Sequence[str], dtypes: Sequence[str | DataType]) -> Sequence[MarketData] | Mapping[float, MarketData] | MarketDataBuffer:
|
|
110
|
+
def __call__(self, market_date: datetime.date, tickers: Sequence[str], dtypes: Sequence[str | DataType] | PyDataScope) -> Sequence[MarketData] | Mapping[float, MarketData] | MarketDataBuffer:
|
|
31
111
|
pass
|
|
32
112
|
|
|
33
113
|
|
|
@@ -40,13 +120,16 @@ def check_protocol_signature(func: Callable, protocol: type) -> bool:
|
|
|
40
120
|
|
|
41
121
|
proto_params = list(proto_sig.parameters.values())[1:] # Skip 'self'
|
|
42
122
|
func_params = list(func_sig.parameters.values())
|
|
123
|
+
enable_keywords = False
|
|
43
124
|
|
|
44
125
|
# Check for *args (VAR_POSITIONAL) — not allowed
|
|
45
126
|
for p in func_params:
|
|
46
127
|
if p.kind == inspect.Parameter.VAR_POSITIONAL:
|
|
47
128
|
raise TypeError(f"{func.__name__} uses *args, which is not allowed")
|
|
129
|
+
elif p.kind == inspect.Parameter.VAR_KEYWORD:
|
|
130
|
+
enable_keywords = True
|
|
48
131
|
|
|
49
|
-
#
|
|
132
|
+
# Extract positional args (POSITIONAL_ONLY or POSITIONAL_OR_KEYWORD)
|
|
50
133
|
proto_arg_names = [p.name for p in proto_params if p.kind in (
|
|
51
134
|
inspect.Parameter.POSITIONAL_ONLY,
|
|
52
135
|
inspect.Parameter.POSITIONAL_OR_KEYWORD
|
|
@@ -57,8 +140,13 @@ def check_protocol_signature(func: Callable, protocol: type) -> bool:
|
|
|
57
140
|
inspect.Parameter.POSITIONAL_OR_KEYWORD
|
|
58
141
|
)]
|
|
59
142
|
|
|
60
|
-
if
|
|
61
|
-
|
|
143
|
+
# Check if required positional args match (ignore **kwargs)
|
|
144
|
+
if not enable_keywords and sorted(proto_arg_names) != sorted(func_arg_names):
|
|
145
|
+
warnings.warn(
|
|
146
|
+
f"{func} argument names {func_arg_names} do not match protocol {proto_arg_names}",
|
|
147
|
+
stacklevel=2
|
|
148
|
+
)
|
|
149
|
+
return False
|
|
62
150
|
|
|
63
151
|
# Type hint comparison (warn if mismatched, but allow)
|
|
64
152
|
proto_hints = get_type_hints(protocol.__call__)
|
|
@@ -104,32 +192,26 @@ class Replay(object, metaclass=abc.ABCMeta):
|
|
|
104
192
|
if eod is not None:
|
|
105
193
|
self.add_eod(eod)
|
|
106
194
|
|
|
107
|
-
def add_bod(self, func: MarketDateCallable):
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
195
|
+
def add_bod(self, func: MarketDateCallable, priority: int = None) -> None:
|
|
196
|
+
if priority is None:
|
|
197
|
+
self.bod.append(func)
|
|
198
|
+
else:
|
|
199
|
+
self.bod.insert(priority, func)
|
|
112
200
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
case DataType.DTYPE_TICK | DataType.DTYPE_ORDER | DataType.DTYPE_TRANSACTION:
|
|
119
|
-
return DataType(dtype).name.removeprefix('DTYPE_').capitalize() + 'Data'
|
|
120
|
-
case DataType.DTYPE_TICK_LITE:
|
|
121
|
-
return 'Data'.join(_.capitalize() for _ in DataType(dtype).name.removeprefix('DTYPE_').split('_'))
|
|
122
|
-
case _:
|
|
123
|
-
raise ValueError(f'Invalid dtype {dtype}, expect str or int.')
|
|
201
|
+
def add_eod(self, func: MarketDateCallable, priority: int = None):
|
|
202
|
+
if priority is None:
|
|
203
|
+
self.eod.append(func)
|
|
204
|
+
else:
|
|
205
|
+
self.eod.insert(priority, func)
|
|
124
206
|
|
|
125
207
|
def add_subscription(self, ticker: str, dtype: DataType | str):
|
|
126
|
-
dtype =
|
|
208
|
+
dtype = PyDataScope.get_dtype(dtype)
|
|
127
209
|
topic = f'{ticker}.{dtype}'
|
|
128
210
|
|
|
129
211
|
self.subscription[topic] = (ticker, dtype)
|
|
130
212
|
|
|
131
213
|
def remove_subscription(self, ticker: str, dtype: DataType | str):
|
|
132
|
-
dtype =
|
|
214
|
+
dtype = PyDataScope.get_dtype(dtype)
|
|
133
215
|
topic = f'{ticker}.{dtype}'
|
|
134
216
|
|
|
135
217
|
try:
|
|
@@ -208,7 +290,9 @@ class SimpleReplay(Replay):
|
|
|
208
290
|
return f'{self.__class__.__name__}{{id={id(self)}, from={self.start_date}, to={self.end_date}}}'
|
|
209
291
|
|
|
210
292
|
def _bulk_load_protocol(self):
|
|
293
|
+
LOGGER.info(f'{self} loading {self._market_date} {(', '.join(self.dtypes)) if self.dtypes else 'data'} for {len(self.tickers)} tickers...')
|
|
211
294
|
buffer = self.loader(market_date=self._market_date, tickers=self.tickers, dtypes=self.dtypes)
|
|
295
|
+
LOGGER.info(f'{self} sorting {self._market_date} data...')
|
|
212
296
|
buffer.sort()
|
|
213
297
|
|
|
214
298
|
if isinstance(buffer, MarketDataBuffer):
|
|
@@ -220,11 +304,12 @@ class SimpleReplay(Replay):
|
|
|
220
304
|
elif isinstance(buffer, Mapping):
|
|
221
305
|
self._buffer = iter(buffer.values())
|
|
222
306
|
self._buffer_size = len(buffer)
|
|
307
|
+
LOGGER.info(f'{self} {self._market_date} total {self._buffer_size:,} items loaded.')
|
|
223
308
|
|
|
224
309
|
def _individual_load_protocol(self):
|
|
225
310
|
buffer = []
|
|
226
311
|
for topic, (_ticker, _dtype) in self.subscription.items():
|
|
227
|
-
LOGGER.info(f'{self} loading {self._market_date} {_ticker} {_dtype}')
|
|
312
|
+
LOGGER.info(f'{self} loading {self._market_date} {_ticker} {_dtype}...')
|
|
228
313
|
data = self.loader(market_date=self._market_date, ticker=_ticker, dtype=_dtype)
|
|
229
314
|
if isinstance(data, Mapping):
|
|
230
315
|
buffer.extend(list(data.values()))
|
|
@@ -232,14 +317,16 @@ class SimpleReplay(Replay):
|
|
|
232
317
|
buffer.extend(data)
|
|
233
318
|
else:
|
|
234
319
|
raise TypeError(f'The loader {self.loader} returned {type(data)}. Expect a sequence or mapping of MarketData')
|
|
320
|
+
LOGGER.info(f'{self} sorting {self._market_date} data...')
|
|
235
321
|
buffer.sort(key=operator.attrgetter('timestamp', 'ticker', '_dtype'))
|
|
236
322
|
self._buffer = iter(buffer)
|
|
237
323
|
self._buffer_size = len(buffer)
|
|
324
|
+
LOGGER.info(f'{self} {self._market_date} total {self._buffer_size:,} items loaded.')
|
|
238
325
|
|
|
239
326
|
def _safe_load(self):
|
|
240
327
|
if self.loader is None:
|
|
241
328
|
assert hasattr(self, '_buffer') and isinstance(self._buffer, Iterable), f'Without assigning a data loader, the _buffer of {self.__class__.__name__} should be set in bod process.'
|
|
242
|
-
return
|
|
329
|
+
return None
|
|
243
330
|
|
|
244
331
|
is_bulk_loader = check_protocol_signature(self.loader, MarketDataBulkLoader)
|
|
245
332
|
is_individual_loader = check_protocol_signature(self.loader, MarketDataLoader)
|
|
@@ -320,29 +407,26 @@ class ProgressReplay(SimpleReplay):
|
|
|
320
407
|
'miniters': 0.001,
|
|
321
408
|
**tqdm_kwargs
|
|
322
409
|
}
|
|
323
|
-
self.
|
|
410
|
+
self._pbar = None
|
|
411
|
+
self.add_bod(self._update_progress_bar, priority=0)
|
|
324
412
|
|
|
325
413
|
def __iter__(self):
|
|
326
414
|
from tqdm.auto import tqdm
|
|
327
415
|
self._pbar = tqdm(**self._tqdm_kwargs)
|
|
328
|
-
|
|
416
|
+
return super().__iter__()
|
|
329
417
|
|
|
418
|
+
def __next__(self) -> MarketData:
|
|
330
419
|
try:
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
except StopIteration:
|
|
338
|
-
break
|
|
339
|
-
finally:
|
|
420
|
+
result = super().__next__()
|
|
421
|
+
if self._pbar:
|
|
422
|
+
self._pbar.update(self.progress)
|
|
423
|
+
self._pbar.refresh()
|
|
424
|
+
return result
|
|
425
|
+
except StopIteration:
|
|
340
426
|
if self._pbar is not None:
|
|
341
427
|
self._pbar.close()
|
|
342
428
|
self._pbar = None
|
|
343
|
-
|
|
344
|
-
def __next__(self) -> MarketData:
|
|
345
|
-
raise RuntimeError("MarketDataBufferReplay should be used as an iterator context")
|
|
429
|
+
raise
|
|
346
430
|
|
|
347
431
|
def _update_progress_bar(self, market_date: datetime.date):
|
|
348
432
|
if self._pbar:
|
|
@@ -378,7 +462,7 @@ class ProgressiveReplay(SimpleReplay):
|
|
|
378
462
|
eod: MarketDateCallable = None,
|
|
379
463
|
**progress_config
|
|
380
464
|
) -> None:
|
|
381
|
-
warnings.
|
|
465
|
+
warnings.warn('User ProgressReplay instead!', DeprecationWarning, stacklevel=2)
|
|
382
466
|
self.loader = loader
|
|
383
467
|
super().__init__(loader=loader, market_date=market_date, start_date=start_date, end_date=end_date, calendar=calendar, bod=bod, eod=eod)
|
|
384
468
|
|
|
@@ -410,6 +494,8 @@ class ProgressiveReplay(SimpleReplay):
|
|
|
410
494
|
tasks=1,
|
|
411
495
|
**progress_config
|
|
412
496
|
)
|
|
497
|
+
self._pbar = None
|
|
498
|
+
self.add_bod(self._update_progress_bar, priority=0)
|
|
413
499
|
|
|
414
500
|
def __iter__(self):
|
|
415
501
|
from ..base import Progress
|
|
@@ -419,16 +505,17 @@ class ProgressiveReplay(SimpleReplay):
|
|
|
419
505
|
def __next__(self) -> MarketData:
|
|
420
506
|
try:
|
|
421
507
|
result = super().__next__()
|
|
422
|
-
self._pbar
|
|
508
|
+
if self._pbar:
|
|
509
|
+
self._pbar.done_tasks = self.progress
|
|
423
510
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
511
|
+
if (not self._pbar.tick_size) \
|
|
512
|
+
or self._pbar.progress >= self._pbar.tick_size + self._pbar.last_output \
|
|
513
|
+
or self._pbar.is_done:
|
|
514
|
+
self._pbar.output()
|
|
428
515
|
|
|
429
516
|
return result
|
|
430
517
|
except StopIteration:
|
|
431
|
-
if not self._pbar.is_done:
|
|
518
|
+
if self._pbar is not None and not self._pbar.is_done:
|
|
432
519
|
self.progress.done_tasks = 1
|
|
433
520
|
self._pbar.output()
|
|
434
521
|
raise
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
|
|
3
|
-
from .. import LOGGER
|
|
4
|
-
|
|
5
|
-
LOGGER = LOGGER.getChild('BackTest')
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def set_logger(logger: logging.Logger):
|
|
9
|
-
global LOGGER
|
|
10
|
-
LOGGER = logger
|
|
11
|
-
|
|
12
|
-
replay.LOGGER = LOGGER.getChild('Replay')
|
|
13
|
-
sim_match.LOGGER = LOGGER.getChild('SimMatch')
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
from .replay import MarketDateCallable, MarketDataLoader, MarketDataBulkLoader, Replay, SimpleReplay, ProgressReplay, ProgressiveReplay
|
|
17
|
-
from .sim_match import SimMatch
|
|
18
|
-
|
|
19
|
-
__all__ = ['MarketDateCallable', 'MarketDataLoader', 'MarketDataBulkLoader', 'Replay', 'SimpleReplay', 'ProgressReplay', 'ProgressiveReplay', 'SimMatch']
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyalgoengine-0.8.0a16 → pyalgoengine-0.8.0a17}/algo_engine/monitor/advanced_data_interface.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|