oakscriptpy 0.1.0__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.
- oakscriptpy/__init__.py +93 -0
- oakscriptpy/_metadata.py +118 -0
- oakscriptpy/_types.py +185 -0
- oakscriptpy/_utils.py +145 -0
- oakscriptpy/adapters/__init__.py +5 -0
- oakscriptpy/adapters/simple_input.py +63 -0
- oakscriptpy/array.py +342 -0
- oakscriptpy/box.py +151 -0
- oakscriptpy/chartpoint.py +21 -0
- oakscriptpy/color.py +134 -0
- oakscriptpy/indicator.py +82 -0
- oakscriptpy/input_.py +38 -0
- oakscriptpy/inputs.py +170 -0
- oakscriptpy/label.py +110 -0
- oakscriptpy/lib/__init__.py +5 -0
- oakscriptpy/lib/zigzag.py +158 -0
- oakscriptpy/line.py +120 -0
- oakscriptpy/linefill.py +26 -0
- oakscriptpy/math_.py +184 -0
- oakscriptpy/matrix.py +1136 -0
- oakscriptpy/plot_.py +49 -0
- oakscriptpy/polyline.py +60 -0
- oakscriptpy/runtime.py +150 -0
- oakscriptpy/runtime_types.py +52 -0
- oakscriptpy/series.py +292 -0
- oakscriptpy/str_.py +166 -0
- oakscriptpy/ta.py +1795 -0
- oakscriptpy/ta_series.py +353 -0
- oakscriptpy/time_.py +22 -0
- oakscriptpy-0.1.0.dist-info/METADATA +120 -0
- oakscriptpy-0.1.0.dist-info/RECORD +32 -0
- oakscriptpy-0.1.0.dist-info/WHEEL +4 -0
oakscriptpy/__init__.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""OakScriptPy — PineScript-like API for technical analysis in Python."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
# Core namespaces (array-based functions)
|
|
5
|
+
from . import ta as ta_core
|
|
6
|
+
from . import math_ as math
|
|
7
|
+
from . import array
|
|
8
|
+
from . import str_ as str
|
|
9
|
+
from . import color
|
|
10
|
+
from . import time_ as time
|
|
11
|
+
from . import matrix
|
|
12
|
+
|
|
13
|
+
# Drawing object namespaces
|
|
14
|
+
from . import line
|
|
15
|
+
from . import box
|
|
16
|
+
from . import label
|
|
17
|
+
from . import linefill
|
|
18
|
+
from . import chartpoint as chart_point
|
|
19
|
+
from . import polyline
|
|
20
|
+
|
|
21
|
+
# Series class (self-contained, no context)
|
|
22
|
+
from .series import Series, BarData
|
|
23
|
+
|
|
24
|
+
# TA-Series namespace (Series-based wrappers)
|
|
25
|
+
from . import ta_series as ta
|
|
26
|
+
|
|
27
|
+
# Types
|
|
28
|
+
from ._types import (
|
|
29
|
+
Bar, Line as LineType, Box as BoxType, Label as LabelType,
|
|
30
|
+
Linefill as LinefillType, ChartPoint, Polyline as PolylineType,
|
|
31
|
+
Table, TableCell, Plot, HLine, OHLC, PineMatrix,
|
|
32
|
+
series_float, series_int, series_bool, series_string,
|
|
33
|
+
Source, color as color_type, na, xloc,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
# Metadata types
|
|
37
|
+
from ._metadata import (
|
|
38
|
+
PlotOptions, HLineOptions, FillOptions, InputMetadata,
|
|
39
|
+
PlotMetadata, IndicatorMetadata, TimeValue, PlotData,
|
|
40
|
+
HLineData, FillData, IndicatorResult,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Utilities
|
|
44
|
+
from ._utils import is_na, nz, ohlc_from_bars, get_close, get_high, get_low, get_open, get_source
|
|
45
|
+
|
|
46
|
+
# Runtime
|
|
47
|
+
from .runtime import (
|
|
48
|
+
set_context, clear_context, get_context,
|
|
49
|
+
register_calculate, recalculate,
|
|
50
|
+
plot, hline, clear_plots, get_active_plots,
|
|
51
|
+
)
|
|
52
|
+
from .runtime_types import OakScriptContext, OhlcvData, InputConfig
|
|
53
|
+
|
|
54
|
+
# Inputs
|
|
55
|
+
from .inputs import (
|
|
56
|
+
input_int, input_float, input_bool, input_string, input_source,
|
|
57
|
+
enable_auto_recalculate, disable_auto_recalculate, reset_inputs,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# Indicator infrastructure
|
|
61
|
+
from .indicator import indicator, IndicatorMetadataConfig, IndicatorContext, IndicatorInstance
|
|
62
|
+
|
|
63
|
+
# Input helper
|
|
64
|
+
from . import input_ as input
|
|
65
|
+
|
|
66
|
+
# Plot helper
|
|
67
|
+
from .plot_ import plot as plot_helper, create_plot, TimeValuePair, PlotResult
|
|
68
|
+
|
|
69
|
+
# Adapters
|
|
70
|
+
from .adapters import SimpleInputAdapter
|
|
71
|
+
|
|
72
|
+
# Libraries
|
|
73
|
+
from .lib import ZigZag, calculate_zigzag
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def alertcondition(_condition: object = None, _title: str | None = None, _message: str | None = None) -> None:
|
|
77
|
+
"""No-op stub for PineScript alertcondition() compatibility."""
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
VERSION = "0.1.0"
|
|
82
|
+
|
|
83
|
+
info = {
|
|
84
|
+
"name": "OakScriptPy",
|
|
85
|
+
"version": VERSION,
|
|
86
|
+
"description": "PineScript-like API for technical analysis in Python",
|
|
87
|
+
"namespaces": {
|
|
88
|
+
"core": ["ta", "math", "array", "str", "color", "time", "matrix"],
|
|
89
|
+
"drawing": ["line", "box", "label", "linefill", "chart_point", "polyline"],
|
|
90
|
+
"runtime": ["set_context", "plot", "hline", "input_*"],
|
|
91
|
+
"indicator": ["indicator", "input", "plot_helper", "create_plot"],
|
|
92
|
+
},
|
|
93
|
+
}
|
oakscriptpy/_metadata.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""Indicator metadata types for oakscriptPy."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
PlotStyle = str # 'line' | 'stepline' | 'histogram' | 'area' | 'circles' | 'columns' | 'cross' | 'areabr' | 'steplinebr' | 'linebr'
|
|
10
|
+
LineStyle = str # 'solid' | 'dashed' | 'dotted'
|
|
11
|
+
PlotDisplay = str # 'all' | 'none' | 'data_window' | 'status_line' | 'pane'
|
|
12
|
+
InputType = str # 'int' | 'float' | 'bool' | 'string' | 'source' | 'color' | 'timeframe' | 'session'
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class PlotOptions:
|
|
17
|
+
title: str | None = None
|
|
18
|
+
color: str | None = None
|
|
19
|
+
linewidth: int | None = None
|
|
20
|
+
style: str | None = None
|
|
21
|
+
trackprice: bool | None = None
|
|
22
|
+
histbase: float | None = None
|
|
23
|
+
offset: int | None = None
|
|
24
|
+
join: bool | None = None
|
|
25
|
+
editable: bool | None = None
|
|
26
|
+
display: str | None = None
|
|
27
|
+
transp: int | None = None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class HLineOptions:
|
|
32
|
+
title: str | None = None
|
|
33
|
+
color: str | None = None
|
|
34
|
+
linestyle: str | None = None
|
|
35
|
+
linewidth: int | None = None
|
|
36
|
+
editable: bool | None = None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class FillOptions:
|
|
41
|
+
color: str | None = None
|
|
42
|
+
transp: int | None = None
|
|
43
|
+
title: str | None = None
|
|
44
|
+
editable: bool | None = None
|
|
45
|
+
fillgaps: bool | None = None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class InputMetadata:
|
|
50
|
+
type: str
|
|
51
|
+
name: str
|
|
52
|
+
title: str
|
|
53
|
+
defval: Any
|
|
54
|
+
minval: float | None = None
|
|
55
|
+
maxval: float | None = None
|
|
56
|
+
step: float | None = None
|
|
57
|
+
tooltip: str | None = None
|
|
58
|
+
inline: str | None = None
|
|
59
|
+
group: str | None = None
|
|
60
|
+
options: list[Any] | None = None
|
|
61
|
+
confirm: bool | None = None
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dataclass
|
|
65
|
+
class PlotMetadata:
|
|
66
|
+
var_name: str
|
|
67
|
+
title: str
|
|
68
|
+
color: str
|
|
69
|
+
linewidth: int
|
|
70
|
+
style: str
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@dataclass
|
|
74
|
+
class IndicatorMetadata:
|
|
75
|
+
title: str
|
|
76
|
+
shorttitle: str | None = None
|
|
77
|
+
overlay: bool = False
|
|
78
|
+
precision: int | None = None
|
|
79
|
+
format: str | None = None
|
|
80
|
+
timeframe: str | None = None
|
|
81
|
+
timeframe_gaps: bool | None = None
|
|
82
|
+
inputs: list[InputMetadata] | None = None
|
|
83
|
+
plots: list[PlotMetadata] | None = None
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@dataclass
|
|
87
|
+
class TimeValue:
|
|
88
|
+
time: Any
|
|
89
|
+
value: float
|
|
90
|
+
color: str | None = None
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@dataclass
|
|
94
|
+
class PlotData:
|
|
95
|
+
data: list[TimeValue]
|
|
96
|
+
options: PlotOptions | None = None
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@dataclass
|
|
100
|
+
class HLineData:
|
|
101
|
+
value: float
|
|
102
|
+
options: HLineOptions | None = None
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@dataclass
|
|
106
|
+
class FillData:
|
|
107
|
+
plot1: int | str
|
|
108
|
+
plot2: int | str
|
|
109
|
+
options: FillOptions | None = None
|
|
110
|
+
colors: list[str] | None = None
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@dataclass
|
|
114
|
+
class IndicatorResult:
|
|
115
|
+
metadata: IndicatorMetadata
|
|
116
|
+
plots: dict[str, list[TimeValue]]
|
|
117
|
+
hlines: list[HLineData] | None = None
|
|
118
|
+
fills: list[FillData] | None = None
|
oakscriptpy/_types.py
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"""Core types for oakscriptPy — mirrors PineScript type system."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
# Basic type aliases
|
|
9
|
+
series_float = list[float]
|
|
10
|
+
series_int = list[int]
|
|
11
|
+
series_bool = list[bool]
|
|
12
|
+
series_string = list[str]
|
|
13
|
+
series_color = list[str | int]
|
|
14
|
+
Source = list[float]
|
|
15
|
+
color = str | int
|
|
16
|
+
|
|
17
|
+
# Constants
|
|
18
|
+
na = None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class _Xloc:
|
|
22
|
+
bar_index: str = "bar_index"
|
|
23
|
+
bar_time: str = "bar_time"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
xloc = _Xloc()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# Drawing objects
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class Line:
|
|
33
|
+
x1: float
|
|
34
|
+
y1: float
|
|
35
|
+
x2: float
|
|
36
|
+
y2: float
|
|
37
|
+
xloc: str = "bar_index"
|
|
38
|
+
extend: str = "none"
|
|
39
|
+
color: str | int | None = None
|
|
40
|
+
style: str | None = None
|
|
41
|
+
width: int | None = None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class Box:
|
|
46
|
+
left: float
|
|
47
|
+
top: float
|
|
48
|
+
right: float
|
|
49
|
+
bottom: float
|
|
50
|
+
xloc: str = "bar_index"
|
|
51
|
+
extend: str = "none"
|
|
52
|
+
border_color: str | int | None = None
|
|
53
|
+
border_width: int | None = None
|
|
54
|
+
border_style: str | None = None
|
|
55
|
+
bgcolor: str | int | None = None
|
|
56
|
+
text: str | None = None
|
|
57
|
+
text_size: str | int | None = None
|
|
58
|
+
text_color: str | int | None = None
|
|
59
|
+
text_halign: str | None = None
|
|
60
|
+
text_valign: str | None = None
|
|
61
|
+
text_wrap: str | None = None
|
|
62
|
+
text_font_family: str | None = None
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclass
|
|
66
|
+
class Label:
|
|
67
|
+
x: float
|
|
68
|
+
y: float
|
|
69
|
+
xloc: str = "bar_index"
|
|
70
|
+
yloc: str = "price"
|
|
71
|
+
text: str | None = None
|
|
72
|
+
tooltip: str | None = None
|
|
73
|
+
color: str | int | None = None
|
|
74
|
+
style: str | None = None
|
|
75
|
+
textcolor: str | int | None = None
|
|
76
|
+
size: str | int | None = None
|
|
77
|
+
textalign: str | None = None
|
|
78
|
+
text_font_family: str | None = None
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@dataclass
|
|
82
|
+
class Linefill:
|
|
83
|
+
line1: Line
|
|
84
|
+
line2: Line
|
|
85
|
+
color: str | int | None = None
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@dataclass
|
|
89
|
+
class Table:
|
|
90
|
+
position: str
|
|
91
|
+
columns: int
|
|
92
|
+
rows: int
|
|
93
|
+
frame_color: str | int | None = None
|
|
94
|
+
frame_width: int | None = None
|
|
95
|
+
border_color: str | int | None = None
|
|
96
|
+
border_width: int | None = None
|
|
97
|
+
bgcolor: str | int | None = None
|
|
98
|
+
cells: dict[str, TableCell] | None = None
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@dataclass
|
|
102
|
+
class TableCell:
|
|
103
|
+
text: str | None = None
|
|
104
|
+
width: float | None = None
|
|
105
|
+
height: float | None = None
|
|
106
|
+
text_color: str | int | None = None
|
|
107
|
+
text_halign: str | None = None
|
|
108
|
+
text_valign: str | None = None
|
|
109
|
+
text_size: str | int | None = None
|
|
110
|
+
bgcolor: str | int | None = None
|
|
111
|
+
tooltip: str | None = None
|
|
112
|
+
text_font_family: str | None = None
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@dataclass
|
|
116
|
+
class Plot:
|
|
117
|
+
id: str
|
|
118
|
+
series: Any = None
|
|
119
|
+
title: str | None = None
|
|
120
|
+
color: Any = None
|
|
121
|
+
linewidth: int | None = None
|
|
122
|
+
style: str | None = None
|
|
123
|
+
trackprice: bool | None = None
|
|
124
|
+
histbase: float | None = None
|
|
125
|
+
offset: int | None = None
|
|
126
|
+
join: bool | None = None
|
|
127
|
+
editable: bool | None = None
|
|
128
|
+
display: str | None = None
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@dataclass
|
|
132
|
+
class HLine:
|
|
133
|
+
id: str
|
|
134
|
+
price: float
|
|
135
|
+
title: str | None = None
|
|
136
|
+
color: Any = None
|
|
137
|
+
linestyle: str | None = None
|
|
138
|
+
linewidth: int | None = None
|
|
139
|
+
editable: bool | None = None
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@dataclass
|
|
143
|
+
class Bar:
|
|
144
|
+
time: int
|
|
145
|
+
open: float
|
|
146
|
+
high: float
|
|
147
|
+
low: float
|
|
148
|
+
close: float
|
|
149
|
+
volume: float = 0.0
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@dataclass
|
|
153
|
+
class OHLC:
|
|
154
|
+
open: list[float]
|
|
155
|
+
high: list[float]
|
|
156
|
+
low: list[float]
|
|
157
|
+
close: list[float]
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@dataclass
|
|
161
|
+
class ChartPoint:
|
|
162
|
+
time: float | None
|
|
163
|
+
index: int | None
|
|
164
|
+
price: float
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
@dataclass
|
|
168
|
+
class Polyline:
|
|
169
|
+
id: str
|
|
170
|
+
points: list[ChartPoint]
|
|
171
|
+
curved: bool = False
|
|
172
|
+
closed: bool = False
|
|
173
|
+
xloc: str = "bar_index"
|
|
174
|
+
line_color: str | int = "#2962FF"
|
|
175
|
+
fill_color: str | int | None = None
|
|
176
|
+
line_style: str = "solid"
|
|
177
|
+
line_width: int = 1
|
|
178
|
+
force_overlay: bool = False
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@dataclass
|
|
182
|
+
class PineMatrix:
|
|
183
|
+
rows: int
|
|
184
|
+
columns: int
|
|
185
|
+
data: list[list[Any]] = field(default_factory=list)
|
oakscriptpy/_utils.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"""Internal utility functions for oakscriptPy."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import math
|
|
6
|
+
import warnings
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
8
|
+
|
|
9
|
+
from ._types import Bar, OHLC, series_float
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from .series import Series
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def validate_series_length(source: list[float], length: int, function_name: str) -> None:
|
|
16
|
+
if len(source) < length:
|
|
17
|
+
warnings.warn(
|
|
18
|
+
f"{function_name}: Source length ({len(source)}) is less than required length ({length})"
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def create_nan_series(length: int) -> series_float:
|
|
23
|
+
return [float("nan")] * length
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def is_na(value: object) -> bool:
|
|
27
|
+
if value is None:
|
|
28
|
+
return True
|
|
29
|
+
if isinstance(value, float) and math.isnan(value):
|
|
30
|
+
return True
|
|
31
|
+
return False
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def nz(value: object, default: object = 0) -> object:
|
|
35
|
+
return default if is_na(value) else value
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def clamp(value: float, min_val: float, max_val: float) -> float:
|
|
39
|
+
return max(min_val, min(max_val, value))
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def lerp(start: float, end: float, t: float) -> float:
|
|
43
|
+
return start + (end - start) * t
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def normalize(
|
|
47
|
+
value: float, min_val: float, max_val: float, new_min: float = 0.0, new_max: float = 1.0
|
|
48
|
+
) -> float:
|
|
49
|
+
return new_min + ((value - min_val) * (new_max - new_min)) / (max_val - min_val)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# Chart data utilities
|
|
53
|
+
|
|
54
|
+
def ohlc_from_bars(bars: list[Bar]) -> OHLC:
|
|
55
|
+
return OHLC(
|
|
56
|
+
open=[b.open for b in bars],
|
|
57
|
+
high=[b.high for b in bars],
|
|
58
|
+
low=[b.low for b in bars],
|
|
59
|
+
close=[b.close for b in bars],
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def get_close(ohlc: OHLC) -> series_float:
|
|
64
|
+
return ohlc.close
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def get_high(ohlc: OHLC) -> series_float:
|
|
68
|
+
return ohlc.high
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def get_low(ohlc: OHLC) -> series_float:
|
|
72
|
+
return ohlc.low
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def get_open(ohlc: OHLC) -> series_float:
|
|
76
|
+
return ohlc.open
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def get_source(
|
|
80
|
+
data: list[Bar] | OHLC,
|
|
81
|
+
source: str = "close",
|
|
82
|
+
) -> series_float:
|
|
83
|
+
if isinstance(data, OHLC):
|
|
84
|
+
ohlc = data
|
|
85
|
+
if source == "close":
|
|
86
|
+
return ohlc.close
|
|
87
|
+
if source == "open":
|
|
88
|
+
return ohlc.open
|
|
89
|
+
if source == "high":
|
|
90
|
+
return ohlc.high
|
|
91
|
+
if source == "low":
|
|
92
|
+
return ohlc.low
|
|
93
|
+
if source == "hl2":
|
|
94
|
+
return [(h + l) / 2 for h, l in zip(ohlc.high, ohlc.low)]
|
|
95
|
+
if source == "hlc3":
|
|
96
|
+
return [(h + l + c) / 3 for h, l, c in zip(ohlc.high, ohlc.low, ohlc.close)]
|
|
97
|
+
if source == "ohlc4":
|
|
98
|
+
return [
|
|
99
|
+
(o + h + l + c) / 4
|
|
100
|
+
for o, h, l, c in zip(ohlc.open, ohlc.high, ohlc.low, ohlc.close)
|
|
101
|
+
]
|
|
102
|
+
if source == "hlcc4":
|
|
103
|
+
return [
|
|
104
|
+
(h + l + c + c) / 4 for h, l, c in zip(ohlc.high, ohlc.low, ohlc.close)
|
|
105
|
+
]
|
|
106
|
+
else:
|
|
107
|
+
bars = data
|
|
108
|
+
if source == "close":
|
|
109
|
+
return [b.close for b in bars]
|
|
110
|
+
if source == "open":
|
|
111
|
+
return [b.open for b in bars]
|
|
112
|
+
if source == "high":
|
|
113
|
+
return [b.high for b in bars]
|
|
114
|
+
if source == "low":
|
|
115
|
+
return [b.low for b in bars]
|
|
116
|
+
if source == "hl2":
|
|
117
|
+
return [(b.high + b.low) / 2 for b in bars]
|
|
118
|
+
if source == "hlc3":
|
|
119
|
+
return [(b.high + b.low + b.close) / 3 for b in bars]
|
|
120
|
+
if source == "ohlc4":
|
|
121
|
+
return [(b.open + b.high + b.low + b.close) / 4 for b in bars]
|
|
122
|
+
if source == "hlcc4":
|
|
123
|
+
return [(b.high + b.low + b.close + b.close) / 4 for b in bars]
|
|
124
|
+
return []
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def shift(series: series_float, offset: int) -> series_float:
|
|
128
|
+
if offset == 0:
|
|
129
|
+
return series
|
|
130
|
+
if offset > 0:
|
|
131
|
+
return [float("nan")] * offset + series[: -offset]
|
|
132
|
+
else:
|
|
133
|
+
return series[-offset:] + [float("nan")] * (-offset)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def format_output(
|
|
137
|
+
values: series_float, timestamps: list[float] | None = None
|
|
138
|
+
) -> list[dict]:
|
|
139
|
+
result = []
|
|
140
|
+
for i, value in enumerate(values):
|
|
141
|
+
result.append({
|
|
142
|
+
"time": timestamps[i] if timestamps else i,
|
|
143
|
+
"value": None if math.isnan(value) else value,
|
|
144
|
+
})
|
|
145
|
+
return result
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""Simple in-memory input adapter for demos and testing."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import math
|
|
6
|
+
from typing import Any, Callable
|
|
7
|
+
|
|
8
|
+
from ..runtime_types import InputConfig
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SimpleInputAdapter:
|
|
12
|
+
"""Stores input configurations and values in memory."""
|
|
13
|
+
|
|
14
|
+
def __init__(self) -> None:
|
|
15
|
+
self._inputs: dict[str, dict[str, Any]] = {}
|
|
16
|
+
self._change_callbacks: list[Callable[[str, Any], None]] = []
|
|
17
|
+
|
|
18
|
+
def register_input(self, config: InputConfig) -> Any:
|
|
19
|
+
if config.id not in self._inputs:
|
|
20
|
+
self._inputs[config.id] = {"config": config, "value": config.defval}
|
|
21
|
+
return self._inputs[config.id]["value"]
|
|
22
|
+
|
|
23
|
+
def get_value(self, id: str) -> Any:
|
|
24
|
+
entry = self._inputs.get(id)
|
|
25
|
+
return entry["value"] if entry is not None else None
|
|
26
|
+
|
|
27
|
+
def set_value(self, id: str, value: Any) -> None:
|
|
28
|
+
entry = self._inputs.get(id)
|
|
29
|
+
if entry is None:
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
config: InputConfig = entry["config"]
|
|
33
|
+
validated = value
|
|
34
|
+
|
|
35
|
+
if config.type in ("int", "float") and isinstance(value, (int, float)):
|
|
36
|
+
if config.min is not None and value < config.min:
|
|
37
|
+
validated = config.min
|
|
38
|
+
if config.max is not None and value > config.max:
|
|
39
|
+
validated = config.max
|
|
40
|
+
if config.type == "int":
|
|
41
|
+
validated = int(validated)
|
|
42
|
+
|
|
43
|
+
if config.type == "string" and config.options and isinstance(value, str):
|
|
44
|
+
if value not in config.options:
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
if config.type == "source" and config.options and isinstance(value, str):
|
|
48
|
+
if value not in config.options:
|
|
49
|
+
return
|
|
50
|
+
|
|
51
|
+
entry["value"] = validated
|
|
52
|
+
for cb in self._change_callbacks:
|
|
53
|
+
cb(id, validated)
|
|
54
|
+
|
|
55
|
+
def on_input_change(self, callback: Callable[[str, Any], None]) -> None:
|
|
56
|
+
self._change_callbacks.append(callback)
|
|
57
|
+
|
|
58
|
+
def get_all_inputs(self) -> dict[str, dict[str, Any]]:
|
|
59
|
+
return dict(self._inputs)
|
|
60
|
+
|
|
61
|
+
def clear(self) -> None:
|
|
62
|
+
self._inputs.clear()
|
|
63
|
+
self._change_callbacks.clear()
|