onetick-py 1.177.0__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 (152) hide show
  1. locator_parser/__init__.py +0 -0
  2. locator_parser/acl.py +73 -0
  3. locator_parser/actions.py +262 -0
  4. locator_parser/common.py +368 -0
  5. locator_parser/io.py +43 -0
  6. locator_parser/locator.py +150 -0
  7. onetick/__init__.py +101 -0
  8. onetick/doc_utilities/__init__.py +3 -0
  9. onetick/doc_utilities/napoleon.py +40 -0
  10. onetick/doc_utilities/ot_doctest.py +140 -0
  11. onetick/doc_utilities/snippets.py +279 -0
  12. onetick/lib/__init__.py +4 -0
  13. onetick/lib/instance.py +141 -0
  14. onetick/py/__init__.py +293 -0
  15. onetick/py/_stack_info.py +89 -0
  16. onetick/py/_version.py +2 -0
  17. onetick/py/aggregations/__init__.py +11 -0
  18. onetick/py/aggregations/_base.py +648 -0
  19. onetick/py/aggregations/_docs.py +948 -0
  20. onetick/py/aggregations/compute.py +286 -0
  21. onetick/py/aggregations/functions.py +2216 -0
  22. onetick/py/aggregations/generic.py +104 -0
  23. onetick/py/aggregations/high_low.py +80 -0
  24. onetick/py/aggregations/num_distinct.py +83 -0
  25. onetick/py/aggregations/order_book.py +501 -0
  26. onetick/py/aggregations/other.py +1014 -0
  27. onetick/py/backports.py +26 -0
  28. onetick/py/cache.py +374 -0
  29. onetick/py/callback/__init__.py +5 -0
  30. onetick/py/callback/callback.py +276 -0
  31. onetick/py/callback/callbacks.py +131 -0
  32. onetick/py/compatibility.py +798 -0
  33. onetick/py/configuration.py +771 -0
  34. onetick/py/core/__init__.py +0 -0
  35. onetick/py/core/_csv_inspector.py +93 -0
  36. onetick/py/core/_internal/__init__.py +0 -0
  37. onetick/py/core/_internal/_manually_bound_value.py +6 -0
  38. onetick/py/core/_internal/_nodes_history.py +250 -0
  39. onetick/py/core/_internal/_op_utils/__init__.py +0 -0
  40. onetick/py/core/_internal/_op_utils/every_operand.py +9 -0
  41. onetick/py/core/_internal/_op_utils/is_const.py +10 -0
  42. onetick/py/core/_internal/_per_tick_scripts/tick_list_sort_template.script +121 -0
  43. onetick/py/core/_internal/_proxy_node.py +140 -0
  44. onetick/py/core/_internal/_state_objects.py +2312 -0
  45. onetick/py/core/_internal/_state_vars.py +93 -0
  46. onetick/py/core/_source/__init__.py +0 -0
  47. onetick/py/core/_source/_symbol_param.py +95 -0
  48. onetick/py/core/_source/schema.py +97 -0
  49. onetick/py/core/_source/source_methods/__init__.py +0 -0
  50. onetick/py/core/_source/source_methods/aggregations.py +809 -0
  51. onetick/py/core/_source/source_methods/applyers.py +296 -0
  52. onetick/py/core/_source/source_methods/columns.py +141 -0
  53. onetick/py/core/_source/source_methods/data_quality.py +301 -0
  54. onetick/py/core/_source/source_methods/debugs.py +272 -0
  55. onetick/py/core/_source/source_methods/drops.py +120 -0
  56. onetick/py/core/_source/source_methods/fields.py +619 -0
  57. onetick/py/core/_source/source_methods/filters.py +1002 -0
  58. onetick/py/core/_source/source_methods/joins.py +1413 -0
  59. onetick/py/core/_source/source_methods/merges.py +605 -0
  60. onetick/py/core/_source/source_methods/misc.py +1455 -0
  61. onetick/py/core/_source/source_methods/pandases.py +155 -0
  62. onetick/py/core/_source/source_methods/renames.py +356 -0
  63. onetick/py/core/_source/source_methods/sorts.py +183 -0
  64. onetick/py/core/_source/source_methods/switches.py +142 -0
  65. onetick/py/core/_source/source_methods/symbols.py +117 -0
  66. onetick/py/core/_source/source_methods/times.py +627 -0
  67. onetick/py/core/_source/source_methods/writes.py +986 -0
  68. onetick/py/core/_source/symbol.py +205 -0
  69. onetick/py/core/_source/tmp_otq.py +222 -0
  70. onetick/py/core/column.py +209 -0
  71. onetick/py/core/column_operations/__init__.py +0 -0
  72. onetick/py/core/column_operations/_methods/__init__.py +4 -0
  73. onetick/py/core/column_operations/_methods/_internal.py +28 -0
  74. onetick/py/core/column_operations/_methods/conversions.py +216 -0
  75. onetick/py/core/column_operations/_methods/methods.py +292 -0
  76. onetick/py/core/column_operations/_methods/op_types.py +160 -0
  77. onetick/py/core/column_operations/accessors/__init__.py +0 -0
  78. onetick/py/core/column_operations/accessors/_accessor.py +28 -0
  79. onetick/py/core/column_operations/accessors/decimal_accessor.py +104 -0
  80. onetick/py/core/column_operations/accessors/dt_accessor.py +537 -0
  81. onetick/py/core/column_operations/accessors/float_accessor.py +184 -0
  82. onetick/py/core/column_operations/accessors/str_accessor.py +1367 -0
  83. onetick/py/core/column_operations/base.py +1121 -0
  84. onetick/py/core/cut_builder.py +150 -0
  85. onetick/py/core/db_constants.py +20 -0
  86. onetick/py/core/eval_query.py +245 -0
  87. onetick/py/core/lambda_object.py +441 -0
  88. onetick/py/core/multi_output_source.py +232 -0
  89. onetick/py/core/per_tick_script.py +2256 -0
  90. onetick/py/core/query_inspector.py +464 -0
  91. onetick/py/core/source.py +1744 -0
  92. onetick/py/db/__init__.py +2 -0
  93. onetick/py/db/_inspection.py +1128 -0
  94. onetick/py/db/db.py +1327 -0
  95. onetick/py/db/utils.py +64 -0
  96. onetick/py/docs/__init__.py +0 -0
  97. onetick/py/docs/docstring_parser.py +112 -0
  98. onetick/py/docs/utils.py +81 -0
  99. onetick/py/functions.py +2398 -0
  100. onetick/py/license.py +190 -0
  101. onetick/py/log.py +88 -0
  102. onetick/py/math.py +935 -0
  103. onetick/py/misc.py +470 -0
  104. onetick/py/oqd/__init__.py +22 -0
  105. onetick/py/oqd/eps.py +1195 -0
  106. onetick/py/oqd/sources.py +325 -0
  107. onetick/py/otq.py +216 -0
  108. onetick/py/pyomd_mock.py +47 -0
  109. onetick/py/run.py +916 -0
  110. onetick/py/servers.py +173 -0
  111. onetick/py/session.py +1347 -0
  112. onetick/py/sources/__init__.py +19 -0
  113. onetick/py/sources/cache.py +167 -0
  114. onetick/py/sources/common.py +128 -0
  115. onetick/py/sources/csv.py +642 -0
  116. onetick/py/sources/custom.py +85 -0
  117. onetick/py/sources/data_file.py +305 -0
  118. onetick/py/sources/data_source.py +1045 -0
  119. onetick/py/sources/empty.py +94 -0
  120. onetick/py/sources/odbc.py +337 -0
  121. onetick/py/sources/order_book.py +271 -0
  122. onetick/py/sources/parquet.py +168 -0
  123. onetick/py/sources/pit.py +191 -0
  124. onetick/py/sources/query.py +495 -0
  125. onetick/py/sources/snapshots.py +419 -0
  126. onetick/py/sources/split_query_output_by_symbol.py +198 -0
  127. onetick/py/sources/symbology_mapping.py +123 -0
  128. onetick/py/sources/symbols.py +374 -0
  129. onetick/py/sources/ticks.py +825 -0
  130. onetick/py/sql.py +70 -0
  131. onetick/py/state.py +251 -0
  132. onetick/py/types.py +2131 -0
  133. onetick/py/utils/__init__.py +70 -0
  134. onetick/py/utils/acl.py +93 -0
  135. onetick/py/utils/config.py +186 -0
  136. onetick/py/utils/default.py +49 -0
  137. onetick/py/utils/file.py +38 -0
  138. onetick/py/utils/helpers.py +76 -0
  139. onetick/py/utils/locator.py +94 -0
  140. onetick/py/utils/perf.py +498 -0
  141. onetick/py/utils/query.py +49 -0
  142. onetick/py/utils/render.py +1374 -0
  143. onetick/py/utils/script.py +244 -0
  144. onetick/py/utils/temp.py +471 -0
  145. onetick/py/utils/types.py +120 -0
  146. onetick/py/utils/tz.py +84 -0
  147. onetick_py-1.177.0.dist-info/METADATA +137 -0
  148. onetick_py-1.177.0.dist-info/RECORD +152 -0
  149. onetick_py-1.177.0.dist-info/WHEEL +5 -0
  150. onetick_py-1.177.0.dist-info/entry_points.txt +2 -0
  151. onetick_py-1.177.0.dist-info/licenses/LICENSE +21 -0
  152. onetick_py-1.177.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,216 @@
1
+ from collections import UserDict
2
+ from functools import partial
3
+
4
+ from onetick.py import types as ott
5
+ from onetick.py.core.column_operations._methods._internal import MethodResult
6
+ from onetick.py.core.column_operations._methods.op_types import are_ints_not_time
7
+
8
+ NSECTIME_TO_STR_FORMAT = "%Y-%m-%d %H:%M:%S.%J"
9
+ NSECTIME_TO_STR_FORMAT_2 = "%Y/%m/%d %H:%M:%S.%J"
10
+ MSECTIME_TO_STR_FORMAT = "%Y-%m-%d %H:%M:%S.%q"
11
+
12
+
13
+ def float_to_int(prev_op, dtype=int):
14
+ # CASE should be uppercased because it can be used in per-tick script
15
+ op_str = f"CASE({str(prev_op)} > 0, 1, floor({str(prev_op)}), ceil({str(prev_op)}))"
16
+ return MethodResult(op_str, dtype)
17
+
18
+
19
+ def float_to_str(prev_op, dtype=str):
20
+ precision = getattr(prev_op, "_precision", 7)
21
+
22
+ op_str = f"tostring({str(prev_op)}, 10, {precision})"
23
+
24
+ if precision == 0:
25
+ # otherwise 1.7 would be 1, but should be 1.0, because it is still float TODO: forbid it?
26
+ op_str = f"({op_str} + '.0')"
27
+ # if
28
+
29
+ # we need it to remove trailing zeros
30
+ op_str = fr'regex_replace({op_str}, "(\.\d+?)0+\b", "\1")'
31
+ return MethodResult(op_str, dtype)
32
+
33
+
34
+ def num_to_decimal(prev_op):
35
+ return MethodResult(f'decimal({str(prev_op)})', ott.decimal)
36
+
37
+
38
+ def decimal_to_str(prev_op, dtype=str):
39
+ precision = getattr(prev_op, "_precision", 8)
40
+ op_str = f"decimal_to_string({str(prev_op)}, {precision})"
41
+ return MethodResult(op_str, dtype)
42
+
43
+
44
+ def decimal_to_float(prev_op):
45
+ return MethodResult(f'{str(prev_op)}', float)
46
+
47
+
48
+ def nsectime_to_str(prev_op, dtype=str):
49
+ op_str = f"nsectime_format('{NSECTIME_TO_STR_FORMAT}', {str(prev_op)}, _TIMEZONE)"
50
+ return MethodResult(op_str, dtype)
51
+
52
+
53
+ def msectime_to_str(prev_op, dtype=str):
54
+ op_str = f"time_format('{MSECTIME_TO_STR_FORMAT}', {str(prev_op)}, _TIMEZONE)"
55
+ return MethodResult(op_str, dtype)
56
+
57
+
58
+ def nsectime_to_int(prev_op, dtype=int):
59
+ op_str = f"nsectime_to_long({str(prev_op)})"
60
+ return MethodResult(op_str, dtype)
61
+
62
+
63
+ def msectime_to_int(prev_op, dtype=int):
64
+ op_str = str(prev_op)
65
+ return MethodResult(op_str, dtype)
66
+
67
+
68
+ def nsectime_to_msectime(prev_op):
69
+ op_str = f"nsectime({str(prev_op)})"
70
+ return MethodResult(op_str, ott.msectime)
71
+
72
+
73
+ def msectime_to_nsectime(prev_op):
74
+ op_str = f"nsectime({str(prev_op)})"
75
+ return MethodResult(op_str, ott.nsectime)
76
+
77
+
78
+ def int_to_str(prev_op, dtype=str):
79
+ op_str = f"tostring({str(prev_op)})"
80
+ return MethodResult(op_str, dtype)
81
+
82
+
83
+ def int_to_float(prev_op):
84
+ op_str = f"{str(prev_op)} * 1.0"
85
+ return MethodResult(op_str, float)
86
+
87
+
88
+ def int_to_nsectime(prev_op):
89
+ op_str = f"nsectime({str(prev_op)})"
90
+ return MethodResult(op_str, ott.nsectime)
91
+
92
+
93
+ def int_to_msectime(prev_op):
94
+ op_str = str(prev_op)
95
+ return MethodResult(op_str, ott.msectime)
96
+
97
+
98
+ def str_to_float(prev_op):
99
+ op_str = f"atof({str(prev_op)})"
100
+ return MethodResult(op_str, float)
101
+
102
+
103
+ def str_to_decimal(prev_op):
104
+ op_str = f"string_to_decimal({str(prev_op)})"
105
+ return MethodResult(op_str, ott.decimal)
106
+
107
+
108
+ def str_to_int(prev_op, dtype=int):
109
+ op_str = f"atol({str(prev_op)})"
110
+ return MethodResult(op_str, dtype)
111
+
112
+
113
+ def str_to_nsectime(prev_op):
114
+ op_str = _str_to_time(prev_op)
115
+ return MethodResult(op_str, ott.nsectime)
116
+
117
+
118
+ def str_to_msectime(prev_op):
119
+ op_str = _str_to_time(prev_op)
120
+ return MethodResult(op_str, ott.msectime)
121
+
122
+
123
+ def _str_to_time(prev_op):
124
+ result = str(prev_op)
125
+
126
+ first_option = f"parse_nsectime('{NSECTIME_TO_STR_FORMAT}', {result}, _TIMEZONE)"
127
+ second_option = f"parse_nsectime('{NSECTIME_TO_STR_FORMAT_2}', {result}, _TIMEZONE)"
128
+
129
+ # CASE should be uppercased because it can be used in per-tick script
130
+ return f"CASE(position('-', {result}) > 0, 1, {first_option}, {second_option})"
131
+
132
+
133
+ def bool_to_int(prev_op, dtype=int):
134
+ op_str = f"CASE({str(prev_op)}, true, 1, 0)"
135
+ return MethodResult(op_str, dtype)
136
+
137
+
138
+ def bool_to_float(prev_op):
139
+ op_str = f"CASE({str(prev_op)}, true, 1.0, 0.0)"
140
+ return MethodResult(op_str, float)
141
+
142
+
143
+ def _same_to_same(prev_op, dtype=None):
144
+ if dtype is None:
145
+ dtype = prev_op.dtype
146
+ return MethodResult(str(prev_op), dtype)
147
+
148
+
149
+ class _ConversionsDict(UserDict):
150
+ def __init__(self, conversions):
151
+ self.data = conversions
152
+
153
+ def __getitem__(self, key):
154
+ if not (isinstance(key, tuple) and len(key) == 2):
155
+ raise ValueError("wrong usage of _ConversionsDict")
156
+
157
+ from_, to = key
158
+ if from_ == to:
159
+ return _same_to_same
160
+
161
+ try:
162
+ return super().__getitem__(key)
163
+ except KeyError:
164
+ pass
165
+
166
+ new_from, new_to = None, None
167
+
168
+ # int subclasses are converted by the same method
169
+ if are_ints_not_time(from_):
170
+ new_from = int
171
+ if are_ints_not_time(to):
172
+ new_to = int
173
+
174
+ # otp.string and str are converted by the same methods
175
+ if from_ is not str and issubclass(from_, str):
176
+ new_from = str
177
+ if to is not str and issubclass(to, str):
178
+ new_to = str
179
+
180
+ if new_from is not None or new_to is not None:
181
+ new_key = (new_from or from_, new_to or to)
182
+ if new_key[0] == new_key[1]:
183
+ method = _same_to_same
184
+ else:
185
+ method = super().__getitem__(new_key)
186
+ if issubclass(to, str) or are_ints_not_time(to):
187
+ method = partial(method, dtype=to)
188
+ return method
189
+
190
+ raise TypeError(f"can not convert {from_} to {to}")
191
+
192
+
193
+ CONVERSIONS = _ConversionsDict({(float, int): float_to_int,
194
+ (float, str): float_to_str,
195
+ (float, ott.decimal): num_to_decimal,
196
+ (ott.decimal, int): float_to_int,
197
+ (ott.decimal, str): decimal_to_str,
198
+ (ott.decimal, float): decimal_to_float,
199
+ (ott.nsectime, str): nsectime_to_str,
200
+ (ott.msectime, str): msectime_to_str,
201
+ (ott.nsectime, int): nsectime_to_int,
202
+ (ott.msectime, int): msectime_to_int,
203
+ (ott.nsectime, ott.msectime): nsectime_to_msectime,
204
+ (ott.msectime, ott.nsectime): msectime_to_nsectime,
205
+ (int, str): int_to_str,
206
+ (int, float): int_to_float,
207
+ (int, ott.nsectime): int_to_nsectime,
208
+ (int, ott.msectime): int_to_msectime,
209
+ (int, ott.decimal): num_to_decimal,
210
+ (str, float): str_to_float,
211
+ (str, ott.decimal): str_to_decimal,
212
+ (str, int): str_to_int,
213
+ (str, ott.nsectime): str_to_nsectime,
214
+ (str, ott.msectime): str_to_msectime,
215
+ (bool, int): bool_to_int,
216
+ (bool, float): bool_to_float})
@@ -0,0 +1,292 @@
1
+ import warnings
2
+
3
+ from onetick.py import types as ott
4
+ from onetick.py.types import value2str
5
+ from ._internal import MethodResult, _wrap_object, _type_error_for_op, _init_binary_op
6
+ from .op_types import (
7
+ are_numerics, are_strings, are_bools, are_time, are_ints_not_time, _get_widest_type,
8
+ are_floats, are_ints_or_time
9
+ )
10
+
11
+
12
+ class DatetimeSubtractionWarning(FutureWarning):
13
+ pass
14
+
15
+
16
+ def round(prev_op, precision):
17
+ return MethodResult(f'round_double({str(prev_op)},{str(precision)})', float)
18
+
19
+
20
+ def isin(prev_op, items):
21
+ if not items:
22
+ raise ValueError("Method isin() can't be used without values")
23
+ op_str = f'IN({str(prev_op)}, {",".join(value2str(i) for i in items)})'
24
+ return MethodResult(op_str, bool)
25
+
26
+
27
+ def _map(prev_op, items, values_type, default=None):
28
+ default_str = '' if default is None else f', {value2str(default)}'
29
+ op_str = (f'CASE({str(prev_op)}, {",".join(value2str(k) + "," + value2str(v) for k, v in items.items())}'
30
+ f'{default_str})')
31
+ return MethodResult(op_str, values_type)
32
+
33
+
34
+ def fillna(prev_op, value=0):
35
+ result = str(prev_op)
36
+
37
+ if not issubclass(prev_op.dtype, float):
38
+ raise TypeError(f"It is allowed to apply .fillna() is only columns that have 'float' type, "
39
+ f"but it is applied on a column with the {prev_op.dtype.__name__} type")
40
+ dtype = ott.get_object_type(value)
41
+ if not are_numerics(dtype):
42
+ raise TypeError(f"fillna expects numeric type, but got value of type '{type(value)}'")
43
+ op_str = f'replace_nan({result},{value2str(value)})'
44
+ return MethodResult(op_str, float)
45
+
46
+
47
+ def add(prev_op, other):
48
+ left, right, left_t, right_t, _, _ = _init_binary_op(prev_op, other)
49
+ result = _return_dateadd_command(prev_op, other, left, right, left_t, right_t, "")
50
+ # if it is DATEADD command return it, else try to form + operation
51
+ if result:
52
+ return result
53
+ return _plus(prev_op, other, left, right, left_t, right_t)
54
+
55
+
56
+ def sub(prev_op, other):
57
+ left, right, left_t, right_t, _, _ = _init_binary_op(prev_op, other)
58
+ result = _return_dateadd_command(prev_op, other, left, right, left_t, right_t, "-")
59
+ # if it is DATEADD command return it, else try to form - operation
60
+ if result:
61
+ return result
62
+ return _minus(prev_op, other, left, right, left_t, right_t)
63
+
64
+
65
+ def _return_dateadd_command(prev_op, other, left, right, left_t, right_t, op_sign):
66
+ if issubclass(left_t, ott.OTPBaseTimeOffset) and are_time(right_t):
67
+ return _form_method_result_for_dateadd(right, right_t, op_sign, prev_op)
68
+ elif issubclass(right_t, ott.OTPBaseTimeOffset) and are_time(left_t):
69
+ return _form_method_result_for_dateadd(left, left_t, op_sign, other)
70
+ return None
71
+
72
+
73
+ def _form_method_result_for_dateadd(op_str, dtype, op_sign, datepart):
74
+ op_str = f"DATEADD({datepart.datepart}, {op_sign}({str(datepart.n)}), {op_str}, _TIMEZONE)"
75
+ return MethodResult(op_str, dtype)
76
+
77
+
78
+ def _plus(prev_op, other, left, right, left_t, right_t):
79
+ def get_str_len(op_or_str_const, dtype):
80
+ if not isinstance(op_or_str_const, str):
81
+ if dtype is str:
82
+ return ott.string.DEFAULT_LENGTH
83
+ else:
84
+ return dtype.length
85
+ elif isinstance(op_or_str_const, ott.varstring):
86
+ return Ellipsis
87
+ else:
88
+ return len(op_or_str_const) # ott.string as well as builtin strings have len operand
89
+
90
+ op_str = f"{left} + {right}"
91
+ if are_strings(left_t, right_t): # strings support concatenation
92
+ left_length, right_length = get_str_len(prev_op, left_t), get_str_len(other, right_t)
93
+ if left_length is Ellipsis or right_length is Ellipsis:
94
+ dtype = ott.varstring
95
+ else:
96
+ length = max(left_length, right_length)
97
+ dtype = str if length <= ott.string.DEFAULT_LENGTH else ott.string[length]
98
+ else:
99
+ dtype = _get_dtype_for_plus_or_minus(left_t, right_t)
100
+ if dtype:
101
+ return MethodResult(op_str, dtype)
102
+ else:
103
+ raise _type_error_for_op("+", f"'{left_t}' and '{right_t}'")
104
+
105
+
106
+ def _minus(prev_op, other, left, right, left_t, right_t): # noqa # NOSONAR
107
+ op_str = f"{left} - {right}"
108
+ # Between datetime (msectime and nsectime) types, only the - operator is allowed.
109
+ if are_time(left_t, right_t):
110
+ warnings.warn("Subtracting datetimes without specifying resulted time unit is deprecated. "
111
+ "Specify resulted time unit explicitly, ex: otp.Hour(a - b) or otp.Nano(a - b). "
112
+ "Difference in milliseconds will be returned now, but in future it will throw error. ",
113
+ DatetimeSubtractionWarning)
114
+ dtype = int
115
+ # TODO uncomment if we will decide to use nanosecond as default time unit
116
+ # op_str = f"DATEDIFF('nanosecond', {right}, {left}, _TIMEZONE)"
117
+ # return MethodResult(op_str, dtype)
118
+ else:
119
+ dtype = _get_dtype_for_plus_or_minus(left_t, right_t)
120
+ if dtype:
121
+ return MethodResult(op_str, dtype)
122
+ else:
123
+ raise _type_error_for_op("-", f"'{left_t}' and '{right_t}'")
124
+
125
+
126
+ def _get_dtype_for_plus_or_minus(left_t, right_t):
127
+ dtype = None
128
+ if are_numerics(left_t, right_t):
129
+ dtype = _get_widest_type(left_t, right_t)
130
+ # It is possible to add an integral value to datetime or subtract an integer from it,
131
+ # in this case the type is not changed
132
+ elif (are_ints_not_time(left_t) and issubclass(right_t, ott.nsectime)
133
+ or are_ints_not_time(right_t) and issubclass(left_t, ott.nsectime)):
134
+ dtype = ott.nsectime
135
+ elif (are_ints_not_time(left_t) and issubclass(right_t, ott.msectime)
136
+ or are_ints_not_time(right_t) and issubclass(left_t, ott.msectime)):
137
+ dtype = ott.msectime
138
+ # Any operation between floating-point and datetime types are not allowed in tick script,
139
+ # but supported in EP, so we also allow such operation, but generate warning.
140
+ elif (are_floats(left_t) and issubclass(right_t, ott.nsectime)
141
+ or are_floats(right_t) and issubclass(left_t, ott.nsectime)):
142
+ dtype = ott.nsectime
143
+ warnings.warn("Onetick will shrink the fractional part")
144
+ elif (are_floats(left_t) and issubclass(right_t, ott.msectime)
145
+ or are_floats(right_t) and issubclass(left_t, ott.msectime)):
146
+ dtype = ott.msectime
147
+ warnings.warn("Onetick will shrink the fractional part")
148
+ return dtype
149
+
150
+
151
+ def mul(prev_op, other):
152
+ left, right, left_t, right_t, op_str, dtype = _init_binary_op(prev_op, other)
153
+
154
+ if are_numerics(left_t, right_t):
155
+ dtype = _get_widest_type(left_t, right_t)
156
+ op_str = f"{left} * {right}"
157
+ elif issubclass(left_t, str) and are_ints_not_time(right_t):
158
+ op_str = f"repeat({left}, {right})"
159
+ dtype = left_t
160
+ elif issubclass(right_t, str) and are_ints_not_time(left_t):
161
+ op_str = f"repeat({right}, {left})"
162
+ dtype = right_t
163
+ if dtype and op_str:
164
+ return MethodResult(op_str, dtype)
165
+ else:
166
+ raise _type_error_for_op("*", f"'{left_t}' and '{right_t}'")
167
+
168
+
169
+ def div(prev_op, other):
170
+ left, right, left_t, right_t, op_str, dtype = _init_binary_op(prev_op, other)
171
+ if not are_numerics(left_t, right_t):
172
+ raise _type_error_for_op("/", f"'{left_t}' and '{right_t}'")
173
+ dtype = _get_widest_type(_get_widest_type(left_t, right_t), float)
174
+ op_str = f"{left} / {right}"
175
+ return MethodResult(op_str, dtype)
176
+
177
+
178
+ def mod(prev_op, other):
179
+ left, right, left_t, right_t, op_str, dtype = _init_binary_op(prev_op, other)
180
+ if not are_ints_not_time(left_t, right_t):
181
+ raise _type_error_for_op("mod", f"'{left_t}' and '{right_t}'")
182
+ dtype = int
183
+ op_str = f"mod({left}, {right})"
184
+ return MethodResult(op_str, dtype)
185
+
186
+
187
+ def abs(prev_op):
188
+ dtype = ott.get_object_type(prev_op)
189
+ if not are_numerics(dtype):
190
+ raise TypeError(f"Operation is not supported for type '{dtype}'")
191
+ op_str = f"abs{_wrap_object(prev_op)}"
192
+ return MethodResult(op_str, dtype)
193
+
194
+
195
+ def pos(prev_op):
196
+ dtype = ott.get_object_type(prev_op)
197
+ if not are_numerics(dtype):
198
+ raise TypeError(f"Operation is not supported for type '{dtype}'")
199
+ op_str = f"(+{_wrap_object(prev_op)})"
200
+ return MethodResult(op_str, dtype)
201
+
202
+
203
+ def neg(prev_op):
204
+ dtype = ott.get_object_type(prev_op)
205
+ if not are_numerics(dtype):
206
+ raise TypeError(f"Operation is not supported for type '{dtype}'")
207
+ op_str = f"(-{_wrap_object(prev_op)})"
208
+ return MethodResult(op_str, dtype)
209
+
210
+
211
+ def invert(prev_op):
212
+ dtype = ott.get_object_type(prev_op)
213
+ if not issubclass(dtype, bool):
214
+ raise TypeError(f"Operation is not supported for type '{dtype}'")
215
+ op_str = f"not({_wrap_object(prev_op)})"
216
+ return MethodResult(op_str, dtype)
217
+
218
+
219
+ def eq(prev_op, other):
220
+ return _compare(prev_op, other, "=", "==")
221
+
222
+
223
+ def ne(prev_op, other):
224
+ return _compare(prev_op, other, "!=")
225
+
226
+
227
+ def lt(prev_op, other):
228
+ return _compare(prev_op, other, "<")
229
+
230
+
231
+ def le(prev_op, other):
232
+ return _compare(prev_op, other, "<=")
233
+
234
+
235
+ def gt(prev_op, other):
236
+ return _compare(prev_op, other, ">")
237
+
238
+
239
+ def ge(prev_op, other):
240
+ return _compare(prev_op, other, ">=")
241
+
242
+
243
+ def _compare(prev_op, other, op_sign, op_sign_python_level=None):
244
+ def one_boolean_operation_is_allowed(prev_op, other, left_t, right_t):
245
+ return (isinstance(prev_op, bool) and issubclass(left_t, bool) or
246
+ isinstance(other, bool) and issubclass(right_t, bool))
247
+
248
+ def the_same_type(left_t, right_t):
249
+ # Operands of compare operator are expected to be both numeric, both booleans, both matrices or both strings
250
+ return (
251
+ not are_bools(left_t) and not are_bools(right_t)
252
+ and (
253
+ are_numerics(left_t, right_t) or are_strings(left_t, right_t)
254
+ or are_ints_or_time(left_t, right_t)
255
+ )
256
+ or are_bools(left_t, right_t)
257
+ )
258
+
259
+ left, right, left_t, right_t, op_str, _ = _init_binary_op(prev_op, other)
260
+ if one_boolean_operation_is_allowed(prev_op, other, left_t, right_t) or the_same_type(left_t, right_t):
261
+ op_str = f"{left} {op_sign} {right}"
262
+ return MethodResult(op_str, bool)
263
+ else:
264
+ # replace = with == for comparisions
265
+ raise _type_error_for_op(f"{op_sign_python_level or op_sign}", f"'{left_t}' and '{right_t}'")
266
+
267
+
268
+ def and_(prev_op, other):
269
+ return _and_or(prev_op, other, "AND")
270
+
271
+
272
+ def or_(prev_op, other):
273
+ return _and_or(prev_op, other, "OR")
274
+
275
+
276
+ def _and_or(prev_op, other, op_sign):
277
+ left, right, left_t, right_t, op_str, _ = _init_binary_op(prev_op, other)
278
+ if are_bools(left_t, right_t):
279
+ op_str = f"{left} {op_sign} {right}"
280
+ return MethodResult(op_str, prev_op.dtype)
281
+ else:
282
+ raise _type_error_for_op(f"{op_sign}", f"'{left_t}' and '{right_t}'")
283
+
284
+
285
+ def is_arithmetical(op):
286
+ op = getattr(op, "_op_func", None)
287
+ return op in {neg, pos, abs, add, sub, mul, div, mod}
288
+
289
+
290
+ def is_compare(op):
291
+ op = getattr(op, "_op_func", None)
292
+ return op in {invert, or_, and_, eq, ne, lt, le, gt, ge}
@@ -0,0 +1,160 @@
1
+ import datetime
2
+ import inspect
3
+
4
+ import pandas as pd
5
+ import numpy as np
6
+
7
+ from onetick.py import types as ott
8
+
9
+
10
+ def are_numerics(*dtypes):
11
+ return all(
12
+ inspect.isclass(dtype)
13
+ and (issubclass(dtype, (float, int)) or np.issubdtype(dtype, np.integer) or issubclass(dtype, ott.decimal))
14
+ and not issubclass(dtype, (ott.nsectime, ott.msectime))
15
+ for dtype in dtypes
16
+ )
17
+
18
+
19
+ def are_ints_not_time(*dtypes):
20
+ return all(inspect.isclass(dtype)
21
+ and (issubclass(dtype, int) or np.issubdtype(dtype, np.integer))
22
+ and not issubclass(dtype, (ott.nsectime, ott.msectime)) for dtype in dtypes)
23
+
24
+
25
+ def are_time(*dtypes):
26
+ return all(inspect.isclass(dtype) and issubclass(dtype, (ott.nsectime, ott.msectime)) for dtype in dtypes)
27
+
28
+
29
+ def are_ints_or_time(*dtypes):
30
+ return all(inspect.isclass(dtype)
31
+ and (issubclass(dtype, (int, datetime.datetime, ott.datetime, pd.Timestamp))
32
+ or np.issubdtype(dtype, np.integer))
33
+ for dtype in dtypes)
34
+
35
+
36
+ def are_floats(*dtypes):
37
+ return all(inspect.isclass(dtype) and issubclass(dtype, (float, np.float64, np.double)) for dtype in dtypes)
38
+
39
+
40
+ def are_strings(*dtypes):
41
+ return all(inspect.isclass(dtype) and issubclass(dtype, str) for dtype in dtypes)
42
+
43
+
44
+ def are_bools(*dtypes):
45
+ return all(inspect.isclass(dtype) and issubclass(dtype, bool) for dtype in dtypes)
46
+
47
+
48
+ def _get_widest_type(left, right):
49
+ """ return the widest type
50
+
51
+ If the left is subclass of right return left, if right is subclass of left return right,
52
+ returns None in all other cases. ott.nsectime, ott.msectime and bool won't be considered as int, only
53
+ custom classes.
54
+
55
+ Parameters
56
+ ----------
57
+ left: type
58
+ type of the first argument
59
+ right: type
60
+ type of the second argument
61
+
62
+ Returns
63
+ -------
64
+ result: type or None
65
+ widest type if it exist, or None in another case
66
+
67
+ Examples
68
+ --------
69
+
70
+ >>> class MyInt(int): # OTdirective: skip-snippet:;
71
+ ... pass
72
+ >>> class MyFloat(float):
73
+ ... pass
74
+ >>> def test():
75
+ ... print(_get_widest_type(int, MyInt))
76
+ ... print(_get_widest_type(float, MyFloat))
77
+ ... print(_get_widest_type(int, float))
78
+ ... print(_get_widest_type(MyInt, MyFloat))
79
+ ... print(_get_widest_type(bool, float))
80
+ ... print(_get_widest_type(int, bool))
81
+ ... print(_get_widest_type(MyInt, bool))
82
+ ... print(_get_widest_type(bool, MyFloat))
83
+ >>> test() # doctest: +ELLIPSIS
84
+ <class '...MyInt'>
85
+ <class '...MyFloat'>
86
+ <class 'float'>
87
+ <class '...MyFloat'>
88
+ <class 'float'>
89
+ <class 'int'>
90
+ <class '...MyInt'>
91
+ <class '...MyFloat'>
92
+
93
+ >>> from onetick.py import types as ott # OTdirective: skip-snippet:;
94
+ >>> class MyString(str):
95
+ ... pass
96
+ >>> (_get_widest_type(str, MyString),
97
+ ... _get_widest_type(str, ott.string[15]),
98
+ ... _get_widest_type(MyString, ott.string[15])) # doctest: +ELLIPSIS
99
+ (<class 'onetick.py.core.column_operations._methods.op_types.MyString'>, string[15], None)
100
+
101
+ >>> from onetick.py import types as ott # OTdirective: skip-snippet:;
102
+ >>> _get_widest_type(int, ott.msectime), _get_widest_type(int, ott.nsectime)
103
+ (None, None)
104
+
105
+ >>> from onetick.py import types as ott # OTdirective: skip-snippet:;
106
+ >>> class MyTime(ott.OTPBaseTimeStamp):
107
+ ... pass
108
+ >>> class MyNSec(ott.nsectime):
109
+ ... pass
110
+ >>> (_get_widest_type(MyTime, ott.OTPBaseTimeStamp), _get_widest_type(MyTime, ott.nsectime),
111
+ ... _get_widest_type(MyNSec, ott.nsectime)) # doctest: +ELLIPSIS
112
+ (<class '...MyTime'>, None, <class '...MyNSec'>)
113
+ """
114
+
115
+ # decimal takes precedence before integer and floating point types
116
+ if issubclass(left, ott.decimal) and are_numerics(right):
117
+ return left
118
+ if are_numerics(left) and issubclass(right, ott.decimal):
119
+ return right
120
+
121
+ if issubclass(left, float) and issubclass(right, float):
122
+ # between np.float and float we choose base float
123
+ if left is not float and np.issubdtype(left, np.floating):
124
+ left = float
125
+ if right is not float and np.issubdtype(right, np.floating):
126
+ right = float
127
+
128
+ if issubclass(left, float):
129
+ if are_ints_not_time(right):
130
+ return left
131
+ else:
132
+ return _get_wideclass(left, right) if issubclass(right, float) else None
133
+ elif issubclass(right, float):
134
+ if are_ints_not_time(left):
135
+ return right
136
+ else:
137
+ return _get_wideclass(left, right) if issubclass(left, float) else None
138
+
139
+ if are_time(left) and are_ints_not_time(right) or are_time(right) and are_ints_not_time(left):
140
+ return None
141
+
142
+ if issubclass(right, int) and issubclass(left, bool):
143
+ return right
144
+ if issubclass(left, int) and issubclass(right, bool):
145
+ return left
146
+
147
+ if issubclass(right, int) and np.issubdtype(left, np.integer):
148
+ return right
149
+ if issubclass(left, int) and np.issubdtype(right, np.integer):
150
+ return left
151
+
152
+ return _get_wideclass(left, right)
153
+
154
+
155
+ def _get_wideclass(left, right):
156
+ if issubclass(left, right):
157
+ return left
158
+ if issubclass(right, left):
159
+ return right
160
+ return None
@@ -0,0 +1,28 @@
1
+ import typing
2
+
3
+ from onetick.py import Operation
4
+ from onetick.py.core.column_operations.base import _Operation
5
+ from onetick.py.types import value2str
6
+
7
+
8
+ class _Accessor:
9
+
10
+ def __init__(self, base_column):
11
+ self._base_column = base_column
12
+
13
+ class Formatter(_Operation):
14
+ def __init__(self, dtype, formatter, op_params):
15
+ def op_func(*args, **kwargs):
16
+ return formatter(*args, **kwargs), dtype
17
+
18
+ super().__init__(op_func=op_func, op_params=op_params, dtype=dtype)
19
+
20
+ def _preprocess_tz_and_format(self,
21
+ timezone: typing.Union[Operation, str, None],
22
+ format_str): # it is common for str and dt accessors
23
+ if timezone is None or isinstance(timezone, str) and timezone == "_TIMEZONE":
24
+ timezone = "_TIMEZONE"
25
+ else:
26
+ timezone = value2str(timezone)
27
+ format_str = value2str(format_str)
28
+ return timezone, format_str