PyAlgoEngine 0.7.4__py3-none-any.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.
- PyAlgoEngine-0.7.4.dist-info/LICENSE +21 -0
- PyAlgoEngine-0.7.4.dist-info/METADATA +27 -0
- PyAlgoEngine-0.7.4.dist-info/RECORD +43 -0
- PyAlgoEngine-0.7.4.dist-info/WHEEL +5 -0
- PyAlgoEngine-0.7.4.dist-info/top_level.txt +1 -0
- algo_engine/__init__.py +41 -0
- algo_engine/apps/__init__.py +17 -0
- algo_engine/apps/backtest/__init__.py +20 -0
- algo_engine/apps/backtest/doc_server.py +331 -0
- algo_engine/apps/backtest/tester.py +254 -0
- algo_engine/apps/backtest/web_app.py +127 -0
- algo_engine/apps/bokeh_server.py +205 -0
- algo_engine/apps/demo/__init__.py +0 -0
- algo_engine/apps/demo/test.py +39 -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 +261 -0
- algo_engine/backtest/sim_match.py +295 -0
- algo_engine/base/__init__.py +40 -0
- algo_engine/base/console_utils.py +1070 -0
- algo_engine/base/finance_decimal.py +258 -0
- algo_engine/base/market_buffer.py +571 -0
- algo_engine/base/market_utils.py +3092 -0
- algo_engine/base/market_utils_nt.py +188 -0
- algo_engine/base/market_utils_posix.py +3004 -0
- algo_engine/base/technical_analysis.py +406 -0
- algo_engine/base/telemetrics.py +78 -0
- algo_engine/base/trade_utils.py +709 -0
- algo_engine/engine/__init__.py +28 -0
- algo_engine/engine/algo_engine.py +901 -0
- algo_engine/engine/event_engine.py +53 -0
- algo_engine/engine/market_engine.py +370 -0
- algo_engine/engine/trade_engine.py +2037 -0
- algo_engine/monitor/__init__.py +15 -0
- algo_engine/monitor/advanced_data_interface.py +239 -0
- algo_engine/profile/__init__.py +121 -0
- algo_engine/profile/cn.py +175 -0
- algo_engine/strategy/__init__.py +44 -0
- algo_engine/strategy/strategy_engine.py +440 -0
- algo_engine/utils/__init__.py +3 -0
- algo_engine/utils/commit_regularizer.py +49 -0
- algo_engine/utils/data_utils.py +251 -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()
|