onetick-py 1.177.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. locator_parser/__init__.py +0 -0
  2. locator_parser/acl.py +73 -0
  3. locator_parser/actions.py +262 -0
  4. locator_parser/common.py +368 -0
  5. locator_parser/io.py +43 -0
  6. locator_parser/locator.py +150 -0
  7. onetick/__init__.py +101 -0
  8. onetick/doc_utilities/__init__.py +3 -0
  9. onetick/doc_utilities/napoleon.py +40 -0
  10. onetick/doc_utilities/ot_doctest.py +140 -0
  11. onetick/doc_utilities/snippets.py +279 -0
  12. onetick/lib/__init__.py +4 -0
  13. onetick/lib/instance.py +141 -0
  14. onetick/py/__init__.py +293 -0
  15. onetick/py/_stack_info.py +89 -0
  16. onetick/py/_version.py +2 -0
  17. onetick/py/aggregations/__init__.py +11 -0
  18. onetick/py/aggregations/_base.py +648 -0
  19. onetick/py/aggregations/_docs.py +948 -0
  20. onetick/py/aggregations/compute.py +286 -0
  21. onetick/py/aggregations/functions.py +2216 -0
  22. onetick/py/aggregations/generic.py +104 -0
  23. onetick/py/aggregations/high_low.py +80 -0
  24. onetick/py/aggregations/num_distinct.py +83 -0
  25. onetick/py/aggregations/order_book.py +501 -0
  26. onetick/py/aggregations/other.py +1014 -0
  27. onetick/py/backports.py +26 -0
  28. onetick/py/cache.py +374 -0
  29. onetick/py/callback/__init__.py +5 -0
  30. onetick/py/callback/callback.py +276 -0
  31. onetick/py/callback/callbacks.py +131 -0
  32. onetick/py/compatibility.py +798 -0
  33. onetick/py/configuration.py +771 -0
  34. onetick/py/core/__init__.py +0 -0
  35. onetick/py/core/_csv_inspector.py +93 -0
  36. onetick/py/core/_internal/__init__.py +0 -0
  37. onetick/py/core/_internal/_manually_bound_value.py +6 -0
  38. onetick/py/core/_internal/_nodes_history.py +250 -0
  39. onetick/py/core/_internal/_op_utils/__init__.py +0 -0
  40. onetick/py/core/_internal/_op_utils/every_operand.py +9 -0
  41. onetick/py/core/_internal/_op_utils/is_const.py +10 -0
  42. onetick/py/core/_internal/_per_tick_scripts/tick_list_sort_template.script +121 -0
  43. onetick/py/core/_internal/_proxy_node.py +140 -0
  44. onetick/py/core/_internal/_state_objects.py +2312 -0
  45. onetick/py/core/_internal/_state_vars.py +93 -0
  46. onetick/py/core/_source/__init__.py +0 -0
  47. onetick/py/core/_source/_symbol_param.py +95 -0
  48. onetick/py/core/_source/schema.py +97 -0
  49. onetick/py/core/_source/source_methods/__init__.py +0 -0
  50. onetick/py/core/_source/source_methods/aggregations.py +809 -0
  51. onetick/py/core/_source/source_methods/applyers.py +296 -0
  52. onetick/py/core/_source/source_methods/columns.py +141 -0
  53. onetick/py/core/_source/source_methods/data_quality.py +301 -0
  54. onetick/py/core/_source/source_methods/debugs.py +272 -0
  55. onetick/py/core/_source/source_methods/drops.py +120 -0
  56. onetick/py/core/_source/source_methods/fields.py +619 -0
  57. onetick/py/core/_source/source_methods/filters.py +1002 -0
  58. onetick/py/core/_source/source_methods/joins.py +1413 -0
  59. onetick/py/core/_source/source_methods/merges.py +605 -0
  60. onetick/py/core/_source/source_methods/misc.py +1455 -0
  61. onetick/py/core/_source/source_methods/pandases.py +155 -0
  62. onetick/py/core/_source/source_methods/renames.py +356 -0
  63. onetick/py/core/_source/source_methods/sorts.py +183 -0
  64. onetick/py/core/_source/source_methods/switches.py +142 -0
  65. onetick/py/core/_source/source_methods/symbols.py +117 -0
  66. onetick/py/core/_source/source_methods/times.py +627 -0
  67. onetick/py/core/_source/source_methods/writes.py +986 -0
  68. onetick/py/core/_source/symbol.py +205 -0
  69. onetick/py/core/_source/tmp_otq.py +222 -0
  70. onetick/py/core/column.py +209 -0
  71. onetick/py/core/column_operations/__init__.py +0 -0
  72. onetick/py/core/column_operations/_methods/__init__.py +4 -0
  73. onetick/py/core/column_operations/_methods/_internal.py +28 -0
  74. onetick/py/core/column_operations/_methods/conversions.py +216 -0
  75. onetick/py/core/column_operations/_methods/methods.py +292 -0
  76. onetick/py/core/column_operations/_methods/op_types.py +160 -0
  77. onetick/py/core/column_operations/accessors/__init__.py +0 -0
  78. onetick/py/core/column_operations/accessors/_accessor.py +28 -0
  79. onetick/py/core/column_operations/accessors/decimal_accessor.py +104 -0
  80. onetick/py/core/column_operations/accessors/dt_accessor.py +537 -0
  81. onetick/py/core/column_operations/accessors/float_accessor.py +184 -0
  82. onetick/py/core/column_operations/accessors/str_accessor.py +1367 -0
  83. onetick/py/core/column_operations/base.py +1121 -0
  84. onetick/py/core/cut_builder.py +150 -0
  85. onetick/py/core/db_constants.py +20 -0
  86. onetick/py/core/eval_query.py +245 -0
  87. onetick/py/core/lambda_object.py +441 -0
  88. onetick/py/core/multi_output_source.py +232 -0
  89. onetick/py/core/per_tick_script.py +2256 -0
  90. onetick/py/core/query_inspector.py +464 -0
  91. onetick/py/core/source.py +1744 -0
  92. onetick/py/db/__init__.py +2 -0
  93. onetick/py/db/_inspection.py +1128 -0
  94. onetick/py/db/db.py +1327 -0
  95. onetick/py/db/utils.py +64 -0
  96. onetick/py/docs/__init__.py +0 -0
  97. onetick/py/docs/docstring_parser.py +112 -0
  98. onetick/py/docs/utils.py +81 -0
  99. onetick/py/functions.py +2398 -0
  100. onetick/py/license.py +190 -0
  101. onetick/py/log.py +88 -0
  102. onetick/py/math.py +935 -0
  103. onetick/py/misc.py +470 -0
  104. onetick/py/oqd/__init__.py +22 -0
  105. onetick/py/oqd/eps.py +1195 -0
  106. onetick/py/oqd/sources.py +325 -0
  107. onetick/py/otq.py +216 -0
  108. onetick/py/pyomd_mock.py +47 -0
  109. onetick/py/run.py +916 -0
  110. onetick/py/servers.py +173 -0
  111. onetick/py/session.py +1347 -0
  112. onetick/py/sources/__init__.py +19 -0
  113. onetick/py/sources/cache.py +167 -0
  114. onetick/py/sources/common.py +128 -0
  115. onetick/py/sources/csv.py +642 -0
  116. onetick/py/sources/custom.py +85 -0
  117. onetick/py/sources/data_file.py +305 -0
  118. onetick/py/sources/data_source.py +1045 -0
  119. onetick/py/sources/empty.py +94 -0
  120. onetick/py/sources/odbc.py +337 -0
  121. onetick/py/sources/order_book.py +271 -0
  122. onetick/py/sources/parquet.py +168 -0
  123. onetick/py/sources/pit.py +191 -0
  124. onetick/py/sources/query.py +495 -0
  125. onetick/py/sources/snapshots.py +419 -0
  126. onetick/py/sources/split_query_output_by_symbol.py +198 -0
  127. onetick/py/sources/symbology_mapping.py +123 -0
  128. onetick/py/sources/symbols.py +374 -0
  129. onetick/py/sources/ticks.py +825 -0
  130. onetick/py/sql.py +70 -0
  131. onetick/py/state.py +251 -0
  132. onetick/py/types.py +2131 -0
  133. onetick/py/utils/__init__.py +70 -0
  134. onetick/py/utils/acl.py +93 -0
  135. onetick/py/utils/config.py +186 -0
  136. onetick/py/utils/default.py +49 -0
  137. onetick/py/utils/file.py +38 -0
  138. onetick/py/utils/helpers.py +76 -0
  139. onetick/py/utils/locator.py +94 -0
  140. onetick/py/utils/perf.py +498 -0
  141. onetick/py/utils/query.py +49 -0
  142. onetick/py/utils/render.py +1374 -0
  143. onetick/py/utils/script.py +244 -0
  144. onetick/py/utils/temp.py +471 -0
  145. onetick/py/utils/types.py +120 -0
  146. onetick/py/utils/tz.py +84 -0
  147. onetick_py-1.177.0.dist-info/METADATA +137 -0
  148. onetick_py-1.177.0.dist-info/RECORD +152 -0
  149. onetick_py-1.177.0.dist-info/WHEEL +5 -0
  150. onetick_py-1.177.0.dist-info/entry_points.txt +2 -0
  151. onetick_py-1.177.0.dist-info/licenses/LICENSE +21 -0
  152. onetick_py-1.177.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,1455 @@
1
+ import functools
2
+ import re
3
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union
4
+ from onetick.py.backports import Literal
5
+
6
+ import onetick.py as otp
7
+ from onetick.py import types as ott
8
+ from onetick.py import utils
9
+ from onetick.py.core.column import _Column
10
+ from onetick.py.otq import otq
11
+ from onetick.py.compatibility import (
12
+ is_ob_virtual_prl_and_show_full_detail_supported,
13
+ is_per_tick_script_boolean_problem,
14
+ )
15
+ from onetick.py.aggregations._docs import copy_signature
16
+
17
+ if TYPE_CHECKING:
18
+ from onetick.py.core.source import Source
19
+
20
+
21
+ def inplace_operation(method):
22
+ """Decorator that adds the `inplace` parameter and logic according to this
23
+ flag. inplace=True means that method modifies an object, otherwise it copies
24
+ the object firstly, modifies copy and returns the copy.
25
+ """
26
+
27
+ @functools.wraps(method)
28
+ def _inner(self, *args, inplace=False, **kwargs):
29
+ kwargs['inplace'] = inplace
30
+ if inplace:
31
+ method(self, *args, **kwargs)
32
+ return None
33
+ else:
34
+ obj = self.copy()
35
+ return method(obj, *args, **kwargs)
36
+
37
+ return _inner
38
+
39
+
40
+ def _columns_names_regex(
41
+ self: 'Source', objs: Tuple[Union[_Column, str]], drop: bool = False
42
+ ) -> Tuple[List[str], bool]:
43
+ """
44
+ We can't be sure python Source has consistent columns cache, because sinking complex event processors
45
+ can change columns unpredictable, so if user will specify regex as a param, we will pass regex
46
+ as an onetick's param, but pass or delete all matched columns from python Source cache.
47
+
48
+ Parameters
49
+ ----------
50
+ objs:
51
+ Tuple of _Columns or string to pass or drop. String can be regex.
52
+ drop: bool
53
+ To drop columns from python schema or not.
54
+
55
+ Returns
56
+ -------
57
+ items_to_passthrough:
58
+ Items to pass to Passthrough as `field` parameter.
59
+ regex:
60
+ Value to pass to Passthrough as `use_regex` parameter.
61
+ """
62
+ def is_regex_string(obj):
63
+ return isinstance(obj, str) and any(c in r"*+?\:[]{}()^$" for c in obj)
64
+
65
+ # if any object from the list is a regexp, then all other objects will be treated like regexps too
66
+ regex = any(is_regex_string(obj) for obj in objs)
67
+
68
+ items_to_passthrough = []
69
+ names_of_columns = []
70
+ for obj in objs:
71
+ if not isinstance(obj, (str, _Column)):
72
+ raise TypeError(f"It is not supported to select or delete item '{obj}' of type '{type(obj)}'")
73
+ if regex:
74
+ # if column object or non-regex string is specified in regex mode,
75
+ # then we assume that the user requested that exact column to be dropped
76
+ if isinstance(obj, _Column):
77
+ names_of_columns.append(obj.name)
78
+ obj = f'^{obj.name}$'
79
+ elif not is_regex_string(obj):
80
+ names_of_columns.append(obj)
81
+ obj = f'^{obj}$'
82
+ else:
83
+ names_of_columns.extend(col for col in self.columns() if re.search(obj, col))
84
+ items_to_passthrough.append(obj)
85
+ else:
86
+ name = obj.name if isinstance(obj, _Column) else obj
87
+ items_to_passthrough.append(name)
88
+ names_of_columns.append(name)
89
+
90
+ # remove duplications and meta_fields
91
+ names_of_columns: set[str] = set(names_of_columns) - set(self.__class__.meta_fields) # type: ignore[no-redef]
92
+ # TODO: we definitely have the same logic of checking columns somewhere else too, need to refactor
93
+ for item in names_of_columns:
94
+ if item not in self.__dict__ or not isinstance(self.__dict__[item], _Column):
95
+ raise AttributeError(f"There is no '{item}' column")
96
+ if drop:
97
+ for item in names_of_columns:
98
+ del self.__dict__[item]
99
+ return items_to_passthrough, regex
100
+
101
+
102
+ @inplace_operation
103
+ def pause(self: 'Source', delay, busy_waiting=False, where=None, inplace=False) -> Optional['Source']:
104
+ """
105
+ Pauses processing of each tick for number of milliseconds specified via ``delay`` expression.
106
+
107
+ Parameters
108
+ ----------
109
+ delay: int or :py:class:`onetick.py.Operation`
110
+ Integer number or OneTick expression used to calculate the delay.
111
+ Delay is in milliseconds.
112
+ Note that number can't be negative.
113
+ busy_waiting: bool
114
+ If True then delay is done via busy loop (consuming CPU time).
115
+ where: :py:class:`onetick.py.Operation`
116
+ Expression to select ticks for which processing will be paused.
117
+ By default, all ticks are selected.
118
+ inplace: bool
119
+ The flag controls whether operation should be applied inplace or not.
120
+ If ``inplace=True``, then it returns nothing.
121
+ Otherwise method returns a new modified object.
122
+
123
+ See also
124
+ --------
125
+ **PAUSE** OneTick event processor
126
+
127
+ Returns
128
+ -------
129
+ :class:`Source` or ``None``
130
+
131
+ Examples
132
+ --------
133
+
134
+ Pause every tick for 100 milliseconds (0.1 second).
135
+
136
+ >>> data = otp.Tick(A=1)
137
+ >>> data = data.pause(100)
138
+
139
+ Set the ``delay`` with expression and use ``where`` parameter to pause conditionally:
140
+
141
+ >>> data = otp.Ticks(A=[-1, 1, 2])
142
+ >>> data = data.pause(data['A'] * 100, where=data['A'] > 0)
143
+ """
144
+ if not isinstance(delay, int):
145
+ delay = str(delay)
146
+ elif delay < 0:
147
+ raise ValueError("Parameter 'delay' can't be negative")
148
+ where = '' if where is None else str(where)
149
+ self.sink(
150
+ otq.Pause(
151
+ delay=delay,
152
+ busy_waiting=busy_waiting,
153
+ where=where,
154
+ )
155
+ )
156
+ return self
157
+
158
+
159
+ @inplace_operation
160
+ def insert_tick(
161
+ self: 'Source',
162
+ fields=None,
163
+ where=None,
164
+ preserve_input_ticks=True,
165
+ num_ticks_to_insert=1,
166
+ insert_before=True,
167
+ inplace=False,
168
+ ) -> Optional['Source']:
169
+ """
170
+ Insert tick.
171
+
172
+ Parameters
173
+ ----------
174
+ fields: dict of str to :py:class:`onetick.py.Operation`
175
+ Mapping of field names to some expressions or values.
176
+ These fields in inserted ticks will be set to corresponding values or results of expressions.
177
+ If field is presented in input tick, but not set in ``fields`` dict,
178
+ then the value of the field will be copied from input tick to inserted tick.
179
+ If parameter ``fields`` is not set at all, then values for inserted ticks' fields
180
+ will be default values for fields' types from input ticks (0 for integers etc.).
181
+ where: :py:class:`onetick.py.Operation`
182
+ Expression to select ticks near which the new ticks will be inserted.
183
+ By default, all ticks are selected.
184
+ preserve_input_ticks: bool
185
+ A switch controlling whether input ticks have to be preserved in output time series or not.
186
+ While the former case results in fields of input ticks to be present in the output time series
187
+ together with those defined by the ``fields`` parameter,
188
+ the latter case results in only defined fields to be present.
189
+ If a field of the input time series is defined in the ``fields`` parameter,
190
+ the defined value takes precedence.
191
+ num_ticks_to_insert: int
192
+ Number of ticks to insert.
193
+ insert_before: bool
194
+ Insert tick before each input tick or after.
195
+ inplace: bool
196
+ The flag controls whether operation should be applied inplace or not.
197
+ If ``inplace=True``, then it returns nothing.
198
+ Otherwise method returns a new modified object.
199
+
200
+ See also
201
+ --------
202
+ **INSERT_TICK** OneTick event processor
203
+
204
+ Returns
205
+ -------
206
+ :class:`Source` or ``None``
207
+
208
+ Examples
209
+ --------
210
+
211
+ Insert tick before each tick with default type values.
212
+
213
+ >>> data = otp.Tick(A=1)
214
+ >>> data = data.insert_tick()
215
+ >>> otp.run(data)
216
+ Time A
217
+ 0 2003-12-01 0
218
+ 1 2003-12-01 1
219
+
220
+ Insert tick before each tick with field `A` copied from input tick
221
+ and field `B` set to specified value.
222
+
223
+ >>> data = otp.Tick(A=1)
224
+ >>> data = data.insert_tick(fields={'B': 'b'})
225
+ >>> otp.run(data)
226
+ Time B A
227
+ 0 2003-12-01 b 1
228
+ 1 2003-12-01 1
229
+
230
+ Insert two ticks only after first tick.
231
+
232
+ >>> data = otp.Ticks(A=[1, 2, 3])
233
+ >>> data = data.insert_tick(where=data['A'] == 1,
234
+ ... insert_before=False,
235
+ ... num_ticks_to_insert=2)
236
+ >>> otp.run(data)
237
+ Time A
238
+ 0 2003-12-01 00:00:00.000 1
239
+ 1 2003-12-01 00:00:00.000 0
240
+ 2 2003-12-01 00:00:00.000 0
241
+ 3 2003-12-01 00:00:00.001 2
242
+ 4 2003-12-01 00:00:00.002 3
243
+ """
244
+ if not isinstance(num_ticks_to_insert, int) or num_ticks_to_insert <= 0:
245
+ raise ValueError("Parameter 'num_ticks_to_insert' must be a positive integer")
246
+ if not preserve_input_ticks and not fields:
247
+ raise ValueError("Parameter 'fields' must be set if 'preserve_input_ticks' is False")
248
+ where = '' if where is None else str(where)
249
+
250
+ fields = fields or {}
251
+ update_schema = {}
252
+ for field, value in fields.items():
253
+ dtype = ott.get_object_type(value)
254
+ if field not in self.schema:
255
+ update_schema[field] = dtype
256
+ elif dtype is not self.schema[field]:
257
+ raise ValueError(f"Incompatible types for field '{field}': {self.schema[field]} --> {dtype}")
258
+ dtype = ott.type2str(dtype)
259
+ if isinstance(value, type):
260
+ value = ott.default_by_type(value)
261
+ value = ott.value2str(value)
262
+ fields[field] = (dtype, value)
263
+ fields = ','.join(
264
+ f'{field} {dtype}={value}' if value else f'{field} {dtype}' for field, (dtype, value) in fields.items()
265
+ )
266
+
267
+ self.sink(
268
+ otq.InsertTick(
269
+ fields=fields,
270
+ where=where,
271
+ preserve_input_ticks=preserve_input_ticks,
272
+ num_ticks_to_insert=num_ticks_to_insert,
273
+ insert_before=insert_before,
274
+ )
275
+ )
276
+ if preserve_input_ticks:
277
+ self.table(inplace=True, strict=False, **update_schema)
278
+ else:
279
+ self.table(inplace=True, strict=True, **update_schema)
280
+ return self
281
+
282
+
283
+ @inplace_operation
284
+ def insert_at_end(
285
+ self: 'Source',
286
+ *,
287
+ propagate_ticks: bool = True,
288
+ delimiter_name: str = 'AT_END',
289
+ inplace: bool = False,
290
+ ) -> Optional['Source']:
291
+ """
292
+ This function adds a field ``delimiter_name``,
293
+ which is set to zero for all inbound ticks
294
+ and set to 1 for an additional tick that is generated when the data ends.
295
+
296
+ The timestamp of the additional tick is set to the query end time.
297
+ The values of all fields from the input schema of additional tick are set to default values for each type.
298
+
299
+ Parameters
300
+ ----------
301
+ propagate_ticks: bool
302
+ If True (default) this function returns all input ticks and an additionally generated tick,
303
+ otherwise it returns only the last generated tick.
304
+ delimiter_name: str
305
+ The name of the delimiter field to add.
306
+ inplace: bool
307
+ The flag controls whether operation should be applied inplace or not.
308
+ If ``inplace=True``, then it returns nothing.
309
+ Otherwise method returns a new modified object.
310
+
311
+ See also
312
+ --------
313
+ **INSERT_AT_END** OneTick event processor
314
+
315
+ Returns
316
+ -------
317
+ :class:`Source` or ``None``
318
+
319
+ Examples
320
+ --------
321
+ Insert tick at the end of the stream:
322
+
323
+ >>> data = otp.Ticks(A=[1, 2, 3])
324
+ >>> data = data.insert_at_end()
325
+ >>> otp.run(data)
326
+ Time A AT_END
327
+ 0 2003-12-01 00:00:00.000 1 0
328
+ 1 2003-12-01 00:00:00.001 2 0
329
+ 2 2003-12-01 00:00:00.002 3 0
330
+ 3 2003-12-04 00:00:00.000 0 1
331
+
332
+ The name of the added field can be changed:
333
+
334
+ >>> data = otp.Ticks(A=[1, 2, 3])
335
+ >>> data = data.insert_at_end(delimiter_name='LAST_TICK')
336
+ >>> otp.run(data)
337
+ Time A LAST_TICK
338
+ 0 2003-12-01 00:00:00.000 1 0
339
+ 1 2003-12-01 00:00:00.001 2 0
340
+ 2 2003-12-01 00:00:00.002 3 0
341
+ 3 2003-12-04 00:00:00.000 0 1
342
+
343
+ If parameter ``propagate_ticks`` is set to False, then only the last tick is returned:
344
+
345
+ >>> data = otp.Ticks(A=[1, 2, 3])
346
+ >>> data = data.insert_at_end(propagate_ticks=False)
347
+ >>> otp.run(data)
348
+ Time A AT_END
349
+ 0 2003-12-04 0 1
350
+ """
351
+ if not hasattr(otq, 'InsertAtEnd'):
352
+ raise RuntimeError("Function insert_at_end() is not available on this OneTick API version")
353
+ if delimiter_name in self.schema:
354
+ raise ValueError(f"Field '{delimiter_name}' is already in schema")
355
+
356
+ self.sink(
357
+ otq.InsertAtEnd(
358
+ propagate_ticks=propagate_ticks,
359
+ delimiter_name=delimiter_name,
360
+ )
361
+ )
362
+ self.schema.update(**{delimiter_name: int})
363
+ return self
364
+
365
+
366
+ @inplace_operation
367
+ def transpose(
368
+ self: 'Source',
369
+ inplace: bool = False,
370
+ direction: Literal['rows', 'columns'] = 'rows',
371
+ n: Optional[int] = None,
372
+ ) -> Optional['Source']:
373
+ """
374
+ Data transposing.
375
+ The main idea is joining many ticks into one or splitting one tick to many.
376
+
377
+ Parameters
378
+ ----------
379
+ inplace: bool, default=False
380
+ if `True` method will modify current object,
381
+ otherwise it will return modified copy of the object.
382
+ direction: 'rows', 'columns', default='rows'
383
+ - `rows`: join certain input ticks (depending on other parameters) with preceding ones.
384
+ Fields of each tick will be added to the output tick and their names will be suffixed
385
+ with **_K** where **K** is the positional number of tick (starting from 1) in reverse order.
386
+ So fields of current tick will be suffixed with **_1**, fields of previous tick will be
387
+ suffixed with **_2** and so on.
388
+ - `columns`: the operation is opposite to `rows`. It splits each input tick to several
389
+ output ticks. Each input tick must have fields with names suffixed with **_K**
390
+ where **K** is the positional number of tick (starting from 1) in reverse order.
391
+ n: Optional[int], default=None
392
+ must be specified only if ``direction`` is 'rows'.
393
+ Joins every **n** number of ticks with **n-1** preceding ticks.
394
+
395
+ Returns
396
+ -------
397
+ If ``inplace`` parameter is `True` method will return `None`,
398
+ otherwise it will return modified copy of the object.
399
+
400
+ See also
401
+ --------
402
+ **TRANSPOSE** OneTick event processor
403
+
404
+ Examples
405
+ --------
406
+ Merging two ticks into one.
407
+
408
+ >>> data = otp.Ticks(dict(A=[1, 2],
409
+ ... B=[3, 4]))
410
+ >>> data = data.transpose(direction='rows', n=2) # OTdirective: skip-snippet:;
411
+ >>> otp.run(data)
412
+ Time TIMESTAMP_1 A_1 B_1 TIMESTAMP_2 A_2 B_2
413
+ 0 2003-12-01 00:00:00.001 2003-12-01 00:00:00.001 2 4 2003-12-01 1 3
414
+
415
+ And splitting them back into two.
416
+
417
+ >>> data = data.transpose(direction='columns') # OTdirective: skip-snippet:;
418
+ >>> otp.run(data)
419
+ Time A B
420
+ 0 2003-12-01 00:00:00.000 1 3
421
+ 1 2003-12-01 00:00:00.001 2 4
422
+ """
423
+
424
+ direction_map = {'rows': 'ROWS_TO_COLUMNS', 'columns': 'COLUMNS_TO_ROWS'}
425
+ n: Union[str, int] = '' if n is None else n # type: ignore[no-redef]
426
+
427
+ self.sink(otq.Transpose(direction=direction_map[direction], key_constraint_values=n))
428
+ # TODO: we should change source's schema after transposing
429
+ return self
430
+
431
+
432
+ def cache(
433
+ self: 'Source',
434
+ cache_name: str,
435
+ delete_if_exists: bool = True,
436
+ inheritability: bool = True,
437
+ otq_params: Union[dict, None] = None,
438
+ time_granularity: int = 0,
439
+ time_granularity_units: Optional[str] = None,
440
+ timezone: str = "",
441
+ time_intervals_to_cache: Optional[List[tuple]] = None,
442
+ allow_delete_to_everyone: bool = False,
443
+ allow_update_to_everyone: bool = False,
444
+ allow_search_to_everyone: bool = True,
445
+ cache_expiration_interval: int = 0,
446
+ start: Optional[ott.datetime] = None,
447
+ end: Optional[ott.datetime] = None,
448
+ read_mode: str = "automatic",
449
+ update_cache: bool = True,
450
+ tick_type: str = "ANY",
451
+ symbol: Optional[str] = None,
452
+ db: Optional[str] = None,
453
+ ) -> 'Source':
454
+ """
455
+ Create cache from query and :py:class:`onetick.py.ReadCache` for created cache.
456
+
457
+ Cache will be created only for current session.
458
+
459
+ By default, if cache with specified name exists, it will be deleted and recreated.
460
+ You can change this behaviour via ``delete_if_exists`` parameter.
461
+
462
+ Parameters
463
+ ----------
464
+ cache_name: str
465
+ Name of the cache to be deleted.
466
+ delete_if_exists: bool
467
+ If set to ``True`` (default), a check will be made to detect the existence of a cache
468
+ with the specified name. Cache will be deleted and recreated only if it exists.
469
+ If set to ``False``, if cache exists it won't be deleted and recreated.
470
+ inheritability: bool
471
+ Indicates whether results can be obtained by combining time intervals that were cached with intervals
472
+ freshly computed to obtain results for larger intervals.
473
+ otq_params: dict
474
+ OTQ params of the query to be cached.
475
+ time_granularity: int
476
+ Value N for seconds/days/months granularity means that start and end time of the query have to be on N
477
+ second/day/month boundaries relative to start of the day/month/year.
478
+ This doesn't affect the frequency of data within the cache, just the start and end dates.
479
+ time_granularity_units: str, None
480
+ Units used in ``time_granularity`` parameter. Possible values: 'none', 'days', 'months', 'seconds' or None.
481
+ timezone: str
482
+ Timezone of the query to be cached.
483
+ time_intervals_to_cache: List[tuple]
484
+ List of tuples with start and end times in ``[(<start_time_1>, <end_time_1>), ...]`` format,
485
+ where ``<start_time>`` and ``<end_time>`` should be one of these:
486
+
487
+ * string in ``YYYYMMDDhhmmss[.msec]`` format.
488
+ * :py:class:`datetime.datetime`
489
+ * :py:class:`onetick.py.types.datetime`
490
+
491
+ If specified only these time intervals can be cached. Ignored if ``inheritability=True``.
492
+ If you try to make a query outside defined interval, error will be raised.
493
+ allow_delete_to_everyone: bool
494
+ When set to ``True`` everyone is allowed to delete the cache.
495
+ allow_update_to_everyone: bool
496
+ When set to ``True`` everyone is allowed to update the cache.
497
+ allow_search_to_everyone: bool
498
+ When set to ``True`` everyone is allowed to read the cached data.
499
+ cache_expiration_interval: int
500
+ If set to a non-zero value determines the periodicity of cache clearing, in seconds.
501
+ The cache will be cleared every X seconds, triggering new query executions when data is requested.
502
+ start: :py:class:`otp.datetime <onetick.py.datetime>`
503
+ Start time for cache query. By default, the start time of the query will be used.
504
+ end: :py:class:`otp.datetime <onetick.py.datetime>`
505
+ End time for cache query. By default, the end time of the query will be used.
506
+ read_mode: str
507
+ Mode of querying cache. One of these:
508
+
509
+ * ``cache_only`` - only cached results are returned and queries are not performed.
510
+ * ``query_only`` - the query is run irrespective of whether the result is already available in the cache.
511
+ * ``automatic`` (default) - perform the query if the data is not found in the cache.
512
+ update_cache: bool
513
+ If set to ``True``, updates the cached data if ``read_mode=query_only`` or if ``read_mode=automatic`` and
514
+ the result data not found in the cache. Otherwise, the cache remains unchanged.
515
+ tick_type: str
516
+ Tick type.
517
+ symbol: str, list of str, list of otq.Symbol, :py:class:`onetick.py.Source`, :pandas:`pandas.DataFrame`, optional
518
+ ``symbols`` parameter of ``otp.run()``.
519
+ db: str
520
+ Database.
521
+
522
+ See also
523
+ --------
524
+ | :py:func:`onetick.py.create_cache`
525
+ | :py:class:`onetick.py.ReadCache`
526
+
527
+ Examples
528
+ --------
529
+ Simple usage
530
+
531
+ >>> src = otp.DataSource("COMMON", tick_type="TRD", symbols="AAPL")
532
+ >>> data = src.cache(
533
+ ... cache_name="some_cache",
534
+ ... tick_type="TRD", symbol="SYM", db="LOCAL",
535
+ ... )
536
+ >>> df = otp.run(data) # doctest: +SKIP
537
+ """
538
+ from onetick.py.cache import create_cache, delete_cache, modify_cache_config
539
+ from onetick.py.sources import ReadCache
540
+
541
+ cache_exists = True
542
+
543
+ if delete_if_exists:
544
+ try:
545
+ modify_cache_config(cache_name, "TEST_PARAM", "TEST_VALUE")
546
+ except Exception as exc:
547
+ if "There is no cache" in str(exc):
548
+ cache_exists = False
549
+
550
+ if cache_exists and delete_if_exists:
551
+ delete_cache(cache_name)
552
+ cache_exists = False
553
+
554
+ if not cache_exists:
555
+ create_cache(
556
+ cache_name=cache_name,
557
+ query=self,
558
+ inheritability=inheritability,
559
+ otq_params=otq_params,
560
+ time_granularity=time_granularity,
561
+ time_granularity_units=time_granularity_units,
562
+ timezone=timezone,
563
+ time_intervals_to_cache=time_intervals_to_cache,
564
+ allow_delete_to_everyone=allow_delete_to_everyone,
565
+ allow_update_to_everyone=allow_update_to_everyone,
566
+ allow_search_to_everyone=allow_search_to_everyone,
567
+ cache_expiration_interval=cache_expiration_interval,
568
+ tick_type=tick_type,
569
+ symbol=symbol,
570
+ db=db,
571
+ )
572
+
573
+ return ReadCache(
574
+ cache_name=cache_name,
575
+ start=start if start is not None else utils.adaptive,
576
+ end=end if end is not None else utils.adaptive,
577
+ read_mode=read_mode,
578
+ update_cache=update_cache,
579
+ tick_type=tick_type,
580
+ symbol=symbol if symbol is not None else utils.adaptive,
581
+ db=db if db is not None else utils.adaptive_to_default,
582
+ )
583
+
584
+
585
+ @inplace_operation
586
+ def pnl_realized(
587
+ self: 'Source',
588
+ computation_method: str = 'fifo',
589
+ output_field_name: str = 'PNL_REALIZED',
590
+ size_field: Union[str, _Column] = 'SIZE',
591
+ price_field: Union[str, _Column] = 'PRICE',
592
+ buy_sell_flag_field: Union[str, _Column] = 'BUY_SELL_FLAG',
593
+ inplace=False,
594
+ ) -> Optional['Source']:
595
+ """
596
+ Computes the realized Profit and Loss (**PNL**) on each tick and is applicable to both scenarios,
597
+ whether selling after buying or buying after selling.
598
+
599
+ Parameters
600
+ ----------
601
+ computation_method: str
602
+ This parameter determines the approach used to calculate the realized Profit and Loss (**PnL**).
603
+
604
+ Possible options are:
605
+
606
+ * ``fifo`` - Stands for 'First-In-First-Out,' is used to calculate Profit and Loss (**PnL**)
607
+ based on the principle that the first trading positions bought are the first ones to be sold,
608
+ or conversely, the first trading positions sold are the first ones to be bought.
609
+
610
+ output_field_name: str
611
+ This parameter defines the name of the output field.
612
+
613
+ Default: **PNL_REALIZED**.
614
+
615
+ size_field: str, :py:class:`otp.Column <onetick.py.Column>`
616
+ The name of the field with size, default is **SIZE**.
617
+ price_field: str, :py:class:`otp.Column <onetick.py.Column>`
618
+ The name of the field with price, default is **PRICE**.
619
+ buy_sell_flag_field: str, :py:class:`otp.Column <onetick.py.Column>`
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
+
624
+
625
+ See also
626
+ --------
627
+ **PNL_REALIZED** OneTick event processor
628
+
629
+ Examples
630
+ --------
631
+ Let's generate some data:
632
+
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
688
+ """
689
+ if computation_method not in ['fifo']:
690
+ raise ValueError(
691
+ f"Parameter 'computation_method' has incorrect value: '{computation_method}', "
692
+ f"should be one of these: 'fifo'."
693
+ )
694
+
695
+ for field in (size_field, price_field, buy_sell_flag_field):
696
+ if str(field) not in self.schema:
697
+ raise ValueError(f'Field {field} is not in schema')
698
+
699
+ # PNL_REALIZED doesn't support choosing input fields, so we workaround by renaming fields
700
+ restore_fields = {}
701
+ added_fields = []
702
+ for default_field, field in (('SIZE', size_field), ('PRICE', price_field), ('BUY_SELL_FLAG', buy_sell_flag_field)):
703
+ if field != default_field:
704
+ if default_field in self.schema:
705
+ tmp_field_name = f'__TMP__{field}__TMP__'
706
+ self[tmp_field_name] = self[default_field]
707
+ restore_fields[default_field] = self[tmp_field_name]
708
+ self[default_field] = self[field]
709
+ added_fields.append(default_field)
710
+
711
+ if output_field_name in self.schema:
712
+ raise ValueError(f'Field {output_field_name} is already in schema')
713
+
714
+ computation_method = computation_method.upper()
715
+ self.sink(otq.PnlRealized(computation_method=computation_method, output_field_name=output_field_name))
716
+ self.schema[output_field_name] = float
717
+
718
+ if added_fields:
719
+ self.drop(added_fields, inplace=True)
720
+ if restore_fields:
721
+ # restore fields from temporary fields and delete temporary fields
722
+ self.add_fields(restore_fields)
723
+ self.drop(list(restore_fields.values()), inplace=True)
724
+
725
+ return self
726
+
727
+
728
+ @inplace_operation
729
+ def execute(self: 'Source', *operations, inplace=False) -> Optional['Source']:
730
+ """
731
+ Execute operations without returning their values.
732
+ Some operations in onetick.py can be used to modify the state of some object
733
+ (tick sequences mostly) and in that case user may not want to save the result of the
734
+ operation to column.
735
+
736
+ Parameters
737
+ ----------
738
+ operations : list of :py:class:`~onetick.py.Operation`
739
+ operations to execute.
740
+ inplace : bool
741
+ The flag controls whether operation should be applied inplace or not.
742
+ If ``inplace=True``, then it returns nothing. Otherwise method returns a new modified
743
+ object.
744
+
745
+ Returns
746
+ -------
747
+ :class:`Source` or ``None``
748
+
749
+ See Also
750
+ --------
751
+ **EXECUTE_EXPRESSIONS** OneTick event processor
752
+
753
+ Examples
754
+ --------
755
+ >>> data = otp.Tick(A=1)
756
+ >>> data.state_vars['SET'] = otp.state.tick_set('oldest', 'A')
757
+ >>> data = data.execute(data.state_vars['SET'].erase(A=1))
758
+ """
759
+ if not operations:
760
+ raise ValueError('At least one operation must be specified in execute() method')
761
+ op_str = ';'.join(map(str, operations))
762
+ self.sink(otq.ExecuteExpressions(op_str))
763
+ return self
764
+
765
+
766
+ @inplace_operation
767
+ def fillna(self: 'Source', value=None, columns=None, inplace=False) -> Optional['Source']:
768
+ """
769
+ Replace NaN values in floating point type fields with the ``value``.
770
+
771
+ Parameters
772
+ ----------
773
+ value : float, :py:class:`~onetick.py.Operation`
774
+ The value to replace NaN.
775
+ If not specified then the value from the previous tick will be used.
776
+ columns: list
777
+ List of strings with column names or :py:class:`~onetick.py.Column` objects.
778
+ Only the values in specified columns will be replaced.
779
+ By default the values in all floating point type fields will be replaced.
780
+ inplace : bool
781
+ The flag controls whether operation should be applied inplace or not.
782
+ If ``inplace=True``, then it returns nothing. Otherwise method returns a new modified
783
+ object.
784
+
785
+ Returns
786
+ -------
787
+ :class:`Source` or ``None``
788
+
789
+ See Also
790
+ --------
791
+ :py:meth:`onetick.py.Operation.fillna`
792
+
793
+ Examples
794
+ --------
795
+
796
+ By default, the value of the previous tick is used as a value to replace NaN
797
+ (for the first tick the previous value do not exist, so it will still be NaN):
798
+
799
+ >>> data = otp.Ticks({'A': [0, 1, 2, 3], 'B': [otp.nan, 2.2, otp.nan, 3.3]})
800
+ >>> data = data.fillna()
801
+ >>> otp.run(data)
802
+ Time A B
803
+ 0 2003-12-01 00:00:00.000 0 NaN
804
+ 1 2003-12-01 00:00:00.001 1 2.2
805
+ 2 2003-12-01 00:00:00.002 2 2.2
806
+ 3 2003-12-01 00:00:00.003 3 3.3
807
+
808
+ The value can also be a constant:
809
+
810
+ >>> data = otp.Ticks({'A': [0, 1, 2, 3], 'B': [otp.nan, 2.2, otp.nan, 3.3]})
811
+ >>> data = data.fillna(777)
812
+ >>> otp.run(data)
813
+ Time A B
814
+ 0 2003-12-01 00:00:00.000 0 777.0
815
+ 1 2003-12-01 00:00:00.001 1 2.2
816
+ 2 2003-12-01 00:00:00.002 2 777.0
817
+ 3 2003-12-01 00:00:00.003 3 3.3
818
+
819
+ :py:class:`~onetick.py.Operation` objects can also be used as a ``value``:
820
+
821
+ >>> data = otp.Ticks({'A': [0, 1, 2, 3], 'B': [otp.nan, 2.2, otp.nan, 3.3]})
822
+ >>> data = data.fillna(data['A'])
823
+ >>> otp.run(data)
824
+ Time A B
825
+ 0 2003-12-01 00:00:00.000 0 0.0
826
+ 1 2003-12-01 00:00:00.001 1 2.2
827
+ 2 2003-12-01 00:00:00.002 2 2.0
828
+ 3 2003-12-01 00:00:00.003 3 3.3
829
+
830
+ Parameter ``columns`` can be used to specify the columns where values will be replaced:
831
+
832
+ .. testcode::
833
+ :skipif: is_per_tick_script_boolean_problem()
834
+
835
+ data = otp.Ticks({'A': [0, 1, 2, 3], 'B': [otp.nan, 2.2, otp.nan, 3.3], 'C': [otp.nan, 2.2, otp.nan, 3.3]})
836
+ data = data.fillna(columns=['B'])
837
+ df = otp.run(data)
838
+ print(df)
839
+
840
+ .. testoutput::
841
+
842
+ Time A B C
843
+ 0 2003-12-01 00:00:00.000 0 NaN NaN
844
+ 1 2003-12-01 00:00:00.001 1 2.2 2.2
845
+ 2 2003-12-01 00:00:00.002 2 2.2 NaN
846
+ 3 2003-12-01 00:00:00.003 3 3.3 3.3
847
+ """
848
+ if columns:
849
+ for column in columns:
850
+ if column not in self.schema:
851
+ raise ValueError(f"Column '{column}' is not in schema")
852
+ if self.schema[column] is not float:
853
+ raise TypeError(f"Column '{column}' doesn't have float type")
854
+ columns = list(map(str, columns))
855
+ no_columns = columns is None
856
+
857
+ if value is not None:
858
+ if isinstance(value, int):
859
+ value = float(value)
860
+ if isinstance(value, otp.Operation) and value.dtype is int:
861
+ value = value.astype(float)
862
+ dtype = ott.get_object_type(value)
863
+ if dtype is not float:
864
+ raise ValueError(f"The type of parameter 'value' must be float, not {dtype}")
865
+
866
+ def fun(tick):
867
+ for field in otp.tick_descriptor_fields():
868
+ if (field.get_type() == 'double'
869
+ and tick.get_double_value(field.get_name()) == otp.nan
870
+ and (no_columns or field.get_name().isin(*columns))):
871
+ tick.set_double_value(field.get_name(), value)
872
+ else:
873
+ # deque may have already been created by previous call of fillna() method
874
+ if '__OTP_FILLNA_DEQUE__' not in self.state_vars:
875
+ self.state_vars['__OTP_FILLNA_DEQUE__'] = otp.state.tick_deque(scope='branch')
876
+
877
+ def fun(tick):
878
+ i = otp.static(0)
879
+ prev_tick = otp.tick_deque_tick()
880
+ # first tick doesn't have the previous tick, so skipping it
881
+ if i > 0:
882
+ # get the previous tick from the deque
883
+ tick.state_vars['__OTP_FILLNA_DEQUE__'].get_tick(0, prev_tick)
884
+ # replace value in all double fields with the value of the previous tick
885
+ for field in otp.tick_descriptor_fields():
886
+ if (field.get_type() == 'double'
887
+ and tick.get_double_value(field.get_name()) == otp.nan
888
+ and (no_columns or field.get_name().isin(*columns))):
889
+ tick.set_double_value(field.get_name(),
890
+ prev_tick.get_double_value(field.get_name()))
891
+ # clear the deque and add the current tick to it
892
+ tick.state_vars['__OTP_FILLNA_DEQUE__'].clear()
893
+ tick.state_vars['__OTP_FILLNA_DEQUE__'].push_back(tick)
894
+ i = i + 1
895
+
896
+ return self.script(fun, inplace=inplace)
897
+
898
+
899
+ @inplace_operation
900
+ def mkt_activity(self: 'Source', calendar_name=None, inplace=False) -> Optional['Source']:
901
+ """
902
+ Adds a string field named **MKT_ACTIVITY** to each tick in the input tick stream.
903
+
904
+ The value of this field is set to the union of session flags that apply for the security at the time of the tick,
905
+ as specified in the calendar sections of the reference database (see Reference Database Guide).
906
+
907
+ Session flags may differ between databases,
908
+ but the following letters have reserved meaning: L - half day, H - holiday, W - weekend, R - regular.
909
+
910
+ The calendar can either be specified explicitly by name
911
+ (the *CALENDAR* sections of the reference database are assumed to contain a calendar with such a name),
912
+ or default to the security- or exchange-level calendars for the queried symbol
913
+ (the *SYMBOL_CALENDAR* or *EXCH_CALENDAR* sections of the reference database).
914
+
915
+ The latter case requires a non-zero symbol date to be specified for queried symbols
916
+ (see parameter ``symbol_date`` in :py:func:`otp.run <onetick.py.run>`).
917
+
918
+ Parameters
919
+ ----------
920
+ calendar_name : str, :py:class:`~onetick.py.Column`
921
+ The calendar name to choose for the respective calendar from the *CALENDAR* sections of the reference database.
922
+ It can be a string constant or the name of the field with per-tick calendar name.
923
+
924
+ When this parameter is not specified,
925
+ default security- or exchange-level calendars configured for the queried database and symbol are used
926
+ (but symbol date must be specified in this case).
927
+ inplace : bool
928
+ The flag controls whether operation should be applied inplace or not.
929
+ If ``inplace=True``, then it returns nothing. Otherwise method returns a new modified
930
+ object.
931
+
932
+ Note
933
+ ----
934
+ When applying :py:meth:`mkt_activity` to aggregated data,
935
+ please take into account that session flags may change during the aggregation bucket.
936
+ The field **MKT_ACTIVITY**, in this case, will represent the session flags at the time assigned to the bucket,
937
+ which may be different from the session flags at some times during the bucket.
938
+
939
+ Returns
940
+ -------
941
+ :class:`Source` or ``None``
942
+
943
+ See Also
944
+ --------
945
+ **MKT_ACTIVITY** OneTick event processor
946
+
947
+ Examples
948
+ --------
949
+
950
+ By default, security- or exchange-level calendars configured for the queried database and symbol are used
951
+ (but symbol date must be specified in this case):
952
+
953
+ >>> data = otp.DataSource(...) # doctest: +SKIP
954
+ >>> data = data.mkt_activity() # doctest: +SKIP
955
+ >>> otp.run(data, date=otp.date(2022, 1, 1), symbol_date=otp.date(2022, 1, 1)) # doctest: +SKIP
956
+
957
+ Otherwise, parameter ``calendar_name`` must be specified:
958
+
959
+ >>> data = otp.DataSource(...) # doctest: +SKIP
960
+ >>> data = data.mkt_activity(calendar_name='WNY') # doctest: +SKIP
961
+ >>> otp.run(data, date=otp.date(2022, 1, 1)) # doctest: +SKIP
962
+
963
+ Parameter ``calendar_name`` can also be specified as a column.
964
+ In this case calendar name can be different for each tick:
965
+
966
+ >>> data = otp.DataSource(...) # doctest: +SKIP
967
+ >>> data = data.mkt_activity(calendar_name=data['CALENDAR_NAME']) # doctest: +SKIP
968
+ >>> otp.run(data, date=otp.date(2022, 1, 1)) # doctest: +SKIP
969
+
970
+ In this example you can see how market activity status is changing during the days.
971
+ We are getting first and last tick of the group each time the type of market activity is changed.
972
+ You can see regular trades (R) from 9:30 to 16:00, and a holiday (H) on 2018-02-07.
973
+
974
+ >>> data = otp.DataSource('TRAIN_A_PRL_TRD', tick_type='TRD', symbols='MSFT') # doctest: +SKIP
975
+ >>> data = data.mkt_activity('FRED') # doctest: +SKIP
976
+ >>> data = data[['PRICE', 'SIZE', 'MKT_ACTIVITY']] # doctest: +SKIP
977
+ >>> first = data.first(1, bucket_interval=(data['MKT_ACTIVITY'] != data['MKT_ACTIVITY'][-1])) # doctest: +SKIP
978
+ >>> last = data.last(1, bucket_interval=(data['MKT_ACTIVITY'] != data['MKT_ACTIVITY'][-1])) # doctest: +SKIP
979
+ >>> data = otp.merge([first, last]) # doctest: +SKIP
980
+ >>> df = otp.run(data, # doctest: +SKIP
981
+ ... start=otp.dt(2018, 2, 1), end=otp.dt(2018, 2, 9),
982
+ ... symbol_date=otp.dt(2018, 2, 1), timezone='EST5EDT')
983
+ >>> df[['Time', 'MKT_ACTIVITY']] # doctest: +SKIP
984
+ Time MKT_ACTIVITY
985
+ 0 2018-02-01 01:31:44.466
986
+ 1 2018-02-01 09:29:59.996
987
+ 2 2018-02-01 09:30:00.225 R
988
+ 3 2018-02-01 15:59:58.857 R
989
+ 4 2018-02-01 16:00:01.858
990
+ 5 2018-02-02 09:29:50.366
991
+ 6 2018-02-02 09:30:01.847 R
992
+ 7 2018-02-02 15:59:59.829 R
993
+ 8 2018-02-02 16:00:01.782
994
+ 9 2018-02-05 09:29:43.084
995
+ 10 2018-02-05 09:30:00.301 R
996
+ 11 2018-02-05 15:59:59.974 R
997
+ 12 2018-02-05 16:00:02.438
998
+ 13 2018-02-06 09:29:27.279
999
+ 14 2018-02-06 09:30:00.045 R
1000
+ 15 2018-02-06 15:59:59.903 R
1001
+ 16 2018-02-06 16:01:03.524
1002
+ 17 2018-02-07 09:29:56.739
1003
+ 18 2018-02-07 09:30:00.365 H
1004
+ 19 2018-02-07 15:59:59.940 H
1005
+ 20 2018-02-07 16:00:00.187
1006
+ 21 2018-02-08 09:29:28.446
1007
+ 22 2018-02-08 09:30:00.658 F
1008
+ 23 2018-02-08 15:59:59.564 F
1009
+ 24 2018-02-08 16:00:02.355
1010
+ 25 2018-02-08 19:59:57.061
1011
+ """
1012
+ if calendar_name is None:
1013
+ calendar_name = ''
1014
+ if isinstance(calendar_name, str):
1015
+ calendar_field_name = ''
1016
+ elif isinstance(calendar_name, _Column):
1017
+ calendar_field_name = str(calendar_name)
1018
+ calendar_name = ''
1019
+ else:
1020
+ raise ValueError(f"Unsupported type for parameter 'calendar_name': {type(calendar_name)}")
1021
+ self.sink(
1022
+ otq.MktActivity(calendar_name=calendar_name, calendar_field_name=calendar_field_name)
1023
+ )
1024
+ self.schema['MKT_ACTIVITY'] = str
1025
+ return self
1026
+
1027
+
1028
+ @inplace_operation
1029
+ def book_diff(self: 'Source', include_initial_book: bool = False, inplace=False) -> Optional['Source']:
1030
+ """
1031
+ Performs book diffing for every pair of consecutive ticks, each representing a flat book of a fixed depth.
1032
+
1033
+ This method can be thought to be an operation, opposite to :py:meth:`~onetick.py.Source.ob_snapshot_flat`.
1034
+ Every input tick, different from the previous one, results in a series of output PRL ticks to be propagated,
1035
+ each carrying information about a level deletion, addition or update.
1036
+
1037
+ The input of this method is a time series of flat book ticks,
1038
+ just like the result of :py:meth:`~onetick.py.Source.ob_snapshot_flat`.
1039
+ More precisely, for a fixed depth N of the flat book,
1040
+ input ticks should carry the fields BID_<FIELD_NAME>K and ASK_<FIELD_NAME>K,
1041
+ where 1 <= K <= N and <FIELD_NAME> ranges over a specific set of fields F,
1042
+ among which PRICE and SIZE are mandatory.
1043
+
1044
+ The output of this method is a time series of PRL ticks,
1045
+ carrying information about a level deletion, addition, or update.
1046
+ Each tick carries the fields from the above-mentioned set F,
1047
+ plus BUY_SELL_FLAG, RECORD_TYPE, TICK_STATUS, and DELETED_TIME.
1048
+ BUY_SELL_FLAG carries the side (0 - bid, 1 - ask), the rest are for internal use.
1049
+
1050
+ Parameters
1051
+ ----------
1052
+ include_initial_book : bool
1053
+ This method treats the first tick as an "initial state" of the book.
1054
+ If this parameter is set to True, then this initial tick will also be in the output.
1055
+ inplace : bool
1056
+ The flag controls whether operation should be applied inplace or not.
1057
+ If ``inplace=True``, then it returns nothing. Otherwise method returns a new modified
1058
+ object.
1059
+
1060
+ Note
1061
+ ----
1062
+ Tick descriptor changes are not allowed in this event processor.
1063
+
1064
+ Returns
1065
+ -------
1066
+ :class:`Source` or ``None``
1067
+
1068
+ See Also
1069
+ --------
1070
+ **BOOK_DIFF** OneTick event processor
1071
+
1072
+ Examples
1073
+ --------
1074
+
1075
+ First let's get flat order book snapshot from the database (top level each 12 hours):
1076
+
1077
+ >>> data = otp.DataSource('TRAIN_A_PRL_TRD', tick_type='PRL', symbols='MSFT') # doctest: +SKIP
1078
+ >>> flat = data.ob_snapshot_flat(max_levels=1, bucket_interval=otp.Hour(12)) # doctest: +SKIP
1079
+ >>> otp.run(flat, date=otp.dt(2018, 2, 1)) # doctest: +SKIP
1080
+ Time BID_PRICE1 BID_SIZE1 BID_UPDATE_TIME1 ASK_PRICE1 ASK_SIZE1 ASK_UPDATE_TIME1
1081
+ 0 2018-02-01 12:00:00 95.53 1400 2018-02-01 11:59:59.797 95.54 200 2018-02-01 11:59:59.978
1082
+ 1 2018-02-02 00:00:00 94.52 100 2018-02-01 19:57:02.502 94.90 250 2018-02-01 18:35:38.543
1083
+
1084
+ Then we can apply ``book_diff`` method to the result to get the PRL ticks again:
1085
+
1086
+ >>> diff = flat.book_diff() # doctest: +SKIP
1087
+ >>> otp.run(diff, date=otp.dt(2018, 2, 1)) # doctest: +SKIP
1088
+ Time PRICE SIZE UPDATE_TIME BUY_SELL_FLAG RECORD_TYPE TICK_STATUS DELETED_TIME
1089
+ 0 2018-02-02 95.53 0 2018-02-01 11:59:59.797 0 R 0 1969-12-31 19:00:00
1090
+ 1 2018-02-02 94.52 100 2018-02-01 19:57:02.502 0 R 0 1969-12-31 19:00:00
1091
+ 2 2018-02-02 94.90 250 2018-02-01 18:35:38.543 1 R 0 1969-12-31 19:00:00
1092
+ 3 2018-02-02 95.54 0 2018-02-01 11:59:59.978 1 R 0 1969-12-31 19:00:00
1093
+
1094
+ By default the first tick in the query range is not included,
1095
+ use parameter ``include_initial_book`` to include it:
1096
+
1097
+ >>> diff = flat.book_diff(include_initial_book=True) # doctest: +SKIP
1098
+ >>> otp.run(diff, date=otp.dt(2018, 2, 1)) # doctest: +SKIP
1099
+ Time PRICE SIZE UPDATE_TIME BUY_SELL_FLAG RECORD_TYPE TICK_STATUS DELETED_TIME
1100
+ 0 2018-02-01 12:00:00 95.53 1400 2018-02-01 11:59:59.797 0 R 0 1969-12-31 19:00:00
1101
+ 1 2018-02-01 12:00:00 95.54 200 2018-02-01 11:59:59.978 1 R 0 1969-12-31 19:00:00
1102
+ 2 2018-02-02 00:00:00 95.53 0 2018-02-01 11:59:59.797 0 R 0 1969-12-31 19:00:00
1103
+ 3 2018-02-02 00:00:00 94.52 100 2018-02-01 19:57:02.502 0 R 0 1969-12-31 19:00:00
1104
+ 4 2018-02-02 00:00:00 94.90 250 2018-02-01 18:35:38.543 1 R 0 1969-12-31 19:00:00
1105
+ 5 2018-02-02 00:00:00 95.54 0 2018-02-01 11:59:59.978 1 R 0 1969-12-31 19:00:00
1106
+ """
1107
+ self.sink(otq.BookDiff(include_initial_book=include_initial_book))
1108
+ return self
1109
+
1110
+
1111
+ @inplace_operation
1112
+ def limit(self: 'Source', tick_limit: int, inplace=False) -> Optional['Source']:
1113
+ """
1114
+ Propagates ticks until the count limit is reached. Once the limit is reached,
1115
+ hidden ticks will still continue to propagate until the next regular tick appears.
1116
+
1117
+ Parameters
1118
+ ----------
1119
+ tick_limit: int
1120
+ The number of regular ticks to propagate. Must be a non-negative integer or -1, which will mean no limit.
1121
+ inplace : bool
1122
+ The flag controls whether operation should be applied inplace or not.
1123
+ If ``inplace=True``, then it returns nothing. Otherwise method returns a new modified
1124
+ object.
1125
+
1126
+ Returns
1127
+ -------
1128
+ :class:`Source` or ``None``
1129
+
1130
+ See Also
1131
+ --------
1132
+ **LIMIT** OneTick event processor
1133
+
1134
+ Examples
1135
+ --------
1136
+
1137
+ Basic example
1138
+
1139
+ .. testcode::
1140
+ :skipif: not otp.compatibility.is_limit_ep_supported()
1141
+
1142
+ data = otp.Ticks(X=[1, 2, 3, 4, 5, 6])
1143
+ data = data.limit(tick_limit=3)
1144
+ print(otp.run(data))
1145
+
1146
+ .. testoutput::
1147
+
1148
+ Time X
1149
+ 0 2003-12-01 00:00:00.000 1
1150
+ 1 2003-12-01 00:00:00.001 2
1151
+ 2 2003-12-01 00:00:00.002 3
1152
+
1153
+ Disable limit
1154
+
1155
+ .. testcode::
1156
+ :skipif: not otp.compatibility.is_limit_ep_supported()
1157
+
1158
+ data = otp.Ticks(X=[1, 2, 3, 4, 5, 6])
1159
+ data = data.limit(tick_limit=-1)
1160
+ print(otp.run(data))
1161
+
1162
+ .. testoutput::
1163
+
1164
+ Time X
1165
+ 0 2003-12-01 00:00:00.000 1
1166
+ 1 2003-12-01 00:00:00.001 2
1167
+ 2 2003-12-01 00:00:00.002 3
1168
+ 3 2003-12-01 00:00:00.003 4
1169
+ 4 2003-12-01 00:00:00.004 5
1170
+ 5 2003-12-01 00:00:00.005 6
1171
+ """
1172
+ if not hasattr(otq, 'Limit'):
1173
+ raise RuntimeError('LIMIT EP isn\'t supported by the current OneTick version.')
1174
+
1175
+ if tick_limit < 0 and tick_limit != -1:
1176
+ raise ValueError('Negative values, except -1, not allowed as `tick_limit` in `limit` method.')
1177
+
1178
+ self.sink(otq.Limit(tick_limit=tick_limit))
1179
+ return self
1180
+
1181
+
1182
+ def _merge_fields_by_regex(schema: dict, columns_regex: str, match_index: int):
1183
+ new_columns: Dict[str, type] = {}
1184
+ for column, column_type in list(schema.items()):
1185
+ match = re.match(columns_regex, column)
1186
+ if match:
1187
+ new_column = match.group(match_index)
1188
+
1189
+ if new_column in schema:
1190
+ raise KeyError(
1191
+ f'Can\'t apply `show_full_detail` for column `{column}`: '
1192
+ f'column `{new_column}` is already in schema.'
1193
+ )
1194
+
1195
+ if new_column in new_columns:
1196
+ if column_type != new_columns[new_column]:
1197
+ raise TypeError(
1198
+ f'Can\'t apply `show_full_detail`: '
1199
+ f'type mismatch for columns with suffix `{new_column}`.'
1200
+ )
1201
+ else:
1202
+ new_columns[new_column] = column_type
1203
+
1204
+ del schema[column]
1205
+
1206
+ schema.update(**new_columns)
1207
+
1208
+
1209
+ @inplace_operation
1210
+ def virtual_ob(
1211
+ self: 'Source',
1212
+ quote_source_fields: Optional[List[Union[str, _Column]]] = None,
1213
+ quote_timeout: Optional[float] = None,
1214
+ show_full_detail: bool = False,
1215
+ output_book_format: Literal['ob', 'prl'] = 'ob',
1216
+ inplace=False,
1217
+ ) -> Optional['Source']:
1218
+ """
1219
+ Creates a series of fake orders from a time series of best bids and asks. The algorithm used is as follows:
1220
+ For a tick with the best bid or ask, create an order tick adding the new best bid or ask and an order
1221
+ withdrawing the old one. Virtual order books can be created for multiple subgroups at once
1222
+ by using the ``quote_source_fields`` parameter to specify a list of string fields to be used for grouping.
1223
+ A separate book will be created for each combination.
1224
+
1225
+ Parameters
1226
+ ----------
1227
+ quote_source_fields: Optional[List[Union[str, Column]]]
1228
+ Specifies a list of string fields for grouping quotes.
1229
+ The virtual order book is then constructed for each subgroup separately and
1230
+ the ``SOURCE`` field is constructed to contain the description of the group.
1231
+ quote_timeout: Optional[float]
1232
+ Specifies the maximum age of a quote that is not stale.
1233
+ A quote that is not replaced after more than ``quote_timeout`` seconds is considered stale,
1234
+ and delete orders will be generated for it.
1235
+
1236
+ A value of ``quote_timeout`` can be fractional (for example, 3.51)
1237
+ show_full_detail: bool
1238
+ If set to ``True``, **virtual_ob** will attempt to include
1239
+ all fields in the input tick when forming the output tick.
1240
+
1241
+ ``ASK_X/BID_X`` fields will combine under paired field ``X``
1242
+ ``ASK_X`` and ``BID_X`` must have the same type.
1243
+
1244
+ If only ``ASK_X`` or ``BID_X`` exist, output will have ``X`` and the missing field
1245
+ will be assumed to have its default value.
1246
+
1247
+ Paired and non-paired fields must not interfere with each other and the fields originally added by this EP
1248
+ output_book_format: 'prl' or 'ob'
1249
+ Supported values are ``prl`` and ``ob``. When set to ``prl``, field ``SIZE`` of output ticks represents
1250
+ current size for the tick's source, price, and side, and the EP propagates ``PRICE`` and ``SOURCE``
1251
+ as the state keys of its output time series.
1252
+
1253
+ When set to ``ob``, field ``SIZE`` of output ticks represents the delta of size for
1254
+ the tick's source, price, and side, and the state key of the output ticks is empty.
1255
+ inplace : bool
1256
+ The flag controls whether operation should be applied inplace or not.
1257
+ If ``inplace=True``, then it returns nothing. Otherwise, method returns a new modified object.
1258
+
1259
+ Returns
1260
+ -------
1261
+ :class:`Source` or ``None``
1262
+
1263
+ See Also
1264
+ --------
1265
+ **VIRTUAL_OB** OneTick event processor
1266
+
1267
+ Examples
1268
+ --------
1269
+
1270
+ Basic example
1271
+
1272
+ >>> data = otp.DataSource(
1273
+ ... db='US_COMP', symbols='AAPL', tick_type='QTE', date=otp.date(2003, 12, 1)
1274
+ ... ) # doctest: +SKIP
1275
+ >>> data = data[['ASK_PRICE', 'ASK_SIZE', 'BID_PRICE', 'BID_SIZE']] # doctest: +SKIP
1276
+ >>> data = data.virtual_ob() # doctest: +SKIP
1277
+ >>> otp.run(data) # doctest: +SKIP
1278
+ Time PRICE DELETED_TIME SIZE BUY_SELL_FLAG TICK_STATUS SOURCE
1279
+ 0 2003-12-01 00:00:00.000 22.28 1969-12-31 19:00:00 500 1 0 AAPL
1280
+ 1 2003-12-01 00:00:00.000 21.66 1969-12-31 19:00:00 100 0 0 AAPL
1281
+ 2 2003-12-01 00:00:00.001 21.8 1969-12-31 19:00:00 500 1 0 AAPL
1282
+ ...
1283
+
1284
+ Specify columns to group quotes
1285
+
1286
+ >>> data = otp.DataSource(
1287
+ ... db='US_COMP', symbols='AAPL', tick_type='QTE', date=otp.date(2003, 12, 1)
1288
+ ... ) # doctest: +SKIP
1289
+ >>> data = data[['ASK_PRICE', 'ASK_SIZE', 'BID_PRICE', 'BID_SIZE', 'EXCHANGE']] # doctest: +SKIP
1290
+ >>> data = data.virtual_ob(['EXCHANGE']) # doctest: +SKIP
1291
+ >>> otp.run(data) # doctest: +SKIP
1292
+ Time PRICE DELETED_TIME SIZE BUY_SELL_FLAG TICK_STATUS SOURCE
1293
+ 0 2003-12-01 00:00:00.000 22.28 1969-12-31 19:00:00 500 1 0 D
1294
+ 1 2003-12-01 00:00:00.000 21.66 1969-12-31 19:00:00 100 0 0 D
1295
+ 2 2003-12-01 00:00:00.001 21.8 1969-12-31 19:00:00 500 1 0 P
1296
+ ...
1297
+ """
1298
+ kwargs: Dict[str, Any] = {}
1299
+
1300
+ required_fields = {'BID_PRICE', 'BID_SIZE', 'ASK_PRICE', 'ASK_SIZE'}
1301
+ missing_fields = required_fields - set(self.schema.keys())
1302
+
1303
+ if missing_fields:
1304
+ raise ValueError('Missing fields required by `virtual_ob`: ' + ', '.join(missing_fields))
1305
+
1306
+ if output_book_format not in {'ob', 'prl'}:
1307
+ raise ValueError(
1308
+ f'Incorrect value for `output_book_format` parameter: {output_book_format}. '
1309
+ f'Supported values: \'ob\' or \'prl\''
1310
+ )
1311
+
1312
+ if show_full_detail and output_book_format != 'prl':
1313
+ raise ValueError('`output_book_format` should be set to \'prl\' when `show_full_detail` set to `True`')
1314
+
1315
+ is_pnl_and_show_full_detail_supported = is_ob_virtual_prl_and_show_full_detail_supported()
1316
+
1317
+ if show_full_detail and not is_pnl_and_show_full_detail_supported:
1318
+ raise RuntimeError('`virtual_ob` not supports `show_full_detail` parameter on this OneTick version')
1319
+
1320
+ if output_book_format != 'ob' and not is_pnl_and_show_full_detail_supported:
1321
+ raise RuntimeError('`virtual_ob` not supports `output_book_format` parameter on this OneTick version')
1322
+
1323
+ if is_pnl_and_show_full_detail_supported:
1324
+ kwargs['show_full_detail'] = show_full_detail
1325
+ kwargs['output_book_format'] = output_book_format
1326
+
1327
+ if quote_source_fields is None:
1328
+ quote_source_fields = []
1329
+
1330
+ quote_source_fields_list = list(map(str, quote_source_fields))
1331
+ for column in quote_source_fields_list:
1332
+ if column not in self.schema:
1333
+ raise ValueError(f'Column \'{column}\' passed in `quote_source_fields` parameter is missing in the schema')
1334
+
1335
+ self.sink(
1336
+ otq.VirtualOb(
1337
+ quote_source_fields=','.join(quote_source_fields_list),
1338
+ quote_timeout='' if quote_timeout is None else str(quote_timeout),
1339
+ **kwargs,
1340
+ )
1341
+ )
1342
+
1343
+ schema = {
1344
+ 'PRICE': float,
1345
+ 'DELETED_TIME': otp.nsectime,
1346
+ 'SIZE': float,
1347
+ 'BUY_SELL_FLAG': int,
1348
+ 'TICK_STATUS': int,
1349
+ 'SOURCE': str,
1350
+ }
1351
+
1352
+ if show_full_detail:
1353
+ exclude_fields = list(required_fields) + quote_source_fields_list
1354
+ schema.update({
1355
+ k: v for k, v in self.schema.copy().items()
1356
+ if k not in exclude_fields
1357
+ })
1358
+
1359
+ _merge_fields_by_regex(schema, r'^(ASK|BID)_(.*)$', 2)
1360
+
1361
+ self.schema.set(**schema)
1362
+
1363
+ return self
1364
+
1365
+
1366
+ @copy_signature(otp.functions.corp_actions, add_self=True, drop_parameters=['source'])
1367
+ def corp_actions(self: 'Source', *args, **kwargs) -> 'Source':
1368
+ """
1369
+ Adjusts values using corporate actions information loaded into OneTick
1370
+ from the reference data file. To use it, location of reference database must
1371
+ be specified via OneTick configuration.
1372
+
1373
+ Parameters
1374
+ ----------
1375
+ adjustment_date : :py:class:`onetick.py.date`, :py:class:`onetick.py.datetime`, int, str, None, optional
1376
+ The date as of which the values are adjusted.
1377
+ All corporate actions of the types specified in the parameters
1378
+ that lie between the tick timestamp and the adjustment date will be applied to each tick.
1379
+
1380
+ This parameter can be either date or datetime .
1381
+ `int` and `str` format can be *YYYYMMDD* or *YYYYMMDDhhmmss*.
1382
+ When parameter is a date, the time is assumed to be 17:00:00 GMT
1383
+ and parameter ``adjustment_date_tz`` is ignored.
1384
+
1385
+ If it is not set, the values are adjusted as of the end date in the query.
1386
+
1387
+ Notice that the ``adjustment date`` is not affected neither by *_SYMBOL_PARAM._PARAM_END_TIME_NANOS*
1388
+ nor by the *apply_times_daily* setting in :py:func:`onetick.py.run`.
1389
+
1390
+ adjustment_date_tz : str, optional
1391
+ Timezone for ``adjustment date``.
1392
+
1393
+ By default global :py:attr:`tz<onetick.py.configuration.Config.tz>` value is used.
1394
+ Local timezone can't be used so in this case parameter is set to GMT.
1395
+ When ``adjustment_date`` is in YYYYMMDD format, this parameter is set to GMT.
1396
+ fields : str, optional
1397
+ A comma-separated list of fields to be adjusted. If this parameter is not set,
1398
+ some default adjustments will take place if appropriately named fields exist in the tick:
1399
+
1400
+ - If the ``adjust_rule`` parameter is set to PRICE, and the PRICE field is present,
1401
+ it will get adjusted. If the fields ASK_PRICE or BID_PRICE are present, they will get adjusted.
1402
+ If fields ASK_VALUE or BID_VALUE are present, they will get adjusted
1403
+
1404
+ - If the ``adjust_rule`` parameter is set to SIZE, and the SIZE field is present,
1405
+ it will get adjusted. If the fields ASK_SIZE or BID_SIZE are present, they will get adjusted.
1406
+ If fields ASK_VALUE or BID_VALUE are present, they will get adjusted.
1407
+
1408
+ adjust_rule : str, optional
1409
+ When set to PRICE, adjustments are applied under the assumption that fields to be adjusted contain prices
1410
+ (adjustment direction is determined appropriately).
1411
+
1412
+ When set to SIZE, adjustments are applied under the assumption that fields contain sizes
1413
+ (adjustment direction is opposite to that when the parameter's value is PRICE).
1414
+
1415
+ By default the value is PRICE.
1416
+ apply_split : bool, optional
1417
+ If True, adjustments for splits are applied.
1418
+ apply_spinoff : bool, optional
1419
+ If True, adjustments for spin-offs are applied.
1420
+ apply_cash_dividend : bool, optional
1421
+ If True, adjustments for cash dividends are applied.
1422
+ apply_stock_dividend : bool, optional
1423
+ If True, adjustments for stock dividends are applied.
1424
+ apply_security_splice : bool, optional
1425
+ If True, adjustments for security splices are applied.
1426
+ apply_others : str, optional
1427
+ A comma-separated list of names of custom adjustment types to apply.
1428
+ apply_all : bool, optional
1429
+ If True, applies all types of adjustments, both built-in and custom.
1430
+
1431
+ Returns
1432
+ -------
1433
+ :py:class:`onetick.py.Source`
1434
+ A new source object with applied adjustments.
1435
+
1436
+ See also
1437
+ --------
1438
+ **CORP_ACTIONS** OneTick event processor
1439
+
1440
+ Examples
1441
+ --------
1442
+ >>> src = otp.DataSource('US_COMP',
1443
+ ... tick_type='TRD',
1444
+ ... start=otp.dt(2022, 5, 20, 9, 30),
1445
+ ... end=otp.dt(2022, 5, 26, 16))
1446
+ >>> df = otp.run(src, symbols='MKD', symbol_date=otp.date(2022, 5, 22))
1447
+ >>> df["PRICE"][0]
1448
+ 0.0911
1449
+ >>> src = src.corp_actions(adjustment_date=otp.date(2022, 5, 22),
1450
+ ... fields="PRICE")
1451
+ >>> df = otp.run(src, symbols='MKD', symbol_date=otp.date(2022, 5, 22))
1452
+ >>> df["PRICE"][0]
1453
+ 1.36649931675
1454
+ """
1455
+ return otp.functions.corp_actions(self, *args, **kwargs)