opencode-pine2pyne 0.1.0__tar.gz → 0.1.1__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.
Files changed (42) hide show
  1. opencode_pine2pyne-0.1.1/PKG-INFO +219 -0
  2. opencode_pine2pyne-0.1.1/README.md +199 -0
  3. opencode_pine2pyne-0.1.1/opencode_pine2pyne.egg-info/PKG-INFO +219 -0
  4. {opencode_pine2pyne-0.1.0 → opencode_pine2pyne-0.1.1}/opencode_pine2pyne.egg-info/SOURCES.txt +11 -1
  5. {opencode_pine2pyne-0.1.0 → opencode_pine2pyne-0.1.1}/pine2pyne/__init__.py +1 -1
  6. {opencode_pine2pyne-0.1.0 → opencode_pine2pyne-0.1.1}/pine2pyne/codegen.py +14 -2
  7. {opencode_pine2pyne-0.1.0 → opencode_pine2pyne-0.1.1}/pine2pyne/lexer.py +13 -10
  8. {opencode_pine2pyne-0.1.0 → opencode_pine2pyne-0.1.1}/pine2pyne/parser.py +6 -1
  9. {opencode_pine2pyne-0.1.0 → opencode_pine2pyne-0.1.1}/pyproject.toml +7 -0
  10. opencode_pine2pyne-0.1.1/tests/test_cli.py +697 -0
  11. opencode_pine2pyne-0.1.1/tests/test_codegen.py +1139 -0
  12. opencode_pine2pyne-0.1.1/tests/test_corpus_regression.py +316 -0
  13. opencode_pine2pyne-0.1.1/tests/test_errors.py +269 -0
  14. opencode_pine2pyne-0.1.1/tests/test_import_resolver.py +791 -0
  15. opencode_pine2pyne-0.1.1/tests/test_lexer.py +546 -0
  16. opencode_pine2pyne-0.1.1/tests/test_parser.py +964 -0
  17. opencode_pine2pyne-0.1.1/tests/test_pine_builtins.py +661 -0
  18. opencode_pine2pyne-0.1.1/tests/test_symbol_table.py +451 -0
  19. opencode_pine2pyne-0.1.1/tests/test_transformer.py +651 -0
  20. opencode_pine2pyne-0.1.0/PKG-INFO +0 -123
  21. opencode_pine2pyne-0.1.0/README.md +0 -103
  22. opencode_pine2pyne-0.1.0/opencode_pine2pyne.egg-info/PKG-INFO +0 -123
  23. {opencode_pine2pyne-0.1.0 → opencode_pine2pyne-0.1.1}/LICENSE +0 -0
  24. {opencode_pine2pyne-0.1.0 → opencode_pine2pyne-0.1.1}/opencode_pine2pyne.egg-info/dependency_links.txt +0 -0
  25. {opencode_pine2pyne-0.1.0 → opencode_pine2pyne-0.1.1}/opencode_pine2pyne.egg-info/entry_points.txt +0 -0
  26. {opencode_pine2pyne-0.1.0 → opencode_pine2pyne-0.1.1}/opencode_pine2pyne.egg-info/requires.txt +0 -0
  27. {opencode_pine2pyne-0.1.0 → opencode_pine2pyne-0.1.1}/opencode_pine2pyne.egg-info/top_level.txt +0 -0
  28. {opencode_pine2pyne-0.1.0 → opencode_pine2pyne-0.1.1}/pine2pyne/README.md +0 -0
  29. {opencode_pine2pyne-0.1.0 → opencode_pine2pyne-0.1.1}/pine2pyne/TRANSPILER_BEST_PRACTICES.md +0 -0
  30. {opencode_pine2pyne-0.1.0 → opencode_pine2pyne-0.1.1}/pine2pyne/TRANSPILER_USAGE.md +0 -0
  31. {opencode_pine2pyne-0.1.0 → opencode_pine2pyne-0.1.1}/pine2pyne/__main__.py +0 -0
  32. {opencode_pine2pyne-0.1.0 → opencode_pine2pyne-0.1.1}/pine2pyne/ast_nodes.py +0 -0
  33. {opencode_pine2pyne-0.1.0 → opencode_pine2pyne-0.1.1}/pine2pyne/cli.py +0 -0
  34. {opencode_pine2pyne-0.1.0 → opencode_pine2pyne-0.1.1}/pine2pyne/errors.py +0 -0
  35. {opencode_pine2pyne-0.1.0 → opencode_pine2pyne-0.1.1}/pine2pyne/import_resolver.py +0 -0
  36. {opencode_pine2pyne-0.1.0 → opencode_pine2pyne-0.1.1}/pine2pyne/pine_builtins.py +0 -0
  37. {opencode_pine2pyne-0.1.0 → opencode_pine2pyne-0.1.1}/pine2pyne/symbol_table.py +0 -0
  38. {opencode_pine2pyne-0.1.0 → opencode_pine2pyne-0.1.1}/pine2pyne/tokens.py +0 -0
  39. {opencode_pine2pyne-0.1.0 → opencode_pine2pyne-0.1.1}/pine2pyne/transformer.py +0 -0
  40. {opencode_pine2pyne-0.1.0 → opencode_pine2pyne-0.1.1}/pine2pyne/type_inference.py +0 -0
  41. {opencode_pine2pyne-0.1.0 → opencode_pine2pyne-0.1.1}/setup.cfg +0 -0
  42. {opencode_pine2pyne-0.1.0 → opencode_pine2pyne-0.1.1}/tests/test_screener_regression.py +0 -0
@@ -0,0 +1,219 @@
1
+ Metadata-Version: 2.4
2
+ Name: opencode-pine2pyne
3
+ Version: 0.1.1
4
+ Summary: Pine Script v6 to PyneCore Python transpiler
5
+ Author: rubycell
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/rubycell/pine2pyne
8
+ Project-URL: Repository, https://github.com/rubycell/pine2pyne
9
+ Keywords: pinescript,tradingview,transpiler,pynecore,backtesting
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Topic :: Office/Business :: Financial :: Investment
14
+ Requires-Python: >=3.11
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Provides-Extra: runtime
18
+ Requires-Dist: opencode-pyneruntime; extra == "runtime"
19
+ Dynamic: license-file
20
+
21
+ # PyneCore - Pine Script to Python Transpiler
22
+
23
+ > **Backlog & known bugs:** [Projects board #10](https://github.com/users/rubycell/projects/10)
24
+ > — open work is in **Backlog** (prioritised P0/P1/P2), shipped work in **Done**.
25
+ > Confirmed unfixed transpiler bugs live there too; several are pinned by strict
26
+ > `xfail` tests (see [Running Tests](#running-tests)).
27
+
28
+ ## Installation (New Computer)
29
+
30
+ ### Fastest: the sandbox-setup skill
31
+
32
+ From the repo root, run the bundled setup script (idempotent — safe to re-run):
33
+
34
+ ```bash
35
+ bash .claude/skills/sandbox-setup/setup.sh
36
+ ```
37
+
38
+ It builds `.venv`, installs everything below, detects `PINE_CPU_COUNT` /
39
+ `PINE_MAX_WORKERS`, seeds a demo dataset, and runs a smoke test that ends in
40
+ `smoke test : PASS` / `READY`. In Claude Code you can also invoke it as
41
+ `/sandbox-setup`.
42
+
43
+ ### Manual (equivalent steps)
44
+
45
+ ```bash
46
+ # From the repo root:
47
+ python3 -m venv .venv # 1. virtualenv
48
+ .venv/bin/pip install -e . # 2. this transpiler (editable)
49
+ .venv/bin/pip install 'opencode-pyneruntime[cli]' # 3. PyneCore runtime + `pyne` CLI
50
+ .venv/bin/pip install optuna numpy # 4. optimizer deps (not pulled in by the runtime)
51
+ source .venv/bin/activate # 5. activate (Linux/macOS)
52
+ # .venv\Scripts\activate # (Windows)
53
+ ```
54
+
55
+ Verify the install:
56
+
57
+ ```bash
58
+ .venv/bin/python -c "import pynecore, pine2pyne, optuna, numpy; print('all ok')"
59
+ ```
60
+
61
+ **Note**: All commands below assume you are at the repo root with the venv activated.
62
+
63
+ ## How to Convert Pine Script to Python
64
+
65
+ ### Single File Conversion
66
+
67
+ ```bash
68
+ python -m pine2pyne path/to/script.pine -o path/to/output.py
69
+ ```
70
+
71
+ ### Batch Conversion
72
+
73
+ ```bash
74
+ python -m pine2pyne "sample/pinescript/*.pine" -o workdir/scripts/
75
+ ```
76
+
77
+ ## Running Tests
78
+
79
+ There are **three** independent test systems. They answer different questions,
80
+ so a green run of one says nothing about the others.
81
+
82
+ | System | Question it answers | Runtime |
83
+ |---|---|---|
84
+ | **1. Unit tests** (`tests/`, pytest) | Are the transpiler's internals correct? | ~5 s |
85
+ | **2. Sample suites** (`tools/test_all_*.py`) | Does every corpus sample transpile and run? | ~35 s |
86
+ | **3. Ground truth** (`tools/*_ground_truth*`) | Does it produce the *same numbers* as before? | minutes |
87
+
88
+ ### 1. Unit tests — pytest
89
+
90
+ 2,000+ tests over `pine2pyne/` (93% line / 86% branch coverage).
91
+
92
+ ```bash
93
+ .venv/bin/python -m pytest tests/ --ignore=tests/test_screener_regression.py -q
94
+
95
+ # with coverage
96
+ .venv/bin/python -m pytest tests/ --ignore=tests/test_screener_regression.py \
97
+ --cov=pine2pyne --cov-report=term-missing
98
+ ```
99
+
100
+ > `--ignore` is required: `tests/test_screener_regression.py` imports a
101
+ > `screener` module from a **sibling repo** that is absent here, and fails at
102
+ > collection — which aborts the whole run. Tracked as a P0 card on
103
+ > [board #10](https://github.com/users/rubycell/projects/10).
104
+
105
+ Some tests are `@pytest.mark.xfail(strict=True)` and pin **confirmed unfixed
106
+ bugs**, each with a card on [board #10](https://github.com/users/rubycell/projects/10).
107
+ Strict means fixing a bug turns its marker into a failure until the marker is
108
+ removed, so nothing regresses silently in either direction.
109
+
110
+ The highest-priority one is **P0: codegen emits arithmetically wrong Python** —
111
+ `10 - (5 - 3)` transpiles to `10 - 5 - 3`, which evaluates to 2 rather than 8.
112
+ See [docs/test_plan.md](docs/test_plan.md) for the full bug inventory and the
113
+ measured coverage/mutation results.
114
+
115
+ ### 2. Sample suites — end-to-end transpile + run
116
+
117
+ Transpiles every `.pine` in the corpus and runs it through `pyne`.
118
+
119
+ ```bash
120
+ python tools/test_all_samples.py # sample/pinescript (~411 files)
121
+ python tools/test_all_sample_pds.py # sample_pds (~328 files)
122
+
123
+ python tools/test_all_samples.py "ex_001*" # filter by pattern
124
+ python tools/test_all_samples.py --timeout 30 # default 15s
125
+ python tools/test_all_samples.py --verbose # stderr on failure
126
+ python tools/test_all_samples.py -j 0 # all CPU cores
127
+ ```
128
+
129
+ - Source: `sample/pinescript/`, `sample_pds/` · Output: `workdir/scripts/`
130
+ - Data: `workdir/data/demo.ohlcv` · Results: `test_results{,_pds}.{json,txt}`
131
+
132
+ **Per-sample expectations** live in `tools/sample_expectations.toml`:
133
+
134
+ ```toml
135
+ [expds_156_Out_of_bounds_index]
136
+ expect_error = "index_error" # MUST raise; running clean = FAIL
137
+
138
+ [ex_300_ticker_modify]
139
+ requires_feeds = { D = "demo", "2D" = "demo_2D" } # -> --security D=demo …
140
+ ```
141
+
142
+ `expect_error` marks Pine documentation samples that exist to *demonstrate* an
143
+ error — they are reported as `XFAIL` and count as passes. `requires_feeds`
144
+ supplies extra OHLCV feeds for samples calling `request.security()` at a
145
+ timeframe other than the chart's; pynecore deliberately will not resample the
146
+ chart feed, so the feed must be explicit. Generate one with:
147
+
148
+ ```bash
149
+ cd workdir && pyne data aggregate data/demo.ohlcv -tf 2D
150
+ ```
151
+
152
+ Shared logic for both suites lives in `tools/_suite_common.py` — edit it there,
153
+ not in each harness.
154
+
155
+ ### 3. Ground truth — numeric regression
156
+
157
+ Diffs freshly generated output CSVs against ~1,985 stored baselines in
158
+ `ground_truth/` (779 MB). This is the only system that catches *silently wrong
159
+ numbers* — output that runs fine but computes something different.
160
+
161
+ ```bash
162
+ python tools/generate_ground_truth.py # (re)build baselines
163
+ python tools/compare_ground_truth.py # diff current output vs baselines
164
+ python tools/compare_ground_truth.py --suite sample_pds -j 12
165
+ ./tools/verify_ground_truth.sh # end-to-end wrapper
166
+ ```
167
+
168
+ > Regenerate baselines only when you have *confirmed* the new output is correct
169
+ > — otherwise a bug gets frozen in as the expected result.
170
+
171
+ ### Test data
172
+
173
+ `workdir/data/` is gitignored, so a fresh clone has none and the sample suites
174
+ abort with `Data file not found`. Restore the exact dataset the recorded results
175
+ were measured against:
176
+
177
+ ```bash
178
+ git show 227f3d2^:workdir/data/demo.ohlcv > workdir/data/demo.ohlcv
179
+ git show 227f3d2^:workdir/data/demo.toml > workdir/data/demo.toml
180
+ ```
181
+
182
+ (CCXT BTCUSDT 1D. `/sandbox-setup` instead seeds a small *synthetic* fixture —
183
+ fine for a smoke test, but results will not match the recorded rates.)
184
+
185
+ ## For Claude Code / LLM Development
186
+
187
+ See `CLAUDE.md` in this directory for transpiler architecture, output format specs, optimizer documentation, and CSV rounding rules. See `pine2pyne/README.md` for transpiler internals and transformation rules.
188
+
189
+ ## Optimizing Strategies
190
+
191
+ Quick start: `pyne optimize script.py data.ohlcv params.json -n 20`
192
+
193
+ See [Optimizer.md](./Optimizer.md) for full documentation (parameter JSON format, parallel execution, output files).
194
+
195
+ ### Distributed Optimization (multi-machine)
196
+
197
+ Two tools distribute `pyne optimize` across SSH clusters:
198
+
199
+ | Tool | Model | Best for |
200
+ |------|-------|----------|
201
+ | `pyne-dynamic.sh` | Flat-queue (on-demand dispatch) | Multi-variant runs, heterogeneous clusters, long jobs |
202
+ | `pyne-parallel.sh` | Static pre-assignment | Quick jobs on similar-speed machines |
203
+
204
+ ```bash
205
+ # Dynamic (recommended): flat-queue, pre-syncs once, auto-adapts to machine speed
206
+ # Run from workdir/ directory:
207
+ ../tools/pyne-dynamic.sh scripts/strategy.py data/data.ohlcv optimize_variants/ \
208
+ -H ../tools/machines.txt -C 24 --name my_run --output-dir runs/output/
209
+
210
+ # Static: pre-assigns chunks by core count
211
+ ./tools/pyne-parallel.sh scripts/strategy.py data/data.ohlcv optimize.json \
212
+ -H tools/machines.txt --sync
213
+
214
+ # Check progress / collect results
215
+ ../tools/pyne-dynamic.sh --status
216
+ ../tools/pyne-dynamic.sh --collect
217
+ ```
218
+
219
+ Both use the same `machines.txt` format. See `CLAUDE.md` for cluster setup, machine file format, and troubleshooting.
@@ -0,0 +1,199 @@
1
+ # PyneCore - Pine Script to Python Transpiler
2
+
3
+ > **Backlog & known bugs:** [Projects board #10](https://github.com/users/rubycell/projects/10)
4
+ > — open work is in **Backlog** (prioritised P0/P1/P2), shipped work in **Done**.
5
+ > Confirmed unfixed transpiler bugs live there too; several are pinned by strict
6
+ > `xfail` tests (see [Running Tests](#running-tests)).
7
+
8
+ ## Installation (New Computer)
9
+
10
+ ### Fastest: the sandbox-setup skill
11
+
12
+ From the repo root, run the bundled setup script (idempotent — safe to re-run):
13
+
14
+ ```bash
15
+ bash .claude/skills/sandbox-setup/setup.sh
16
+ ```
17
+
18
+ It builds `.venv`, installs everything below, detects `PINE_CPU_COUNT` /
19
+ `PINE_MAX_WORKERS`, seeds a demo dataset, and runs a smoke test that ends in
20
+ `smoke test : PASS` / `READY`. In Claude Code you can also invoke it as
21
+ `/sandbox-setup`.
22
+
23
+ ### Manual (equivalent steps)
24
+
25
+ ```bash
26
+ # From the repo root:
27
+ python3 -m venv .venv # 1. virtualenv
28
+ .venv/bin/pip install -e . # 2. this transpiler (editable)
29
+ .venv/bin/pip install 'opencode-pyneruntime[cli]' # 3. PyneCore runtime + `pyne` CLI
30
+ .venv/bin/pip install optuna numpy # 4. optimizer deps (not pulled in by the runtime)
31
+ source .venv/bin/activate # 5. activate (Linux/macOS)
32
+ # .venv\Scripts\activate # (Windows)
33
+ ```
34
+
35
+ Verify the install:
36
+
37
+ ```bash
38
+ .venv/bin/python -c "import pynecore, pine2pyne, optuna, numpy; print('all ok')"
39
+ ```
40
+
41
+ **Note**: All commands below assume you are at the repo root with the venv activated.
42
+
43
+ ## How to Convert Pine Script to Python
44
+
45
+ ### Single File Conversion
46
+
47
+ ```bash
48
+ python -m pine2pyne path/to/script.pine -o path/to/output.py
49
+ ```
50
+
51
+ ### Batch Conversion
52
+
53
+ ```bash
54
+ python -m pine2pyne "sample/pinescript/*.pine" -o workdir/scripts/
55
+ ```
56
+
57
+ ## Running Tests
58
+
59
+ There are **three** independent test systems. They answer different questions,
60
+ so a green run of one says nothing about the others.
61
+
62
+ | System | Question it answers | Runtime |
63
+ |---|---|---|
64
+ | **1. Unit tests** (`tests/`, pytest) | Are the transpiler's internals correct? | ~5 s |
65
+ | **2. Sample suites** (`tools/test_all_*.py`) | Does every corpus sample transpile and run? | ~35 s |
66
+ | **3. Ground truth** (`tools/*_ground_truth*`) | Does it produce the *same numbers* as before? | minutes |
67
+
68
+ ### 1. Unit tests — pytest
69
+
70
+ 2,000+ tests over `pine2pyne/` (93% line / 86% branch coverage).
71
+
72
+ ```bash
73
+ .venv/bin/python -m pytest tests/ --ignore=tests/test_screener_regression.py -q
74
+
75
+ # with coverage
76
+ .venv/bin/python -m pytest tests/ --ignore=tests/test_screener_regression.py \
77
+ --cov=pine2pyne --cov-report=term-missing
78
+ ```
79
+
80
+ > `--ignore` is required: `tests/test_screener_regression.py` imports a
81
+ > `screener` module from a **sibling repo** that is absent here, and fails at
82
+ > collection — which aborts the whole run. Tracked as a P0 card on
83
+ > [board #10](https://github.com/users/rubycell/projects/10).
84
+
85
+ Some tests are `@pytest.mark.xfail(strict=True)` and pin **confirmed unfixed
86
+ bugs**, each with a card on [board #10](https://github.com/users/rubycell/projects/10).
87
+ Strict means fixing a bug turns its marker into a failure until the marker is
88
+ removed, so nothing regresses silently in either direction.
89
+
90
+ The highest-priority one is **P0: codegen emits arithmetically wrong Python** —
91
+ `10 - (5 - 3)` transpiles to `10 - 5 - 3`, which evaluates to 2 rather than 8.
92
+ See [docs/test_plan.md](docs/test_plan.md) for the full bug inventory and the
93
+ measured coverage/mutation results.
94
+
95
+ ### 2. Sample suites — end-to-end transpile + run
96
+
97
+ Transpiles every `.pine` in the corpus and runs it through `pyne`.
98
+
99
+ ```bash
100
+ python tools/test_all_samples.py # sample/pinescript (~411 files)
101
+ python tools/test_all_sample_pds.py # sample_pds (~328 files)
102
+
103
+ python tools/test_all_samples.py "ex_001*" # filter by pattern
104
+ python tools/test_all_samples.py --timeout 30 # default 15s
105
+ python tools/test_all_samples.py --verbose # stderr on failure
106
+ python tools/test_all_samples.py -j 0 # all CPU cores
107
+ ```
108
+
109
+ - Source: `sample/pinescript/`, `sample_pds/` · Output: `workdir/scripts/`
110
+ - Data: `workdir/data/demo.ohlcv` · Results: `test_results{,_pds}.{json,txt}`
111
+
112
+ **Per-sample expectations** live in `tools/sample_expectations.toml`:
113
+
114
+ ```toml
115
+ [expds_156_Out_of_bounds_index]
116
+ expect_error = "index_error" # MUST raise; running clean = FAIL
117
+
118
+ [ex_300_ticker_modify]
119
+ requires_feeds = { D = "demo", "2D" = "demo_2D" } # -> --security D=demo …
120
+ ```
121
+
122
+ `expect_error` marks Pine documentation samples that exist to *demonstrate* an
123
+ error — they are reported as `XFAIL` and count as passes. `requires_feeds`
124
+ supplies extra OHLCV feeds for samples calling `request.security()` at a
125
+ timeframe other than the chart's; pynecore deliberately will not resample the
126
+ chart feed, so the feed must be explicit. Generate one with:
127
+
128
+ ```bash
129
+ cd workdir && pyne data aggregate data/demo.ohlcv -tf 2D
130
+ ```
131
+
132
+ Shared logic for both suites lives in `tools/_suite_common.py` — edit it there,
133
+ not in each harness.
134
+
135
+ ### 3. Ground truth — numeric regression
136
+
137
+ Diffs freshly generated output CSVs against ~1,985 stored baselines in
138
+ `ground_truth/` (779 MB). This is the only system that catches *silently wrong
139
+ numbers* — output that runs fine but computes something different.
140
+
141
+ ```bash
142
+ python tools/generate_ground_truth.py # (re)build baselines
143
+ python tools/compare_ground_truth.py # diff current output vs baselines
144
+ python tools/compare_ground_truth.py --suite sample_pds -j 12
145
+ ./tools/verify_ground_truth.sh # end-to-end wrapper
146
+ ```
147
+
148
+ > Regenerate baselines only when you have *confirmed* the new output is correct
149
+ > — otherwise a bug gets frozen in as the expected result.
150
+
151
+ ### Test data
152
+
153
+ `workdir/data/` is gitignored, so a fresh clone has none and the sample suites
154
+ abort with `Data file not found`. Restore the exact dataset the recorded results
155
+ were measured against:
156
+
157
+ ```bash
158
+ git show 227f3d2^:workdir/data/demo.ohlcv > workdir/data/demo.ohlcv
159
+ git show 227f3d2^:workdir/data/demo.toml > workdir/data/demo.toml
160
+ ```
161
+
162
+ (CCXT BTCUSDT 1D. `/sandbox-setup` instead seeds a small *synthetic* fixture —
163
+ fine for a smoke test, but results will not match the recorded rates.)
164
+
165
+ ## For Claude Code / LLM Development
166
+
167
+ See `CLAUDE.md` in this directory for transpiler architecture, output format specs, optimizer documentation, and CSV rounding rules. See `pine2pyne/README.md` for transpiler internals and transformation rules.
168
+
169
+ ## Optimizing Strategies
170
+
171
+ Quick start: `pyne optimize script.py data.ohlcv params.json -n 20`
172
+
173
+ See [Optimizer.md](./Optimizer.md) for full documentation (parameter JSON format, parallel execution, output files).
174
+
175
+ ### Distributed Optimization (multi-machine)
176
+
177
+ Two tools distribute `pyne optimize` across SSH clusters:
178
+
179
+ | Tool | Model | Best for |
180
+ |------|-------|----------|
181
+ | `pyne-dynamic.sh` | Flat-queue (on-demand dispatch) | Multi-variant runs, heterogeneous clusters, long jobs |
182
+ | `pyne-parallel.sh` | Static pre-assignment | Quick jobs on similar-speed machines |
183
+
184
+ ```bash
185
+ # Dynamic (recommended): flat-queue, pre-syncs once, auto-adapts to machine speed
186
+ # Run from workdir/ directory:
187
+ ../tools/pyne-dynamic.sh scripts/strategy.py data/data.ohlcv optimize_variants/ \
188
+ -H ../tools/machines.txt -C 24 --name my_run --output-dir runs/output/
189
+
190
+ # Static: pre-assigns chunks by core count
191
+ ./tools/pyne-parallel.sh scripts/strategy.py data/data.ohlcv optimize.json \
192
+ -H tools/machines.txt --sync
193
+
194
+ # Check progress / collect results
195
+ ../tools/pyne-dynamic.sh --status
196
+ ../tools/pyne-dynamic.sh --collect
197
+ ```
198
+
199
+ Both use the same `machines.txt` format. See `CLAUDE.md` for cluster setup, machine file format, and troubleshooting.
@@ -0,0 +1,219 @@
1
+ Metadata-Version: 2.4
2
+ Name: opencode-pine2pyne
3
+ Version: 0.1.1
4
+ Summary: Pine Script v6 to PyneCore Python transpiler
5
+ Author: rubycell
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/rubycell/pine2pyne
8
+ Project-URL: Repository, https://github.com/rubycell/pine2pyne
9
+ Keywords: pinescript,tradingview,transpiler,pynecore,backtesting
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Topic :: Office/Business :: Financial :: Investment
14
+ Requires-Python: >=3.11
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Provides-Extra: runtime
18
+ Requires-Dist: opencode-pyneruntime; extra == "runtime"
19
+ Dynamic: license-file
20
+
21
+ # PyneCore - Pine Script to Python Transpiler
22
+
23
+ > **Backlog & known bugs:** [Projects board #10](https://github.com/users/rubycell/projects/10)
24
+ > — open work is in **Backlog** (prioritised P0/P1/P2), shipped work in **Done**.
25
+ > Confirmed unfixed transpiler bugs live there too; several are pinned by strict
26
+ > `xfail` tests (see [Running Tests](#running-tests)).
27
+
28
+ ## Installation (New Computer)
29
+
30
+ ### Fastest: the sandbox-setup skill
31
+
32
+ From the repo root, run the bundled setup script (idempotent — safe to re-run):
33
+
34
+ ```bash
35
+ bash .claude/skills/sandbox-setup/setup.sh
36
+ ```
37
+
38
+ It builds `.venv`, installs everything below, detects `PINE_CPU_COUNT` /
39
+ `PINE_MAX_WORKERS`, seeds a demo dataset, and runs a smoke test that ends in
40
+ `smoke test : PASS` / `READY`. In Claude Code you can also invoke it as
41
+ `/sandbox-setup`.
42
+
43
+ ### Manual (equivalent steps)
44
+
45
+ ```bash
46
+ # From the repo root:
47
+ python3 -m venv .venv # 1. virtualenv
48
+ .venv/bin/pip install -e . # 2. this transpiler (editable)
49
+ .venv/bin/pip install 'opencode-pyneruntime[cli]' # 3. PyneCore runtime + `pyne` CLI
50
+ .venv/bin/pip install optuna numpy # 4. optimizer deps (not pulled in by the runtime)
51
+ source .venv/bin/activate # 5. activate (Linux/macOS)
52
+ # .venv\Scripts\activate # (Windows)
53
+ ```
54
+
55
+ Verify the install:
56
+
57
+ ```bash
58
+ .venv/bin/python -c "import pynecore, pine2pyne, optuna, numpy; print('all ok')"
59
+ ```
60
+
61
+ **Note**: All commands below assume you are at the repo root with the venv activated.
62
+
63
+ ## How to Convert Pine Script to Python
64
+
65
+ ### Single File Conversion
66
+
67
+ ```bash
68
+ python -m pine2pyne path/to/script.pine -o path/to/output.py
69
+ ```
70
+
71
+ ### Batch Conversion
72
+
73
+ ```bash
74
+ python -m pine2pyne "sample/pinescript/*.pine" -o workdir/scripts/
75
+ ```
76
+
77
+ ## Running Tests
78
+
79
+ There are **three** independent test systems. They answer different questions,
80
+ so a green run of one says nothing about the others.
81
+
82
+ | System | Question it answers | Runtime |
83
+ |---|---|---|
84
+ | **1. Unit tests** (`tests/`, pytest) | Are the transpiler's internals correct? | ~5 s |
85
+ | **2. Sample suites** (`tools/test_all_*.py`) | Does every corpus sample transpile and run? | ~35 s |
86
+ | **3. Ground truth** (`tools/*_ground_truth*`) | Does it produce the *same numbers* as before? | minutes |
87
+
88
+ ### 1. Unit tests — pytest
89
+
90
+ 2,000+ tests over `pine2pyne/` (93% line / 86% branch coverage).
91
+
92
+ ```bash
93
+ .venv/bin/python -m pytest tests/ --ignore=tests/test_screener_regression.py -q
94
+
95
+ # with coverage
96
+ .venv/bin/python -m pytest tests/ --ignore=tests/test_screener_regression.py \
97
+ --cov=pine2pyne --cov-report=term-missing
98
+ ```
99
+
100
+ > `--ignore` is required: `tests/test_screener_regression.py` imports a
101
+ > `screener` module from a **sibling repo** that is absent here, and fails at
102
+ > collection — which aborts the whole run. Tracked as a P0 card on
103
+ > [board #10](https://github.com/users/rubycell/projects/10).
104
+
105
+ Some tests are `@pytest.mark.xfail(strict=True)` and pin **confirmed unfixed
106
+ bugs**, each with a card on [board #10](https://github.com/users/rubycell/projects/10).
107
+ Strict means fixing a bug turns its marker into a failure until the marker is
108
+ removed, so nothing regresses silently in either direction.
109
+
110
+ The highest-priority one is **P0: codegen emits arithmetically wrong Python** —
111
+ `10 - (5 - 3)` transpiles to `10 - 5 - 3`, which evaluates to 2 rather than 8.
112
+ See [docs/test_plan.md](docs/test_plan.md) for the full bug inventory and the
113
+ measured coverage/mutation results.
114
+
115
+ ### 2. Sample suites — end-to-end transpile + run
116
+
117
+ Transpiles every `.pine` in the corpus and runs it through `pyne`.
118
+
119
+ ```bash
120
+ python tools/test_all_samples.py # sample/pinescript (~411 files)
121
+ python tools/test_all_sample_pds.py # sample_pds (~328 files)
122
+
123
+ python tools/test_all_samples.py "ex_001*" # filter by pattern
124
+ python tools/test_all_samples.py --timeout 30 # default 15s
125
+ python tools/test_all_samples.py --verbose # stderr on failure
126
+ python tools/test_all_samples.py -j 0 # all CPU cores
127
+ ```
128
+
129
+ - Source: `sample/pinescript/`, `sample_pds/` · Output: `workdir/scripts/`
130
+ - Data: `workdir/data/demo.ohlcv` · Results: `test_results{,_pds}.{json,txt}`
131
+
132
+ **Per-sample expectations** live in `tools/sample_expectations.toml`:
133
+
134
+ ```toml
135
+ [expds_156_Out_of_bounds_index]
136
+ expect_error = "index_error" # MUST raise; running clean = FAIL
137
+
138
+ [ex_300_ticker_modify]
139
+ requires_feeds = { D = "demo", "2D" = "demo_2D" } # -> --security D=demo …
140
+ ```
141
+
142
+ `expect_error` marks Pine documentation samples that exist to *demonstrate* an
143
+ error — they are reported as `XFAIL` and count as passes. `requires_feeds`
144
+ supplies extra OHLCV feeds for samples calling `request.security()` at a
145
+ timeframe other than the chart's; pynecore deliberately will not resample the
146
+ chart feed, so the feed must be explicit. Generate one with:
147
+
148
+ ```bash
149
+ cd workdir && pyne data aggregate data/demo.ohlcv -tf 2D
150
+ ```
151
+
152
+ Shared logic for both suites lives in `tools/_suite_common.py` — edit it there,
153
+ not in each harness.
154
+
155
+ ### 3. Ground truth — numeric regression
156
+
157
+ Diffs freshly generated output CSVs against ~1,985 stored baselines in
158
+ `ground_truth/` (779 MB). This is the only system that catches *silently wrong
159
+ numbers* — output that runs fine but computes something different.
160
+
161
+ ```bash
162
+ python tools/generate_ground_truth.py # (re)build baselines
163
+ python tools/compare_ground_truth.py # diff current output vs baselines
164
+ python tools/compare_ground_truth.py --suite sample_pds -j 12
165
+ ./tools/verify_ground_truth.sh # end-to-end wrapper
166
+ ```
167
+
168
+ > Regenerate baselines only when you have *confirmed* the new output is correct
169
+ > — otherwise a bug gets frozen in as the expected result.
170
+
171
+ ### Test data
172
+
173
+ `workdir/data/` is gitignored, so a fresh clone has none and the sample suites
174
+ abort with `Data file not found`. Restore the exact dataset the recorded results
175
+ were measured against:
176
+
177
+ ```bash
178
+ git show 227f3d2^:workdir/data/demo.ohlcv > workdir/data/demo.ohlcv
179
+ git show 227f3d2^:workdir/data/demo.toml > workdir/data/demo.toml
180
+ ```
181
+
182
+ (CCXT BTCUSDT 1D. `/sandbox-setup` instead seeds a small *synthetic* fixture —
183
+ fine for a smoke test, but results will not match the recorded rates.)
184
+
185
+ ## For Claude Code / LLM Development
186
+
187
+ See `CLAUDE.md` in this directory for transpiler architecture, output format specs, optimizer documentation, and CSV rounding rules. See `pine2pyne/README.md` for transpiler internals and transformation rules.
188
+
189
+ ## Optimizing Strategies
190
+
191
+ Quick start: `pyne optimize script.py data.ohlcv params.json -n 20`
192
+
193
+ See [Optimizer.md](./Optimizer.md) for full documentation (parameter JSON format, parallel execution, output files).
194
+
195
+ ### Distributed Optimization (multi-machine)
196
+
197
+ Two tools distribute `pyne optimize` across SSH clusters:
198
+
199
+ | Tool | Model | Best for |
200
+ |------|-------|----------|
201
+ | `pyne-dynamic.sh` | Flat-queue (on-demand dispatch) | Multi-variant runs, heterogeneous clusters, long jobs |
202
+ | `pyne-parallel.sh` | Static pre-assignment | Quick jobs on similar-speed machines |
203
+
204
+ ```bash
205
+ # Dynamic (recommended): flat-queue, pre-syncs once, auto-adapts to machine speed
206
+ # Run from workdir/ directory:
207
+ ../tools/pyne-dynamic.sh scripts/strategy.py data/data.ohlcv optimize_variants/ \
208
+ -H ../tools/machines.txt -C 24 --name my_run --output-dir runs/output/
209
+
210
+ # Static: pre-assigns chunks by core count
211
+ ./tools/pyne-parallel.sh scripts/strategy.py data/data.ohlcv optimize.json \
212
+ -H tools/machines.txt --sync
213
+
214
+ # Check progress / collect results
215
+ ../tools/pyne-dynamic.sh --status
216
+ ../tools/pyne-dynamic.sh --collect
217
+ ```
218
+
219
+ Both use the same `machines.txt` format. See `CLAUDE.md` for cluster setup, machine file format, and troubleshooting.
@@ -24,4 +24,14 @@ pine2pyne/symbol_table.py
24
24
  pine2pyne/tokens.py
25
25
  pine2pyne/transformer.py
26
26
  pine2pyne/type_inference.py
27
- tests/test_screener_regression.py
27
+ tests/test_cli.py
28
+ tests/test_codegen.py
29
+ tests/test_corpus_regression.py
30
+ tests/test_errors.py
31
+ tests/test_import_resolver.py
32
+ tests/test_lexer.py
33
+ tests/test_parser.py
34
+ tests/test_pine_builtins.py
35
+ tests/test_screener_regression.py
36
+ tests/test_symbol_table.py
37
+ tests/test_transformer.py
@@ -5,7 +5,7 @@ A tool to convert TradingView Pine Script indicators and strategies
5
5
  to PyneCore Python code for local backtesting and analysis.
6
6
  """
7
7
 
8
- __version__ = '0.1.0'
8
+ __version__ = '0.1.1'
9
9
  __author__ = 'Pine2Pyne Transpiler'
10
10
 
11
11
  from .lexer import Lexer, tokenize
@@ -799,9 +799,21 @@ class CodeGenerator:
799
799
  return f'{obj}[{index}]'
800
800
 
801
801
  def generate_array_literal(self, expr: ArrayLiteral) -> str:
802
- """Generate array literal."""
802
+ """Generate a Pine tuple expression as a Python tuple.
803
+
804
+ Pine writes tuple literals with square brackets and has no bracket
805
+ syntax for arrays -- those come from ``array.new``/``array.from`` -- so
806
+ every bracket that reaches here is a tuple, never a collection literal.
807
+ Emitting a list instead makes the runtime take its array branch: it
808
+ answers ``request.seed``/``request.security`` with a single ``na``
809
+ rather than one ``na`` per element, and the caller's destructuring then
810
+ dies with "na is not iterable".
811
+ """
803
812
  elements = [self.generate_expression(e) for e in expr.elements]
804
- return f'[{", ".join(elements)}]'
813
+ if len(elements) == 1:
814
+ # A lone parenthesised value is not a tuple without the comma.
815
+ return f'({elements[0]},)'
816
+ return f'({", ".join(elements)})'
805
817
 
806
818
  def _escape_string(self, value: str, quote: str) -> str:
807
819
  """Escape a string value for Python output."""