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
onetick/py/db/db.py ADDED
@@ -0,0 +1,1423 @@
1
+ # pylama:ignore=W0237
2
+ import logging
3
+ import os
4
+ import datetime as dt
5
+ import subprocess
6
+ import warnings
7
+
8
+ from datetime import timedelta
9
+ from collections import defaultdict
10
+ from dateutil.relativedelta import relativedelta
11
+ from typing import List, Union, Optional
12
+ from uuid import uuid4
13
+
14
+ from onetick import py as otp
15
+ from onetick.py.core import db_constants as constants
16
+ from onetick.py.compatibility import is_native_plus_zstd_supported
17
+ from onetick.py import utils, sources, session, configuration
18
+
19
+ import pandas
20
+
21
+
22
+ def _tick_type_detector(tick_type, obj):
23
+ if tick_type is not None:
24
+ return tick_type
25
+
26
+ type2traits = {
27
+ "ORDER": ["ID", "BUY_FLAG", "SIDE", "QTY", "QTY_FILLED", "QTY", "ORDTYPE", "PRICE", "PRICE_FILLED"],
28
+ "QTE": ["ASK_PRICE", "BID_PRICE", "ASK_SIZE", "BID_SIZE"],
29
+ "NBBO": ["ASK_PRICE", "BID_PRICE", "ASK_SIZE", "BID_SIZE", "BID_EXCHANGE", "ASK_EXCHANGE"],
30
+ "TRD": ["PRICE", "SIZE"],
31
+ }
32
+
33
+ type2count = defaultdict(lambda: 0)
34
+ max_tt = "TRD"
35
+ max_count = 0
36
+
37
+ for column in obj.columns():
38
+ for tt, traits in type2traits.items():
39
+ if column in traits:
40
+ type2count[tt] += 1
41
+
42
+ if type2count[tt] > max_count:
43
+ max_count = type2count[tt]
44
+ max_tt = tt
45
+
46
+ return max_tt
47
+
48
+
49
+ def write_to_db(src: 'otp.Source',
50
+ dest: Union[str, 'otp.DB'],
51
+ date: dt.date,
52
+ symbol: Union[str, 'otp.Column'],
53
+ tick_type: Union[str, 'otp.Column'],
54
+ timezone: Optional[str] = None,
55
+ execute: bool = True,
56
+ start: Optional[dt.date] = None,
57
+ end: Optional[dt.date] = None,
58
+ propagate: bool = False,
59
+ append: bool = True,
60
+ **kwargs):
61
+ """
62
+ Writes source to the database.
63
+ The main differences from otp.Source.write() function are
64
+ appending ticks by default, automatic tick_type detection and executing the query right here.
65
+
66
+ Parameters
67
+ ----------
68
+ src: :class:`otp.Source`
69
+ source that will be written to the database.
70
+ dest: str or :py:class:`otp.DB <onetick.py.DB>`
71
+ database name or object.
72
+ date: datetime or None
73
+ date where to save data.
74
+ Cannot be used together with `start` and `end` parameters.
75
+ start: datetime or None
76
+ start date of period where to save data.
77
+ Cannot be used together with `date` parameters.
78
+ Default is None.
79
+ end: datetime or None
80
+ end date of period where to save data.
81
+ Cannot be used together with `date` parameters.
82
+ Default is None.
83
+ symbol: str or Column
84
+ resulting symbol name string or column to get symbol name from.
85
+ tick_type: str or Column
86
+ resulting tick type string or column to get tick type from.
87
+ If tick type is None then an attempt will be taken to get
88
+ tick type name automatically based on the ``src`` source's schema.
89
+ (ORDER, QTE, TRD and NBBO tick types are supported).
90
+ timezone: str
91
+ If ``execute`` parameter is set then this timezone
92
+ will be used for running the query.
93
+ By default, it is set to `otp.config.tz`.
94
+ execute: bool
95
+ execute the query right here or not.
96
+ If True, `date` or `start`+`end` parameters are required,
97
+ and then dataframe will be returned.
98
+ (Probably empty, unless 'propagate' parameter is specified).
99
+ If False, modified copy of the source ``src`` will be returned.
100
+ propagate: bool
101
+ Propagate ticks after writing or not.
102
+ append: bool
103
+ Write the data in append mode or not.
104
+ kwargs:
105
+ other arguments that will be passed to :py:meth:`onetick.py.Source.write` function.
106
+ """
107
+
108
+ tick_type = _tick_type_detector(tick_type, src)
109
+
110
+ if timezone is None:
111
+ timezone = configuration.config.tz
112
+ if date is None or date is otp.adaptive:
113
+ kwargs['start'] = start
114
+ kwargs['end'] = end
115
+ else:
116
+ kwargs['date'] = date
117
+
118
+ writer = src.write(db=dest,
119
+ symbol=symbol,
120
+ tick_type=tick_type,
121
+ propagate=propagate,
122
+ append=append,
123
+ **kwargs)
124
+
125
+ if execute:
126
+ if not start or not end:
127
+ start = getattr(date, 'ts', date)
128
+ end = start + relativedelta(days=1)
129
+ return otp.run(writer, start=start, end=end, timezone=timezone)
130
+ return writer
131
+
132
+
133
+ class _DB:
134
+
135
+ _LOCAL = False
136
+ # this flag means that db section should be added to locator and acl (True)
137
+ # or db locates on some ts (False)
138
+
139
+ def __init__(
140
+ self,
141
+ name,
142
+ src=None,
143
+ date=None,
144
+ symbol=None,
145
+ tick_type=None,
146
+ db_properties: Optional[dict] = None,
147
+ db_locations: Optional[list] = None,
148
+ db_raw_data: Optional[dict] = None,
149
+ db_feed: Optional[dict] = None,
150
+ write=True,
151
+ minimum_start_date: Optional[Union[str, dt.date, 'otp.date']] = None,
152
+ maximum_end_date: Optional[Union[str, dt.date, 'otp.date']] = None,
153
+ ):
154
+ # we assume here that db_properties and db_locations fully prepared here or None (in case of remote db)
155
+ self.name = name
156
+ self.id, _, _ = name.partition("//")
157
+ self._db_properties = db_properties
158
+ self._db_locations = db_locations
159
+ self._db_raw_data = db_raw_data
160
+ self._db_feed = db_feed
161
+ self._write = write
162
+ self._minimum_start_date = (
163
+ dt.datetime.strptime(minimum_start_date, '%Y%m%d').date()
164
+ if isinstance(minimum_start_date, str)
165
+ else minimum_start_date
166
+ )
167
+ self._maximum_end_date = (
168
+ dt.datetime.strptime(maximum_end_date, '%Y%m%d').date()
169
+ if isinstance(maximum_end_date, str)
170
+ else maximum_end_date
171
+ )
172
+ if src is not None:
173
+ self.add(src=src, date=date, symbol=symbol, tick_type=tick_type)
174
+ elif any(x is not None for x in (date, symbol, tick_type)):
175
+ warnings.warn(
176
+ "Parameters 'date', 'symbol' and 'tick_type' can only be set when parameter 'src' is specified.",
177
+ FutureWarning,
178
+ stacklevel=3,
179
+ )
180
+
181
+ @staticmethod
182
+ def _format_params(params):
183
+ res = {}
184
+ for key, value in params.items():
185
+ if isinstance(value, dt.datetime):
186
+ res[key] = value.strftime("%Y%m%d%H%M%S")
187
+ else:
188
+ res[key] = str(value)
189
+ return res
190
+
191
+ # TODO: move this method to DB
192
+ def add(self,
193
+ src,
194
+ date=None,
195
+ start=None,
196
+ end=None,
197
+ symbol=None,
198
+ tick_type=None,
199
+ timezone=None,
200
+ **kwargs):
201
+ """
202
+ Add data to a database.
203
+ If ticks with the same timestamp are already presented in database old values won't be updated.
204
+
205
+ Parameters
206
+ ----------
207
+ src: :class:`otp.Source`
208
+ source that will be written to the database.
209
+ date: datetime or None
210
+ date of the day in which the data will be saved.
211
+ The timestamps of the ticks should be between the start and the end of the day.
212
+ Be default, it is set to `otp.config.default_date`.
213
+ start: datetime or None
214
+ start day of period in which the data will be saved.
215
+ The timestamps of the ticks should be between `start` and `end` dates.
216
+ Cannot be used with `date` parameter.
217
+ Be default, None.
218
+ end: datetime or None
219
+ end day of period in which the data will be saved.
220
+ The timestamps of the ticks should be between `start` and `end` dates.
221
+ Cannot be used with `date` parameter.
222
+ Be default, None.
223
+ symbol: str or Column
224
+ resulting symbol name string or column to get symbol name from.
225
+ Be default, it is set to `otp.config.default_db_symbol`.
226
+ tick_type: str or Column
227
+ resulting tick type string or column to get tick type from.
228
+ If tick type is None then an attempt will be taken to get
229
+ tick type name automatically based on the ``src`` source's schema.
230
+ (ORDER, QTE, TRD and NBBO tick types are supported).
231
+ timezone: str
232
+ This timezone will be used for running the query.
233
+ By default, it is set to `otp.config.tz`.
234
+ kwargs:
235
+ other arguments that will be passed to :py:meth:`onetick.py.Source.write` function.
236
+
237
+ Examples
238
+ --------
239
+
240
+ Data is saved to the specified date, symbol and tick type:
241
+ (note that ``session`` is created before this example)
242
+
243
+ >>> db = otp.DB('MYDB2')
244
+ >>> db.add(otp.Ticks(A=[4, 5, 6]), date=otp.dt(2003, 1, 1), symbol='SMB', tick_type='TT')
245
+ >>> session.use(db)
246
+
247
+ We can get the same data by specifying the same parameters:
248
+
249
+ >>> data = otp.DataSource(db, date=otp.dt(2003, 1, 1), symbols='SMB', tick_type='TT')
250
+ >>> otp.run(data)
251
+ Time A
252
+ 0 2003-01-01 00:00:00.000 4
253
+ 1 2003-01-01 00:00:00.001 5
254
+ 2 2003-01-01 00:00:00.002 6
255
+ """
256
+ if timezone is None:
257
+ timezone = configuration.config.tz
258
+ if start and end and date:
259
+ raise ValueError("You can't specify both start/end and date parameters")
260
+
261
+ if start and end:
262
+ kwargs['start'] = start
263
+ kwargs['end'] = end
264
+ kwargs['date'] = otp.adaptive
265
+ else:
266
+ kwargs['date'] = date if date is not None else configuration.config.default_start_time
267
+
268
+ _symbol = symbol if symbol is not None else configuration.config.default_db_symbol
269
+ kwargs.setdefault('propagate', kwargs.get('propagate_ticks', False))
270
+
271
+ res = self._session_handler(write_to_db,
272
+ src=src,
273
+ dest=self.name,
274
+ symbol=_symbol,
275
+ tick_type=tick_type,
276
+ timezone=timezone,
277
+ **kwargs)
278
+
279
+ # We need to keep backward-compatibility,
280
+ # because before there was no ability to get written ticks
281
+ if kwargs.get('propagate'):
282
+ return res
283
+
284
+ @property
285
+ def properties(self):
286
+ """
287
+ Get dict of database properties.
288
+
289
+ Returns
290
+ -------
291
+ dict
292
+
293
+ Examples
294
+ --------
295
+ >>> otp.DB('X').properties # doctest: +SKIP
296
+ {'symbology': 'BZX',
297
+ 'archive_compression_type': 'NATIVE_PLUS_GZIP',
298
+ 'tick_timestamp_type': 'NANOS'}
299
+ """
300
+ return self._db_properties
301
+
302
+ @property
303
+ def locations(self):
304
+ """
305
+ Get list of database locations.
306
+
307
+ Returns
308
+ -------
309
+ list of dict
310
+
311
+ Examples
312
+ --------
313
+ >>> otp.DB('X').locations # doctest:+ELLIPSIS
314
+ [{'access_method': 'file',
315
+ 'start_time': '20021230000000',
316
+ 'end_time': '21000101000000',
317
+ ...}]
318
+ """
319
+ return self._db_locations
320
+
321
+ @property
322
+ def raw_data(self):
323
+ """
324
+ Get dict of database raw configurations.
325
+
326
+ Returns
327
+ -------
328
+ dict of dict
329
+
330
+ Examples
331
+ --------
332
+ >>> db = otp.DB('RAW_EXAMPLE',
333
+ ... db_raw_data=[{
334
+ ... 'id': 'PRIMARY_A',
335
+ ... 'prefix': 'DATA.',
336
+ ... 'locations': [
337
+ ... {'mount': 'mount1'}
338
+ ... ]
339
+ ... }]
340
+ ... )
341
+ >>> db.raw_data # doctest:+ELLIPSIS
342
+ [{'id': 'PRIMARY_A', 'prefix': 'DATA.', 'locations': [{'mount': 'mount1', 'access_method': 'file', ...}]}]
343
+ """
344
+ return self._db_raw_data
345
+
346
+ @property
347
+ def feed(self):
348
+ """
349
+ Get dict of database feed configuration.
350
+
351
+ Returns
352
+ -------
353
+ dict
354
+
355
+ Examples
356
+ --------
357
+ >>> db = otp.DB('RAW_EXAMPLE',
358
+ ... db_raw_data=[{
359
+ ... 'id': 'PRIMARY_A',
360
+ ... 'prefix': 'DATA.',
361
+ ... 'locations': [
362
+ ... {'mount': 'mount1'}
363
+ ... ]
364
+ ... }],
365
+ ... db_feed={'type': 'rawdb', 'raw_source': 'PRIMARY_A'},
366
+ ... )
367
+ >>> db.feed
368
+ {'type': 'rawdb', 'raw_source': 'PRIMARY_A', 'format': 'native'}
369
+ """
370
+ return self._db_feed
371
+
372
+ @property
373
+ def symbols(self):
374
+ result = self._session_handler(self._symbols)
375
+ return result if result else []
376
+
377
+ def _session_handler(self, func, *args, **kwargs):
378
+ """
379
+ Handler to check if database is already in locator and
380
+ run function with separate session or using current
381
+
382
+ :param func: function to run
383
+ """
384
+ __result = None
385
+
386
+ _session = session.Session._instance
387
+ close_session = False
388
+ _remove_from_locator = False
389
+ _remove_from_acl = False
390
+ if _session is None:
391
+ close_session = True
392
+ _session = session.Session()
393
+
394
+ try:
395
+ if self._LOCAL:
396
+ if self.id not in _session.locator.databases:
397
+ if self.id != self.name and not otp.compatibility.is_supported_reload_locator_with_derived_db():
398
+ # Derived DB
399
+ raise ValueError(
400
+ "You need include derived DB into the session use the .use method before adding "
401
+ " data there."
402
+ )
403
+
404
+ _remove_from_locator = True
405
+ _session.locator.add(self)
406
+ if self.id not in _session.acl.databases:
407
+ _remove_from_acl = True
408
+ _session.acl.add(self)
409
+ __result = func(*args, **kwargs)
410
+ finally:
411
+ if close_session:
412
+ _session.close()
413
+ else:
414
+ if self._LOCAL:
415
+ if _remove_from_locator:
416
+ _session.locator.remove(self)
417
+ if _remove_from_acl:
418
+ _session.acl.remove(self)
419
+
420
+ return __result
421
+
422
+ def _symbols(self):
423
+ src = sources.Symbols(self)
424
+ symbols = otp.run(src)
425
+ result = []
426
+ if symbols.empty:
427
+ return []
428
+ for s in list(symbols["SYMBOL_NAME"]):
429
+ result.append(s.split(":")[-1])
430
+ return result
431
+
432
+ def __repr__(self):
433
+ return "DB: " + self.name
434
+
435
+ def __str__(self):
436
+ return self.name
437
+
438
+
439
+ class DB(_DB):
440
+ """
441
+ A class representing OneTick databases when configuring
442
+ :py:class:`locators <onetick.py.session.Locator>` and :py:class:`ACL <onetick.py.session.ACL>`.
443
+
444
+ A database can point to an existing OneTick archive
445
+ or the temporary directory can be created with the data provided.
446
+
447
+ This class object can then be :py:meth:`used <onetick.py.Session.use>`
448
+ in :py:class:`onetick.py.Session`.
449
+
450
+ The data to add to the database can be passed into the
451
+ constructor with parameter ``src`` or later with the :py:meth:`add` method.
452
+
453
+ Note that after ticks were written to a particular timestamps in the database,
454
+ they can't be updated for the same timestamps.
455
+
456
+ Note
457
+ ----
458
+ This class can only be used to create database on the local machine.
459
+ Database is created by using/creating a directory in the local filesystem
460
+ and adding entries to the OneTick locator and ACL local files.
461
+ This class can't be used to manage remote databases.
462
+
463
+ Parameters
464
+ ----------
465
+ name : str
466
+ Database name.
467
+ Derived databases are specified in "parent//derived" format.
468
+ A derived database inherits the parent database properties.
469
+ src : :py:class:`~onetick.py.Source`, optional
470
+ Data to add to the database.
471
+ date : :py:class:`onetick.py.date` or :py:class:`datetime.date`, optional
472
+ ``src`` data will be added to this date.
473
+ Can be set only if ``src`` is set.
474
+ Default value is the same as in :py:meth:`add` method.
475
+ symbol : str, optional
476
+ Symbol name to add ``src`` data for.
477
+ Can be set only if ``src`` is set.
478
+ Default value is the same as in :py:meth:`add` method.
479
+ tick_type : str, optional
480
+ Tick type to add ``src`` data for.
481
+ Can be set only if ``src`` is set.
482
+ Default value is the same as in :py:meth:`add` method.
483
+ db_properties : :obj:`dict`, optional
484
+ Properties of the database to add to the locator
485
+ db_locations : :obj:`list` of :obj:`dict`, optional
486
+ A list of locations for the database to add to the locator.
487
+ This parameter is a list, because databases in a locator can have several location sections.
488
+ If not specified, a temporary directory is used as the database location.
489
+ db_raw_data: :obj:`list` of :obj:`dict`, optional
490
+ Raw database configuration.
491
+ db_feed: dict, optional
492
+ Feed configuration.
493
+ write : bool, optional
494
+ Flag that controls access to write to database.
495
+ clean_up : bool, optional
496
+ Flag that controls temporary database cleanup
497
+ destroy_access : bool, optional
498
+ Flag that controls access to destroy the database.
499
+ minimum_start_date: str or :py:class:`datetime.date` or :py:class:`onetick.py.date`
500
+ Specifies the minimum date of the tick that can be served to a user.
501
+ The format for the value is *YYYYMMDD*.
502
+ The OneTick server enforces this permission by verifying that
503
+ the query start time is not less than time of 00:00:00, in GMT timezone, for the specified minimum start date.
504
+ maximum_end_date: str or :py:class:`datetime.date` or :py:class:`onetick.py.date`
505
+ Specifies the date of the tick, starting from which ticks are not allowed to be returned to a user.
506
+ The format for the value is *YYYYMMDD*.
507
+ The OneTick server enforces this permission by verifying that
508
+ the query end time is less than time of 00:00:00, in GMT timezone, for the specified maximum end date.
509
+
510
+ Examples
511
+ --------
512
+
513
+ A database can be initialized along with data:
514
+
515
+ >>> data = otp.Ticks(X=['hello', 'world!'])
516
+ >>> db = otp.DB('MYDB', data)
517
+
518
+ Database symbol name, tick type and date to which the data will be written can be changed like this:
519
+
520
+ >>> db = otp.DB('MYDB', data, symbol='S_S', tick_type='T_T', date=otp.dt(2003, 12, 1))
521
+
522
+ You can specify a derived db by using ``//`` as a separator:
523
+
524
+ >>> data = otp.Ticks(X=['parent1', 'parent2'])
525
+ >>> db = otp.DB('DB_A', data)
526
+ >>> db.add(data)
527
+
528
+ >>> data_derived = otp.Ticks(X=['derived1', 'derived2'])
529
+ >>> db_derived = otp.DB('DB_A//DB_D')
530
+ >>> session.use(db_derived)
531
+ >>> db_derived.add(data_derived)
532
+
533
+ You can add an existing OneTick database to the locator or create a new one:
534
+
535
+ >>> existing_db = otp.DB('MY_US_COMP', # doctest: +SKIP
536
+ ... db_locations=[{'location': '/home/user/data/US_COMP',
537
+ ... 'start_time': datetime(2003, 1, 1),
538
+ ... 'end_time': datetime(2010, 1, 1),
539
+ ... 'day_boundary_tz': 'EST5EDT'}])
540
+ >>> session.use(existing_db) # doctest: +SKIP
541
+ """
542
+
543
+ _LOCAL = True
544
+
545
+ def __init__(
546
+ self,
547
+ name=None,
548
+ src=None,
549
+ date=None,
550
+ symbol=None,
551
+ tick_type=None,
552
+ kind='archive',
553
+ db_properties=None,
554
+ db_locations=None,
555
+ db_raw_data=None,
556
+ db_feed=None,
557
+ write=True,
558
+ clean_up=utils.default,
559
+ destroy_access=False,
560
+ minimum_start_date=None,
561
+ maximum_end_date=None,
562
+ ):
563
+ if name is not None and not isinstance(name, str):
564
+ message = f"Database name expected to be string got {type(name)}"
565
+ logging.error(message)
566
+ raise TypeError(message)
567
+
568
+ self._clean_up = clean_up
569
+ self._destroy_access = destroy_access
570
+ self._path = None
571
+ self._db_suffix = ""
572
+
573
+ if name:
574
+ self._db_suffix = name
575
+ else:
576
+ # Mostly for temporary databases
577
+ name = uuid4().hex.upper()
578
+ self._db_suffix = "db_" + name
579
+
580
+ db_properties = self._prepare_db_properties(db_properties)
581
+ db_day_boundary_tz_set = 'day_boundary_tz' in db_properties.keys()
582
+ db_locations = self._prepare_db_locations(db_locations,
583
+ db_day_boundary_tz_set=db_day_boundary_tz_set,
584
+ kind=kind)
585
+ db_raw_data = self._prepare_db_raw_data(db_raw_data, db_properties)
586
+ db_feed = self._prepare_db_feed(db_feed)
587
+
588
+ if isinstance(src, pandas.DataFrame):
589
+ csv_path = os.path.join(self._tmp_dir.path, uuid4().hex.upper() + ".csv")
590
+ src.to_csv(csv_path, index=False)
591
+ if otp.__webapi__:
592
+ src = sources.CSV(utils.FileBuffer(csv_path))
593
+ else:
594
+ src = sources.CSV(csv_path)
595
+
596
+ super().__init__(
597
+ name=name,
598
+ src=src,
599
+ date=date,
600
+ symbol=symbol,
601
+ tick_type=tick_type,
602
+ db_properties=db_properties,
603
+ db_locations=db_locations,
604
+ db_raw_data=db_raw_data,
605
+ db_feed=db_feed,
606
+ write=write,
607
+ minimum_start_date=minimum_start_date,
608
+ maximum_end_date=maximum_end_date,
609
+ )
610
+
611
+ def _prepare_db_properties(self, properties):
612
+ if properties is None:
613
+ properties = {}
614
+
615
+ # convert all property keys to lowercase
616
+ properties = {k.lower(): v for k, v in properties.items()}
617
+
618
+ # set default properties if they are not specified
619
+ properties.setdefault("symbology", configuration.config.default_symbology)
620
+ properties.setdefault("tick_timestamp_type", "NANOS")
621
+
622
+ if is_native_plus_zstd_supported():
623
+ properties.setdefault("archive_compression_type", constants.compression_type.NATIVE_PLUS_ZSTD)
624
+ else:
625
+ properties.setdefault("archive_compression_type", constants.compression_type.NATIVE_PLUS_GZIP)
626
+
627
+ return self._format_params(properties)
628
+
629
+ def _create_db(self):
630
+ logging.debug(f'Creating temporary directory for db "{self._db_suffix}"')
631
+
632
+ dirs_list = self._db_suffix.replace("//", " DERIVED ").split()
633
+ dir_name = ''
634
+ base_dir = ""
635
+ if os.getenv('OTP_WEBAPI_TEST_MODE'):
636
+ # copied from onetick.test.fixtures _keep_generated_dir()
637
+ base_dir = os.path.join(otp.utils.TMP_CONFIGS_DIR(), os.environ.get("ONE_TICK_TMP_DIR", "dbs"))
638
+ for cur_dir in dirs_list:
639
+ dir_name = os.path.join(dir_name, cur_dir)
640
+ self._tmp_dir = utils.TmpDir(dir_name, clean_up=self._clean_up, base_dir=base_dir)
641
+ if not self._path:
642
+ self._path = self._tmp_dir.path
643
+
644
+ def _prepare_db_locations(self, locations, db_day_boundary_tz_set=None, kind=None, default_location=None):
645
+ if not locations:
646
+ locations = [{}]
647
+ result = []
648
+ # set default properties if they are not specified
649
+ for location in locations:
650
+ location.setdefault("access_method", constants.access_method.FILE)
651
+ location.setdefault("start_time", constants.DEFAULT_START_DATE - timedelta(days=2))
652
+ location.setdefault("end_time", constants.DEFAULT_END_DATE + timedelta(days=1))
653
+ if not db_day_boundary_tz_set and db_day_boundary_tz_set is not None:
654
+ # If the day_boundary_tz is not set database-wide, then we want it to have
655
+ # a default value for each location
656
+ day_boundary_tz = utils.default_day_boundary_tz(self._db_suffix)
657
+ if day_boundary_tz:
658
+ location.setdefault("day_boundary_tz", day_boundary_tz)
659
+
660
+ if 'location' not in location:
661
+ methods = {constants.access_method.SOCKET, constants.access_method.MEMORY}
662
+ if location['access_method'] in methods:
663
+ raise ValueError("Parameter 'location' must be specified when parameter"
664
+ f" 'access_method' is set to {methods}")
665
+ if not default_location:
666
+ self._create_db()
667
+ location['location'] = self._path
668
+ else:
669
+ location['location'] = default_location
670
+
671
+ if kind == 'accelerator':
672
+ location.setdefault("archive_duration", "continuous")
673
+ location.setdefault("growable_archive", "true")
674
+ # TODO: think what to do if there will be several locations
675
+
676
+ result.append(location)
677
+
678
+ return list(map(self._format_params, result))
679
+
680
+ def _prepare_db_raw_data(self, raw_data, db_properties):
681
+ if not raw_data:
682
+ return []
683
+
684
+ raw_ids = set()
685
+ auto_discover_mounts = db_properties.get('auto_discover_mounts', '').lower() == 'yes'
686
+ default_location = None
687
+
688
+ for raw_db in raw_data:
689
+ raw_db.setdefault('id', 'PRIMARY')
690
+ if raw_db['id'] in raw_ids:
691
+ raise ValueError("Parameter 'id' must be set and must be unique for raw databases")
692
+ raw_ids.add(raw_db['id'])
693
+
694
+ if 'prefix' not in raw_db:
695
+ raise ValueError("Parameter 'prefix' must be specified for raw database")
696
+
697
+ if self._path is not None and default_location is None:
698
+ default_location = utils.TmpDir(rel_path='raw', base_dir=self._path, clean_up=self._clean_up).path
699
+ raw_db['locations'] = self._prepare_db_locations(raw_db.get('locations'),
700
+ db_day_boundary_tz_set=None,
701
+ default_location=default_location)
702
+ if auto_discover_mounts and len(raw_db['locations']) > 1:
703
+ raise ValueError("Only one location must be specified for raw database"
704
+ " when parameter 'auto_discover_mounts' is specified for database")
705
+ for location in raw_db['locations']:
706
+ if 'mount' not in location and not auto_discover_mounts:
707
+ raise ValueError("Parameter 'mount' must be specified for raw database location")
708
+ if 'mount' in location and auto_discover_mounts:
709
+ raise ValueError("Parameter 'mount' must not be specified for raw database location"
710
+ " when parameter 'auto_discover_mounts' is specified for database")
711
+ return raw_data
712
+
713
+ def _prepare_db_feed(self, feed):
714
+ if not feed:
715
+ return {}
716
+ if 'type' not in feed:
717
+ raise ValueError("Parameter 'type' must be specified for database feed")
718
+ if feed['type'] == 'rawdb':
719
+ feed.setdefault('format', 'native')
720
+ formats = ('native', 'rt', 'ascii', 'xml')
721
+ if feed['format'] not in formats:
722
+ raise ValueError(f"Parameter 'format' must be one of {formats}")
723
+ feed.setdefault('raw_source', 'PRIMARY')
724
+ return self._format_params(feed)
725
+
726
+
727
+ """
728
+ Keep here as example of custom databases that can be defined in client code
729
+
730
+
731
+ P_CME = DB(
732
+ name="P_CME",
733
+ db_properties={
734
+ "symbology": "CTA",
735
+ "archive_compression_type": constants.compression_type.NATIVE_PLUS_GZIP,
736
+ "tick_search_max_boundary_offset_sec": 1800,
737
+ "tick_timestamp_type": "NANOS",
738
+ },
739
+ db_locations=[
740
+ {
741
+ "access_method": constants.access_method.SOCKET,
742
+ "location": "192.168.5.63:50025",
743
+ "start_time": datetime(year=2008, month=9, day=1),
744
+ "end_time": constants.DEFAULT_END_DATE,
745
+ }
746
+ ],
747
+ )
748
+
749
+ MS127 = DB(
750
+ name="MS127",
751
+ db_properties={
752
+ "symbology": "MSGR",
753
+ "ref_data_db": "REF_DATA_MS127",
754
+ "archive_compression_type": constants.compression_type.NATIVE_PLUS_GZIP,
755
+ "ignore_previous_day_corrections_on_reload": "yes",
756
+ },
757
+ db_locations=[
758
+ {
759
+ "access_method": constants.access_method.SOCKET,
760
+ "location": "192.168.5.63:50025",
761
+ "start_time": datetime(year=2010, month=12, day=31),
762
+ "end_time": constants.DEFAULT_END_DATE,
763
+ }
764
+ ],
765
+ )
766
+
767
+ MS44 = DB(
768
+ name="MS44",
769
+ db_properties={
770
+ "symbology": "MSGR",
771
+ "ref_data_db": "REF_DATA_MS44",
772
+ "archive_compression_type": constants.compression_type.NATIVE_PLUS_GZIP,
773
+ "ignore_previous_day_corrections_on_reload": "yes",
774
+ },
775
+ db_locations=[
776
+ {
777
+ "access_method": constants.access_method.SOCKET,
778
+ "location": "192.168.5.63:50025",
779
+ "start_time": datetime(year=2010, month=12, day=31),
780
+ "end_time": constants.DEFAULT_END_DATE,
781
+ }
782
+ ],
783
+ )
784
+
785
+ TAQ_NBBO = DB(
786
+ name="TAQ_NBBO",
787
+ db_properties={
788
+ "symbology": "BZX",
789
+ "price_not_key": True,
790
+ "memory_data_max_life_hours": 30,
791
+ "memory_db_dir": "/onetick-tickdata-com/STORAGE_GATEWAY/DEEP_HISTORY/US_TED/NBBO/shmem",
792
+ "mmap_db_compression_type": constants.compression_type.NATIVE_PLUS_GZIP,
793
+ },
794
+ db_locations=[
795
+ {
796
+ "access_method": constants.access_method.FILE,
797
+ "location": "/onetick-tickdata-com/STORAGE_GATEWAY/DEEP_HISTORY/US_TED/NBBO/",
798
+ "start_time": datetime(year=2001, month=1, day=1),
799
+ "end_time": constants.DEFAULT_END_DATE,
800
+ }
801
+ ],
802
+ )
803
+
804
+
805
+ US_COMP = DB(
806
+ name="US_COMP",
807
+ db_properties={
808
+ "symbology": "BZX",
809
+ "price_not_key": True,
810
+ "memory_data_max_life_hours": 30,
811
+ "memory_db_dir": "/onetick-tickdata-com/STORAGE_GATEWAY/DEEP_HISTORY/US_TED/TAQ/shmem",
812
+ "mmap_db_compression_type": constants.compression_type.NATIVE_PLUS_GZIP,
813
+ },
814
+ db_locations=[
815
+ {
816
+ "access_method": constants.access_method.FILE,
817
+ "location": "/onetick-tickdata-com/STORAGE_GATEWAY/DEEP_HISTORY/US_TED/TAQ/",
818
+ "start_time": datetime(year=2003, month=10, day=1),
819
+ "end_time": constants.DEFAULT_END_DATE,
820
+ }
821
+ ],
822
+ )
823
+ """
824
+
825
+
826
+ class RefDB(DB):
827
+ """ Creates reference database object.
828
+
829
+ Parameters
830
+ ----------
831
+ name : str
832
+ Database name
833
+ clean_up : bool, optional
834
+ Flag that controls temporary database cleanup
835
+ db_properties : :obj:`dict`, optional
836
+ Properties of database to add to locator
837
+ db_location : :obj:`dict`, optional
838
+ Location of database to add to locator. Reference database must have a single location,
839
+ pointing to a continuous archive database.
840
+ write : bool, optional
841
+ Flag that controls access to write to database
842
+ destroy_access : bool, optional
843
+ Flag that controls access to destroy to database
844
+
845
+ Examples
846
+ --------
847
+
848
+ >>> properties = {'symbology': 'TICKER'}
849
+ >>> location = {'archive_duration': 'continuous'}
850
+ >>> ref_db = otp.RefDB('REF_DATA_MYDB', db_properties=properties, db_location=location)
851
+ >>> session.use(ref_db)
852
+ >>>
853
+ >>> data = 'A||20100102000000|20100103000000|B||20100103000000|20100104000000|'
854
+ >>> out, err = ref_db.put([otp.RefDB.SymbolNameHistory(data, 'TICKER')])
855
+ >>> b'Total ticks 8' in err and b'Total symbols 6' in err
856
+ True
857
+ >>>
858
+ >>> properties = {'ref_data_db': ref_db.name, 'symbology': 'TICKER'}
859
+ >>> db = otp.DB('MYDB', db_properties=properties)
860
+ >>> session.use(db)
861
+ >>>
862
+ >>> data = otp.Ticks(X=['hello'], start=otp.dt(2010, 1, 2), end=otp.dt(2010, 1, 3))
863
+ >>> data = otp.run(data.write(db.name, 'A', 'MSG', date=otp.dt(2010, 1, 2)))
864
+ >>> data = otp.Ticks(X=['world!'], start=otp.dt(2010, 1, 3), end=otp.dt(2010, 1, 4))
865
+ >>> data = otp.run(data.write(db.name, 'B', 'MSG', date=otp.dt(2010, 1, 3)))
866
+ >>>
867
+ >>> data = otp.DataSource(db.name, tick_type='MSG')
868
+ >>> s_dt, e_dt, symbol_date = otp.dt(2010, 1, 1), otp.dt(2010, 1, 4), otp.dt(2010, 1, 2)
869
+ >>> otp.run(data, symbols='A', start=s_dt, end=e_dt, symbol_date=symbol_date)
870
+ Time X
871
+ 0 2010-01-02 hello
872
+ 1 2010-01-03 world!
873
+ """
874
+
875
+ def __init__(
876
+ self,
877
+ name=None,
878
+ kind='archive',
879
+ db_properties=None,
880
+ db_location=None,
881
+ write=True,
882
+ clean_up=utils.default,
883
+ destroy_access=False,
884
+ ):
885
+ # ref db must have a single location, pointing to a continuous archive database
886
+ # (its location in the locator file must have archive_duration=continuous set)
887
+ if db_location is None:
888
+ db_location = {}
889
+ db_location.setdefault('archive_duration', 'continuous')
890
+ super().__init__(
891
+ name=name,
892
+ kind=kind,
893
+ db_properties=db_properties,
894
+ db_locations=[db_location],
895
+ write=write,
896
+ clean_up=clean_up,
897
+ destroy_access=destroy_access,
898
+ )
899
+
900
+ class Section():
901
+ """ Specification of a reference database section. Section content can be specified as a string or source.
902
+ The format of string and output columns of source must correspond with the section documentation.
903
+
904
+ Parameters
905
+ ----------
906
+ name : str
907
+ Section name
908
+ data : str or :class:`otp.Source`
909
+ Content of the section
910
+ attrs : :obj:`dict`, optional
911
+ Attributes of the section
912
+
913
+ Examples
914
+ --------
915
+
916
+ Data provided as a string:
917
+
918
+ >>> data = 'SYM1|20100101093000|20100101110000' + os.linesep
919
+ >>> data += 'SYM2|20100101110000|20100103140000'
920
+ >>> section = otp.RefDB.Section('SECTION_NAME', data, {'ATTR1': 'VAL1', 'ATTR2': 'VAL2'})
921
+ >>> print(section)
922
+ <SECTION_NAME ATTR1="VAL1" ATTR2="VAL2">
923
+ SYM1|20100101093000|20100101110000
924
+ SYM2|20100101110000|20100103140000
925
+ </SECTION_NAME>
926
+
927
+ Data provided as a :class:`otp.Source`:
928
+
929
+ >>> data = dict()
930
+ >>> data['SYMBOL_NAME'] = ['SYM1', 'SYM2']
931
+ >>> data['START_DATETIME'] = [otp.dt(2010, 1, 1, 9, 30, tz='EST5EDT'), otp.dt(2010, 1, 1, 11, tz='EST5EDT')]
932
+ >>> data['END_DATETIME'] = [otp.dt(2010, 1, 1, 11, tz='EST5EDT'), otp.dt(2010, 1, 3, 14, tz='EST5EDT')]
933
+ >>> ticks = otp.Ticks(**data, offset=[0] * 2, db='LOCAL')
934
+ >>> section = otp.RefDB.Section('SECTION_NAME', ticks, {'ATTR1': 'VAL1', 'ATTR2': 'VAL2'})
935
+ >>> print(section) # doctest:+ELLIPSIS
936
+ <SECTION_NAME ATTR1="VAL1" ATTR2="VAL2" OTQ_QUERY=...>
937
+ </SECTION_NAME>
938
+
939
+ where OTQ_QUERY is path to :class:`otp.Source`, dumped to disk as temporary .otq file.
940
+ """
941
+ # Read ref db guide for details on input format of sections
942
+ # http://solutions.pages.soltest.onetick.com/iac/onetick-server/ReferenceDatabaseGuide.html
943
+ def __init__(self, name: str, data: Union[str, 'otp.Source'], attrs: Optional[dict] = None):
944
+ self._name = name
945
+ self._data = data
946
+ self._attrs = ' '.join([f'{name}="{value}"' for name, value in attrs.items()]) if attrs else ''
947
+
948
+ def __str__(self):
949
+ if isinstance(self._data, str):
950
+ return f'<{self._name} {self._attrs}>{os.linesep}{self._data}{os.linesep}</{self._name}>'
951
+ otq = self._data.to_otq()
952
+ return f'<{self._name} {self._attrs} OTQ_QUERY={otq}>{os.linesep}</{self._name}>'
953
+
954
+ class SymbolNameHistory(Section):
955
+ """ Describes symbol changes for the same security. The continuity can be expressed in terms of any symbol type
956
+ and can be specified on the security level or the security+exchange level (more explicit).
957
+
958
+ Examples
959
+ --------
960
+
961
+ >>> data = 'CORE_A||20100101093000|20100101110000|CORE_B||20100101110000|20100103140000|'
962
+ >>> section = otp.RefDB.SymbolNameHistory(data, symbology='CORE')
963
+ >>> print(section)
964
+ <SYMBOL_NAME_HISTORY SYMBOLOGY="CORE">
965
+ CORE_A||20100101093000|20100101110000|CORE_B||20100101110000|20100103140000|
966
+ </SYMBOL_NAME_HISTORY>
967
+
968
+ Equivalent :class:`otp.Source`:
969
+
970
+ >>> data = dict()
971
+ >>> data['SYMBOL_NAME'] = ['CORE_A'] * 2
972
+ >>> data['SYMBOL_NAME_IN_HISTORY'] = ['CORE_A', 'CORE_B']
973
+ >>> data['SYMBOL_START_DATETIME'] = [otp.dt(2010, 1, 2, tz='EST5EDT')] * 2
974
+ >>> data['SYMBOL_END_DATETIME'] = [otp.dt(2010, 1, 5, tz='EST5EDT')] * 2
975
+ >>> data['START_DATETIME'] = [otp.dt(2010, 1, 2, tz='EST5EDT'), otp.dt(2010, 1, 3, tz='EST5EDT')]
976
+ >>> data['END_DATETIME'] = [otp.dt(2010, 1, 3, tz='EST5EDT'), otp.dt(2010, 1, 4, tz='EST5EDT')]
977
+ >>> ticks = otp.Ticks(**data, offset=[0] * 2, db='LOCAL')
978
+ >>> section = otp.RefDB.SymbolNameHistory(ticks, symbology='CORE')
979
+ >>> print(section) # doctest:+ELLIPSIS
980
+ <SYMBOL_NAME_HISTORY SYMBOLOGY="CORE" OTQ_QUERY=...>
981
+ </SYMBOL_NAME_HISTORY>
982
+ """
983
+ def __init__(self, data: Union[str, 'otp.Source'], symbology: str):
984
+ super().__init__('SYMBOL_NAME_HISTORY', data, {'SYMBOLOGY': symbology})
985
+
986
+ class SymbologyMapping(Section):
987
+ """ Describes a history of mapping of symbols of one symbology to the symbols of another symbology.
988
+
989
+ Examples
990
+ --------
991
+
992
+ >>> data = 'A||20100101093000|20100101110000|CORE_A|' + os.linesep
993
+ >>> data += 'B||20100101110000|20100103140000|CORE_B|'
994
+ >>> section = otp.RefDB.SymbologyMapping(data, source_symbology='TICKER', dest_symbology='CORE')
995
+ >>> print(section)
996
+ <SYMBOLOGY_MAPPING SOURCE_SYMBOLOGY="TICKER" DEST_SYMBOLOGY="CORE">
997
+ A||20100101093000|20100101110000|CORE_A|
998
+ B||20100101110000|20100103140000|CORE_B|
999
+ </SYMBOLOGY_MAPPING>
1000
+
1001
+ Equivalent :class:`otp.Source`:
1002
+
1003
+ >>> data = dict()
1004
+ >>> data['SYMBOL_NAME'] = ['A', 'B']
1005
+ >>> data['MAPPED_SYMBOL_NAME'] = ['CORE_A', 'CORE_B']
1006
+ >>> data['START_DATETIME'] = [otp.dt(2010, 1, 2, tz='EST5EDT'), otp.dt(2010, 1, 3, tz='EST5EDT')]
1007
+ >>> data['END_DATETIME'] = [otp.dt(2010, 1, 3, tz='EST5EDT'), otp.dt(2010, 1, 4, tz='EST5EDT')]
1008
+ >>> ticks = otp.Ticks(**data, offset=[0] * 2, db='LOCAL')
1009
+ >>> section = otp.RefDB.SymbologyMapping(ticks, source_symbology='TICKER', dest_symbology='CORE')
1010
+ >>> print(section) # doctest:+ELLIPSIS
1011
+ <SYMBOLOGY_MAPPING SOURCE_SYMBOLOGY="TICKER" DEST_SYMBOLOGY="CORE" OTQ_QUERY=...>
1012
+ </SYMBOLOGY_MAPPING>
1013
+ """
1014
+ def __init__(self, data: Union[str, 'otp.Source'], source_symbology: str, dest_symbology: str):
1015
+ super().__init__('SYMBOLOGY_MAPPING', data, {'SOURCE_SYMBOLOGY': source_symbology,
1016
+ 'DEST_SYMBOLOGY': dest_symbology})
1017
+
1018
+ class CorpActions(Section):
1019
+ """ Describes corporate actions. Used by OneTick to compute prices adjusted for various types of
1020
+ corporate actions. Supports both built-in and custom (user-defined) types of corporate actions.
1021
+
1022
+ Examples
1023
+ --------
1024
+
1025
+ >>> data = 'CORE_C||20100103180000|0.25|0.0|SPLIT'
1026
+ >>> section = otp.RefDB.CorpActions(data, symbology='CORE')
1027
+ >>> print(section)
1028
+ <CORP_ACTIONS SYMBOLOGY="CORE">
1029
+ CORE_C||20100103180000|0.25|0.0|SPLIT
1030
+ </CORP_ACTIONS>
1031
+
1032
+ Equivalent :class:`otp.Source`:
1033
+
1034
+ >>> data = dict()
1035
+ >>> data['SYMBOL_NAME'] = ['CORE_C']
1036
+ >>> data['EFFECTIVE_DATETIME'] = [otp.dt(2010, 1, 3, 18, tz='EST5EDT')]
1037
+ >>> data['MULTIPLICATIVE_ADJUSTMENT'] = [0.25]
1038
+ >>> data['ADDITIVE_ADJUSTMENT'] = [0.0]
1039
+ >>> data['ADJUSTMENT_TYPE_NAME'] = ['SPLIT']
1040
+ >>> ticks = otp.Ticks(**data, offset=[0], db='LOCAL')
1041
+ >>> section = otp.RefDB.CorpActions(ticks, symbology='CORE')
1042
+ >>> print(section) # doctest:+ELLIPSIS
1043
+ <CORP_ACTIONS SYMBOLOGY="CORE" OTQ_QUERY=...>
1044
+ </CORP_ACTIONS>
1045
+ """
1046
+ def __init__(self, data: Union[str, 'otp.Source'], symbology: str):
1047
+ super().__init__('CORP_ACTIONS', data, {'SYMBOLOGY': symbology})
1048
+
1049
+ class ContinuousContracts(Section):
1050
+ """ Describes continuous contracts. Continuity is expressed in terms of stitched history
1051
+ of real contracts and rollover adjustments in between them and can be specified
1052
+ on the continuous contract level or continuous contract+exchange level (more explicit).
1053
+
1054
+ Examples
1055
+ --------
1056
+
1057
+ >>> data = 'CC||CORE_A||20100101093000|20100101110000|0.5|0|CORE_B||20100101110000|20100103140000'
1058
+ >>> section = otp.RefDB.ContinuousContracts(data, symbology='CORE')
1059
+ >>> print(section)
1060
+ <CONTINUOUS_CONTRACTS SYMBOLOGY="CORE">
1061
+ CC||CORE_A||20100101093000|20100101110000|0.5|0|CORE_B||20100101110000|20100103140000
1062
+ </CONTINUOUS_CONTRACTS>
1063
+
1064
+ Equivalent :class:`otp.Source`:
1065
+
1066
+ >>> data = dict()
1067
+ >>> data['CONTINUOUS_CONTRACT_NAME'] = ['CC'] * 2
1068
+ >>> data['SYMBOL_NAME'] = ['CORE_A', 'CORE_B']
1069
+ >>> data['START_DATETIME'] = [otp.dt(2010, 1, 2, tz='EST5EDT'), otp.dt(2010, 1, 3, tz='EST5EDT')]
1070
+ >>> data['END_DATETIME'] = [otp.dt(2010, 1, 3, tz='EST5EDT'), otp.dt(2010, 1, 4, tz='EST5EDT')]
1071
+ >>> data['MULTIPLICATIVE_ADJUSTMENT'] = [0.5, None]
1072
+ >>> data['ADDITIVE_ADJUSTMENT'] = [3, None]
1073
+ >>> ticks = otp.Ticks(**data, offset=[0] * 2, db='LOCAL')
1074
+ >>> section = otp.RefDB.ContinuousContracts(ticks, symbology='CORE')
1075
+ >>> print(section) # doctest:+ELLIPSIS
1076
+ <CONTINUOUS_CONTRACTS SYMBOLOGY="CORE" OTQ_QUERY=...>
1077
+ </CONTINUOUS_CONTRACTS>
1078
+ """
1079
+ def __init__(self, data: Union[str, 'otp.Source'], symbology: str):
1080
+ super().__init__('CONTINUOUS_CONTRACTS', data, {'SYMBOLOGY': symbology})
1081
+
1082
+ class SymbolCurrency(Section):
1083
+ """ Specifies symbols' currencies in 3-letter ISO codes for currencies. These are used for currency conversion
1084
+ (e.g., when calculating portfolio price for a list of securities with different currencies).
1085
+
1086
+ Examples
1087
+ --------
1088
+
1089
+ >>> data = 'CORE_A||20100101093000|20100101110000|USD|1.0' + os.linesep
1090
+ >>> data += 'CORE_B||20100101110000|20100103140000|RUB|1.8'
1091
+ >>> section = otp.RefDB.SymbolCurrency(data, symbology='CORE')
1092
+ >>> print(section)
1093
+ <SYMBOL_CURRENCY SYMBOLOGY="CORE">
1094
+ CORE_A||20100101093000|20100101110000|USD|1.0
1095
+ CORE_B||20100101110000|20100103140000|RUB|1.8
1096
+ </SYMBOL_CURRENCY>
1097
+
1098
+ Equivalent :class:`otp.Source`:
1099
+
1100
+ >>> data = dict()
1101
+ >>> data['SYMBOL_NAME'] = ['CORE_A', 'CORE_B',]
1102
+ >>> data['CURRENCY'] = ['USD', 'RUB']
1103
+ >>> data['MULTIPLIER'] = [1., 1.8]
1104
+ >>> data['START_DATETIME'] = [otp.dt(2010, 1, 1, 9, 30, tz='EST5EDT'), otp.dt(2010, 1, 1, 11, tz='EST5EDT')]
1105
+ >>> data['END_DATETIME'] = [otp.dt(2010, 1, 1, 11, tz='EST5EDT'), otp.dt(2010, 1, 3, 14, tz='EST5EDT')]
1106
+ >>> ticks = otp.Ticks(**data, offset=[0] * 2, db='LOCAL')
1107
+ >>> section = otp.RefDB.SymbolCurrency(ticks, symbology='CORE')
1108
+ >>> print(section) # doctest:+ELLIPSIS
1109
+ <SYMBOL_CURRENCY SYMBOLOGY="CORE" OTQ_QUERY=...>
1110
+ </SYMBOL_CURRENCY>
1111
+ """
1112
+ def __init__(self, data: Union[str, 'otp.Source'], symbology: str):
1113
+ super().__init__('SYMBOL_CURRENCY', data, {'SYMBOLOGY': symbology})
1114
+
1115
+ class Calendar(Section):
1116
+ """ Specifies a named calendar. Needed to analyze tick data during specific market time intervals (i.e., during
1117
+ normal trading hours). Can either be used directly in queries as described below, or referred to
1118
+ from the SYMBOL_CALENDAR and EXCH_CALENDAR sections.
1119
+
1120
+ Examples
1121
+ --------
1122
+
1123
+ >>> data = 'CAL1|20100101093000|20100101110000|Regular|R|0.0.12345|093000|160000|GMT|1|DESCRIPTION1'
1124
+ >>> data += os.linesep
1125
+ >>> data += 'CAL2|20100101110000|20100103140000|Holiday|F|0.0.12345|094000|170000|GMT|0|DESCRIPTION2'
1126
+ >>> section = otp.RefDB.Calendar(data)
1127
+ >>> print(section)
1128
+ <CALENDAR >
1129
+ CAL1|20100101093000|20100101110000|Regular|R|0.0.12345|093000|160000|GMT|1|DESCRIPTION1
1130
+ CAL2|20100101110000|20100103140000|Holiday|F|0.0.12345|094000|170000|GMT|0|DESCRIPTION2
1131
+ </CALENDAR>
1132
+
1133
+ Equivalent :class:`otp.Source`:
1134
+
1135
+ >>> data = dict()
1136
+ >>> data['CALENDAR_NAME'] = ['CAL1', 'CAL2']
1137
+ >>> data['START_DATETIME'] = [otp.dt(2010, 1, 1, 9, 30, tz='EST5EDT'), otp.dt(2010, 1, 1, 11, tz='EST5EDT')]
1138
+ >>> data['END_DATETIME'] = [otp.dt(2010, 1, 1, 11, tz='EST5EDT'), otp.dt(2010, 1, 3, 14, tz='EST5EDT')]
1139
+ >>> data['SESSION_NAME'] = ['Regular', 'Holiday']
1140
+ >>> data['SESSION_FLAGS'] = ['R', 'H']
1141
+ >>> data['DAY_PATTERN'] = ['0.0.12345', '0.0.12345']
1142
+ >>> data['START_HHMMSS'] = ['093000', '094000']
1143
+ >>> data['END_HHMMSS'] = ['160000', '170000']
1144
+ >>> data['TIMEZONE'] = ['GMT', 'GMT']
1145
+ >>> data['PRIORITY'] = [1, 0]
1146
+ >>> data['DESCRIPTION'] = ['DESCRIPTION1', 'DESCRIPTION2']
1147
+ >>> ticks = otp.Ticks(**data, offset=[0] * 2, db='LOCAL')
1148
+ >>> section = otp.RefDB.Calendar(ticks)
1149
+ >>> print(section) # doctest:+ELLIPSIS
1150
+ <CALENDAR OTQ_QUERY=...>
1151
+ </CALENDAR>
1152
+ """
1153
+ def __init__(self, data: Union[str, 'otp.Source']):
1154
+ super().__init__('CALENDAR', data)
1155
+
1156
+ class SymbolCalendar(Section):
1157
+ """ Specifies a calendar for a symbol. Needed to analyze tick data during specific market time intervals
1158
+ (i.e., during normal trading hours). Can either be specified directly or refer to a named calendar by its name
1159
+ (see the CALENDAR section).
1160
+
1161
+ Examples
1162
+ --------
1163
+
1164
+ Symbol calendar section, referring to named calendar section:
1165
+
1166
+ >>> data = 'CORE_A|20100101093000|20100101110000|CAL1' + os.linesep
1167
+ >>> data += 'CORE_B|20100101110000|20100103140000|CAL2'
1168
+ >>> section = otp.RefDB.SymbolCalendar(data, symbology='CORE')
1169
+ >>> print(section)
1170
+ <SYMBOL_CALENDAR SYMBOLOGY="CORE">
1171
+ CORE_A|20100101093000|20100101110000|CAL1
1172
+ CORE_B|20100101110000|20100103140000|CAL2
1173
+ </SYMBOL_CALENDAR>
1174
+
1175
+ Equivalent :class:`otp.Source`:
1176
+
1177
+ >>> data = dict()
1178
+ >>> data['SYMBOL_NAME'] = ['CORE_A', 'CORE_B']
1179
+ >>> data['START_DATETIME'] = [otp.dt(2010, 1, 1, 9, 30, tz='EST5EDT'), otp.dt(2010, 1, 1, 11, tz='EST5EDT')]
1180
+ >>> data['END_DATETIME'] = [otp.dt(2010, 1, 1, 11, tz='EST5EDT'), otp.dt(2010, 1, 3, 14, tz='EST5EDT')]
1181
+ >>> data['CALENDAR_NAME'] = ['CAL1', 'CAL2']
1182
+ >>> ticks = otp.Ticks(**data, offset=[0] * 2, db='LOCAL')
1183
+ >>> section = otp.RefDB.SymbolCalendar(ticks, symbology='CORE')
1184
+ >>> print(section) # doctest:+ELLIPSIS
1185
+ <SYMBOL_CALENDAR SYMBOLOGY="CORE" OTQ_QUERY=...>
1186
+ </SYMBOL_CALENDAR>
1187
+
1188
+ Symbol calendar section without using named calendar section:
1189
+
1190
+ >>> data = 'CORE_A|20100101093000|20100101110000|Regular|R|0.0.12345|093000|160000|EST5EDT|1|' + os.linesep
1191
+ >>> data += 'CORE_B|20100101110000|20100103140000|Regular|F|0.0.12345|093000|160000|EST5EDT|1|'
1192
+ >>> section = otp.RefDB.SymbolCalendar(data, symbology='CORE')
1193
+ >>> print(section)
1194
+ <SYMBOL_CALENDAR SYMBOLOGY="CORE">
1195
+ CORE_A|20100101093000|20100101110000|Regular|R|0.0.12345|093000|160000|EST5EDT|1|
1196
+ CORE_B|20100101110000|20100103140000|Regular|F|0.0.12345|093000|160000|EST5EDT|1|
1197
+ </SYMBOL_CALENDAR>
1198
+
1199
+ Equivalent :class:`otp.Source`:
1200
+
1201
+ >>> data = dict()
1202
+ >>> data['SYMBOL_NAME'] = ['CORE_A', 'CORE_B']
1203
+ >>> data['START_DATETIME'] = [otp.dt(2010, 1, 1, 9, 30, tz='EST5EDT'), otp.dt(2010, 1, 1, 11, tz='EST5EDT')]
1204
+ >>> data['END_DATETIME'] = [otp.dt(2010, 1, 1, 11, tz='EST5EDT'), otp.dt(2010, 1, 3, 14, tz='EST5EDT')]
1205
+ >>> data['SESSION_NAME'] = ['Regular', 'Regular']
1206
+ >>> data['SESSION_FLAGS'] = ['R', 'F']
1207
+ >>> data['DAY_PATTERN'] = ['0.0.12345', '0.0.12345']
1208
+ >>> data['START_HHMMSS'] = ['093000', '160000']
1209
+ >>> data['END_HHMMSS'] = ['CAL1', 'CAL2']
1210
+ >>> data['TIMEZONE'] = ['EST5EDT', 'EST5EDT']
1211
+ >>> data['PRIORITY'] = [1, 1]
1212
+ >>> data['DESCRIPTION'] = ['', '']
1213
+ >>> ticks = otp.Ticks(**data, offset=[0] * 2, db='LOCAL')
1214
+ >>> section = otp.RefDB.SymbolCalendar(ticks, symbology='CORE')
1215
+ >>> print(section) # doctest:+ELLIPSIS
1216
+ <SYMBOL_CALENDAR SYMBOLOGY="CORE" OTQ_QUERY=...>
1217
+ </SYMBOL_CALENDAR>
1218
+ """
1219
+ def __init__(self, data: Union[str, 'otp.Source'], symbology: str):
1220
+ super().__init__('SYMBOL_CALENDAR', data, {'SYMBOLOGY': symbology})
1221
+
1222
+ class SectionStr(Section):
1223
+ """ Specification of a reference database section that can be specified only as a string.
1224
+ Section content still can be provided as a :class:`otp.Source`, but the :class:`otp.Source` is executed and
1225
+ result data is used as string in section. It's up to user to provide :class:`otp.Source` with correct number
1226
+ and order of columns.
1227
+
1228
+ Examples
1229
+ --------
1230
+
1231
+ Data provided as a string returns the same result as :class:`otp.RefDB.Section`.
1232
+
1233
+ Data provided as a :class:`otp.Source`:
1234
+
1235
+ >>> data = dict()
1236
+ >>> data['SYMBOL_NAME'] = ['SYM1', 'SYM2']
1237
+ >>> data['START_DATETIME'] = [otp.dt(2010, 1, 1, 9, 30, tz='EST5EDT'), otp.dt(2010, 1, 1, 11, tz='EST5EDT')]
1238
+ >>> data['END_DATETIME'] = [otp.dt(2010, 1, 1, 11, tz='EST5EDT'), otp.dt(2010, 1, 3, 14, tz='EST5EDT')]
1239
+ >>> ticks = otp.Ticks(**data, offset=[0] * 2, db='LOCAL')
1240
+ >>> ticks = ticks.table(SYMBOL_NAME=otp.string[128], START_DATETIME=otp.msectime, END_DATETIME=otp.msectime)
1241
+ >>> section = otp.RefDB.SectionStr('SECTION_NAME', ticks, {'ATTR1': 'VAL1', 'ATTR2': 'VAL2'})
1242
+ >>> print(section) # doctest:+ELLIPSIS
1243
+ <SECTION_NAME ATTR1="VAL1" ATTR2="VAL2">
1244
+ SYM1|20100101093000|20100101110000
1245
+ SYM2|20100101110000|20100103140000
1246
+ </SECTION_NAME>
1247
+
1248
+ where OTQ_QUERY is path to :class:`otp.Source`, dumped to disk as temporary .otq file.
1249
+ """
1250
+ def __init__(self, name: str, data: Union[str, 'otp.Source'], attrs: Optional[dict] = None):
1251
+ data_str = data if isinstance(data, str) else self._source_to_str(data)
1252
+ super().__init__(name, data_str, attrs)
1253
+
1254
+ def _source_to_str(self, data: 'otp.Source'):
1255
+ df = otp.run(data)
1256
+ df.drop(columns=['Time'], inplace=True)
1257
+ df = df.to_csv(sep='|', header=False, index=False, date_format='%Y%m%d%H%M%S')
1258
+ return df
1259
+
1260
+ class PrimaryExchange(SectionStr):
1261
+ """ Specifies symbols' primary exchanges. Used to extract and analyze tick data for a security on
1262
+ its primary exchange, without having to explicitly specify the name of the primary exchange.
1263
+
1264
+ Examples
1265
+ --------
1266
+
1267
+ >>> data = 'A||19991118000000|99999999000000|N|'
1268
+ >>> data += os.linesep
1269
+ >>> data += 'AA||19991118000000|99999999000000|N|AA.N'
1270
+ >>> section = otp.RefDB.PrimaryExchange(data, symbology='TICKER')
1271
+ >>> print(section)
1272
+ <PRIMARY_EXCHANGE SYMBOLOGY="TICKER">
1273
+ A||19991118000000|99999999000000|N|
1274
+ AA||19991118000000|99999999000000|N|AA.N
1275
+ </PRIMARY_EXCHANGE>
1276
+
1277
+ Equivalent query should return the same data values in the same order. Column names does not matter.
1278
+ """
1279
+ def __init__(self, data: Union[str, 'otp.Source'], symbology: str):
1280
+ super().__init__('PRIMARY_EXCHANGE', data, {'SYMBOLOGY': symbology})
1281
+
1282
+ class ExchCalendar(SectionStr):
1283
+ """ Specifies symbols' primary exchanges. Used to extract and analyze tick data for a security on
1284
+ its primary exchange, without having to explicitly specify the name of the primary exchange.
1285
+
1286
+ Examples
1287
+ --------
1288
+
1289
+ >>> data = 'NYSE||19600101000000|20501231235959|Regular|R|0.0.12345|093000|160000|EST5EDT|'
1290
+ >>> data += os.linesep
1291
+ >>> data += 'NYSE||19600101000000|20501231235959|Half-day|RL|12/31|093000|130000|EST5EDT|'
1292
+ >>> data += os.linesep
1293
+ >>> data += 'NYSE||19600101000000|20501231235959|Holiday|H|01/01|000000|240000|EST5EDT|'
1294
+ >>> section = otp.RefDB.ExchCalendar(data, symbology='MIC')
1295
+ >>> print(section)
1296
+ <EXCH_CALENDAR SYMBOLOGY="MIC">
1297
+ NYSE||19600101000000|20501231235959|Regular|R|0.0.12345|093000|160000|EST5EDT|
1298
+ NYSE||19600101000000|20501231235959|Half-day|RL|12/31|093000|130000|EST5EDT|
1299
+ NYSE||19600101000000|20501231235959|Holiday|H|01/01|000000|240000|EST5EDT|
1300
+ </EXCH_CALENDAR>
1301
+
1302
+ If a CALENDAR section is used:
1303
+
1304
+ >>> data = 'LSE||19600101000000|20501231235959|WNY'
1305
+ >>> section = otp.RefDB.ExchCalendar(data, symbology='MIC')
1306
+ >>> print(section)
1307
+ <EXCH_CALENDAR SYMBOLOGY="MIC">
1308
+ LSE||19600101000000|20501231235959|WNY
1309
+ </EXCH_CALENDAR>
1310
+
1311
+ Equivalent query should return the same data values in the same order. Column names does not matter.
1312
+ """
1313
+ def __init__(self, data: Union[str, 'otp.Source'], symbology: str):
1314
+ super().__init__('EXCH_CALENDAR', data, {'SYMBOLOGY': symbology})
1315
+
1316
+ class SymbolExchange(SectionStr):
1317
+ """ Specifies the exchange where a security is traded. Needs to be provided for the symbologies where
1318
+ the symbol name is unique across all exchanges.
1319
+
1320
+ Examples
1321
+ --------
1322
+
1323
+ >>> data = 'IBM.N|19980825000000|20501231235959|NYSE||'
1324
+ >>> section = otp.RefDB.SymbolExchange(data, symbology='RIC', exchange_symbology='MIC')
1325
+ >>> print(section)
1326
+ <SYMBOL_EXCHANGE SYMBOLOGY="RIC" EXCHANGE_SYMBOLOGY="MIC">
1327
+ IBM.N|19980825000000|20501231235959|NYSE||
1328
+ </SYMBOL_EXCHANGE>
1329
+
1330
+ Equivalent query should return the same data values in the same order. Column names does not matter.
1331
+ """
1332
+ def __init__(self, data: Union[str, 'otp.Source'], symbology: str, exchange_symbology: str):
1333
+ super().__init__('SYMBOL_EXCHANGE', data, {'SYMBOLOGY': symbology,
1334
+ 'EXCHANGE_SYMBOLOGY': exchange_symbology})
1335
+
1336
+ def put(
1337
+ self,
1338
+ src: Union[str, List[Section]],
1339
+ tickdb_symbology: Optional[List[str]] = None,
1340
+ delta_mode: bool = False,
1341
+ full_integrity_check: bool = False,
1342
+ load_by_sections: bool = True,
1343
+ ):
1344
+ """
1345
+ Loads data in database with reference_data_loader.exe.
1346
+ If db properties contain SUPPORT_DELTAS=YES, delta_mode set to True, and proper delta file is used
1347
+ then data is loaded in incremental mode (in other words, replace or modification mode).
1348
+ If the above conditions are not met, reference database content is entirely rewritten with the new data.
1349
+
1350
+ Parameters
1351
+ ----------
1352
+ src : str, list of str or otp.RefDB.Section
1353
+ Path to data file, or list of data per section in specified format
1354
+ tickdb_symbology : list of str, optional
1355
+ All symbologies for which the reference data needs to be generated
1356
+ delta_mode : bool, default is False
1357
+ If set to True loader will perform incremental load. Cannot be used if ``tickdb_symbology`` is specified
1358
+ full_integrity_check : bool, default is False
1359
+ If set to True loader checks all mappings to symbologies with symbol name history section
1360
+ and gives warning if mapped securities do not have symbol name history
1361
+ load_by_sections : bool, default is True
1362
+ If set to True loader will perform input data file splitting by data types and symbologies
1363
+ to load each part separately instead loading the entire file at once
1364
+ """
1365
+ # More info:
1366
+ # http://solutions.pages.soltest.onetick.com/iac/onetick-server/reference_data_loader.html - loader doc
1367
+ # https://onemarketdata.atlassian.net/browse/KB-286 - details on delta mode
1368
+ return self._session_handler(
1369
+ self._put,
1370
+ src=src,
1371
+ delta_mode=delta_mode,
1372
+ full_integrity_check=full_integrity_check,
1373
+ load_by_sections=load_by_sections,
1374
+ tickdb_symbology=tickdb_symbology,
1375
+ )
1376
+
1377
+ def _prepare_data_file(self, src):
1378
+ if isinstance(src, str):
1379
+ return src
1380
+
1381
+ data = f'<VERSION_INFO VERSION="1">{os.linesep}</VERSION_INFO>'
1382
+ for section in src:
1383
+ data += f'{os.linesep}{os.linesep}{section}'
1384
+
1385
+ data_file = utils.TmpFile(suffix='.txt')
1386
+ with open(data_file.path, 'w') as f:
1387
+ f.writelines(data)
1388
+ return data_file.path
1389
+
1390
+ def _prepare_loader_args(self, data_file, tickdb_symbology, delta_mode, full_integrity_check, load_by_sections):
1391
+ loader_args = ['-dbname', self.name]
1392
+ loader_args += ['-data_file', data_file]
1393
+ if tickdb_symbology:
1394
+ for symbology in tickdb_symbology:
1395
+ loader_args += ['-tickdb_symbology', symbology]
1396
+ loader_args += ['-delta_mode', 'yes' if delta_mode else 'no']
1397
+ loader_args += ['-full_integrity_check', 'yes' if full_integrity_check else 'no']
1398
+ loader_args += ['-load_by_sections', 'yes' if load_by_sections else 'no']
1399
+ return loader_args
1400
+
1401
+ def _put(self, src, tickdb_symbology, delta_mode, full_integrity_check, load_by_sections):
1402
+ if otp.__webapi__:
1403
+ raise NotImplementedError("Reference database loader is not supported in WebAPI mode.")
1404
+ data_file = self._prepare_data_file(src)
1405
+ loader_args = self._prepare_loader_args(
1406
+ data_file, tickdb_symbology, delta_mode, full_integrity_check, load_by_sections
1407
+ )
1408
+ loader_path = os.path.join(utils.omd_dist_path(), 'one_tick', 'bin', 'reference_data_loader.exe')
1409
+ p = subprocess.run(
1410
+ [loader_path] + loader_args,
1411
+ env={
1412
+ 'ONE_TICK_CONFIG': session.Session._instance.config.path,
1413
+ 'TZ': os.environ.get('TZ', otp.config['tz']),
1414
+ },
1415
+ stdout=subprocess.PIPE,
1416
+ stderr=subprocess.PIPE,
1417
+ )
1418
+ return p.stdout, p.stderr
1419
+
1420
+ def add(self, *args, **kwargs):
1421
+ # this method is not implemented because
1422
+ # reference database loader can only rewrite the data, not add new entries
1423
+ raise NotImplementedError("Method is not supported for reference databases. Use put instead.")