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/ta_series.py
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
"""Series-based TA wrappers — accepts/returns Series objects."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from . import ta as ta_core
|
|
6
|
+
from ._types import Bar
|
|
7
|
+
from .series import Series
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _bars_from(s: Series) -> list[Bar]:
|
|
11
|
+
return s.bars
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _wrap(bars: list[Bar] | Series, values: list[float]) -> Series:
|
|
15
|
+
return Series.from_array(bars if isinstance(bars, list) else bars.bar_data, values)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# --- Moving Averages ---
|
|
19
|
+
|
|
20
|
+
def sma(source: Series, length: int) -> Series:
|
|
21
|
+
return _wrap(source, ta_core.sma(source.to_array(), length))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def ema(source: Series, length: int) -> Series:
|
|
25
|
+
return _wrap(source, ta_core.ema(source.to_array(), length))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def wma(source: Series, length: int) -> Series:
|
|
29
|
+
return _wrap(source, ta_core.wma(source.to_array(), length))
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def rma(source: Series, length: int) -> Series:
|
|
33
|
+
return _wrap(source, ta_core.rma(source.to_array(), length))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def vwma(source: Series, length: int, volume: Series) -> Series:
|
|
37
|
+
return _wrap(source, ta_core.vwma(source.to_array(), length, volume.to_array()))
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def hma(source: Series, length: int) -> Series:
|
|
41
|
+
return _wrap(source, ta_core.hma(source.to_array(), length))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def alma(source: Series, length: int = 9, offset: float = 0.85, sigma: float = 6, floor: bool = False) -> Series:
|
|
45
|
+
return _wrap(source, ta_core.alma(source.to_array(), length, offset, sigma, floor))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def swma(source: Series) -> Series:
|
|
49
|
+
return _wrap(source, ta_core.swma(source.to_array()))
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def linreg(source: Series, length: int, offset: int = 0) -> Series:
|
|
53
|
+
return _wrap(source, ta_core.linreg(source.to_array(), length, offset))
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# --- Oscillators ---
|
|
57
|
+
|
|
58
|
+
def rsi(source: Series, length: int) -> Series:
|
|
59
|
+
return _wrap(source, ta_core.rsi(source.to_array(), length))
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def stoch(source: Series, high: Series, low: Series, length: int) -> Series:
|
|
63
|
+
return _wrap(source, ta_core.stoch(source.to_array(), high.to_array(), low.to_array(), length))
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def cci(source: Series, length: int) -> Series:
|
|
67
|
+
return _wrap(source, ta_core.cci(source.to_array(), length))
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def cmo(source: Series, length: int) -> Series:
|
|
71
|
+
return _wrap(source, ta_core.cmo(source.to_array(), length))
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def tsi(source: Series, short_length: int, long_length: int) -> Series:
|
|
75
|
+
return _wrap(source, ta_core.tsi(source.to_array(), short_length, long_length))
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def mfi(source: Series, length: int, volume: Series) -> Series:
|
|
79
|
+
return _wrap(source, ta_core.mfi(source.to_array(), length, volume.to_array()))
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def cog(source: Series, length: int = 10) -> Series:
|
|
83
|
+
return _wrap(source, ta_core.cog(source.to_array(), length))
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def rci(source: Series, length: int) -> Series:
|
|
87
|
+
return _wrap(source, ta_core.rci(source.to_array(), length))
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def percentrank(source: Series, length: int) -> Series:
|
|
91
|
+
return _wrap(source, ta_core.percentrank(source.to_array(), length))
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# --- Bands & Channels ---
|
|
95
|
+
|
|
96
|
+
def macd(source: Series, fast_length: int, slow_length: int, signal_length: int) -> tuple[Series, Series, Series]:
|
|
97
|
+
vals = ta_core.macd(source.to_array(), fast_length, slow_length, signal_length)
|
|
98
|
+
return _wrap(source, vals[0]), _wrap(source, vals[1]), _wrap(source, vals[2])
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def bb(source: Series, length: int, mult: float) -> tuple[Series, Series, Series]:
|
|
102
|
+
upper, basis, lower = ta_core.bb(source.to_array(), length, mult)
|
|
103
|
+
return _wrap(source, upper), _wrap(source, basis), _wrap(source, lower)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def bbw(source: Series, length: int, mult: float) -> Series:
|
|
107
|
+
return _wrap(source, ta_core.bbw(source.to_array(), length, mult))
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def kc(bars: list[Bar], source: Series, length: int, mult: float, use_true_range: bool = True) -> tuple[Series, Series, Series]:
|
|
111
|
+
high = [b.high for b in bars]
|
|
112
|
+
low = [b.low for b in bars]
|
|
113
|
+
close = [b.close for b in bars]
|
|
114
|
+
middle, upper, lower = ta_core.kc(source.to_array(), length, mult, use_true_range, high, low, close)
|
|
115
|
+
return _wrap(bars, middle), _wrap(bars, upper), _wrap(bars, lower)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def kcw(bars: list[Bar], source: Series, length: int = 20, mult: float = 2, use_true_range: bool = True) -> Series:
|
|
119
|
+
high = [b.high for b in bars]
|
|
120
|
+
low = [b.low for b in bars]
|
|
121
|
+
close = [b.close for b in bars]
|
|
122
|
+
return _wrap(bars, ta_core.kcw(source.to_array(), length, mult, use_true_range, high, low, close))
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
# --- Volatility ---
|
|
126
|
+
|
|
127
|
+
def stdev(source: Series, length: int) -> Series:
|
|
128
|
+
return _wrap(source, ta_core.stdev(source.to_array(), length))
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def variance(source: Series, length: int, biased: bool = True) -> Series:
|
|
132
|
+
return _wrap(source, ta_core.variance(source.to_array(), length, biased))
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def dev(source: Series, length: int) -> Series:
|
|
136
|
+
return _wrap(source, ta_core.dev(source.to_array(), length))
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def atr(bars: list[Bar], length: int) -> Series:
|
|
140
|
+
high = [b.high for b in bars]
|
|
141
|
+
low = [b.low for b in bars]
|
|
142
|
+
close = [b.close for b in bars]
|
|
143
|
+
return _wrap(bars, ta_core.atr(length, high, low, close))
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def tr(bars: list[Bar], handle_na: bool = False) -> Series:
|
|
147
|
+
high = [b.high for b in bars]
|
|
148
|
+
low = [b.low for b in bars]
|
|
149
|
+
close = [b.close for b in bars]
|
|
150
|
+
return _wrap(bars, ta_core.tr(handle_na, high, low, close))
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
# --- Trend ---
|
|
154
|
+
|
|
155
|
+
def supertrend(bars: list[Bar], factor: float, atr_length: int) -> tuple[Series, Series]:
|
|
156
|
+
high = [b.high for b in bars]
|
|
157
|
+
low = [b.low for b in bars]
|
|
158
|
+
close = [b.close for b in bars]
|
|
159
|
+
trend_vals, dir_vals = ta_core.supertrend(factor, atr_length, high, low, close)
|
|
160
|
+
return _wrap(bars, trend_vals), _wrap(bars, dir_vals)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def vwap(source: Series, volume: Series) -> Series:
|
|
164
|
+
return _wrap(source, ta_core.vwap(source.to_array(), volume.to_array()))
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def sar(bars: list[Bar], start: float = 0.02, inc: float = 0.02, max_val: float = 0.2) -> Series:
|
|
168
|
+
high = [b.high for b in bars]
|
|
169
|
+
low = [b.low for b in bars]
|
|
170
|
+
close = [b.close for b in bars]
|
|
171
|
+
return _wrap(bars, ta_core.sar(start, inc, max_val, high, low, close))
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def ichimoku(
|
|
175
|
+
bars: list[Bar], conversion_periods: int, base_periods: int,
|
|
176
|
+
lagging_span2_periods: int, displacement: int,
|
|
177
|
+
) -> tuple[Series, Series, Series, Series, Series]:
|
|
178
|
+
high = [b.high for b in bars]
|
|
179
|
+
low = [b.low for b in bars]
|
|
180
|
+
close = [b.close for b in bars]
|
|
181
|
+
tenkan, kijun, senkou_a, senkou_b, chikou = ta_core.ichimoku(
|
|
182
|
+
conversion_periods, base_periods, lagging_span2_periods, displacement, high, low, close,
|
|
183
|
+
)
|
|
184
|
+
return (
|
|
185
|
+
_wrap(bars, tenkan), _wrap(bars, kijun),
|
|
186
|
+
_wrap(bars, senkou_a), _wrap(bars, senkou_b), _wrap(bars, chikou),
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def dmi(bars: list[Bar], di_length: int, adx_smoothing: int) -> tuple[Series, Series, Series]:
|
|
191
|
+
high = [b.high for b in bars]
|
|
192
|
+
low = [b.low for b in bars]
|
|
193
|
+
close = [b.close for b in bars]
|
|
194
|
+
plus_di, minus_di, adx = ta_core.dmi(di_length, adx_smoothing, high, low, close)
|
|
195
|
+
return _wrap(bars, plus_di), _wrap(bars, minus_di), _wrap(bars, adx)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
# --- Cross Detection ---
|
|
199
|
+
|
|
200
|
+
def crossover(source1: Series, source2: Series | float | int) -> Series:
|
|
201
|
+
vals1 = source1.to_array()
|
|
202
|
+
if isinstance(source2, Series):
|
|
203
|
+
vals2 = source2.to_array()
|
|
204
|
+
else:
|
|
205
|
+
vals2 = [float(source2)] * len(vals1)
|
|
206
|
+
result = ta_core.crossover(vals1, vals2)
|
|
207
|
+
return _wrap(source1, [1.0 if b else 0.0 for b in result])
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def crossunder(source1: Series, source2: Series | float | int) -> Series:
|
|
211
|
+
vals1 = source1.to_array()
|
|
212
|
+
if isinstance(source2, Series):
|
|
213
|
+
vals2 = source2.to_array()
|
|
214
|
+
else:
|
|
215
|
+
vals2 = [float(source2)] * len(vals1)
|
|
216
|
+
result = ta_core.crossunder(vals1, vals2)
|
|
217
|
+
return _wrap(source1, [1.0 if b else 0.0 for b in result])
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def cross(source1: Series, source2: Series | float | int) -> Series:
|
|
221
|
+
vals1 = source1.to_array()
|
|
222
|
+
if isinstance(source2, Series):
|
|
223
|
+
vals2 = source2.to_array()
|
|
224
|
+
else:
|
|
225
|
+
vals2 = [float(source2)] * len(vals1)
|
|
226
|
+
result = ta_core.cross(vals1, vals2)
|
|
227
|
+
return _wrap(source1, [1.0 if b else 0.0 for b in result])
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
# --- Momentum ---
|
|
231
|
+
|
|
232
|
+
def change(source: Series, length: int = 1) -> Series:
|
|
233
|
+
return _wrap(source, ta_core.change(source.to_array(), length))
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def mom(source: Series, length: int) -> Series:
|
|
237
|
+
return _wrap(source, ta_core.mom(source.to_array(), length))
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def roc(source: Series, length: int) -> Series:
|
|
241
|
+
return _wrap(source, ta_core.roc(source.to_array(), length))
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
# --- Extremes ---
|
|
245
|
+
|
|
246
|
+
def highest(source: Series, length: int) -> Series:
|
|
247
|
+
return _wrap(source, ta_core.highest(source.to_array(), length))
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def lowest(source: Series, length: int) -> Series:
|
|
251
|
+
return _wrap(source, ta_core.lowest(source.to_array(), length))
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def highestbars(source: Series, length: int) -> Series:
|
|
255
|
+
return _wrap(source, ta_core.highestbars(source.to_array(), length))
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def lowestbars(source: Series, length: int) -> Series:
|
|
259
|
+
return _wrap(source, ta_core.lowestbars(source.to_array(), length))
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
# --- Detection ---
|
|
263
|
+
|
|
264
|
+
def rising(source: Series, length: int) -> Series:
|
|
265
|
+
result = ta_core.rising(source.to_array(), length)
|
|
266
|
+
return _wrap(source, [1.0 if b else 0.0 for b in result])
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def falling(source: Series, length: int) -> Series:
|
|
270
|
+
result = ta_core.falling(source.to_array(), length)
|
|
271
|
+
return _wrap(source, [1.0 if b else 0.0 for b in result])
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def pivothigh(source: Series, leftbars: int, rightbars: int) -> Series:
|
|
275
|
+
return _wrap(source, ta_core.pivothigh(source.to_array(), leftbars, rightbars))
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def pivotlow(source: Series, leftbars: int, rightbars: int) -> Series:
|
|
279
|
+
return _wrap(source, ta_core.pivotlow(source.to_array(), leftbars, rightbars))
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
# --- Cumulative & Stats ---
|
|
283
|
+
|
|
284
|
+
def cum(source: Series) -> Series:
|
|
285
|
+
return _wrap(source, ta_core.cum(source.to_array()))
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def correlation(source1: Series, source2: Series, length: int) -> Series:
|
|
289
|
+
return _wrap(source1, ta_core.correlation(source1.to_array(), source2.to_array(), length))
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def median(source: Series, length: int) -> Series:
|
|
293
|
+
return _wrap(source, ta_core.median(source.to_array(), length))
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def mode(source: Series, length: int) -> Series:
|
|
297
|
+
return _wrap(source, ta_core.mode(source.to_array(), length))
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def percentile_linear_interpolation(source: Series, length: int, percentage: float) -> Series:
|
|
301
|
+
return _wrap(source, ta_core.percentile_linear_interpolation(source.to_array(), length, percentage))
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def percentile_nearest_rank(source: Series, length: int, percentage: float) -> Series:
|
|
305
|
+
return _wrap(source, ta_core.percentile_nearest_rank(source.to_array(), length, percentage))
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
# --- Bar-based ---
|
|
309
|
+
|
|
310
|
+
def barssince(condition: Series) -> Series:
|
|
311
|
+
bool_values = [v == 1 for v in condition.to_array()]
|
|
312
|
+
return _wrap(condition, ta_core.barssince(bool_values))
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def valuewhen(condition: Series, source: Series, occurrence: int) -> Series:
|
|
316
|
+
bool_values = [v == 1 for v in condition.to_array()]
|
|
317
|
+
return _wrap(condition, ta_core.valuewhen(bool_values, source.to_array(), occurrence))
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
# --- Element-wise ---
|
|
321
|
+
|
|
322
|
+
def max(source1: Series, source2: Series) -> Series:
|
|
323
|
+
return _wrap(source1, ta_core.max_(source1.to_array(), source2.to_array()))
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def min(source1: Series, source2: Series) -> Series:
|
|
327
|
+
return _wrap(source1, ta_core.min_(source1.to_array(), source2.to_array()))
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def range(high: Series, low: Series) -> Series:
|
|
331
|
+
return _wrap(high, ta_core.range_(high.to_array(), low.to_array()))
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
# --- Complex indicators ---
|
|
335
|
+
|
|
336
|
+
def zigzag(
|
|
337
|
+
bars: list[Bar], deviation: float = 5, depth: int = 10, backstep: int = 3,
|
|
338
|
+
) -> tuple[Series, Series, Series]:
|
|
339
|
+
high = [b.high for b in bars]
|
|
340
|
+
low = [b.low for b in bars]
|
|
341
|
+
zigzag_vals, dir_vals, pivot_vals = ta_core.zigzag(deviation, depth, backstep, None, high, low)
|
|
342
|
+
return (
|
|
343
|
+
_wrap(bars, zigzag_vals),
|
|
344
|
+
_wrap(bars, dir_vals),
|
|
345
|
+
_wrap(bars, [1.0 if b else 0.0 for b in pivot_vals]),
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def wpr(bars: list[Bar], length: int = 14) -> Series:
|
|
350
|
+
high = [b.high for b in bars]
|
|
351
|
+
low = [b.low for b in bars]
|
|
352
|
+
close = [b.close for b in bars]
|
|
353
|
+
return _wrap(bars, ta_core.wpr(high, low, close, length))
|
oakscriptpy/time_.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Time namespace — mirrors PineScript time functions."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import time as _time
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def now() -> int:
|
|
10
|
+
return int(_time.time() * 1000)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def timestamp(
|
|
14
|
+
year: int,
|
|
15
|
+
month: int,
|
|
16
|
+
day: int,
|
|
17
|
+
hour: int = 0,
|
|
18
|
+
minute: int = 0,
|
|
19
|
+
second: int = 0,
|
|
20
|
+
) -> int:
|
|
21
|
+
dt = datetime(year, month, day, hour, minute, second)
|
|
22
|
+
return int(dt.timestamp() * 1000)
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: oakscriptpy
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: PineScript-like API for technical analysis in Python
|
|
5
|
+
Project-URL: Homepage, https://github.com/deepentropy/oakscriptPy
|
|
6
|
+
Project-URL: Repository, https://github.com/deepentropy/oakscriptPy
|
|
7
|
+
Project-URL: Issues, https://github.com/deepentropy/oakscriptPy/issues
|
|
8
|
+
Author: deepentropy
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
Keywords: finance,pinescript,ta,technical-analysis,trading
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
<p align="center">
|
|
23
|
+
<img src="./logo.png" width="150" />
|
|
24
|
+
</p>
|
|
25
|
+
|
|
26
|
+
<h1 align="center">OakScriptPy</h1>
|
|
27
|
+
|
|
28
|
+
<p align="center">
|
|
29
|
+
PineScript-like API for technical analysis in Python. Pure Python, zero dependencies.
|
|
30
|
+
</p>
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Install
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install oakscriptpy
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Quick Start
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from oakscriptpy import ta, math, color, Series
|
|
44
|
+
from oakscriptpy._types import Bar
|
|
45
|
+
|
|
46
|
+
# Create bars
|
|
47
|
+
bars = [
|
|
48
|
+
Bar(time=1, open=10.0, high=12.0, low=9.0, close=11.0, volume=100.0),
|
|
49
|
+
Bar(time=2, open=11.0, high=13.0, low=10.0, close=12.0, volume=110.0),
|
|
50
|
+
Bar(time=3, open=12.0, high=14.0, low=11.0, close=13.0, volume=120.0),
|
|
51
|
+
# ...
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
# Array-based TA
|
|
55
|
+
closes = [b.close for b in bars]
|
|
56
|
+
sma_values = ta.sma(closes, 3)
|
|
57
|
+
ema_values = ta.ema(closes, 3)
|
|
58
|
+
rsi_values = ta.rsi(closes, 14)
|
|
59
|
+
|
|
60
|
+
# Series-based TA (with operator overloading)
|
|
61
|
+
from oakscriptpy import ta as ta_series
|
|
62
|
+
close_series = Series.from_bars(bars, lambda b: b.close)
|
|
63
|
+
sma_series = ta_series.sma(close_series, 3)
|
|
64
|
+
ema_series = ta_series.ema(close_series, 3)
|
|
65
|
+
|
|
66
|
+
# Series arithmetic
|
|
67
|
+
spread = close_series - sma_series
|
|
68
|
+
doubled = close_series * 2
|
|
69
|
+
above_sma = close_series > sma_series
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Namespaces
|
|
73
|
+
|
|
74
|
+
| Namespace | Functions | Description |
|
|
75
|
+
|-----------|-----------|-------------|
|
|
76
|
+
| `ta` | 64 | Technical analysis (SMA, EMA, RSI, MACD, Bollinger Bands, Ichimoku, SuperTrend, ZigZag, etc.) |
|
|
77
|
+
| `math` | 26 | Mathematical functions (abs, ceil, floor, sqrt, pow, trig, etc.) |
|
|
78
|
+
| `array` | 42+ | Array manipulation (push, pop, sort, slice, binary_search, percentile, etc.) |
|
|
79
|
+
| `color` | 14 + 17 constants | Color creation and manipulation (rgb, from_hex, new_color, named constants) |
|
|
80
|
+
| `str` | 20+ | String functions (split, replace, substring, contains, match, etc.) |
|
|
81
|
+
| `time` | 2 | Time utilities (format_time) |
|
|
82
|
+
| `matrix` | 43 | Matrix operations (add, mult, det, inv, eigenvalues, eigenvectors, etc.) |
|
|
83
|
+
| `line` | 10 | Line drawing objects |
|
|
84
|
+
| `box` | 12 | Box drawing objects |
|
|
85
|
+
| `label` | 10 | Label drawing objects |
|
|
86
|
+
| `linefill` | 4 | Linefill functions |
|
|
87
|
+
| `chart_point` | 3 | Chart point functions |
|
|
88
|
+
| `polyline` | 5 | Polyline functions |
|
|
89
|
+
|
|
90
|
+
## Series
|
|
91
|
+
|
|
92
|
+
The `Series` class provides lazy evaluation with Python operator overloading:
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
a = Series.from_bars(bars, lambda b: b.close)
|
|
96
|
+
b = Series.from_bars(bars, lambda b: b.open)
|
|
97
|
+
|
|
98
|
+
# Arithmetic: +, -, *, /, %
|
|
99
|
+
spread = a - b
|
|
100
|
+
|
|
101
|
+
# Comparison: >, >=, <, <=, eq(), neq()
|
|
102
|
+
bullish = a > b
|
|
103
|
+
|
|
104
|
+
# Logical: and_(), or_(), not_()
|
|
105
|
+
signal = bullish.and_(a > sma_series)
|
|
106
|
+
|
|
107
|
+
# History access
|
|
108
|
+
prev_close = a.offset(1)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Tests
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
uv run pytest
|
|
115
|
+
# 1,224 passed, 56 skipped
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## License
|
|
119
|
+
|
|
120
|
+
MIT
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
oakscriptpy/__init__.py,sha256=i3T6t_SlkPpMLoIQlVsZi-bXWQdT9m8ivXNlLTWbVXo,2737
|
|
2
|
+
oakscriptpy/_metadata.py,sha256=wwVjzgCoQ5usGjuMscaw8FoamHAm4yPnreuocum3GYE,2666
|
|
3
|
+
oakscriptpy/_types.py,sha256=5xaSbo8OP639DLLDVOLyZY5o2fzFl5GtXV3lN8mjJu8,3763
|
|
4
|
+
oakscriptpy/_utils.py,sha256=U_9d9etcUsW-cB9rLErrnuikY4XI0Rnh4Xazd6ThuAs,4001
|
|
5
|
+
oakscriptpy/array.py,sha256=bgLCgGOs2-EQJ1ujhdEFoejS0PjCFiNGBS8XInAP9n8,8275
|
|
6
|
+
oakscriptpy/box.py,sha256=fRGw_IkV1pqQsPPQBeK0yyWN3XArUbfR-yfRmKiSUpE,3197
|
|
7
|
+
oakscriptpy/chartpoint.py,sha256=PM370m4NlKAC6VnEy5aQ06DJww_icGYg8a8GGGavHlk,635
|
|
8
|
+
oakscriptpy/color.py,sha256=DUsDlJVrKYlNhN3Bs-RLkGwWWwEWmpWgtoXt-IdzMDQ,3425
|
|
9
|
+
oakscriptpy/indicator.py,sha256=flmj9Ps3ruM0cAf_wFECyKbgHfewPafhnt5us3gKKLo,2209
|
|
10
|
+
oakscriptpy/input_.py,sha256=B0Ou9-2Jae7N6Wd-p5oI-lKN0yE8RDFJ-YMgvQN1oFk,1672
|
|
11
|
+
oakscriptpy/inputs.py,sha256=O_Tr08-PMEaGwVAA28boqlswA8DSzpmww9B9KdjDFC8,5347
|
|
12
|
+
oakscriptpy/label.py,sha256=pzQcK9EyZrtkIXYC0gEAtjsT9sU8KMSi1azWZT7j9nk,2153
|
|
13
|
+
oakscriptpy/line.py,sha256=DLIbqr5WjLL-W-48cHZQOA5teO76K1M3eb8m0RwkxGg,2349
|
|
14
|
+
oakscriptpy/linefill.py,sha256=IQ-P5bPOnR1Sm325t4KaIE9oQIuZjM8TUE5B0MrzpiA,541
|
|
15
|
+
oakscriptpy/math_.py,sha256=bS2223jYIrbuHtI_VRf38BCf_hAJ_NB73S2AujLS36U,3878
|
|
16
|
+
oakscriptpy/matrix.py,sha256=yUifrNj-6t7Or4m1weJB7FXUVgxs8TGjFTUwH3eIboU,32448
|
|
17
|
+
oakscriptpy/plot_.py,sha256=JlYcFNFfeDfepWqVaZfSy8KdOnXUl09alENz4oMovHY,1151
|
|
18
|
+
oakscriptpy/polyline.py,sha256=luIc0D9puQECA49pd1MZP4N32D5in-2We1vK5LmgXso,1499
|
|
19
|
+
oakscriptpy/runtime.py,sha256=nSiBwwNbuH7mE3YWiCf7TN2Z78cquFIPDQuHWZlJJUw,3924
|
|
20
|
+
oakscriptpy/runtime_types.py,sha256=hhaZFrm5Z0z2fy1aNfe6Qx2svnW-Ur7lIISfbwbfrWQ,1346
|
|
21
|
+
oakscriptpy/series.py,sha256=x7E2LMOT9XMIkkhtJRNtdOzQD6D5oGzirDdOHgBy3Z8,10563
|
|
22
|
+
oakscriptpy/str_.py,sha256=v343ieUv9FE6FIHMA2VKHCB-BxSl-duVzlif0S6SZgU,3920
|
|
23
|
+
oakscriptpy/ta.py,sha256=Voh1wADo4eqWiukNv8eMV9nASYRCb9NrEg_9phBKqtY,55351
|
|
24
|
+
oakscriptpy/ta_series.py,sha256=26nkB-J4y3hIkLI8eH65UlgUtKoisChHPW0y6y9aCMw,11858
|
|
25
|
+
oakscriptpy/time_.py,sha256=OKONmAOZ_ciH_gcdJMAnsRuy5ww4N0ttmyWaVNDH7T8,433
|
|
26
|
+
oakscriptpy/adapters/__init__.py,sha256=dTCswDoAPkYrBxg8EpehPkq3LxgojjMcEfcY-pPVPnY,111
|
|
27
|
+
oakscriptpy/adapters/simple_input.py,sha256=Y4EU39Qn198nHyfuv90FViGMvVLKx6ASy2rdrvH-8aI,2096
|
|
28
|
+
oakscriptpy/lib/__init__.py,sha256=tMl14ueGpVPkMchWhLfN68QmZ4CNwjaP5K-C-MNh3nE,105
|
|
29
|
+
oakscriptpy/lib/zigzag.py,sha256=pBcQRJGYhqBnop67BPSsFyhMd_F4rSiNXW_B-MgFqSw,5298
|
|
30
|
+
oakscriptpy-0.1.0.dist-info/METADATA,sha256=Ec88p-eOKDvutQIP8mtii8XVopUnD1gB2U8PZ_ui75k,3508
|
|
31
|
+
oakscriptpy-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
32
|
+
oakscriptpy-0.1.0.dist-info/RECORD,,
|