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.
- opencode_pyneruntime-6.6.4.dist-info/METADATA +281 -0
- opencode_pyneruntime-6.6.4.dist-info/RECORD +261 -0
- opencode_pyneruntime-6.6.4.dist-info/WHEEL +5 -0
- opencode_pyneruntime-6.6.4.dist-info/entry_points.txt +6 -0
- opencode_pyneruntime-6.6.4.dist-info/licenses/LICENSE +201 -0
- opencode_pyneruntime-6.6.4.dist-info/licenses/NOTICE +21 -0
- opencode_pyneruntime-6.6.4.dist-info/top_level.txt +1 -0
- pynecore/__init__.py +6 -0
- pynecore/cli/__init__.py +2 -0
- pynecore/cli/app.py +238 -0
- pynecore/cli/commands/__init__.py +343 -0
- pynecore/cli/commands/benchmark.py +186 -0
- pynecore/cli/commands/compile.py +198 -0
- pynecore/cli/commands/data.py +857 -0
- pynecore/cli/commands/debug.py +63 -0
- pynecore/cli/commands/optimize.py +956 -0
- pynecore/cli/commands/plugin.py +242 -0
- pynecore/cli/commands/run.py +2006 -0
- pynecore/cli/pluggable.py +132 -0
- pynecore/cli/utils/__init__.py +0 -0
- pynecore/cli/utils/api_error_handler.py +168 -0
- pynecore/cli/utils/broker_picker.py +330 -0
- pynecore/cli/utils/error_hook.py +28 -0
- pynecore/cli/utils/keyreader.py +178 -0
- pynecore/cli/utils/provider_picker.py +19 -0
- pynecore/cli/utils/symbol_browser.py +1149 -0
- pynecore/core/__init__.py +0 -0
- pynecore/core/aggregator.py +257 -0
- pynecore/core/bar_magnifier.py +168 -0
- pynecore/core/broker/__init__.py +64 -0
- pynecore/core/broker/defaults.py +113 -0
- pynecore/core/broker/disappearance.py +927 -0
- pynecore/core/broker/emulator.py +345 -0
- pynecore/core/broker/exceptions.py +346 -0
- pynecore/core/broker/idempotency.py +401 -0
- pynecore/core/broker/intent_builder.py +334 -0
- pynecore/core/broker/journal.py +1785 -0
- pynecore/core/broker/models.py +1600 -0
- pynecore/core/broker/native_failsafe_manager.py +1436 -0
- pynecore/core/broker/one_way_emulator.py +1128 -0
- pynecore/core/broker/position.py +787 -0
- pynecore/core/broker/run_identity.py +126 -0
- pynecore/core/broker/software_entry_stop_engine.py +351 -0
- pynecore/core/broker/software_partial_bracket_engine.py +1379 -0
- pynecore/core/broker/spot_inventory.py +1327 -0
- pynecore/core/broker/storage.py +2655 -0
- pynecore/core/broker/store_helpers.py +2161 -0
- pynecore/core/broker/sync_engine.py +16070 -0
- pynecore/core/broker/validation.py +382 -0
- pynecore/core/class_property.py +7 -0
- pynecore/core/config.py +392 -0
- pynecore/core/csv_file.py +547 -0
- pynecore/core/currency.py +262 -0
- pynecore/core/data_converter.py +1002 -0
- pynecore/core/datetime.py +296 -0
- pynecore/core/download_info.py +71 -0
- pynecore/core/download_runner.py +274 -0
- pynecore/core/htf_aggregator.py +181 -0
- pynecore/core/import_hook.py +358 -0
- pynecore/core/instance_state.py +494 -0
- pynecore/core/live_ltf_collector.py +442 -0
- pynecore/core/live_ltf_window.py +189 -0
- pynecore/core/live_runner.py +1347 -0
- pynecore/core/module_property.py +26 -0
- pynecore/core/ohlcv_file.py +1888 -0
- pynecore/core/overload.py +371 -0
- pynecore/core/pine_cast.py +113 -0
- pynecore/core/pine_export.py +95 -0
- pynecore/core/pine_method.py +244 -0
- pynecore/core/pine_range.py +86 -0
- pynecore/core/pine_udt.py +69 -0
- pynecore/core/plugin/__init__.py +394 -0
- pynecore/core/plugin/broker.py +781 -0
- pynecore/core/plugin/cli.py +96 -0
- pynecore/core/plugin/live_provider.py +208 -0
- pynecore/core/plugin/provider.py +331 -0
- pynecore/core/provider_string.py +148 -0
- pynecore/core/random.py +40 -0
- pynecore/core/resampler.py +686 -0
- pynecore/core/safe_convert.py +64 -0
- pynecore/core/script.py +1011 -0
- pynecore/core/script_runner.py +3202 -0
- pynecore/core/security.py +1749 -0
- pynecore/core/security_process.py +1253 -0
- pynecore/core/security_shm.py +456 -0
- pynecore/core/series.py +417 -0
- pynecore/core/strategy_stats.py +669 -0
- pynecore/core/symbol_map.py +134 -0
- pynecore/core/syminfo.py +505 -0
- pynecore/core/viz.py +591 -0
- pynecore/lib/__init__.py +1771 -0
- pynecore/lib/_fixnan.py +32 -0
- pynecore/lib/_math_stateful.py +202 -0
- pynecore/lib/_timeframe_change.py +101 -0
- pynecore/lib/adjustment.py +6 -0
- pynecore/lib/alert.py +39 -0
- pynecore/lib/alert.pyi +14 -0
- pynecore/lib/array.py +1051 -0
- pynecore/lib/barmerge.py +60 -0
- pynecore/lib/barstate.py +30 -0
- pynecore/lib/box.py +415 -0
- pynecore/lib/chart.py +128 -0
- pynecore/lib/color.py +152 -0
- pynecore/lib/color.pyi +50 -0
- pynecore/lib/currency.py +62 -0
- pynecore/lib/dayofweek.py +36 -0
- pynecore/lib/dayofweek.pyi +18 -0
- pynecore/lib/display.py +8 -0
- pynecore/lib/dividends.py +9 -0
- pynecore/lib/earnings.py +11 -0
- pynecore/lib/extend.py +6 -0
- pynecore/lib/font.py +5 -0
- pynecore/lib/footprint.py +79 -0
- pynecore/lib/format.py +11 -0
- pynecore/lib/hline.py +67 -0
- pynecore/lib/hline.pyi +24 -0
- pynecore/lib/label.py +409 -0
- pynecore/lib/line.py +433 -0
- pynecore/lib/linefill.py +93 -0
- pynecore/lib/location.py +11 -0
- pynecore/lib/log.py +362 -0
- pynecore/lib/map.py +150 -0
- pynecore/lib/math.py +385 -0
- pynecore/lib/matrix.py +708 -0
- pynecore/lib/order.py +8 -0
- pynecore/lib/pivotpointtype.py +8 -0
- pynecore/lib/plot.py +95 -0
- pynecore/lib/plot.pyi +33 -0
- pynecore/lib/polyline.py +91 -0
- pynecore/lib/position.py +15 -0
- pynecore/lib/request.py +281 -0
- pynecore/lib/runtime.py +5 -0
- pynecore/lib/scale.py +9 -0
- pynecore/lib/session.py +267 -0
- pynecore/lib/session.pyi +12 -0
- pynecore/lib/shape.py +18 -0
- pynecore/lib/size.py +12 -0
- pynecore/lib/splits.py +4 -0
- pynecore/lib/strategy/__init__.py +4778 -0
- pynecore/lib/strategy/closedtrades.py +347 -0
- pynecore/lib/strategy/closedtrades.pyi +53 -0
- pynecore/lib/strategy/commission.py +9 -0
- pynecore/lib/strategy/direction.py +9 -0
- pynecore/lib/strategy/oca.py +13 -0
- pynecore/lib/strategy/opentrades.py +281 -0
- pynecore/lib/strategy/opentrades.pyi +49 -0
- pynecore/lib/strategy/risk.py +109 -0
- pynecore/lib/string.py +649 -0
- pynecore/lib/syminfo.py +84 -0
- pynecore/lib/ta.py +2230 -0
- pynecore/lib/table.py +290 -0
- pynecore/lib/text.py +17 -0
- pynecore/lib/ticker.py +207 -0
- pynecore/lib/timeframe.py +293 -0
- pynecore/lib/volume_row.py +67 -0
- pynecore/lib/xloc.py +4 -0
- pynecore/lib/yloc.py +5 -0
- pynecore/providers/__init__.py +0 -0
- pynecore/providers/ccxt.py +664 -0
- pynecore/providers/replay.py +187 -0
- pynecore/pynesys/__init__.py +0 -0
- pynecore/pynesys/api.py +498 -0
- pynecore/pynesys/compiler.py +112 -0
- pynecore/standalone.py +99 -0
- pynecore/testing/__init__.py +1 -0
- pynecore/testing/broker_lab/__init__.py +41 -0
- pynecore/testing/broker_lab/__main__.py +5 -0
- pynecore/testing/broker_lab/cli.py +87 -0
- pynecore/testing/broker_lab/generate.py +47 -0
- pynecore/testing/broker_lab/model.py +84 -0
- pynecore/testing/broker_lab/reference.py +645 -0
- pynecore/testing/broker_lab/runner.py +372 -0
- pynecore/testing/broker_lab/scheduler.py +50 -0
- pynecore/testing/broker_lab/subprocess.py +73 -0
- pynecore/transformers/__init__.py +0 -0
- pynecore/transformers/builtin_shadow.py +136 -0
- pynecore/transformers/closure_arguments_transformer.py +428 -0
- pynecore/transformers/display_rewrite.py +140 -0
- pynecore/transformers/dynamic_default.py +147 -0
- pynecore/transformers/function_isolation.py +757 -0
- pynecore/transformers/import_lifter.py +61 -0
- pynecore/transformers/import_normalizer.py +328 -0
- pynecore/transformers/inline_series_hoist.py +178 -0
- pynecore/transformers/input_transformer.py +175 -0
- pynecore/transformers/lib_series.py +201 -0
- pynecore/transformers/locations.py +70 -0
- pynecore/transformers/module_properties.json +3387 -0
- pynecore/transformers/module_property.py +221 -0
- pynecore/transformers/ne_guard.py +70 -0
- pynecore/transformers/persistent.py +320 -0
- pynecore/transformers/persistent_series.py +76 -0
- pynecore/transformers/safe_convert_transformer.py +97 -0
- pynecore/transformers/safe_division_transformer.py +95 -0
- pynecore/transformers/script_requirements.py +308 -0
- pynecore/transformers/security.py +752 -0
- pynecore/transformers/security_instantiation.py +274 -0
- pynecore/transformers/series.py +275 -0
- pynecore/transformers/slot_layout.py +381 -0
- pynecore/transformers/type_checking_stripper.py +25 -0
- pynecore/transformers/unused_series_detector.py +267 -0
- pynecore/types/__init__.py +21 -0
- pynecore/types/alert.py +5 -0
- pynecore/types/barmerge.py +5 -0
- pynecore/types/base.py +39 -0
- pynecore/types/box.py +37 -0
- pynecore/types/chart.py +17 -0
- pynecore/types/color.py +107 -0
- pynecore/types/currency.py +5 -0
- pynecore/types/datetime.py +6 -0
- pynecore/types/display.py +5 -0
- pynecore/types/dividends.py +5 -0
- pynecore/types/earnings.py +5 -0
- pynecore/types/extend.py +5 -0
- pynecore/types/font.py +5 -0
- pynecore/types/footprint.py +41 -0
- pynecore/types/format.py +5 -0
- pynecore/types/hline.py +24 -0
- pynecore/types/ib_persistent.py +8 -0
- pynecore/types/ib_persistent.pyi +10 -0
- pynecore/types/label.py +35 -0
- pynecore/types/line.py +32 -0
- pynecore/types/linefill.py +13 -0
- pynecore/types/location.py +5 -0
- pynecore/types/matrix.py +999 -0
- pynecore/types/na.py +237 -0
- pynecore/types/na.pyi +83 -0
- pynecore/types/ohlcv.py +12 -0
- pynecore/types/order.py +5 -0
- pynecore/types/persistent.py +8 -0
- pynecore/types/persistent.pyi +13 -0
- pynecore/types/pine_types.py +11 -0
- pynecore/types/pine_types.pyi +15 -0
- pynecore/types/pivotpointtype.py +5 -0
- pynecore/types/plot.py +12 -0
- pynecore/types/plot_meta.py +60 -0
- pynecore/types/polyline.py +40 -0
- pynecore/types/position.py +5 -0
- pynecore/types/scale.py +5 -0
- pynecore/types/script_type.py +15 -0
- pynecore/types/series.py +23 -0
- pynecore/types/series.pyi +19 -0
- pynecore/types/session.py +35 -0
- pynecore/types/shape.py +5 -0
- pynecore/types/size.py +5 -0
- pynecore/types/source.py +33 -0
- pynecore/types/splits.py +5 -0
- pynecore/types/strategy.py +45 -0
- pynecore/types/table.py +87 -0
- pynecore/types/text.py +13 -0
- pynecore/types/type_checker.py +7 -0
- pynecore/types/type_checker.pyi +48 -0
- pynecore/types/volume_row.py +36 -0
- pynecore/types/weekdays.py +11 -0
- pynecore/types/xloc.py +5 -0
- pynecore/types/yloc.py +5 -0
- pynecore/utils/__init__.py +0 -0
- pynecore/utils/file_utils.py +50 -0
- pynecore/utils/rich/__init__.py +0 -0
- pynecore/utils/rich/date_column.py +25 -0
- pynecore/utils/sequence_view.py +92 -0
- pynecore/utils/stdlib_checker.py +17 -0
pynecore/lib/_fixnan.py
ADDED
|
@@ -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
|
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
|