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,32 @@
1
+ """
2
+ @pyne lib
3
+
4
+ Stateful implementation of ``lib.fixnan``. It lives in its own small module
5
+ because the ``@pyne`` marker is module-level and the host module
6
+ (``lib/__init__.py``) must stay untransformed; the host re-exports the
7
+ function, and the layout travels on the function object.
8
+ """
9
+ # Absolute imports on purpose: the call-site classifier resolves absolute
10
+ # imports at transform time, so NA() calls stay direct instead of anchored
11
+ from typing import Any
12
+
13
+ from pynecore.types import NA, Persistent
14
+
15
+ __all__ = ['fixnan']
16
+
17
+
18
+ def fixnan(source: Any) -> Any:
19
+ """
20
+ Fix NA values by replacing them with the last non-NA value
21
+
22
+ Hole detection is nan-only (``x != x``), deliberately NARROWER than the
23
+ ``na()`` predicate: TradingView's ``fixnan`` passes inf through as a
24
+ regular value (TV-verified) even though ``na(inf)`` is true. NA objects
25
+ (non-float na) are holes too.
26
+
27
+ :param source: The source value
28
+ :return: The source value if it is not a nan-hole, otherwise the last such value
29
+ """
30
+ last_not_nan: Persistent[Any] = NA(None)
31
+ last_not_nan = last_not_nan if (isinstance(source, NA) or source != source) else source
32
+ return last_not_nan
@@ -0,0 +1,202 @@
1
+ """
2
+ @pyne lib
3
+
4
+ Stateful implementations of ``lib.math.random`` and ``lib.math.sum``. They
5
+ live in their own small module because the ``@pyne`` marker is module-level
6
+ and the host module (``lib/math.py``) must stay untransformed; the host
7
+ re-exports the functions, and the layouts travel on the function objects.
8
+ """
9
+ # Absolute imports on purpose: the call-site classifier resolves absolute
10
+ # imports at transform time, so NA() calls stay direct instead of anchored
11
+ from typing import TypeVar
12
+
13
+ from pynecore.types import NA, Persistent, PyneFloat, PyneInt, Series, na_float
14
+ from pynecore.core.random import PineRandom as _PineRandom
15
+ # lib import (normalized to ``from pynecore import lib``) so the statement-position
16
+ # ``max_bars_back`` call below is anchored and converted to a buffer resize.
17
+ from pynecore.lib import max_bars_back
18
+
19
+ TFI = TypeVar('TFI', float, int)
20
+
21
+ __all__ = ['random', 'sum']
22
+
23
+
24
+ # The lazy-init narrowing of ``prng`` is invisible to the IDE: ``Persistent`` is a
25
+ # marker the AST transformer rewrites, so flow analysis keeps the ``| None`` arm.
26
+ # noinspection PyShadowingBuiltins,PyShadowingNames,PyUnresolvedReferences
27
+ def random(min: TFI | NA[TFI] = 0, max: TFI | NA[TFI] = 1, seed: PyneInt = NA(int)) -> PyneFloat:
28
+ """
29
+ Returns a random number between two numbers.
30
+
31
+ :param min: The minimum number.
32
+ :param max: The maximum number.
33
+ :param seed: The seed for the random number generator.
34
+ :return: A random number between the minimum and maximum numbers.
35
+ """
36
+ prng: Persistent[_PineRandom | None] = None
37
+ if prng is None: # Lazy init: the PRNG must not be created before the seed is known
38
+ # An unseeded Pine `math.random(min, max)` arrives here with seed == na,
39
+ # not None; pass None so PineRandom time-seeds instead of building its
40
+ # state from an NA (which would XOR to NA and make every draw na).
41
+ prng = _PineRandom(None if isinstance(seed, NA) else seed)
42
+ res = prng.random(min, max)
43
+ return res
44
+
45
+
46
+ # Three groups of IDE findings here are artifacts of the ``@pyne`` transform, not real
47
+ # defects: ``Persistent`` assignments look dead because their value is read on the NEXT
48
+ # bar, ``src`` looks possibly-unbound because it is a series whose storage outlives the
49
+ # ``if`` that feeds it, and ``src[i]`` looks like subscripting a float because
50
+ # ``Series[T]`` erases to ``T`` for the IDE.
51
+ # noinspection PyShadowingBuiltins,PyUnusedLocal,PyUnboundLocalVariable,PyUnresolvedReferences
52
+ def sum(source: TFI | NA[TFI], length: int) -> PyneFloat | TFI | NA[TFI]:
53
+ """
54
+ Returns the sum of a series over a specified length using Kahan summation.
55
+
56
+ :param source: Source series
57
+ :param length: Length of the sum
58
+ :return: The sliding sum of the series
59
+ """
60
+ summ: Persistent[float] = 0.0
61
+ count: Persistent[int] = 0
62
+ compensation: Persistent[float] = 0.0
63
+ prev_length: Persistent[int] = 0
64
+ removals: Persistent[int] = 0
65
+
66
+ # Representation-agnostic na test: an na source is either an NA object or a
67
+ # native nan (OHLCV gaps can already deliver a bare nan). Both must be
68
+ # excluded from the na-compacted buffer, or ``src[k]`` would poison ``summ``.
69
+ source_na = isinstance(source, NA) or source != source
70
+
71
+ if not source_na:
72
+ # Record every non-na bar's value into the sliding buffer BEFORE any
73
+ # early return (shortcut / warmup), so the positional recompute below
74
+ # sees a complete history with no holes. NA values are intentionally
75
+ # not stored: the buffer stays na-compacted, so ``src[k]`` is the k-th
76
+ # most recent non-na value — exactly the "last N non-na" window Pine's
77
+ # sum/sma use.
78
+ src: Series[float] = source
79
+ # The sliding window drops the value leaving it via ``src[length]`` (``length``
80
+ # non-na bars back). Grow the na-compacted buffer so that index stays addressable
81
+ # for lengths beyond the per-series default ``max_bars_back`` (500); otherwise the
82
+ # removal reads na and poisons ``summ`` permanently, collapsing any ``ta.sma`` /
83
+ # ``ta.sum`` with length > 500 to na right after warmup. Capacity persists across
84
+ # bars, so setting it on each non-na bar (from the first on) suffices — the window
85
+ # never removes before ``length`` non-na values have accumulated.
86
+ max_bars_back(src, int(length))
87
+
88
+ if length == 1: # Shortcut
89
+ # The sliding accumulator is left untouched here; record length == 1 so a
90
+ # following bar with a different length recomputes instead of trusting the
91
+ # now-stale state.
92
+ prev_length = 1
93
+ return source
94
+ assert length > 0, "Invalid length, length must be greater than 0!"
95
+ length = int(length)
96
+
97
+ # The Kahan sliding window below is only valid while ``length`` stays
98
+ # constant. Pine allows a series ``length`` (e.g. ``ta.sma(src, barssince(...))``);
99
+ # when it changes bar-to-bar the accumulator no longer describes the requested
100
+ # trailing window, so recompute the sum directly from the source series over the
101
+ # current length (mirroring ``ta.highest``'s positional fallback) and re-seed the
102
+ # accumulator so a subsequently stable length resumes the O(1) fast path.
103
+ changed = prev_length != 0 and length != prev_length
104
+ prev_length = length
105
+ if changed:
106
+ removals = 0
107
+ if source_na:
108
+ # Length changed on an na bar: the old window is stale and cannot be
109
+ # rebuilt from a missing current value; restart the warmup cleanly.
110
+ summ = 0.0
111
+ count = 0
112
+ compensation = 0.0
113
+ else:
114
+ recomputed = 0.0
115
+ comp = 0.0
116
+ found = 0
117
+ for i in range(length):
118
+ v = src[i]
119
+ if isinstance(v, NA) or v != v:
120
+ # The buffer is na-compacted, so the first na marks the end of
121
+ # available history — every deeper index is na too. Stop here
122
+ # instead of scanning the rest (keeps the recompute O(available),
123
+ # never O(length) when length outruns the stored history).
124
+ break
125
+ found += 1
126
+ corrected = float(v) - comp
127
+ new_recomputed = recomputed + corrected
128
+ comp = (new_recomputed - recomputed) - corrected
129
+ recomputed = new_recomputed
130
+ if found < length: # Not enough non-na history for this window yet
131
+ summ = 0.0
132
+ count = 0
133
+ compensation = 0.0
134
+ return na_float
135
+ summ = recomputed
136
+ compensation = comp
137
+ count = length
138
+ return recomputed
139
+
140
+ if count < length - 1:
141
+ if not source_na:
142
+ count += 1
143
+ # Kahan summation for adding new value
144
+ corrected_value = float(source) - compensation
145
+ new_sum = summ + corrected_value
146
+ compensation = (new_sum - summ) - corrected_value
147
+ summ = new_sum
148
+ return na_float
149
+ elif count == length - 1:
150
+ if source_na:
151
+ return na_float
152
+ count += 1
153
+ else:
154
+ if source_na:
155
+ return summ
156
+ # Exact resync: the incremental remove+add path below carries residual
157
+ # rounding error from bars long outside the window (Kahan bounds it but
158
+ # never clears it). That residue breaks identities TV preserves — e.g.
159
+ # a window of equal values must sum to exactly n*v (a run of zeros must
160
+ # sum to 0.0, not -3e-15), or ``sma(sma(x))`` of a flat series flips
161
+ # strict comparisons like the Technical Ratings
162
+ # ``kStochRsi < dStochRsi`` on last-bit noise. Small windows (the
163
+ # precision-sensitive ``sma(x, 3)`` chains) recompute from the
164
+ # na-compacted buffer on EVERY bar — for those lengths the fresh Kahan
165
+ # pass costs the same as the remove+add pair it replaces, and the
166
+ # result depends on the window alone. Longer windows resync once per
167
+ # ``length`` removals, capping any drift's lifetime at one window
168
+ # turnover at an amortized O(1) cost per bar.
169
+ removals += 1
170
+ if removals >= length or length <= 8:
171
+ removals = 0
172
+ recomputed = 0.0
173
+ comp = 0.0
174
+ found = 0
175
+ for i in range(length):
176
+ v = src[i]
177
+ if isinstance(v, NA) or v != v:
178
+ break
179
+ found += 1
180
+ corrected = float(v) - comp
181
+ new_recomputed = recomputed + corrected
182
+ comp = (new_recomputed - recomputed) - corrected
183
+ recomputed = new_recomputed
184
+ if found == length:
185
+ summ = recomputed
186
+ compensation = comp
187
+ return summ
188
+ # Kahan summation for removing old value (float() compiles to
189
+ # safe_convert.safe_float, returning NA instead of raising on NA)
190
+ old_value = float(src[length])
191
+ corrected_old = -old_value - compensation
192
+ new_sum = summ + corrected_old
193
+ compensation = (new_sum - summ) - corrected_old
194
+ summ = new_sum
195
+
196
+ # Kahan summation for adding new value
197
+ corrected_value = float(source) - compensation
198
+ new_sum = summ + corrected_value
199
+ compensation = (new_sum - summ) - corrected_value
200
+ summ = new_sum
201
+
202
+ return summ
@@ -0,0 +1,101 @@
1
+ """
2
+ @pyne lib
3
+
4
+ Stateful implementation of ``lib.timeframe.change``. It lives in its own
5
+ small module because the ``@pyne`` marker is module-level and the host
6
+ module (``lib/timeframe.py``) must stay untransformed; the host re-exports
7
+ the function, and the layout travels on the function object.
8
+
9
+ The stateless helpers (session math) stay in the host module so they run
10
+ untransformed at full speed.
11
+
12
+ For daily/weekly/monthly timeframes ``change`` is equivalent to
13
+ ``ta.change(time(timeframe))`` on the year-reset scheduled grid (see the
14
+ ``core.resampler`` module docs): it returns ``true`` on the first chart bar
15
+ of each new period. Intraday timeframes replay virtual candles against the
16
+ session template to find period starts within the session.
17
+ """
18
+ from datetime import datetime, timedelta
19
+
20
+ from pynecore import lib
21
+ from pynecore.lib import syminfo as _syminfo
22
+ from pynecore.lib import timeframe as _timeframe
23
+ from pynecore.types import Persistent
24
+
25
+ __all__ = ['change']
26
+
27
+
28
+ # The engine-private ``lib._*`` members are the intended interface here, and the
29
+ # ``Persistent`` markers are rewritten by the AST transformer, so the IDE sees neither
30
+ # the next-bar reads of the state variables nor their lazy-init narrowing.
31
+ # noinspection PyProtectedMember,PyUnusedLocal,PyUnresolvedReferences,PyTypeChecker
32
+ def change(timeframe: str) -> bool:
33
+ """
34
+ Detects changes in the specified timeframe.
35
+
36
+ :param timeframe: The timeframe to check
37
+ :return: Returns true on the first bar of a `timeframe`, false otherwise.
38
+ """
39
+ last_dt: Persistent[datetime | None] = None
40
+ last_signal: Persistent[datetime | None] = None
41
+ last_period: Persistent[object] = None
42
+
43
+ tf_sec = _timeframe.in_seconds(timeframe)
44
+ xchg_tf_sec = _timeframe.in_seconds(_syminfo.period)
45
+
46
+ # The timeframe to check must be greater (or equal) than the current timeframe
47
+ if tf_sec < xchg_tf_sec:
48
+ return False
49
+
50
+ _modifier, _multiplier = _timeframe._process_tf(timeframe)
51
+ assert _modifier != 'T', "Ticks are not (yet) supported!"
52
+
53
+ if _modifier == '' or _modifier == 'S':
54
+ # Intraday: replay virtual candles (even those missing from the
55
+ # dataset) and anchor period starts to the session start
56
+ dt: datetime = lib._datetime
57
+
58
+ if last_dt is None:
59
+ last_dt = dt - timedelta(seconds=xchg_tf_sec)
60
+ last_signal = last_dt
61
+
62
+ while last_dt < dt:
63
+ prev_dt = last_dt
64
+ last_dt = last_dt + timedelta(seconds=xchg_tf_sec)
65
+
66
+ # The anchor point is the session start
67
+ if _timeframe._is_new_session(last_dt, prev_dt, xchg_tf_sec):
68
+ # We need to round the session start to the nearest hour
69
+ last_signal = last_dt.replace(minute=0, second=0, microsecond=0)
70
+ seconds_since_last_session = (last_dt - last_signal).total_seconds()
71
+
72
+ if seconds_since_last_session % tf_sec == 0:
73
+ last_dt = dt
74
+ return lib.bar_index > 0
75
+
76
+ return False
77
+
78
+ # Daily/Weekly/Monthly: true on the first chart bar of a new period of
79
+ # the scheduled grid — the period identity changing is exactly
80
+ # ``ta.change(time(timeframe))`` on the verified grid
81
+ if _multiplier > 1:
82
+ if (_modifier, _multiplier) == _timeframe._process_tf(str(_syminfo.period)):
83
+ # The chart's own bars are the requested grid: every bar starts one
84
+ key = lib._time
85
+ else:
86
+ key = lib._dwm_change_key(timeframe, _modifier, _multiplier)
87
+ elif _modifier == 'D':
88
+ key = lib._dg_trading_day()
89
+ elif _modifier == 'W':
90
+ td = lib._dg_trading_day()
91
+ key = td - timedelta(days=td.weekday())
92
+ else: # 'M'
93
+ td = lib._dg_trading_day()
94
+ key = (td.year, td.month)
95
+
96
+ if key != last_period:
97
+ is_first = last_period is None
98
+ last_period = key
99
+ if not is_first:
100
+ return lib.bar_index > 0
101
+ return False
@@ -0,0 +1,6 @@
1
+ #
2
+ # Constants
3
+ #
4
+ none = 0
5
+ dividends = 1
6
+ splits = 2
pynecore/lib/alert.py ADDED
@@ -0,0 +1,39 @@
1
+ """
2
+ Alert
3
+
4
+ The module is both a function (``alert(...)``) and a namespace (``alert.freq_once_per_bar``);
5
+ call sites are routed to :func:`alert` by the module property AST transformer.
6
+ """
7
+ from ..types.alert import AlertEnum
8
+
9
+
10
+ #
11
+ # Constants
12
+ #
13
+
14
+ freq_all = AlertEnum('all')
15
+ freq_once_per_bar = AlertEnum('once_per_bar')
16
+ freq_once_per_bar_close = AlertEnum('once_per_bar_close')
17
+
18
+
19
+ #
20
+ # Module function
21
+ #
22
+
23
+ def alert(
24
+ message: str,
25
+ freq: AlertEnum = freq_once_per_bar
26
+ ) -> None:
27
+ """
28
+ Display alert message. Uses rich formatting if available, falls back to print.
29
+
30
+ :param message: Alert message to display
31
+ :param freq: Alert frequency (currently ignored)
32
+ """
33
+ try:
34
+ # Try to use typer for nice colored output
35
+ import typer
36
+ typer.secho(f"🚨 ALERT: {message}", fg=typer.colors.BRIGHT_YELLOW, bold=True)
37
+ except ImportError:
38
+ # Fallback to simple print
39
+ print(f"🚨 ALERT: {message}")
pynecore/lib/alert.pyi ADDED
@@ -0,0 +1,14 @@
1
+ from ..types.alert import AlertEnum
2
+
3
+
4
+ # IDE-facing view of the function-and-namespace module: user code reads the
5
+ # constants and calls the bare name; the AST transformer resolves both at runtime.
6
+ class AlertModule:
7
+ freq_all: AlertEnum
8
+ freq_once_per_bar: AlertEnum
9
+ freq_once_per_bar_close: AlertEnum
10
+
11
+ def __call__(self, message: str, freq: AlertEnum = ...) -> None: ...
12
+
13
+
14
+ alert: AlertModule