PyAlgoEngine 0.12.3__cp315-cp315-win_amd64.whl

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 (156) hide show
  1. algo_engine/__infra__.pxd +3 -0
  2. algo_engine/__init__.pxd +3 -0
  3. algo_engine/__init__.py +107 -0
  4. algo_engine/apps/__init__.py +17 -0
  5. algo_engine/apps/backtest/__init__.py +20 -0
  6. algo_engine/apps/backtest/doc_server.py +328 -0
  7. algo_engine/apps/backtest/static/styles/dash.css +48 -0
  8. algo_engine/apps/backtest/templates/dash.html +20 -0
  9. algo_engine/apps/backtest/templates/index.html +40 -0
  10. algo_engine/apps/backtest/tester.py +252 -0
  11. algo_engine/apps/backtest/web_app.py +125 -0
  12. algo_engine/apps/bokeh_server.py +245 -0
  13. algo_engine/apps/demo/__init__.py +0 -0
  14. algo_engine/apps/demo/test.py +40 -0
  15. algo_engine/apps/sim_input/__init__.py +23 -0
  16. algo_engine/apps/sim_input/client.py +412 -0
  17. algo_engine/apps/sim_input/sim_keyboard.py +88 -0
  18. algo_engine/apps/sim_input/sim_mouse.py +137 -0
  19. algo_engine/apps/sim_input/window.py +162 -0
  20. algo_engine/backtest/__init__.py +19 -0
  21. algo_engine/backtest/__main__.py +51 -0
  22. algo_engine/backtest/metrics.py +179 -0
  23. algo_engine/backtest/replay.py +635 -0
  24. algo_engine/backtest/sim_match.py +506 -0
  25. algo_engine/base/__infra__.pxd +3 -0
  26. algo_engine/base/__init__.pxd +3 -0
  27. algo_engine/base/__init__.py +60 -0
  28. algo_engine/base/c_allocator_protocol.c +11608 -0
  29. algo_engine/base/c_allocator_protocol.cp315-win_amd64.pyd +0 -0
  30. algo_engine/base/c_allocator_protocol.pxd +24 -0
  31. algo_engine/base/c_allocator_protocol.pyi +68 -0
  32. algo_engine/base/c_allocator_protocol.pyx +111 -0
  33. algo_engine/base/c_intern_string.c +5908 -0
  34. algo_engine/base/c_intern_string.cp315-win_amd64.pyd +0 -0
  35. algo_engine/base/c_intern_string.pxd +14 -0
  36. algo_engine/base/c_intern_string.pyi +22 -0
  37. algo_engine/base/c_intern_string.pyx +17 -0
  38. algo_engine/base/c_market_data/__infra__.pxd +196 -0
  39. algo_engine/base/c_market_data/__init__.pxd +196 -0
  40. algo_engine/base/c_market_data/__init__.py +24 -0
  41. algo_engine/base/c_market_data/c_candlestick.c +18996 -0
  42. algo_engine/base/c_market_data/c_candlestick.cp315-win_amd64.pyd +0 -0
  43. algo_engine/base/c_market_data/c_candlestick.pxd +27 -0
  44. algo_engine/base/c_market_data/c_candlestick.pyi +217 -0
  45. algo_engine/base/c_market_data/c_candlestick.pyx +255 -0
  46. algo_engine/base/c_market_data/c_internal.c +14059 -0
  47. algo_engine/base/c_market_data/c_internal.cp315-win_amd64.pyd +0 -0
  48. algo_engine/base/c_market_data/c_internal.pxd +14 -0
  49. algo_engine/base/c_market_data/c_internal.pyi +47 -0
  50. algo_engine/base/c_market_data/c_internal.pyx +41 -0
  51. algo_engine/base/c_market_data/c_market_data.c +30420 -0
  52. algo_engine/base/c_market_data/c_market_data.cp315-win_amd64.pyd +0 -0
  53. algo_engine/base/c_market_data/c_market_data.h +1464 -0
  54. algo_engine/base/c_market_data/c_market_data.pxd +414 -0
  55. algo_engine/base/c_market_data/c_market_data.pyi +550 -0
  56. algo_engine/base/c_market_data/c_market_data.pyx +701 -0
  57. algo_engine/base/c_market_data/c_market_data_buffer.c +29407 -0
  58. algo_engine/base/c_market_data/c_market_data_buffer.cp315-win_amd64.pyd +0 -0
  59. algo_engine/base/c_market_data/c_market_data_buffer.h +941 -0
  60. algo_engine/base/c_market_data/c_market_data_buffer.pxd +143 -0
  61. algo_engine/base/c_market_data/c_market_data_buffer.pyi +355 -0
  62. algo_engine/base/c_market_data/c_market_data_buffer.pyx +571 -0
  63. algo_engine/base/c_market_data/c_market_data_config.h +29 -0
  64. algo_engine/base/c_market_data/c_tick.c +44303 -0
  65. algo_engine/base/c_market_data/c_tick.cp315-win_amd64.pyd +0 -0
  66. algo_engine/base/c_market_data/c_tick.pxd +53 -0
  67. algo_engine/base/c_market_data/c_tick.pyi +466 -0
  68. algo_engine/base/c_market_data/c_tick.pyx +673 -0
  69. algo_engine/base/c_market_data/c_trade_utils.c +28702 -0
  70. algo_engine/base/c_market_data/c_trade_utils.cp315-win_amd64.pyd +0 -0
  71. algo_engine/base/c_market_data/c_trade_utils.pxd +53 -0
  72. algo_engine/base/c_market_data/c_trade_utils.pyi +602 -0
  73. algo_engine/base/c_market_data/c_trade_utils.pyx +609 -0
  74. algo_engine/base/c_market_data/c_transaction.c +23558 -0
  75. algo_engine/base/c_market_data/c_transaction.cp315-win_amd64.pyd +0 -0
  76. algo_engine/base/c_market_data/c_transaction.pxd +27 -0
  77. algo_engine/base/c_market_data/c_transaction.pyi +433 -0
  78. algo_engine/base/c_market_data/c_transaction.pyx +460 -0
  79. algo_engine/base/console_utils.py +1070 -0
  80. algo_engine/base/finance_decimal.py +258 -0
  81. algo_engine/base/telemetrics.py +18 -0
  82. algo_engine/engine/__infra__.pxd +10 -0
  83. algo_engine/engine/__init__.pxd +10 -0
  84. algo_engine/engine/__init__.py +40 -0
  85. algo_engine/engine/algo_engine.py +904 -0
  86. algo_engine/engine/c_event_engine.c +16325 -0
  87. algo_engine/engine/c_event_engine.cp315-win_amd64.pyd +0 -0
  88. algo_engine/engine/c_event_engine.pxd +25 -0
  89. algo_engine/engine/c_event_engine.pyi +68 -0
  90. algo_engine/engine/c_market_engine.c +24258 -0
  91. algo_engine/engine/c_market_engine.cp315-win_amd64.pyd +0 -0
  92. algo_engine/engine/c_market_engine.pxd +87 -0
  93. algo_engine/engine/c_market_engine.pyi +357 -0
  94. algo_engine/engine/event_engine.py +53 -0
  95. algo_engine/engine/trade_engine.py +2037 -0
  96. algo_engine/exchange_profile/__infra__.pxd +100 -0
  97. algo_engine/exchange_profile/__init__.pxd +100 -0
  98. algo_engine/exchange_profile/__init__.py +53 -0
  99. algo_engine/exchange_profile/c_ex_profile_base.c +87 -0
  100. algo_engine/exchange_profile/c_ex_profile_base.h +1204 -0
  101. algo_engine/exchange_profile/c_ex_profile_cn.c +968 -0
  102. algo_engine/exchange_profile/c_ex_profile_cn.h +39 -0
  103. algo_engine/exchange_profile/c_exchange_profile.c +52375 -0
  104. algo_engine/exchange_profile/c_exchange_profile.cp315-win_amd64.pyd +0 -0
  105. algo_engine/exchange_profile/c_exchange_profile.pxd +336 -0
  106. algo_engine/exchange_profile/c_exchange_profile.pyi +883 -0
  107. algo_engine/exchange_profile/c_exchange_profile.pyx +1495 -0
  108. algo_engine/exchange_profile/c_profile_cn.c +7798 -0
  109. algo_engine/exchange_profile/c_profile_cn.cp315-win_amd64.pyd +0 -0
  110. algo_engine/exchange_profile/c_profile_cn.pxd +1 -0
  111. algo_engine/exchange_profile/c_profile_cn.pyi +3 -0
  112. algo_engine/exchange_profile/c_profile_cn.pyx +1 -0
  113. algo_engine/exchange_profile/c_profile_default.c +7798 -0
  114. algo_engine/exchange_profile/c_profile_default.cp315-win_amd64.pyd +0 -0
  115. algo_engine/exchange_profile/c_profile_default.pxd +1 -0
  116. algo_engine/exchange_profile/c_profile_default.pyi +3 -0
  117. algo_engine/exchange_profile/c_profile_default.pyx +1 -0
  118. algo_engine/exchange_profile/c_profile_dispatcher.c +7798 -0
  119. algo_engine/exchange_profile/c_profile_dispatcher.cp315-win_amd64.pyd +0 -0
  120. algo_engine/exchange_profile/c_profile_dispatcher.pxd +1 -0
  121. algo_engine/exchange_profile/c_profile_dispatcher.pyi +3 -0
  122. algo_engine/exchange_profile/c_profile_dispatcher.pyx +1 -0
  123. algo_engine/includes/algo_engine/base/c_allocator_protocol.c +11608 -0
  124. algo_engine/includes/algo_engine/base/c_intern_string.c +5908 -0
  125. algo_engine/includes/algo_engine/base/c_market_data/c_candlestick.c +18996 -0
  126. algo_engine/includes/algo_engine/base/c_market_data/c_internal.c +14059 -0
  127. algo_engine/includes/algo_engine/base/c_market_data/c_market_data.c +30420 -0
  128. algo_engine/includes/algo_engine/base/c_market_data/c_market_data.h +1464 -0
  129. algo_engine/includes/algo_engine/base/c_market_data/c_market_data_buffer.c +29407 -0
  130. algo_engine/includes/algo_engine/base/c_market_data/c_market_data_buffer.h +941 -0
  131. algo_engine/includes/algo_engine/base/c_market_data/c_market_data_config.h +29 -0
  132. algo_engine/includes/algo_engine/base/c_market_data/c_tick.c +44303 -0
  133. algo_engine/includes/algo_engine/base/c_market_data/c_trade_utils.c +28702 -0
  134. algo_engine/includes/algo_engine/base/c_market_data/c_transaction.c +23558 -0
  135. algo_engine/includes/algo_engine/engine/c_event_engine.c +16325 -0
  136. algo_engine/includes/algo_engine/engine/c_market_engine.c +24258 -0
  137. algo_engine/includes/algo_engine/exchange_profile/c_ex_profile_base.c +87 -0
  138. algo_engine/includes/algo_engine/exchange_profile/c_ex_profile_base.h +1204 -0
  139. algo_engine/includes/algo_engine/exchange_profile/c_ex_profile_cn.c +968 -0
  140. algo_engine/includes/algo_engine/exchange_profile/c_ex_profile_cn.h +39 -0
  141. algo_engine/includes/algo_engine/exchange_profile/c_exchange_profile.c +52375 -0
  142. algo_engine/includes/algo_engine/exchange_profile/c_profile_cn.c +7798 -0
  143. algo_engine/includes/algo_engine/exchange_profile/c_profile_default.c +7798 -0
  144. algo_engine/includes/algo_engine/exchange_profile/c_profile_dispatcher.c +7798 -0
  145. algo_engine/monitor/__init__.py +15 -0
  146. algo_engine/monitor/advanced_data_interface.py +334 -0
  147. algo_engine/strategy/__init__.py +44 -0
  148. algo_engine/strategy/strategy_engine.py +441 -0
  149. algo_engine/utils/__init__.py +3 -0
  150. algo_engine/utils/commit_regularizer.py +49 -0
  151. algo_engine/utils/data_utils.py +296 -0
  152. pyalgoengine-0.12.3.dist-info/METADATA +142 -0
  153. pyalgoengine-0.12.3.dist-info/RECORD +156 -0
  154. pyalgoengine-0.12.3.dist-info/WHEEL +5 -0
  155. pyalgoengine-0.12.3.dist-info/licenses/LICENSE +21 -0
  156. pyalgoengine-0.12.3.dist-info/top_level.txt +2 -0
@@ -0,0 +1,15 @@
1
+ from ..engine import MDS, MarketDataMonitor as Monitor
2
+
3
+
4
+ def add_synthetic_orderbook():
5
+ # init synthetic orderbook monitor
6
+ monitor = SyntheticOrderBookMonitor(mds=MDS)
7
+ MDS.add_monitor(monitor=monitor)
8
+ # override current orderbook
9
+ MDS.bid = monitor.bid
10
+ MDS.ask = monitor.ask
11
+
12
+
13
+ from .advanced_data_interface import *
14
+
15
+ __all__ = ['Monitor', 'SyntheticOrderBookMonitor', 'MinuteBarMonitor']
@@ -0,0 +1,334 @@
1
+ import datetime
2
+ import json
3
+ import pickle
4
+ from ctypes import Structure, c_double
5
+ from multiprocessing import shared_memory
6
+ from typing import Self
7
+
8
+ from . import Monitor
9
+ from ..base import TradeData, MarketData, OrderData, TransactionData, TransactionSide, TickData, TickDataLite, BarData
10
+
11
+
12
+ class Entry(Structure):
13
+ _fields_ = [
14
+ ("price", c_double), # Double precision floating point for price
15
+ ("volume", c_double), # Double precision floating point for volume
16
+ ]
17
+
18
+ def __repr__(self):
19
+ return f"Entry(price={self.price}, volume={self.volume}, n_orders={self.n_orders})"
20
+
21
+
22
+ class PyOrderBook(object):
23
+ def __init__(self, side: TransactionSide, data: dict[float, Entry] = None):
24
+ self.side = TransactionSide(side)
25
+ self.data: dict[float, Entry] = {}
26
+ self.timestamp: float = 0
27
+
28
+ if data:
29
+ self.data.update(data)
30
+
31
+
32
+ class SyntheticOrderBookMonitor(Monitor):
33
+
34
+ def __init__(self, **kwargs):
35
+
36
+ super().__init__(
37
+ name=kwargs.pop('name', 'Monitor.SyntheticOrderBook'),
38
+ monitor_id=kwargs.pop('monitor_id', None)
39
+ )
40
+
41
+ self.bid: dict[str, PyOrderBook] = {}
42
+ self.ask: dict[str, PyOrderBook] = {}
43
+ self.tick_data: dict[str, TickData | TickDataLite] = {}
44
+
45
+ def __call__(self, market_data: MarketData, **kwargs):
46
+ if isinstance(market_data, (TickData, TickDataLite)):
47
+ self._on_tick_data(market_data)
48
+ if isinstance(market_data, (TransactionData, TradeData)):
49
+ self._on_trade_data(market_data)
50
+
51
+ def _get_order_book(self, ticker: str) -> tuple[PyOrderBook, PyOrderBook]:
52
+ if ticker in self.bid:
53
+ bid = self.bid[ticker]
54
+ else:
55
+ bid = self.bid[ticker] = PyOrderBook(side=TransactionSide.SIDE_BID)
56
+
57
+ if ticker in self.ask:
58
+ ask = self.ask[ticker]
59
+ else:
60
+ ask = self.ask[ticker] = PyOrderBook(side=TransactionSide.SIDE_ASK)
61
+
62
+ return bid, ask
63
+
64
+ def _on_tick_data(self, tick_data: TickData):
65
+ if isinstance(tick_data, TickDataLite):
66
+ self.tick_data[tick_data.ticker] = tick_data
67
+ else:
68
+ self.tick_data[tick_data.ticker] = tick_data.lite
69
+
70
+ def _on_trade_data(self, trade_data: TransactionData | TradeData):
71
+ ticker = trade_data.ticker
72
+ bid, ask = self._get_order_book(ticker)
73
+ price = trade_data.price
74
+ volume = trade_data.volume
75
+ sign = trade_data.side.sign
76
+
77
+ if sign == 1:
78
+ entry = ask.data.get(price)
79
+
80
+ if entry is None:
81
+ return
82
+
83
+ entry.volume -= volume
84
+ if entry.volume <= 0:
85
+ ask.data.pop(price, None)
86
+ elif sign == -1:
87
+ entry = bid.data.get(price)
88
+
89
+ if entry is None:
90
+ return
91
+
92
+ entry.volume -= volume
93
+ if entry.volume <= 0:
94
+ bid.data.pop(price, None)
95
+
96
+ def on_order_data(self, order_data: OrderData):
97
+ ticker = order_data.ticker
98
+ bid, ask = self._get_order_book(ticker)
99
+ price = order_data.price
100
+ volume = order_data.volume
101
+ sign = order_data.side.sign
102
+
103
+ if sign == 1:
104
+ entry = ask.data.get(price)
105
+
106
+ if entry is None:
107
+ ask.data[price] = Entry(price=price, volume=volume)
108
+ else:
109
+ entry.volume += volume
110
+ elif sign == -1:
111
+ entry = bid.data.get(price)
112
+
113
+ if entry is None:
114
+ bid.data[price] = Entry(price=price, volume=volume)
115
+ else:
116
+ entry.volume += volume
117
+
118
+ def to_json(self, fmt='str', **kwargs) -> str | dict:
119
+ data_dict = dict(
120
+ name=self.name,
121
+ monitor_id=self.monitor_id,
122
+ bid={ticker: {price: order_book.data[price].volume for price in sorted(order_book.data)} for ticker, order_book in self.bid.items()},
123
+ ask={ticker: {price: order_book.data[price].volume for price in sorted(order_book.data)} for ticker, order_book in self.ask.items()},
124
+ )
125
+
126
+ if fmt == 'dict':
127
+ return data_dict
128
+ elif fmt == 'str':
129
+ return json.dumps(data_dict, **kwargs)
130
+ else:
131
+ raise ValueError(f'Invalid format {fmt}, except "dict" or "str".')
132
+
133
+ @classmethod
134
+ def from_json(cls, json_message: str | bytes | bytearray | dict) -> Self:
135
+ if isinstance(json_message, dict):
136
+ json_dict = json_message
137
+ else:
138
+ json_dict = json.loads(json_message)
139
+
140
+ self = cls(
141
+ name=json_dict['name'],
142
+ monitor_id=json_dict['monitor_id'],
143
+ )
144
+
145
+ self.bid = {ticker: (book := PyOrderBook(side=TransactionSide.SIDE_BID, data={price: Entry(price=price, volume=volume) for price, volume in order_book.items()})) for ticker, order_book in json_dict['bid'].items()}
146
+ self.bid = {ticker: (book := PyOrderBook(side=TransactionSide.SIDE_ASK, data={price: Entry(price=price, volume=volume) for price, volume in order_book.items()})) for ticker, order_book in json_dict['ask'].items()}
147
+ return self
148
+
149
+ def clear(self) -> None:
150
+ self.bid.clear()
151
+ self.ask.clear()
152
+ self.tick_data.clear()
153
+
154
+ @property
155
+ def value(self) -> dict[str, TickData]:
156
+ order_book = {}
157
+ for ticker, tick_lite in self.tick_data.items():
158
+ bid, ask = self._get_order_book(ticker)
159
+
160
+ bid_price, bid_volume, ask_price, ask_volume = {}, {}, {}, {}
161
+
162
+ for i, price in enumerate(sorted(bid.data, reverse=True), start=1):
163
+ bid_price[f'bid_price_{i}'] = price
164
+ bid_price[f'bid_volume_{i}'] = bid.data[price].volume
165
+
166
+ for i, price in enumerate(sorted(ask.data), start=1):
167
+ bid_price[f'ask_price_{i}'] = price
168
+ bid_price[f'ask_volume_{i}'] = ask.data[price].volume
169
+
170
+ data = TickData(
171
+ ticker=tick_lite.ticker,
172
+ timestamp=tick_lite.timestamp,
173
+ last_price=tick_lite.last_price,
174
+ total_traded_volume=tick_lite.total_traded_volume,
175
+ total_traded_notional=tick_lite.total_traded_notional,
176
+ total_trade_count=tick_lite.total_trade_count,
177
+ **bid_price, **bid_volume, **ask_price, **ask_volume
178
+ )
179
+
180
+ order_book[ticker] = data
181
+
182
+ return order_book
183
+
184
+
185
+ class MinuteBarMonitor(Monitor):
186
+
187
+ def __init__(self, interval: float = 60., **kwargs):
188
+ self.interval = interval
189
+
190
+ super().__init__(
191
+ name=kwargs.pop('name', 'Monitor.MinuteBarMonitor'),
192
+ monitor_id=kwargs.pop('monitor_id', None)
193
+ )
194
+
195
+ self._minute_bar_data: dict[str, BarData] = {}
196
+ self._last_bar_data: dict[str, BarData] = {}
197
+
198
+ def __call__(self, market_data: MarketData, **kwargs):
199
+ self._update_last_bar(market_data=market_data, interval=self.interval)
200
+ # self._update_active_bar(market_data=market_data, interval=self.interval)
201
+
202
+ def _update_last_bar(self, market_data: MarketData, interval: float):
203
+ ticker = market_data.ticker
204
+ market_price = market_data.market_price
205
+ market_time = market_data.market_time
206
+ timestamp = market_data.timestamp
207
+
208
+ if ticker not in self._minute_bar_data or market_time >= self._minute_bar_data[ticker].bar_end_time:
209
+ # update bar_data
210
+ if ticker in self._minute_bar_data:
211
+ self._last_bar_data[ticker] = self._minute_bar_data[ticker]
212
+
213
+ bar_data = self._minute_bar_data[ticker] = BarData(
214
+ ticker=ticker,
215
+ timestamp=int(timestamp // interval + 1) * interval,
216
+ start_timestamp=int(timestamp // interval) * interval,
217
+ bar_span=datetime.timedelta(seconds=interval),
218
+ high_price=market_price,
219
+ low_price=market_price,
220
+ open_price=market_price,
221
+ close_price=market_price,
222
+ volume=0.,
223
+ notional=0.,
224
+ trade_count=0
225
+ )
226
+ else:
227
+ bar_data = self._minute_bar_data[ticker]
228
+
229
+ if isinstance(market_data, TradeData):
230
+ bar_data['volume'] += market_data.volume
231
+ bar_data['notional'] += market_data.notional
232
+ bar_data['trade_count'] += 1
233
+
234
+ bar_data['close_price'] = market_price
235
+ bar_data['high_price'] = max(bar_data.high_price, market_price)
236
+ bar_data['low_price'] = min(bar_data.low_price, market_price)
237
+
238
+ def _update_active_bar(self, market_data: MarketData, interval: float):
239
+ ticker = market_data.ticker
240
+ market_price = market_data.market_price
241
+ market_time = market_data.market_time
242
+ timestamp = market_data.timestamp
243
+
244
+ if ticker not in self._minute_bar_data or market_time >= self._minute_bar_data[ticker].bar_end_time:
245
+ bar_data = self._minute_bar_data[ticker] = BarData(
246
+ ticker=ticker,
247
+ start_timestamp=timestamp - interval,
248
+ timestamp=timestamp,
249
+ bar_span=datetime.timedelta(seconds=interval),
250
+ high_price=market_price,
251
+ low_price=market_price,
252
+ open_price=market_price,
253
+ close_price=market_price,
254
+ volume=0.,
255
+ notional=0.,
256
+ trade_count=0
257
+ )
258
+ bar_data.history = []
259
+
260
+ else:
261
+ bar_data = self._minute_bar_data[ticker]
262
+
263
+ history: list[TradeData] = getattr(bar_data, 'history')
264
+ bar_data['start_timestamp'] = timestamp - interval
265
+
266
+ if isinstance(market_data, TradeData):
267
+ history.append(market_data)
268
+
269
+ while True:
270
+ if history[0].market_time >= bar_data.bar_start_time:
271
+ break
272
+ else:
273
+ history.pop(0)
274
+
275
+ bar_data['volume'] = sum([_.volume for _ in history])
276
+ bar_data['notional'] = sum([_.notional for _ in history])
277
+ bar_data['trade_count'] = len([_.notional for _ in history])
278
+ bar_data['close_price'] = market_price
279
+ bar_data['open_price'] = history[0].market_price
280
+ bar_data['high_price'] = max([_.market_price for _ in history])
281
+ bar_data['low_price'] = min([_.market_price for _ in history])
282
+
283
+ def to_json(self, fmt='str', **kwargs) -> str | dict:
284
+ data_dict = dict(
285
+ name=self.name,
286
+ monitor_id=self.monitor_id,
287
+ interval=self.interval,
288
+ minute_bar_data={k: v.to_bytes() for k, v in self._minute_bar_data.items()},
289
+ last_bar_data={k: v.to_bytes() for k, v in self._last_bar_data.items()},
290
+ )
291
+
292
+ if fmt == 'dict':
293
+ return data_dict
294
+ elif fmt == 'str':
295
+ return json.dumps(data_dict, **kwargs)
296
+ else:
297
+ raise ValueError(f'Invalid format {fmt}, except "dict" or "str".')
298
+
299
+ @classmethod
300
+ def from_json(cls, json_message: str | bytes | bytearray | dict) -> Self:
301
+ if isinstance(json_message, dict):
302
+ json_dict = json_message
303
+ else:
304
+ json_dict = json.loads(json_message)
305
+
306
+ self = cls(
307
+ name=json_dict['name'],
308
+ monitor_id=json_dict['monitor_id'],
309
+ interval=json_dict['interval'],
310
+ )
311
+
312
+ self._minute_bar_data = {k: BarData.from_bytes(v) for k, v in json_dict['minute_bar_data'].items()}
313
+ self._last_bar_data = {k: BarData.from_bytes(v) for k, v in json_dict['last_bar_data'].items()}
314
+ return self
315
+
316
+ def from_shm(self, name: str = None) -> None:
317
+ if name is None:
318
+ name = f'{self.monitor_id}.json'
319
+
320
+ shm = shared_memory.SharedMemory(name=name)
321
+ json_dict = pickle.loads(bytes(shm.buf))
322
+
323
+ self.clear()
324
+
325
+ self._minute_bar_data.update({k: BarData.from_bytes(v) for k, v in json_dict['minute_bar_data'].items()})
326
+ self._last_bar_data.update({k: BarData.from_bytes(v) for k, v in json_dict['last_bar_data'].items()})
327
+
328
+ def clear(self) -> None:
329
+ self._minute_bar_data.clear()
330
+ self._last_bar_data.clear()
331
+
332
+ @property
333
+ def value(self) -> dict[str, BarData]:
334
+ return self._last_bar_data
@@ -0,0 +1,44 @@
1
+ import logging
2
+ from threading import Lock
3
+
4
+ from .. import LOGGER
5
+ from ..base import TradeInstruction
6
+ from ..engine import EVENT_ENGINE, TOPIC, MDS, MarketDataService, Balance, Inventory, DirectMarketAccess, RiskProfile, PositionManagementService
7
+
8
+ LOGGER = LOGGER.getChild('Strategy')
9
+
10
+ from .strategy_engine import StrategyEngine
11
+
12
+
13
+ class EventDMA(DirectMarketAccess):
14
+ def __init__(self, mds: MarketDataService, risk_profile: RiskProfile, event_engine=None, cool_down: float = None):
15
+ self.event_engine = EVENT_ENGINE if event_engine is None else event_engine
16
+ super().__init__(mds=mds, risk_profile=risk_profile, cool_down=cool_down)
17
+
18
+ def _launch_order_handler(self, order: TradeInstruction, **kwargs):
19
+ self.event_engine.put(topic=TOPIC.launch_order(ticker=order.ticker), order=order, **kwargs)
20
+
21
+ def _cancel_order_handler(self, order: TradeInstruction, **kwargs):
22
+ self.event_engine.put(topic=TOPIC.cancel_order(ticker=order.ticker), order_id=order.order_id, **kwargs)
23
+
24
+ def _reject_order_handler(self, order: TradeInstruction, **kwargs):
25
+ raise NotImplementedError()
26
+
27
+
28
+ def set_logger(logger: logging.Logger):
29
+ global LOGGER
30
+ LOGGER = logger
31
+
32
+ strategy_engine.LOGGER = logger.getChild('Strategy')
33
+
34
+
35
+ REPLAY_LOCK = Lock()
36
+ INVENTORY = Inventory()
37
+ BALANCE = Balance(inventory=INVENTORY) # need to be registered
38
+ RISK_PROFILE = RiskProfile(mds=MDS, balance=BALANCE)
39
+ DMA = EventDMA(mds=MDS, risk_profile=RISK_PROFILE)
40
+ POSITION_TRACKER = PositionManagementService(dma=DMA)
41
+ STRATEGY_ENGINE = StrategyEngine(event_engine=EVENT_ENGINE, position_tracker=POSITION_TRACKER) # need to be registered, also register MDS
42
+ BALANCE.add(strategy=STRATEGY_ENGINE, position_tracker=POSITION_TRACKER)
43
+
44
+ __all__ = ['INVENTORY', 'BALANCE', 'RISK_PROFILE', 'DMA', 'POSITION_TRACKER', 'STRATEGY_ENGINE']