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.
Files changed (43) hide show
  1. PyAlgoEngine-0.7.4.dist-info/LICENSE +21 -0
  2. PyAlgoEngine-0.7.4.dist-info/METADATA +27 -0
  3. PyAlgoEngine-0.7.4.dist-info/RECORD +43 -0
  4. PyAlgoEngine-0.7.4.dist-info/WHEEL +5 -0
  5. PyAlgoEngine-0.7.4.dist-info/top_level.txt +1 -0
  6. algo_engine/__init__.py +41 -0
  7. algo_engine/apps/__init__.py +17 -0
  8. algo_engine/apps/backtest/__init__.py +20 -0
  9. algo_engine/apps/backtest/doc_server.py +331 -0
  10. algo_engine/apps/backtest/tester.py +254 -0
  11. algo_engine/apps/backtest/web_app.py +127 -0
  12. algo_engine/apps/bokeh_server.py +205 -0
  13. algo_engine/apps/demo/__init__.py +0 -0
  14. algo_engine/apps/demo/test.py +39 -0
  15. algo_engine/backtest/__init__.py +19 -0
  16. algo_engine/backtest/__main__.py +51 -0
  17. algo_engine/backtest/metrics.py +179 -0
  18. algo_engine/backtest/replay.py +261 -0
  19. algo_engine/backtest/sim_match.py +295 -0
  20. algo_engine/base/__init__.py +40 -0
  21. algo_engine/base/console_utils.py +1070 -0
  22. algo_engine/base/finance_decimal.py +258 -0
  23. algo_engine/base/market_buffer.py +571 -0
  24. algo_engine/base/market_utils.py +3092 -0
  25. algo_engine/base/market_utils_nt.py +188 -0
  26. algo_engine/base/market_utils_posix.py +3004 -0
  27. algo_engine/base/technical_analysis.py +406 -0
  28. algo_engine/base/telemetrics.py +78 -0
  29. algo_engine/base/trade_utils.py +709 -0
  30. algo_engine/engine/__init__.py +28 -0
  31. algo_engine/engine/algo_engine.py +901 -0
  32. algo_engine/engine/event_engine.py +53 -0
  33. algo_engine/engine/market_engine.py +370 -0
  34. algo_engine/engine/trade_engine.py +2037 -0
  35. algo_engine/monitor/__init__.py +15 -0
  36. algo_engine/monitor/advanced_data_interface.py +239 -0
  37. algo_engine/profile/__init__.py +121 -0
  38. algo_engine/profile/cn.py +175 -0
  39. algo_engine/strategy/__init__.py +44 -0
  40. algo_engine/strategy/strategy_engine.py +440 -0
  41. algo_engine/utils/__init__.py +3 -0
  42. algo_engine/utils/commit_regularizer.py +49 -0
  43. 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()