oakscriptpy 0.1.0__tar.gz
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-0.1.0/.github/workflows/publish.yml +26 -0
- oakscriptpy-0.1.0/.gitignore +7 -0
- oakscriptpy-0.1.0/DEVLOG.md +81 -0
- oakscriptpy-0.1.0/PKG-INFO +120 -0
- oakscriptpy-0.1.0/README.md +99 -0
- oakscriptpy-0.1.0/logo.png +0 -0
- oakscriptpy-0.1.0/pyproject.toml +37 -0
- oakscriptpy-0.1.0/src/oakscriptpy/__init__.py +93 -0
- oakscriptpy-0.1.0/src/oakscriptpy/_metadata.py +118 -0
- oakscriptpy-0.1.0/src/oakscriptpy/_types.py +185 -0
- oakscriptpy-0.1.0/src/oakscriptpy/_utils.py +145 -0
- oakscriptpy-0.1.0/src/oakscriptpy/adapters/__init__.py +5 -0
- oakscriptpy-0.1.0/src/oakscriptpy/adapters/simple_input.py +63 -0
- oakscriptpy-0.1.0/src/oakscriptpy/array.py +342 -0
- oakscriptpy-0.1.0/src/oakscriptpy/box.py +151 -0
- oakscriptpy-0.1.0/src/oakscriptpy/chartpoint.py +21 -0
- oakscriptpy-0.1.0/src/oakscriptpy/color.py +134 -0
- oakscriptpy-0.1.0/src/oakscriptpy/indicator.py +82 -0
- oakscriptpy-0.1.0/src/oakscriptpy/input_.py +38 -0
- oakscriptpy-0.1.0/src/oakscriptpy/inputs.py +170 -0
- oakscriptpy-0.1.0/src/oakscriptpy/label.py +110 -0
- oakscriptpy-0.1.0/src/oakscriptpy/lib/__init__.py +5 -0
- oakscriptpy-0.1.0/src/oakscriptpy/lib/zigzag.py +158 -0
- oakscriptpy-0.1.0/src/oakscriptpy/line.py +120 -0
- oakscriptpy-0.1.0/src/oakscriptpy/linefill.py +26 -0
- oakscriptpy-0.1.0/src/oakscriptpy/math_.py +184 -0
- oakscriptpy-0.1.0/src/oakscriptpy/matrix.py +1136 -0
- oakscriptpy-0.1.0/src/oakscriptpy/plot_.py +49 -0
- oakscriptpy-0.1.0/src/oakscriptpy/polyline.py +60 -0
- oakscriptpy-0.1.0/src/oakscriptpy/runtime.py +150 -0
- oakscriptpy-0.1.0/src/oakscriptpy/runtime_types.py +52 -0
- oakscriptpy-0.1.0/src/oakscriptpy/series.py +292 -0
- oakscriptpy-0.1.0/src/oakscriptpy/str_.py +166 -0
- oakscriptpy-0.1.0/src/oakscriptpy/ta.py +1795 -0
- oakscriptpy-0.1.0/src/oakscriptpy/ta_series.py +353 -0
- oakscriptpy-0.1.0/src/oakscriptpy/time_.py +22 -0
- oakscriptpy-0.1.0/tests/__init__.py +0 -0
- oakscriptpy-0.1.0/tests/array/__init__.py +0 -0
- oakscriptpy-0.1.0/tests/array/test_drawing_objects.py +452 -0
- oakscriptpy-0.1.0/tests/array/test_final_functions.py +277 -0
- oakscriptpy-0.1.0/tests/array/test_new_functions.py +257 -0
- oakscriptpy-0.1.0/tests/array/test_percentile.py +260 -0
- oakscriptpy-0.1.0/tests/array/test_search_and_stats.py +303 -0
- oakscriptpy-0.1.0/tests/box/__init__.py +0 -0
- oakscriptpy-0.1.0/tests/box/test_box.py +292 -0
- oakscriptpy-0.1.0/tests/chartpoint/__init__.py +0 -0
- oakscriptpy-0.1.0/tests/chartpoint/test_chartpoint.py +156 -0
- oakscriptpy-0.1.0/tests/color/__init__.py +0 -0
- oakscriptpy-0.1.0/tests/color/test_components.py +172 -0
- oakscriptpy-0.1.0/tests/color/test_constants.py +125 -0
- oakscriptpy-0.1.0/tests/color/test_creation.py +110 -0
- oakscriptpy-0.1.0/tests/color/test_manipulation.py +75 -0
- oakscriptpy-0.1.0/tests/conftest.py +31 -0
- oakscriptpy-0.1.0/tests/indicator/__init__.py +0 -0
- oakscriptpy-0.1.0/tests/indicator/test_indicator.py +387 -0
- oakscriptpy-0.1.0/tests/label/__init__.py +0 -0
- oakscriptpy-0.1.0/tests/label/test_label.py +311 -0
- oakscriptpy-0.1.0/tests/line/__init__.py +0 -0
- oakscriptpy-0.1.0/tests/line/test_line.py +251 -0
- oakscriptpy-0.1.0/tests/linefill/__init__.py +0 -0
- oakscriptpy-0.1.0/tests/linefill/test_linefill.py +257 -0
- oakscriptpy-0.1.0/tests/math/__init__.py +0 -0
- oakscriptpy-0.1.0/tests/math/test_algebraic.py +166 -0
- oakscriptpy-0.1.0/tests/math/test_basic.py +224 -0
- oakscriptpy-0.1.0/tests/math/test_series_support.py +200 -0
- oakscriptpy-0.1.0/tests/math/test_sum_series.py +21 -0
- oakscriptpy-0.1.0/tests/math/test_trigonometric.py +188 -0
- oakscriptpy-0.1.0/tests/math/test_utility.py +150 -0
- oakscriptpy-0.1.0/tests/matrix/__init__.py +0 -0
- oakscriptpy-0.1.0/tests/matrix/test_foundational.py +525 -0
- oakscriptpy-0.1.0/tests/matrix/test_linear_algebra.py +837 -0
- oakscriptpy-0.1.0/tests/matrix/test_manipulation.py +999 -0
- oakscriptpy-0.1.0/tests/polyline/__init__.py +0 -0
- oakscriptpy-0.1.0/tests/polyline/test_polyline.py +318 -0
- oakscriptpy-0.1.0/tests/runtime/__init__.py +0 -0
- oakscriptpy-0.1.0/tests/runtime/test_adapters.py +182 -0
- oakscriptpy-0.1.0/tests/runtime/test_inputs.py +352 -0
- oakscriptpy-0.1.0/tests/runtime/test_runtime.py +310 -0
- oakscriptpy-0.1.0/tests/runtime/test_series.py +475 -0
- oakscriptpy-0.1.0/tests/str/__init__.py +0 -0
- oakscriptpy-0.1.0/tests/str/test_conversion.py +103 -0
- oakscriptpy-0.1.0/tests/str/test_formatting.py +108 -0
- oakscriptpy-0.1.0/tests/str/test_manipulation.py +92 -0
- oakscriptpy-0.1.0/tests/str/test_predicates.py +121 -0
- oakscriptpy-0.1.0/tests/str/test_search.py +108 -0
- oakscriptpy-0.1.0/tests/str/test_whitespace.py +157 -0
- oakscriptpy-0.1.0/tests/ta/__init__.py +0 -0
- oakscriptpy-0.1.0/tests/ta/test_ichimoku.py +204 -0
- oakscriptpy-0.1.0/tests/ta/test_new_functions.py +223 -0
- oakscriptpy-0.1.0/tests/ta/test_new_functions2.py +225 -0
- oakscriptpy-0.1.0/tests/ta/test_new_functions3.py +226 -0
- oakscriptpy-0.1.0/tests/ta/test_new_ta_functions.py +185 -0
- oakscriptpy-0.1.0/tests/ta/test_rsi.py +147 -0
- oakscriptpy-0.1.0/tests/ta/test_sma.py +37 -0
- oakscriptpy-0.1.0/tests/ta/test_supertrend.py +147 -0
- oakscriptpy-0.1.0/tests/ta/test_ta_series_integration.py +79 -0
- oakscriptpy-0.1.0/tests/ta/test_zigzag.py +281 -0
- oakscriptpy-0.1.0/tests/test_new_functions.py +107 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- uses: actions/checkout@v4
|
|
12
|
+
- uses: astral-sh/setup-uv@v5
|
|
13
|
+
- run: uv sync --dev
|
|
14
|
+
- run: uv run pytest
|
|
15
|
+
|
|
16
|
+
publish:
|
|
17
|
+
needs: test
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
environment: pypi
|
|
20
|
+
permissions:
|
|
21
|
+
id-token: write
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
- uses: astral-sh/setup-uv@v5
|
|
25
|
+
- run: uv build
|
|
26
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# DEVLOG
|
|
2
|
+
|
|
3
|
+
## 2026-02-26 - Port oakscriptJS to oakscriptPy
|
|
4
|
+
|
|
5
|
+
### Goal
|
|
6
|
+
Port the complete oakscriptJS TypeScript library (PineScript-like API) to Python, covering all 217+ functions across 13 namespaces.
|
|
7
|
+
|
|
8
|
+
### Approach Taken
|
|
9
|
+
7-phase implementation following dependency order:
|
|
10
|
+
1. Foundation types & utils
|
|
11
|
+
2. Core computation (ta, math, array, color, str, time, matrix)
|
|
12
|
+
3. Drawing objects (line, box, label, linefill, chartpoint, polyline)
|
|
13
|
+
4. Series + TA-Series (lazy evaluation, operator overloading)
|
|
14
|
+
5. Runtime system (context, plot, hline, inputs, adapters)
|
|
15
|
+
6. Infrastructure (indicator factory, input helpers, plot helpers, zigzag library)
|
|
16
|
+
7. Package integration (__init__.py, re-exports, verification)
|
|
17
|
+
|
|
18
|
+
### What Worked
|
|
19
|
+
- Parallel background agents for ta.py (1795 lines) and matrix.py (1136 lines) while writing smaller modules directly
|
|
20
|
+
- Python operator overloading for Series (`__add__`, `__radd__`, `__gt__`, etc.) maps naturally to the JS `.add()`, `.sub()`, `.gt()` methods
|
|
21
|
+
- `typing.Protocol` for adapter interfaces mirrors TS interfaces cleanly
|
|
22
|
+
- Trailing underscore naming (`math_.py`, `str_.py`, `time_.py`) avoids stdlib clashes, re-exported as `math`, `str`, `time` in `__init__.py`
|
|
23
|
+
- `range_`, `max_`, `min_` in ta.py avoid Python builtin clashes
|
|
24
|
+
|
|
25
|
+
### Current State
|
|
26
|
+
- **Done**: All 7 phases complete, 6224 lines across 28 Python files
|
|
27
|
+
- **Verified**: Package imports, SMA/EMA calculations, Series operators, matrix operations, line interpolation, string functions, chart points, time functions, is_na/nz
|
|
28
|
+
- **Not yet done**: Series dual-mode support in math_.py (scalar functions work, Series support deferred)
|
|
29
|
+
|
|
30
|
+
### Key Decisions
|
|
31
|
+
- `PineArray` = plain `list[T]` (no wrapper class)
|
|
32
|
+
- `PineMatrix` = `@dataclass` with `rows`, `columns`, `data: list[list[Any]]`
|
|
33
|
+
- NaN = `float('nan')`, NA = `None`, `is_na()` checks both
|
|
34
|
+
- Series logical ops as methods (`and_()`, `or_()`, `not_()`) since Python reserves keywords
|
|
35
|
+
- No external dependencies (pure Python)
|
|
36
|
+
- Python 3.11+ required (union type syntax)
|
|
37
|
+
|
|
38
|
+
## 2026-02-26 - Port Jest tests to pytest (math, color, str, new_functions)
|
|
39
|
+
|
|
40
|
+
### Goal
|
|
41
|
+
Port all Jest test files from oakscriptJS to Python pytest equivalents for math, color, str namespaces and new_functions.
|
|
42
|
+
|
|
43
|
+
### Approach Taken
|
|
44
|
+
1-to-1 file mapping from JS test files to Python test files. Adapted for Python-specific behavior differences (ValueError vs NaN for math domain errors, float formatting, Python slicing semantics, Unicode code points vs UTF-16 surrogate pairs).
|
|
45
|
+
|
|
46
|
+
### What Worked
|
|
47
|
+
- Direct porting of all scalar math, trig, utility, color, and string tests
|
|
48
|
+
- Series-based math tests marked with `pytest.mark.skip` (56 tests)
|
|
49
|
+
- Adapted JS-specific edge cases to Python equivalents (e.g., `math.sqrt(-1)` raises ValueError in Python vs returning NaN in JS)
|
|
50
|
+
|
|
51
|
+
### Files Created
|
|
52
|
+
- `tests/math/test_basic.py` (abs, ceil, floor, round, max, min, avg, sum)
|
|
53
|
+
- `tests/math/test_algebraic.py` (sqrt, pow, exp, log, log10)
|
|
54
|
+
- `tests/math/test_trigonometric.py` (sin, cos, tan, asin, acos, atan)
|
|
55
|
+
- `tests/math/test_utility.py` (toradians, todegrees, random, sign)
|
|
56
|
+
- `tests/math/test_series_support.py` (skipped - Series not implemented)
|
|
57
|
+
- `tests/math/test_sum_series.py` (skipped - Series not implemented)
|
|
58
|
+
- `tests/color/test_creation.py` (rgb, from_hex)
|
|
59
|
+
- `tests/color/test_components.py` (r, g, b, t)
|
|
60
|
+
- `tests/color/test_constants.py` (all named color constants)
|
|
61
|
+
- `tests/color/test_manipulation.py` (new_color)
|
|
62
|
+
- `tests/str/test_conversion.py` (length, tostring, tonumber)
|
|
63
|
+
- `tests/str/test_formatting.py` (split, concat, format)
|
|
64
|
+
- `tests/str/test_manipulation.py` (substring, upper, lower)
|
|
65
|
+
- `tests/str/test_predicates.py` (startswith, endswith, char_at)
|
|
66
|
+
- `tests/str/test_search.py` (contains, pos, replace)
|
|
67
|
+
- `tests/str/test_whitespace.py` (trim, trim_left, trim_right, match)
|
|
68
|
+
- `tests/test_new_functions.py` (color.new alias, replace_all, format_time)
|
|
69
|
+
- `__init__.py` in tests/, tests/math/, tests/color/, tests/str/
|
|
70
|
+
|
|
71
|
+
### Current State
|
|
72
|
+
- **339 passed, 56 skipped** (all skipped are Series-based math tests)
|
|
73
|
+
- No failures
|
|
74
|
+
|
|
75
|
+
### Key Decisions
|
|
76
|
+
- Python `math.sqrt(-1)` / `math.log(0)` raise ValueError (tests use `pytest.raises`), unlike JS which returns NaN/-Infinity
|
|
77
|
+
- Python `round(0.005, 2)` = 0.0 due to IEEE 754 float representation (adapted test expectation)
|
|
78
|
+
- Python `color.rgb(r,g,b,100)` produces `"rgba(..., 0.0)"` not `"rgba(..., 0)"` (float formatting)
|
|
79
|
+
- Python `str.split('', '')` raises ValueError (tested as such instead of returning `[]`)
|
|
80
|
+
- Python `substring` uses direct slicing (negative indices and begin>end behave differently from JS)
|
|
81
|
+
- Python counts Unicode code points, not UTF-16 units (emoji = 1 char, not 2)
|
|
@@ -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,99 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="./logo.png" width="150" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">OakScriptPy</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
PineScript-like API for technical analysis in Python. Pure Python, zero dependencies.
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install oakscriptpy
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
from oakscriptpy import ta, math, color, Series
|
|
23
|
+
from oakscriptpy._types import Bar
|
|
24
|
+
|
|
25
|
+
# Create bars
|
|
26
|
+
bars = [
|
|
27
|
+
Bar(time=1, open=10.0, high=12.0, low=9.0, close=11.0, volume=100.0),
|
|
28
|
+
Bar(time=2, open=11.0, high=13.0, low=10.0, close=12.0, volume=110.0),
|
|
29
|
+
Bar(time=3, open=12.0, high=14.0, low=11.0, close=13.0, volume=120.0),
|
|
30
|
+
# ...
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
# Array-based TA
|
|
34
|
+
closes = [b.close for b in bars]
|
|
35
|
+
sma_values = ta.sma(closes, 3)
|
|
36
|
+
ema_values = ta.ema(closes, 3)
|
|
37
|
+
rsi_values = ta.rsi(closes, 14)
|
|
38
|
+
|
|
39
|
+
# Series-based TA (with operator overloading)
|
|
40
|
+
from oakscriptpy import ta as ta_series
|
|
41
|
+
close_series = Series.from_bars(bars, lambda b: b.close)
|
|
42
|
+
sma_series = ta_series.sma(close_series, 3)
|
|
43
|
+
ema_series = ta_series.ema(close_series, 3)
|
|
44
|
+
|
|
45
|
+
# Series arithmetic
|
|
46
|
+
spread = close_series - sma_series
|
|
47
|
+
doubled = close_series * 2
|
|
48
|
+
above_sma = close_series > sma_series
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Namespaces
|
|
52
|
+
|
|
53
|
+
| Namespace | Functions | Description |
|
|
54
|
+
|-----------|-----------|-------------|
|
|
55
|
+
| `ta` | 64 | Technical analysis (SMA, EMA, RSI, MACD, Bollinger Bands, Ichimoku, SuperTrend, ZigZag, etc.) |
|
|
56
|
+
| `math` | 26 | Mathematical functions (abs, ceil, floor, sqrt, pow, trig, etc.) |
|
|
57
|
+
| `array` | 42+ | Array manipulation (push, pop, sort, slice, binary_search, percentile, etc.) |
|
|
58
|
+
| `color` | 14 + 17 constants | Color creation and manipulation (rgb, from_hex, new_color, named constants) |
|
|
59
|
+
| `str` | 20+ | String functions (split, replace, substring, contains, match, etc.) |
|
|
60
|
+
| `time` | 2 | Time utilities (format_time) |
|
|
61
|
+
| `matrix` | 43 | Matrix operations (add, mult, det, inv, eigenvalues, eigenvectors, etc.) |
|
|
62
|
+
| `line` | 10 | Line drawing objects |
|
|
63
|
+
| `box` | 12 | Box drawing objects |
|
|
64
|
+
| `label` | 10 | Label drawing objects |
|
|
65
|
+
| `linefill` | 4 | Linefill functions |
|
|
66
|
+
| `chart_point` | 3 | Chart point functions |
|
|
67
|
+
| `polyline` | 5 | Polyline functions |
|
|
68
|
+
|
|
69
|
+
## Series
|
|
70
|
+
|
|
71
|
+
The `Series` class provides lazy evaluation with Python operator overloading:
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
a = Series.from_bars(bars, lambda b: b.close)
|
|
75
|
+
b = Series.from_bars(bars, lambda b: b.open)
|
|
76
|
+
|
|
77
|
+
# Arithmetic: +, -, *, /, %
|
|
78
|
+
spread = a - b
|
|
79
|
+
|
|
80
|
+
# Comparison: >, >=, <, <=, eq(), neq()
|
|
81
|
+
bullish = a > b
|
|
82
|
+
|
|
83
|
+
# Logical: and_(), or_(), not_()
|
|
84
|
+
signal = bullish.and_(a > sma_series)
|
|
85
|
+
|
|
86
|
+
# History access
|
|
87
|
+
prev_close = a.offset(1)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Tests
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
uv run pytest
|
|
94
|
+
# 1,224 passed, 56 skipped
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## License
|
|
98
|
+
|
|
99
|
+
MIT
|
|
Binary file
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "oakscriptpy"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "PineScript-like API for technical analysis in Python"
|
|
5
|
+
requires-python = ">=3.11"
|
|
6
|
+
dependencies = []
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
license = "MIT"
|
|
9
|
+
authors = [{ name = "deepentropy" }]
|
|
10
|
+
keywords = ["pinescript", "technical-analysis", "trading", "finance", "ta"]
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 3 - Alpha",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"Intended Audience :: Financial and Insurance Industry",
|
|
15
|
+
"License :: OSI Approved :: MIT License",
|
|
16
|
+
"Programming Language :: Python :: 3.11",
|
|
17
|
+
"Programming Language :: Python :: 3.12",
|
|
18
|
+
"Programming Language :: Python :: 3.13",
|
|
19
|
+
"Topic :: Office/Business :: Financial :: Investment",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[project.urls]
|
|
23
|
+
Homepage = "https://github.com/deepentropy/oakscriptPy"
|
|
24
|
+
Repository = "https://github.com/deepentropy/oakscriptPy"
|
|
25
|
+
Issues = "https://github.com/deepentropy/oakscriptPy/issues"
|
|
26
|
+
|
|
27
|
+
[build-system]
|
|
28
|
+
requires = ["hatchling"]
|
|
29
|
+
build-backend = "hatchling.build"
|
|
30
|
+
|
|
31
|
+
[tool.hatch.build.targets.wheel]
|
|
32
|
+
packages = ["src/oakscriptpy"]
|
|
33
|
+
|
|
34
|
+
[dependency-groups]
|
|
35
|
+
dev = [
|
|
36
|
+
"pytest>=9.0.2",
|
|
37
|
+
]
|
|
@@ -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
|
+
}
|
|
@@ -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
|