onetick-py 1.162.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. locator_parser/__init__.py +0 -0
  2. locator_parser/acl.py +73 -0
  3. locator_parser/actions.py +266 -0
  4. locator_parser/common.py +365 -0
  5. locator_parser/io.py +41 -0
  6. locator_parser/locator.py +150 -0
  7. onetick/__init__.py +101 -0
  8. onetick/doc_utilities/__init__.py +3 -0
  9. onetick/doc_utilities/napoleon.py +40 -0
  10. onetick/doc_utilities/ot_doctest.py +140 -0
  11. onetick/doc_utilities/snippets.py +280 -0
  12. onetick/lib/__init__.py +4 -0
  13. onetick/lib/instance.py +138 -0
  14. onetick/py/__init__.py +290 -0
  15. onetick/py/_stack_info.py +89 -0
  16. onetick/py/_version.py +2 -0
  17. onetick/py/aggregations/__init__.py +11 -0
  18. onetick/py/aggregations/_base.py +645 -0
  19. onetick/py/aggregations/_docs.py +912 -0
  20. onetick/py/aggregations/compute.py +286 -0
  21. onetick/py/aggregations/functions.py +2216 -0
  22. onetick/py/aggregations/generic.py +104 -0
  23. onetick/py/aggregations/high_low.py +80 -0
  24. onetick/py/aggregations/num_distinct.py +83 -0
  25. onetick/py/aggregations/order_book.py +427 -0
  26. onetick/py/aggregations/other.py +1014 -0
  27. onetick/py/backports.py +26 -0
  28. onetick/py/cache.py +373 -0
  29. onetick/py/callback/__init__.py +5 -0
  30. onetick/py/callback/callback.py +275 -0
  31. onetick/py/callback/callbacks.py +131 -0
  32. onetick/py/compatibility.py +752 -0
  33. onetick/py/configuration.py +736 -0
  34. onetick/py/core/__init__.py +0 -0
  35. onetick/py/core/_csv_inspector.py +93 -0
  36. onetick/py/core/_internal/__init__.py +0 -0
  37. onetick/py/core/_internal/_manually_bound_value.py +6 -0
  38. onetick/py/core/_internal/_nodes_history.py +250 -0
  39. onetick/py/core/_internal/_op_utils/__init__.py +0 -0
  40. onetick/py/core/_internal/_op_utils/every_operand.py +9 -0
  41. onetick/py/core/_internal/_op_utils/is_const.py +10 -0
  42. onetick/py/core/_internal/_per_tick_scripts/tick_list_sort_template.script +121 -0
  43. onetick/py/core/_internal/_proxy_node.py +140 -0
  44. onetick/py/core/_internal/_state_objects.py +2307 -0
  45. onetick/py/core/_internal/_state_vars.py +87 -0
  46. onetick/py/core/_source/__init__.py +0 -0
  47. onetick/py/core/_source/_symbol_param.py +95 -0
  48. onetick/py/core/_source/schema.py +97 -0
  49. onetick/py/core/_source/source_methods/__init__.py +0 -0
  50. onetick/py/core/_source/source_methods/aggregations.py +810 -0
  51. onetick/py/core/_source/source_methods/applyers.py +296 -0
  52. onetick/py/core/_source/source_methods/columns.py +141 -0
  53. onetick/py/core/_source/source_methods/data_quality.py +301 -0
  54. onetick/py/core/_source/source_methods/debugs.py +270 -0
  55. onetick/py/core/_source/source_methods/drops.py +120 -0
  56. onetick/py/core/_source/source_methods/fields.py +619 -0
  57. onetick/py/core/_source/source_methods/filters.py +1001 -0
  58. onetick/py/core/_source/source_methods/joins.py +1393 -0
  59. onetick/py/core/_source/source_methods/merges.py +566 -0
  60. onetick/py/core/_source/source_methods/misc.py +1325 -0
  61. onetick/py/core/_source/source_methods/pandases.py +155 -0
  62. onetick/py/core/_source/source_methods/renames.py +356 -0
  63. onetick/py/core/_source/source_methods/sorts.py +183 -0
  64. onetick/py/core/_source/source_methods/switches.py +142 -0
  65. onetick/py/core/_source/source_methods/symbols.py +117 -0
  66. onetick/py/core/_source/source_methods/times.py +627 -0
  67. onetick/py/core/_source/source_methods/writes.py +702 -0
  68. onetick/py/core/_source/symbol.py +202 -0
  69. onetick/py/core/_source/tmp_otq.py +222 -0
  70. onetick/py/core/column.py +209 -0
  71. onetick/py/core/column_operations/__init__.py +0 -0
  72. onetick/py/core/column_operations/_methods/__init__.py +4 -0
  73. onetick/py/core/column_operations/_methods/_internal.py +28 -0
  74. onetick/py/core/column_operations/_methods/conversions.py +215 -0
  75. onetick/py/core/column_operations/_methods/methods.py +294 -0
  76. onetick/py/core/column_operations/_methods/op_types.py +150 -0
  77. onetick/py/core/column_operations/accessors/__init__.py +0 -0
  78. onetick/py/core/column_operations/accessors/_accessor.py +30 -0
  79. onetick/py/core/column_operations/accessors/decimal_accessor.py +92 -0
  80. onetick/py/core/column_operations/accessors/dt_accessor.py +464 -0
  81. onetick/py/core/column_operations/accessors/float_accessor.py +160 -0
  82. onetick/py/core/column_operations/accessors/str_accessor.py +1374 -0
  83. onetick/py/core/column_operations/base.py +1061 -0
  84. onetick/py/core/cut_builder.py +149 -0
  85. onetick/py/core/db_constants.py +20 -0
  86. onetick/py/core/eval_query.py +244 -0
  87. onetick/py/core/lambda_object.py +442 -0
  88. onetick/py/core/multi_output_source.py +193 -0
  89. onetick/py/core/per_tick_script.py +2253 -0
  90. onetick/py/core/query_inspector.py +465 -0
  91. onetick/py/core/source.py +1663 -0
  92. onetick/py/db/__init__.py +2 -0
  93. onetick/py/db/_inspection.py +1042 -0
  94. onetick/py/db/db.py +1423 -0
  95. onetick/py/db/utils.py +64 -0
  96. onetick/py/docs/__init__.py +0 -0
  97. onetick/py/docs/docstring_parser.py +112 -0
  98. onetick/py/docs/utils.py +81 -0
  99. onetick/py/functions.py +2354 -0
  100. onetick/py/license.py +188 -0
  101. onetick/py/log.py +88 -0
  102. onetick/py/math.py +947 -0
  103. onetick/py/misc.py +437 -0
  104. onetick/py/oqd/__init__.py +22 -0
  105. onetick/py/oqd/eps.py +1195 -0
  106. onetick/py/oqd/sources.py +325 -0
  107. onetick/py/otq.py +211 -0
  108. onetick/py/pyomd_mock.py +47 -0
  109. onetick/py/run.py +841 -0
  110. onetick/py/servers.py +173 -0
  111. onetick/py/session.py +1342 -0
  112. onetick/py/sources/__init__.py +19 -0
  113. onetick/py/sources/cache.py +167 -0
  114. onetick/py/sources/common.py +126 -0
  115. onetick/py/sources/csv.py +642 -0
  116. onetick/py/sources/custom.py +85 -0
  117. onetick/py/sources/data_file.py +305 -0
  118. onetick/py/sources/data_source.py +1049 -0
  119. onetick/py/sources/empty.py +94 -0
  120. onetick/py/sources/odbc.py +337 -0
  121. onetick/py/sources/order_book.py +238 -0
  122. onetick/py/sources/parquet.py +168 -0
  123. onetick/py/sources/pit.py +191 -0
  124. onetick/py/sources/query.py +495 -0
  125. onetick/py/sources/snapshots.py +419 -0
  126. onetick/py/sources/split_query_output_by_symbol.py +198 -0
  127. onetick/py/sources/symbology_mapping.py +123 -0
  128. onetick/py/sources/symbols.py +357 -0
  129. onetick/py/sources/ticks.py +825 -0
  130. onetick/py/sql.py +70 -0
  131. onetick/py/state.py +256 -0
  132. onetick/py/types.py +2056 -0
  133. onetick/py/utils/__init__.py +70 -0
  134. onetick/py/utils/acl.py +93 -0
  135. onetick/py/utils/config.py +186 -0
  136. onetick/py/utils/default.py +49 -0
  137. onetick/py/utils/file.py +38 -0
  138. onetick/py/utils/helpers.py +76 -0
  139. onetick/py/utils/locator.py +94 -0
  140. onetick/py/utils/perf.py +499 -0
  141. onetick/py/utils/query.py +49 -0
  142. onetick/py/utils/render.py +1139 -0
  143. onetick/py/utils/script.py +244 -0
  144. onetick/py/utils/temp.py +471 -0
  145. onetick/py/utils/types.py +118 -0
  146. onetick/py/utils/tz.py +82 -0
  147. onetick_py-1.162.2.dist-info/METADATA +148 -0
  148. onetick_py-1.162.2.dist-info/RECORD +152 -0
  149. onetick_py-1.162.2.dist-info/WHEEL +5 -0
  150. onetick_py-1.162.2.dist-info/entry_points.txt +2 -0
  151. onetick_py-1.162.2.dist-info/licenses/LICENSE +21 -0
  152. onetick_py-1.162.2.dist-info/top_level.txt +2 -0
@@ -0,0 +1,810 @@
1
+ import warnings
2
+ from inspect import Parameter
3
+ from typing import TYPE_CHECKING, Optional, Tuple, Union
4
+
5
+ from onetick import py as otp
6
+ from onetick.py import aggregations
7
+ from onetick.py.aggregations._docs import (
8
+ _all_fields_with_policy_doc,
9
+ _boundary_tick_bucket_doc,
10
+ _bucket_end_condition_doc,
11
+ _bucket_interval_doc,
12
+ _bucket_time_doc,
13
+ _bucket_units_doc,
14
+ _end_condition_per_group_doc,
15
+ _group_by_doc,
16
+ _groups_to_display_doc,
17
+ _running_doc,
18
+ copy_method,
19
+ )
20
+ from onetick.py.docs.utils import docstring, param_doc
21
+ from onetick.py.otq import otq
22
+
23
+ from .misc import inplace_operation
24
+
25
+ if TYPE_CHECKING:
26
+ from onetick.py.core.source import Source
27
+
28
+
29
+ _agg_doc = param_doc(
30
+ name='aggs',
31
+ annotation=dict,
32
+ str_annotation='dict of aggregations',
33
+ desc="""
34
+ aggregation dict:
35
+
36
+ * key - output column name for regular aggregations, prefix for column names for tick and multi-column aggregations;
37
+ * value - aggregation
38
+ """,
39
+ )
40
+
41
+
42
+ @docstring(
43
+ parameters=[
44
+ _agg_doc,
45
+ _running_doc,
46
+ _all_fields_with_policy_doc,
47
+ _bucket_interval_doc,
48
+ _bucket_time_doc,
49
+ _bucket_units_doc,
50
+ _bucket_end_condition_doc,
51
+ _end_condition_per_group_doc,
52
+ _boundary_tick_bucket_doc,
53
+ _group_by_doc,
54
+ _groups_to_display_doc,
55
+ ],
56
+ add_self=True,
57
+ )
58
+ def agg(self: 'Source', aggs, *args, **kwargs) -> 'Source':
59
+ """
60
+ Applies composition of :ref:`otp.agg <aggregations_funcs>` aggregations
61
+
62
+ See Also
63
+ --------
64
+ | :ref:`Aggregations <aggregations_funcs>`
65
+ | **COMPUTE** OneTick event processor
66
+
67
+ Returns
68
+ -------
69
+ :py:class:`~onetick.py.Source`
70
+
71
+ Examples
72
+ --------
73
+
74
+ By default the whole data is aggregated:
75
+
76
+ >>> data = otp.Ticks(X=[1, 2, 3, 4])
77
+ >>> data = data.agg({'X_SUM': otp.agg.sum('X')})
78
+ >>> otp.run(data)
79
+ Time X_SUM
80
+ 0 2003-12-04 10
81
+
82
+ Multiple aggregations can be applied at the same time:
83
+
84
+ >>> data = otp.Ticks(X=[1, 2, 3, 4])
85
+ >>> data = data.agg({'X_SUM': otp.agg.sum('X'),
86
+ ... 'X_MEAN': otp.agg.average('X')})
87
+ >>> otp.run(data)
88
+ Time X_SUM X_MEAN
89
+ 0 2003-12-04 10 2.5
90
+
91
+ Aggregation can be used in running mode:
92
+
93
+ >>> data = otp.Ticks(X=[1, 2, 3, 4])
94
+ >>> data = data.agg({'CUM_SUM': otp.agg.sum('X')}, running=True)
95
+ >>> otp.run(data)
96
+ Time CUM_SUM
97
+ 0 2003-12-01 00:00:00.000 1
98
+ 1 2003-12-01 00:00:00.001 3
99
+ 2 2003-12-01 00:00:00.002 6
100
+ 3 2003-12-01 00:00:00.003 10
101
+
102
+ Aggregation can be split in buckets:
103
+
104
+ >>> data = otp.Ticks(X=[1, 2, 3, 4])
105
+ >>> data = data.agg({'X_SUM': otp.agg.sum('X')}, bucket_interval=2, bucket_units='ticks')
106
+ >>> otp.run(data)
107
+ Time X_SUM
108
+ 0 2003-12-01 00:00:00.001 3
109
+ 1 2003-12-01 00:00:00.003 7
110
+
111
+ Running aggregation can be used with buckets too. In this case (all_fields=False and running=True) output ticks
112
+ are created when a tick enters or leaves the sliding window (that's why for this example there are 8 output
113
+ ticks for 4 input ticks):
114
+
115
+ >>> data = otp.Ticks(X=[1, 2, 3, 4], offset=[0, 1000, 1500, 3600])
116
+ >>> data = data.agg(dict(X_MEAN=otp.agg.average("X"),
117
+ ... X_STD=otp.agg.stddev("X")),
118
+ ... running=True, bucket_interval=2)
119
+ >>> otp.run(data)
120
+ Time X_MEAN X_STD
121
+ 0 2003-12-01 00:00:00.000 1.0 0.000000
122
+ 1 2003-12-01 00:00:01.000 1.5 0.500000
123
+ 2 2003-12-01 00:00:01.500 2.0 0.816497
124
+ 3 2003-12-01 00:00:02.000 2.5 0.500000
125
+ 4 2003-12-01 00:00:03.000 3.0 0.000000
126
+ 5 2003-12-01 00:00:03.500 NaN NaN
127
+ 6 2003-12-01 00:00:03.600 4.0 0.000000
128
+ 7 2003-12-01 00:00:05.600 NaN NaN
129
+
130
+ By default, if you run aggregation with buckets and group_by, then a bucket will be taken first, and after that
131
+ grouping and aggregation will be performed:
132
+
133
+ >>> ticks = otp.Ticks(
134
+ ... {
135
+ ... 'QTY': [10, 2, 30, 4, 50],
136
+ ... 'TRADER': ['A', 'B', 'A', 'B', 'A']
137
+ ... }
138
+ ... )
139
+ >>>
140
+ >>> ticks = ticks.agg(
141
+ ... {'SUM_QTY': otp.agg.sum('QTY')}, group_by='TRADER',
142
+ ... bucket_interval=3, bucket_units='ticks',
143
+ ... running=True, all_fields=True,
144
+ ... )
145
+ >>>
146
+ >>> otp.run(ticks)
147
+ Time TRADER QTY SUM_QTY
148
+ 0 2003-12-01 00:00:00.000 A 10 10
149
+ 1 2003-12-01 00:00:00.001 B 2 2
150
+ 2 2003-12-01 00:00:00.002 A 30 40
151
+ 3 2003-12-01 00:00:00.003 B 4 6
152
+ 4 2003-12-01 00:00:00.004 A 50 80
153
+
154
+ In the row with index 4, the result of summing up the trades for trader "A" turned out to be 80, instead of 90.
155
+ We first took a bucket of 3 ticks, then within it took the group with trader "A" (2 ticks remained) and
156
+ added up the volumes.
157
+ To prevent this behaviour, and group ticks first, set parameter ``end_condition_per_group`` to True:
158
+
159
+ >>> ticks = otp.Ticks(
160
+ ... {
161
+ ... 'QTY': [10, 2, 30, 4, 50],
162
+ ... 'TRADER': ['A', 'B', 'A', 'B', 'A']
163
+ ... }
164
+ ... )
165
+ >>>
166
+ >>> ticks = ticks.agg(
167
+ ... {'SUM_QTY': otp.agg.sum('QTY')}, group_by='TRADER',
168
+ ... bucket_interval=3, bucket_units='ticks',
169
+ ... running=True, all_fields=True,
170
+ ... end_condition_per_group=True,
171
+ ... )
172
+ >>>
173
+ >>> otp.run(ticks)
174
+ Time TRADER QTY SUM_QTY
175
+ 0 2003-12-01 00:00:00.000 A 10 10
176
+ 1 2003-12-01 00:00:00.001 B 2 2
177
+ 2 2003-12-01 00:00:00.002 A 30 40
178
+ 3 2003-12-01 00:00:00.003 B 4 6
179
+ 4 2003-12-01 00:00:00.004 A 50 90
180
+
181
+ Tick aggregations and aggregations, which return more than one output column, could be also used.
182
+ Dict key set for an aggregation in ``aggs`` parameter will be used as prefix
183
+ for each output column of this aggregation.
184
+
185
+ >>> data = otp.Ticks(X=[1, 2, 3, 4], Y=[10, 20, 30, 40])
186
+ >>> data = data.agg({'X_SUM': otp.agg.sum('X'), 'X_FIRST': otp.agg.first_tick()})
187
+ >>> otp.run(data)
188
+ Time X_FIRST.X X_FIRST.Y X_SUM
189
+ 0 2003-12-04 1 10 10
190
+
191
+ These aggregations can be split in buckets too:
192
+
193
+ >>> data = otp.Ticks(X=[1, 2, 3, 4], Y=[10, 20, 30, 40])
194
+ >>> data = data.agg(
195
+ ... {'X_SUM': otp.agg.sum('X'), 'X_FIRST': otp.agg.first_tick()},
196
+ ... bucket_interval=2, bucket_units='ticks',
197
+ ... )
198
+ >>> otp.run(data)
199
+ Time X_FIRST.X X_FIRST.Y X_SUM
200
+ 0 2003-12-01 00:00:00.001 1 10 3
201
+ 1 2003-12-01 00:00:00.003 3 30 7
202
+
203
+ If all_fields=True an output tick is generated only for arrival events, but all attributes from the input tick
204
+ causing an arrival event are copied over to the output tick and the aggregation is added as another attribute:
205
+
206
+ >>> data = otp.Ticks(X=[1, 2, 3, 4], offset=[0, 1000, 1500, 3600])
207
+ >>> data = data.agg(dict(X_MEAN=otp.agg.average("X"),
208
+ ... X_STD=otp.agg.stddev("X")),
209
+ ... all_fields=True, running=True)
210
+ >>> otp.run(data)
211
+ Time X X_MEAN X_STD
212
+ 0 2003-12-01 00:00:00.000 1 1.0 0.000000
213
+ 1 2003-12-01 00:00:01.000 2 1.5 0.500000
214
+ 2 2003-12-01 00:00:01.500 3 2.0 0.816497
215
+ 3 2003-12-01 00:00:03.600 4 2.5 1.118034
216
+
217
+ ``all_fields`` parameter can be used when there is need to have all original fields in the output:
218
+
219
+ >>> ticks = otp.Ticks(X=[3, 4, 1, 2])
220
+ >>> data = ticks.agg(dict(X_MEAN=otp.agg.average("X"),
221
+ ... X_STD=otp.agg.stddev("X")),
222
+ ... all_fields=True)
223
+ >>> otp.run(data)
224
+ Time X X_MEAN X_STD
225
+ 0 2003-12-04 3 2.5 1.118034
226
+
227
+ There are different politics for ``all_fields`` parameter:
228
+
229
+ >>> data = ticks.agg(dict(X_MEAN=otp.agg.average("X"),
230
+ ... X_STD=otp.agg.stddev("X")),
231
+ ... all_fields="last")
232
+ >>> otp.run(data)
233
+ Time X X_MEAN X_STD
234
+ 0 2003-12-04 2 2.5 1.118034
235
+
236
+ For low/high policies the field selected as input is set this way:
237
+
238
+ >>> data = ticks.agg(dict(X_MEAN=otp.agg.average("X"),
239
+ ... X_STD=otp.agg.stddev("X")),
240
+ ... all_fields=otp.agg.low_tick(data["X"]))
241
+ >>> otp.run(data)
242
+ Time X X_MEAN X_STD
243
+ 0 2003-12-04 1 2.5 1.118034
244
+
245
+ Example of using 'flexible' buckets. Here every bucket consists of consecutive upticks.
246
+
247
+ >>> trades = otp.Ticks(PRICE=[194.65, 194.65, 194.65, 194.75, 194.75, 194.51, 194.70, 194.71, 194.75, 194.71])
248
+ >>> trades = trades.agg({'COUNT': otp.agg.count(),
249
+ ... 'FIRST_TIME': otp.agg.first('Time'),
250
+ ... 'LAST_TIME': otp.agg.last('Time')},
251
+ ... bucket_units='flexible',
252
+ ... bucket_end_condition=trades['PRICE'] < trades['PRICE'][-1])
253
+ >>> otp.run(trades)
254
+ Time COUNT FIRST_TIME LAST_TIME
255
+ 0 2003-12-01 00:00:00.005 5 2003-12-01 00:00:00.000 2003-12-01 00:00:00.004
256
+ 1 2003-12-01 00:00:00.009 4 2003-12-01 00:00:00.005 2003-12-01 00:00:00.008
257
+ 2 2003-12-04 00:00:00.000 1 2003-12-01 00:00:00.009 2003-12-01 00:00:00.009
258
+ """
259
+
260
+ aggs = aggs.copy()
261
+ result = self.copy()
262
+
263
+ what_to_aggregate = aggregations.compute(
264
+ *args,
265
+ **kwargs,
266
+ )
267
+
268
+ for name, ag in aggs.items():
269
+ what_to_aggregate.add(name, ag)
270
+
271
+ result = what_to_aggregate.apply(result)
272
+ result._add_table()
273
+
274
+ return result
275
+
276
+
277
+ # Aggregations copy
278
+ # we need this functions to store and collect documentation
279
+ # copy_method decorator will
280
+ # set docstring (will compare docstring of donor function and method docstring)
281
+ # apply same signature from donor function + self
282
+ # for mimic=True will apply agg function as is
283
+ @copy_method(aggregations.functions.high_tick)
284
+ def high(self, *args, **kwargs):
285
+ """
286
+ Examples
287
+ --------
288
+ >>> data = otp.Ticks(X=[1, 2, 3, 4], offset=[0, 1000, 1500, 3000])
289
+ >>> data = data.high(['X'], 2) # OTdirective: snippet-name: Aggregations.high tick;
290
+ >>> otp.run(data)
291
+ Time X
292
+ 0 2003-12-01 00:00:01.500 3
293
+ 1 2003-12-01 00:00:03.000 4
294
+ """
295
+ pass
296
+
297
+
298
+ @copy_method(aggregations.functions.low_tick)
299
+ def low(self, *args, **kwargs):
300
+ """
301
+ Examples
302
+ --------
303
+ >>> data = otp.Ticks(X=[1, 2, 3, 4], offset=[0, 1000, 1500, 3000])
304
+ >>> data = data.low(['X'],2) # OTdirective: snippet-name: Aggregations.low tick;
305
+ >>> otp.run(data)
306
+ Time X
307
+ 0 2003-12-01 00:00:00 1
308
+ 1 2003-12-01 00:00:01 2
309
+ """
310
+ pass
311
+
312
+
313
+ @copy_method(aggregations.functions.first_tick)
314
+ def first(self, *args, **kwargs):
315
+ """
316
+ Examples
317
+ --------
318
+
319
+ Get first tick:
320
+
321
+ >>> data = otp.Ticks(X=[1, 2, 3, 4])
322
+ >>> data = data.first() # OTdirective: snippet-name: Aggregations.first;
323
+ >>> otp.run(data)
324
+ Time X
325
+ 0 2003-12-01 1
326
+
327
+ Get first tick each day:
328
+
329
+ >>> data = otp.Ticks(X=[1, 2, 3, 4], offset=[otp.Day(0), otp.Day(0), otp.Day(2), otp.Day(2)])
330
+ >>> data = data.first(bucket_interval=1, bucket_units='days', bucket_time='start')
331
+ >>> otp.run(data)
332
+ Time X
333
+ 0 2003-12-01 1
334
+ 1 2003-12-03 3
335
+
336
+ Get first tick each day and set tick value for empty buckets:
337
+
338
+ >>> data = otp.Ticks(X=[1, 2, 3, 4], offset=[otp.Day(0), otp.Day(0), otp.Day(2), otp.Day(2)])
339
+ >>> data = data.first(bucket_interval=1, bucket_units='days', bucket_time='start', default_tick={'X': -1})
340
+ >>> otp.run(data)
341
+ Time X
342
+ 0 2003-12-01 1
343
+ 1 2003-12-02 -1
344
+ 2 2003-12-03 3
345
+ """
346
+ pass
347
+
348
+
349
+ @copy_method(aggregations.functions.last_tick)
350
+ def last(self, *args, **kwargs):
351
+ """
352
+ Examples
353
+ --------
354
+
355
+ Get last tick:
356
+
357
+ >>> data = otp.Ticks(X=[1, 2, 3, 4])
358
+ >>> data = data.last() # OTdirective: snippet-name: Aggregations.last;
359
+ >>> otp.run(data)
360
+ Time X
361
+ 0 2003-12-01 00:00:00.003 4
362
+
363
+ Get last tick each day:
364
+
365
+ >>> data = otp.Ticks(X=[1, 2, 3, 4], offset=[otp.Day(0), otp.Day(0), otp.Day(2), otp.Day(2)])
366
+ >>> data = data.last(bucket_interval=1, bucket_units='days', bucket_time='start')
367
+ >>> otp.run(data)
368
+ Time X
369
+ 0 2003-12-01 2
370
+ 1 2003-12-03 4
371
+
372
+ Get last tick each day and set tick value for empty buckets:
373
+
374
+ >>> data = otp.Ticks(X=[1, 2, 3, 4], offset=[otp.Day(0), otp.Day(0), otp.Day(2), otp.Day(2)])
375
+ >>> data = data.last(bucket_interval=1, bucket_units='days', bucket_time='start', default_tick={'X': -1})
376
+ >>> otp.run(data)
377
+ Time X
378
+ 0 2003-12-01 2
379
+ 1 2003-12-02 -1
380
+ 2 2003-12-03 4
381
+ """
382
+ pass
383
+
384
+
385
+ @copy_method(aggregations.functions.distinct, mimic=False)
386
+ def distinct(self: 'Source', *args, **kwargs):
387
+ """
388
+ Examples
389
+ --------
390
+ >>> data = otp.Ticks(dict(x=[1, 3, 1, 5, 3]))
391
+ >>> data = data.distinct('x') # OTdirective: snippet-name: Aggregations.distinct;
392
+ >>> otp.run(data)
393
+ Time x
394
+ 0 2003-12-04 1
395
+ 1 2003-12-04 3
396
+ 2 2003-12-04 5
397
+ """
398
+ if 'bucket_interval_units' in kwargs:
399
+ kwargs['bucket_units'] = kwargs.pop('bucket_interval_units')
400
+ agg = aggregations.functions.distinct(*args, **kwargs)
401
+ return agg.apply(self)
402
+
403
+
404
+ # mimic=False for backward compatibility
405
+ @copy_method(aggregations.functions.high_time, mimic=False, drop_examples=True)
406
+ def high_time(self: 'Source', *args, **kwargs):
407
+ """
408
+ Returns timestamp of tick with the highest value of input field
409
+
410
+ .. deprecated:: 1.14.5
411
+
412
+ Use :py:func:`.high_time` instead
413
+
414
+ See Also
415
+ --------
416
+ :py:func:`.high_time`
417
+
418
+ """
419
+ warnings.warn(
420
+ f"{self.__class__.__name__}.high_time deprecated. Use otp.agg.high_time instead",
421
+ FutureWarning,
422
+ stacklevel=2,
423
+ )
424
+ agg = aggregations.functions.high_time(*args, **kwargs)
425
+ return agg.apply(self, 'VALUE')
426
+
427
+
428
+ # mimic=False for backward compatibility
429
+ @copy_method(aggregations.functions.low_time, mimic=False, drop_examples=True)
430
+ def low_time(self: 'Source', *args, **kwargs):
431
+ """
432
+ Returns timestamp of tick with the lowest value of input field
433
+
434
+ .. deprecated:: 1.14.5
435
+
436
+ Use :py:func:`.low_time` instead
437
+
438
+ See Also
439
+ --------
440
+ :py:func:`.low_time`
441
+
442
+ """
443
+ warnings.warn(
444
+ f"{self.__class__.__name__}.low_time deprecated. Use otp.agg.low_time instead",
445
+ FutureWarning,
446
+ stacklevel=2,
447
+ )
448
+ agg = aggregations.functions.low_time(*args, **kwargs)
449
+ return agg.apply(self, 'VALUE')
450
+
451
+
452
+ @copy_method(aggregations.functions.ob_snapshot)
453
+ def ob_snapshot(self, *args, **kwargs):
454
+ """
455
+ Examples
456
+ --------
457
+ >>> data = otp.DataSource(db='SOME_DB', tick_type='PRL', symbols='AA') # doctest: +SKIP
458
+ >>> data = data.ob_snapshot(max_levels=1) # doctest: +SKIP
459
+ >>> otp.run(data) # doctest: +SKIP
460
+ Time PRICE UPDATE_TIME SIZE LEVEL BUY_SELL_FLAG
461
+ 0 2003-12-04 2.0 2003-12-01 00:00:00.003 6 1 1
462
+ 1 2003-12-04 5.0 2003-12-01 00:00:00.004 7 1 0
463
+ """
464
+ pass
465
+
466
+
467
+ @copy_method(aggregations.functions.ob_snapshot_wide)
468
+ def ob_snapshot_wide(self, *args, **kwargs):
469
+ """
470
+ Examples
471
+ --------
472
+ >>> data = otp.DataSource(db='SOME_DB', tick_type='PRL', symbols='AA') # doctest: +SKIP
473
+ >>> data = data.ob_snapshot_wide(max_levels=1) # doctest: +SKIP
474
+ >>> otp.run(data) # doctest: +SKIP
475
+ Time BID_PRICE BID_UPDATE_TIME BID_SIZE ASK_PRICE ASK_UPDATE_TIME ASK_SIZE LEVEL
476
+ 0 2003-12-03 5.0 2003-12-01 00:00:00.004 7 2.0 2003-12-01 00:00:00.003 6 1
477
+ """
478
+ pass
479
+
480
+
481
+ @copy_method(aggregations.functions.ob_snapshot_flat)
482
+ def ob_snapshot_flat(self, *args, **kwargs):
483
+ """
484
+ Examples
485
+ --------
486
+ >>> data = otp.DataSource(db='SOME_DB', tick_type='PRL', symbols='AA') # doctest: +SKIP
487
+ >>> data = data.ob_snapshot_flat(max_levels=1) # doctest: +SKIP
488
+ >>> otp.run(data) # doctest: +SKIP
489
+ Time BID_PRICE1 BID_UPDATE_TIME1 BID_SIZE1 ASK_PRICE1 ASK_UPDATE_TIME1 ASK_SIZE1
490
+ 0 2003-12-03 5.0 2003-12-01 00:00:00.004 7 2.0 2003-12-01 00:00:00.003 6
491
+ """
492
+ pass
493
+
494
+
495
+ @copy_method(aggregations.functions.ob_summary)
496
+ def ob_summary(self, *args, **kwargs):
497
+ """
498
+ Examples
499
+ --------
500
+ >>> data = otp.DataSource(db='SOME_DB', tick_type='PRL', symbols='AA') # doctest: +SKIP
501
+ >>> data = data.ob_summary(max_levels=1) # doctest: +SKIP
502
+ >>> otp.run(data) # doctest: +SKIP
503
+ Time BID_PRICE BID_SIZE BID_VWAP BEST_BID_PRICE WORST_BID_SIZE NUM_BID_LEVELS ASK_SIZE\
504
+ ASK_VWAP BEST_ASK_PRICE WORST_ASK_PRICE NUM_ASK_LEVELS
505
+ 0 2003-12-04 NaN 7 5.0 5.0 NaN 1 6\
506
+ 2.0 2.0 2.0 1
507
+ """
508
+ pass
509
+
510
+
511
+ @copy_method(aggregations.functions.ob_size)
512
+ def ob_size(self, *args, **kwargs):
513
+ """
514
+ Examples
515
+ --------
516
+ >>> data = otp.DataSource(db='SOME_DB', tick_type='PRL', symbols='AA') # doctest: +SKIP
517
+ >>> data = data.ob_size(max_levels=10) # doctest: +SKIP
518
+ >>> otp.run(data) # doctest: +SKIP
519
+ Time ASK_VALUE BID_VALUE
520
+ 0 2003-12-01 84800 64500
521
+ """
522
+ pass
523
+
524
+
525
+ @copy_method(aggregations.functions.ob_vwap)
526
+ def ob_vwap(self, *args, **kwargs):
527
+ """
528
+ Examples
529
+ --------
530
+ >>> data = otp.DataSource(db='SOME_DB', tick_type='PRL', symbols='AA') # doctest: +SKIP
531
+ >>> data = data.ob_vwap(max_levels=10) # doctest: +SKIP
532
+ >>> otp.run(data) # doctest: +SKIP
533
+ Time ASK_VALUE BID_VALUE
534
+ 0 2003-12-01 23.313 23.20848
535
+ """
536
+ pass
537
+
538
+
539
+ @copy_method(aggregations.functions.ob_num_levels)
540
+ def ob_num_levels(self, *args, **kwargs):
541
+ """
542
+ Examples
543
+ --------
544
+ >>> data = otp.DataSource(db='SOME_DB', tick_type='PRL', symbols='AA') # doctest: +SKIP
545
+ >>> data = data.ob_num_levels() # doctest: +SKIP
546
+ >>> otp.run(data) # doctest: +SKIP
547
+ Time ASK_VALUE BID_VALUE
548
+ 0 2003-12-01 248 67
549
+ """
550
+ pass
551
+
552
+
553
+ @copy_method(aggregations.functions.ranking)
554
+ def ranking(self: 'Source', *args, **kwargs):
555
+ # method implementation is copied by decorator
556
+ pass
557
+
558
+
559
+ @copy_method(aggregations.functions.percentile)
560
+ def percentile(self: 'Source', *args, **kwargs):
561
+ # method implementation is copied by decorator
562
+ pass
563
+
564
+
565
+ @copy_method(aggregations.functions.find_value_for_percentile)
566
+ def find_value_for_percentile(self: 'Source', *args, **kwargs):
567
+ # method implementation is copied by decorator
568
+ pass
569
+
570
+
571
+ @copy_method(aggregations.functions.exp_w_average)
572
+ def exp_w_average(self: 'Source', *args, **kwargs):
573
+ # method implementation is copied by decorator
574
+ pass
575
+
576
+
577
+ @copy_method(aggregations.functions.exp_tw_average)
578
+ def exp_tw_average(self: 'Source', *args, **kwargs):
579
+ # method implementation is copied by decorator
580
+ pass
581
+
582
+
583
+ @copy_method(aggregations.functions.standardized_moment)
584
+ def standardized_moment(self: 'Source', *args, **kwargs):
585
+ # method implementation is copied by decorator
586
+ pass
587
+
588
+
589
+ @copy_method(aggregations.functions.portfolio_price)
590
+ def portfolio_price(self: 'Source', *args, **kwargs):
591
+ # method implementation is copied by decorator
592
+ pass
593
+
594
+
595
+ @copy_method(aggregations.functions.multi_portfolio_price)
596
+ def multi_portfolio_price(self: 'Source', *args, **kwargs):
597
+ # method implementation is copied by decorator
598
+ pass
599
+
600
+
601
+ @copy_method(aggregations.functions.return_ep)
602
+ def return_ep(self: 'Source', *args, **kwargs):
603
+ # method implementation is copied by decorator
604
+ pass
605
+
606
+
607
+ @copy_method(aggregations.functions.implied_vol)
608
+ def implied_vol(self: 'Source', *args, **kwargs):
609
+ # method implementation is copied by decorator
610
+ pass
611
+
612
+
613
+ @copy_method(aggregations.functions.linear_regression)
614
+ def linear_regression(self: 'Source', *args, **kwargs):
615
+ # method implementation is copied by decorator
616
+ pass
617
+
618
+
619
+ @inplace_operation
620
+ def process_by_group(
621
+ self: 'Source', process_source_func, group_by=None, source_name=None, num_threads=None, inplace=False
622
+ ) -> Union['Source', Tuple['Source', ...], None]:
623
+ """
624
+ Groups data by ``group_by`` and run ``process_source_func`` for each group and merge outputs for every group.
625
+ Note ``process_source_func`` will be converted to Onetick object and passed to query,
626
+ that means that python callable will be called only once.
627
+
628
+ Parameters
629
+ ----------
630
+ process_source_func: callable
631
+ ``process_source_func`` should take :class:`Source` apply necessary logic and return it
632
+ or tuple of :class:`Source` in this case all of them should have a common root that is the
633
+ input :class:`Source`.
634
+ The number of sources returned by this method is the same as the number of sources
635
+ returned by ``process_source_func``.
636
+ group_by: list
637
+ A list of field names to group input ticks by.
638
+
639
+ If group_by is None then no group_by fields are defined
640
+ and logic of ``process_source_func`` is applied to all input ticks
641
+ at once
642
+ source_name: str
643
+ A name for the source that represents all of group_by sources. Can be passed here or as a name
644
+ of the inner sources; if passed by both ways, should be consistent
645
+ num_threads: int
646
+ If specified and not zero, turns on asynchronous processing mode
647
+ and specifies number of threads to be used for processing input ticks.
648
+ If this parameter is not specified or zero, then input ticks are processed synchronously.
649
+ inplace: bool
650
+ If True - nothing will be returned and changes will be applied to current query
651
+ otherwise changes query will be returned.
652
+ Error is raised if ``inplace`` is set to True
653
+ and multiple sources returned by ``process_source_func``.
654
+
655
+ Returns
656
+ -------
657
+ :class:`Source`, Tuple[:class:`Source`] or None:
658
+
659
+ See also
660
+ --------
661
+ **GROUP_BY** OneTick event processor
662
+
663
+ Examples
664
+ --------
665
+
666
+ >>> # OTdirective: snippet-name: Arrange.group.single output;
667
+ >>> d = otp.Ticks(X=[1, 1, 2, 2],
668
+ ... Y=[1, 2, 3, 4])
669
+ >>>
670
+ >>> def func(source):
671
+ ... return source.first()
672
+ >>>
673
+ >>> res = d.process_by_group(func, group_by=['X'])
674
+ >>> otp.run(res)[["X", "Y"]]
675
+ X Y
676
+ 0 1 1
677
+ 1 2 3
678
+
679
+ Set asynchronous processing:
680
+
681
+ >>> res = d.process_by_group(func, group_by=['X'], num_threads=2)
682
+ >>> otp.run(res)[['X', 'Y']]
683
+ X Y
684
+ 0 1 1
685
+ 1 2 3
686
+
687
+ Return multiple outputs, each with unique grouping logic:
688
+
689
+ >>> d = otp.Ticks(X=[1, 1, 2, 2],
690
+ ... Y=[1, 2, 1, 3])
691
+ >>>
692
+ >>> def func(source):
693
+ ... source['Z'] = source['X']
694
+ ... source2 = source.copy()
695
+ ... source = source.first()
696
+ ... source2 = source2.last()
697
+ ... return source, source2
698
+ >>> # OTdirective: snippet-name: Arrange.group.multiple output;
699
+ >>> res1, res2 = d.process_by_group(func, group_by=['Y'])
700
+ >>> df1 = otp.run(res1)
701
+ >>> df2 = otp.run(res2)
702
+ >>> df1[['X', 'Y', 'Z']]
703
+ X Y Z
704
+ 0 1 1 1
705
+ 1 1 2 1
706
+ 2 2 3 2
707
+ >>> df2[['X', 'Y', 'Z']] # OTdirective: skip-snippet:;
708
+ X Y Z
709
+ 0 1 2 1
710
+ 1 2 1 2
711
+ 2 2 3 2
712
+ """
713
+
714
+ if group_by is None:
715
+ group_by = []
716
+
717
+ if inplace:
718
+ main_source = self
719
+ else:
720
+ main_source = self.copy()
721
+
722
+ input_schema = main_source.columns(skip_meta_fields=True)
723
+ for field in group_by:
724
+ if field not in input_schema:
725
+ raise ValueError(f"Group by field name {field} not present in input source schema")
726
+
727
+ process_source_root = otp.DataSource(tick_type="ANY", schema_policy="manual", schema=input_schema)
728
+ if source_name:
729
+ process_source_root.set_name(source_name)
730
+ process_sources = process_source_func(process_source_root)
731
+
732
+ if isinstance(process_sources, otp.Source):
733
+ # returned one source
734
+ process_sources = [process_sources]
735
+ elif len(process_sources) == 1:
736
+ # returned one source as an iterable
737
+ pass
738
+ else:
739
+ # returned multiple sources
740
+ if inplace:
741
+ raise ValueError("Cannot use inplace=True with multi-source processing function!")
742
+
743
+ num_source = 0
744
+ for process_source in process_sources:
745
+ output_schema = process_source.columns(skip_meta_fields=True)
746
+
747
+ if process_source.get_name():
748
+ if not process_source_root.get_name():
749
+ process_source_root.set_name(process_source.get_name())
750
+ if process_source_root.get_name() != process_source.get_name():
751
+ warnings.warn(
752
+ "Different strings passed as names for the root source used in "
753
+ f"process_by_group: '{process_source.get_name()}' "
754
+ f"and '{process_source_root.get_name()}'"
755
+ )
756
+
757
+ # removing key fields from output schema since they will be
758
+ # added by the GROUP_BY EP
759
+ process_source.drop([field for field in group_by if field in output_schema], inplace=True)
760
+ process_source.sink(otq.Passthrough().node_name(f"OUT_{num_source}"))
761
+ process_source_root.node().add_rules(process_source.node().copy_rules())
762
+ main_source._merge_tmp_otq(process_source)
763
+ num_source += 1
764
+
765
+ query_name = process_source_root._store_in_tmp_otq(
766
+ main_source._tmp_otq, operation_suffix="group_by", add_passthrough=False,
767
+ # set default symbol, even if it's not set by user, symbol's value doesn't matter in this case
768
+ symbols=otp.config.get('default_symbol', 'ANY')
769
+ )
770
+ process_path = f'THIS::{query_name}'
771
+ num_outputs = len(process_sources)
772
+
773
+ # we shouldn't set named outputs if GROUP_BY EP has only one output due to onetick behaviour
774
+ if num_outputs == 1:
775
+ outputs = ""
776
+ else:
777
+ outputs = ",".join([f"OUT_{i}" for i in range(0, num_outputs)])
778
+
779
+ kwargs = {}
780
+ if num_threads is not None:
781
+ if num_threads < 0:
782
+ raise ValueError("Parameter 'num_threads' can't be negative.")
783
+ kwargs['num_threads'] = num_threads
784
+
785
+ main_source.sink(otq.GroupBy(key_fields=",".join(group_by), query_name=process_path, outputs=outputs, **kwargs))
786
+
787
+ output_sources = []
788
+ for num_output in range(0, num_outputs):
789
+ if num_outputs == 1 and inplace:
790
+ output_source = main_source
791
+ else:
792
+ output_source = main_source.copy()
793
+
794
+ if num_outputs > 1:
795
+ output_source.node().out_pin(f"OUT_{num_output}")
796
+
797
+ # setting schema after processing
798
+ output_schema = process_sources[num_output].columns(skip_meta_fields=True)
799
+ for field in group_by:
800
+ output_schema[field] = input_schema[field]
801
+ for field, field_type in output_schema.items():
802
+ output_source.schema[field] = field_type
803
+ output_source = output_source[[field for field in output_schema]]
804
+ output_source._merge_tmp_otq(main_source)
805
+ output_sources.append(output_source)
806
+
807
+ if num_outputs == 1:
808
+ return output_sources[0]
809
+ else:
810
+ return tuple(output_sources)