onetick-py 1.181.0__tar.gz → 1.183.0__tar.gz

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 (163) hide show
  1. {onetick_py-1.181.0/src/onetick_py.egg-info → onetick_py-1.183.0}/PKG-INFO +1 -1
  2. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/locator_parser/locator.py +5 -0
  3. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/_version.py +1 -1
  4. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/aggregations/__init__.py +1 -1
  5. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/aggregations/_docs.py +38 -0
  6. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/aggregations/functions.py +45 -3
  7. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/aggregations/other.py +53 -0
  8. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/compatibility.py +50 -23
  9. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_source/source_methods/aggregations.py +6 -0
  10. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_source/source_methods/debugs.py +14 -2
  11. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_source/source_methods/fields.py +4 -0
  12. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_source/source_methods/filters.py +62 -0
  13. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/source.py +12 -3
  14. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/db/_inspection.py +3 -1
  15. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/run.py +28 -16
  16. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/utils/render.py +42 -2
  17. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/utils/render_cli.py +9 -1
  18. {onetick_py-1.181.0 → onetick_py-1.183.0/src/onetick_py.egg-info}/PKG-INFO +1 -1
  19. {onetick_py-1.181.0 → onetick_py-1.183.0}/LICENSE +0 -0
  20. {onetick_py-1.181.0 → onetick_py-1.183.0}/README.md +0 -0
  21. {onetick_py-1.181.0 → onetick_py-1.183.0}/pyproject.toml +0 -0
  22. {onetick_py-1.181.0 → onetick_py-1.183.0}/requirements.strict.txt +0 -0
  23. {onetick_py-1.181.0 → onetick_py-1.183.0}/requirements.txt +0 -0
  24. {onetick_py-1.181.0 → onetick_py-1.183.0}/setup.cfg +0 -0
  25. {onetick_py-1.181.0 → onetick_py-1.183.0}/setup.py +0 -0
  26. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/locator_parser/__init__.py +0 -0
  27. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/locator_parser/acl.py +0 -0
  28. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/locator_parser/actions.py +0 -0
  29. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/locator_parser/common.py +0 -0
  30. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/locator_parser/io.py +0 -0
  31. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/__init__.py +0 -0
  32. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/doc_utilities/__init__.py +0 -0
  33. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/doc_utilities/napoleon.py +0 -0
  34. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/doc_utilities/ot_doctest.py +0 -0
  35. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/doc_utilities/snippets.py +0 -0
  36. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/lib/__init__.py +0 -0
  37. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/lib/instance.py +0 -0
  38. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/__init__.py +0 -0
  39. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/_stack_info.py +0 -0
  40. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/aggregations/_base.py +0 -0
  41. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/aggregations/compute.py +0 -0
  42. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/aggregations/generic.py +0 -0
  43. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/aggregations/high_low.py +0 -0
  44. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/aggregations/num_distinct.py +0 -0
  45. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/aggregations/order_book.py +0 -0
  46. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/backports.py +0 -0
  47. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/cache.py +0 -0
  48. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/callback/__init__.py +0 -0
  49. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/callback/callback.py +0 -0
  50. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/callback/callbacks.py +0 -0
  51. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/configuration.py +0 -0
  52. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/__init__.py +0 -0
  53. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_csv_inspector.py +0 -0
  54. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_internal/__init__.py +0 -0
  55. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_internal/_manually_bound_value.py +0 -0
  56. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_internal/_nodes_history.py +0 -0
  57. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_internal/_op_utils/__init__.py +0 -0
  58. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_internal/_op_utils/every_operand.py +0 -0
  59. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_internal/_op_utils/is_const.py +0 -0
  60. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_internal/_per_tick_scripts/tick_list_sort_template.script +0 -0
  61. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_internal/_proxy_node.py +0 -0
  62. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_internal/_state_objects.py +0 -0
  63. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_internal/_state_vars.py +0 -0
  64. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_source/__init__.py +0 -0
  65. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_source/_symbol_param.py +0 -0
  66. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_source/schema.py +0 -0
  67. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_source/source_methods/__init__.py +0 -0
  68. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_source/source_methods/applyers.py +0 -0
  69. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_source/source_methods/columns.py +0 -0
  70. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_source/source_methods/data_quality.py +0 -0
  71. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_source/source_methods/drops.py +0 -0
  72. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_source/source_methods/joins.py +0 -0
  73. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_source/source_methods/merges.py +0 -0
  74. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_source/source_methods/misc.py +0 -0
  75. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_source/source_methods/pandases.py +0 -0
  76. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_source/source_methods/renames.py +0 -0
  77. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_source/source_methods/sorts.py +0 -0
  78. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_source/source_methods/switches.py +0 -0
  79. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_source/source_methods/symbols.py +0 -0
  80. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_source/source_methods/times.py +0 -0
  81. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_source/source_methods/writes.py +0 -0
  82. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_source/symbol.py +0 -0
  83. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/_source/tmp_otq.py +0 -0
  84. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/column.py +0 -0
  85. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/column_operations/__init__.py +0 -0
  86. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/column_operations/_methods/__init__.py +0 -0
  87. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/column_operations/_methods/_internal.py +0 -0
  88. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/column_operations/_methods/conversions.py +0 -0
  89. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/column_operations/_methods/methods.py +0 -0
  90. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/column_operations/_methods/op_types.py +0 -0
  91. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/column_operations/accessors/__init__.py +0 -0
  92. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/column_operations/accessors/_accessor.py +0 -0
  93. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/column_operations/accessors/decimal_accessor.py +0 -0
  94. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/column_operations/accessors/dt_accessor.py +0 -0
  95. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/column_operations/accessors/float_accessor.py +0 -0
  96. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/column_operations/accessors/str_accessor.py +0 -0
  97. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/column_operations/base.py +0 -0
  98. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/cut_builder.py +0 -0
  99. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/db_constants.py +0 -0
  100. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/eval_query.py +0 -0
  101. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/lambda_object.py +0 -0
  102. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/multi_output_source.py +0 -0
  103. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/per_tick_script.py +0 -0
  104. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/core/query_inspector.py +0 -0
  105. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/db/__init__.py +0 -0
  106. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/db/db.py +0 -0
  107. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/db/utils.py +0 -0
  108. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/docs/__init__.py +0 -0
  109. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/docs/docstring_parser.py +0 -0
  110. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/docs/utils.py +0 -0
  111. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/functions.py +0 -0
  112. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/license.py +0 -0
  113. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/log.py +0 -0
  114. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/math.py +0 -0
  115. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/misc.py +0 -0
  116. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/oqd/__init__.py +0 -0
  117. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/oqd/eps.py +0 -0
  118. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/oqd/sources.py +0 -0
  119. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/otq.py +0 -0
  120. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/pyomd_mock.py +0 -0
  121. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/servers.py +0 -0
  122. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/session.py +0 -0
  123. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/sources/__init__.py +0 -0
  124. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/sources/cache.py +0 -0
  125. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/sources/common.py +0 -0
  126. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/sources/csv.py +0 -0
  127. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/sources/custom.py +0 -0
  128. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/sources/data_file.py +0 -0
  129. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/sources/data_source.py +0 -0
  130. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/sources/dataframe.py +0 -0
  131. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/sources/empty.py +0 -0
  132. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/sources/odbc.py +0 -0
  133. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/sources/order_book.py +0 -0
  134. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/sources/parquet.py +0 -0
  135. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/sources/pit.py +0 -0
  136. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/sources/query.py +0 -0
  137. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/sources/snapshots.py +0 -0
  138. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/sources/split_query_output_by_symbol.py +0 -0
  139. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/sources/symbology_mapping.py +0 -0
  140. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/sources/symbols.py +0 -0
  141. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/sources/ticks.py +0 -0
  142. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/sql.py +0 -0
  143. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/state.py +0 -0
  144. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/types.py +0 -0
  145. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/utils/__init__.py +0 -0
  146. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/utils/acl.py +0 -0
  147. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/utils/config.py +0 -0
  148. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/utils/debug.py +0 -0
  149. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/utils/default.py +0 -0
  150. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/utils/file.py +0 -0
  151. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/utils/helpers.py +0 -0
  152. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/utils/locator.py +0 -0
  153. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/utils/perf.py +0 -0
  154. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/utils/query.py +0 -0
  155. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/utils/script.py +0 -0
  156. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/utils/temp.py +0 -0
  157. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/utils/types.py +0 -0
  158. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick/py/utils/tz.py +0 -0
  159. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick_py.egg-info/SOURCES.txt +0 -0
  160. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick_py.egg-info/dependency_links.txt +0 -0
  161. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick_py.egg-info/entry_points.txt +0 -0
  162. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick_py.egg-info/requires.txt +0 -0
  163. {onetick_py-1.181.0 → onetick_py-1.183.0}/src/onetick_py.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: onetick-py
3
- Version: 1.181.0
3
+ Version: 1.183.0
4
4
  Summary: Python package that allows you to work with OneTick
5
5
  Author-email: solutions <solutions@onetick.com>
6
6
  License-Expression: MIT
@@ -135,6 +135,11 @@ def parse_locator(reader, writer, action=DoNothing(), recursively=False):
135
135
  cep_tick_servers_p(reader, writer, action)
136
136
  includes_p(reader, writer, action)
137
137
 
138
+ # force reset not full conditions match after processing root tags
139
+ if 0 < list(action.conditions.values()).count(True) < len(action.conditions):
140
+ for k in action.conditions:
141
+ action.conditions[k] = False
142
+
138
143
 
139
144
  # ----------------------------------------- #
140
145
  # set parent
@@ -1,2 +1,2 @@
1
1
  # This file was generated automatically. DO NOT CHANGE.
2
- VERSION = '1.181.0'
2
+ VERSION = '1.183.0'
@@ -3,7 +3,7 @@ from .functions import (compute, max, min, high_tick, low_tick, high_time, low_t
3
3
  ob_num_levels, ob_size, ob_snapshot, ob_snapshot_wide, ob_snapshot_flat, ob_summary, ob_vwap,
4
4
  generic, correlation, option_price, ranking, variance, percentile, find_value_for_percentile,
5
5
  exp_w_average, exp_tw_average, standardized_moment, portfolio_price, multi_portfolio_price,
6
- return_ep, implied_vol, linear_regression)
6
+ return_ep, implied_vol, linear_regression, partition_evenly_into_groups)
7
7
 
8
8
  try:
9
9
  from .num_distinct import num_distinct
@@ -858,6 +858,44 @@ _expiration_date_field_doc = param_doc(
858
858
  annotation=Union[str, Column],
859
859
  default='',
860
860
  )
861
+ _dependent_variable_field_name_doc = param_doc(
862
+ name='dependent_variable_field_name',
863
+ desc='''
864
+ Specifies the attribute used as the dependent variable in the calculation of the slope and intercept.
865
+ The ticks in the input time series must contain this attribute.
866
+ ''',
867
+ annotation=Union[str, Column],
868
+ )
869
+ _independent_variable_field_name_doc = param_doc(
870
+ name='independent_variable_field_name',
871
+ desc='''
872
+ Specifies the attribute used as the independent variable in the calculation of the slope and intercept.
873
+ The ticks in the input time series must contain this attribute.
874
+ ''',
875
+ annotation=Union[str, Column],
876
+ )
877
+ _field_to_partition_doc = param_doc(
878
+ name='field_to_partition',
879
+ desc='''
880
+ Specifies the tick field that will be partitioned.
881
+ ''',
882
+ annotation=Union[str, Column],
883
+ )
884
+ _weight_field_doc = param_doc(
885
+ name='weight_field',
886
+ desc='''
887
+ Specifies the tick field, the values of which are evaluated as weight; and then, the partitioning is be applied,
888
+ so that the total weight of the groups are as close as possible.
889
+ ''',
890
+ annotation=Union[str, Column],
891
+ )
892
+ _number_of_groups_doc = param_doc(
893
+ name='number_of_groups',
894
+ desc='''
895
+ Specifies the target number of partitions to which the tick should be divided.
896
+ ''',
897
+ annotation=int,
898
+ )
861
899
 
862
900
 
863
901
  class DocMetaclass(type):
@@ -9,7 +9,7 @@ from .other import (First, Last, FirstTime, LastTime, Count, Vwap, FirstTick,
9
9
  Median, Correlation, OptionPrice, Ranking, Variance,
10
10
  Percentile, FindValueForPercentile, ExpWAverage, ExpTwAverage,
11
11
  StandardizedMoment, PortfolioPrice, MultiPortfolioPrice, Return, ImpliedVol,
12
- LinearRegression)
12
+ LinearRegression, PartitionEvenlyIntoGroups)
13
13
  from .order_book import (ObSnapshot, OB_SNAPSHOT_DOC_PARAMS,
14
14
  ObSnapshotWide, OB_SNAPSHOT_WIDE_DOC_PARAMS,
15
15
  ObSnapshotFlat, OB_SNAPSHOT_FLAT_DOC_PARAMS,
@@ -64,7 +64,13 @@ from ._docs import (_column_doc,
64
64
  _strike_price_field_doc,
65
65
  _days_in_year_doc,
66
66
  _days_till_expiration_field_doc,
67
- _expiration_date_field_doc)
67
+ _expiration_date_field_doc,
68
+ _dependent_variable_field_name_doc,
69
+ _independent_variable_field_name_doc,
70
+ _field_to_partition_doc,
71
+ _weight_field_doc,
72
+ _number_of_groups_doc)
73
+
68
74
  from onetick.py.docs.utils import docstring, param_doc
69
75
 
70
76
  # This module mostly focused on providing annotations and documentation for end user
@@ -2183,7 +2189,8 @@ def implied_vol(*args, **kwargs):
2183
2189
  return ImpliedVol(*args, **kwargs)
2184
2190
 
2185
2191
 
2186
- @docstring(parameters=[_running_doc, _all_fields_doc, _bucket_interval_doc, _bucket_time_doc,
2192
+ @docstring(parameters=[_dependent_variable_field_name_doc, _independent_variable_field_name_doc,
2193
+ _running_doc, _all_fields_doc, _bucket_interval_doc, _bucket_time_doc,
2187
2194
  _bucket_units_doc, _bucket_end_condition_doc, _end_condition_per_group_doc,
2188
2195
  _boundary_tick_bucket_doc,
2189
2196
  ])
@@ -2214,3 +2221,38 @@ def linear_regression(*args, **kwargs):
2214
2221
  0 2003-12-04 -0.3 6.7
2215
2222
  """
2216
2223
  return LinearRegression(*args, **kwargs)
2224
+
2225
+
2226
+ @docstring(parameters=[_field_to_partition_doc, _weight_field_doc, _number_of_groups_doc,
2227
+ _running_doc, _bucket_interval_doc, _bucket_time_doc,
2228
+ _bucket_units_doc, _bucket_end_condition_doc, _boundary_tick_bucket_doc,
2229
+ ])
2230
+ def partition_evenly_into_groups(*args, **kwargs):
2231
+ """
2232
+ ``PARTITION_EVENLY_INTO_GROUPS`` aggregation.
2233
+
2234
+ For each bucket, this EP breaks ticks into the specified number of groups (``number_of_groups``)
2235
+ by the specified field (``field_to_partition``) in a way that the sums
2236
+ of the specified weight fields (``weight_field``) in each group are as close as possible.
2237
+
2238
+ See also
2239
+ --------
2240
+ **PARTITION_EVENLY_INTO_GROUPS** OneTick event processor
2241
+
2242
+ Examples
2243
+ --------
2244
+
2245
+ >>> data = otp.Ticks(X=['A', 'B', 'A', 'C', 'D'], SIZE=[10, 30, 20, 15, 14])
2246
+ >>> data = data.partition_evenly_into_groups(
2247
+ ... field_to_partition=data['X'],
2248
+ ... weight_field=data['SIZE'],
2249
+ ... number_of_groups=3,
2250
+ ... )
2251
+ >>> otp.run(data)
2252
+ Time FIELD_TO_PARTITION GROUP_ID
2253
+ 0 2003-12-04 A 0
2254
+ 1 2003-12-04 B 1
2255
+ 2 2003-12-04 C 2
2256
+ 3 2003-12-04 D 2
2257
+ """
2258
+ return PartitionEvenlyIntoGroups(*args, **kwargs)
@@ -1012,3 +1012,56 @@ class LinearRegression(_Aggregation, _MultiColumnAggregation):
1012
1012
  'SLOPE': float,
1013
1013
  'INTERCEPT': float,
1014
1014
  }
1015
+
1016
+
1017
+ class PartitionEvenlyIntoGroups(_Aggregation, _MultiColumnAggregation):
1018
+ NAME = 'PARTITION_EVENLY_INTO_GROUPS'
1019
+ EP = otq.PartitionEvenlyIntoGroups
1020
+
1021
+ FIELDS_MAPPING = deepcopy(_Aggregation.FIELDS_MAPPING)
1022
+ FIELDS_MAPPING['field_to_partition'] = 'FIELD_TO_PARTITION'
1023
+ FIELDS_MAPPING['weight_field'] = 'WEIGHT_FIELD'
1024
+ FIELDS_MAPPING['number_of_groups'] = 'NUMBER_OF_GROUPS'
1025
+
1026
+ FIELDS_TO_SKIP = ['column_name', 'output_field_name']
1027
+
1028
+ def __init__(
1029
+ self,
1030
+ field_to_partition: Union[_Column, str, OnetickParameter],
1031
+ weight_field: Union[_Column, str, OnetickParameter],
1032
+ number_of_groups: Union[int, OnetickParameter],
1033
+ *args, **kwargs,
1034
+ ):
1035
+ if isinstance(field_to_partition, _Column):
1036
+ field_to_partition = str(field_to_partition)
1037
+ if isinstance(weight_field, _Column):
1038
+ weight_field = str(weight_field)
1039
+
1040
+ if number_of_groups <= 0:
1041
+ raise ValueError('Parameter `number_of_groups` value should be greater than zero')
1042
+
1043
+ self.field_to_partition = field_to_partition
1044
+ self.weight_field = weight_field
1045
+ self.number_of_groups = number_of_groups
1046
+
1047
+ super().__init__(_Column('TIMESTAMP'), *args, **kwargs)
1048
+
1049
+ def validate_input_columns(self, src: 'Source'):
1050
+ super().validate_input_columns(src)
1051
+
1052
+ for column in [self.field_to_partition, self.weight_field]:
1053
+ if isinstance(column, OnetickParameter):
1054
+ continue
1055
+
1056
+ if column not in src.schema:
1057
+ raise TypeError(f"Aggregation `{self.NAME}` uses column `{column}` as input, which doesn't exist")
1058
+
1059
+ for out_field in ['FIELD_TO_PARTITION', 'GROUP_ID']:
1060
+ if out_field in src.schema:
1061
+ raise TypeError(f"Field `{out_field}`, which is `{self.NAME}` aggregation output column, is in schema.")
1062
+
1063
+ def _get_output_schema(self, src: 'Source', name: Optional[str] = None) -> dict:
1064
+ return {
1065
+ 'FIELD_TO_PARTITION': str,
1066
+ 'GROUP_ID': int,
1067
+ }
@@ -1,14 +1,13 @@
1
1
  import os
2
2
  import warnings
3
3
  from dataclasses import dataclass, astuple
4
- from datetime import datetime
4
+ from datetime import datetime, timezone as dt_timezone
5
5
  from typing import Optional
6
6
 
7
- import pandas as pd
8
7
  from packaging.version import parse as parse_version
9
8
 
10
9
  import onetick.py as otp
11
- from onetick.py.otq import otq, otli
10
+ from onetick.py.otq import otq, otli, pyomd
12
11
  from onetick.py.backports import cache
13
12
 
14
13
 
@@ -104,34 +103,57 @@ def _parse_release_string(release_string: str, build_number: int) -> OnetickVers
104
103
  raise ValueError(f"Unknown release type '{release_type}' in release string '{release_string}'")
105
104
 
106
105
 
107
- def _get_locator_min_date(db_name, context):
106
+ def _get_locator_intervals(db_name, context) -> list[tuple[datetime, datetime]]:
108
107
  graph = otq.GraphQuery(otq.DbShowConfiguredTimeRanges(db_name=db_name).tick_type('ANY')
109
108
  >> otq.Table(fields='long START_DATE, long END_DATE'))
110
109
  symbols = f'{db_name}::'
110
+
111
+ # setting this is important so we don't get access error
112
+ qp = pyomd.QueryProperties()
113
+ qp.set_property_value('IGNORE_TICKS_IN_UNENTITLED_TIME_RANGE', 'TRUE')
114
+
111
115
  result = otq.run(graph,
112
116
  symbols=symbols,
113
117
  # start and end times don't matter for this query, use some constants
114
118
  start=datetime(2003, 12, 1),
115
119
  end=datetime(2003, 12, 1),
116
- # GMT, because start/end timestamp in locator are in GMT
117
- timezone='GMT',
120
+ # timezone is irrelevant, because times are returned as epoch numbers
121
+ timezone='UTC',
122
+ query_properties=qp,
118
123
  context=context)
119
124
  data = result.output(symbols).data
120
- first_date = data['START_DATE'][0]
121
- return datetime.fromtimestamp(first_date / 1000)
125
+ if not data:
126
+ raise RuntimeError(f"Database '{db_name}' doesn't have locations")
127
+ return [
128
+ (
129
+ datetime.fromtimestamp(data['START_DATE'][i] / 1000, dt_timezone.utc).replace(tzinfo=None),
130
+ datetime.fromtimestamp(data['END_DATE'][i] / 1000, dt_timezone.utc).replace(tzinfo=None),
131
+ )
132
+ for i in range(len(data['START_DATE']))
133
+ ]
122
134
 
123
135
 
124
- def _get_onetick_version(symbols, context, start, end):
136
+ def _get_onetick_version(db_name, context, start, end) -> dict:
125
137
  node = otq.TickGenerator(bucket_interval=0,
126
138
  fields='BUILD=GET_ONETICK_VERSION(), RELEASE=GET_ONETICK_RELEASE()')
127
139
  graph = otq.GraphQuery(node.tick_type('DUMMY'))
140
+ symbols = f'{db_name}::'
141
+
142
+ # setting this is important so we don't get access error
143
+ qp = pyomd.QueryProperties()
144
+ qp.set_property_value('IGNORE_TICKS_IN_UNENTITLED_TIME_RANGE', 'TRUE')
145
+
128
146
  result = otq.run(graph,
129
147
  symbols=symbols,
130
148
  start=start,
131
149
  end=end,
132
150
  context=context,
151
+ query_properties=qp,
133
152
  timezone='UTC')
134
- return result
153
+ data = result.output(symbols).data
154
+ if not data:
155
+ raise RuntimeError(f"Can't get OneTick version from database '{db_name}'")
156
+ return data
135
157
 
136
158
 
137
159
  @cache
@@ -166,28 +188,33 @@ def get_onetick_version(db=None, context=None) -> OnetickVersionFromServer:
166
188
 
167
189
  # if otp.config.default_db is set, then we use it to check compatibility
168
190
  # otherwise we use LOCAL database available everywhere
169
- db = db or otp.config.get('default_db', 'LOCAL')
170
- symbols = f'{db}::'
191
+ db_name = db or otp.config.get('default_db', 'LOCAL')
171
192
  context = context or otp.config.context
172
193
 
173
194
  try:
174
- # let's try some default time range first
175
- start = end = datetime(2003, 12, 1)
176
- result = _get_onetick_version(symbols, context, start, end)
177
- except Exception:
178
- if db != 'LOCAL':
195
+ if db_name == 'LOCAL':
196
+ # for LOCAL db any date will do
197
+ start = end = datetime(2003, 12, 1)
198
+ result_data = _get_onetick_version(db_name, context, start, end)
199
+ else:
179
200
  # for real db we need to set time range correctly
180
201
  # otherwise we may get error "Database locator has a gap"
181
- start = end = _get_locator_min_date(db, context)
182
- result = _get_onetick_version(symbols, context, start, end)
183
- else:
184
- raise
202
+ locator_intervals = _get_locator_intervals(db_name, context)
203
+ for i, (start, end) in enumerate(locator_intervals):
204
+ try:
205
+ result_data = _get_onetick_version(db_name, context, start, end)
206
+ break
207
+ except Exception as e:
208
+ if i < len(locator_intervals) - 1:
209
+ continue
210
+ else:
211
+ raise e
185
212
  finally:
186
213
  if s:
187
214
  s.close()
188
215
 
189
- build_number = result[symbols]["BUILD"][0]
190
- release_string = result[symbols]["RELEASE"][0]
216
+ build_number = result_data["BUILD"][0]
217
+ release_string = result_data["RELEASE"][0]
191
218
 
192
219
  try:
193
220
  onetick_version = _parse_release_string(release_string, build_number=build_number)
@@ -615,6 +615,12 @@ def linear_regression(self: 'Source', *args, **kwargs):
615
615
  pass
616
616
 
617
617
 
618
+ @copy_method(aggregations.functions.partition_evenly_into_groups)
619
+ def partition_evenly_into_groups(self: 'Source', *args, **kwargs):
620
+ # method implementation is copied by decorator
621
+ pass
622
+
623
+
618
624
  @inplace_operation
619
625
  def process_by_group(
620
626
  self: 'Source', process_source_func, group_by=None, source_name=None, num_threads=None, inplace=False
@@ -103,7 +103,9 @@ def throw(
103
103
  >>> t = otp.Ticks(A=[1, 2, 3, 4])
104
104
  >>> t = t.throw(message='warning A=1', scope='symbol', error_code=2, where=(t['A']==1))
105
105
  >>> t = t.throw(message='error A=3', scope='symbol', error_code=1502, where=(t['A']==3))
106
- >>> otp.run(t)
106
+ >>> otp.run(t) # doctest: +SKIP
107
+ UserWarning: Symbol error: [2] warning A=1
108
+ UserWarning: Symbol error: [1502] error A=3
107
109
  Time A
108
110
  0 2003-12-01 00:00:00.000 1
109
111
  1 2003-12-01 00:00:00.001 2
@@ -246,7 +248,17 @@ def dump(
246
248
 
247
249
  # print <no data> in case there are 0 ticks
248
250
  if hasattr(otq, 'InsertAtEnd'):
249
- self_c.sink(otq.InsertAtEnd(delimiter_name='AT_END'))
251
+ ep_kwargs = {}
252
+ if {'propagate_ticks', 'insert_if_input_is_empty', 'fields_of_added_tick'}.issubset(
253
+ otq.InsertAtEnd.Parameters.list_parameters()
254
+ ):
255
+ # if supported, fix PY-1433
256
+ ep_kwargs = dict(
257
+ propagate_ticks=True,
258
+ insert_if_input_is_empty=True,
259
+ fields_of_added_tick='AT_END=1'
260
+ )
261
+ self_c.sink(otq.InsertAtEnd(delimiter_name='AT_END', **ep_kwargs))
250
262
  self_c.schema['AT_END'] = int
251
263
  self_c.state_vars['COUNT'] = otp.state.var(0, scope='branch')
252
264
  self_c.state_vars['COUNT'] += 1
@@ -285,6 +285,7 @@ def _update_field(self: 'Source', field, value):
285
285
  are_ints_not_time(field.dtype, value_dtype) and
286
286
  not issubclass(field.dtype, bool) and not issubclass(value_dtype, bool) and
287
287
  value_dtype is not field.dtype
288
+ and not isinstance(field, _StateColumn)
288
289
  ):
289
290
  # case for converting values between int based types, like long or byte
290
291
  convert_to_type = value_dtype
@@ -298,6 +299,9 @@ def _update_field(self: 'Source', field, value):
298
299
  field._dtype = value_dtype
299
300
  type_changes = True
300
301
 
302
+ if isinstance(field, _StateColumn) and (convert_to_type is not None or type_changes):
303
+ raise ValueError(f"The type of the state variable {field.name} can't be changed")
304
+
301
305
  # for aliases, TIMESTAMP ~ Time as an example
302
306
  key = str(field)
303
307
 
@@ -1000,3 +1000,65 @@ def character_present(
1000
1000
  ))
1001
1001
 
1002
1002
  return self
1003
+
1004
+
1005
+ def primary_exch(self: 'Source', discard_on_match: bool = False) -> Tuple['Source', 'Source']:
1006
+ """
1007
+ Propagates the tick if its exchange is the PRIMARY exchange of the security. The primary exchange information
1008
+ is supplied through the Reference Database. It expects the security level symbol (IBM, not IBM.N) and works
1009
+ by looking for a field called ``EXCHANGE`` and filtering out ticks where the field does not match
1010
+ the primary exchange for the security.
1011
+
1012
+ .. note::
1013
+ This EP may not work correctly with OneTick Cloud databases, due to differences
1014
+ in format of exchange names in RefDB and in tick data.
1015
+
1016
+ Parameters
1017
+ ----------
1018
+ discard_on_match: bool
1019
+ When set to ``True`` only ticks from non-primary exchange are propagated,
1020
+ otherwise ticks from primary exchange are propagated.
1021
+
1022
+ See also
1023
+ --------
1024
+ **PRIMARY_EXCH** OneTick event processor
1025
+
1026
+ Returns
1027
+ -------
1028
+ Two :class:`Source` for each of if-else branches
1029
+
1030
+ Examples
1031
+ --------
1032
+
1033
+ Get ticks from primary exchange:
1034
+
1035
+ >>> src = otp.DataSource('SOME_DB', tick_type='TRD', symbols='AAA', date=otp.date(2003, 12, 1)) # doctest: +SKIP
1036
+ >>> src, _ = src.primary_exch() # doctest: +SKIP
1037
+ >>> otp.run(src, symbol_date=otp.date(2003, 12, 1)) # doctest: +SKIP
1038
+ Time PRICE SIZE EXCHANGE
1039
+ 0 2003-12-01 00:00:00.001 26.5 150 B
1040
+ 1 2003-12-01 00:00:00.002 25.7 20 B
1041
+
1042
+ Get all ticks, but mark ticks from primary exchange in column ``T``:
1043
+
1044
+ >>> src = otp.DataSource('SOME_DB', tick_type='TRD', symbols='AAA', date=otp.date(2003, 12, 1)) # doctest: +SKIP
1045
+ >>> primary, other = src.primary_exch() # doctest: +SKIP
1046
+ >>> primary['T'] = 1 # doctest: +SKIP
1047
+ >>> other['T'] = 0 # doctest: +SKIP
1048
+ >>> data = otp.merge([primary, other]) # doctest: +SKIP
1049
+ >>> otp.run(src, symbol_date=otp.date(2003, 12, 1)) # doctest: +SKIP
1050
+ Time PRICE SIZE EXCHANGE T
1051
+ 0 2003-12-01 00:00:00.000 25.2 100 A 0
1052
+ 1 2003-12-01 00:00:00.001 26.5 150 B 1
1053
+ 2 2003-12-01 00:00:00.002 25.7 20 B 1
1054
+ 3 2003-12-01 00:00:00.003 24.8 40 A 0
1055
+ """
1056
+ source = self.copy(ep=otq.PrimaryExch(discard_on_match=discard_on_match))
1057
+
1058
+ if_source = source.copy()
1059
+ if_source.node().out_pin("IF")
1060
+
1061
+ else_source = source.copy()
1062
+ else_source.node().out_pin("ELSE")
1063
+
1064
+ return if_source, else_source
@@ -5,7 +5,7 @@ import warnings
5
5
  from collections import defaultdict
6
6
  from datetime import datetime, date
7
7
  import pandas as pd
8
- from typing import Optional, Tuple
8
+ from typing import Optional, Tuple, Union
9
9
 
10
10
  from onetick.py.otq import otq
11
11
 
@@ -979,6 +979,8 @@ class Source:
979
979
  render_debug_info: bool = False,
980
980
  debug: bool = False,
981
981
  graphviz_compat_mode: bool = False,
982
+ font_family: Optional[str] = None,
983
+ font_size: Optional[Union[int, float]] = None,
982
984
  **kwargs,
983
985
  ):
984
986
  """
@@ -1011,6 +1013,12 @@ class Source:
1011
1013
  graphviz_compat_mode: bool
1012
1014
  Change internal parameters of result graph for better compatibility with old `Graphviz` versions.
1013
1015
  Could produce larger and less readable graphs.
1016
+ font_family: str, optional
1017
+ Font name
1018
+
1019
+ Default: **Monospace**
1020
+ font_size: int, float, str, optional
1021
+ Font size
1014
1022
  kwargs:
1015
1023
  Additional arguments to be passed to :py:meth:`onetick.py.Source.to_otq` method (except
1016
1024
  ``file_name``, ``file_suffix`` and ``query_name`` parameters)
@@ -1043,7 +1051,7 @@ class Source:
1043
1051
  otq_path = self.to_otq(**kwargs)
1044
1052
  return render_otq(
1045
1053
  otq_path, image_path, output_format, load_external_otqs, view, line_limit, parse_eval_from_params,
1046
- render_debug_info, debug, graphviz_compat_mode,
1054
+ render_debug_info, debug, graphviz_compat_mode, font_family, font_size,
1047
1055
  )
1048
1056
 
1049
1057
  def copy(self, ep=None, columns=None, deep=False) -> 'Source':
@@ -1652,7 +1660,7 @@ class Source:
1652
1660
  ranking, percentile, find_value_for_percentile,
1653
1661
  exp_w_average, exp_tw_average, standardized_moment,
1654
1662
  portfolio_price, multi_portfolio_price, return_ep, implied_vol,
1655
- linear_regression,
1663
+ linear_regression, partition_evenly_into_groups,
1656
1664
  process_by_group,
1657
1665
  )
1658
1666
  from ._source.source_methods.joins import ( # type: ignore[misc]
@@ -1689,6 +1697,7 @@ class Source:
1689
1697
  time_filter,
1690
1698
  skip_bad_tick,
1691
1699
  character_present,
1700
+ primary_exch,
1692
1701
  )
1693
1702
  from ._source.source_methods.drops import ( # type: ignore[misc]
1694
1703
  drop,
@@ -165,7 +165,9 @@ class DB:
165
165
  # ACCESS_INFO can return ACL violation error if we use database name as symbol
166
166
  query_properties={'IGNORE_TICKS_IN_UNENTITLED_TIME_RANGE': 'TRUE'},
167
167
  username=username,
168
- context=self.context)
168
+ context=self.context,
169
+ # don't print symbol error from onetick about start/end time adjusted due to entitlement checks
170
+ print_symbol_errors=False)
169
171
  if not df.empty:
170
172
  df = df.drop(columns='Time')
171
173
  if deep_scan:
@@ -60,7 +60,8 @@ def run(query: Union[Callable, Dict, otp.Source, otp.MultiOutputSource, # NOSON
60
60
  max_expected_ticks_per_symbol: Optional[int] = None,
61
61
  log_symbol: Union[bool, Type[utils.default]] = utils.default,
62
62
  encoding: Optional[str] = None,
63
- manual_dataframe_callback: bool = False):
63
+ manual_dataframe_callback: bool = False,
64
+ print_symbol_errors: bool = True):
64
65
  """
65
66
  Executes a query and returns its result.
66
67
 
@@ -214,6 +215,9 @@ def run(query: Union[Callable, Dict, otp.Source, otp.MultiOutputSource, # NOSON
214
215
  Create dataframe manually with ``callback`` mode.
215
216
  Only works if ``output_structure='df'`` is specified and parameter ``callback`` is not.
216
217
  May improve performance in some cases.
218
+ print_symbol_errors_from_onetick: bool
219
+ Applicable only when ``output_structure`` is set to *df*.
220
+ Print symbol errors from OneTick as python warnings.
217
221
 
218
222
  Returns
219
223
  -------
@@ -614,7 +618,8 @@ def run(query: Union[Callable, Dict, otp.Source, otp.MultiOutputSource, # NOSON
614
618
  _process_empty_results(result, query_schema, output_structure)
615
619
 
616
620
  return _format_call_output(result, output_structure=output_structure,
617
- require_dict=require_dict, node_names=node_names)
621
+ require_dict=require_dict, node_names=node_names,
622
+ print_symbol_errors=print_symbol_errors)
618
623
 
619
624
 
620
625
  async def run_async(*args, **kwargs):
@@ -698,7 +703,7 @@ def _filter_returned_map_by_node(result, _node_names):
698
703
 
699
704
  def _filter_returned_list_by_node(result, node_names):
700
705
  """
701
- Here, result has the following format: [(symbol, data_1, data_2, node_name)]
706
+ Here, result has the following format: [(symbol, ticks_data, error_data, node_name)]
702
707
  We need to filter by correct node_names
703
708
  """
704
709
  if not node_names:
@@ -708,12 +713,12 @@ def _filter_returned_list_by_node(result, node_names):
708
713
 
709
714
  res = []
710
715
  empty_result = True
711
- for symbol, data_1, data_2, node, *_ in result:
712
- if len(data_1) > 0:
716
+ for symbol, ticks_data, error_data, node, *_ in result:
717
+ if len(ticks_data) > 0:
713
718
  empty_result = False
714
719
  if node in node_names:
715
720
  node_found = True
716
- res.append((symbol, data_1, data_2, node))
721
+ res.append((symbol, ticks_data, error_data, node))
717
722
 
718
723
  if not empty_result and not node_found:
719
724
  # TODO: Do we even want to raise it?
@@ -721,19 +726,19 @@ def _filter_returned_list_by_node(result, node_names):
721
726
  return res
722
727
 
723
728
 
724
- def _form_dict_from_list(data_list, output_structure):
729
+ def _form_dict_from_list(data_list, output_structure, print_symbol_errors):
725
730
  """
726
- Here, data_list has the following format: [(symbol, data_1, data_2, node_name), ...]
731
+ Here, data_list has the following format: [(symbol, ticks_data, error_data, node_name), ...]
727
732
  We need to create the following result:
728
- either {symbol: DataFrame(data_1)} if there is only one result per symbol
729
- or {symbol: [DataFrame(data_1)]} if there are multiple results for symbol for a single node_name
730
- or {symbol: {node_name: DataFrame(data_1)}} if there are single results for multiple node names for a symbol
731
- or {symbol: {node_name: [DataFrame(data_1)]}} if there are multiple results for multiple node names for a symbol
733
+ either {symbol: DataFrame(ticks_data)} if there is only one result per symbol
734
+ or {symbol: [DataFrame(ticks_data)]} if there are multiple results for symbol for a single node_name
735
+ or {symbol: {node_name: DataFrame(ticks_data)}} if there are single results for multiple node names for a symbol
736
+ or {symbol: {node_name: [DataFrame(ticks_data)]}} if there are multiple results for multiple node names for a symbol
732
737
  """
733
738
 
734
739
  def form_node_name_dict(lst):
735
740
  """
736
- lst is a lit of (node, dataframe)
741
+ lst is a list of (node, dataframe)
737
742
  """
738
743
  d = defaultdict(list)
739
744
  for node, df in lst:
@@ -760,7 +765,12 @@ def _form_dict_from_list(data_list, output_structure):
760
765
  return polars.DataFrame()
761
766
 
762
767
  symbols_dict = defaultdict(list)
763
- for symbol, data, _, node, *_ in data_list:
768
+ for symbol, data, error_data, node, *_ in data_list:
769
+
770
+ if print_symbol_errors:
771
+ for err_code, err_msg, *_ in error_data:
772
+ warnings.warn(f"Symbol error: [{err_code}] {err_msg}")
773
+
764
774
  df = get_dataframe(data)
765
775
 
766
776
  list_item = (node, df)
@@ -772,7 +782,7 @@ def _form_dict_from_list(data_list, output_structure):
772
782
  return dict(symbols_dict)
773
783
 
774
784
 
775
- def _format_call_output(result, output_structure, node_names, require_dict):
785
+ def _format_call_output(result, output_structure, node_names, require_dict, print_symbol_errors):
776
786
  """Formats output of otq.run() according to passed parameters.
777
787
  See parameters' description for more information
778
788
 
@@ -795,6 +805,8 @@ def _format_call_output(result, output_structure, node_names, require_dict):
795
805
  require_dict: bool
796
806
  If True, forces output for output_structure='df' to always be a dictionary, even if only one symbol is returned
797
807
  Has no effect for other values of output_structure
808
+ print_symbol_errors: bool
809
+ Print OneTick symbol errors in when ``output_structure`` is set to 'df' or not.
798
810
 
799
811
  Returns
800
812
  ----------
@@ -811,7 +823,7 @@ def _format_call_output(result, output_structure, node_names, require_dict):
811
823
 
812
824
  # "df" output structure implies that raw results came as a list
813
825
  result_list = _filter_returned_list_by_node(result, node_names)
814
- result_dict = _form_dict_from_list(result_list, output_structure)
826
+ result_dict = _form_dict_from_list(result_list, output_structure, print_symbol_errors)
815
827
 
816
828
  if len(result_dict) == 1 and not require_dict:
817
829
  return list(result_dict.values())[0]