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/array.py
ADDED
|
@@ -0,0 +1,1051 @@
|
|
|
1
|
+
from typing import TypeVar, Any, cast
|
|
2
|
+
|
|
3
|
+
import builtins
|
|
4
|
+
|
|
5
|
+
import math
|
|
6
|
+
import statistics
|
|
7
|
+
|
|
8
|
+
from ..utils.sequence_view import SequenceView
|
|
9
|
+
|
|
10
|
+
from ..types.na import NA, na_float
|
|
11
|
+
from ..types.color import Color
|
|
12
|
+
from ..types.box import Box
|
|
13
|
+
from ..types.line import Line
|
|
14
|
+
from ..types.label import Label
|
|
15
|
+
from ..types.linefill import LineFill
|
|
16
|
+
from ..types.table import Table
|
|
17
|
+
from . import order as _order
|
|
18
|
+
|
|
19
|
+
T = TypeVar('T')
|
|
20
|
+
Number = TypeVar('Number', int, float)
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
'abs',
|
|
24
|
+
'avg',
|
|
25
|
+
'binary_search',
|
|
26
|
+
'binary_search_leftmost',
|
|
27
|
+
'binary_search_rightmost',
|
|
28
|
+
'clear',
|
|
29
|
+
'concat',
|
|
30
|
+
'copy',
|
|
31
|
+
'covariance',
|
|
32
|
+
'every',
|
|
33
|
+
'fill',
|
|
34
|
+
'first',
|
|
35
|
+
'from_items',
|
|
36
|
+
'get',
|
|
37
|
+
'includes',
|
|
38
|
+
'indexof',
|
|
39
|
+
'insert',
|
|
40
|
+
'join',
|
|
41
|
+
'last',
|
|
42
|
+
'lastindexof',
|
|
43
|
+
'max',
|
|
44
|
+
'median',
|
|
45
|
+
'min',
|
|
46
|
+
'mode',
|
|
47
|
+
'new',
|
|
48
|
+
'new_bool',
|
|
49
|
+
'new_box',
|
|
50
|
+
'new_color',
|
|
51
|
+
'new_float',
|
|
52
|
+
'new_int',
|
|
53
|
+
'new_label',
|
|
54
|
+
'new_line',
|
|
55
|
+
'new_linefill',
|
|
56
|
+
'new_string',
|
|
57
|
+
'new_table',
|
|
58
|
+
'percentile_linear_interpolation',
|
|
59
|
+
'percentile_nearest_rank',
|
|
60
|
+
'percentrank',
|
|
61
|
+
'pop',
|
|
62
|
+
'push',
|
|
63
|
+
'range',
|
|
64
|
+
'remove',
|
|
65
|
+
'reverse',
|
|
66
|
+
'set',
|
|
67
|
+
'shift',
|
|
68
|
+
'size',
|
|
69
|
+
'slice',
|
|
70
|
+
'some',
|
|
71
|
+
'sort',
|
|
72
|
+
'sort_indices',
|
|
73
|
+
'standardize',
|
|
74
|
+
'stdev',
|
|
75
|
+
'sum',
|
|
76
|
+
'unshift',
|
|
77
|
+
'variance',
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# noinspection PyShadowingBuiltins
|
|
82
|
+
def _non_na(id: list[T]) -> list[T]:
|
|
83
|
+
"""
|
|
84
|
+
Return the array's elements with na values removed.
|
|
85
|
+
|
|
86
|
+
TradingView's array math and statistics reductions ignore na elements and
|
|
87
|
+
reduce over the remaining values only, yielding na when none remain. Every
|
|
88
|
+
such reduction filters its input through this helper so their na handling
|
|
89
|
+
stays consistent with Pine.
|
|
90
|
+
|
|
91
|
+
:param id: Input array, possibly containing na elements
|
|
92
|
+
:return: New list containing only the non-na elements, in original order
|
|
93
|
+
"""
|
|
94
|
+
return [i for i in id if not (isinstance(i, NA) or i != i)]
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
# noinspection PyShadowingBuiltins
|
|
98
|
+
def abs(id: list[int | float]) -> list[int | float]:
|
|
99
|
+
"""
|
|
100
|
+
Returns an array containing the absolute value of each element in the original array.
|
|
101
|
+
|
|
102
|
+
:param id: Input array
|
|
103
|
+
:return: Array containing the absolute value of each element in the original array
|
|
104
|
+
"""
|
|
105
|
+
return [builtins.abs(v) for v in id]
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
# noinspection PyShadowingBuiltins
|
|
109
|
+
def avg(id: list[Number]) -> float:
|
|
110
|
+
"""
|
|
111
|
+
Returns the average value of the elements in the array.
|
|
112
|
+
|
|
113
|
+
:param id: Input array
|
|
114
|
+
:return: Average value of the elements in the array, or na if the array is empty
|
|
115
|
+
"""
|
|
116
|
+
a = _non_na(id)
|
|
117
|
+
if not a:
|
|
118
|
+
return na_float
|
|
119
|
+
return builtins.sum(a) / len(a)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# noinspection PyShadowingBuiltins
|
|
123
|
+
def binary_search(id: list[Any], val: Any) -> int:
|
|
124
|
+
"""
|
|
125
|
+
Returns the index of the specified value in the sorted array using binary search.
|
|
126
|
+
If the value is not found, returns -1.
|
|
127
|
+
The array to search must be sorted in ascending order.
|
|
128
|
+
|
|
129
|
+
:param id: Input array
|
|
130
|
+
:param val: Value to search for
|
|
131
|
+
:return: Index of the specified value in the sorted array, or -1 if not found
|
|
132
|
+
"""
|
|
133
|
+
low = 0
|
|
134
|
+
high = len(id) - 1
|
|
135
|
+
while low <= high:
|
|
136
|
+
mid = (low + high) // 2
|
|
137
|
+
if id[mid] == val:
|
|
138
|
+
return mid
|
|
139
|
+
else:
|
|
140
|
+
if val < id[mid]:
|
|
141
|
+
high = mid - 1
|
|
142
|
+
else:
|
|
143
|
+
low = mid + 1
|
|
144
|
+
return -1
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
# noinspection PyShadowingBuiltins
|
|
148
|
+
def binary_search_leftmost(id: list[Any], val: Any) -> int:
|
|
149
|
+
"""
|
|
150
|
+
Returns the index of the specified value in the sorted array using binary search.
|
|
151
|
+
If the value is not found, returns the index of the leftmost element greater than the value.
|
|
152
|
+
The array to search must be sorted in ascending order.
|
|
153
|
+
|
|
154
|
+
:param id: Input array
|
|
155
|
+
:param val: Value to search for
|
|
156
|
+
:return: Index of the specified value in the sorted array, or the index of the leftmost element
|
|
157
|
+
greater than the value
|
|
158
|
+
"""
|
|
159
|
+
low = 0
|
|
160
|
+
high = len(id) - 1
|
|
161
|
+
while low <= high:
|
|
162
|
+
mid = (low + high) // 2
|
|
163
|
+
if id[mid] == val:
|
|
164
|
+
return mid
|
|
165
|
+
else:
|
|
166
|
+
if val < id[mid]:
|
|
167
|
+
high = mid - 1
|
|
168
|
+
else:
|
|
169
|
+
low = mid + 1
|
|
170
|
+
return low - 1
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
# noinspection PyShadowingBuiltins
|
|
174
|
+
def binary_search_rightmost(id: list[Any], val: Any) -> int:
|
|
175
|
+
"""
|
|
176
|
+
Returns the index of the specified value in the sorted array using binary search.
|
|
177
|
+
If the value is not found, returns the index of the rightmost element less than the value.
|
|
178
|
+
The array to search must be sorted in ascending order.
|
|
179
|
+
|
|
180
|
+
:param id: Input array
|
|
181
|
+
:param val: Value to search for
|
|
182
|
+
:return: Index of the specified value in the sorted array, or the index of the rightmost element less than the value
|
|
183
|
+
"""
|
|
184
|
+
low = 0
|
|
185
|
+
high = len(id) - 1
|
|
186
|
+
while low <= high:
|
|
187
|
+
mid = (low + high) // 2
|
|
188
|
+
if id[mid] == val:
|
|
189
|
+
return mid
|
|
190
|
+
else:
|
|
191
|
+
if val < id[mid]:
|
|
192
|
+
high = mid - 1
|
|
193
|
+
else:
|
|
194
|
+
low = mid + 1
|
|
195
|
+
return high + 1
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
# noinspection PyShadowingBuiltins
|
|
199
|
+
def clear(id: list[Any]) -> None:
|
|
200
|
+
"""
|
|
201
|
+
Removes all elements from the array.
|
|
202
|
+
|
|
203
|
+
:param id: Input array
|
|
204
|
+
"""
|
|
205
|
+
id.clear()
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
# noinspection PyShadowingBuiltins
|
|
209
|
+
def concat(id1: list[T], id2: list[T]) -> list[T]:
|
|
210
|
+
"""
|
|
211
|
+
Concatenates two arrays into a single array.
|
|
212
|
+
|
|
213
|
+
:param id1: First array
|
|
214
|
+
:param id2: Second array
|
|
215
|
+
:return: Array containing the elements of both input arrays
|
|
216
|
+
"""
|
|
217
|
+
id1.extend(id2)
|
|
218
|
+
return id1
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
# noinspection PyShadowingBuiltins
|
|
222
|
+
def copy(id: list[T]) -> list[T]:
|
|
223
|
+
"""
|
|
224
|
+
Returns a shallow copy of the array.
|
|
225
|
+
|
|
226
|
+
:param id: Input array
|
|
227
|
+
:return: Shallow copy of the array
|
|
228
|
+
"""
|
|
229
|
+
return list(id)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
# noinspection PyShadowingBuiltins
|
|
233
|
+
def covariance(id1: list[Number], id2: list[Number], biased: bool = True) -> float:
|
|
234
|
+
"""
|
|
235
|
+
Returns the covariance between the elements in the two arrays.
|
|
236
|
+
|
|
237
|
+
:param id1: First input array
|
|
238
|
+
:param id2: Second input array
|
|
239
|
+
:param biased: If True, calculates the biased covariance. If False, calculates the unbiased covariance.
|
|
240
|
+
:return: Covariance between the elements in the two arrays, or na if the arrays are empty
|
|
241
|
+
"""
|
|
242
|
+
assert len(id1) == len(id2), "Input arrays must have the same length!"
|
|
243
|
+
pairs = [(v1, v2) for v1, v2 in zip(id1, id2)
|
|
244
|
+
if not (isinstance(v1, NA) or v1 != v1 or isinstance(v2, NA) or v2 != v2)]
|
|
245
|
+
if not pairs:
|
|
246
|
+
return na_float
|
|
247
|
+
# Online (Welford) co-moment — matches TradingView bit-for-bit for both
|
|
248
|
+
# the biased and unbiased result, where the classic two-pass
|
|
249
|
+
# ``sum((x-mx)*(y-my)) / divisor`` lands a couple of ulps off on the
|
|
250
|
+
# unbiased path (TV-verified in test_003_array_functions_float).
|
|
251
|
+
length = 0
|
|
252
|
+
mean1 = 0.0
|
|
253
|
+
mean2 = 0.0
|
|
254
|
+
comoment = 0.0
|
|
255
|
+
for v1, v2 in pairs:
|
|
256
|
+
length += 1
|
|
257
|
+
d1 = v1 - mean1
|
|
258
|
+
mean1 += d1 / length
|
|
259
|
+
mean2 += (v2 - mean2) / length
|
|
260
|
+
comoment += d1 * (v2 - mean2)
|
|
261
|
+
divisor = (length - 1) if not biased else length
|
|
262
|
+
if divisor == 0:
|
|
263
|
+
return 0.0
|
|
264
|
+
return comoment / divisor
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
# noinspection PyShadowingBuiltins
|
|
268
|
+
def every(id: list[Any]) -> bool:
|
|
269
|
+
"""
|
|
270
|
+
Returns true if all elements of the id array are true, false otherwise.
|
|
271
|
+
|
|
272
|
+
:param id: Input array
|
|
273
|
+
:return: True if all elements of the id array are true, false otherwise
|
|
274
|
+
"""
|
|
275
|
+
return all(id)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
# noinspection PyShadowingBuiltins
|
|
279
|
+
def fill(id: list[T], value: T, index_from: int = 0, index_to: int | NA = NA(int)) -> None:
|
|
280
|
+
"""
|
|
281
|
+
Fills the elements in the array with the specified value.
|
|
282
|
+
|
|
283
|
+
:param id: Input array
|
|
284
|
+
:param value: Value to fill
|
|
285
|
+
:param index_from: Index to start filling from
|
|
286
|
+
:param index_to: Index to stop filling at
|
|
287
|
+
"""
|
|
288
|
+
if isinstance(index_to, NA):
|
|
289
|
+
index_to = len(id)
|
|
290
|
+
id[index_from:index_to] = [value] * (index_to - index_from)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
# noinspection PyShadowingBuiltins
|
|
294
|
+
def first(id: list[T]) -> T:
|
|
295
|
+
"""
|
|
296
|
+
Returns the first element in the array.
|
|
297
|
+
|
|
298
|
+
:param id: Input array
|
|
299
|
+
:return: First element in the array
|
|
300
|
+
"""
|
|
301
|
+
if len(id) == 0:
|
|
302
|
+
raise RuntimeError("Cannot get first element of an empty array!")
|
|
303
|
+
return id[0]
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
# noinspection PyShadowingBuiltins
|
|
307
|
+
def from_items(*items: T) -> list[T]:
|
|
308
|
+
"""
|
|
309
|
+
Returns an array containing the specified elements.
|
|
310
|
+
NOTE: this is `array.from()` in Pine Script, but `from` is a reserved keyword in Python
|
|
311
|
+
|
|
312
|
+
:param items: Elements to include in the array
|
|
313
|
+
:return: Array containing the specified elements
|
|
314
|
+
"""
|
|
315
|
+
return list(items)
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
# noinspection PyShadowingBuiltins
|
|
319
|
+
def get(id: list[T] | SequenceView[T], index: int) -> T:
|
|
320
|
+
"""
|
|
321
|
+
Returns the element at the specified index in the array.
|
|
322
|
+
|
|
323
|
+
:param id: Input array
|
|
324
|
+
:param index: Index of the element to return
|
|
325
|
+
:return: Element at the specified index in the array
|
|
326
|
+
"""
|
|
327
|
+
# A na index (e.g. an index derived from a warmup ta.* value) yields na
|
|
328
|
+
# rather than crashing, matching Pine's na-propagation.
|
|
329
|
+
if isinstance(index, NA) or (isinstance(index, float) and math.isnan(index)):
|
|
330
|
+
return NA(None) # type: ignore[return-value]
|
|
331
|
+
# Pine floors a float index (documented: `array.get()` uses floor(index)),
|
|
332
|
+
# whereas Python cannot index a list with a float.
|
|
333
|
+
return id[int(index)]
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
# noinspection PyShadowingBuiltins
|
|
337
|
+
def includes(id: list[T], value: T) -> bool:
|
|
338
|
+
"""
|
|
339
|
+
Returns true if the array contains the specified value, false otherwise.
|
|
340
|
+
|
|
341
|
+
:param id: Input array
|
|
342
|
+
:param value: Value to search for
|
|
343
|
+
:return: True if the array contains the specified value, false otherwise
|
|
344
|
+
"""
|
|
345
|
+
return value in id
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
# noinspection PyShadowingBuiltins
|
|
349
|
+
def indexof(id: list[T], value: T) -> int:
|
|
350
|
+
"""
|
|
351
|
+
Returns the index of the first occurrence of the specified value in the array.
|
|
352
|
+
|
|
353
|
+
:param id: Input array
|
|
354
|
+
:param value: Value to search for
|
|
355
|
+
:return: Index of the first occurrence of the specified value in the array
|
|
356
|
+
"""
|
|
357
|
+
try:
|
|
358
|
+
return id.index(value)
|
|
359
|
+
except ValueError:
|
|
360
|
+
return -1
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
# noinspection PyShadowingBuiltins
|
|
364
|
+
def insert(id: list[T], index: int, value: T) -> None:
|
|
365
|
+
"""
|
|
366
|
+
Inserts the specified value at the specified index in the array.
|
|
367
|
+
|
|
368
|
+
:param id: Input array
|
|
369
|
+
:param index: Index to insert the value at
|
|
370
|
+
:param value: Value to insert
|
|
371
|
+
"""
|
|
372
|
+
id.insert(index, value)
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
# noinspection PyShadowingBuiltins
|
|
376
|
+
def join(id: list, separator: str) -> str:
|
|
377
|
+
"""
|
|
378
|
+
Concatenates the elements in the array into a single string, separated by the specified separator.
|
|
379
|
+
|
|
380
|
+
:param id: Input array
|
|
381
|
+
:param separator: Separator to use
|
|
382
|
+
:return: String containing the concatenated elements
|
|
383
|
+
"""
|
|
384
|
+
sa = [str(i) for i in id] # Ensure all elements are strings
|
|
385
|
+
return separator.join(sa)
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
# noinspection PyShadowingBuiltins
|
|
389
|
+
def last(id: list[T]) -> T:
|
|
390
|
+
"""
|
|
391
|
+
Returns the last element in the array.
|
|
392
|
+
|
|
393
|
+
:param id: Input array
|
|
394
|
+
:return: Last element in the array
|
|
395
|
+
"""
|
|
396
|
+
if len(id) == 0:
|
|
397
|
+
raise RuntimeError("Cannot get last element of an empty array!")
|
|
398
|
+
return id[-1]
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
# noinspection PyShadowingBuiltins
|
|
402
|
+
def lastindexof(id: list[T], value: T) -> int:
|
|
403
|
+
"""
|
|
404
|
+
Returns the index of the last occurrence of the specified value in the array.
|
|
405
|
+
|
|
406
|
+
:param id: Input array
|
|
407
|
+
:param value: Value to search for
|
|
408
|
+
:return: Index of the last occurrence of the specified value in the array
|
|
409
|
+
"""
|
|
410
|
+
try:
|
|
411
|
+
return len(id) - 1 - id[::-1].index(value)
|
|
412
|
+
except ValueError:
|
|
413
|
+
return -1
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
# noinspection PyShadowingBuiltins
|
|
417
|
+
def max(id: list[Number], nth: int = 0) -> Number:
|
|
418
|
+
"""
|
|
419
|
+
Returns the maximum value in the array, or the nth largest value.
|
|
420
|
+
|
|
421
|
+
na elements are ignored. ``nth`` is 0-based: 0 is the maximum, 1 the second
|
|
422
|
+
largest, and so on. Returns na if the array holds no non-na values or ``nth``
|
|
423
|
+
is out of range.
|
|
424
|
+
|
|
425
|
+
:param id: Input array
|
|
426
|
+
:param nth: Rank of the maximum to return (0 = maximum)
|
|
427
|
+
:return: The nth largest value in the array, or na
|
|
428
|
+
"""
|
|
429
|
+
a = _non_na(id)
|
|
430
|
+
if not a:
|
|
431
|
+
return id[0] if id else NA(None)
|
|
432
|
+
if nth == 0:
|
|
433
|
+
return builtins.max(a)
|
|
434
|
+
if nth < 0 or nth >= len(a):
|
|
435
|
+
return cast(Number, NA(builtins.type(a[0])))
|
|
436
|
+
return sorted(a, reverse=True)[nth]
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
# noinspection PyShadowingBuiltins
|
|
440
|
+
def median(id: list[Number]) -> float:
|
|
441
|
+
"""
|
|
442
|
+
Returns the median value of the elements in the array.
|
|
443
|
+
|
|
444
|
+
:param id: Input array
|
|
445
|
+
:return: Median value of the elements in the array, or na if the array is empty
|
|
446
|
+
"""
|
|
447
|
+
a = _non_na(id)
|
|
448
|
+
if not a:
|
|
449
|
+
return na_float
|
|
450
|
+
return statistics.median(a)
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
# noinspection PyShadowingBuiltins
|
|
454
|
+
def min(id: list[Number], nth: int = 0) -> Number:
|
|
455
|
+
"""
|
|
456
|
+
Returns the minimum value in the array, or the nth smallest value.
|
|
457
|
+
|
|
458
|
+
na elements are ignored. ``nth`` is 0-based: 0 is the minimum, 1 the second
|
|
459
|
+
smallest, and so on. Returns na if the array holds no non-na values or ``nth``
|
|
460
|
+
is out of range.
|
|
461
|
+
|
|
462
|
+
:param id: Input array
|
|
463
|
+
:param nth: Rank of the minimum to return (0 = minimum)
|
|
464
|
+
:return: The nth smallest value in the array, or na
|
|
465
|
+
"""
|
|
466
|
+
a = _non_na(id)
|
|
467
|
+
if not a:
|
|
468
|
+
return id[0] if id else NA(None)
|
|
469
|
+
if nth == 0:
|
|
470
|
+
return builtins.min(a)
|
|
471
|
+
if nth < 0 or nth >= len(a):
|
|
472
|
+
return cast(Number, NA(builtins.type(a[0])))
|
|
473
|
+
return sorted(a)[nth]
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
# noinspection PyShadowingBuiltins
|
|
477
|
+
def mode(id: list[T]) -> T:
|
|
478
|
+
"""
|
|
479
|
+
Returns the most frequently occurring element in the array.
|
|
480
|
+
|
|
481
|
+
:param id: Input array
|
|
482
|
+
:return: Most frequently occurring element in the array, or na if the array is empty
|
|
483
|
+
"""
|
|
484
|
+
a = _non_na(id)
|
|
485
|
+
if not a:
|
|
486
|
+
# An all-na array still knows its element type through its na elements;
|
|
487
|
+
# a truly empty one does not, so it gets a typeless na
|
|
488
|
+
return id[0] if id else NA(None)
|
|
489
|
+
return statistics.mode(a)
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
# noinspection PyShadowingNames
|
|
493
|
+
def _na_size(size: int | NA) -> int:
|
|
494
|
+
"""
|
|
495
|
+
Normalize an array constructor ``size`` argument.
|
|
496
|
+
|
|
497
|
+
TradingView treats an ``na`` size (e.g. ``array.new<line>(na)``) as 0,
|
|
498
|
+
producing an empty array, so mirror that instead of failing. A genuinely
|
|
499
|
+
negative size is still rejected.
|
|
500
|
+
|
|
501
|
+
:param size: Requested array size, possibly ``na``
|
|
502
|
+
:return: Non-negative integer size
|
|
503
|
+
"""
|
|
504
|
+
if isinstance(size, NA) or size != size:
|
|
505
|
+
return 0
|
|
506
|
+
assert size >= 0, "Size must be >=0!"
|
|
507
|
+
return size
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
# noinspection PyShadowingNames
|
|
511
|
+
def new_box(size: int | NA = 0, initial_value: Box = NA(Box)) -> list[Box]:
|
|
512
|
+
"""
|
|
513
|
+
Creates a new array of box objects of the specified size, with each element initialized
|
|
514
|
+
to the specified value.
|
|
515
|
+
|
|
516
|
+
:param size: Size of the new array
|
|
517
|
+
:param initial_value: Initial value to set for each element in the array
|
|
518
|
+
:return: New array of box objects
|
|
519
|
+
"""
|
|
520
|
+
size = _na_size(size)
|
|
521
|
+
assert isinstance(initial_value, (Box, NA)), "Initial value must be Box!"
|
|
522
|
+
return [initial_value] * size
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
# noinspection PyShadowingNames
|
|
526
|
+
def new_line(size: int | NA = 0, initial_value: Line = NA(Line)) -> list[Line]:
|
|
527
|
+
"""
|
|
528
|
+
Creates a new array of line objects of the specified size, with each element initialized
|
|
529
|
+
to the specified value.
|
|
530
|
+
|
|
531
|
+
:param size: Size of the new array
|
|
532
|
+
:param initial_value: Initial value to set for each element in the array
|
|
533
|
+
:return: New array of line objects
|
|
534
|
+
"""
|
|
535
|
+
size = _na_size(size)
|
|
536
|
+
assert isinstance(initial_value, (Line, NA)), "Initial value must be Line!"
|
|
537
|
+
return [initial_value] * size
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
# noinspection PyShadowingNames
|
|
541
|
+
def new_label(size: int | NA = 0, initial_value: Label = NA(Label)) -> list[Label]:
|
|
542
|
+
"""
|
|
543
|
+
Creates a new array of label objects of the specified size, with each element initialized
|
|
544
|
+
to the specified value.
|
|
545
|
+
|
|
546
|
+
:param size: Size of the new array
|
|
547
|
+
:param initial_value: Initial value to set for each element in the array
|
|
548
|
+
:return: New array of label objects
|
|
549
|
+
"""
|
|
550
|
+
size = _na_size(size)
|
|
551
|
+
assert isinstance(initial_value, (Label, NA)), "Initial value must be Label!"
|
|
552
|
+
return [initial_value] * size
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
# noinspection PyShadowingNames
|
|
556
|
+
def new_linefill(size: int | NA = 0,
|
|
557
|
+
initial_value: LineFill = NA(LineFill)) -> list[LineFill]:
|
|
558
|
+
"""
|
|
559
|
+
Creates a new array of linefill objects of the specified size, with each element initialized
|
|
560
|
+
to the specified value.
|
|
561
|
+
|
|
562
|
+
:param size: Size of the new array
|
|
563
|
+
:param initial_value: Initial value to set for each element in the array
|
|
564
|
+
:return: New array of linefill objects
|
|
565
|
+
"""
|
|
566
|
+
size = _na_size(size)
|
|
567
|
+
assert isinstance(initial_value, (LineFill, NA)), "Initial value must be LineFill!"
|
|
568
|
+
return [initial_value] * size
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
# noinspection PyShadowingNames
|
|
572
|
+
def new_table(size: int | NA = 0, initial_value: Table = NA(Table)) -> list[Table]:
|
|
573
|
+
"""
|
|
574
|
+
Creates a new array of table objects of the specified size, with each element initialized
|
|
575
|
+
to the specified value.
|
|
576
|
+
|
|
577
|
+
:param size: Size of the new array
|
|
578
|
+
:param initial_value: Initial value to set for each element in the array
|
|
579
|
+
:return: New array of table objects
|
|
580
|
+
"""
|
|
581
|
+
size = _na_size(size)
|
|
582
|
+
assert isinstance(initial_value, (Table, NA)), "Initial value must be Table!"
|
|
583
|
+
return [initial_value] * size
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
# noinspection PyShadowingNames
|
|
587
|
+
def new(size: int | NA = 0, initial_value: T = NA(T)) -> list[T]:
|
|
588
|
+
"""
|
|
589
|
+
Creates a new array of the specified size, with each element initialized to the specified value.
|
|
590
|
+
|
|
591
|
+
:param size: Size of the new array
|
|
592
|
+
:param initial_value: Initial value to set for each element in the array
|
|
593
|
+
:return: New array of the specified size
|
|
594
|
+
"""
|
|
595
|
+
size = _na_size(size)
|
|
596
|
+
return [initial_value] * size
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
# noinspection PyShadowingNames
|
|
600
|
+
def new_bool(size: int | NA = 0, initial_value: bool = NA(bool)) -> list[bool]:
|
|
601
|
+
"""
|
|
602
|
+
Creates a new array of the specified size, with each element initialized to the specified value.
|
|
603
|
+
|
|
604
|
+
:param size: Size of the new array
|
|
605
|
+
:param initial_value: Initial value to set for each element in the array
|
|
606
|
+
:return: New array of the specified size
|
|
607
|
+
"""
|
|
608
|
+
size = _na_size(size)
|
|
609
|
+
assert isinstance(initial_value, (bool, NA)), "Initial value must be bool!"
|
|
610
|
+
return [initial_value] * size
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
# noinspection PyShadowingNames
|
|
614
|
+
def new_color(size: int | NA = 0, initial_value: Color = NA(Color)) -> list[Color]:
|
|
615
|
+
"""
|
|
616
|
+
Creates a new array of the specified size, with each element initialized to the specified value.
|
|
617
|
+
|
|
618
|
+
:param size: Size of the new array
|
|
619
|
+
:param initial_value: Initial value to set for each element in the array
|
|
620
|
+
:return: New array of the specified size
|
|
621
|
+
"""
|
|
622
|
+
size = _na_size(size)
|
|
623
|
+
assert isinstance(initial_value, (Color, NA)), "Initial value must be Color!"
|
|
624
|
+
return [initial_value] * size
|
|
625
|
+
|
|
626
|
+
|
|
627
|
+
# noinspection PyShadowingNames
|
|
628
|
+
def new_float(size: int | NA = 0, initial_value: float | int = na_float) -> list[float]:
|
|
629
|
+
"""
|
|
630
|
+
Creates a new array of the specified size, with each element initialized to the specified value.
|
|
631
|
+
|
|
632
|
+
:param size: Size of the new array
|
|
633
|
+
:param initial_value: Initial value to set for each element in the array
|
|
634
|
+
:return: New array of the specified size
|
|
635
|
+
"""
|
|
636
|
+
size = _na_size(size)
|
|
637
|
+
assert isinstance(initial_value, (float, int, NA)), "Initial value must be float!"
|
|
638
|
+
if isinstance(initial_value, int):
|
|
639
|
+
initial_value = float(initial_value)
|
|
640
|
+
return [initial_value] * size
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
# noinspection PyShadowingNames
|
|
644
|
+
def new_int(size: int | NA = 0, initial_value: int = NA(int)) -> list[int]:
|
|
645
|
+
"""
|
|
646
|
+
Creates a new array of the specified size, with each element initialized to the specified value.
|
|
647
|
+
|
|
648
|
+
:param size: Size of the new array
|
|
649
|
+
:param initial_value: Initial value to set for each element in the array
|
|
650
|
+
:return: New array of the specified size
|
|
651
|
+
"""
|
|
652
|
+
size = _na_size(size)
|
|
653
|
+
assert isinstance(initial_value, (int, NA)), "Initial value must be int!"
|
|
654
|
+
return [initial_value] * size
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
# noinspection PyShadowingNames
|
|
658
|
+
def new_string(size: int | NA = 0, initial_value: str = NA(str)) -> list[str]:
|
|
659
|
+
"""
|
|
660
|
+
Creates a new array of the specified size, with each element initialized to the specified value.
|
|
661
|
+
|
|
662
|
+
:param size: Size of the new array
|
|
663
|
+
:param initial_value: Initial value to set for each element in the array
|
|
664
|
+
:return: New array of the specified size
|
|
665
|
+
"""
|
|
666
|
+
size = _na_size(size)
|
|
667
|
+
assert isinstance(initial_value, (str, NA)), "Initial value must be str!"
|
|
668
|
+
return [initial_value] * size
|
|
669
|
+
|
|
670
|
+
|
|
671
|
+
# noinspection PyShadowingBuiltins,PyShadowingNames
|
|
672
|
+
def percentile_linear_interpolation(id: list[float], percentage: float) -> float:
|
|
673
|
+
"""
|
|
674
|
+
Calculate the percentile value using linear interpolation, following TradingView's logic.
|
|
675
|
+
|
|
676
|
+
Values are sorted ascending with na elements pushed to the end (as if they
|
|
677
|
+
were the largest values). The interpolation position is 1-based over the full
|
|
678
|
+
array length, ``pos = n * percentage / 100 + 0.5``, clamped to the array
|
|
679
|
+
bounds.
|
|
680
|
+
|
|
681
|
+
Without na the value is interpolated linearly between the two ranks
|
|
682
|
+
straddling ``pos``. TradingView diverges once the array holds any na element:
|
|
683
|
+
it then yields a value only for the low-end clamp or for a ``pos`` that lands
|
|
684
|
+
exactly on an integer rank, and returns na for every fractional position --
|
|
685
|
+
even when both neighbouring values are numeric. An exact rank falling in the
|
|
686
|
+
sorted-to-end na tail likewise yields na.
|
|
687
|
+
|
|
688
|
+
:param id: List of numeric values, possibly containing na elements
|
|
689
|
+
:param percentage: Percentile (0-100, not 0-1)
|
|
690
|
+
:return: Interpolated value at the given percentile, or na (see above)
|
|
691
|
+
:raises ValueError: If arr is empty or percentage is not in [0, 100]
|
|
692
|
+
"""
|
|
693
|
+
if not id:
|
|
694
|
+
raise ValueError("Input array is empty")
|
|
695
|
+
if not (0 <= percentage <= 100):
|
|
696
|
+
raise ValueError("Percentage must be between 0 and 100")
|
|
697
|
+
|
|
698
|
+
has_na = any(isinstance(v, NA) or v != v for v in id)
|
|
699
|
+
# filter() instead of a comprehension: PyCharm mis-narrows `not isinstance`
|
|
700
|
+
# inside comprehension conditions (elements would type as NA, not float)
|
|
701
|
+
non_na = sorted(filter(lambda v: not (isinstance(v, NA) or v != v), id))
|
|
702
|
+
sorted_arr = non_na + [na_float] * (len(id) - len(non_na))
|
|
703
|
+
n = len(id)
|
|
704
|
+
|
|
705
|
+
# 1-based interpolation position over the full length
|
|
706
|
+
pos = n * percentage / 100.0 + 0.5
|
|
707
|
+
# Snap to an exact integer rank when floating-point noise leaves us just shy
|
|
708
|
+
nearest = round(pos)
|
|
709
|
+
if builtins.abs(pos - nearest) < 1e-9:
|
|
710
|
+
pos = float(nearest)
|
|
711
|
+
|
|
712
|
+
if pos <= 1:
|
|
713
|
+
return sorted_arr[0]
|
|
714
|
+
if pos >= n:
|
|
715
|
+
return sorted_arr[-1]
|
|
716
|
+
|
|
717
|
+
lower = math.floor(pos) # 1-based lower rank
|
|
718
|
+
frac = pos - lower
|
|
719
|
+
if frac == 0:
|
|
720
|
+
return sorted_arr[lower - 1]
|
|
721
|
+
if has_na:
|
|
722
|
+
return na_float
|
|
723
|
+
return sorted_arr[lower - 1] + frac * (sorted_arr[lower] - sorted_arr[lower - 1])
|
|
724
|
+
|
|
725
|
+
|
|
726
|
+
# noinspection PyShadowingBuiltins,PyShadowingNames
|
|
727
|
+
def percentile_nearest_rank(id: list[float], percentage: float) -> float:
|
|
728
|
+
"""
|
|
729
|
+
Calculate the nearest rank percentile without interpolation.
|
|
730
|
+
|
|
731
|
+
Matches TradingView: na elements are kept and sort to the end (as if they
|
|
732
|
+
were the largest values), so the full array length (na included) drives the
|
|
733
|
+
rank. A rank that lands on a na element yields na.
|
|
734
|
+
|
|
735
|
+
:param id: List of numeric values
|
|
736
|
+
:param percentage: Percentile (0-100)
|
|
737
|
+
:return: The value at the nearest rank for the specified percentile, or na
|
|
738
|
+
if that rank falls on a na element
|
|
739
|
+
:raises ValueError: If arr is empty or percentage is not between 0 and 100
|
|
740
|
+
"""
|
|
741
|
+
if not id:
|
|
742
|
+
raise ValueError("Input array is empty")
|
|
743
|
+
if not (0 <= percentage <= 100):
|
|
744
|
+
raise ValueError("Percentage must be between 0 and 100")
|
|
745
|
+
|
|
746
|
+
# filter() instead of a comprehension: see percentile_linear_interpolation
|
|
747
|
+
non_na = sorted(filter(lambda v: not (isinstance(v, NA) or v != v), id))
|
|
748
|
+
sorted_arr = non_na + [na_float] * (len(id) - len(non_na))
|
|
749
|
+
n = len(id)
|
|
750
|
+
if percentage == 0:
|
|
751
|
+
return sorted_arr[0]
|
|
752
|
+
|
|
753
|
+
# Calculate the rank using the ceiling function as per the nearest rank method
|
|
754
|
+
rank = math.ceil(percentage * n / 100)
|
|
755
|
+
# Clamp rank to be within the valid range [1, n]
|
|
756
|
+
rank = builtins.max(1, builtins.min(rank, n))
|
|
757
|
+
# Adjust for 0-indexed array: return the (rank-1)th element
|
|
758
|
+
return sorted_arr[rank - 1]
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
# noinspection PyShadowingBuiltins,PyShadowingNames
|
|
762
|
+
def percentrank(id: list[Number], index: int) -> float:
|
|
763
|
+
"""
|
|
764
|
+
Returns the percentile rank of the element at the specified index.
|
|
765
|
+
The percentile rank is the percentage of values less than or equal to the value at index.
|
|
766
|
+
|
|
767
|
+
Matches TradingView: na elements are ignored when counting values at or below
|
|
768
|
+
the target, but still count toward the array length. If the element at
|
|
769
|
+
``index`` is itself na, the rank is na.
|
|
770
|
+
|
|
771
|
+
:param id: Input array
|
|
772
|
+
:param index: Index of the element to calculate rank for
|
|
773
|
+
:return: Percentile rank (0-100), or na if the element at index is na
|
|
774
|
+
:raises ValueError: If input array is empty or index is out of range
|
|
775
|
+
"""
|
|
776
|
+
if not id:
|
|
777
|
+
raise ValueError("Input array is empty")
|
|
778
|
+
|
|
779
|
+
if not 0 <= index < len(id):
|
|
780
|
+
raise ValueError("Index out of range")
|
|
781
|
+
|
|
782
|
+
# Get value at index
|
|
783
|
+
value = id[index]
|
|
784
|
+
if isinstance(value, NA) or value != value:
|
|
785
|
+
return na_float
|
|
786
|
+
|
|
787
|
+
# Count non-na elements less than or equal to the target value
|
|
788
|
+
count = builtins.sum(1 for x in id if not (isinstance(x, NA) or x != x) and x <= value)
|
|
789
|
+
|
|
790
|
+
# Calculate percentage
|
|
791
|
+
return (count - 1) * 100 / (len(id) - 1)
|
|
792
|
+
|
|
793
|
+
|
|
794
|
+
# noinspection PyShadowingBuiltins
|
|
795
|
+
def pop(id: list[T]) -> T:
|
|
796
|
+
"""
|
|
797
|
+
Removes the last element from the array and returns it.
|
|
798
|
+
|
|
799
|
+
:param id: Input array
|
|
800
|
+
:return: Last element from the array
|
|
801
|
+
"""
|
|
802
|
+
return id.pop()
|
|
803
|
+
|
|
804
|
+
|
|
805
|
+
# noinspection PyShadowingBuiltins
|
|
806
|
+
def push(id: list[T], value: T) -> None:
|
|
807
|
+
"""
|
|
808
|
+
Appends the specified value to the end of the array.
|
|
809
|
+
|
|
810
|
+
:param id: Input array
|
|
811
|
+
:param value: Value to append
|
|
812
|
+
"""
|
|
813
|
+
id.append(value)
|
|
814
|
+
|
|
815
|
+
|
|
816
|
+
# noinspection PyShadowingBuiltins
|
|
817
|
+
def range(id: list[Number]) -> Number:
|
|
818
|
+
"""
|
|
819
|
+
Returns the range of the elements in the array.
|
|
820
|
+
|
|
821
|
+
:param id: Input array
|
|
822
|
+
:return: Range of the elements in the array, or na if the array is empty
|
|
823
|
+
"""
|
|
824
|
+
a = _non_na(id)
|
|
825
|
+
if not a:
|
|
826
|
+
return id[0] if id else NA(None)
|
|
827
|
+
return builtins.max(a) - builtins.min(a)
|
|
828
|
+
|
|
829
|
+
|
|
830
|
+
# noinspection PyShadowingBuiltins
|
|
831
|
+
def remove(id: list[T], index: int) -> T:
|
|
832
|
+
"""
|
|
833
|
+
Removes the element at the specified index from the array.
|
|
834
|
+
|
|
835
|
+
:param id: Input array
|
|
836
|
+
:param index: Index of the element to remove
|
|
837
|
+
:return: The removed element
|
|
838
|
+
"""
|
|
839
|
+
return id.pop(index)
|
|
840
|
+
|
|
841
|
+
|
|
842
|
+
# noinspection PyShadowingBuiltins
|
|
843
|
+
def reverse(id: list[T]) -> None:
|
|
844
|
+
"""
|
|
845
|
+
Reverses the order of the elements in the array.
|
|
846
|
+
|
|
847
|
+
:param id: Input array
|
|
848
|
+
"""
|
|
849
|
+
id.reverse()
|
|
850
|
+
|
|
851
|
+
|
|
852
|
+
# noinspection PyShadowingBuiltins
|
|
853
|
+
def set(id: list[T] | SequenceView[T], index: int, value: T) -> None:
|
|
854
|
+
"""
|
|
855
|
+
Sets the value of the element at the specified index in the array.
|
|
856
|
+
|
|
857
|
+
:param id: Input array
|
|
858
|
+
:param index: Index of the element to set
|
|
859
|
+
:param value: Value to set
|
|
860
|
+
"""
|
|
861
|
+
id[index] = value
|
|
862
|
+
|
|
863
|
+
|
|
864
|
+
# noinspection PyShadowingBuiltins
|
|
865
|
+
def shift(id: list[T]) -> T:
|
|
866
|
+
"""
|
|
867
|
+
Removes the first element from the array and returns it.
|
|
868
|
+
|
|
869
|
+
:param id: Input array
|
|
870
|
+
:return: First element from the array
|
|
871
|
+
"""
|
|
872
|
+
return id.pop(0)
|
|
873
|
+
|
|
874
|
+
|
|
875
|
+
# noinspection PyShadowingBuiltins
|
|
876
|
+
def size(id: list[Any] | SequenceView[Any]) -> int:
|
|
877
|
+
"""
|
|
878
|
+
Returns the number of elements in the array.
|
|
879
|
+
|
|
880
|
+
:param id: Input array
|
|
881
|
+
:return: Number of elements in the array
|
|
882
|
+
"""
|
|
883
|
+
return len(id)
|
|
884
|
+
|
|
885
|
+
|
|
886
|
+
# noinspection PyShadowingBuiltins
|
|
887
|
+
def slice(id: list[T], index_from: int, index_to: int) -> SequenceView[T]:
|
|
888
|
+
"""
|
|
889
|
+
The function creates a slice from an existing array. If an object from the slice changes, the
|
|
890
|
+
changes are applied to both the new and the original arrays.
|
|
891
|
+
|
|
892
|
+
:param id: Input array
|
|
893
|
+
:param index_from: Index to start the sub-array from
|
|
894
|
+
:param index_to: Index to end the sub-array at
|
|
895
|
+
:return: Slice view of the original array
|
|
896
|
+
"""
|
|
897
|
+
return SequenceView(id)[int(index_from):int(index_to)] # type: ignore
|
|
898
|
+
|
|
899
|
+
|
|
900
|
+
# noinspection PyShadowingBuiltins
|
|
901
|
+
def some(id: list[Any]) -> bool:
|
|
902
|
+
"""
|
|
903
|
+
Returns true if at least one element of the id array is true, false otherwise.
|
|
904
|
+
|
|
905
|
+
:param id: Input array
|
|
906
|
+
:return: True if at least one element of the id array is true, false otherwise
|
|
907
|
+
"""
|
|
908
|
+
return any(id)
|
|
909
|
+
|
|
910
|
+
|
|
911
|
+
# noinspection PyShadowingBuiltins
|
|
912
|
+
def sort(id: list[int | float | str], order: _order.Order = _order.ascending) -> None:
|
|
913
|
+
"""
|
|
914
|
+
Sorts the elements in the array in ascending or descending order.
|
|
915
|
+
|
|
916
|
+
:param id: Input array
|
|
917
|
+
:param order: Order to sort the elements in
|
|
918
|
+
"""
|
|
919
|
+
id.sort(reverse=order == _order.descending)
|
|
920
|
+
|
|
921
|
+
|
|
922
|
+
# noinspection PyShadowingBuiltins
|
|
923
|
+
def sort_indices(id: list[T], order: _order.Order = _order.ascending) -> list[int]:
|
|
924
|
+
"""
|
|
925
|
+
Returns an array of indices which, when used to index the original array, will access its elements
|
|
926
|
+
in their sorted order. It does not modify the original array.
|
|
927
|
+
|
|
928
|
+
:param id: Input array
|
|
929
|
+
:param order: Order to sort the elements in
|
|
930
|
+
:return: Array of indices to access the elements in their sorted order
|
|
931
|
+
"""
|
|
932
|
+
indices: list[int] = sorted(builtins.range(len(id)), key=id.__getitem__) # type: ignore
|
|
933
|
+
if order == _order.descending:
|
|
934
|
+
indices.reverse()
|
|
935
|
+
return indices
|
|
936
|
+
|
|
937
|
+
|
|
938
|
+
# noinspection PyShadowingBuiltins,PyShadowingNames
|
|
939
|
+
def standardize(id: list[float | int]) -> list[float | int]:
|
|
940
|
+
"""
|
|
941
|
+
Standardizes the input array in a Pine Script-like manner:
|
|
942
|
+
1) Uses a left-to-right summation for the mean (population mean).
|
|
943
|
+
2) Uses a second pass for summing squared differences (population variance).
|
|
944
|
+
3) Computes the population standard deviation (divisor = N).
|
|
945
|
+
4) Returns the z-score for each element.
|
|
946
|
+
- If all input elements are integers, it applies thresholding:
|
|
947
|
+
z < -1 -> -1,
|
|
948
|
+
z > 1 -> 1,
|
|
949
|
+
otherwise 0
|
|
950
|
+
- If any element is float, the result is the continuous z-score value.
|
|
951
|
+
This version is bit-by-bit compatible with Pine Script's `standardize()` function.
|
|
952
|
+
|
|
953
|
+
:param id: A list of numeric values (int or float).
|
|
954
|
+
:return: A list containing the standardized values.
|
|
955
|
+
"""
|
|
956
|
+
n = len(id)
|
|
957
|
+
if n == 0:
|
|
958
|
+
# You can decide how you want to handle the empty list.
|
|
959
|
+
return []
|
|
960
|
+
|
|
961
|
+
mean = statistics.mean(id)
|
|
962
|
+
stdev = math.sqrt(statistics.mean([(v - mean) ** 2 for v in id]))
|
|
963
|
+
if stdev == 0:
|
|
964
|
+
# All elements are equal: TV's standardize() yields 1.0 for every element.
|
|
965
|
+
z_scores = [1.0 for _ in id]
|
|
966
|
+
else:
|
|
967
|
+
z_scores = [(v - mean) / stdev for v in id]
|
|
968
|
+
|
|
969
|
+
# If all values are integers, apply the thresholding to get -1, 0, or 1.
|
|
970
|
+
if all(isinstance(v, int) for v in id):
|
|
971
|
+
# Pine Script-style integer thresholding
|
|
972
|
+
return [
|
|
973
|
+
-1 if z < -1 else
|
|
974
|
+
1 if z > 1 else
|
|
975
|
+
0
|
|
976
|
+
for z in z_scores
|
|
977
|
+
]
|
|
978
|
+
|
|
979
|
+
# Otherwise, return the continuous z-score values.
|
|
980
|
+
return z_scores
|
|
981
|
+
|
|
982
|
+
|
|
983
|
+
# noinspection PyShadowingBuiltins
|
|
984
|
+
def stdev(id: list[Number], biased: bool = True) -> float:
|
|
985
|
+
"""
|
|
986
|
+
Returns the standard deviation of the elements in the array.
|
|
987
|
+
|
|
988
|
+
:param id: Input array
|
|
989
|
+
:param biased: If True, calculates the biased standard deviation. If False, calculates the
|
|
990
|
+
unbiased standard deviation.
|
|
991
|
+
:return: Standard deviation of the elements in the array, or na if the array is empty
|
|
992
|
+
"""
|
|
993
|
+
a = _non_na(id)
|
|
994
|
+
if not a:
|
|
995
|
+
return na_float
|
|
996
|
+
if len(a) < 2:
|
|
997
|
+
return 0.0
|
|
998
|
+
if not biased:
|
|
999
|
+
return statistics.stdev(a)
|
|
1000
|
+
mean = statistics.mean(a)
|
|
1001
|
+
return math.sqrt(statistics.mean([(v - mean) ** 2 for v in a]))
|
|
1002
|
+
|
|
1003
|
+
|
|
1004
|
+
# noinspection PyShadowingBuiltins
|
|
1005
|
+
def sum(id: list[float | int]) -> float | int:
|
|
1006
|
+
"""
|
|
1007
|
+
Returns the sum of the elements in the array.
|
|
1008
|
+
|
|
1009
|
+
:param id: Input array
|
|
1010
|
+
:return: Sum of the elements in the array, or na if the array is empty
|
|
1011
|
+
"""
|
|
1012
|
+
a = _non_na(id)
|
|
1013
|
+
if not a:
|
|
1014
|
+
return na_float
|
|
1015
|
+
return builtins.sum(a)
|
|
1016
|
+
|
|
1017
|
+
|
|
1018
|
+
# noinspection PyShadowingBuiltins
|
|
1019
|
+
def unshift(id: list[T], value: T) -> None:
|
|
1020
|
+
"""
|
|
1021
|
+
Prepends the specified value to the beginning of the array.
|
|
1022
|
+
|
|
1023
|
+
:param id: Input array
|
|
1024
|
+
:param value: Value to prepend
|
|
1025
|
+
"""
|
|
1026
|
+
id.insert(0, value)
|
|
1027
|
+
|
|
1028
|
+
|
|
1029
|
+
# noinspection PyShadowingBuiltins
|
|
1030
|
+
def variance(id: list[Number], biased: bool = True) -> float:
|
|
1031
|
+
"""
|
|
1032
|
+
Returns the variance of the elements in the array.
|
|
1033
|
+
|
|
1034
|
+
:param id: Input array
|
|
1035
|
+
:param biased: If True, calculates the biased variance. If False, calculates the unbiased variance.
|
|
1036
|
+
:return: Variance of the elements in the array, or na if the array is empty
|
|
1037
|
+
"""
|
|
1038
|
+
a = _non_na(id)
|
|
1039
|
+
if not a:
|
|
1040
|
+
return na_float
|
|
1041
|
+
if len(a) < 2:
|
|
1042
|
+
return 0.0
|
|
1043
|
+
if not biased:
|
|
1044
|
+
return statistics.variance(a)
|
|
1045
|
+
|
|
1046
|
+
length = len(a)
|
|
1047
|
+
mean = statistics.mean(a)
|
|
1048
|
+
summ = 0.0
|
|
1049
|
+
for v in a:
|
|
1050
|
+
summ += (v - mean) ** 2
|
|
1051
|
+
return summ / length
|