onetick-py 1.162.2__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 +266 -0
  4. locator_parser/common.py +365 -0
  5. locator_parser/io.py +41 -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 +280 -0
  12. onetick/lib/__init__.py +4 -0
  13. onetick/lib/instance.py +138 -0
  14. onetick/py/__init__.py +290 -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 +645 -0
  19. onetick/py/aggregations/_docs.py +912 -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 +427 -0
  26. onetick/py/aggregations/other.py +1014 -0
  27. onetick/py/backports.py +26 -0
  28. onetick/py/cache.py +373 -0
  29. onetick/py/callback/__init__.py +5 -0
  30. onetick/py/callback/callback.py +275 -0
  31. onetick/py/callback/callbacks.py +131 -0
  32. onetick/py/compatibility.py +752 -0
  33. onetick/py/configuration.py +736 -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 +2307 -0
  45. onetick/py/core/_internal/_state_vars.py +87 -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 +810 -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 +270 -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 +1001 -0
  58. onetick/py/core/_source/source_methods/joins.py +1393 -0
  59. onetick/py/core/_source/source_methods/merges.py +566 -0
  60. onetick/py/core/_source/source_methods/misc.py +1325 -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 +702 -0
  68. onetick/py/core/_source/symbol.py +202 -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 +215 -0
  75. onetick/py/core/column_operations/_methods/methods.py +294 -0
  76. onetick/py/core/column_operations/_methods/op_types.py +150 -0
  77. onetick/py/core/column_operations/accessors/__init__.py +0 -0
  78. onetick/py/core/column_operations/accessors/_accessor.py +30 -0
  79. onetick/py/core/column_operations/accessors/decimal_accessor.py +92 -0
  80. onetick/py/core/column_operations/accessors/dt_accessor.py +464 -0
  81. onetick/py/core/column_operations/accessors/float_accessor.py +160 -0
  82. onetick/py/core/column_operations/accessors/str_accessor.py +1374 -0
  83. onetick/py/core/column_operations/base.py +1061 -0
  84. onetick/py/core/cut_builder.py +149 -0
  85. onetick/py/core/db_constants.py +20 -0
  86. onetick/py/core/eval_query.py +244 -0
  87. onetick/py/core/lambda_object.py +442 -0
  88. onetick/py/core/multi_output_source.py +193 -0
  89. onetick/py/core/per_tick_script.py +2253 -0
  90. onetick/py/core/query_inspector.py +465 -0
  91. onetick/py/core/source.py +1663 -0
  92. onetick/py/db/__init__.py +2 -0
  93. onetick/py/db/_inspection.py +1042 -0
  94. onetick/py/db/db.py +1423 -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 +2354 -0
  100. onetick/py/license.py +188 -0
  101. onetick/py/log.py +88 -0
  102. onetick/py/math.py +947 -0
  103. onetick/py/misc.py +437 -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 +211 -0
  108. onetick/py/pyomd_mock.py +47 -0
  109. onetick/py/run.py +841 -0
  110. onetick/py/servers.py +173 -0
  111. onetick/py/session.py +1342 -0
  112. onetick/py/sources/__init__.py +19 -0
  113. onetick/py/sources/cache.py +167 -0
  114. onetick/py/sources/common.py +126 -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 +1049 -0
  119. onetick/py/sources/empty.py +94 -0
  120. onetick/py/sources/odbc.py +337 -0
  121. onetick/py/sources/order_book.py +238 -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 +357 -0
  129. onetick/py/sources/ticks.py +825 -0
  130. onetick/py/sql.py +70 -0
  131. onetick/py/state.py +256 -0
  132. onetick/py/types.py +2056 -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 +499 -0
  141. onetick/py/utils/query.py +49 -0
  142. onetick/py/utils/render.py +1139 -0
  143. onetick/py/utils/script.py +244 -0
  144. onetick/py/utils/temp.py +471 -0
  145. onetick/py/utils/types.py +118 -0
  146. onetick/py/utils/tz.py +82 -0
  147. onetick_py-1.162.2.dist-info/METADATA +148 -0
  148. onetick_py-1.162.2.dist-info/RECORD +152 -0
  149. onetick_py-1.162.2.dist-info/WHEEL +5 -0
  150. onetick_py-1.162.2.dist-info/entry_points.txt +2 -0
  151. onetick_py-1.162.2.dist-info/licenses/LICENSE +21 -0
  152. onetick_py-1.162.2.dist-info/top_level.txt +2 -0
@@ -0,0 +1,215 @@
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 float_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): float_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
+ (str, float): str_to_float,
210
+ (str, ott.decimal): str_to_decimal,
211
+ (str, int): str_to_int,
212
+ (str, ott.nsectime): str_to_nsectime,
213
+ (str, ott.msectime): str_to_msectime,
214
+ (bool, int): bool_to_int,
215
+ (bool, float): bool_to_float})
@@ -0,0 +1,294 @@
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
+ if precision is not None:
18
+ return MethodResult(f'round_double({str(prev_op)},{str(precision)})', float)
19
+ else:
20
+ return MethodResult(f'round({str(prev_op)})', int)
21
+
22
+
23
+ def isin(prev_op, items):
24
+ if not items:
25
+ raise ValueError("Method isin() can't be used without values")
26
+ op_str = f'IN({str(prev_op)}, {",".join(value2str(i) for i in items)})'
27
+ return MethodResult(op_str, bool)
28
+
29
+
30
+ def _map(prev_op, items, values_type, default=None):
31
+ default_str = '' if default is None else f', {value2str(default)}'
32
+ op_str = (f'CASE({str(prev_op)}, {",".join(value2str(k) + "," + value2str(v) for k, v in items.items())}'
33
+ f'{default_str})')
34
+ return MethodResult(op_str, values_type)
35
+
36
+
37
+ def fillna(prev_op, value=0):
38
+ result = str(prev_op)
39
+
40
+ if not issubclass(prev_op.dtype, float):
41
+ raise TypeError(f"It is allowed to apply .fillna() is only columns that have 'float' type, "
42
+ f"but it is applied on a column with the {prev_op.dtype.__name__} type")
43
+ dtype = ott.get_object_type(value)
44
+ if not are_numerics(dtype):
45
+ raise TypeError(f"fillna expects numeric type, but got value of type '{type(value)}'")
46
+ op_str = f'replace_nan({result},{value2str(value)})'
47
+ return MethodResult(op_str, float)
48
+
49
+
50
+ def add(prev_op, other):
51
+ left, right, left_t, right_t, _, _ = _init_binary_op(prev_op, other)
52
+ result = _return_dateadd_command(prev_op, other, left, right, left_t, right_t, "")
53
+ # if it is DATEADD command return it, else try to form + operation
54
+ if result:
55
+ return result
56
+ return _plus(prev_op, other, left, right, left_t, right_t)
57
+
58
+
59
+ def sub(prev_op, other):
60
+ left, right, left_t, right_t, _, _ = _init_binary_op(prev_op, other)
61
+ result = _return_dateadd_command(prev_op, other, left, right, left_t, right_t, "-")
62
+ # if it is DATEADD command return it, else try to form - operation
63
+ if result:
64
+ return result
65
+ return _minus(prev_op, other, left, right, left_t, right_t)
66
+
67
+
68
+ def _return_dateadd_command(prev_op, other, left, right, left_t, right_t, op_sign):
69
+ if issubclass(left_t, ott.OTPBaseTimeOffset) and are_time(right_t):
70
+ return _form_method_result_for_dateadd(right, right_t, op_sign, prev_op)
71
+ elif issubclass(right_t, ott.OTPBaseTimeOffset) and are_time(left_t):
72
+ return _form_method_result_for_dateadd(left, left_t, op_sign, other)
73
+
74
+
75
+ def _form_method_result_for_dateadd(op_str, dtype, op_sign, datepart):
76
+ op_str = f"DATEADD({datepart.datepart}, {op_sign}({str(datepart.n)}), {op_str}, _TIMEZONE)"
77
+ return MethodResult(op_str, dtype)
78
+
79
+
80
+ def _plus(prev_op, other, left, right, left_t, right_t):
81
+ def get_str_len(op_or_str_const, dtype):
82
+ if not isinstance(op_or_str_const, str):
83
+ if dtype is str:
84
+ return ott.string.DEFAULT_LENGTH
85
+ else:
86
+ return dtype.length
87
+ elif isinstance(op_or_str_const, ott.varstring):
88
+ return Ellipsis
89
+ else:
90
+ return len(op_or_str_const) # ott.string as well as builtin strings have len operand
91
+
92
+ op_str = f"{left} + {right}"
93
+ if are_strings(left_t, right_t): # strings support concatenation
94
+ left_length, right_length = get_str_len(prev_op, left_t), get_str_len(other, right_t)
95
+ if left_length is Ellipsis or right_length is Ellipsis:
96
+ dtype = ott.varstring
97
+ else:
98
+ length = max(left_length, right_length)
99
+ dtype = str if length <= ott.string.DEFAULT_LENGTH else ott.string[length]
100
+ else:
101
+ dtype = _get_dtype_for_plus_or_minus(left_t, right_t)
102
+ if dtype:
103
+ return MethodResult(op_str, dtype)
104
+ else:
105
+ _type_error_for_op("+", f"'{left_t}' and '{right_t}'")
106
+
107
+
108
+ def _minus(prev_op, other, left, right, left_t, right_t): # noqa # NOSONAR
109
+ op_str = f"{left} - {right}"
110
+ # Between datetime (msectime and nsectime) types, only the - operator is allowed.
111
+ if are_time(left_t, right_t):
112
+ warnings.warn("Subtracting datetimes without specifying resulted time unit is deprecated. "
113
+ "Specify resulted time unit explicitly, ex: otp.Hour(a - b) or otp.Nano(a - b). "
114
+ "Difference in milliseconds will be returned now, but in future it will throw error. ",
115
+ DatetimeSubtractionWarning)
116
+ dtype = int
117
+ # TODO uncomment if we will decide to use nanosecond as default time unit
118
+ # op_str = f"DATEDIFF('nanosecond', {right}, {left}, _TIMEZONE)"
119
+ # return MethodResult(op_str, dtype)
120
+ else:
121
+ dtype = _get_dtype_for_plus_or_minus(left_t, right_t)
122
+ if dtype:
123
+ return MethodResult(op_str, dtype)
124
+ else:
125
+ _type_error_for_op("-", f"'{left_t}' and '{right_t}'")
126
+
127
+
128
+ def _get_dtype_for_plus_or_minus(left_t, right_t):
129
+ dtype = None
130
+ if are_numerics(left_t, right_t):
131
+ dtype = _get_widest_type(left_t, right_t)
132
+ # It is possible to add an integral value to datetime or subtract an integer from it,
133
+ # in this case the type is not changed
134
+ elif (are_ints_not_time(left_t) and issubclass(right_t, ott.nsectime)
135
+ or are_ints_not_time(right_t) and issubclass(left_t, ott.nsectime)):
136
+ dtype = ott.nsectime
137
+ elif (are_ints_not_time(left_t) and issubclass(right_t, ott.msectime)
138
+ or are_ints_not_time(right_t) and issubclass(left_t, ott.msectime)):
139
+ dtype = ott.msectime
140
+ # Any operation between floating-point and datetime types are not allowed in tick script,
141
+ # but supported in EP, so we also allow such operation, but generate warning.
142
+ elif (are_floats(left_t) and issubclass(right_t, ott.nsectime)
143
+ or are_floats(right_t) and issubclass(left_t, ott.nsectime)):
144
+ dtype = ott.nsectime
145
+ warnings.warn("Onetick will shrink the fractional part")
146
+ elif (are_floats(left_t) and issubclass(right_t, ott.msectime)
147
+ or are_floats(right_t) and issubclass(left_t, ott.msectime)):
148
+ dtype = ott.msectime
149
+ warnings.warn("Onetick will shrink the fractional part")
150
+ return dtype
151
+
152
+
153
+ def mul(prev_op, other):
154
+ left, right, left_t, right_t, op_str, dtype = _init_binary_op(prev_op, other)
155
+
156
+ if are_numerics(left_t, right_t):
157
+ dtype = _get_widest_type(left_t, right_t)
158
+ op_str = f"{left} * {right}"
159
+ elif issubclass(left_t, str) and are_ints_not_time(right_t):
160
+ op_str = f"repeat({left}, {right})"
161
+ dtype = left_t
162
+ elif issubclass(right_t, str) and are_ints_not_time(left_t):
163
+ op_str = f"repeat({right}, {left})"
164
+ dtype = right_t
165
+ if dtype and op_str:
166
+ return MethodResult(op_str, dtype)
167
+ else:
168
+ _type_error_for_op("*", f"'{left_t}' and '{right_t}'")
169
+
170
+
171
+ def div(prev_op, other):
172
+ left, right, left_t, right_t, op_str, dtype = _init_binary_op(prev_op, other)
173
+ if not are_numerics(left_t, right_t):
174
+ _type_error_for_op("/", f"'{left_t}' and '{right_t}'")
175
+ dtype = _get_widest_type(_get_widest_type(left_t, right_t), float)
176
+ op_str = f"{left} / {right}"
177
+ return MethodResult(op_str, dtype)
178
+
179
+
180
+ def mod(prev_op, other):
181
+ left, right, left_t, right_t, op_str, dtype = _init_binary_op(prev_op, other)
182
+ if not are_ints_not_time(left_t, right_t):
183
+ _type_error_for_op("mod", f"'{left_t}' and '{right_t}'")
184
+ dtype = int
185
+ op_str = f"mod({left}, {right})"
186
+ return MethodResult(op_str, dtype)
187
+
188
+
189
+ def abs(prev_op):
190
+ dtype = ott.get_object_type(prev_op)
191
+ if not are_numerics(dtype):
192
+ raise TypeError(f"Operation is not supported for type '{dtype}'")
193
+ op_str = f"abs{_wrap_object(prev_op)}"
194
+ return MethodResult(op_str, dtype)
195
+
196
+
197
+ def pos(prev_op):
198
+ dtype = ott.get_object_type(prev_op)
199
+ if not are_numerics(dtype):
200
+ raise TypeError(f"Operation is not supported for type '{dtype}'")
201
+ op_str = f"(+{_wrap_object(prev_op)})"
202
+ return MethodResult(op_str, dtype)
203
+
204
+
205
+ def neg(prev_op):
206
+ dtype = ott.get_object_type(prev_op)
207
+ if not are_numerics(dtype):
208
+ raise TypeError(f"Operation is not supported for type '{dtype}'")
209
+ op_str = f"(-{_wrap_object(prev_op)})"
210
+ return MethodResult(op_str, dtype)
211
+
212
+
213
+ def invert(prev_op):
214
+ dtype = ott.get_object_type(prev_op)
215
+ if not issubclass(dtype, bool):
216
+ raise TypeError(f"Operation is not supported for type '{dtype}'")
217
+ op_str = f"not({_wrap_object(prev_op)})"
218
+ return MethodResult(op_str, dtype)
219
+
220
+
221
+ def eq(prev_op, other):
222
+ return _compare(prev_op, other, "=", "==")
223
+
224
+
225
+ def ne(prev_op, other):
226
+ return _compare(prev_op, other, "!=")
227
+
228
+
229
+ def lt(prev_op, other):
230
+ return _compare(prev_op, other, "<")
231
+
232
+
233
+ def le(prev_op, other):
234
+ return _compare(prev_op, other, "<=")
235
+
236
+
237
+ def gt(prev_op, other):
238
+ return _compare(prev_op, other, ">")
239
+
240
+
241
+ def ge(prev_op, other):
242
+ return _compare(prev_op, other, ">=")
243
+
244
+
245
+ def _compare(prev_op, other, op_sign, op_sign_python_level=None):
246
+ def one_boolean_operation_is_allowed(prev_op, other, left_t, right_t):
247
+ return (isinstance(prev_op, bool) and issubclass(left_t, bool) or
248
+ isinstance(other, bool) and issubclass(right_t, bool))
249
+
250
+ def the_same_type(left_t, right_t):
251
+ # Operands of compare operator are expected to be both numeric, both booleans, both matrices or both strings
252
+ return (
253
+ not are_bools(left_t) and not are_bools(right_t)
254
+ and (
255
+ are_numerics(left_t, right_t) or are_strings(left_t, right_t)
256
+ or are_ints_or_time(left_t, right_t)
257
+ )
258
+ or are_bools(left_t, right_t)
259
+ )
260
+
261
+ left, right, left_t, right_t, op_str, _ = _init_binary_op(prev_op, other)
262
+ if one_boolean_operation_is_allowed(prev_op, other, left_t, right_t) or the_same_type(left_t, right_t):
263
+ op_str = f"{left} {op_sign} {right}"
264
+ return MethodResult(op_str, bool)
265
+ else:
266
+ # replace = with == for comparisions
267
+ _type_error_for_op(f"{op_sign_python_level or op_sign}", f"'{left_t}' and '{right_t}'")
268
+
269
+
270
+ def and_(prev_op, other):
271
+ return _and_or(prev_op, other, "AND")
272
+
273
+
274
+ def or_(prev_op, other):
275
+ return _and_or(prev_op, other, "OR")
276
+
277
+
278
+ def _and_or(prev_op, other, op_sign):
279
+ left, right, left_t, right_t, op_str, _ = _init_binary_op(prev_op, other)
280
+ if are_bools(left_t, right_t):
281
+ op_str = f"{left} {op_sign} {right}"
282
+ return MethodResult(op_str, prev_op.dtype)
283
+ else:
284
+ _type_error_for_op(f"{op_sign}", f"'{left_t}' and '{right_t}'")
285
+
286
+
287
+ def is_arithmetical(op):
288
+ op = getattr(op, "_op_func", None)
289
+ return op in {neg, pos, abs, add, sub, mul, div, mod}
290
+
291
+
292
+ def is_compare(op):
293
+ op = getattr(op, "_op_func", None)
294
+ return op in {invert, or_, and_, eq, ne, lt, le, gt, ge}
@@ -0,0 +1,150 @@
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(inspect.isclass(dtype)
12
+ and (issubclass(dtype, (float, int)) or np.issubdtype(dtype, np.integer))
13
+ and not issubclass(dtype, (ott.nsectime, ott.msectime)) for dtype in dtypes)
14
+
15
+
16
+ def are_ints_not_time(*dtypes):
17
+ return all(inspect.isclass(dtype)
18
+ and (issubclass(dtype, int) or np.issubdtype(dtype, np.integer))
19
+ and not issubclass(dtype, (ott.nsectime, ott.msectime)) for dtype in dtypes)
20
+
21
+
22
+ def are_time(*dtypes):
23
+ return all(inspect.isclass(dtype) and issubclass(dtype, (ott.nsectime, ott.msectime)) for dtype in dtypes)
24
+
25
+
26
+ def are_ints_or_time(*dtypes):
27
+ return all(inspect.isclass(dtype)
28
+ and (issubclass(dtype, (int, datetime.datetime, ott.datetime, pd.Timestamp))
29
+ or np.issubdtype(dtype, np.integer))
30
+ for dtype in dtypes)
31
+
32
+
33
+ def are_floats(*dtypes):
34
+ return all(inspect.isclass(dtype) and issubclass(dtype, (float, np.float64, np.double)) for dtype in dtypes)
35
+
36
+
37
+ def are_strings(*dtypes):
38
+ return all(inspect.isclass(dtype) and issubclass(dtype, str) for dtype in dtypes)
39
+
40
+
41
+ def are_bools(*dtypes):
42
+ return all(inspect.isclass(dtype) and issubclass(dtype, bool) for dtype in dtypes)
43
+
44
+
45
+ def _get_widest_type(left, right):
46
+ """ return the widest type
47
+
48
+ If the left is subclass of right return left, if right is subclass of left return right,
49
+ returns None in all other cases. ott.nsectime, ott.msectime and bool won't be considered as int, only
50
+ custom classes.
51
+
52
+ Parameters
53
+ ----------
54
+ left: type
55
+ type of the first argument
56
+ right: type
57
+ type of the second argument
58
+
59
+ Returns
60
+ -------
61
+ result: type or None
62
+ widest type if it exist, or None in another case
63
+
64
+ Examples
65
+ --------
66
+
67
+ >>> class MyInt(int): # OTdirective: skip-snippet:;
68
+ ... pass
69
+ >>> class MyFloat(float):
70
+ ... pass
71
+ >>> def test():
72
+ ... print(_get_widest_type(int, MyInt))
73
+ ... print(_get_widest_type(float, MyFloat))
74
+ ... print(_get_widest_type(int, float))
75
+ ... print(_get_widest_type(MyInt, MyFloat))
76
+ ... print(_get_widest_type(bool, float))
77
+ ... print(_get_widest_type(int, bool))
78
+ ... print(_get_widest_type(MyInt, bool))
79
+ ... print(_get_widest_type(bool, MyFloat))
80
+ >>> test() # doctest: +ELLIPSIS
81
+ <class '...MyInt'>
82
+ <class '...MyFloat'>
83
+ <class 'float'>
84
+ <class '...MyFloat'>
85
+ <class 'float'>
86
+ <class 'int'>
87
+ <class '...MyInt'>
88
+ <class '...MyFloat'>
89
+
90
+ >>> from onetick.py import types as ott # OTdirective: skip-snippet:;
91
+ >>> class MyString(str):
92
+ ... pass
93
+ >>> (_get_widest_type(str, MyString),
94
+ ... _get_widest_type(str, ott.string[15]),
95
+ ... _get_widest_type(MyString, ott.string[15])) # doctest: +ELLIPSIS
96
+ (<class 'onetick.py.core.column_operations._methods.op_types.MyString'>, string[15], None)
97
+
98
+ >>> from onetick.py import types as ott # OTdirective: skip-snippet:;
99
+ >>> _get_widest_type(int, ott.msectime), _get_widest_type(int, ott.nsectime)
100
+ (None, None)
101
+
102
+ >>> from onetick.py import types as ott # OTdirective: skip-snippet:;
103
+ >>> class MyTime(ott.OTPBaseTimeStamp):
104
+ ... pass
105
+ >>> class MyNSec(ott.nsectime):
106
+ ... pass
107
+ >>> (_get_widest_type(MyTime, ott.OTPBaseTimeStamp), _get_widest_type(MyTime, ott.nsectime),
108
+ ... _get_widest_type(MyNSec, ott.nsectime)) # doctest: +ELLIPSIS
109
+ (<class '...MyTime'>, None, <class '...MyNSec'>)
110
+ """
111
+
112
+ if issubclass(left, float) and issubclass(right, float):
113
+ # between np.float and float we choose base float
114
+ if left is not float and np.issubdtype(left, np.floating):
115
+ left = float
116
+ if right is not float and np.issubdtype(right, np.floating):
117
+ right = float
118
+
119
+ if issubclass(left, float):
120
+ if are_ints_not_time(right):
121
+ return left
122
+ else:
123
+ return _get_wideclass(left, right) if issubclass(right, float) else None
124
+ elif issubclass(right, float):
125
+ if are_ints_not_time(left):
126
+ return right
127
+ else:
128
+ return _get_wideclass(left, right) if issubclass(left, float) else None
129
+
130
+ if are_time(left) and are_ints_not_time(right) or are_time(right) and are_ints_not_time(left):
131
+ return None
132
+
133
+ if issubclass(right, int) and issubclass(left, bool):
134
+ return right
135
+ if issubclass(left, int) and issubclass(right, bool):
136
+ return left
137
+
138
+ if issubclass(right, int) and np.issubdtype(left, np.integer):
139
+ return right
140
+ if issubclass(left, int) and np.issubdtype(right, np.integer):
141
+ return left
142
+
143
+ return _get_wideclass(left, right)
144
+
145
+
146
+ def _get_wideclass(left, right):
147
+ if issubclass(left, right):
148
+ return left
149
+ if issubclass(right, left):
150
+ return right
@@ -0,0 +1,30 @@
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
+
15
+ def __init__(self, base_column, dtype, formatter):
16
+ super().__init__(dtype=dtype, op_params=[base_column])
17
+ self._formatter = formatter
18
+
19
+ def __str__(self):
20
+ return self._formatter(str(self._op_params[0]))
21
+
22
+ def _preprocess_tz_and_format(self,
23
+ timezone: typing.Union[Operation, str, None],
24
+ format_str): # it is common for str and dt accessors
25
+ if timezone is None or isinstance(timezone, str) and timezone == "_TIMEZONE":
26
+ timezone = "_TIMEZONE"
27
+ else:
28
+ timezone = value2str(timezone)
29
+ format_str = value2str(format_str)
30
+ return timezone, format_str