opencode-pyneruntime 6.6.4__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 (261) hide show
  1. opencode_pyneruntime-6.6.4.dist-info/METADATA +281 -0
  2. opencode_pyneruntime-6.6.4.dist-info/RECORD +261 -0
  3. opencode_pyneruntime-6.6.4.dist-info/WHEEL +5 -0
  4. opencode_pyneruntime-6.6.4.dist-info/entry_points.txt +6 -0
  5. opencode_pyneruntime-6.6.4.dist-info/licenses/LICENSE +201 -0
  6. opencode_pyneruntime-6.6.4.dist-info/licenses/NOTICE +21 -0
  7. opencode_pyneruntime-6.6.4.dist-info/top_level.txt +1 -0
  8. pynecore/__init__.py +6 -0
  9. pynecore/cli/__init__.py +2 -0
  10. pynecore/cli/app.py +238 -0
  11. pynecore/cli/commands/__init__.py +343 -0
  12. pynecore/cli/commands/benchmark.py +186 -0
  13. pynecore/cli/commands/compile.py +198 -0
  14. pynecore/cli/commands/data.py +857 -0
  15. pynecore/cli/commands/debug.py +63 -0
  16. pynecore/cli/commands/optimize.py +956 -0
  17. pynecore/cli/commands/plugin.py +242 -0
  18. pynecore/cli/commands/run.py +2006 -0
  19. pynecore/cli/pluggable.py +132 -0
  20. pynecore/cli/utils/__init__.py +0 -0
  21. pynecore/cli/utils/api_error_handler.py +168 -0
  22. pynecore/cli/utils/broker_picker.py +330 -0
  23. pynecore/cli/utils/error_hook.py +28 -0
  24. pynecore/cli/utils/keyreader.py +178 -0
  25. pynecore/cli/utils/provider_picker.py +19 -0
  26. pynecore/cli/utils/symbol_browser.py +1149 -0
  27. pynecore/core/__init__.py +0 -0
  28. pynecore/core/aggregator.py +257 -0
  29. pynecore/core/bar_magnifier.py +168 -0
  30. pynecore/core/broker/__init__.py +64 -0
  31. pynecore/core/broker/defaults.py +113 -0
  32. pynecore/core/broker/disappearance.py +927 -0
  33. pynecore/core/broker/emulator.py +345 -0
  34. pynecore/core/broker/exceptions.py +346 -0
  35. pynecore/core/broker/idempotency.py +401 -0
  36. pynecore/core/broker/intent_builder.py +334 -0
  37. pynecore/core/broker/journal.py +1785 -0
  38. pynecore/core/broker/models.py +1600 -0
  39. pynecore/core/broker/native_failsafe_manager.py +1436 -0
  40. pynecore/core/broker/one_way_emulator.py +1128 -0
  41. pynecore/core/broker/position.py +787 -0
  42. pynecore/core/broker/run_identity.py +126 -0
  43. pynecore/core/broker/software_entry_stop_engine.py +351 -0
  44. pynecore/core/broker/software_partial_bracket_engine.py +1379 -0
  45. pynecore/core/broker/spot_inventory.py +1327 -0
  46. pynecore/core/broker/storage.py +2655 -0
  47. pynecore/core/broker/store_helpers.py +2161 -0
  48. pynecore/core/broker/sync_engine.py +16070 -0
  49. pynecore/core/broker/validation.py +382 -0
  50. pynecore/core/class_property.py +7 -0
  51. pynecore/core/config.py +392 -0
  52. pynecore/core/csv_file.py +547 -0
  53. pynecore/core/currency.py +262 -0
  54. pynecore/core/data_converter.py +1002 -0
  55. pynecore/core/datetime.py +296 -0
  56. pynecore/core/download_info.py +71 -0
  57. pynecore/core/download_runner.py +274 -0
  58. pynecore/core/htf_aggregator.py +181 -0
  59. pynecore/core/import_hook.py +358 -0
  60. pynecore/core/instance_state.py +494 -0
  61. pynecore/core/live_ltf_collector.py +442 -0
  62. pynecore/core/live_ltf_window.py +189 -0
  63. pynecore/core/live_runner.py +1347 -0
  64. pynecore/core/module_property.py +26 -0
  65. pynecore/core/ohlcv_file.py +1888 -0
  66. pynecore/core/overload.py +371 -0
  67. pynecore/core/pine_cast.py +113 -0
  68. pynecore/core/pine_export.py +95 -0
  69. pynecore/core/pine_method.py +244 -0
  70. pynecore/core/pine_range.py +86 -0
  71. pynecore/core/pine_udt.py +69 -0
  72. pynecore/core/plugin/__init__.py +394 -0
  73. pynecore/core/plugin/broker.py +781 -0
  74. pynecore/core/plugin/cli.py +96 -0
  75. pynecore/core/plugin/live_provider.py +208 -0
  76. pynecore/core/plugin/provider.py +331 -0
  77. pynecore/core/provider_string.py +148 -0
  78. pynecore/core/random.py +40 -0
  79. pynecore/core/resampler.py +686 -0
  80. pynecore/core/safe_convert.py +64 -0
  81. pynecore/core/script.py +1011 -0
  82. pynecore/core/script_runner.py +3202 -0
  83. pynecore/core/security.py +1749 -0
  84. pynecore/core/security_process.py +1253 -0
  85. pynecore/core/security_shm.py +456 -0
  86. pynecore/core/series.py +417 -0
  87. pynecore/core/strategy_stats.py +669 -0
  88. pynecore/core/symbol_map.py +134 -0
  89. pynecore/core/syminfo.py +505 -0
  90. pynecore/core/viz.py +591 -0
  91. pynecore/lib/__init__.py +1771 -0
  92. pynecore/lib/_fixnan.py +32 -0
  93. pynecore/lib/_math_stateful.py +202 -0
  94. pynecore/lib/_timeframe_change.py +101 -0
  95. pynecore/lib/adjustment.py +6 -0
  96. pynecore/lib/alert.py +39 -0
  97. pynecore/lib/alert.pyi +14 -0
  98. pynecore/lib/array.py +1051 -0
  99. pynecore/lib/barmerge.py +60 -0
  100. pynecore/lib/barstate.py +30 -0
  101. pynecore/lib/box.py +415 -0
  102. pynecore/lib/chart.py +128 -0
  103. pynecore/lib/color.py +152 -0
  104. pynecore/lib/color.pyi +50 -0
  105. pynecore/lib/currency.py +62 -0
  106. pynecore/lib/dayofweek.py +36 -0
  107. pynecore/lib/dayofweek.pyi +18 -0
  108. pynecore/lib/display.py +8 -0
  109. pynecore/lib/dividends.py +9 -0
  110. pynecore/lib/earnings.py +11 -0
  111. pynecore/lib/extend.py +6 -0
  112. pynecore/lib/font.py +5 -0
  113. pynecore/lib/footprint.py +79 -0
  114. pynecore/lib/format.py +11 -0
  115. pynecore/lib/hline.py +67 -0
  116. pynecore/lib/hline.pyi +24 -0
  117. pynecore/lib/label.py +409 -0
  118. pynecore/lib/line.py +433 -0
  119. pynecore/lib/linefill.py +93 -0
  120. pynecore/lib/location.py +11 -0
  121. pynecore/lib/log.py +362 -0
  122. pynecore/lib/map.py +150 -0
  123. pynecore/lib/math.py +385 -0
  124. pynecore/lib/matrix.py +708 -0
  125. pynecore/lib/order.py +8 -0
  126. pynecore/lib/pivotpointtype.py +8 -0
  127. pynecore/lib/plot.py +95 -0
  128. pynecore/lib/plot.pyi +33 -0
  129. pynecore/lib/polyline.py +91 -0
  130. pynecore/lib/position.py +15 -0
  131. pynecore/lib/request.py +281 -0
  132. pynecore/lib/runtime.py +5 -0
  133. pynecore/lib/scale.py +9 -0
  134. pynecore/lib/session.py +267 -0
  135. pynecore/lib/session.pyi +12 -0
  136. pynecore/lib/shape.py +18 -0
  137. pynecore/lib/size.py +12 -0
  138. pynecore/lib/splits.py +4 -0
  139. pynecore/lib/strategy/__init__.py +4778 -0
  140. pynecore/lib/strategy/closedtrades.py +347 -0
  141. pynecore/lib/strategy/closedtrades.pyi +53 -0
  142. pynecore/lib/strategy/commission.py +9 -0
  143. pynecore/lib/strategy/direction.py +9 -0
  144. pynecore/lib/strategy/oca.py +13 -0
  145. pynecore/lib/strategy/opentrades.py +281 -0
  146. pynecore/lib/strategy/opentrades.pyi +49 -0
  147. pynecore/lib/strategy/risk.py +109 -0
  148. pynecore/lib/string.py +649 -0
  149. pynecore/lib/syminfo.py +84 -0
  150. pynecore/lib/ta.py +2230 -0
  151. pynecore/lib/table.py +290 -0
  152. pynecore/lib/text.py +17 -0
  153. pynecore/lib/ticker.py +207 -0
  154. pynecore/lib/timeframe.py +293 -0
  155. pynecore/lib/volume_row.py +67 -0
  156. pynecore/lib/xloc.py +4 -0
  157. pynecore/lib/yloc.py +5 -0
  158. pynecore/providers/__init__.py +0 -0
  159. pynecore/providers/ccxt.py +664 -0
  160. pynecore/providers/replay.py +187 -0
  161. pynecore/pynesys/__init__.py +0 -0
  162. pynecore/pynesys/api.py +498 -0
  163. pynecore/pynesys/compiler.py +112 -0
  164. pynecore/standalone.py +99 -0
  165. pynecore/testing/__init__.py +1 -0
  166. pynecore/testing/broker_lab/__init__.py +41 -0
  167. pynecore/testing/broker_lab/__main__.py +5 -0
  168. pynecore/testing/broker_lab/cli.py +87 -0
  169. pynecore/testing/broker_lab/generate.py +47 -0
  170. pynecore/testing/broker_lab/model.py +84 -0
  171. pynecore/testing/broker_lab/reference.py +645 -0
  172. pynecore/testing/broker_lab/runner.py +372 -0
  173. pynecore/testing/broker_lab/scheduler.py +50 -0
  174. pynecore/testing/broker_lab/subprocess.py +73 -0
  175. pynecore/transformers/__init__.py +0 -0
  176. pynecore/transformers/builtin_shadow.py +136 -0
  177. pynecore/transformers/closure_arguments_transformer.py +428 -0
  178. pynecore/transformers/display_rewrite.py +140 -0
  179. pynecore/transformers/dynamic_default.py +147 -0
  180. pynecore/transformers/function_isolation.py +757 -0
  181. pynecore/transformers/import_lifter.py +61 -0
  182. pynecore/transformers/import_normalizer.py +328 -0
  183. pynecore/transformers/inline_series_hoist.py +178 -0
  184. pynecore/transformers/input_transformer.py +175 -0
  185. pynecore/transformers/lib_series.py +201 -0
  186. pynecore/transformers/locations.py +70 -0
  187. pynecore/transformers/module_properties.json +3387 -0
  188. pynecore/transformers/module_property.py +221 -0
  189. pynecore/transformers/ne_guard.py +70 -0
  190. pynecore/transformers/persistent.py +320 -0
  191. pynecore/transformers/persistent_series.py +76 -0
  192. pynecore/transformers/safe_convert_transformer.py +97 -0
  193. pynecore/transformers/safe_division_transformer.py +95 -0
  194. pynecore/transformers/script_requirements.py +308 -0
  195. pynecore/transformers/security.py +752 -0
  196. pynecore/transformers/security_instantiation.py +274 -0
  197. pynecore/transformers/series.py +275 -0
  198. pynecore/transformers/slot_layout.py +381 -0
  199. pynecore/transformers/type_checking_stripper.py +25 -0
  200. pynecore/transformers/unused_series_detector.py +267 -0
  201. pynecore/types/__init__.py +21 -0
  202. pynecore/types/alert.py +5 -0
  203. pynecore/types/barmerge.py +5 -0
  204. pynecore/types/base.py +39 -0
  205. pynecore/types/box.py +37 -0
  206. pynecore/types/chart.py +17 -0
  207. pynecore/types/color.py +107 -0
  208. pynecore/types/currency.py +5 -0
  209. pynecore/types/datetime.py +6 -0
  210. pynecore/types/display.py +5 -0
  211. pynecore/types/dividends.py +5 -0
  212. pynecore/types/earnings.py +5 -0
  213. pynecore/types/extend.py +5 -0
  214. pynecore/types/font.py +5 -0
  215. pynecore/types/footprint.py +41 -0
  216. pynecore/types/format.py +5 -0
  217. pynecore/types/hline.py +24 -0
  218. pynecore/types/ib_persistent.py +8 -0
  219. pynecore/types/ib_persistent.pyi +10 -0
  220. pynecore/types/label.py +35 -0
  221. pynecore/types/line.py +32 -0
  222. pynecore/types/linefill.py +13 -0
  223. pynecore/types/location.py +5 -0
  224. pynecore/types/matrix.py +999 -0
  225. pynecore/types/na.py +237 -0
  226. pynecore/types/na.pyi +83 -0
  227. pynecore/types/ohlcv.py +12 -0
  228. pynecore/types/order.py +5 -0
  229. pynecore/types/persistent.py +8 -0
  230. pynecore/types/persistent.pyi +13 -0
  231. pynecore/types/pine_types.py +11 -0
  232. pynecore/types/pine_types.pyi +15 -0
  233. pynecore/types/pivotpointtype.py +5 -0
  234. pynecore/types/plot.py +12 -0
  235. pynecore/types/plot_meta.py +60 -0
  236. pynecore/types/polyline.py +40 -0
  237. pynecore/types/position.py +5 -0
  238. pynecore/types/scale.py +5 -0
  239. pynecore/types/script_type.py +15 -0
  240. pynecore/types/series.py +23 -0
  241. pynecore/types/series.pyi +19 -0
  242. pynecore/types/session.py +35 -0
  243. pynecore/types/shape.py +5 -0
  244. pynecore/types/size.py +5 -0
  245. pynecore/types/source.py +33 -0
  246. pynecore/types/splits.py +5 -0
  247. pynecore/types/strategy.py +45 -0
  248. pynecore/types/table.py +87 -0
  249. pynecore/types/text.py +13 -0
  250. pynecore/types/type_checker.py +7 -0
  251. pynecore/types/type_checker.pyi +48 -0
  252. pynecore/types/volume_row.py +36 -0
  253. pynecore/types/weekdays.py +11 -0
  254. pynecore/types/xloc.py +5 -0
  255. pynecore/types/yloc.py +5 -0
  256. pynecore/utils/__init__.py +0 -0
  257. pynecore/utils/file_utils.py +50 -0
  258. pynecore/utils/rich/__init__.py +0 -0
  259. pynecore/utils/rich/date_column.py +25 -0
  260. pynecore/utils/sequence_view.py +92 -0
  261. pynecore/utils/stdlib_checker.py +17 -0
@@ -0,0 +1,244 @@
1
+ """
2
+ Pine method dispatch on the slot-based instance-state scheme.
3
+
4
+ ``method_call`` is on the transformer's NON_TRANSFORMABLE list, so its call
5
+ sites stay raw and there is no caller anchor — the method is selected at
6
+ runtime and bound through a module-lifetime per-method cache instead: ONE
7
+ shared instance per method function, cleared between runs. This formalizes
8
+ what the legacy runtime did implicitly (its ``__scope_id__`` here was forever
9
+ ``''``, so every isolation it requested landed on the same empty-scope cache
10
+ key, shared by all call sites of a method and dropped on ``reset()``).
11
+ """
12
+ import sys
13
+ from types import ModuleType
14
+ from typing import Any, Callable
15
+ from functools import partial
16
+
17
+ from ..lib import array, matrix, box, line, label, table, linefill, polyline
18
+ from ..lib import map as map_lib
19
+ from ..utils.sequence_view import SequenceView
20
+ from ..types import matrix as matrix_types
21
+ from ..types import line as line_types
22
+ from ..types import box as box_types
23
+ from ..types import label as label_types
24
+ from ..types import table as table_types
25
+ from ..types import linefill as linefill_types
26
+ from ..types import polyline as polyline_types
27
+ from ..types.na import NA
28
+
29
+ from .instance_state import _make_state, register_shared_cache
30
+ from .pine_export import Exported
31
+
32
+
33
+ def method(func: Callable) -> Callable:
34
+ """
35
+ Decorator to mark a function as a Pine method.
36
+ This is used to indicate that the function should be treated as a method in Pine Script.
37
+ """
38
+ setattr(func, '__pine_method__', True)
39
+ return func
40
+
41
+
42
+ def _get_builtin_method(method_name: str, var: Any) -> Callable | None:
43
+ """
44
+ Get the built-in method for a Pine Script object.
45
+ :param method_name: The name of the method
46
+ :param var: The object on which the method is being called
47
+ :return: The built-in method, or None if not found
48
+ """
49
+ try:
50
+ var_type = type(var)
51
+ if var_type is NA:
52
+ # An na value carries its DECLARED type. A builtin method invoked on
53
+ # an na builtin object (e.g. ``naLine.delete()`` on a line that was
54
+ # never drawn) must dispatch to that builtin namespace, which treats
55
+ # na as a no-op the way Pine does. An na of a UDT (or untyped) type
56
+ # matches nothing here and falls through to user-method dispatch.
57
+ declared = var.type
58
+ if declared is None:
59
+ return None
60
+ var_type = declared if isinstance(declared, type) else type(declared)
61
+ match var_type:
62
+ case _ if var_type is list:
63
+ return getattr(array, method_name)
64
+ case _ if var_type is SequenceView:
65
+ # array.slice() returns a view; method calls on it (e.g.
66
+ # slice(...).max()) dispatch to the array namespace too.
67
+ return getattr(array, method_name)
68
+ case _ if var_type is dict:
69
+ return getattr(map_lib, method_name)
70
+ case _ if var_type is matrix_types.Matrix:
71
+ return getattr(matrix, method_name)
72
+ case _ if var_type is line_types.Line:
73
+ return getattr(line, method_name)
74
+ case _ if var_type is box_types.Box:
75
+ return getattr(box, method_name)
76
+ case _ if var_type is label_types.Label:
77
+ return getattr(label, method_name)
78
+ case _ if var_type is table_types.Table:
79
+ return getattr(table, method_name)
80
+ case _ if var_type is linefill_types.LineFill:
81
+ return getattr(linefill, method_name)
82
+ case _ if var_type is polyline_types.Polyline:
83
+ return getattr(polyline, method_name)
84
+ case _:
85
+ return None
86
+ except AttributeError:
87
+ pass
88
+
89
+ return None
90
+
91
+
92
+ # Method key -> (function identity, state vector or None, bound callable).
93
+ # A per-function key is essential: a constant key would make every method
94
+ # share one cache entry, running one method with another method's state.
95
+ # Registered so instance_state.reset() clears it between runs.
96
+ _method_anchors: dict[str, tuple[Any, list | None, Callable]] = register_shared_cache({})
97
+
98
+
99
+ # noinspection PyShadowingNames
100
+ def _bound_method(method: Any) -> Callable:
101
+ """Module-lifetime bound instance of a runtime-dispatched method.
102
+
103
+ The cache key is the method's qualified name and the entry survives
104
+ function-object re-creation: a method defined inside ``main()`` is a
105
+ fresh object every invocation, but its persistent state must live on —
106
+ on an identity miss the entry's state vector is rebound to the new
107
+ object (whose closure cells are the live ones). State from the cache,
108
+ closure from the passed object: the exact legacy split.
109
+
110
+ :param method: The method callable, or an ``Exported`` proxy of one.
111
+ :return: The callable to invoke with the visible arguments.
112
+ :raises ValueError: If an ``Exported`` proxy is not initialized yet.
113
+ """
114
+ target = method
115
+ if isinstance(target, Exported):
116
+ target = target.__fn__
117
+ if target is None:
118
+ raise ValueError("Exported proxy has not been initialized with a function yet")
119
+ key = f"{getattr(target, '__module__', '?')}.{getattr(target, '__qualname__', '?')}"
120
+ entry = _method_anchors.get(key)
121
+ if entry is not None and entry[0] is target:
122
+ return entry[2]
123
+ if isinstance(target, type) or (
124
+ hasattr(target, '__self__') and isinstance(target.__self__, type)):
125
+ return target # types and classmethods are called as-is (legacy guard)
126
+ state: list | None = None
127
+ bind = getattr(target, '__pyne_bind__', None)
128
+ if bind is not None:
129
+ bound: Callable = bind() # overload dispatcher: one shared anchored entry
130
+ else:
131
+ layout = getattr(target, '__pyne_layout__', None)
132
+ if layout is not None:
133
+ state = entry[1] if entry is not None and entry[1] is not None \
134
+ else _make_state(layout)
135
+ bound = partial(target, state)
136
+ else:
137
+ bound = target # stateless — called raw
138
+ _method_anchors[key] = (target, state, bound)
139
+ return bound
140
+
141
+
142
+ def _adapt_exported_kwargs(exported: Exported, kwargs: dict) -> dict:
143
+ """
144
+ Map keyword arguments onto a compiled library's canonically renamed
145
+ parameter names when needed.
146
+
147
+ A compiler cannot always know at emission time that a string-dispatched
148
+ method call targets a library export (the receiver may be untyped), so a
149
+ keyword argument can arrive under its original Pine spelling while the
150
+ library ``def`` declares ``name + '__ren__'``. The rename is applied only
151
+ when the raw name is absent from the target signature and the suffixed
152
+ one is present — a correct call is never altered.
153
+
154
+ :param exported: The resolved Exported proxy
155
+ :param kwargs: Keyword arguments as emitted at the call site
156
+ :return: Keyword arguments matching the target's parameter names
157
+ """
158
+ if not kwargs:
159
+ return kwargs
160
+ code: Any = getattr(exported.__fn__, '__code__', None)
161
+ if code is None:
162
+ return kwargs
163
+ params = code.co_varnames[:code.co_argcount + code.co_kwonlyargcount]
164
+ if all(k in params for k in kwargs):
165
+ return kwargs
166
+ return {(k + '__ren__' if k not in params and k + '__ren__' in params else k): v
167
+ for k, v in kwargs.items()}
168
+
169
+
170
+ # noinspection PyShadowingNames
171
+ def method_call(method: str | Callable, var: Any, *args, **kwargs) -> Any:
172
+ """
173
+ Dispatch a method call on a Pine Script variable to the appropriate handler.
174
+
175
+ This function serves as the central dispatcher for Pine Script method calls, handling both
176
+ built-in type methods (like array and matrix operations) and user-defined local methods.
177
+ It provides the Pine Script-like method calling syntax by routing calls to the correct
178
+ implementation based on the variable type and method name.
179
+
180
+ Closure-converted methods need no special handling: the closure transform
181
+ prepends the closure parameters to the method's signature and inserts the
182
+ matching arguments BEFORE the receiver at the call site, so the plain
183
+ positional order already lines up.
184
+
185
+ :param method: The method to call, either as a string name (for built-in methods) or a callable (for local methods)
186
+ :param var: The object/variable on which the method is being called (e.g., array, matrix, or custom object)
187
+ :param args: Positional arguments to pass to the method
188
+ :param kwargs: Keyword arguments to pass to the method
189
+ :return: The result of the method call, or None if the method cannot be dispatched
190
+ :raises AssertionError: If a string method name is provided but no matching method is found for the variable type
191
+ """
192
+ # If method is a string
193
+ if isinstance(method, str):
194
+ # Support for builtin methods
195
+ _method = _get_builtin_method(method, var)
196
+ if _method is not None:
197
+ return _method(var, *args, **kwargs)
198
+
199
+ # Modules
200
+ try:
201
+ return getattr(var, method)(*args, **kwargs)
202
+ except AttributeError:
203
+ pass
204
+
205
+ # Methods exported by a compiled library are module-level Exported proxies.
206
+ # Try the module that defines the receiver's UDT class first, then the
207
+ # caller's own module, then every library module the caller imports -- a
208
+ # library can export methods on another library's UDT, so the defining
209
+ # module is not always the UDT's own.
210
+ # If the raw name misses everywhere, retry once with PyneComp's canonical
211
+ # rename suffix: a compiled library emits a method whose Pine name cannot
212
+ # live verbatim in Python (``lambda``, ``method_call``, ...) under
213
+ # ``name + '__ren__'``. A stub with that shape can only be the image of
214
+ # this Pine name (a Pine ``x__ren__`` compiles to ``x__ren____ren__``),
215
+ # so the retry can never hit a wrong export.
216
+ caller_globals = sys._getframe(1).f_globals
217
+ mod = sys.modules.get(type(var).__module__)
218
+ for lookup_name in (method, method + '__ren__'):
219
+ _method = getattr(mod, lookup_name, None) if mod is not None else None
220
+ if not isinstance(_method, Exported):
221
+ _method = caller_globals.get(lookup_name)
222
+ if not isinstance(_method, Exported):
223
+ for _gval in caller_globals.values():
224
+ if isinstance(_gval, ModuleType) and _gval.__name__.startswith('lib.'):
225
+ _method = getattr(_gval, lookup_name, None)
226
+ if isinstance(_method, Exported):
227
+ break
228
+ if isinstance(_method, Exported):
229
+ return _bound_method(_method)(var, *args,
230
+ **_adapt_exported_kwargs(_method, kwargs))
231
+
232
+ assert False, f'No such method: {var}->{method}'
233
+
234
+ # It is a local method, it should be a local function
235
+ elif callable(method):
236
+ # It may not detected well the type and there may be a user with the same method name.
237
+ # So we 1st trt if it is a built-in object and has that method, because it has priority
238
+ _method = _get_builtin_method(method.__name__, var)
239
+ if _method:
240
+ return _method(var, *args, **kwargs)
241
+
242
+ return _bound_method(method)(var, *args, **kwargs)
243
+
244
+ return None
@@ -0,0 +1,86 @@
1
+ from typing import Generator, overload
2
+
3
+ from pynecore.types.pine_types import PyneFloat, PyneInt
4
+
5
+
6
+ @overload
7
+ def pine_range(from_num: int, to_num: int, step_num: int | None = None) -> range: ...
8
+
9
+
10
+ @overload
11
+ def pine_range(from_num: PyneInt | PyneFloat, to_num: PyneInt | PyneFloat,
12
+ step_num: PyneInt | PyneFloat | None = None) -> Generator[float, None, None]: ...
13
+
14
+
15
+ def pine_range(from_num: PyneInt | PyneFloat, to_num: PyneInt | PyneFloat, step_num: PyneInt | PyneFloat | None = None):
16
+ """
17
+ Emulates Pine Script's for loop range behavior.
18
+
19
+ :param from_num: Start value (inclusive)
20
+ :param to_num: End value (inclusive)
21
+ :param step_num: Step value (optional, defaults to +1/-1 based on direction)
22
+ :return: A native ``range`` for integer bounds, otherwise a generator that yields
23
+ values from from_num to to_num (inclusive)
24
+ :raises ValueError: If step_num is zero
25
+ """
26
+ # Fast path: pure-integer bounds map exactly onto a native range, which iterates at
27
+ # C speed instead of resuming a Python generator on every step. The vast majority of
28
+ # Pine for loops are integer index ranges, so this is the common case.
29
+ if isinstance(from_num, int) and isinstance(to_num, int) and (step_num is None or isinstance(step_num, int)):
30
+ if from_num <= to_num:
31
+ step = 1 if step_num is None else abs(step_num)
32
+ if step == 0:
33
+ raise ValueError("Step cannot be zero in pine_range")
34
+ # +1 makes the upper bound inclusive, matching Pine's `to`
35
+ return range(from_num, to_num + 1, step)
36
+ step = -1 if step_num is None else -abs(step_num)
37
+ if step == 0:
38
+ raise ValueError("Step cannot be zero in pine_range")
39
+ # -1 makes the lower bound inclusive for the descending direction
40
+ return range(from_num, to_num - 1, step)
41
+
42
+ return _pine_range_float(from_num, to_num, step_num)
43
+
44
+
45
+ def _pine_range_float(from_num: PyneFloat, to_num: PyneFloat, step_num: PyneFloat | None = None):
46
+ """
47
+ Generator fallback for Pine for-loop ranges with non-integer bounds.
48
+
49
+ :param from_num: Start value (inclusive)
50
+ :param to_num: End value (inclusive)
51
+ :param step_num: Step value (optional, defaults to +1/-1 based on direction)
52
+ :return: A generator that yields values from from_num to to_num (inclusive)
53
+ :raises ValueError: If step_num is zero
54
+ """
55
+ # Determine direction based on from_num and to_num
56
+ direction = 1 if from_num <= to_num else -1
57
+
58
+ # Use default step if none provided
59
+ step_val = step_num if step_num is not None else direction
60
+
61
+ # Prevent infinite loops
62
+ if step_val == 0:
63
+ raise ValueError("Step cannot be zero in pine_range")
64
+
65
+ # Ensure step direction matches the from->to direction
66
+ if (direction > 0 > step_val) or (direction < 0 < step_val):
67
+ step_val = -step_val
68
+
69
+ # Generate values
70
+ current = from_num
71
+ if direction > 0:
72
+ # Ascending loop
73
+ while current <= to_num:
74
+ yield current
75
+ current += step_val
76
+ # Safety check to prevent infinite loops due to floating point precision
77
+ if step_val > 0 and current > to_num + abs(step_val):
78
+ break
79
+ else:
80
+ # Descending loop
81
+ while current >= to_num:
82
+ yield current
83
+ current += step_val
84
+ # Safety check to prevent infinite loops due to floating point precision
85
+ if step_val < 0 and current < to_num - abs(step_val):
86
+ break
@@ -0,0 +1,69 @@
1
+ import copy as _copy
2
+ from dataclasses import dataclass, replace as _dataclass_replace, is_dataclass
3
+ from typing import TypeVar, Type, Any, dataclass_transform
4
+
5
+ __all__ = ['udt', 'udt_copy']
6
+
7
+ T = TypeVar('T')
8
+
9
+
10
+ def udt_copy(obj: T, **changes: Any) -> T:
11
+ """
12
+ Copy a UDT or object value (used by the transpiler for Pine `.copy()`).
13
+
14
+ A dataclass UDT is copied with ``dataclasses.replace`` (honouring optional
15
+ field overrides). Non-dataclass objects — the drawing types (Label, Box,
16
+ Line, …) are plain classes, not dataclasses — are deep-copied instead, so
17
+ `.copy()` on e.g. a `map<string, label>` value works rather than raising
18
+ "replace() should be called on dataclass instances".
19
+ """
20
+ if is_dataclass(obj):
21
+ return _dataclass_replace(obj, **changes)
22
+ return _copy.deepcopy(obj)
23
+
24
+
25
+ @dataclass_transform()
26
+ def udt(cls: Type[T]) -> Type[T]:
27
+ """
28
+ Custom dataclass decorator that adds a `copy` method to the class.
29
+
30
+ This decorator applies the standard dataclass decorator and then adds
31
+ a `copy` method that creates a copy of the instance using dataclass.replace().
32
+
33
+ :param cls: The class to decorate
34
+ :return: The decorated class with added copy method
35
+ """
36
+ # Apply the standard dataclass decorator with slots=True for better performance
37
+ decorated_cls = dataclass(cls, slots=True) # type: ignore
38
+
39
+ def copy(self: T, **changes: Any) -> T:
40
+ """
41
+ Create a copy of this instance with optional field modifications.
42
+
43
+ :param self: The instance to copy
44
+ :param changes: Optional keyword arguments to override field values
45
+ :return: A new instance with the specified changes
46
+ """
47
+ return udt_copy(self, **changes)
48
+
49
+ # noinspection PyShadowingNames
50
+ @classmethod # noqa
51
+ def new(cls, *args, **kwargs) -> T:
52
+ """
53
+ Pine Script-style constructor method.
54
+
55
+ Creates a new instance of the class using the same arguments as __init__.
56
+ This provides Pine Script compatibility where UDTs are created with .new().
57
+
58
+ :param cls: The class to construct
59
+ :param args: Positional arguments for the constructor
60
+ :param kwargs: Keyword arguments for the constructor
61
+ :return: A new instance of the class
62
+ """
63
+ return cls(*args, **kwargs)
64
+
65
+ # Add the methods to the class
66
+ decorated_cls.copy = copy # type: ignore
67
+ decorated_cls.new = new
68
+
69
+ return decorated_cls