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.
- locator_parser/__init__.py +0 -0
- locator_parser/acl.py +73 -0
- locator_parser/actions.py +262 -0
- locator_parser/common.py +368 -0
- locator_parser/io.py +43 -0
- locator_parser/locator.py +150 -0
- onetick/__init__.py +101 -0
- onetick/doc_utilities/__init__.py +3 -0
- onetick/doc_utilities/napoleon.py +40 -0
- onetick/doc_utilities/ot_doctest.py +140 -0
- onetick/doc_utilities/snippets.py +279 -0
- onetick/lib/__init__.py +4 -0
- onetick/lib/instance.py +141 -0
- onetick/py/__init__.py +293 -0
- onetick/py/_stack_info.py +89 -0
- onetick/py/_version.py +2 -0
- onetick/py/aggregations/__init__.py +11 -0
- onetick/py/aggregations/_base.py +648 -0
- onetick/py/aggregations/_docs.py +948 -0
- onetick/py/aggregations/compute.py +286 -0
- onetick/py/aggregations/functions.py +2216 -0
- onetick/py/aggregations/generic.py +104 -0
- onetick/py/aggregations/high_low.py +80 -0
- onetick/py/aggregations/num_distinct.py +83 -0
- onetick/py/aggregations/order_book.py +501 -0
- onetick/py/aggregations/other.py +1014 -0
- onetick/py/backports.py +26 -0
- onetick/py/cache.py +374 -0
- onetick/py/callback/__init__.py +5 -0
- onetick/py/callback/callback.py +276 -0
- onetick/py/callback/callbacks.py +131 -0
- onetick/py/compatibility.py +798 -0
- onetick/py/configuration.py +771 -0
- onetick/py/core/__init__.py +0 -0
- onetick/py/core/_csv_inspector.py +93 -0
- onetick/py/core/_internal/__init__.py +0 -0
- onetick/py/core/_internal/_manually_bound_value.py +6 -0
- onetick/py/core/_internal/_nodes_history.py +250 -0
- onetick/py/core/_internal/_op_utils/__init__.py +0 -0
- onetick/py/core/_internal/_op_utils/every_operand.py +9 -0
- onetick/py/core/_internal/_op_utils/is_const.py +10 -0
- onetick/py/core/_internal/_per_tick_scripts/tick_list_sort_template.script +121 -0
- onetick/py/core/_internal/_proxy_node.py +140 -0
- onetick/py/core/_internal/_state_objects.py +2312 -0
- onetick/py/core/_internal/_state_vars.py +93 -0
- onetick/py/core/_source/__init__.py +0 -0
- onetick/py/core/_source/_symbol_param.py +95 -0
- onetick/py/core/_source/schema.py +97 -0
- onetick/py/core/_source/source_methods/__init__.py +0 -0
- onetick/py/core/_source/source_methods/aggregations.py +809 -0
- onetick/py/core/_source/source_methods/applyers.py +296 -0
- onetick/py/core/_source/source_methods/columns.py +141 -0
- onetick/py/core/_source/source_methods/data_quality.py +301 -0
- onetick/py/core/_source/source_methods/debugs.py +272 -0
- onetick/py/core/_source/source_methods/drops.py +120 -0
- onetick/py/core/_source/source_methods/fields.py +619 -0
- onetick/py/core/_source/source_methods/filters.py +1002 -0
- onetick/py/core/_source/source_methods/joins.py +1413 -0
- onetick/py/core/_source/source_methods/merges.py +605 -0
- onetick/py/core/_source/source_methods/misc.py +1455 -0
- onetick/py/core/_source/source_methods/pandases.py +155 -0
- onetick/py/core/_source/source_methods/renames.py +356 -0
- onetick/py/core/_source/source_methods/sorts.py +183 -0
- onetick/py/core/_source/source_methods/switches.py +142 -0
- onetick/py/core/_source/source_methods/symbols.py +117 -0
- onetick/py/core/_source/source_methods/times.py +627 -0
- onetick/py/core/_source/source_methods/writes.py +986 -0
- onetick/py/core/_source/symbol.py +205 -0
- onetick/py/core/_source/tmp_otq.py +222 -0
- onetick/py/core/column.py +209 -0
- onetick/py/core/column_operations/__init__.py +0 -0
- onetick/py/core/column_operations/_methods/__init__.py +4 -0
- onetick/py/core/column_operations/_methods/_internal.py +28 -0
- onetick/py/core/column_operations/_methods/conversions.py +216 -0
- onetick/py/core/column_operations/_methods/methods.py +292 -0
- onetick/py/core/column_operations/_methods/op_types.py +160 -0
- onetick/py/core/column_operations/accessors/__init__.py +0 -0
- onetick/py/core/column_operations/accessors/_accessor.py +28 -0
- onetick/py/core/column_operations/accessors/decimal_accessor.py +104 -0
- onetick/py/core/column_operations/accessors/dt_accessor.py +537 -0
- onetick/py/core/column_operations/accessors/float_accessor.py +184 -0
- onetick/py/core/column_operations/accessors/str_accessor.py +1367 -0
- onetick/py/core/column_operations/base.py +1121 -0
- onetick/py/core/cut_builder.py +150 -0
- onetick/py/core/db_constants.py +20 -0
- onetick/py/core/eval_query.py +245 -0
- onetick/py/core/lambda_object.py +441 -0
- onetick/py/core/multi_output_source.py +232 -0
- onetick/py/core/per_tick_script.py +2256 -0
- onetick/py/core/query_inspector.py +464 -0
- onetick/py/core/source.py +1744 -0
- onetick/py/db/__init__.py +2 -0
- onetick/py/db/_inspection.py +1128 -0
- onetick/py/db/db.py +1327 -0
- onetick/py/db/utils.py +64 -0
- onetick/py/docs/__init__.py +0 -0
- onetick/py/docs/docstring_parser.py +112 -0
- onetick/py/docs/utils.py +81 -0
- onetick/py/functions.py +2398 -0
- onetick/py/license.py +190 -0
- onetick/py/log.py +88 -0
- onetick/py/math.py +935 -0
- onetick/py/misc.py +470 -0
- onetick/py/oqd/__init__.py +22 -0
- onetick/py/oqd/eps.py +1195 -0
- onetick/py/oqd/sources.py +325 -0
- onetick/py/otq.py +216 -0
- onetick/py/pyomd_mock.py +47 -0
- onetick/py/run.py +916 -0
- onetick/py/servers.py +173 -0
- onetick/py/session.py +1347 -0
- onetick/py/sources/__init__.py +19 -0
- onetick/py/sources/cache.py +167 -0
- onetick/py/sources/common.py +128 -0
- onetick/py/sources/csv.py +642 -0
- onetick/py/sources/custom.py +85 -0
- onetick/py/sources/data_file.py +305 -0
- onetick/py/sources/data_source.py +1045 -0
- onetick/py/sources/empty.py +94 -0
- onetick/py/sources/odbc.py +337 -0
- onetick/py/sources/order_book.py +271 -0
- onetick/py/sources/parquet.py +168 -0
- onetick/py/sources/pit.py +191 -0
- onetick/py/sources/query.py +495 -0
- onetick/py/sources/snapshots.py +419 -0
- onetick/py/sources/split_query_output_by_symbol.py +198 -0
- onetick/py/sources/symbology_mapping.py +123 -0
- onetick/py/sources/symbols.py +374 -0
- onetick/py/sources/ticks.py +825 -0
- onetick/py/sql.py +70 -0
- onetick/py/state.py +251 -0
- onetick/py/types.py +2131 -0
- onetick/py/utils/__init__.py +70 -0
- onetick/py/utils/acl.py +93 -0
- onetick/py/utils/config.py +186 -0
- onetick/py/utils/default.py +49 -0
- onetick/py/utils/file.py +38 -0
- onetick/py/utils/helpers.py +76 -0
- onetick/py/utils/locator.py +94 -0
- onetick/py/utils/perf.py +498 -0
- onetick/py/utils/query.py +49 -0
- onetick/py/utils/render.py +1374 -0
- onetick/py/utils/script.py +244 -0
- onetick/py/utils/temp.py +471 -0
- onetick/py/utils/types.py +120 -0
- onetick/py/utils/tz.py +84 -0
- onetick_py-1.177.0.dist-info/METADATA +137 -0
- onetick_py-1.177.0.dist-info/RECORD +152 -0
- onetick_py-1.177.0.dist-info/WHEEL +5 -0
- onetick_py-1.177.0.dist-info/entry_points.txt +2 -0
- onetick_py-1.177.0.dist-info/licenses/LICENSE +21 -0
- 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
|
|
File without changes
|
|
@@ -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
|