onetick-py 1.173.0__py3-none-any.whl → 1.175.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.
- onetick/py/_version.py +1 -1
- onetick/py/aggregations/_base.py +1 -1
- onetick/py/aggregations/order_book.py +13 -7
- onetick/py/aggregations/other.py +2 -2
- onetick/py/core/_source/source_methods/misc.py +59 -13
- onetick/py/core/column_operations/_methods/conversions.py +3 -2
- onetick/py/core/column_operations/_methods/op_types.py +12 -3
- onetick/py/core/column_operations/base.py +2 -2
- onetick/py/core/source.py +9 -1
- onetick/py/functions.py +3 -2
- onetick/py/types.py +115 -40
- onetick/py/utils/render.py +271 -58
- onetick/py/utils/types.py +2 -0
- {onetick_py-1.173.0.dist-info → onetick_py-1.175.0.dist-info}/METADATA +2 -2
- {onetick_py-1.173.0.dist-info → onetick_py-1.175.0.dist-info}/RECORD +19 -19
- {onetick_py-1.173.0.dist-info → onetick_py-1.175.0.dist-info}/WHEEL +0 -0
- {onetick_py-1.173.0.dist-info → onetick_py-1.175.0.dist-info}/entry_points.txt +0 -0
- {onetick_py-1.173.0.dist-info → onetick_py-1.175.0.dist-info}/licenses/LICENSE +0 -0
- {onetick_py-1.173.0.dist-info → onetick_py-1.175.0.dist-info}/top_level.txt +0 -0
onetick/py/_version.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# This file was generated automatically. DO NOT CHANGE.
|
|
2
|
-
VERSION = '1.
|
|
2
|
+
VERSION = '1.175.0'
|
onetick/py/aggregations/_base.py
CHANGED
|
@@ -547,7 +547,7 @@ class _AggregationTSSelection(_Aggregation):
|
|
|
547
547
|
|
|
548
548
|
class _FloatAggregation(_Aggregation):
|
|
549
549
|
|
|
550
|
-
require_type = (int, float, ott._inf)
|
|
550
|
+
require_type = (int, float, ott._inf, ott.decimal)
|
|
551
551
|
|
|
552
552
|
"""
|
|
553
553
|
Aggregation that expect int or float as input
|
|
@@ -229,6 +229,9 @@ class ObSnapshot(_OrderBookAggregation):
|
|
|
229
229
|
# we don't want to set hard limit on the output of order book aggregations
|
|
230
230
|
if self.show_full_detail:
|
|
231
231
|
kwargs['all_fields'] = True
|
|
232
|
+
self._size_type = int
|
|
233
|
+
if self.size_max_fractional_digits > 0:
|
|
234
|
+
self._size_type = float # type: ignore[assignment]
|
|
232
235
|
super().__init__(*args, **kwargs)
|
|
233
236
|
|
|
234
237
|
def _param_validation(self):
|
|
@@ -244,7 +247,7 @@ class ObSnapshot(_OrderBookAggregation):
|
|
|
244
247
|
def _get_output_schema(self, src: 'Source', name: Optional[str] = None) -> dict:
|
|
245
248
|
schema = {
|
|
246
249
|
'PRICE': float,
|
|
247
|
-
'SIZE':
|
|
250
|
+
'SIZE': self._size_type,
|
|
248
251
|
'LEVEL': int,
|
|
249
252
|
'UPDATE_TIME': otp.nsectime,
|
|
250
253
|
'BUY_SELL_FLAG': int,
|
|
@@ -265,10 +268,10 @@ class ObSnapshotWide(ObSnapshot):
|
|
|
265
268
|
def _get_output_schema(self, src: 'Source', name: Optional[str] = None) -> dict:
|
|
266
269
|
schema = {
|
|
267
270
|
'BID_PRICE': float,
|
|
268
|
-
'BID_SIZE':
|
|
271
|
+
'BID_SIZE': self._size_type,
|
|
269
272
|
'BID_UPDATE_TIME': otp.nsectime,
|
|
270
273
|
'ASK_PRICE': float,
|
|
271
|
-
'ASK_SIZE':
|
|
274
|
+
'ASK_SIZE': self._size_type,
|
|
272
275
|
'ASK_UPDATE_TIME': otp.nsectime,
|
|
273
276
|
'LEVEL': int,
|
|
274
277
|
}
|
|
@@ -299,10 +302,10 @@ class ObSnapshotFlat(ObSnapshot):
|
|
|
299
302
|
for level in range(1, self.max_levels + 1):
|
|
300
303
|
schema.update({
|
|
301
304
|
f'BID_PRICE{level}': float,
|
|
302
|
-
f'BID_SIZE{level}':
|
|
305
|
+
f'BID_SIZE{level}': self._size_type,
|
|
303
306
|
f'BID_UPDATE_TIME{level}': otp.nsectime,
|
|
304
307
|
f'ASK_PRICE{level}': float,
|
|
305
|
-
f'ASK_SIZE{level}':
|
|
308
|
+
f'ASK_SIZE{level}': self._size_type,
|
|
306
309
|
f'ASK_UPDATE_TIME{level}': otp.nsectime,
|
|
307
310
|
})
|
|
308
311
|
return schema
|
|
@@ -343,6 +346,9 @@ class ObSummary(_OrderBookAggregation):
|
|
|
343
346
|
self.state_key_max_inactivity_sec = state_key_max_inactivity_sec
|
|
344
347
|
self.size_max_fractional_digits = size_max_fractional_digits
|
|
345
348
|
self.include_market_order_ticks = include_market_order_ticks
|
|
349
|
+
self._size_type = int
|
|
350
|
+
if self.size_max_fractional_digits > 0:
|
|
351
|
+
self._size_type = float # type: ignore[assignment]
|
|
346
352
|
super().__init__(*args, **kwargs)
|
|
347
353
|
|
|
348
354
|
def _param_validation(self):
|
|
@@ -357,12 +363,12 @@ class ObSummary(_OrderBookAggregation):
|
|
|
357
363
|
|
|
358
364
|
def _get_output_schema(self, src: 'Source', name: Optional[str] = None) -> dict:
|
|
359
365
|
schema = {
|
|
360
|
-
'BID_SIZE':
|
|
366
|
+
'BID_SIZE': self._size_type,
|
|
361
367
|
'BID_VWAP': float,
|
|
362
368
|
'BEST_BID_PRICE': float,
|
|
363
369
|
'WORST_BID_PRICE': float,
|
|
364
370
|
'NUM_BID_LEVELS': int,
|
|
365
|
-
'ASK_SIZE':
|
|
371
|
+
'ASK_SIZE': self._size_type,
|
|
366
372
|
'ASK_VWAP': float,
|
|
367
373
|
'BEST_ASK_PRICE': float,
|
|
368
374
|
'WORST_ASK_PRICE': float,
|
onetick/py/aggregations/other.py
CHANGED
|
@@ -100,7 +100,7 @@ class Vwap(_Aggregation):
|
|
|
100
100
|
FIELDS_TO_SKIP: List = ['column_name']
|
|
101
101
|
|
|
102
102
|
output_field_type = float
|
|
103
|
-
require_type = (int, float, ott.nsectime)
|
|
103
|
+
require_type = (int, float, ott.nsectime, ott.decimal)
|
|
104
104
|
|
|
105
105
|
def __init__(self,
|
|
106
106
|
price_column: str,
|
|
@@ -271,7 +271,7 @@ class Average(_FloatAggregation):
|
|
|
271
271
|
class StdDev(_Aggregation): # Stddev does not support inf, so no need to use _FloatAggregation
|
|
272
272
|
NAME = "STDDEV"
|
|
273
273
|
EP = otq.Stddev
|
|
274
|
-
require_type = (int, float)
|
|
274
|
+
require_type = (int, float, ott.decimal)
|
|
275
275
|
output_field_type = float
|
|
276
276
|
FIELDS_MAPPING = deepcopy(_Aggregation.FIELDS_MAPPING)
|
|
277
277
|
FIELDS_MAPPING['biased'] = 'BIASED'
|
|
@@ -618,6 +618,9 @@ def pnl_realized(
|
|
|
618
618
|
The name of the field with price, default is **PRICE**.
|
|
619
619
|
buy_sell_flag_field: str, :py:class:`otp.Column <onetick.py.Column>`
|
|
620
620
|
The name of the field with buy/sell flag, default is **BUY_SELL_FLAG**.
|
|
621
|
+
If the type of this field is string, then possible values are 'B' or 'b' for buy and 'S' or 's' for sell.
|
|
622
|
+
If the type of this field is integer, then possible values are 0 for buy and 1 for sell.
|
|
623
|
+
|
|
621
624
|
|
|
622
625
|
See also
|
|
623
626
|
--------
|
|
@@ -625,20 +628,63 @@ def pnl_realized(
|
|
|
625
628
|
|
|
626
629
|
Examples
|
|
627
630
|
--------
|
|
631
|
+
Let's generate some data:
|
|
628
632
|
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
633
|
+
>>> trades = otp.Ticks(
|
|
634
|
+
... PRICE=[1.0, 2.0, 3.0, 2.5, 4.0, 5.0, 6.0, 7.0, 3.0, 4.0, 1.0],
|
|
635
|
+
... SIZE=[700, 20, 570, 600, 100, 100, 100, 100, 150, 10, 100],
|
|
636
|
+
... SELL_FLAG=[0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1],
|
|
637
|
+
... SIDE=['B', 'B', 'B', 'S', 'S', 'S', 'S', 'S', 'B', 'B', 'S'],
|
|
638
|
+
... )
|
|
639
|
+
>>> otp.run(trades)
|
|
640
|
+
Time PRICE SIZE SELL_FLAG SIDE
|
|
641
|
+
0 2003-12-01 00:00:00.000 1.0 700 0 B
|
|
642
|
+
1 2003-12-01 00:00:00.001 2.0 20 0 B
|
|
643
|
+
2 2003-12-01 00:00:00.002 3.0 570 0 B
|
|
644
|
+
3 2003-12-01 00:00:00.003 2.5 600 1 S
|
|
645
|
+
4 2003-12-01 00:00:00.004 4.0 100 1 S
|
|
646
|
+
5 2003-12-01 00:00:00.005 5.0 100 1 S
|
|
647
|
+
6 2003-12-01 00:00:00.006 6.0 100 1 S
|
|
648
|
+
7 2003-12-01 00:00:00.007 7.0 100 1 S
|
|
649
|
+
8 2003-12-01 00:00:00.008 3.0 150 0 B
|
|
650
|
+
9 2003-12-01 00:00:00.009 4.0 10 0 B
|
|
651
|
+
10 2003-12-01 00:00:00.010 1.0 100 1 S
|
|
652
|
+
|
|
653
|
+
And then calculate profit and loss metric for it.
|
|
654
|
+
|
|
655
|
+
First let's use string ``buy_sell_flag_field`` field:
|
|
656
|
+
|
|
657
|
+
>>> data = trades.pnl_realized(buy_sell_flag_field='SIDE') # doctest: +SKIP
|
|
658
|
+
>>> otp.run(data)[['Time', 'PRICE', 'SIZE', 'SIDE', 'PNL_REALIZED']] # doctest: +SKIP
|
|
659
|
+
Time PRICE SIZE SIDE PNL_REALIZED
|
|
660
|
+
0 2003-12-01 00:00:00.000 1.0 700 B 0.0
|
|
661
|
+
1 2003-12-01 00:00:00.001 2.0 20 B 0.0
|
|
662
|
+
2 2003-12-01 00:00:00.002 3.0 570 B 0.0
|
|
663
|
+
3 2003-12-01 00:00:00.003 2.5 600 S 900.0
|
|
664
|
+
4 2003-12-01 00:00:00.004 4.0 100 S 300.0
|
|
665
|
+
5 2003-12-01 00:00:00.005 5.0 100 S 220.0
|
|
666
|
+
6 2003-12-01 00:00:00.006 6.0 100 S 300.0
|
|
667
|
+
7 2003-12-01 00:00:00.007 7.0 100 S 400.0
|
|
668
|
+
8 2003-12-01 00:00:00.008 3.0 150 B 0.0
|
|
669
|
+
9 2003-12-01 00:00:00.009 4.0 10 B 0.0
|
|
670
|
+
10 2003-12-01 00:00:00.010 1.0 100 S -200.0
|
|
671
|
+
|
|
672
|
+
We can get the same result using integer ``buy_sell_flag_field`` field:
|
|
673
|
+
|
|
674
|
+
>>> data = trades.pnl_realized(buy_sell_flag_field='SELL_FLAG') # doctest: +SKIP
|
|
675
|
+
>>> otp.run(data)[['Time', 'PRICE', 'SIZE', 'SELL_FLAG', 'PNL_REALIZED']] # doctest: +SKIP
|
|
676
|
+
Time PRICE SIZE SELL_FLAG PNL_REALIZED
|
|
677
|
+
0 2003-12-01 00:00:00.000 1.0 700 0 0.0
|
|
678
|
+
1 2003-12-01 00:00:00.001 2.0 20 0 0.0
|
|
679
|
+
2 2003-12-01 00:00:00.002 3.0 570 0 0.0
|
|
680
|
+
3 2003-12-01 00:00:00.003 2.5 600 1 900.0
|
|
681
|
+
4 2003-12-01 00:00:00.004 4.0 100 1 300.0
|
|
682
|
+
5 2003-12-01 00:00:00.005 5.0 100 1 220.0
|
|
683
|
+
6 2003-12-01 00:00:00.006 6.0 100 1 300.0
|
|
684
|
+
7 2003-12-01 00:00:00.007 7.0 100 1 400.0
|
|
685
|
+
8 2003-12-01 00:00:00.008 3.0 150 0 0.0
|
|
686
|
+
9 2003-12-01 00:00:00.009 4.0 10 0 0.0
|
|
687
|
+
10 2003-12-01 00:00:00.010 1.0 100 1 -200.0
|
|
642
688
|
"""
|
|
643
689
|
if computation_method not in ['fifo']:
|
|
644
690
|
raise ValueError(
|
|
@@ -31,7 +31,7 @@ def float_to_str(prev_op, dtype=str):
|
|
|
31
31
|
return MethodResult(op_str, dtype)
|
|
32
32
|
|
|
33
33
|
|
|
34
|
-
def
|
|
34
|
+
def num_to_decimal(prev_op):
|
|
35
35
|
return MethodResult(f'decimal({str(prev_op)})', ott.decimal)
|
|
36
36
|
|
|
37
37
|
|
|
@@ -192,7 +192,7 @@ class _ConversionsDict(UserDict):
|
|
|
192
192
|
|
|
193
193
|
CONVERSIONS = _ConversionsDict({(float, int): float_to_int,
|
|
194
194
|
(float, str): float_to_str,
|
|
195
|
-
(float, ott.decimal):
|
|
195
|
+
(float, ott.decimal): num_to_decimal,
|
|
196
196
|
(ott.decimal, int): float_to_int,
|
|
197
197
|
(ott.decimal, str): decimal_to_str,
|
|
198
198
|
(ott.decimal, float): decimal_to_float,
|
|
@@ -206,6 +206,7 @@ CONVERSIONS = _ConversionsDict({(float, int): float_to_int,
|
|
|
206
206
|
(int, float): int_to_float,
|
|
207
207
|
(int, ott.nsectime): int_to_nsectime,
|
|
208
208
|
(int, ott.msectime): int_to_msectime,
|
|
209
|
+
(int, ott.decimal): num_to_decimal,
|
|
209
210
|
(str, float): str_to_float,
|
|
210
211
|
(str, ott.decimal): str_to_decimal,
|
|
211
212
|
(str, int): str_to_int,
|
|
@@ -8,9 +8,12 @@ from onetick.py import types as ott
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def are_numerics(*dtypes):
|
|
11
|
-
return all(
|
|
12
|
-
|
|
13
|
-
|
|
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
|
+
)
|
|
14
17
|
|
|
15
18
|
|
|
16
19
|
def are_ints_not_time(*dtypes):
|
|
@@ -109,6 +112,12 @@ def _get_widest_type(left, right):
|
|
|
109
112
|
(<class '...MyTime'>, None, <class '...MyNSec'>)
|
|
110
113
|
"""
|
|
111
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
|
+
|
|
112
121
|
if issubclass(left, float) and issubclass(right, float):
|
|
113
122
|
# between np.float and float we choose base float
|
|
114
123
|
if left is not float and np.issubdtype(left, np.floating):
|
|
@@ -476,7 +476,7 @@ class Operation:
|
|
|
476
476
|
Property that provides access to
|
|
477
477
|
methods specific to float type.
|
|
478
478
|
"""
|
|
479
|
-
if issubclass(self.dtype, float)
|
|
479
|
+
if issubclass(self.dtype, float):
|
|
480
480
|
from onetick.py.core.column_operations.accessors.float_accessor import _FloatAccessor
|
|
481
481
|
return _FloatAccessor(self)
|
|
482
482
|
else:
|
|
@@ -1043,7 +1043,7 @@ class Raw(Operation):
|
|
|
1043
1043
|
def __init__(self, raw, dtype):
|
|
1044
1044
|
if dtype is str:
|
|
1045
1045
|
warnings.warn(
|
|
1046
|
-
f'Be careful, default string length in OneTick is {ott.string.DEFAULT_LENGTH}.'
|
|
1046
|
+
f'Be careful, default string length in OneTick is {ott.string.DEFAULT_LENGTH}. '
|
|
1047
1047
|
"Length of the result raw expression can't be calculated automatically, "
|
|
1048
1048
|
"so you'd better use onetick.py.string type.",
|
|
1049
1049
|
stacklevel=2,
|
onetick/py/core/source.py
CHANGED
|
@@ -947,7 +947,9 @@ class Source:
|
|
|
947
947
|
view: bool = False,
|
|
948
948
|
line_limit: Optional[Tuple[int, int]] = (10, 30),
|
|
949
949
|
parse_eval_from_params: bool = False,
|
|
950
|
+
render_debug_info: bool = False,
|
|
950
951
|
debug: bool = False,
|
|
952
|
+
graphviz_compat_mode: bool = False,
|
|
951
953
|
**kwargs,
|
|
952
954
|
):
|
|
953
955
|
"""
|
|
@@ -973,8 +975,13 @@ class Source:
|
|
|
973
975
|
If one of tuple values set to zero the corresponding limit disabled.
|
|
974
976
|
parse_eval_from_params: bool
|
|
975
977
|
Enable parsing and printing `eval` sub-queries from EP parameters.
|
|
978
|
+
render_debug_info: bool
|
|
979
|
+
Render additional debug information.
|
|
976
980
|
debug: bool
|
|
977
981
|
Allow to print stdout or stderr from `Graphviz` render.
|
|
982
|
+
graphviz_compat_mode: bool
|
|
983
|
+
Change internal parameters of result graph for better compatibility with old `Graphviz` versions.
|
|
984
|
+
Could produce larger and less readable graphs.
|
|
978
985
|
kwargs:
|
|
979
986
|
Additional arguments to be passed to :py:meth:`onetick.py.Source.to_otq` method (except
|
|
980
987
|
``file_name``, ``file_suffix`` and ``query_name`` parameters)
|
|
@@ -1006,7 +1013,8 @@ class Source:
|
|
|
1006
1013
|
|
|
1007
1014
|
otq_path = self.to_otq(**kwargs)
|
|
1008
1015
|
return render_otq(
|
|
1009
|
-
otq_path, image_path, output_format, load_external_otqs, view, line_limit, parse_eval_from_params,
|
|
1016
|
+
otq_path, image_path, output_format, load_external_otqs, view, line_limit, parse_eval_from_params,
|
|
1017
|
+
render_debug_info, debug, graphviz_compat_mode,
|
|
1010
1018
|
)
|
|
1011
1019
|
|
|
1012
1020
|
def copy(self, ep=None, columns=None, deep=False) -> 'Source':
|
onetick/py/functions.py
CHANGED
|
@@ -327,6 +327,7 @@ def merge(sources, align_schema=True, symbols=None, identify_input_ts=False,
|
|
|
327
327
|
|
|
328
328
|
if enforce_order:
|
|
329
329
|
result.drop('OMDSEQ', inplace=True)
|
|
330
|
+
merged_columns.pop('OMDSEQ')
|
|
330
331
|
|
|
331
332
|
if identify_input_ts:
|
|
332
333
|
result.schema['SYMBOL_NAME' + added_field_name_suffix] = str
|
|
@@ -2030,7 +2031,7 @@ def _add_element(cur_res, element, format_spec_additional=None):
|
|
|
2030
2031
|
if isinstance(element, Operation):
|
|
2031
2032
|
if format_spec_additional is None:
|
|
2032
2033
|
cur_res += element.apply(str)
|
|
2033
|
-
elif issubclass(element.dtype, float) and re.fullmatch(r'\.\d+f', format_spec_additional):
|
|
2034
|
+
elif issubclass(element.dtype, (float, ott.decimal)) and re.fullmatch(r'\.\d+f', format_spec_additional):
|
|
2034
2035
|
# float has strange behavior when precision=0
|
|
2035
2036
|
decimal_elem = element.apply(ott.decimal)
|
|
2036
2037
|
precision_str = re.findall(r'\d+', format_spec_additional)[0]
|
|
@@ -2047,7 +2048,7 @@ def _add_element(cur_res, element, format_spec_additional=None):
|
|
|
2047
2048
|
else:
|
|
2048
2049
|
if format_spec_additional is None:
|
|
2049
2050
|
cur_res += str(element)
|
|
2050
|
-
elif isinstance(element, float):
|
|
2051
|
+
elif isinstance(element, (float, ott.decimal)):
|
|
2051
2052
|
formatting = f'{{:{format_spec_additional}}}'
|
|
2052
2053
|
cur_res += formatting.format(element)
|
|
2053
2054
|
else:
|
onetick/py/types.py
CHANGED
|
@@ -2,6 +2,7 @@ import ctypes
|
|
|
2
2
|
import functools
|
|
3
3
|
import inspect
|
|
4
4
|
import warnings
|
|
5
|
+
import decimal as _decimal
|
|
5
6
|
from typing import Optional, Type, Union
|
|
6
7
|
from datetime import date as _date
|
|
7
8
|
from datetime import datetime as _datetime
|
|
@@ -707,18 +708,23 @@ class _inf(float, metaclass=_nan_base):
|
|
|
707
708
|
inf = _inf()
|
|
708
709
|
|
|
709
710
|
|
|
710
|
-
class
|
|
711
|
-
def __str__(cls):
|
|
712
|
-
return 'decimal'
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
class decimal(float, metaclass=_decimal_str):
|
|
711
|
+
class decimal:
|
|
716
712
|
"""
|
|
717
713
|
Object that represents decimal OneTick value.
|
|
718
714
|
Decimal is 128 bit base 10 floating point number.
|
|
719
715
|
|
|
716
|
+
Parameters
|
|
717
|
+
----------
|
|
718
|
+
value: int, float, str
|
|
719
|
+
The value to initialize decimal from.
|
|
720
|
+
Note that float values may be converted with precision lost.
|
|
721
|
+
|
|
720
722
|
Examples
|
|
721
723
|
--------
|
|
724
|
+
|
|
725
|
+
:py:class:`~onetick.py.types.decimal` objects can be used in tick generators
|
|
726
|
+
and column operations as any other onetick-py type:
|
|
727
|
+
|
|
722
728
|
>>> t = otp.Ticks({'A': [otp.decimal(1), otp.decimal(2)]})
|
|
723
729
|
>>> t['B'] = otp.decimal(1.23456789)
|
|
724
730
|
>>> t['C'] = t['A'] / 0
|
|
@@ -727,43 +733,107 @@ class decimal(float, metaclass=_decimal_str):
|
|
|
727
733
|
Time A B C D
|
|
728
734
|
0 2003-12-01 00:00:00.000 1.0 1.234568 inf NaN
|
|
729
735
|
1 2003-12-01 00:00:00.001 2.0 1.234568 inf NaN
|
|
730
|
-
"""
|
|
731
|
-
def __wrap(self, res):
|
|
732
|
-
# if parent class doesn't support some operation, it returns NotImplemented and so do we
|
|
733
|
-
# In other case we wrap float result with our decimal class
|
|
734
|
-
if isinstance(res, type(NotImplemented)):
|
|
735
|
-
return NotImplemented
|
|
736
|
-
return self.__class__(res)
|
|
737
|
-
|
|
738
|
-
def __add__(self, other):
|
|
739
|
-
return self.__wrap(super().__add__(other))
|
|
740
736
|
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
def __sub__(self, other):
|
|
745
|
-
return self.__wrap(super().__sub__(other))
|
|
737
|
+
Additionally, any arithmetic operation with :py:class:`~onetick.py.types.decimal` object will return
|
|
738
|
+
an :py:class:`~onetick.py.Operation` object:
|
|
746
739
|
|
|
747
|
-
|
|
748
|
-
|
|
740
|
+
>>> t = otp.Tick(A=1)
|
|
741
|
+
>>> t['X'] = otp.decimal(1) / 0
|
|
742
|
+
>>> otp.run(t)
|
|
743
|
+
Time A X
|
|
744
|
+
0 2003-12-01 1 inf
|
|
745
|
+
|
|
746
|
+
Note that converting from float (first row) may result in losing precision.
|
|
747
|
+
:py:class:`~onetick.py.types.decimal` objects are created from strings or integers, so they don't lose precision:
|
|
748
|
+
|
|
749
|
+
>>> t0 = otp.Tick(A=0.1)
|
|
750
|
+
>>> t1 = otp.Tick(A=otp.decimal(0.01))
|
|
751
|
+
>>> t2 = otp.Tick(A=otp.decimal('0.001'))
|
|
752
|
+
>>> t3 = otp.Tick(A=otp.decimal(1) / otp.decimal(10_000))
|
|
753
|
+
>>> t = otp.merge([t0, t1, t2, t3], enforce_order=True)
|
|
754
|
+
>>> t['STR_A'] = t['A'].decimal.str(34)
|
|
755
|
+
>>> otp.run(t)
|
|
756
|
+
Time A STR_A
|
|
757
|
+
0 2003-12-01 0.1000 0.1000000000000000055511151231257827
|
|
758
|
+
1 2003-12-01 0.0100 0.0100000000000000000000000000000000
|
|
759
|
+
2 2003-12-01 0.0010 0.0010000000000000000000000000000000
|
|
760
|
+
3 2003-12-01 0.0001 0.0001000000000000000000000000000000
|
|
749
761
|
|
|
750
|
-
|
|
751
|
-
|
|
762
|
+
Note that :py:class:`otp.Ticks <onetick.py.Ticks>` will convert everything from string under the hood,
|
|
763
|
+
so even the float values will not lose precision:
|
|
752
764
|
|
|
753
|
-
|
|
754
|
-
|
|
765
|
+
>>> t = otp.Ticks({'A': [0.1, otp.decimal(0.01), otp.decimal('0.001'), otp.decimal(1e-4)]})
|
|
766
|
+
>>> t['STR_A'] = t['A'].decimal.str(34)
|
|
767
|
+
>>> otp.run(t)
|
|
768
|
+
Time A STR_A
|
|
769
|
+
0 2003-12-01 00:00:00.000 0.1000 0.1000000000000000000000000000000000
|
|
770
|
+
1 2003-12-01 00:00:00.001 0.0100 0.0100000000000000000000000000000000
|
|
771
|
+
2 2003-12-01 00:00:00.002 0.0010 0.0010000000000000000000000000000000
|
|
772
|
+
3 2003-12-01 00:00:00.003 0.0001 0.0001000000000000000000000000000000
|
|
773
|
+
"""
|
|
774
|
+
def __new__(cls, *args, **kwargs):
|
|
775
|
+
# this method dynamically adds properties and methods
|
|
776
|
+
# from otp.Operation class to this one
|
|
777
|
+
|
|
778
|
+
# otp.decimal class doesn't fit well in onetick-py type system,
|
|
779
|
+
# so this class is a mix of both type and Operation logic
|
|
780
|
+
|
|
781
|
+
# Basically it works like this:
|
|
782
|
+
# otp.decimal is a OneTick type
|
|
783
|
+
# otp.decimal(1) is a decimal type object
|
|
784
|
+
# Doing anything with this object returns an otp.Operation:
|
|
785
|
+
# otp.decimal(1) / 2
|
|
786
|
+
|
|
787
|
+
def proxy_wrap(attr, value):
|
|
788
|
+
if callable(value):
|
|
789
|
+
@functools.wraps(value)
|
|
790
|
+
def f(self, *args, **kwargs):
|
|
791
|
+
op = self.to_operation()
|
|
792
|
+
return getattr(op, attr)(*args, **kwargs)
|
|
793
|
+
return f
|
|
794
|
+
else:
|
|
795
|
+
@functools.wraps(value)
|
|
796
|
+
def f(self):
|
|
797
|
+
op = self.to_operation()
|
|
798
|
+
return getattr(op, attr)
|
|
799
|
+
return property(f)
|
|
800
|
+
|
|
801
|
+
for attr, value in inspect.getmembers(otp.Operation):
|
|
802
|
+
# comparison methods are defined by default for some reason,
|
|
803
|
+
# but we want to get them from otp.Operation
|
|
804
|
+
if not hasattr(cls, attr) or attr in ('__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__'):
|
|
805
|
+
setattr(cls, attr, proxy_wrap(attr, value))
|
|
806
|
+
|
|
807
|
+
return super().__new__(cls)
|
|
808
|
+
|
|
809
|
+
def __init__(self, value):
|
|
810
|
+
supported_types = (str, int, float)
|
|
811
|
+
if not isinstance(value, supported_types):
|
|
812
|
+
raise TypeError("Parameter 'value' must be one of these types: {supported_types}")
|
|
813
|
+
self.__value = value
|
|
814
|
+
|
|
815
|
+
@classmethod
|
|
816
|
+
def _to_onetick_type_string(cls):
|
|
817
|
+
# called by ott.type2str
|
|
818
|
+
return 'decimal'
|
|
755
819
|
|
|
756
|
-
def
|
|
757
|
-
|
|
820
|
+
def _to_onetick_string(self):
|
|
821
|
+
# called by ott.value2str
|
|
822
|
+
value = str(self.__value)
|
|
823
|
+
return f'STRING_TO_DECIMAL({value2str(value)})'
|
|
758
824
|
|
|
759
|
-
def
|
|
760
|
-
return
|
|
825
|
+
def to_operation(self):
|
|
826
|
+
return otp.Operation(op_str=self._to_onetick_string(), dtype=decimal)
|
|
761
827
|
|
|
762
828
|
def __str__(self):
|
|
763
|
-
|
|
829
|
+
# called by otp.CSV, we don't need to convert the value with OneTick functions in this case
|
|
830
|
+
return str(self.__value)
|
|
764
831
|
|
|
765
832
|
def __repr__(self):
|
|
766
|
-
return f"{self.__class__.__name__}({self})"
|
|
833
|
+
return f"{self.__class__.__name__}({value2str(self.__value)})"
|
|
834
|
+
|
|
835
|
+
def __format__(self, __format_spec: str) -> str:
|
|
836
|
+
return _decimal.Decimal(self.__value).__format__(__format_spec)
|
|
767
837
|
|
|
768
838
|
# --------------------------------------------------------------- #
|
|
769
839
|
# AUXILIARY FUNCTIONS
|
|
@@ -809,7 +879,7 @@ def get_source_base_type(value):
|
|
|
809
879
|
value_type = nsectime
|
|
810
880
|
|
|
811
881
|
# check valid value type
|
|
812
|
-
if get_base_type(value_type) not in [int, float, str, bool]:
|
|
882
|
+
if get_base_type(value_type) not in [int, float, str, bool, decimal]:
|
|
813
883
|
raise TypeError(f'Type "{repr(value_type)}" is not supported.')
|
|
814
884
|
|
|
815
885
|
if not is_type_basic(value_type):
|
|
@@ -818,7 +888,7 @@ def get_source_base_type(value):
|
|
|
818
888
|
|
|
819
889
|
|
|
820
890
|
def is_type_supported(dtype):
|
|
821
|
-
return get_base_type(dtype) in [int, float, str, bool] or issubclass(dtype, (datetime, date))
|
|
891
|
+
return get_base_type(dtype) in [int, float, str, bool, decimal] or issubclass(dtype, (datetime, date))
|
|
822
892
|
|
|
823
893
|
|
|
824
894
|
def get_base_type(obj):
|
|
@@ -830,6 +900,8 @@ def get_base_type(obj):
|
|
|
830
900
|
return int
|
|
831
901
|
elif issubclass(obj, float):
|
|
832
902
|
return float
|
|
903
|
+
elif issubclass(obj, decimal):
|
|
904
|
+
return decimal
|
|
833
905
|
|
|
834
906
|
return type(None)
|
|
835
907
|
|
|
@@ -1686,6 +1758,8 @@ def type2str(t):
|
|
|
1686
1758
|
return "double"
|
|
1687
1759
|
if t is None:
|
|
1688
1760
|
return ''
|
|
1761
|
+
if t is decimal:
|
|
1762
|
+
return t._to_onetick_type_string()
|
|
1689
1763
|
return str(t)
|
|
1690
1764
|
|
|
1691
1765
|
|
|
@@ -1841,15 +1915,16 @@ def value2str(v):
|
|
|
1841
1915
|
# there is no escape, so replacing double quotes with concatenation with it
|
|
1842
1916
|
return '"' + str(v).replace('"', '''"+'"'+"''') + '"'
|
|
1843
1917
|
|
|
1844
|
-
if isinstance(v,
|
|
1918
|
+
if isinstance(v, decimal):
|
|
1919
|
+
return v._to_onetick_string()
|
|
1920
|
+
|
|
1921
|
+
if isinstance(v, float) and not (isinstance(v, (_inf, _nan))):
|
|
1845
1922
|
# PY-286: support science notation
|
|
1846
1923
|
s = str(v)
|
|
1847
1924
|
if "e" in s:
|
|
1848
|
-
|
|
1925
|
+
return f'atof({value2str(s)})'
|
|
1849
1926
|
if s == "nan":
|
|
1850
1927
|
return str(nan)
|
|
1851
|
-
if isinstance(v, decimal):
|
|
1852
|
-
return f'DECIMAL({s})'
|
|
1853
1928
|
return s
|
|
1854
1929
|
|
|
1855
1930
|
if is_time_type(v):
|
|
@@ -1960,7 +2035,7 @@ def default_by_type(dtype):
|
|
|
1960
2035
|
>>> otp.default_by_type(float)
|
|
1961
2036
|
nan
|
|
1962
2037
|
>>> otp.default_by_type(otp.decimal)
|
|
1963
|
-
decimal(0
|
|
2038
|
+
decimal(0)
|
|
1964
2039
|
>>> otp.default_by_type(int)
|
|
1965
2040
|
0
|
|
1966
2041
|
>>> otp.default_by_type(otp.ulong)
|
onetick/py/utils/render.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import re
|
|
3
3
|
import html
|
|
4
|
+
import textwrap
|
|
4
5
|
import graphviz as gv
|
|
5
6
|
from collections import defaultdict, deque
|
|
6
7
|
from datetime import datetime
|
|
@@ -29,6 +30,82 @@ IF_ELSE_EPS = {
|
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
|
|
33
|
+
def _parse_table_fields(line: str) -> list:
|
|
34
|
+
result = line.strip().split(',')
|
|
35
|
+
for idx in range(0, len(result) - 1):
|
|
36
|
+
result[idx] = result[idx] + ','
|
|
37
|
+
|
|
38
|
+
return result
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _light_function_splitter(line: str, sep=',') -> list:
|
|
42
|
+
lines = []
|
|
43
|
+
current_line: list = []
|
|
44
|
+
parentheses_stack = 0
|
|
45
|
+
quotes_stack = 0
|
|
46
|
+
lead_quote_type = None
|
|
47
|
+
|
|
48
|
+
for ch in line:
|
|
49
|
+
if ch == sep and not parentheses_stack and not quotes_stack:
|
|
50
|
+
lines.append(''.join(current_line) + sep)
|
|
51
|
+
current_line = []
|
|
52
|
+
continue
|
|
53
|
+
|
|
54
|
+
current_line.append(ch)
|
|
55
|
+
|
|
56
|
+
if ch == '(' and not quotes_stack:
|
|
57
|
+
parentheses_stack += 1
|
|
58
|
+
continue
|
|
59
|
+
|
|
60
|
+
if ch == ')' and not quotes_stack:
|
|
61
|
+
parentheses_stack -= 1
|
|
62
|
+
if parentheses_stack < 0:
|
|
63
|
+
break
|
|
64
|
+
|
|
65
|
+
if ch in ["\"", "'"]:
|
|
66
|
+
if lead_quote_type is None:
|
|
67
|
+
lead_quote_type = ch
|
|
68
|
+
quotes_stack = 1
|
|
69
|
+
elif ch == lead_quote_type:
|
|
70
|
+
lead_quote_type = None
|
|
71
|
+
quotes_stack = 0
|
|
72
|
+
|
|
73
|
+
if parentheses_stack != 0:
|
|
74
|
+
raise ValueError(f'Incorrect parentheses count in function: `{line}`')
|
|
75
|
+
|
|
76
|
+
if quotes_stack != 0:
|
|
77
|
+
raise ValueError(f'Incorrect quotes count in function: `{line}`')
|
|
78
|
+
|
|
79
|
+
lines.append(''.join(current_line))
|
|
80
|
+
|
|
81
|
+
return lines
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
EP_TO_MULTILINE_ATTRS: dict = {
|
|
85
|
+
"ADD_FIELDS": {
|
|
86
|
+
"set": _light_function_splitter,
|
|
87
|
+
},
|
|
88
|
+
"UPDATE_FIELDS": {
|
|
89
|
+
"set": _light_function_splitter,
|
|
90
|
+
},
|
|
91
|
+
"TABLE": {
|
|
92
|
+
"fields": _parse_table_fields,
|
|
93
|
+
},
|
|
94
|
+
"PASSTHROUGH": {
|
|
95
|
+
"fields": _parse_table_fields,
|
|
96
|
+
},
|
|
97
|
+
"COMPUTE": {
|
|
98
|
+
"compute": _light_function_splitter,
|
|
99
|
+
},
|
|
100
|
+
"DECLARE_STATE_VARIABLES": {
|
|
101
|
+
"variables": _light_function_splitter,
|
|
102
|
+
},
|
|
103
|
+
"RENAME_FIELDS": {
|
|
104
|
+
"rename_fields": _parse_table_fields,
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
32
109
|
@dataclass
|
|
33
110
|
class NestedQuery:
|
|
34
111
|
name: str
|
|
@@ -53,6 +130,14 @@ class NestedQuery:
|
|
|
53
130
|
return "::".join(i for i in [self.file_path, self.query] if i)
|
|
54
131
|
|
|
55
132
|
|
|
133
|
+
@dataclass
|
|
134
|
+
class Config:
|
|
135
|
+
height: int = field(default=0)
|
|
136
|
+
width: int = field(default=0)
|
|
137
|
+
render_debug_info: bool = field(default=False)
|
|
138
|
+
constraint_edges: str = field(default="true")
|
|
139
|
+
|
|
140
|
+
|
|
56
141
|
@dataclass
|
|
57
142
|
class EP:
|
|
58
143
|
name: str
|
|
@@ -341,11 +426,14 @@ def _parse_function_params(func_params: str) -> Tuple[list, dict]:
|
|
|
341
426
|
return args, kwargs
|
|
342
427
|
|
|
343
428
|
|
|
344
|
-
def _parse_function(expression: str) -> Tuple[Optional[str], list, dict]:
|
|
429
|
+
def _parse_function(expression: str, pattern: Optional[str] = None) -> Tuple[Optional[str], list, dict]:
|
|
345
430
|
# EP_NAME(PARAM_NAME=PARAM_VALUE,...)
|
|
346
431
|
# [a-zA-Z_:] is EP_NAME, can contain letters, underscore and colon
|
|
347
432
|
# [\s\S] is any symbol including newline (because . doesn't include newline by default)
|
|
348
|
-
|
|
433
|
+
if not pattern:
|
|
434
|
+
pattern = r"^([a-zA-Z_:]*)\s*\(([\s\S]*)\)\s*$"
|
|
435
|
+
|
|
436
|
+
m = re.search(pattern, expression)
|
|
349
437
|
|
|
350
438
|
if not m:
|
|
351
439
|
return None, [], {}
|
|
@@ -412,9 +500,9 @@ def _parse_ep(ep_string: str, parse_eval_from_params: bool = False) -> Union[EP,
|
|
|
412
500
|
is_query_found = True
|
|
413
501
|
|
|
414
502
|
if kwargs_key in kwargs:
|
|
415
|
-
query_path = kwargs
|
|
503
|
+
query_path = kwargs[kwargs_key][1]
|
|
416
504
|
elif 0 <= args_idx < len(args):
|
|
417
|
-
query_path = args
|
|
505
|
+
query_path = args[args_idx]
|
|
418
506
|
else:
|
|
419
507
|
# don't do anything, just process as EP
|
|
420
508
|
is_query_found = False
|
|
@@ -697,32 +785,59 @@ def read_otq(path: str, parse_eval_from_params: bool = False) -> Optional[Graph]
|
|
|
697
785
|
return graph
|
|
698
786
|
|
|
699
787
|
|
|
700
|
-
def
|
|
701
|
-
|
|
702
|
-
|
|
788
|
+
def _truncate_param_value(value, height, width):
|
|
789
|
+
lines = [
|
|
790
|
+
line if len(line) <= width or not width else line[:width] + "..."
|
|
791
|
+
for line in value.splitlines()
|
|
792
|
+
]
|
|
703
793
|
|
|
704
|
-
height
|
|
705
|
-
|
|
706
|
-
|
|
794
|
+
if height and len(lines) > height:
|
|
795
|
+
lines = lines[:height] + ["..."]
|
|
796
|
+
|
|
797
|
+
return "\n".join(lines)
|
|
798
|
+
|
|
799
|
+
|
|
800
|
+
def _split_long_value_to_lines(value, height, width, indent=0, escape=False) -> list:
|
|
801
|
+
if len(value) <= width:
|
|
802
|
+
return [value]
|
|
803
|
+
|
|
804
|
+
result = []
|
|
805
|
+
lines = value.splitlines()
|
|
806
|
+
|
|
807
|
+
# textwrap.wrap replaces newline character to whitespace and brakes multiline strings
|
|
808
|
+
# If replace_whitespace=False, it preserves newline, but not use it for result array line splitting
|
|
809
|
+
for line in lines:
|
|
810
|
+
result.extend(textwrap.wrap(line, width=width, replace_whitespace=False))
|
|
811
|
+
|
|
812
|
+
if escape:
|
|
813
|
+
result = [html.escape(s) for s in result]
|
|
814
|
+
|
|
815
|
+
if indent:
|
|
816
|
+
indent_str = " " * indent
|
|
817
|
+
for i in range(1, len(result)):
|
|
818
|
+
result[i] = indent_str + result[i]
|
|
707
819
|
|
|
820
|
+
if height and len(result) > height:
|
|
821
|
+
result = result[:height] + ['...']
|
|
822
|
+
return result
|
|
823
|
+
|
|
824
|
+
|
|
825
|
+
def transform_param_value(ep: Any, param, value, height, width):
|
|
708
826
|
if isinstance(ep, EP) and (
|
|
709
827
|
ep.name == "PER_TICK_SCRIPT" and param.lower() == "script" or
|
|
710
828
|
ep.name == "CSV_FILE_LISTING" and param.lower() == "file_contents"
|
|
711
829
|
):
|
|
712
|
-
|
|
713
|
-
line if len(line) <= width or not width else line[:width] + "..."
|
|
714
|
-
for line in value.split("\n")
|
|
715
|
-
]
|
|
716
|
-
|
|
717
|
-
if height and len(lines) > height:
|
|
718
|
-
lines = lines[:height] + ["..."]
|
|
830
|
+
return _truncate_param_value(value, height, width)
|
|
719
831
|
|
|
720
|
-
|
|
832
|
+
if not (isinstance(ep, EP) and EP_TO_MULTILINE_ATTRS.get(ep.name, {}).get(param.lower())):
|
|
833
|
+
return "\n".join(_split_long_value_to_lines(value, height, width))
|
|
721
834
|
|
|
722
835
|
return value
|
|
723
836
|
|
|
724
837
|
|
|
725
|
-
def build_symbols(
|
|
838
|
+
def build_symbols(
|
|
839
|
+
symbols, gr_nested, gr_static, graphs: GraphStorage, graph_node, config: Config, reverse=False, graph_file=None,
|
|
840
|
+
):
|
|
726
841
|
table = GVTable()
|
|
727
842
|
|
|
728
843
|
for symbol_data in symbols:
|
|
@@ -732,11 +847,17 @@ def build_symbols(symbols, gr_nested, gr_static, graphs: GraphStorage, graph_nod
|
|
|
732
847
|
if symbol.query:
|
|
733
848
|
if symbol.is_local:
|
|
734
849
|
# reversed directions here brakes everything
|
|
850
|
+
|
|
851
|
+
if graph_file is None:
|
|
852
|
+
raise ValueError('`graph_file` parameter required for this case')
|
|
853
|
+
|
|
854
|
+
nested_cluster_id = graphs.get_query_unique_id(symbol.query, graph_file)
|
|
855
|
+
|
|
735
856
|
gr_nested.edge(
|
|
736
|
-
f"
|
|
857
|
+
f"{nested_cluster_id}__footer",
|
|
737
858
|
f"{graph_node}:symbols",
|
|
738
|
-
ltail=f"
|
|
739
|
-
style="dashed",
|
|
859
|
+
ltail=f"{nested_cluster_id}",
|
|
860
|
+
style="dashed", dir="both", constraint=config.constraint_edges,
|
|
740
861
|
)
|
|
741
862
|
continue
|
|
742
863
|
|
|
@@ -747,7 +868,7 @@ def build_symbols(symbols, gr_nested, gr_static, graphs: GraphStorage, graph_nod
|
|
|
747
868
|
f"{nested_cluster_id}__footer",
|
|
748
869
|
f"{graph_node}:symbols",
|
|
749
870
|
ltail=nested_cluster_id,
|
|
750
|
-
style="dashed",
|
|
871
|
+
style="dashed", dir="both", constraint=config.constraint_edges,
|
|
751
872
|
)
|
|
752
873
|
continue
|
|
753
874
|
|
|
@@ -764,11 +885,48 @@ def build_symbols(symbols, gr_nested, gr_static, graphs: GraphStorage, graph_nod
|
|
|
764
885
|
gr_static.edge(
|
|
765
886
|
f"{graph_node}__symbols" if not reverse else f"{graph_node}:symbols",
|
|
766
887
|
f"{graph_node}:symbols" if not reverse else f"{graph_node}__symbols",
|
|
767
|
-
style="dashed", constraint=
|
|
888
|
+
style="dashed", constraint=config.constraint_edges,
|
|
768
889
|
)
|
|
769
890
|
|
|
770
891
|
|
|
771
|
-
def
|
|
892
|
+
def _parse_special_attribute(param_name, param_lines, parser, height, width, cols=4):
|
|
893
|
+
"""
|
|
894
|
+
Builds better param representation for selected parameters and EPs
|
|
895
|
+
"""
|
|
896
|
+
def generate_row_string(_line: list) -> list:
|
|
897
|
+
sep = " "
|
|
898
|
+
|
|
899
|
+
# only in this case line could be longer than width
|
|
900
|
+
if len(_line) == 1 and len(_line[0]) > width:
|
|
901
|
+
_lines = _split_long_value_to_lines(_line[0], height, width, indent=4, escape=True)
|
|
902
|
+
else:
|
|
903
|
+
_lines = [sep.join(html.escape(s) for s in _line)]
|
|
904
|
+
|
|
905
|
+
return [" " * 2 + s for s in _lines]
|
|
906
|
+
|
|
907
|
+
param_value = ' '.join(param_lines)
|
|
908
|
+
params = parser(param_value)
|
|
909
|
+
|
|
910
|
+
params_table = [f"{param_name}:"]
|
|
911
|
+
current_line = []
|
|
912
|
+
current_width = 0
|
|
913
|
+
|
|
914
|
+
for param in params:
|
|
915
|
+
if width and current_line and current_width + len(param) >= width or len(current_line) == cols:
|
|
916
|
+
params_table.extend(generate_row_string(current_line))
|
|
917
|
+
current_line = []
|
|
918
|
+
current_width = 0
|
|
919
|
+
|
|
920
|
+
current_line.append(param)
|
|
921
|
+
current_width += len(param)
|
|
922
|
+
|
|
923
|
+
if current_line:
|
|
924
|
+
params_table.extend(generate_row_string(current_line))
|
|
925
|
+
|
|
926
|
+
return [(params_table, {"ALIGN": "LEFT", "BALIGN": "LEFT"})]
|
|
927
|
+
|
|
928
|
+
|
|
929
|
+
def build_node(graphs: GraphStorage, node: Node, config: Config):
|
|
772
930
|
if node.ep is None:
|
|
773
931
|
raise ValueError(f"EP of node {node.id} could not be None")
|
|
774
932
|
|
|
@@ -786,6 +944,9 @@ def build_node(graphs: GraphStorage, node: Node, line_limit: Optional[Tuple[int,
|
|
|
786
944
|
if node.tick_type:
|
|
787
945
|
table.cell([node.tick_type])
|
|
788
946
|
|
|
947
|
+
if config.render_debug_info:
|
|
948
|
+
table.cell([node.id])
|
|
949
|
+
|
|
789
950
|
if node.symbols:
|
|
790
951
|
table.cell([("[■]", {"port": "symbols"})])
|
|
791
952
|
|
|
@@ -793,6 +954,10 @@ def build_node(graphs: GraphStorage, node: Node, line_limit: Optional[Tuple[int,
|
|
|
793
954
|
params: List[Tuple[Optional[str], Union[str, NestedQuery]]] = \
|
|
794
955
|
[(None, v) for v in node.ep.args] + list(node.ep.kwargs.values())
|
|
795
956
|
|
|
957
|
+
param_args_lines = []
|
|
958
|
+
param_kwargs_lines = []
|
|
959
|
+
special_params = []
|
|
960
|
+
|
|
796
961
|
for idx, data in enumerate(params):
|
|
797
962
|
k, v = data
|
|
798
963
|
attrs = {"port": k}
|
|
@@ -806,34 +971,53 @@ def build_node(graphs: GraphStorage, node: Node, line_limit: Optional[Tuple[int,
|
|
|
806
971
|
else:
|
|
807
972
|
param_value = v
|
|
808
973
|
|
|
809
|
-
|
|
810
|
-
param_value = html.escape(param_value).replace("\t", " " * 4)
|
|
811
|
-
param_lines = param_value.split("\n")
|
|
974
|
+
is_special_attribute = k and EP_TO_MULTILINE_ATTRS.get(node.ep.name, {}).get(k.lower())
|
|
812
975
|
|
|
813
|
-
|
|
814
|
-
if len(param_lines) == 1:
|
|
815
|
-
param_lines[0] = f"{html.escape(k)}={param_lines[0]}"
|
|
816
|
-
else:
|
|
817
|
-
param_lines = [f"{html.escape(k)}="] + param_lines
|
|
976
|
+
param_value = transform_param_value(node.ep, k, param_value, config.height, config.width)
|
|
818
977
|
|
|
819
|
-
if
|
|
820
|
-
|
|
821
|
-
# if there are multiline parameter for EP.
|
|
822
|
-
# Align change affects all parameters for EP.
|
|
823
|
-
for i in range(len(param_lines)):
|
|
824
|
-
if i > 0:
|
|
825
|
-
param_lines[i] = " " * 2 + param_lines[i]
|
|
978
|
+
if not is_special_attribute:
|
|
979
|
+
param_value = html.escape(param_value)
|
|
826
980
|
|
|
827
|
-
|
|
981
|
+
param_value = param_value.replace("\t", " " * 4)
|
|
982
|
+
param_lines = param_value.splitlines()
|
|
828
983
|
|
|
984
|
+
# additional k check required by mypy
|
|
985
|
+
if is_special_attribute and k:
|
|
986
|
+
special_params.extend(
|
|
987
|
+
_parse_special_attribute(
|
|
988
|
+
k, param_lines, EP_TO_MULTILINE_ATTRS[node.ep.name][k.lower()], config.height, config.width,
|
|
989
|
+
)
|
|
990
|
+
)
|
|
991
|
+
else:
|
|
992
|
+
if k:
|
|
993
|
+
if len(param_lines) == 1:
|
|
994
|
+
param_lines[0] = f"{html.escape(k)}={param_lines[0]}"
|
|
995
|
+
else:
|
|
996
|
+
param_lines = [f"{html.escape(k)}:"] + param_lines
|
|
997
|
+
|
|
998
|
+
if len(param_lines) > 1:
|
|
999
|
+
# Add idents disable default horizontal central align
|
|
1000
|
+
# if there are multiline parameter for EP.
|
|
1001
|
+
# Align change affects all parameters for EP.
|
|
1002
|
+
for i in range(len(param_lines)):
|
|
1003
|
+
if i > 0:
|
|
1004
|
+
param_lines[i] = " " * 2 + param_lines[i]
|
|
1005
|
+
|
|
1006
|
+
attrs.update({"ALIGN": "LEFT", "BALIGN": "LEFT"})
|
|
1007
|
+
|
|
1008
|
+
if k:
|
|
1009
|
+
param_kwargs_lines.append((param_lines, attrs))
|
|
1010
|
+
else:
|
|
1011
|
+
param_args_lines.append((param_lines, attrs))
|
|
1012
|
+
|
|
1013
|
+
for param_lines, attrs in param_args_lines + special_params + param_kwargs_lines:
|
|
829
1014
|
table.row([param_lines], attrs=attrs)
|
|
830
1015
|
|
|
831
1016
|
if node.params:
|
|
832
|
-
table.row([[
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
table.row([node.ep.to_string()])
|
|
1017
|
+
table.row([[
|
|
1018
|
+
f"{html.escape(k)}={html.escape(_truncate_param_value(v, config.height, config.width))}"
|
|
1019
|
+
for k, v in node.params.items()
|
|
1020
|
+
]])
|
|
837
1021
|
|
|
838
1022
|
if isinstance(node.ep, IfElseEP):
|
|
839
1023
|
table.row([
|
|
@@ -886,7 +1070,7 @@ def _get_nested_query(nested_query: NestedQuery, local_graph: Graph, graphs: Gra
|
|
|
886
1070
|
|
|
887
1071
|
|
|
888
1072
|
def _render_graph(
|
|
889
|
-
gr_root, gr, graphs: GraphStorage, graph_name: str, queries: set,
|
|
1073
|
+
gr_root, gr, graphs: GraphStorage, graph_name: str, queries: set, config: Config,
|
|
890
1074
|
):
|
|
891
1075
|
graph = graphs[graph_name]
|
|
892
1076
|
|
|
@@ -947,15 +1131,18 @@ def _render_graph(
|
|
|
947
1131
|
|
|
948
1132
|
gr_sub.edge(
|
|
949
1133
|
f"{footer_id}:params", f"{query_id}__params",
|
|
950
|
-
style="dashed", constraint=
|
|
1134
|
+
style="dashed", constraint=config.constraint_edges,
|
|
951
1135
|
)
|
|
952
1136
|
|
|
953
1137
|
if query.symbols:
|
|
954
|
-
build_symbols(
|
|
1138
|
+
build_symbols(
|
|
1139
|
+
query.symbols, gr, gr_sub, graphs, f"{query_id}__footer", config,
|
|
1140
|
+
reverse=True, graph_file=graph.file_path,
|
|
1141
|
+
)
|
|
955
1142
|
|
|
956
1143
|
for node_id, node in query.nodes.items():
|
|
957
1144
|
node_unique_id = _get_node_unique_id(node, query)
|
|
958
|
-
gr_sub.node(node_unique_id, build_node(graphs, node,
|
|
1145
|
+
gr_sub.node(node_unique_id, build_node(graphs, node, config), group=query_name)
|
|
959
1146
|
|
|
960
1147
|
for sink in node.sinks:
|
|
961
1148
|
if "OUT" in node.labels:
|
|
@@ -989,11 +1176,11 @@ def _render_graph(
|
|
|
989
1176
|
f"{node_unique_id}:{param_name}",
|
|
990
1177
|
_get_node_unique_id(nested_cluster.roots[0], nested_cluster),
|
|
991
1178
|
lhead=nested_cluster.get_id(),
|
|
992
|
-
style="dashed", dir="both",
|
|
1179
|
+
style="dashed", dir="both", constraint=config.constraint_edges,
|
|
993
1180
|
)
|
|
994
1181
|
|
|
995
1182
|
if node.symbols:
|
|
996
|
-
build_symbols(node.symbols, gr, gr_sub, graphs, node_unique_id)
|
|
1183
|
+
build_symbols(node.symbols, gr, gr_sub, graphs, node_unique_id, config, graph_file=graph.file_path)
|
|
997
1184
|
|
|
998
1185
|
if isinstance(node.ep, NestedQuery):
|
|
999
1186
|
nested_cluster = _get_nested_query(node.ep, graph, graphs)
|
|
@@ -1004,7 +1191,7 @@ def _render_graph(
|
|
|
1004
1191
|
node_unique_id,
|
|
1005
1192
|
_get_node_unique_id(nested_cluster.roots[0], nested_cluster),
|
|
1006
1193
|
lhead=nested_cluster.get_id(),
|
|
1007
|
-
style="dashed", dir="both",
|
|
1194
|
+
style="dashed", dir="both", constraint=config.constraint_edges,
|
|
1008
1195
|
)
|
|
1009
1196
|
|
|
1010
1197
|
|
|
@@ -1014,9 +1201,11 @@ def render_otq(
|
|
|
1014
1201
|
output_format: Optional[str] = None,
|
|
1015
1202
|
load_external_otqs: bool = True,
|
|
1016
1203
|
view: bool = False,
|
|
1017
|
-
line_limit: Optional[Tuple[int, int]] = (10,
|
|
1204
|
+
line_limit: Optional[Tuple[int, int]] = (10, 60),
|
|
1018
1205
|
parse_eval_from_params: bool = False,
|
|
1206
|
+
render_debug_info: bool = False,
|
|
1019
1207
|
debug: bool = False,
|
|
1208
|
+
graphviz_compat_mode: bool = False,
|
|
1020
1209
|
) -> str:
|
|
1021
1210
|
"""
|
|
1022
1211
|
Render queries from .otq files.
|
|
@@ -1029,7 +1218,7 @@ def render_otq(
|
|
|
1029
1218
|
image_path: str, None
|
|
1030
1219
|
Path for generated image. If omitted, image will be saved in a temp dir
|
|
1031
1220
|
output_format: str, None
|
|
1032
|
-
`Graphviz` rendering format. Default: `
|
|
1221
|
+
`Graphviz` rendering format. Default: `svg`.
|
|
1033
1222
|
If `image_path` contains one of next extensions, `output_format` will be set automatically: `png`, `svg`, `dot`.
|
|
1034
1223
|
load_external_otqs: bool
|
|
1035
1224
|
If set to `True` (default) dependencies from external .otq files (not listed in ``path`` param)
|
|
@@ -1043,8 +1232,13 @@ def render_otq(
|
|
|
1043
1232
|
If one of tuple values set to zero the corresponding limit disabled.
|
|
1044
1233
|
parse_eval_from_params: bool
|
|
1045
1234
|
Enable parsing and printing `eval` sub-queries from EP parameters.
|
|
1235
|
+
render_debug_info: bool
|
|
1236
|
+
Render additional debug information.
|
|
1046
1237
|
debug: bool
|
|
1047
1238
|
Allow to print stdout or stderr from `Graphviz` render.
|
|
1239
|
+
graphviz_compat_mode: bool
|
|
1240
|
+
Change internal parameters of result graph for better compatibility with old `Graphviz` versions.
|
|
1241
|
+
Could produce larger and less readable graphs.
|
|
1048
1242
|
|
|
1049
1243
|
Returns
|
|
1050
1244
|
-------
|
|
@@ -1069,6 +1263,19 @@ def render_otq(
|
|
|
1069
1263
|
|
|
1070
1264
|
>>> otp.utils.render_otq(["./first.otq", "./second.otq::some_query"]) # doctest: +SKIP
|
|
1071
1265
|
"""
|
|
1266
|
+
if line_limit is None:
|
|
1267
|
+
line_limit = (0, 0)
|
|
1268
|
+
|
|
1269
|
+
height, width = line_limit
|
|
1270
|
+
if height < 0 or width < 0:
|
|
1271
|
+
raise ValueError("line_limit values should not be negative")
|
|
1272
|
+
|
|
1273
|
+
config_kwargs = {}
|
|
1274
|
+
if graphviz_compat_mode:
|
|
1275
|
+
config_kwargs["constraint_edges"] = "false"
|
|
1276
|
+
|
|
1277
|
+
config = Config(height=height, width=width, render_debug_info=render_debug_info, **config_kwargs)
|
|
1278
|
+
|
|
1072
1279
|
if not isinstance(path, list):
|
|
1073
1280
|
path = [path]
|
|
1074
1281
|
|
|
@@ -1137,7 +1344,7 @@ def render_otq(
|
|
|
1137
1344
|
output_format = extension
|
|
1138
1345
|
|
|
1139
1346
|
if not output_format:
|
|
1140
|
-
output_format = "
|
|
1347
|
+
output_format = "svg"
|
|
1141
1348
|
|
|
1142
1349
|
if not image_path:
|
|
1143
1350
|
image_path = TmpFile().path
|
|
@@ -1154,8 +1361,14 @@ def render_otq(
|
|
|
1154
1361
|
with gr.subgraph(name=f"cluster__graph__{idx}", node_attr={"shape": "plaintext"}) as gr_otq:
|
|
1155
1362
|
gr_otq.attr(label=otq_path)
|
|
1156
1363
|
gr_otq.attr(margin="16")
|
|
1157
|
-
_render_graph(gr, gr_otq, graphs, otq_path, queries_to_render[otq_path],
|
|
1364
|
+
_render_graph(gr, gr_otq, graphs, otq_path, queries_to_render[otq_path], config)
|
|
1158
1365
|
|
|
1159
1366
|
idx += 1
|
|
1160
1367
|
|
|
1161
|
-
|
|
1368
|
+
try:
|
|
1369
|
+
return gr.render(view=view, quiet=not debug)
|
|
1370
|
+
except Exception as exc:
|
|
1371
|
+
raise RuntimeError(
|
|
1372
|
+
"Graphviz render failed. Try to set parameter `graphviz_compat_mode=True` "
|
|
1373
|
+
"for better compatibility if you use old Graphviz version"
|
|
1374
|
+
) from exc
|
onetick/py/utils/types.py
CHANGED
|
@@ -8,6 +8,8 @@ def get_type_that_includes(types):
|
|
|
8
8
|
if b_type1 != b_type2:
|
|
9
9
|
if {b_type1, b_type2} == {int, float}:
|
|
10
10
|
dtype = float
|
|
11
|
+
elif {b_type1, b_type2} == {ott.decimal, float} or {b_type1, b_type2} == {ott.decimal, int}:
|
|
12
|
+
dtype = ott.decimal
|
|
11
13
|
elif {b_type1, b_type2} == {ott.nsectime, ott.msectime}:
|
|
12
14
|
dtype = ott.nsectime
|
|
13
15
|
else:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: onetick-py
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.175.0
|
|
4
4
|
Summary: Python package that allows you to work with OneTick
|
|
5
5
|
Author-email: solutions <solutions@onetick.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -36,7 +36,7 @@ Requires-Dist: numpy==1.26.4; python_version == "3.12" and extra == "strict"
|
|
|
36
36
|
Requires-Dist: pandas==1.5.2; python_version == "3.9" and extra == "strict"
|
|
37
37
|
Requires-Dist: pandas==1.5.3; python_version == "3.10" and extra == "strict"
|
|
38
38
|
Requires-Dist: pandas==1.5.3; python_version == "3.11" and extra == "strict"
|
|
39
|
-
Requires-Dist: pandas==2.2.
|
|
39
|
+
Requires-Dist: pandas==2.2.1; python_version == "3.12" and extra == "strict"
|
|
40
40
|
Requires-Dist: pandas==2.3.0; python_version == "3.13" and extra == "strict"
|
|
41
41
|
Requires-Dist: pandas==2.3.3; python_version == "3.14" and extra == "strict"
|
|
42
42
|
Provides-Extra: webapi
|
|
@@ -13,12 +13,12 @@ onetick/lib/__init__.py,sha256=Rp7CIDoA4E6LIm1f2mNvl_5b_n-0U3suA3FmBXbmKoU,114
|
|
|
13
13
|
onetick/lib/instance.py,sha256=3FJB8PWs2ap-EGb6DzsnLRL2meTMUViTdy343m6tHvM,4825
|
|
14
14
|
onetick/py/__init__.py,sha256=Ge1Ekl6OHRjm5KrXojq3AzLCaUGvLNWuaIK0C9DdwWU,11164
|
|
15
15
|
onetick/py/_stack_info.py,sha256=PHZOkW_fK7Fbl4YEj5CaYK9L6vh4j-bUU7_cSYOWZ30,2546
|
|
16
|
-
onetick/py/_version.py,sha256=
|
|
16
|
+
onetick/py/_version.py,sha256=T2BdtqEAyiIrXPUtz7ueDQGJEIwu9w3KvGz9NNm-yjE,76
|
|
17
17
|
onetick/py/backports.py,sha256=mR00mxe7E7UgBljf-Wa93Mo6lpi-C4Op561uhPUoEt8,815
|
|
18
18
|
onetick/py/cache.py,sha256=BBZg8n0AGjZzZapg4752LkSZdX5C6DGf7vU9sAStv6A,12798
|
|
19
19
|
onetick/py/compatibility.py,sha256=PyQVs4h8vs_9qKocb117jyu1sNmUvqHj9o9r9NqI-9E,31036
|
|
20
20
|
onetick/py/configuration.py,sha256=KCX44v_nEOZDvo-rItIrNVKKvqyM73QUwIivez2VHvY,28448
|
|
21
|
-
onetick/py/functions.py,sha256=
|
|
21
|
+
onetick/py/functions.py,sha256=Kqromia64o1K4fgbLbijw64o5pFGBZRog52824wOxlA,98004
|
|
22
22
|
onetick/py/license.py,sha256=50dsFrE-NKsPOdkAoyxHr44bH8DzFCr_6TabX0JH6tQ,6140
|
|
23
23
|
onetick/py/log.py,sha256=Els2drZcVjJrD6kvbwgFGEpg6jAacoUEtyN6rCaauuk,2723
|
|
24
24
|
onetick/py/math.py,sha256=MZvlyUjxOWGJvmxK8pfMkCr4THM52AE6NyGEidgDMyE,26311
|
|
@@ -30,17 +30,17 @@ onetick/py/servers.py,sha256=h6l0X55kcnpI25bZcYaBpPAAGBZjqk9mt1SQe4xZrh0,6840
|
|
|
30
30
|
onetick/py/session.py,sha256=AqoS7BFN_Sz3JU2CAaZm0Bro8CmUy6VjmoDjwOU1wYM,49591
|
|
31
31
|
onetick/py/sql.py,sha256=kJ0732bZ0OGF8x3VzcOUs38zEGsxcd1nZOSN75VKXhA,2993
|
|
32
32
|
onetick/py/state.py,sha256=giQpUUXWwuz_CL_VvxNQDaM71LAMPqeRgHfoGhS1gTk,10330
|
|
33
|
-
onetick/py/types.py,sha256=
|
|
33
|
+
onetick/py/types.py,sha256=jzGyG01UmjEy_ISCzgj2S7LzGx35oesQRSjYqVdg_D0,68079
|
|
34
34
|
onetick/py/aggregations/__init__.py,sha256=02WnTc9ocTp34OkGyXRHY2QAZi_QjDMHClE5tvx6Gtk,736
|
|
35
|
-
onetick/py/aggregations/_base.py,sha256=
|
|
35
|
+
onetick/py/aggregations/_base.py,sha256=HbiicjQI6nYmfGZxI1Hx9QdDJoQ4kcCmOI8E-3VZrjc,25715
|
|
36
36
|
onetick/py/aggregations/_docs.py,sha256=7pz6XIhRuP_1vuOYO9UfTlzl6zGD2_jwbPMfJPOX-M8,34798
|
|
37
37
|
onetick/py/aggregations/compute.py,sha256=GJpR8AWlEmfNsnl-L2oJPuY5rFb6PC-VwLZnnRGtCys,11854
|
|
38
38
|
onetick/py/aggregations/functions.py,sha256=MnjINLAxoLehgInxhY-xSI1xw8TtCGzT7APD5HdFmjE,80801
|
|
39
39
|
onetick/py/aggregations/generic.py,sha256=7mWRRAPS-t-e_umQc2T3GPPuW1Pia7mKhgjaVB2sSBM,3896
|
|
40
40
|
onetick/py/aggregations/high_low.py,sha256=B7_Y6KhJjIqJthK0msW0JRkTpxINpnKPuSQjQa2QyZs,2508
|
|
41
41
|
onetick/py/aggregations/num_distinct.py,sha256=s-VrU92kBKnJ6LIAGq2-j-t8amnolIF2EHkEt78A7_s,2830
|
|
42
|
-
onetick/py/aggregations/order_book.py,sha256=
|
|
43
|
-
onetick/py/aggregations/other.py,sha256=
|
|
42
|
+
onetick/py/aggregations/order_book.py,sha256=4_NPXWy5GmtGK4W3fmT089sq3jIROn3mcgc-hX8fqVw,19439
|
|
43
|
+
onetick/py/aggregations/other.py,sha256=0FZw8AIqo73duVsH7Jw7Sx7TcMABoUZAq866P-eII04,39436
|
|
44
44
|
onetick/py/callback/__init__.py,sha256=lF6u1jDy0dv-2qBvonqmSaQAU7dWQAuq5FWyR5LzvLI,108
|
|
45
45
|
onetick/py/callback/callback.py,sha256=i8yVsbceqhVxNTcXU2ie0-nBRyC0nS2LVDFFtqfCMlw,8618
|
|
46
46
|
onetick/py/callback/callbacks.py,sha256=_8Yw_b-OITXUtNTxGJIXm-tz-KfHZYe6SdoyW7-QX4w,5011
|
|
@@ -54,7 +54,7 @@ onetick/py/core/lambda_object.py,sha256=ayob_2c2XYSG7vFsqp-2b9FSPD-3TFReLbx3de3t
|
|
|
54
54
|
onetick/py/core/multi_output_source.py,sha256=f3dXr9kwmED77oWDFVr9cn44aYiJxC81loq2pX4u50Q,8766
|
|
55
55
|
onetick/py/core/per_tick_script.py,sha256=I3pfidriS1TrFPc3DAABt91GKMgby9P_8KBatiFQx6U,83322
|
|
56
56
|
onetick/py/core/query_inspector.py,sha256=Jwnw7qUVvjvaT63mQ5PkUSy8x36knoxWrNll3aXuLZw,16440
|
|
57
|
-
onetick/py/core/source.py,sha256=
|
|
57
|
+
onetick/py/core/source.py,sha256=h-J7Fkl5AyGiW7c0gPoUgjrZxD-6RXO6DHbJLgikByk,64421
|
|
58
58
|
onetick/py/core/_internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
59
59
|
onetick/py/core/_internal/_manually_bound_value.py,sha256=a4JF63g9MtXsIwpDqm_PCLBH51_SaG1-T6jWV5-IJ1k,191
|
|
60
60
|
onetick/py/core/_internal/_nodes_history.py,sha256=fCpJ4FWFDzM-bmi-YyCHmNYuFM-CL_lE2bXySOi-TPY,7194
|
|
@@ -81,7 +81,7 @@ onetick/py/core/_source/source_methods/fields.py,sha256=6GozvRornClXfEu6oY9-JpN3
|
|
|
81
81
|
onetick/py/core/_source/source_methods/filters.py,sha256=WuSnK-tQsP3fL2bqVfMZpbpPgk_LG4L8lrYULSSGoKE,33930
|
|
82
82
|
onetick/py/core/_source/source_methods/joins.py,sha256=edLIJ-BTWbwvxga1YCzsN6NdhIwtJyZIksZawvPWCbU,61175
|
|
83
83
|
onetick/py/core/_source/source_methods/merges.py,sha256=efk-R4WOjB2pcAIGWPsZsDzYuitADztH5xtRWTBfutk,23115
|
|
84
|
-
onetick/py/core/_source/source_methods/misc.py,sha256=
|
|
84
|
+
onetick/py/core/_source/source_methods/misc.py,sha256=tPkyYc7aS6JxUOuPonFeDJ-bO0FKyhT7Zn0EuGvh5lg,59689
|
|
85
85
|
onetick/py/core/_source/source_methods/pandases.py,sha256=BbvIlw6ipqC2dOiKSyyZ5jhAtE4ZtdL30OZXV_e_FuE,3670
|
|
86
86
|
onetick/py/core/_source/source_methods/renames.py,sha256=T5EXq-oHq5NwApPZfYRDOJPVjclqLqAofRAP-Ykzu2M,13749
|
|
87
87
|
onetick/py/core/_source/source_methods/sorts.py,sha256=tDFt3rObNHP-S_HOtDd6SLpgkCCGJnSDcMUxLake080,5583
|
|
@@ -90,12 +90,12 @@ onetick/py/core/_source/source_methods/symbols.py,sha256=MG0dgVVVJ6fD_-GJkkXEgTe
|
|
|
90
90
|
onetick/py/core/_source/source_methods/times.py,sha256=2kbLHG-3RQM6uWmw8pBGDiAz4R83KK46Tp1smTY6SR8,27665
|
|
91
91
|
onetick/py/core/_source/source_methods/writes.py,sha256=fPUbiqlm30Kb4QzXZLtWCsQiTFBDxK5oOWuVyfVXbYM,42717
|
|
92
92
|
onetick/py/core/column_operations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
93
|
-
onetick/py/core/column_operations/base.py,sha256=
|
|
93
|
+
onetick/py/core/column_operations/base.py,sha256=gn-4rz4FMAR2pp-7e9DulKc6L05YAMkGkIiEwzHmYmo,36102
|
|
94
94
|
onetick/py/core/column_operations/_methods/__init__.py,sha256=_C5hpQG51MingtWW2N8vYzSVFpR314xIrtukBfEfPMk,207
|
|
95
95
|
onetick/py/core/column_operations/_methods/_internal.py,sha256=M04DbbD9DjA3NrrbibuQPje8ylkaYnVB5Nmlwx1RvaE,878
|
|
96
|
-
onetick/py/core/column_operations/_methods/conversions.py,sha256=
|
|
96
|
+
onetick/py/core/column_operations/_methods/conversions.py,sha256=V7VREn8fFd1tanRO7x0mltrJtWFnc4ltTXTrxv1Lxpc,7065
|
|
97
97
|
onetick/py/core/column_operations/_methods/methods.py,sha256=OaXiDcdPYXnPlWhwD-BCac5UAecZFnEKCkxBpQOVB5E,11054
|
|
98
|
-
onetick/py/core/column_operations/_methods/op_types.py,sha256=
|
|
98
|
+
onetick/py/core/column_operations/_methods/op_types.py,sha256=HfoFQLVcfsAjDBigMOUxCLGoQMqEecS8IFHIE6yb2rQ,5283
|
|
99
99
|
onetick/py/core/column_operations/accessors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
100
100
|
onetick/py/core/column_operations/accessors/_accessor.py,sha256=b5K4OQmnO4NeFK1QsKjgZ1WfEyBjxKWMkb12RBbnGXQ,983
|
|
101
101
|
onetick/py/core/column_operations/accessors/decimal_accessor.py,sha256=DwgJ-as0p_gex6UGxhxhsn9chSpne5YV-t1ntvr-saw,3345
|
|
@@ -139,14 +139,14 @@ onetick/py/utils/helpers.py,sha256=XY2PaMUSzFNIpiuiAY_gPQ0TnYTmfoPAGNAFh_6aB14,2
|
|
|
139
139
|
onetick/py/utils/locator.py,sha256=YUOXU0yh0ZZZOJpJniAq9WNn0_u3X_M5q2tEwt1cp_4,2887
|
|
140
140
|
onetick/py/utils/perf.py,sha256=g0r-9YUc2mx9Lj4PjG6Fk1keO3m9nC3t2cpHx3M-AeQ,19299
|
|
141
141
|
onetick/py/utils/query.py,sha256=b60JmfJDx3mpP74rTQC3qnlCb6t4fYFEauVaH9ulwL8,1330
|
|
142
|
-
onetick/py/utils/render.py,sha256=
|
|
142
|
+
onetick/py/utils/render.py,sha256=wTSUqkzrzrFeGQgeU5hEt1ckfW2i1hQjiEX1apUHAzs,44910
|
|
143
143
|
onetick/py/utils/script.py,sha256=Y8NujEo2_5QaP6KDnLKJiKQ7SmMjw8_dv8sYL9rHDCE,11184
|
|
144
144
|
onetick/py/utils/temp.py,sha256=j-BC155dE46k0zfKTTs26KTF0CK6WA1hO2GD54iunyM,17380
|
|
145
|
-
onetick/py/utils/types.py,sha256=
|
|
145
|
+
onetick/py/utils/types.py,sha256=7u9s9uN1jlkgud8_TSLy6iFzQFBtKJ0v3yamgOVitrs,3747
|
|
146
146
|
onetick/py/utils/tz.py,sha256=sYUKigaORonp7Xa6x806xVYJ69lYJHd6NrLxQHB5AZo,2878
|
|
147
|
-
onetick_py-1.
|
|
148
|
-
onetick_py-1.
|
|
149
|
-
onetick_py-1.
|
|
150
|
-
onetick_py-1.
|
|
151
|
-
onetick_py-1.
|
|
152
|
-
onetick_py-1.
|
|
147
|
+
onetick_py-1.175.0.dist-info/licenses/LICENSE,sha256=Yhu7lKNFS0fsaN-jSattEMRtCOPueP58Eu5BPH8ZGjM,1075
|
|
148
|
+
onetick_py-1.175.0.dist-info/METADATA,sha256=Xhk0Xhbx2mZ5VTLonvn-LFab_pint8OxKYU5VR_-7XU,7218
|
|
149
|
+
onetick_py-1.175.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
150
|
+
onetick_py-1.175.0.dist-info/entry_points.txt,sha256=QmK_tFswIN-SQRmtnTSBEi8GvT0TVq-66IzXXZIsV3U,81
|
|
151
|
+
onetick_py-1.175.0.dist-info/top_level.txt,sha256=Na1jSJmVMyYGOndaswt554QKIUwQjcYh6th2ATsmw0U,23
|
|
152
|
+
onetick_py-1.175.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|