PyAlgoEngine 0.4.2__tar.gz → 0.4.3__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 (33) hide show
  1. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/LICENSE +0 -0
  2. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/PKG-INFO +1 -1
  3. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/PyAlgoEngine.egg-info/PKG-INFO +1 -1
  4. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/PyAlgoEngine.egg-info/SOURCES.txt +0 -0
  5. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/PyAlgoEngine.egg-info/dependency_links.txt +0 -0
  6. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/PyAlgoEngine.egg-info/requires.txt +0 -0
  7. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/PyAlgoEngine.egg-info/top_level.txt +0 -0
  8. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/README.md +0 -0
  9. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/algo_engine/__init__.py +1 -1
  10. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/algo_engine/back_test/__init__.py +19 -19
  11. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/algo_engine/back_test/__main__.py +0 -0
  12. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/algo_engine/back_test/replay.py +258 -258
  13. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/algo_engine/back_test/sim_match.py +295 -295
  14. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/algo_engine/base/__init__.py +28 -28
  15. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/algo_engine/base/console_utils.py +0 -0
  16. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/algo_engine/base/finance_decimal.py +0 -0
  17. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/algo_engine/base/market_utils.py +0 -0
  18. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/algo_engine/base/technical_analysis.py +0 -0
  19. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/algo_engine/base/telemetrics.py +78 -78
  20. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/algo_engine/base/trade_utils.py +0 -0
  21. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/algo_engine/engine/__init__.py +0 -0
  22. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/algo_engine/engine/algo_engine.py +0 -0
  23. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/algo_engine/engine/event_engine.py +0 -0
  24. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/algo_engine/engine/market_engine.py +0 -0
  25. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/algo_engine/engine/trade_engine.py +0 -0
  26. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/algo_engine/monitor/__init__.py +15 -15
  27. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/algo_engine/monitor/advanced_data_interface.py +239 -239
  28. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/algo_engine/profile/__init__.py +72 -72
  29. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/algo_engine/profile/cn.py +187 -187
  30. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/algo_engine/strategy/__init__.py +0 -0
  31. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/algo_engine/strategy/strategy_engine.py +0 -0
  32. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/setup.cfg +0 -0
  33. {PyAlgoEngine-0.4.2 → PyAlgoEngine-0.4.3}/setup.py +0 -0
File without changes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyAlgoEngine
3
- Version: 0.4.2
3
+ Version: 0.4.3
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.4.2
3
+ Version: 0.4.3
4
4
  Summary: Basic algo engine
5
5
  Home-page: https://github.com/BolunHan/PyAlgoEngine
6
6
  Author: Bolun.Han
File without changes
@@ -1,4 +1,4 @@
1
- __version__ = "0.4.2"
1
+ __version__ = "0.4.3"
2
2
 
3
3
  import logging
4
4
  import os
@@ -1,19 +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 Replay, SimpleReplay, ProgressiveReplay
17
- from .sim_match import SimMatch
18
-
19
- __all__ = ['Replay', 'SimpleReplay', 'ProgressiveReplay', 'SimMatch']
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 Replay, SimpleReplay, ProgressiveReplay
17
+ from .sim_match import SimMatch
18
+
19
+ __all__ = ['Replay', 'SimpleReplay', 'ProgressiveReplay', 'SimMatch']
@@ -1,258 +1,258 @@
1
- import abc
2
- import datetime
3
- import inspect
4
- from typing import Iterable
5
-
6
- from . import LOGGER
7
- from ..base import Progress, TickData, TransactionData, TradeData, OrderBook
8
-
9
- LOGGER = LOGGER.getChild('Replay')
10
-
11
-
12
- class Replay(object, metaclass=abc.ABCMeta):
13
- @abc.abstractmethod
14
- def __next__(self): ...
15
-
16
- @abc.abstractmethod
17
- def __iter__(self): ...
18
-
19
-
20
- class SimpleReplay(Replay):
21
- def __init__(self, **kwargs):
22
- self.eod = kwargs.pop('eod', None)
23
- self.bod = kwargs.pop('bod', None)
24
-
25
- self.replay_task = []
26
- self.task_progress = 0
27
- self.task_date = None
28
- self.progress = Progress(tasks=1, **kwargs)
29
-
30
- def load(self, data):
31
- if isinstance(data, dict):
32
- self.replay_task.extend(list(data.values()))
33
- else:
34
- self.replay_task.extend(data)
35
-
36
- def reset(self):
37
- self.replay_task.clear()
38
- self.task_progress = 0
39
- self.task_date = None
40
- self.progress.reset()
41
-
42
- def next_task(self):
43
- if self.task_progress < len(self.replay_task):
44
- market_data = self.replay_task[self.task_progress]
45
- market_time = market_data.market_time
46
-
47
- if isinstance(market_time, datetime.datetime):
48
- market_date = market_time.date()
49
- else:
50
- market_date = market_time
51
-
52
- if market_date != self.task_date:
53
- if callable(self.eod) and self.task_date:
54
- self.eod(self.task_date)
55
-
56
- self.task_date = market_date
57
- self.progress.prompt = f'Replay {market_date:%Y-%m-%d}:'
58
-
59
- if callable(self.bod):
60
- self.bod(market_date)
61
-
62
- self.progress.done_tasks = self.task_progress / len(self.replay_task)
63
-
64
- if (not self.progress.tick_size) or self.progress.progress >= self.progress.tick_size + self.progress.last_output:
65
- self.progress.output()
66
-
67
- self.task_progress += 1
68
- else:
69
- raise StopIteration()
70
-
71
- return market_data
72
-
73
- def __next__(self):
74
- try:
75
- return self.next_task()
76
- except StopIteration:
77
- if not self.progress.is_done:
78
- self.progress.done_tasks = 1
79
- self.progress.output()
80
-
81
- self.reset()
82
- raise StopIteration()
83
-
84
- def __iter__(self):
85
- return self
86
-
87
-
88
- class ProgressiveReplay(Replay):
89
- """
90
- progressively loading and replaying market data
91
-
92
- requires arguments
93
- loader: a data loading function. Expect loader = Callable(market_date: datetime.date, ticker: str, dtype: str| type) -> dict[any, MarketData]
94
- start_date & end_date: the given replay period
95
- or calendar: the given replay calendar.
96
-
97
- accepts kwargs:
98
- ticker / tickers: the given symbols to replay, expect a str| list[str]
99
- dtype / dtypes: the given dtype(s) of symbol to replay, expect a str | type, list[str | type]. default = all, which is (TradeData, TickData, OrderBook)
100
- subscription / subscribe: the given ticker-dtype pair to replay, expect a list[dict[str, str | type]]
101
- """
102
-
103
- def __init__(
104
- self,
105
- loader,
106
- **kwargs
107
- ):
108
- self.loader = loader
109
- self.start_date: datetime.date | None = kwargs.pop('start_date', None)
110
- self.end_date: datetime.date | None = kwargs.pop('end_date', None)
111
- self.calendar: list[datetime.date] | None = kwargs.pop('calendar', None)
112
-
113
- self.eod = kwargs.pop('eod', None)
114
- self.bod = kwargs.pop('bod', None)
115
-
116
- self.replay_subscription = {}
117
- self.replay_calendar = []
118
- self.replay_task = []
119
-
120
- self.date_progress = 0
121
- self.task_progress = 0
122
- self.progress = Progress(tasks=1, **kwargs)
123
-
124
- tickers: list[str] = kwargs.pop('ticker', kwargs.pop('tickers', []))
125
- dtypes: list[str | type] = kwargs.pop('dtype', kwargs.pop('dtypes', [TradeData, TransactionData, OrderBook, TickData]))
126
-
127
- if not all([arg_name in inspect.getfullargspec(loader).args for arg_name in ['market_date', 'ticker', 'dtype']]):
128
- raise TypeError('loader function has 3 requires args, market_date, ticker and dtype.')
129
-
130
- if isinstance(tickers, str):
131
- tickers = [tickers]
132
- elif isinstance(tickers, Iterable):
133
- tickers = list(tickers)
134
- else:
135
- raise TypeError(f'Invalid ticker {tickers}, expect str or list[str]')
136
-
137
- if isinstance(dtypes, str) or inspect.isclass(dtypes):
138
- dtypes = [dtypes]
139
- elif isinstance(dtypes, Iterable):
140
- dtypes = list(dtypes)
141
- else:
142
- raise TypeError(f'Invalid dtype {dtypes}, expect str or list[str]')
143
-
144
- for ticker in tickers:
145
- for dtype in dtypes:
146
- self.add_subscription(ticker=ticker, dtype=dtype)
147
-
148
- subscription = kwargs.pop('subscription', kwargs.pop('subscribe', []))
149
-
150
- if isinstance(subscription, dict):
151
- subscription = [subscription]
152
-
153
- for sub in subscription:
154
- self.add_subscription(**sub)
155
-
156
- self.reset()
157
-
158
- def add_subscription(self, ticker: str, dtype: type | str):
159
- if isinstance(dtype, str):
160
- pass
161
- elif inspect.isclass(dtype):
162
- dtype = dtype.__name__
163
- else:
164
- raise ValueError(f'Invalid dtype {dtype}, expect str or class.')
165
-
166
- topic = f'{ticker}.{dtype}'
167
- self.replay_subscription[topic] = (ticker, dtype)
168
-
169
- def remove_subscription(self, ticker: str, dtype: type | str):
170
- if isinstance(dtype, str):
171
- pass
172
- else:
173
- dtype = dtype.__name__
174
-
175
- topic = f'{ticker}.{dtype}'
176
- self.replay_subscription.pop(topic, None)
177
-
178
- def reset(self):
179
- if self.calendar is None:
180
- self.replay_calendar = [self.start_date + datetime.timedelta(days=i) for i in range((self.end_date - self.start_date).days + 1)]
181
- else:
182
- self.replay_calendar = self.calendar
183
-
184
- self.task_progress = 0
185
- self.date_progress = sum([1 for _ in self.replay_calendar if _ < self.start_date])
186
- self.progress.reset()
187
-
188
- if self.date_progress:
189
- self.progress.done_tasks = self.date_progress / len(self.replay_calendar)
190
-
191
- def next_trade_day(self):
192
- if self.date_progress < len(self.replay_calendar):
193
- market_date = self.replay_calendar[self.date_progress]
194
- self.progress.prompt = f'Replay {market_date:%Y-%m-%d} ({self.date_progress + 1} / {len(self.replay_calendar)}):'
195
- for topic in self.replay_subscription:
196
- ticker, dtype = self.replay_subscription[topic]
197
- LOGGER.info(f'{self} loading {market_date} {ticker} {dtype}')
198
- data = self.loader(market_date=market_date, ticker=ticker, dtype=dtype)
199
-
200
- if isinstance(data, dict):
201
- self.replay_task.extend(list(data.values()))
202
- elif isinstance(data, (list, tuple)):
203
- self.replay_task.extend(data)
204
-
205
- self.date_progress += 1
206
- else:
207
- raise StopIteration()
208
-
209
- self.replay_task.sort(key=lambda x: x.market_time)
210
-
211
- def next_task(self):
212
- if self.task_progress < len(self.replay_task):
213
- data = self.replay_task[self.task_progress]
214
- self.task_progress += 1
215
- else:
216
- if self.eod is not None and self.date_progress and (self.date_progress >= len(self.replay_calendar) or self.replay_calendar[self.date_progress] > self.start_date):
217
- self.eod(market_date=self.replay_calendar[self.date_progress - 1], replay=self)
218
-
219
- self.replay_task.clear()
220
- self.task_progress = 0
221
-
222
- if self.bod is not None and self.date_progress < len(self.replay_calendar):
223
- self.bod(market_date=self.replay_calendar[self.date_progress], replay=self)
224
-
225
- self.next_trade_day()
226
-
227
- data = self.next_task()
228
-
229
- if self.replay_task and self.replay_calendar:
230
- current_progress = (self.date_progress - 1 + (self.task_progress / len(self.replay_task))) / len(self.replay_calendar)
231
- self.progress.done_tasks = current_progress
232
- else:
233
- self.progress.done_tasks = 1
234
-
235
- if (not self.progress.tick_size) \
236
- or self.progress.progress >= self.progress.tick_size + self.progress.last_output \
237
- or self.progress.is_done:
238
- self.progress.output()
239
-
240
- return data
241
-
242
- def __next__(self):
243
- try:
244
- return self.next_task()
245
- except StopIteration:
246
- if not self.progress.is_done:
247
- self.progress.done_tasks = 1
248
- self.progress.output()
249
-
250
- self.reset()
251
- raise StopIteration()
252
-
253
- def __iter__(self):
254
- self.reset()
255
- return self
256
-
257
- def __repr__(self):
258
- return f'{self.__class__.__name__}{{id={id(self)}, from={self.start_date}, to={self.end_date}}}'
1
+ import abc
2
+ import datetime
3
+ import inspect
4
+ from typing import Iterable
5
+
6
+ from . import LOGGER
7
+ from ..base import Progress, TickData, TransactionData, TradeData, OrderBook
8
+
9
+ LOGGER = LOGGER.getChild('Replay')
10
+
11
+
12
+ class Replay(object, metaclass=abc.ABCMeta):
13
+ @abc.abstractmethod
14
+ def __next__(self): ...
15
+
16
+ @abc.abstractmethod
17
+ def __iter__(self): ...
18
+
19
+
20
+ class SimpleReplay(Replay):
21
+ def __init__(self, **kwargs):
22
+ self.eod = kwargs.pop('eod', None)
23
+ self.bod = kwargs.pop('bod', None)
24
+
25
+ self.replay_task = []
26
+ self.task_progress = 0
27
+ self.task_date = None
28
+ self.progress = Progress(tasks=1, **kwargs)
29
+
30
+ def load(self, data):
31
+ if isinstance(data, dict):
32
+ self.replay_task.extend(list(data.values()))
33
+ else:
34
+ self.replay_task.extend(data)
35
+
36
+ def reset(self):
37
+ self.replay_task.clear()
38
+ self.task_progress = 0
39
+ self.task_date = None
40
+ self.progress.reset()
41
+
42
+ def next_task(self):
43
+ if self.task_progress < len(self.replay_task):
44
+ market_data = self.replay_task[self.task_progress]
45
+ market_time = market_data.market_time
46
+
47
+ if isinstance(market_time, datetime.datetime):
48
+ market_date = market_time.date()
49
+ else:
50
+ market_date = market_time
51
+
52
+ if market_date != self.task_date:
53
+ if callable(self.eod) and self.task_date:
54
+ self.eod(self.task_date)
55
+
56
+ self.task_date = market_date
57
+ self.progress.prompt = f'Replay {market_date:%Y-%m-%d}:'
58
+
59
+ if callable(self.bod):
60
+ self.bod(market_date)
61
+
62
+ self.progress.done_tasks = self.task_progress / len(self.replay_task)
63
+
64
+ if (not self.progress.tick_size) or self.progress.progress >= self.progress.tick_size + self.progress.last_output:
65
+ self.progress.output()
66
+
67
+ self.task_progress += 1
68
+ else:
69
+ raise StopIteration()
70
+
71
+ return market_data
72
+
73
+ def __next__(self):
74
+ try:
75
+ return self.next_task()
76
+ except StopIteration:
77
+ if not self.progress.is_done:
78
+ self.progress.done_tasks = 1
79
+ self.progress.output()
80
+
81
+ self.reset()
82
+ raise StopIteration()
83
+
84
+ def __iter__(self):
85
+ return self
86
+
87
+
88
+ class ProgressiveReplay(Replay):
89
+ """
90
+ progressively loading and replaying market data
91
+
92
+ requires arguments
93
+ loader: a data loading function. Expect loader = Callable(market_date: datetime.date, ticker: str, dtype: str| type) -> dict[any, MarketData]
94
+ start_date & end_date: the given replay period
95
+ or calendar: the given replay calendar.
96
+
97
+ accepts kwargs:
98
+ ticker / tickers: the given symbols to replay, expect a str| list[str]
99
+ dtype / dtypes: the given dtype(s) of symbol to replay, expect a str | type, list[str | type]. default = all, which is (TradeData, TickData, OrderBook)
100
+ subscription / subscribe: the given ticker-dtype pair to replay, expect a list[dict[str, str | type]]
101
+ """
102
+
103
+ def __init__(
104
+ self,
105
+ loader,
106
+ **kwargs
107
+ ):
108
+ self.loader = loader
109
+ self.start_date: datetime.date | None = kwargs.pop('start_date', None)
110
+ self.end_date: datetime.date | None = kwargs.pop('end_date', None)
111
+ self.calendar: list[datetime.date] | None = kwargs.pop('calendar', None)
112
+
113
+ self.eod = kwargs.pop('eod', None)
114
+ self.bod = kwargs.pop('bod', None)
115
+
116
+ self.replay_subscription = {}
117
+ self.replay_calendar = []
118
+ self.replay_task = []
119
+
120
+ self.date_progress = 0
121
+ self.task_progress = 0
122
+ self.progress = Progress(tasks=1, **kwargs)
123
+
124
+ tickers: list[str] = kwargs.pop('ticker', kwargs.pop('tickers', []))
125
+ dtypes: list[str | type] = kwargs.pop('dtype', kwargs.pop('dtypes', [TradeData, TransactionData, OrderBook, TickData]))
126
+
127
+ if not all([arg_name in inspect.getfullargspec(loader).args for arg_name in ['market_date', 'ticker', 'dtype']]):
128
+ raise TypeError('loader function has 3 requires args, market_date, ticker and dtype.')
129
+
130
+ if isinstance(tickers, str):
131
+ tickers = [tickers]
132
+ elif isinstance(tickers, Iterable):
133
+ tickers = list(tickers)
134
+ else:
135
+ raise TypeError(f'Invalid ticker {tickers}, expect str or list[str]')
136
+
137
+ if isinstance(dtypes, str) or inspect.isclass(dtypes):
138
+ dtypes = [dtypes]
139
+ elif isinstance(dtypes, Iterable):
140
+ dtypes = list(dtypes)
141
+ else:
142
+ raise TypeError(f'Invalid dtype {dtypes}, expect str or list[str]')
143
+
144
+ for ticker in tickers:
145
+ for dtype in dtypes:
146
+ self.add_subscription(ticker=ticker, dtype=dtype)
147
+
148
+ subscription = kwargs.pop('subscription', kwargs.pop('subscribe', []))
149
+
150
+ if isinstance(subscription, dict):
151
+ subscription = [subscription]
152
+
153
+ for sub in subscription:
154
+ self.add_subscription(**sub)
155
+
156
+ self.reset()
157
+
158
+ def add_subscription(self, ticker: str, dtype: type | str):
159
+ if isinstance(dtype, str):
160
+ pass
161
+ elif inspect.isclass(dtype):
162
+ dtype = dtype.__name__
163
+ else:
164
+ raise ValueError(f'Invalid dtype {dtype}, expect str or class.')
165
+
166
+ topic = f'{ticker}.{dtype}'
167
+ self.replay_subscription[topic] = (ticker, dtype)
168
+
169
+ def remove_subscription(self, ticker: str, dtype: type | str):
170
+ if isinstance(dtype, str):
171
+ pass
172
+ else:
173
+ dtype = dtype.__name__
174
+
175
+ topic = f'{ticker}.{dtype}'
176
+ self.replay_subscription.pop(topic, None)
177
+
178
+ def reset(self):
179
+ if self.calendar is None:
180
+ self.replay_calendar = [self.start_date + datetime.timedelta(days=i) for i in range((self.end_date - self.start_date).days + 1)]
181
+ else:
182
+ self.replay_calendar = self.calendar
183
+
184
+ self.task_progress = 0
185
+ self.date_progress = sum([1 for _ in self.replay_calendar if _ < self.start_date])
186
+ self.progress.reset()
187
+
188
+ if self.date_progress:
189
+ self.progress.done_tasks = self.date_progress / len(self.replay_calendar)
190
+
191
+ def next_trade_day(self):
192
+ if self.date_progress < len(self.replay_calendar):
193
+ market_date = self.replay_calendar[self.date_progress]
194
+ self.progress.prompt = f'Replay {market_date:%Y-%m-%d} ({self.date_progress + 1} / {len(self.replay_calendar)}):'
195
+ for topic in self.replay_subscription:
196
+ ticker, dtype = self.replay_subscription[topic]
197
+ LOGGER.info(f'{self} loading {market_date} {ticker} {dtype}')
198
+ data = self.loader(market_date=market_date, ticker=ticker, dtype=dtype)
199
+
200
+ if isinstance(data, dict):
201
+ self.replay_task.extend(list(data.values()))
202
+ elif isinstance(data, (list, tuple)):
203
+ self.replay_task.extend(data)
204
+
205
+ self.date_progress += 1
206
+ else:
207
+ raise StopIteration()
208
+
209
+ self.replay_task.sort(key=lambda x: x.market_time)
210
+
211
+ def next_task(self):
212
+ if self.task_progress < len(self.replay_task):
213
+ data = self.replay_task[self.task_progress]
214
+ self.task_progress += 1
215
+ else:
216
+ if self.eod is not None and self.date_progress and (self.date_progress >= len(self.replay_calendar) or self.replay_calendar[self.date_progress] > self.start_date):
217
+ self.eod(market_date=self.replay_calendar[self.date_progress - 1], replay=self)
218
+
219
+ self.replay_task.clear()
220
+ self.task_progress = 0
221
+
222
+ if self.bod is not None and self.date_progress < len(self.replay_calendar):
223
+ self.bod(market_date=self.replay_calendar[self.date_progress], replay=self)
224
+
225
+ self.next_trade_day()
226
+
227
+ data = self.next_task()
228
+
229
+ if self.replay_task and self.replay_calendar:
230
+ current_progress = (self.date_progress - 1 + (self.task_progress / len(self.replay_task))) / len(self.replay_calendar)
231
+ self.progress.done_tasks = current_progress
232
+ else:
233
+ self.progress.done_tasks = 1
234
+
235
+ if (not self.progress.tick_size) \
236
+ or self.progress.progress >= self.progress.tick_size + self.progress.last_output \
237
+ or self.progress.is_done:
238
+ self.progress.output()
239
+
240
+ return data
241
+
242
+ def __next__(self):
243
+ try:
244
+ return self.next_task()
245
+ except StopIteration:
246
+ if not self.progress.is_done:
247
+ self.progress.done_tasks = 1
248
+ self.progress.output()
249
+
250
+ self.reset()
251
+ raise StopIteration()
252
+
253
+ def __iter__(self):
254
+ self.reset()
255
+ return self
256
+
257
+ def __repr__(self):
258
+ return f'{self.__class__.__name__}{{id={id(self)}, from={self.start_date}, to={self.end_date}}}'