PyAlgoEngine 0.12.0.post3__cp313-cp313-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 +91 -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 +10756 -0
  29. algo_engine/base/c_allocator_protocol.cp313-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 +88 -0
  33. algo_engine/base/c_intern_string.c +5845 -0
  34. algo_engine/base/c_intern_string.cp313-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 +18960 -0
  42. algo_engine/base/c_market_data/c_candlestick.cp313-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 +14023 -0
  47. algo_engine/base/c_market_data/c_internal.cp313-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 +30739 -0
  52. algo_engine/base/c_market_data/c_market_data.cp313-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 +418 -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 +688 -0
  57. algo_engine/base/c_market_data/c_market_data_buffer.c +29370 -0
  58. algo_engine/base/c_market_data/c_market_data_buffer.cp313-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 +44230 -0
  65. algo_engine/base/c_market_data/c_tick.cp313-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 +28665 -0
  70. algo_engine/base/c_market_data/c_trade_utils.cp313-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 +23521 -0
  75. algo_engine/base/c_market_data/c_transaction.cp313-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 +104 -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 +16289 -0
  87. algo_engine/engine/c_event_engine.cp313-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 +24222 -0
  91. algo_engine/engine/c_market_engine.cp313-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 +99 -0
  97. algo_engine/exchange_profile/__init__.pxd +99 -0
  98. algo_engine/exchange_profile/__init__.py +36 -0
  99. algo_engine/exchange_profile/c_ex_profile_base.c +87 -0
  100. algo_engine/exchange_profile/c_ex_profile_base.h +1148 -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 +52061 -0
  104. algo_engine/exchange_profile/c_exchange_profile.cp313-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 +867 -0
  107. algo_engine/exchange_profile/c_exchange_profile.pyx +1482 -0
  108. algo_engine/exchange_profile/c_profile_cn.c +7779 -0
  109. algo_engine/exchange_profile/c_profile_cn.cp313-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 +7779 -0
  114. algo_engine/exchange_profile/c_profile_default.cp313-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 +7779 -0
  119. algo_engine/exchange_profile/c_profile_dispatcher.cp313-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 +10756 -0
  124. algo_engine/includes/algo_engine/base/c_intern_string.c +5845 -0
  125. algo_engine/includes/algo_engine/base/c_market_data/c_candlestick.c +18960 -0
  126. algo_engine/includes/algo_engine/base/c_market_data/c_internal.c +14023 -0
  127. algo_engine/includes/algo_engine/base/c_market_data/c_market_data.c +30739 -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 +29370 -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 +44230 -0
  133. algo_engine/includes/algo_engine/base/c_market_data/c_trade_utils.c +28665 -0
  134. algo_engine/includes/algo_engine/base/c_market_data/c_transaction.c +23521 -0
  135. algo_engine/includes/algo_engine/engine/c_event_engine.c +16289 -0
  136. algo_engine/includes/algo_engine/engine/c_market_engine.c +24222 -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 +1148 -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 +52061 -0
  142. algo_engine/includes/algo_engine/exchange_profile/c_profile_cn.c +7779 -0
  143. algo_engine/includes/algo_engine/exchange_profile/c_profile_default.c +7779 -0
  144. algo_engine/includes/algo_engine/exchange_profile/c_profile_dispatcher.c +7779 -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.0.post3.dist-info/METADATA +142 -0
  153. pyalgoengine-0.12.0.post3.dist-info/RECORD +156 -0
  154. pyalgoengine-0.12.0.post3.dist-info/WHEEL +5 -0
  155. pyalgoengine-0.12.0.post3.dist-info/licenses/LICENSE +21 -0
  156. pyalgoengine-0.12.0.post3.dist-info/top_level.txt +2 -0
@@ -0,0 +1,258 @@
1
+ import math
2
+ import numbers
3
+ import operator
4
+
5
+ __all__ = ['FinancialDecimal']
6
+
7
+ from typing import Self
8
+
9
+ TICK_SIZE = 100
10
+
11
+
12
+ class FinancialDecimal(float):
13
+ __slots__ = ('_k', '_tick')
14
+
15
+ # We're immutable, so use __new__ not __init__
16
+ def __new__(cls, value: numbers.Real | str = 0., /, k: int = None, tick: int = None):
17
+ if tick is not None and type(tick) is not int:
18
+ raise TypeError(f'tick of {cls} must be a integer.')
19
+ elif tick is None:
20
+ tick = TICK_SIZE
21
+ elif tick <= 0:
22
+ raise ValueError(f'tick of {cls} must be a positive integer.')
23
+
24
+ if k is not None and type(k) is not int:
25
+ raise TypeError(f'k of {cls} must be a integer.')
26
+ elif k is None:
27
+ if type(value) is int:
28
+ k = value * tick
29
+ elif isinstance(value, numbers.Real):
30
+ k = round(value * tick)
31
+ elif isinstance(value, (str, bytes)):
32
+ k = round(float(value) * tick)
33
+ else:
34
+ raise TypeError(f'value of the {cls} must be a float-convertable.')
35
+
36
+ self = super(FinancialDecimal, cls).__new__(cls, k / tick)
37
+
38
+ self._k = k
39
+ self._tick = tick
40
+ return self
41
+
42
+ def __repr__(self):
43
+ return f'{self.__class__.__name__}({self._k}, {self._tick})'
44
+
45
+ def __str__(self):
46
+ if self._tick == 1:
47
+ return str(self._k)
48
+ else:
49
+ digits = math.ceil(math.log10(self._tick))
50
+ return format(float(self), f'.{digits}f')
51
+
52
+ def __reduce__(self):
53
+ return self.__class__, (self._k, self._tick)
54
+
55
+ def __copy__(self):
56
+ # for a float, the copy method should return a different object / memory location. In fact, this method should not even be implemented!
57
+ # however the Fraction, as an immutable, use the same object / memory location, as clone.
58
+ # this method uses float-style implementation
59
+ return self.__class__(self._k, self._tick)
60
+
61
+ def __deepcopy__(self, memo):
62
+ return self.__class__(self._k, self._tick)
63
+
64
+ @classmethod
65
+ def from_float(cls, f: float, tick=None):
66
+ if isinstance(f, numbers.Integral):
67
+ return cls(int(f), tick=tick)
68
+ elif not isinstance(f, float):
69
+ raise TypeError(f"{cls.__name__}.from_float() only takes floats, not {f!r} ({type(f).__name__})")
70
+
71
+ return cls(f, tick=tick)
72
+
73
+ def as_integer_ratio(self) -> tuple[int, int]:
74
+ return self._k, self._tick
75
+
76
+ def __add__(self, other: int | float | numbers.Real):
77
+ if isinstance(other, int):
78
+ return self.__class__(k=self._k + other * self._tick, tick=self._tick)
79
+ elif isinstance(other, self.__class__) and self._tick == other._tick:
80
+ return self.__class__(k=self._k + other._k, tick=self._tick)
81
+ elif isinstance(other, float):
82
+ return self.__class__(k=round(self._k + float.__mul__(other, self._tick)), tick=self._tick)
83
+ else:
84
+ return float.__add__(self, other)
85
+
86
+ def __radd__(self, other: int | float | numbers.Real):
87
+ if isinstance(other, numbers.Real):
88
+ return self.__add__(other)
89
+ else:
90
+ return other.__add__(float(self))
91
+
92
+ def __sub__(self, other: int | float | numbers.Real):
93
+ if isinstance(other, int):
94
+ return self.__class__(k=self._k - other * self._tick, tick=self._tick)
95
+ elif isinstance(other, self.__class__) and self._tick == other._tick:
96
+ return self.__class__(k=self._k - other._k, tick=self._tick)
97
+ elif isinstance(other, float):
98
+ return self.__class__(k=round(self._k - float.__mul__(other, self._tick)), tick=self._tick)
99
+ else:
100
+ return float.__sub__(self, other)
101
+
102
+ def __rsub__(self, other: int | float | numbers.Real):
103
+ if isinstance(other, numbers.Real):
104
+ return FinancialDecimal(other, tick=self._tick).__sub__(self)
105
+ else:
106
+ return float.__sub__(other, self)
107
+
108
+ def __mul__(self, other: int | float | numbers.Real):
109
+ if isinstance(other, int):
110
+ return self.__class__(k=self._k * other, tick=self._tick)
111
+ elif isinstance(other, float):
112
+ return self.__class__(k=round(self._k * float(other)), tick=self._tick)
113
+ else:
114
+ return float.__mul__(self, other)
115
+
116
+ def __rmul__(self, other: int | float | numbers.Real):
117
+ if isinstance(other, numbers.Real):
118
+ return self.__mul__(other)
119
+ else:
120
+ return other.__mul__(float(self))
121
+
122
+ def __truediv__(self, other: int | float | numbers.Real):
123
+ if isinstance(other, numbers.Real):
124
+ return self.__class__(k=round(self._k / float(other)), tick=self._tick)
125
+ else:
126
+ float.__truediv__(self, other)
127
+
128
+ def __rtruediv__(self, other: int | float | Self):
129
+ if isinstance(other, numbers.Real):
130
+ return self.__class__(k=round(float(other) / float(self) * self._tick), tick=self._tick)
131
+ else:
132
+ float.__truediv__(other, self)
133
+
134
+ def __floordiv__(self, other: int | float | Self):
135
+ if isinstance(other, (int, self.__class__)):
136
+ return (self._k * other.denominator) // (self._tick * other.numerator)
137
+ else:
138
+ return float.__floordiv__(self, other)
139
+
140
+ def __rdivmod__(self, other):
141
+ return divmod(other, float(self))
142
+
143
+ def __pow__(self, other: int | float | Self, __mod: None = None):
144
+ if isinstance(other, numbers.Real):
145
+ return self.__class__(k=round(float(self) ** float(other) * self._tick), tick=self._tick)
146
+ else:
147
+ return float.__pow__(self, other, __mod)
148
+
149
+ def __pos__(self):
150
+ return FinancialDecimal(k=self._k, tick=self._tick)
151
+
152
+ def __neg__(self):
153
+ return FinancialDecimal(k=-self._k, tick=self._tick)
154
+
155
+ def __abs__(self):
156
+ return FinancialDecimal(k=abs(self._k), tick=self._tick)
157
+
158
+ def _richcmp(self, other, op):
159
+ # convert other to a Rational instance where reasonable.
160
+ if isinstance(other, (numbers.Rational, self.__class__)):
161
+ return op(self._k * other.denominator, self._tick * other.numerator)
162
+ if isinstance(other, float):
163
+ if math.isnan(other) or math.isinf(other):
164
+ return op(0.0, other)
165
+ else:
166
+ return op(self, FinancialDecimal(other, tick=self._tick))
167
+ else:
168
+ return NotImplemented
169
+
170
+ def __eq__(self, other):
171
+ return self._richcmp(other, operator.eq)
172
+
173
+ def __lt__(self, other):
174
+ """a < b"""
175
+ return self._richcmp(other, operator.lt)
176
+
177
+ def __gt__(self, other):
178
+ """a > b"""
179
+ return self._richcmp(other, operator.gt)
180
+
181
+ def __le__(self, other):
182
+ """a <= b"""
183
+ return self._richcmp(other, operator.le)
184
+
185
+ def __ge__(self, other):
186
+ """a >= b"""
187
+ return self._richcmp(other, operator.ge)
188
+
189
+ def __bool__(self):
190
+ return bool(self._k)
191
+
192
+ @property
193
+ def k(self):
194
+ return self._k
195
+
196
+ @property
197
+ def numerator(self):
198
+ return self._k
199
+
200
+ @property
201
+ def tick(self):
202
+ return self._tick
203
+
204
+ @property
205
+ def denominator(self):
206
+ return self._tick
207
+
208
+
209
+ def main():
210
+ fd_0 = FinancialDecimal(math.pi, tick=10000)
211
+ print(f'fd_0 => {fd_0}')
212
+ print(f'fd_0:.3f => {fd_0:.3f}')
213
+
214
+ # i = 2
215
+ for i in [2, math.e]:
216
+ print(f'fd_0 + {i} => {fd_0 + i}')
217
+ print(f'{i} + fd_0 => {i + fd_0}')
218
+
219
+ print(f'fd_0 - {i} => {fd_0 - i}')
220
+ print(f'{i} - fd_0 => {i - fd_0}')
221
+
222
+ print(f'fd_0 * {i} => {fd_0 * i}')
223
+ print(f'{i} * fd_0 => {i * fd_0}')
224
+
225
+ print(f'fd_0 / {i} => {fd_0 / i}')
226
+ print(f'{i} / fd_0 => {i / fd_0}')
227
+
228
+ print(f'fd_0 ** {i} => {fd_0 ** i}')
229
+ print(f'{i} ** fd_0 => {i ** fd_0}')
230
+
231
+ print(f'fd_0 // {i} => {fd_0 // i}')
232
+ print(f'{i} // fd_0 => {i // fd_0}')
233
+
234
+ print(f'fd_0 % {i} => {fd_0 % i}')
235
+ print(f'{i} % fd_0 => {i % fd_0}')
236
+
237
+ print(f'fd_0 __divmod__ {i} => {fd_0.__divmod__(i)}')
238
+ print(f'{i} __divmod__ fd_0 => {divmod(i, fd_0)}')
239
+
240
+ print(f'fd_0 == math.pi => {fd_0 == math.pi}')
241
+ print(f'fd_0 == 2 => {fd_0 == 2}')
242
+ print(f'fd_0 == math.e => {fd_0 == math.e}')
243
+
244
+ print(f'fd_0 >= math.pi => {fd_0 >= math.pi}')
245
+ print(f'fd_0 >= 2 => {fd_0 >= 2}')
246
+ print(f'fd_0 >= math.e => {fd_0 >= math.e}')
247
+
248
+ print(f'fd_0 <= math.pi => {fd_0 <= math.pi}')
249
+ print(f'fd_0 <= 2 => {fd_0 <= 2}')
250
+ print(f'fd_0 <= math.e => {fd_0 <= math.e}')
251
+
252
+ print(f'math.pi == fd_0 => {math.pi == fd_0}')
253
+ print(f'2 == fd_0=> {2 == fd_0}')
254
+ print(f'math.e == fd_0 => {math.e == fd_0}')
255
+
256
+
257
+ if __name__ == '__main__':
258
+ main()
@@ -0,0 +1,104 @@
1
+ import logging
2
+ import sys
3
+ import time
4
+
5
+ LOGGER: logging.Logger | None = None
6
+ LOG_LEVEL = logging.INFO
7
+
8
+
9
+ class ColoredFormatter(logging.Formatter):
10
+ """Logging Formatter to add colors and count warning / errors"""
11
+
12
+ def __init__(self, fmt=None, datefmt=None, style='{', validate=True):
13
+ self.format_str = '[{asctime} {name} - {threadName} - {module}:{lineno} - {levelname}] {message}' if fmt is None else fmt
14
+ self.date_fmt = '%Y-%m-%d %H:%M:%S' if datefmt is None else datefmt
15
+ self.style = style
16
+
17
+ super().__init__(fmt=fmt, datefmt=datefmt, style=style, validate=validate)
18
+
19
+ def _get_format(self, level: int, select=False):
20
+ bold_red = f"\33[31;1;3;4{';7' if select else ''}m"
21
+ red = f"\33[31;1{';7' if select else ''}m"
22
+ green = f"\33[32;1{';7' if select else ''}m"
23
+ yellow = f"\33[33;1{';7' if select else ''}m"
24
+ blue = f"\33[34;1{';7' if select else ''}m"
25
+ reset = "\33[0m"
26
+
27
+ if level <= logging.NOTSET:
28
+ fmt = self.format_str
29
+ elif level <= logging.DEBUG:
30
+ fmt = blue + self.format_str + reset
31
+ elif level <= logging.INFO:
32
+ fmt = green + self.format_str + reset
33
+ elif level <= logging.WARNING:
34
+ fmt = yellow + self.format_str + reset
35
+ elif level <= logging.ERROR:
36
+ fmt = red + self.format_str + reset
37
+ else:
38
+ fmt = bold_red + self.format_str + reset
39
+
40
+ return fmt
41
+
42
+ def format(self, record):
43
+ log_fmt = self._get_format(level=record.levelno)
44
+ formatter = logging.Formatter(log_fmt, datefmt=self.date_fmt, style=self.style)
45
+ return formatter.format(record)
46
+
47
+
48
+ class DuplicateWarningFilter(logging.Filter):
49
+ """Filter that lets each WARNING message pass only once per handler."""
50
+
51
+ def __init__(self, name: str = ''):
52
+ super().__init__(name=name)
53
+ self._seen_warnings: set[str] = set()
54
+
55
+ def filter(self, record: logging.LogRecord) -> bool:
56
+ if record.levelno != logging.WARNING:
57
+ return True
58
+
59
+ message = record.getMessage()
60
+ if message in self._seen_warnings:
61
+ return False
62
+
63
+ self._seen_warnings.add(message)
64
+ return True
65
+
66
+
67
+ def get_logger(**kwargs) -> logging.Logger:
68
+ level = kwargs.get('level', LOG_LEVEL)
69
+ stream_io = kwargs.get('stream_io', sys.stdout)
70
+ formatter = kwargs.get('formatter', ColoredFormatter())
71
+ global LOGGER
72
+
73
+ if LOGGER is not None:
74
+ return LOGGER
75
+
76
+ LOGGER = logging.getLogger('PyAlgoEngine')
77
+ LOGGER.setLevel(level)
78
+ logging.Formatter.converter = time.gmtime
79
+
80
+ if stream_io:
81
+ have_handler = False
82
+ stream_handler: logging.StreamHandler | None = None
83
+ for handler in LOGGER.handlers:
84
+ # noinspection PyUnresolvedReferences
85
+ if isinstance(handler, logging.StreamHandler) and handler.stream == stream_io:
86
+ have_handler = True
87
+ stream_handler = handler
88
+ break
89
+
90
+ if not have_handler:
91
+ logger_ch = logging.StreamHandler(stream=stream_io)
92
+ logger_ch.setLevel(level=level)
93
+ logger_ch.setFormatter(fmt=formatter)
94
+ LOGGER.addHandler(logger_ch)
95
+ stream_handler = logger_ch
96
+
97
+ if stream_handler is not None and not any(isinstance(flt, DuplicateWarningFilter) for flt in stream_handler.filters):
98
+ stream_handler.addFilter(DuplicateWarningFilter())
99
+
100
+ assert LOGGER is not None
101
+ return LOGGER
102
+
103
+
104
+ _ = get_logger()
@@ -0,0 +1,10 @@
1
+ from .c_market_engine cimport MDS, MonitorManager, MarketDataService
2
+
3
+ # Involving TopicSet is not necessary, and will causes conflict with quark.collections.c_allocator and quark.collections.c_bytemap
4
+ # Should only cimport directly when necessary
5
+ from .c_event_engine cimport TopicSet, EVENT_ENGINE
6
+
7
+ __all__ = [
8
+ 'MDS', 'MonitorManager', 'MarketDataService',
9
+ 'TopicSet', 'EVENT_ENGINE'
10
+ ]
@@ -0,0 +1,10 @@
1
+ from .c_market_engine cimport MDS, MonitorManager, MarketDataService
2
+
3
+ # Involving TopicSet is not necessary, and will causes conflict with quark.collections.c_allocator and quark.collections.c_bytemap
4
+ # Should only cimport directly when necessary
5
+ from .c_event_engine cimport TopicSet, EVENT_ENGINE
6
+
7
+ __all__ = [
8
+ 'MDS', 'MonitorManager', 'MarketDataService',
9
+ 'TopicSet', 'EVENT_ENGINE'
10
+ ]
@@ -0,0 +1,40 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+
5
+ from .. import LOGGER
6
+
7
+ LOGGER = LOGGER.getChild('Engine')
8
+
9
+
10
+ def set_logger(logger: logging.Logger):
11
+ global LOGGER
12
+ LOGGER = logger
13
+
14
+ algo_engine.LOGGER = logger.getChild('AlgoEngine')
15
+ c_event_engine.EVENT_ENGINE.logger = logger.getChild('EventEngine')
16
+ c_market_engine.LOGGER = logger.getChild('MarketEngine')
17
+ trade_engine.LOGGER = logger.getChild('TradeEngine')
18
+
19
+
20
+ class Singleton(type):
21
+ _instances = {}
22
+
23
+ def __call__(cls, *args, **kwargs):
24
+ if cls not in cls._instances:
25
+ cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
26
+ return cls._instances[cls]
27
+
28
+
29
+ from .c_event_engine import EVENT_ENGINE, TOPIC, TopicSet
30
+ from .c_market_engine import MDS, MarketDataService, MarketDataMonitor, MonitorManager
31
+ from .algo_engine import AlgoTemplate, ALGO_ENGINE, ALGO_REGISTRY
32
+ from .trade_engine import DirectMarketAccess, Balance, PositionManagementService, Inventory, RiskProfile
33
+
34
+ __all__ = [
35
+ 'LOGGER',
36
+ 'EVENT_ENGINE', 'TOPIC', 'TopicSet',
37
+ 'AlgoTemplate', 'ALGO_ENGINE', 'ALGO_REGISTRY',
38
+ 'MDS', 'MarketDataService', 'MarketDataMonitor', 'MonitorManager', 'Singleton',
39
+ 'DirectMarketAccess', 'Balance', 'PositionManagementService', 'Inventory', 'RiskProfile'
40
+ ]