PyAlgoEngine 0.3.13__tar.gz → 0.4.0__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.3.13/PyAlgoEngine.egg-info → pyalgoengine-0.4.0}/PKG-INFO +6 -4
- {pyalgoengine-0.3.13 → pyalgoengine-0.4.0/PyAlgoEngine.egg-info}/PKG-INFO +6 -4
- pyalgoengine-0.4.0/PyAlgoEngine.egg-info/SOURCES.txt +24 -0
- pyalgoengine-0.4.0/PyAlgoEngine.egg-info/requires.txt +5 -0
- pyalgoengine-0.4.0/PyAlgoEngine.egg-info/top_level.txt +1 -0
- pyalgoengine-0.4.0/algo_engine/__init__.py +74 -0
- pyalgoengine-0.4.0/algo_engine/back_test/__init__.py +8 -0
- pyalgoengine-0.3.13/AlgoEngine/Strategies/BackTest.py → pyalgoengine-0.4.0/algo_engine/back_test/__main__.py +7 -8
- pyalgoengine-0.4.0/algo_engine/back_test/replay.py +257 -0
- pyalgoengine-0.4.0/algo_engine/back_test/sim_match.py +290 -0
- pyalgoengine-0.4.0/algo_engine/engine/__init__.py +15 -0
- pyalgoengine-0.3.13/AlgoEngine/Engine/AlgoEngine.py → pyalgoengine-0.4.0/algo_engine/engine/algo_engine.py +63 -62
- pyalgoengine-0.3.13/AlgoEngine/Engine/EventEngine.py → pyalgoengine-0.4.0/algo_engine/engine/event_engine.py +15 -14
- pyalgoengine-0.4.0/algo_engine/engine/market_engine.py +363 -0
- pyalgoengine-0.3.13/AlgoEngine/Engine/TradeEngine.py → pyalgoengine-0.4.0/algo_engine/engine/trade_engine.py +157 -395
- pyalgoengine-0.4.0/algo_engine/monitor/__init__.py +15 -0
- pyalgoengine-0.4.0/algo_engine/monitor/advanced_data_interface.py +240 -0
- pyalgoengine-0.4.0/algo_engine/profile/__init__.py +54 -0
- pyalgoengine-0.4.0/algo_engine/profile/cn.py +173 -0
- {pyalgoengine-0.3.13/AlgoEngine/Strategies → pyalgoengine-0.4.0/algo_engine/strategie}/__init__.py +5 -9
- pyalgoengine-0.3.13/AlgoEngine/Strategies/_StrategyEngine.py → pyalgoengine-0.4.0/algo_engine/strategie/strategy_engine.py +6 -9
- {pyalgoengine-0.3.13 → pyalgoengine-0.4.0}/setup.py +6 -4
- pyalgoengine-0.3.13/AlgoEngine/Engine/MarketEngine.py +0 -1070
- pyalgoengine-0.3.13/AlgoEngine/Engine/__init__.py +0 -101
- pyalgoengine-0.3.13/AlgoEngine/__init__.py +0 -16
- pyalgoengine-0.3.13/PyAlgoEngine.egg-info/SOURCES.txt +0 -17
- pyalgoengine-0.3.13/PyAlgoEngine.egg-info/requires.txt +0 -5
- pyalgoengine-0.3.13/PyAlgoEngine.egg-info/top_level.txt +0 -1
- {pyalgoengine-0.3.13 → pyalgoengine-0.4.0}/LICENSE +0 -0
- {pyalgoengine-0.3.13 → pyalgoengine-0.4.0}/PyAlgoEngine.egg-info/dependency_links.txt +0 -0
- {pyalgoengine-0.3.13 → pyalgoengine-0.4.0}/README.md +0 -0
- {pyalgoengine-0.3.13 → pyalgoengine-0.4.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: PyAlgoEngine
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Basic algo engine
|
|
5
5
|
Home-page: https://github.com/BolunHan/PyAlgoEngine
|
|
6
6
|
Author: Bolun.Han
|
|
@@ -8,15 +8,17 @@ Author-email: Bolun.Han@outlook.com
|
|
|
8
8
|
Classifier: Programming Language :: Python :: 3.8
|
|
9
9
|
Classifier: Programming Language :: Python :: 3.9
|
|
10
10
|
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
13
|
Classifier: Operating System :: OS Independent
|
|
12
|
-
Requires-Python: >=3.
|
|
14
|
+
Requires-Python: >=3.1
|
|
13
15
|
Description-Content-Type: text/markdown
|
|
14
16
|
License-File: LICENSE
|
|
15
17
|
Requires-Dist: numpy
|
|
16
18
|
Requires-Dist: pandas
|
|
17
19
|
Requires-Dist: exchange_calendars
|
|
18
|
-
Requires-Dist: PyQuantKit
|
|
19
|
-
Requires-Dist: PyEventEngine
|
|
20
|
+
Requires-Dist: PyQuantKit>=0.3.0
|
|
21
|
+
Requires-Dist: PyEventEngine>=0.3.0.post3
|
|
20
22
|
|
|
21
23
|
# PyAlgoEngine
|
|
22
24
|
python algo trading engine
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: PyAlgoEngine
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Basic algo engine
|
|
5
5
|
Home-page: https://github.com/BolunHan/PyAlgoEngine
|
|
6
6
|
Author: Bolun.Han
|
|
@@ -8,15 +8,17 @@ Author-email: Bolun.Han@outlook.com
|
|
|
8
8
|
Classifier: Programming Language :: Python :: 3.8
|
|
9
9
|
Classifier: Programming Language :: Python :: 3.9
|
|
10
10
|
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
13
|
Classifier: Operating System :: OS Independent
|
|
12
|
-
Requires-Python: >=3.
|
|
14
|
+
Requires-Python: >=3.1
|
|
13
15
|
Description-Content-Type: text/markdown
|
|
14
16
|
License-File: LICENSE
|
|
15
17
|
Requires-Dist: numpy
|
|
16
18
|
Requires-Dist: pandas
|
|
17
19
|
Requires-Dist: exchange_calendars
|
|
18
|
-
Requires-Dist: PyQuantKit
|
|
19
|
-
Requires-Dist: PyEventEngine
|
|
20
|
+
Requires-Dist: PyQuantKit>=0.3.0
|
|
21
|
+
Requires-Dist: PyEventEngine>=0.3.0.post3
|
|
20
22
|
|
|
21
23
|
# PyAlgoEngine
|
|
22
24
|
python algo trading engine
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
setup.py
|
|
4
|
+
PyAlgoEngine.egg-info/PKG-INFO
|
|
5
|
+
PyAlgoEngine.egg-info/SOURCES.txt
|
|
6
|
+
PyAlgoEngine.egg-info/dependency_links.txt
|
|
7
|
+
PyAlgoEngine.egg-info/requires.txt
|
|
8
|
+
PyAlgoEngine.egg-info/top_level.txt
|
|
9
|
+
algo_engine/__init__.py
|
|
10
|
+
algo_engine/back_test/__init__.py
|
|
11
|
+
algo_engine/back_test/__main__.py
|
|
12
|
+
algo_engine/back_test/replay.py
|
|
13
|
+
algo_engine/back_test/sim_match.py
|
|
14
|
+
algo_engine/engine/__init__.py
|
|
15
|
+
algo_engine/engine/algo_engine.py
|
|
16
|
+
algo_engine/engine/event_engine.py
|
|
17
|
+
algo_engine/engine/market_engine.py
|
|
18
|
+
algo_engine/engine/trade_engine.py
|
|
19
|
+
algo_engine/monitor/__init__.py
|
|
20
|
+
algo_engine/monitor/advanced_data_interface.py
|
|
21
|
+
algo_engine/profile/__init__.py
|
|
22
|
+
algo_engine/profile/cn.py
|
|
23
|
+
algo_engine/strategie/__init__.py
|
|
24
|
+
algo_engine/strategie/strategy_engine.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
algo_engine
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
__version__ = "0.4.0"
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
import time
|
|
7
|
+
import traceback
|
|
8
|
+
|
|
9
|
+
from PyQuantKit import ColoredFormatter
|
|
10
|
+
|
|
11
|
+
LOGGER: logging.Logger | None = None
|
|
12
|
+
LOG_LEVEL = logging.INFO
|
|
13
|
+
|
|
14
|
+
if 'ALGO_DIR' in os.environ:
|
|
15
|
+
WORKING_DIRECTORY = os.path.realpath(os.environ['ALGO_DIR'])
|
|
16
|
+
else:
|
|
17
|
+
WORKING_DIRECTORY = str(os.getcwd())
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def get_logger(**kwargs) -> logging.Logger:
|
|
21
|
+
level = kwargs.get('level', LOG_LEVEL)
|
|
22
|
+
stream_io = kwargs.get('stream_io', sys.stdout)
|
|
23
|
+
formatter = kwargs.get('formatter', ColoredFormatter())
|
|
24
|
+
global LOGGER
|
|
25
|
+
|
|
26
|
+
if LOGGER is not None:
|
|
27
|
+
return LOGGER
|
|
28
|
+
|
|
29
|
+
LOGGER = logging.getLogger('PyAlgoEngine')
|
|
30
|
+
LOGGER.setLevel(level)
|
|
31
|
+
logging.Formatter.converter = time.gmtime
|
|
32
|
+
|
|
33
|
+
if stream_io:
|
|
34
|
+
have_handler = False
|
|
35
|
+
for handler in LOGGER.handlers:
|
|
36
|
+
# noinspection PyUnresolvedReferences
|
|
37
|
+
if type(handler) == logging.StreamHandler and handler.stream == stream_io:
|
|
38
|
+
have_handler = True
|
|
39
|
+
break
|
|
40
|
+
|
|
41
|
+
if not have_handler:
|
|
42
|
+
logger_ch = logging.StreamHandler(stream=stream_io)
|
|
43
|
+
logger_ch.setLevel(level=level)
|
|
44
|
+
logger_ch.setFormatter(fmt=formatter)
|
|
45
|
+
LOGGER.addHandler(logger_ch)
|
|
46
|
+
|
|
47
|
+
return LOGGER
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def set_logger(logger: logging.Logger):
|
|
51
|
+
global LOGGER
|
|
52
|
+
LOGGER = logger
|
|
53
|
+
|
|
54
|
+
engine.LOGGER = LOGGER.getChild('Engine')
|
|
55
|
+
back_test.LOGGER = LOGGER.getChild('BackTest')
|
|
56
|
+
strategie.LOGGER = LOGGER.getChild('Strategies')
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
_ = get_logger()
|
|
60
|
+
|
|
61
|
+
from . import engine
|
|
62
|
+
from . import back_test
|
|
63
|
+
from . import profile
|
|
64
|
+
from . import strategie
|
|
65
|
+
|
|
66
|
+
engine.LOGGER.info(f'AlgoEngine version {__version__}')
|
|
67
|
+
|
|
68
|
+
# import addon module
|
|
69
|
+
try:
|
|
70
|
+
from . import EngineAddon
|
|
71
|
+
|
|
72
|
+
engine.LOGGER.info(f'AlgoEngine_Addons import successful, version {EngineAddon.__version__}')
|
|
73
|
+
except ImportError:
|
|
74
|
+
engine.LOGGER.debug(f'Install AlgoEngine_Addons to use Statistics module\n{traceback.format_exc()}')
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
__package__ = 'algo_engine.back_test'
|
|
2
2
|
|
|
3
3
|
import datetime
|
|
4
|
+
from typing import Callable
|
|
4
5
|
|
|
5
6
|
import EventEngine
|
|
6
7
|
|
|
7
|
-
from
|
|
8
|
-
from .
|
|
9
|
-
from ..
|
|
10
|
-
from ..
|
|
11
|
-
|
|
12
|
-
LOGGER = LOGGER.getChild('BackTest')
|
|
8
|
+
from ..engine import TOPIC, MarketDataService, Balance, RiskProfile, PositionManagementService
|
|
9
|
+
from ..engine.algo_engine import AlgoRegistry, AlgoEngine
|
|
10
|
+
from ..strategie import EventDMA
|
|
11
|
+
from ..strategie.strategy_engine import StrategyEngine
|
|
13
12
|
|
|
14
13
|
|
|
15
14
|
def test_stop(code=0):
|
|
@@ -19,7 +18,7 @@ def test_stop(code=0):
|
|
|
19
18
|
# `os._exit(code)`
|
|
20
19
|
|
|
21
20
|
|
|
22
|
-
def test_start(start_date: datetime.date, end_date: datetime.date, data_loader:
|
|
21
|
+
def test_start(start_date: datetime.date, end_date: datetime.date, data_loader: Callable, **kwargs):
|
|
23
22
|
EVENT_ENGINE.start()
|
|
24
23
|
STRATEGY_ENGINE.back_test(
|
|
25
24
|
start_date=start_date,
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import datetime
|
|
3
|
+
import inspect
|
|
4
|
+
from typing import Iterable
|
|
5
|
+
|
|
6
|
+
from PyQuantKit import Progress, TickData, TransactionData, TradeData, OrderBook
|
|
7
|
+
|
|
8
|
+
from . import LOGGER
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Replay(object, metaclass=abc.ABCMeta):
|
|
12
|
+
@abc.abstractmethod
|
|
13
|
+
def __next__(self): ...
|
|
14
|
+
|
|
15
|
+
@abc.abstractmethod
|
|
16
|
+
def __iter__(self): ...
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class SimpleReplay(Replay):
|
|
20
|
+
def __init__(self, **kwargs):
|
|
21
|
+
self.eod = kwargs.pop('eod', None)
|
|
22
|
+
self.bod = kwargs.pop('bod', None)
|
|
23
|
+
|
|
24
|
+
self.replay_task = []
|
|
25
|
+
self.task_progress = 0
|
|
26
|
+
self.task_date = None
|
|
27
|
+
self.progress = Progress(tasks=1, **kwargs)
|
|
28
|
+
|
|
29
|
+
def load(self, data):
|
|
30
|
+
if isinstance(data, dict):
|
|
31
|
+
self.replay_task.extend(list(data.values()))
|
|
32
|
+
else:
|
|
33
|
+
self.replay_task.extend(data)
|
|
34
|
+
|
|
35
|
+
def reset(self):
|
|
36
|
+
self.replay_task.clear()
|
|
37
|
+
self.task_progress = 0
|
|
38
|
+
self.task_date = None
|
|
39
|
+
self.progress.reset()
|
|
40
|
+
|
|
41
|
+
def next_task(self):
|
|
42
|
+
if self.task_progress < len(self.replay_task):
|
|
43
|
+
market_data = self.replay_task[self.task_progress]
|
|
44
|
+
market_time = market_data.market_time
|
|
45
|
+
|
|
46
|
+
if isinstance(market_time, datetime.datetime):
|
|
47
|
+
market_date = market_time.date()
|
|
48
|
+
else:
|
|
49
|
+
market_date = market_time
|
|
50
|
+
|
|
51
|
+
if market_date != self.task_date:
|
|
52
|
+
if callable(self.eod) and self.task_date:
|
|
53
|
+
self.eod(self.task_date)
|
|
54
|
+
|
|
55
|
+
self.task_date = market_date
|
|
56
|
+
self.progress.prompt = f'Replay {market_date:%Y-%m-%d}:'
|
|
57
|
+
|
|
58
|
+
if callable(self.bod):
|
|
59
|
+
self.bod(market_date)
|
|
60
|
+
|
|
61
|
+
self.progress.done_tasks = self.task_progress / len(self.replay_task)
|
|
62
|
+
|
|
63
|
+
if (not self.progress.tick_size) or self.progress.progress >= self.progress.tick_size + self.progress.last_output:
|
|
64
|
+
self.progress.output()
|
|
65
|
+
|
|
66
|
+
self.task_progress += 1
|
|
67
|
+
else:
|
|
68
|
+
raise StopIteration()
|
|
69
|
+
|
|
70
|
+
return market_data
|
|
71
|
+
|
|
72
|
+
def __next__(self):
|
|
73
|
+
try:
|
|
74
|
+
return self.next_task()
|
|
75
|
+
except StopIteration:
|
|
76
|
+
if not self.progress.is_done:
|
|
77
|
+
self.progress.done_tasks = 1
|
|
78
|
+
self.progress.output()
|
|
79
|
+
|
|
80
|
+
self.reset()
|
|
81
|
+
raise StopIteration()
|
|
82
|
+
|
|
83
|
+
def __iter__(self):
|
|
84
|
+
return self
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class ProgressiveReplay(Replay):
|
|
88
|
+
"""
|
|
89
|
+
progressively loading and replaying market data
|
|
90
|
+
|
|
91
|
+
requires arguments
|
|
92
|
+
loader: a data loading function. Expect loader = Callable(market_date: datetime.date, ticker: str, dtype: str| type) -> dict[any, MarketData]
|
|
93
|
+
start_date & end_date: the given replay period
|
|
94
|
+
or calendar: the given replay calendar.
|
|
95
|
+
|
|
96
|
+
accepts kwargs:
|
|
97
|
+
ticker / tickers: the given symbols to replay, expect a str| list[str]
|
|
98
|
+
dtype / dtypes: the given dtype(s) of symbol to replay, expect a str | type, list[str | type]. default = all, which is (TradeData, TickData, OrderBook)
|
|
99
|
+
subscription / subscribe: the given ticker-dtype pair to replay, expect a list[dict[str, str | type]]
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
def __init__(
|
|
103
|
+
self,
|
|
104
|
+
loader,
|
|
105
|
+
**kwargs
|
|
106
|
+
):
|
|
107
|
+
self.loader = loader
|
|
108
|
+
self.start_date: datetime.date | None = kwargs.pop('start_date', None)
|
|
109
|
+
self.end_date: datetime.date | None = kwargs.pop('end_date', None)
|
|
110
|
+
self.calendar: list[datetime.date] | None = kwargs.pop('calendar', None)
|
|
111
|
+
|
|
112
|
+
self.eod = kwargs.pop('eod', None)
|
|
113
|
+
self.bod = kwargs.pop('bod', None)
|
|
114
|
+
|
|
115
|
+
self.replay_subscription = {}
|
|
116
|
+
self.replay_calendar = []
|
|
117
|
+
self.replay_task = []
|
|
118
|
+
|
|
119
|
+
self.date_progress = 0
|
|
120
|
+
self.task_progress = 0
|
|
121
|
+
self.progress = Progress(tasks=1, **kwargs)
|
|
122
|
+
|
|
123
|
+
tickers: list[str] = kwargs.pop('ticker', kwargs.pop('tickers', []))
|
|
124
|
+
dtypes: list[str | type] = kwargs.pop('dtype', kwargs.pop('dtypes', [TradeData, TransactionData, OrderBook, TickData]))
|
|
125
|
+
|
|
126
|
+
if not all([arg_name in inspect.getfullargspec(loader).args for arg_name in ['market_date', 'ticker', 'dtype']]):
|
|
127
|
+
raise TypeError('loader function has 3 requires args, market_date, ticker and dtype.')
|
|
128
|
+
|
|
129
|
+
if isinstance(tickers, str):
|
|
130
|
+
tickers = [tickers]
|
|
131
|
+
elif isinstance(tickers, Iterable):
|
|
132
|
+
tickers = list(tickers)
|
|
133
|
+
else:
|
|
134
|
+
raise TypeError(f'Invalid ticker {tickers}, expect str or list[str]')
|
|
135
|
+
|
|
136
|
+
if isinstance(dtypes, str) or inspect.isclass(dtypes):
|
|
137
|
+
dtypes = [dtypes]
|
|
138
|
+
elif isinstance(dtypes, Iterable):
|
|
139
|
+
dtypes = list(dtypes)
|
|
140
|
+
else:
|
|
141
|
+
raise TypeError(f'Invalid dtype {dtypes}, expect str or list[str]')
|
|
142
|
+
|
|
143
|
+
for ticker in tickers:
|
|
144
|
+
for dtype in dtypes:
|
|
145
|
+
self.add_subscription(ticker=ticker, dtype=dtype)
|
|
146
|
+
|
|
147
|
+
subscription = kwargs.pop('subscription', kwargs.pop('subscribe', []))
|
|
148
|
+
|
|
149
|
+
if isinstance(subscription, dict):
|
|
150
|
+
subscription = [subscription]
|
|
151
|
+
|
|
152
|
+
for sub in subscription:
|
|
153
|
+
self.add_subscription(**sub)
|
|
154
|
+
|
|
155
|
+
self.reset()
|
|
156
|
+
|
|
157
|
+
def add_subscription(self, ticker: str, dtype: type | str):
|
|
158
|
+
if isinstance(dtype, str):
|
|
159
|
+
pass
|
|
160
|
+
elif inspect.isclass(dtype):
|
|
161
|
+
dtype = dtype.__name__
|
|
162
|
+
else:
|
|
163
|
+
raise ValueError(f'Invalid dtype {dtype}, expect str or class.')
|
|
164
|
+
|
|
165
|
+
topic = f'{ticker}.{dtype}'
|
|
166
|
+
self.replay_subscription[topic] = (ticker, dtype)
|
|
167
|
+
|
|
168
|
+
def remove_subscription(self, ticker: str, dtype: type | str):
|
|
169
|
+
if isinstance(dtype, str):
|
|
170
|
+
pass
|
|
171
|
+
else:
|
|
172
|
+
dtype = dtype.__name__
|
|
173
|
+
|
|
174
|
+
topic = f'{ticker}.{dtype}'
|
|
175
|
+
self.replay_subscription.pop(topic, None)
|
|
176
|
+
|
|
177
|
+
def reset(self):
|
|
178
|
+
if self.calendar is None:
|
|
179
|
+
self.replay_calendar = [self.start_date + datetime.timedelta(days=i) for i in range((self.end_date - self.start_date).days + 1)]
|
|
180
|
+
else:
|
|
181
|
+
self.replay_calendar = self.calendar
|
|
182
|
+
|
|
183
|
+
self.task_progress = 0
|
|
184
|
+
self.date_progress = sum([1 for _ in self.replay_calendar if _ < self.start_date])
|
|
185
|
+
self.progress.reset()
|
|
186
|
+
|
|
187
|
+
if self.date_progress:
|
|
188
|
+
self.progress.done_tasks = self.date_progress / len(self.replay_calendar)
|
|
189
|
+
|
|
190
|
+
def next_trade_day(self):
|
|
191
|
+
if self.date_progress < len(self.replay_calendar):
|
|
192
|
+
market_date = self.replay_calendar[self.date_progress]
|
|
193
|
+
self.progress.prompt = f'Replay {market_date:%Y-%m-%d} ({self.date_progress + 1} / {len(self.replay_calendar)}):'
|
|
194
|
+
for topic in self.replay_subscription:
|
|
195
|
+
ticker, dtype = self.replay_subscription[topic]
|
|
196
|
+
LOGGER.info(f'{self} loading {market_date} {ticker} {dtype}')
|
|
197
|
+
data = self.loader(market_date=market_date, ticker=ticker, dtype=dtype)
|
|
198
|
+
|
|
199
|
+
if isinstance(data, dict):
|
|
200
|
+
self.replay_task.extend(list(data.values()))
|
|
201
|
+
elif isinstance(data, (list, tuple)):
|
|
202
|
+
self.replay_task.extend(data)
|
|
203
|
+
|
|
204
|
+
self.date_progress += 1
|
|
205
|
+
else:
|
|
206
|
+
raise StopIteration()
|
|
207
|
+
|
|
208
|
+
self.replay_task.sort(key=lambda x: x.market_time)
|
|
209
|
+
|
|
210
|
+
def next_task(self):
|
|
211
|
+
if self.task_progress < len(self.replay_task):
|
|
212
|
+
data = self.replay_task[self.task_progress]
|
|
213
|
+
self.task_progress += 1
|
|
214
|
+
else:
|
|
215
|
+
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):
|
|
216
|
+
self.eod(market_date=self.replay_calendar[self.date_progress - 1], replay=self)
|
|
217
|
+
|
|
218
|
+
self.replay_task.clear()
|
|
219
|
+
self.task_progress = 0
|
|
220
|
+
|
|
221
|
+
if self.bod is not None and self.date_progress < len(self.replay_calendar):
|
|
222
|
+
self.bod(market_date=self.replay_calendar[self.date_progress], replay=self)
|
|
223
|
+
|
|
224
|
+
self.next_trade_day()
|
|
225
|
+
|
|
226
|
+
data = self.next_task()
|
|
227
|
+
|
|
228
|
+
if self.replay_task and self.replay_calendar:
|
|
229
|
+
current_progress = (self.date_progress - 1 + (self.task_progress / len(self.replay_task))) / len(self.replay_calendar)
|
|
230
|
+
self.progress.done_tasks = current_progress
|
|
231
|
+
else:
|
|
232
|
+
self.progress.done_tasks = 1
|
|
233
|
+
|
|
234
|
+
if (not self.progress.tick_size) \
|
|
235
|
+
or self.progress.progress >= self.progress.tick_size + self.progress.last_output \
|
|
236
|
+
or self.progress.is_done:
|
|
237
|
+
self.progress.output()
|
|
238
|
+
|
|
239
|
+
return data
|
|
240
|
+
|
|
241
|
+
def __next__(self):
|
|
242
|
+
try:
|
|
243
|
+
return self.next_task()
|
|
244
|
+
except StopIteration:
|
|
245
|
+
if not self.progress.is_done:
|
|
246
|
+
self.progress.done_tasks = 1
|
|
247
|
+
self.progress.output()
|
|
248
|
+
|
|
249
|
+
self.reset()
|
|
250
|
+
raise StopIteration()
|
|
251
|
+
|
|
252
|
+
def __iter__(self):
|
|
253
|
+
self.reset()
|
|
254
|
+
return self
|
|
255
|
+
|
|
256
|
+
def __repr__(self):
|
|
257
|
+
return f'{self.__class__.__name__}{{id={id(self)}, from={self.start_date}, to={self.end_date}}}'
|