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.
- algo_engine/__infra__.pxd +3 -0
- algo_engine/__init__.pxd +3 -0
- algo_engine/__init__.py +91 -0
- algo_engine/apps/__init__.py +17 -0
- algo_engine/apps/backtest/__init__.py +20 -0
- algo_engine/apps/backtest/doc_server.py +328 -0
- algo_engine/apps/backtest/static/styles/dash.css +48 -0
- algo_engine/apps/backtest/templates/dash.html +20 -0
- algo_engine/apps/backtest/templates/index.html +40 -0
- algo_engine/apps/backtest/tester.py +252 -0
- algo_engine/apps/backtest/web_app.py +125 -0
- algo_engine/apps/bokeh_server.py +245 -0
- algo_engine/apps/demo/__init__.py +0 -0
- algo_engine/apps/demo/test.py +40 -0
- algo_engine/apps/sim_input/__init__.py +23 -0
- algo_engine/apps/sim_input/client.py +412 -0
- algo_engine/apps/sim_input/sim_keyboard.py +88 -0
- algo_engine/apps/sim_input/sim_mouse.py +137 -0
- algo_engine/apps/sim_input/window.py +162 -0
- algo_engine/backtest/__init__.py +19 -0
- algo_engine/backtest/__main__.py +51 -0
- algo_engine/backtest/metrics.py +179 -0
- algo_engine/backtest/replay.py +635 -0
- algo_engine/backtest/sim_match.py +506 -0
- algo_engine/base/__infra__.pxd +3 -0
- algo_engine/base/__init__.pxd +3 -0
- algo_engine/base/__init__.py +60 -0
- algo_engine/base/c_allocator_protocol.c +10756 -0
- algo_engine/base/c_allocator_protocol.cp313-win_amd64.pyd +0 -0
- algo_engine/base/c_allocator_protocol.pxd +24 -0
- algo_engine/base/c_allocator_protocol.pyi +68 -0
- algo_engine/base/c_allocator_protocol.pyx +88 -0
- algo_engine/base/c_intern_string.c +5845 -0
- algo_engine/base/c_intern_string.cp313-win_amd64.pyd +0 -0
- algo_engine/base/c_intern_string.pxd +14 -0
- algo_engine/base/c_intern_string.pyi +22 -0
- algo_engine/base/c_intern_string.pyx +17 -0
- algo_engine/base/c_market_data/__infra__.pxd +196 -0
- algo_engine/base/c_market_data/__init__.pxd +196 -0
- algo_engine/base/c_market_data/__init__.py +24 -0
- algo_engine/base/c_market_data/c_candlestick.c +18960 -0
- algo_engine/base/c_market_data/c_candlestick.cp313-win_amd64.pyd +0 -0
- algo_engine/base/c_market_data/c_candlestick.pxd +27 -0
- algo_engine/base/c_market_data/c_candlestick.pyi +217 -0
- algo_engine/base/c_market_data/c_candlestick.pyx +255 -0
- algo_engine/base/c_market_data/c_internal.c +14023 -0
- algo_engine/base/c_market_data/c_internal.cp313-win_amd64.pyd +0 -0
- algo_engine/base/c_market_data/c_internal.pxd +14 -0
- algo_engine/base/c_market_data/c_internal.pyi +47 -0
- algo_engine/base/c_market_data/c_internal.pyx +41 -0
- algo_engine/base/c_market_data/c_market_data.c +30739 -0
- algo_engine/base/c_market_data/c_market_data.cp313-win_amd64.pyd +0 -0
- algo_engine/base/c_market_data/c_market_data.h +1464 -0
- algo_engine/base/c_market_data/c_market_data.pxd +418 -0
- algo_engine/base/c_market_data/c_market_data.pyi +550 -0
- algo_engine/base/c_market_data/c_market_data.pyx +688 -0
- algo_engine/base/c_market_data/c_market_data_buffer.c +29370 -0
- algo_engine/base/c_market_data/c_market_data_buffer.cp313-win_amd64.pyd +0 -0
- algo_engine/base/c_market_data/c_market_data_buffer.h +941 -0
- algo_engine/base/c_market_data/c_market_data_buffer.pxd +143 -0
- algo_engine/base/c_market_data/c_market_data_buffer.pyi +355 -0
- algo_engine/base/c_market_data/c_market_data_buffer.pyx +571 -0
- algo_engine/base/c_market_data/c_market_data_config.h +29 -0
- algo_engine/base/c_market_data/c_tick.c +44230 -0
- algo_engine/base/c_market_data/c_tick.cp313-win_amd64.pyd +0 -0
- algo_engine/base/c_market_data/c_tick.pxd +53 -0
- algo_engine/base/c_market_data/c_tick.pyi +466 -0
- algo_engine/base/c_market_data/c_tick.pyx +673 -0
- algo_engine/base/c_market_data/c_trade_utils.c +28665 -0
- algo_engine/base/c_market_data/c_trade_utils.cp313-win_amd64.pyd +0 -0
- algo_engine/base/c_market_data/c_trade_utils.pxd +53 -0
- algo_engine/base/c_market_data/c_trade_utils.pyi +602 -0
- algo_engine/base/c_market_data/c_trade_utils.pyx +609 -0
- algo_engine/base/c_market_data/c_transaction.c +23521 -0
- algo_engine/base/c_market_data/c_transaction.cp313-win_amd64.pyd +0 -0
- algo_engine/base/c_market_data/c_transaction.pxd +27 -0
- algo_engine/base/c_market_data/c_transaction.pyi +433 -0
- algo_engine/base/c_market_data/c_transaction.pyx +460 -0
- algo_engine/base/console_utils.py +1070 -0
- algo_engine/base/finance_decimal.py +258 -0
- algo_engine/base/telemetrics.py +104 -0
- algo_engine/engine/__infra__.pxd +10 -0
- algo_engine/engine/__init__.pxd +10 -0
- algo_engine/engine/__init__.py +40 -0
- algo_engine/engine/algo_engine.py +904 -0
- algo_engine/engine/c_event_engine.c +16289 -0
- algo_engine/engine/c_event_engine.cp313-win_amd64.pyd +0 -0
- algo_engine/engine/c_event_engine.pxd +25 -0
- algo_engine/engine/c_event_engine.pyi +68 -0
- algo_engine/engine/c_market_engine.c +24222 -0
- algo_engine/engine/c_market_engine.cp313-win_amd64.pyd +0 -0
- algo_engine/engine/c_market_engine.pxd +87 -0
- algo_engine/engine/c_market_engine.pyi +357 -0
- algo_engine/engine/event_engine.py +53 -0
- algo_engine/engine/trade_engine.py +2037 -0
- algo_engine/exchange_profile/__infra__.pxd +99 -0
- algo_engine/exchange_profile/__init__.pxd +99 -0
- algo_engine/exchange_profile/__init__.py +36 -0
- algo_engine/exchange_profile/c_ex_profile_base.c +87 -0
- algo_engine/exchange_profile/c_ex_profile_base.h +1148 -0
- algo_engine/exchange_profile/c_ex_profile_cn.c +968 -0
- algo_engine/exchange_profile/c_ex_profile_cn.h +39 -0
- algo_engine/exchange_profile/c_exchange_profile.c +52061 -0
- algo_engine/exchange_profile/c_exchange_profile.cp313-win_amd64.pyd +0 -0
- algo_engine/exchange_profile/c_exchange_profile.pxd +336 -0
- algo_engine/exchange_profile/c_exchange_profile.pyi +867 -0
- algo_engine/exchange_profile/c_exchange_profile.pyx +1482 -0
- algo_engine/exchange_profile/c_profile_cn.c +7779 -0
- algo_engine/exchange_profile/c_profile_cn.cp313-win_amd64.pyd +0 -0
- algo_engine/exchange_profile/c_profile_cn.pxd +1 -0
- algo_engine/exchange_profile/c_profile_cn.pyi +3 -0
- algo_engine/exchange_profile/c_profile_cn.pyx +1 -0
- algo_engine/exchange_profile/c_profile_default.c +7779 -0
- algo_engine/exchange_profile/c_profile_default.cp313-win_amd64.pyd +0 -0
- algo_engine/exchange_profile/c_profile_default.pxd +1 -0
- algo_engine/exchange_profile/c_profile_default.pyi +3 -0
- algo_engine/exchange_profile/c_profile_default.pyx +1 -0
- algo_engine/exchange_profile/c_profile_dispatcher.c +7779 -0
- algo_engine/exchange_profile/c_profile_dispatcher.cp313-win_amd64.pyd +0 -0
- algo_engine/exchange_profile/c_profile_dispatcher.pxd +1 -0
- algo_engine/exchange_profile/c_profile_dispatcher.pyi +3 -0
- algo_engine/exchange_profile/c_profile_dispatcher.pyx +1 -0
- algo_engine/includes/algo_engine/base/c_allocator_protocol.c +10756 -0
- algo_engine/includes/algo_engine/base/c_intern_string.c +5845 -0
- algo_engine/includes/algo_engine/base/c_market_data/c_candlestick.c +18960 -0
- algo_engine/includes/algo_engine/base/c_market_data/c_internal.c +14023 -0
- algo_engine/includes/algo_engine/base/c_market_data/c_market_data.c +30739 -0
- algo_engine/includes/algo_engine/base/c_market_data/c_market_data.h +1464 -0
- algo_engine/includes/algo_engine/base/c_market_data/c_market_data_buffer.c +29370 -0
- algo_engine/includes/algo_engine/base/c_market_data/c_market_data_buffer.h +941 -0
- algo_engine/includes/algo_engine/base/c_market_data/c_market_data_config.h +29 -0
- algo_engine/includes/algo_engine/base/c_market_data/c_tick.c +44230 -0
- algo_engine/includes/algo_engine/base/c_market_data/c_trade_utils.c +28665 -0
- algo_engine/includes/algo_engine/base/c_market_data/c_transaction.c +23521 -0
- algo_engine/includes/algo_engine/engine/c_event_engine.c +16289 -0
- algo_engine/includes/algo_engine/engine/c_market_engine.c +24222 -0
- algo_engine/includes/algo_engine/exchange_profile/c_ex_profile_base.c +87 -0
- algo_engine/includes/algo_engine/exchange_profile/c_ex_profile_base.h +1148 -0
- algo_engine/includes/algo_engine/exchange_profile/c_ex_profile_cn.c +968 -0
- algo_engine/includes/algo_engine/exchange_profile/c_ex_profile_cn.h +39 -0
- algo_engine/includes/algo_engine/exchange_profile/c_exchange_profile.c +52061 -0
- algo_engine/includes/algo_engine/exchange_profile/c_profile_cn.c +7779 -0
- algo_engine/includes/algo_engine/exchange_profile/c_profile_default.c +7779 -0
- algo_engine/includes/algo_engine/exchange_profile/c_profile_dispatcher.c +7779 -0
- algo_engine/monitor/__init__.py +15 -0
- algo_engine/monitor/advanced_data_interface.py +334 -0
- algo_engine/strategy/__init__.py +44 -0
- algo_engine/strategy/strategy_engine.py +441 -0
- algo_engine/utils/__init__.py +3 -0
- algo_engine/utils/commit_regularizer.py +49 -0
- algo_engine/utils/data_utils.py +296 -0
- pyalgoengine-0.12.0.post3.dist-info/METADATA +142 -0
- pyalgoengine-0.12.0.post3.dist-info/RECORD +156 -0
- pyalgoengine-0.12.0.post3.dist-info/WHEEL +5 -0
- pyalgoengine-0.12.0.post3.dist-info/licenses/LICENSE +21 -0
- 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
|
+
]
|