opencode-pine2pyne 0.1.1__tar.gz → 0.2.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.
- opencode_pine2pyne-0.2.0/PKG-INFO +301 -0
- opencode_pine2pyne-0.2.0/README.md +281 -0
- opencode_pine2pyne-0.2.0/opencode_pine2pyne.egg-info/PKG-INFO +301 -0
- opencode_pine2pyne-0.2.0/opencode_pine2pyne.egg-info/SOURCES.txt +116 -0
- opencode_pine2pyne-0.2.0/opencode_pine2pyne.egg-info/requires.txt +3 -0
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/pine2pyne/TRANSPILER_USAGE.md +3 -3
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/pine2pyne/__init__.py +11 -7
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/pine2pyne/ast_nodes.py +31 -3
- opencode_pine2pyne-0.2.0/pine2pyne/ast_walk.py +93 -0
- opencode_pine2pyne-0.2.0/pine2pyne/block_value_lowering.py +683 -0
- opencode_pine2pyne-0.2.0/pine2pyne/bool_builtins.py +42 -0
- opencode_pine2pyne-0.2.0/pine2pyne/bool_value_typing.py +467 -0
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/pine2pyne/cli.py +36 -12
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/pine2pyne/codegen.py +160 -159
- opencode_pine2pyne-0.2.0/pine2pyne/emitted_names.py +138 -0
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/pine2pyne/errors.py +20 -0
- opencode_pine2pyne-0.2.0/pine2pyne/for_loop_end_bound.py +128 -0
- opencode_pine2pyne-0.2.0/pine2pyne/grouped_history_lowering.py +310 -0
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/pine2pyne/import_resolver.py +89 -96
- opencode_pine2pyne-0.2.0/pine2pyne/library_collision.py +436 -0
- opencode_pine2pyne-0.2.0/pine2pyne/namespace_members.py +247 -0
- opencode_pine2pyne-0.2.0/pine2pyne/once_lowering.py +75 -0
- opencode_pine2pyne-0.2.0/pine2pyne/overload_resolution.py +480 -0
- opencode_pine2pyne-0.2.0/pine2pyne/parse_repair.py +398 -0
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/pine2pyne/parser.py +272 -93
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/pine2pyne/pine_builtins.py +88 -211
- opencode_pine2pyne-0.2.0/pine2pyne/pine_semantics.py +581 -0
- opencode_pine2pyne-0.2.0/pine2pyne/scope_renamer.py +720 -0
- opencode_pine2pyne-0.2.0/pine2pyne/strict_boolean.py +101 -0
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/pine2pyne/symbol_table.py +13 -3
- opencode_pine2pyne-0.2.0/pine2pyne/transformer.py +2166 -0
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/pine2pyne/type_inference.py +89 -1
- opencode_pine2pyne-0.2.0/pine2pyne/udt_field_defaults.py +142 -0
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/pyproject.toml +14 -2
- opencode_pine2pyne-0.2.0/tests/test_block_tail_value_runtime.py +1284 -0
- opencode_pine2pyne-0.2.0/tests/test_block_value_runtime.py +1083 -0
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/tests/test_cli.py +1 -1
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/tests/test_codegen.py +181 -158
- opencode_pine2pyne-0.2.0/tests/test_codegen_operator_grouping.py +252 -0
- opencode_pine2pyne-0.2.0/tests/test_codegen_refusal.py +206 -0
- opencode_pine2pyne-0.2.0/tests/test_collect_namespace_members.py +106 -0
- opencode_pine2pyne-0.2.0/tests/test_compare_ground_truth_completeness.py +185 -0
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/tests/test_corpus_regression.py +31 -1
- opencode_pine2pyne-0.2.0/tests/test_dotted_udt_default_runtime.py +114 -0
- opencode_pine2pyne-0.2.0/tests/test_drawing_receiver_runtime.py +53 -0
- opencode_pine2pyne-0.2.0/tests/test_emitted_name_collision_runtime.py +269 -0
- opencode_pine2pyne-0.2.0/tests/test_emitted_name_plan.py +178 -0
- opencode_pine2pyne-0.2.0/tests/test_error_demo_claims.py +321 -0
- opencode_pine2pyne-0.2.0/tests/test_error_demo_runtime.py +203 -0
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/tests/test_errors.py +52 -20
- opencode_pine2pyne-0.2.0/tests/test_for_in_pairs_runtime.py +264 -0
- opencode_pine2pyne-0.2.0/tests/test_for_loop_dynamic_end_runtime.py +201 -0
- opencode_pine2pyne-0.2.0/tests/test_function_tail_runtime.py +603 -0
- opencode_pine2pyne-0.2.0/tests/test_generated_tables.py +135 -0
- opencode_pine2pyne-0.2.0/tests/test_ground_truth_check_no_output.py +65 -0
- opencode_pine2pyne-0.2.0/tests/test_ground_truth_check_own_directory.py +233 -0
- opencode_pine2pyne-0.2.0/tests/test_ground_truth_digests.py +144 -0
- opencode_pine2pyne-0.2.0/tests/test_ground_truth_manifest_order.py +108 -0
- opencode_pine2pyne-0.2.0/tests/test_ground_truth_no_output.py +59 -0
- opencode_pine2pyne-0.2.0/tests/test_ground_truth_run_dir_isolation.py +527 -0
- opencode_pine2pyne-0.2.0/tests/test_ground_truth_settings.py +86 -0
- opencode_pine2pyne-0.2.0/tests/test_ground_truth_wrapper_retired.py +86 -0
- opencode_pine2pyne-0.2.0/tests/test_grouped_history_lowering.py +367 -0
- opencode_pine2pyne-0.2.0/tests/test_grouped_history_runtime.py +276 -0
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/tests/test_import_resolver.py +107 -34
- opencode_pine2pyne-0.2.0/tests/test_input_constant_and_enum_order_runtime.py +152 -0
- opencode_pine2pyne-0.2.0/tests/test_input_history_read_runtime.py +106 -0
- opencode_pine2pyne-0.2.0/tests/test_library_name_collision.py +158 -0
- opencode_pine2pyne-0.2.0/tests/test_library_name_collision_runtime.py +50 -0
- opencode_pine2pyne-0.2.0/tests/test_method_namespaces.py +74 -0
- opencode_pine2pyne-0.2.0/tests/test_method_receiver_shape_runtime.py +95 -0
- opencode_pine2pyne-0.2.0/tests/test_method_routing_runtime.py +517 -0
- opencode_pine2pyne-0.2.0/tests/test_once_structure_parser.py +72 -0
- opencode_pine2pyne-0.2.0/tests/test_once_structure_runtime.py +205 -0
- opencode_pine2pyne-0.2.0/tests/test_overload_dispatch_runtime.py +403 -0
- opencode_pine2pyne-0.2.0/tests/test_overload_resolution.py +324 -0
- opencode_pine2pyne-0.2.0/tests/test_parse_repair_card55.py +615 -0
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/tests/test_parser.py +6 -3
- opencode_pine2pyne-0.2.0/tests/test_parser_positions.py +87 -0
- opencode_pine2pyne-0.2.0/tests/test_pine_builtins.py +286 -0
- opencode_pine2pyne-0.2.0/tests/test_pine_semantics.py +446 -0
- opencode_pine2pyne-0.2.0/tests/test_release_artifact_grouping.py +225 -0
- opencode_pine2pyne-0.2.0/tests/test_runtime_floor.py +64 -0
- opencode_pine2pyne-0.2.0/tests/test_scope_model_drift.py +122 -0
- opencode_pine2pyne-0.2.0/tests/test_scope_renamer.py +205 -0
- opencode_pine2pyne-0.2.0/tests/test_script_declaration_binding.py +342 -0
- opencode_pine2pyne-0.2.0/tests/test_shadowing_runtime.py +227 -0
- opencode_pine2pyne-0.2.0/tests/test_strict_and_or_runtime.py +326 -0
- opencode_pine2pyne-0.2.0/tests/test_suite_harness_exit_verdict.py +166 -0
- opencode_pine2pyne-0.2.0/tests/test_suite_report_compare.py +231 -0
- opencode_pine2pyne-0.2.0/tests/test_switch_subject_runtime.py +212 -0
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/tests/test_symbol_table.py +57 -26
- opencode_pine2pyne-0.2.0/tests/test_ta_conditional_runtime.py +310 -0
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/tests/test_transformer.py +229 -182
- opencode_pine2pyne-0.2.0/tests/test_transformer_receiver_typing.py +228 -0
- opencode_pine2pyne-0.2.0/tests/test_transformer_single_use.py +49 -0
- opencode_pine2pyne-0.2.0/tests/test_udt_array_new_runtime.py +103 -0
- opencode_pine2pyne-0.2.0/tests/test_udt_bool_field_default_by_version_runtime.py +105 -0
- opencode_pine2pyne-0.2.0/tests/test_udt_field_defaults_runtime.py +254 -0
- opencode_pine2pyne-0.2.0/tests/test_udt_field_history_runtime.py +94 -0
- opencode_pine2pyne-0.2.0/tests/test_udt_type_proof.py +152 -0
- opencode_pine2pyne-0.2.0/tests/test_untyped_na_runtime.py +154 -0
- opencode_pine2pyne-0.2.0/tests/test_user_declaration_shadows_builtin_runtime.py +416 -0
- opencode_pine2pyne-0.2.0/tests/test_v5_bool_cast_runtime.py +100 -0
- opencode_pine2pyne-0.2.0/tests/test_var_history_runtime.py +463 -0
- opencode_pine2pyne-0.2.0/tests/test_variable_function_runtime.py +118 -0
- opencode_pine2pyne-0.1.1/PKG-INFO +0 -219
- opencode_pine2pyne-0.1.1/README.md +0 -199
- opencode_pine2pyne-0.1.1/opencode_pine2pyne.egg-info/PKG-INFO +0 -219
- opencode_pine2pyne-0.1.1/opencode_pine2pyne.egg-info/SOURCES.txt +0 -37
- opencode_pine2pyne-0.1.1/opencode_pine2pyne.egg-info/requires.txt +0 -3
- opencode_pine2pyne-0.1.1/pine2pyne/transformer.py +0 -2327
- opencode_pine2pyne-0.1.1/tests/test_pine_builtins.py +0 -661
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/LICENSE +0 -0
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/opencode_pine2pyne.egg-info/dependency_links.txt +0 -0
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/opencode_pine2pyne.egg-info/entry_points.txt +0 -0
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/opencode_pine2pyne.egg-info/top_level.txt +0 -0
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/pine2pyne/README.md +0 -0
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/pine2pyne/TRANSPILER_BEST_PRACTICES.md +0 -0
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/pine2pyne/__main__.py +0 -0
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/pine2pyne/lexer.py +0 -0
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/pine2pyne/tokens.py +0 -0
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/setup.cfg +0 -0
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/tests/test_lexer.py +0 -0
- {opencode_pine2pyne-0.1.1 → opencode_pine2pyne-0.2.0}/tests/test_screener_regression.py +0 -0
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: opencode-pine2pyne
|
|
3
|
+
Version: 0.2.0
|
|
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[cli]>=6.9.1; 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/opencode-pine2pyne-setup/setup.sh # from inside this clone; the skill lives in ~/.claude/skills
|
|
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]>=6.9.1' # 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 former P0 — codegen dropping Pine grouping, so `10 - (5 - 3)` became
|
|
111
|
+
`10 - 5 - 3` — is fixed (card #6) and guarded by
|
|
112
|
+
`tests/test_codegen_operator_grouping.py`. 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
|
+
python tools/test_all_samples.py --run-dir /tmp/run1 # private scripts/, output/ and report files (card #70)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
- Source: `sample/pinescript/`, `sample_pds/` · Output: `workdir/scripts/`
|
|
131
|
+
- `--run-dir DIR` keeps a run's transpiled scripts (`DIR/scripts/`), pyne outputs (`DIR/output/`, passed to `pyne`
|
|
132
|
+
as explicit `--plot/--strat/--trade` paths) and `test_results*.{json,txt}` reports in `DIR`, so two runs, or a run
|
|
133
|
+
and an old report, cannot read each other's files. Without it nothing changes: the shared `workdir/` paths and
|
|
134
|
+
the repo-root reports. `--retry-failed` with `--run-dir` reads `DIR`'s own report.
|
|
135
|
+
- Data: `workdir/data/demo.ohlcv` · Results: `test_results{,_pds}.{json,txt}`
|
|
136
|
+
|
|
137
|
+
**Per-sample expectations** live in `tools/sample_expectations.toml`:
|
|
138
|
+
|
|
139
|
+
```toml
|
|
140
|
+
[expds_156_Out_of_bounds_index]
|
|
141
|
+
expect_error = "index_error" # MUST raise; running clean = FAIL
|
|
142
|
+
|
|
143
|
+
[ex_300_ticker_modify]
|
|
144
|
+
requires_feeds = { D = "demo", "2D" = "demo_2D" } # -> --security D=demo …
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
`expect_error` marks Pine documentation samples that exist to *demonstrate* an
|
|
148
|
+
error — they are reported as `XFAIL` and count as passes. `requires_feeds`
|
|
149
|
+
supplies extra OHLCV feeds for samples calling `request.security()` at a
|
|
150
|
+
timeframe other than the chart's; pynecore deliberately will not resample the
|
|
151
|
+
chart feed, so the feed must be explicit. Generate one with:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
cd workdir && pyne data aggregate data/demo.ohlcv -tf 2D
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Shared logic for both suites lives in `tools/_suite_common.py` — edit it there,
|
|
158
|
+
not in each harness.
|
|
159
|
+
|
|
160
|
+
### 3. Ground truth — numeric regression
|
|
161
|
+
|
|
162
|
+
Checks every freshly generated output file (~1,650 across ~550 samples) against
|
|
163
|
+
its recorded digest. This is the only system that catches *silently wrong
|
|
164
|
+
numbers*: output that runs fine but computes something different.
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
python tools/generate_ground_truth.py # (re)build baselines
|
|
168
|
+
python tools/compare_ground_truth.py # check current output vs baselines
|
|
169
|
+
python tools/compare_ground_truth.py --suite sample_pds -j 12
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**The baseline is `ground_truth/digests.json`** (card #62). It holds a sha256 and a
|
|
173
|
+
line count for each output file, plus the sha256 of the data file. Git tracks
|
|
174
|
+
only it and `ground_truth/manifest.json` (under 1 MB together). The output
|
|
175
|
+
CSVs themselves (~920 MB) are gitignored: the generator still copies them to
|
|
176
|
+
`ground_truth/{sample,sample_pds}/` on your machine for diffing. Before writing a
|
|
177
|
+
new baseline, it moves the previous CSVs to `backup/deleteable/ground_truth_csv_<time>/`
|
|
178
|
+
(nothing is deleted; clear old ones out by hand). `manifest.json` lists its samples sorted by
|
|
179
|
+
suite, then filename (card #68), so a one-suite rebuild (`--suite sample`) and a full rebuild on the same data
|
|
180
|
+
give the same samples in the same order, and a rebuild never reorders the tracked file; only `generated_at`
|
|
181
|
+
changes by design.
|
|
182
|
+
|
|
183
|
+
- **Exit codes.** The compare exits 1 when `digests.json` is missing or empty, and
|
|
184
|
+
2 with `DATA CHANGED` when the data file is not the one the baseline was made
|
|
185
|
+
on (restore it, see *Test data* below, or rebaseline).
|
|
186
|
+
- **Every sample runs in its own directory** (card #70): the generator and the compare make one fresh run
|
|
187
|
+
root, `backup/deleteable/ground_truth_runs/<tool>-<time>/` (or an empty `--run-dir DIR`; a directory that already
|
|
188
|
+
holds files is refused), with `<suite>/<sample>/<data file>/` inside it holding the script, its 10B settings and the three
|
|
189
|
+
CSVs, and collect only from there. An earlier run's file of the same name cannot be read as this run's; a run
|
|
190
|
+
that exits 0 and writes nothing is `no_output` (a failure), never `ok` or `NEW PASS`. Run roots are never
|
|
191
|
+
deleted (see the paragraph below on where they go).
|
|
192
|
+
- **Where run roots go, and the disk they use.** The default run root of the generator, the compare and the
|
|
193
|
+
checker (`ground_truth_check.py capture` and `compare` each make one) is
|
|
194
|
+
`backup/deleteable/ground_truth_runs/<tool>-<time>/`, next to the other disposable output, and each run leaves
|
|
195
|
+
one tree of roughly 0.9 GB there (script, settings and three CSVs per sample). A generate run also copies the
|
|
196
|
+
collected files into `ground_truth/<suite>/` (gitignored). Nothing here is deleted by a tool: when you want the
|
|
197
|
+
space back, move old run roots (and the `gt_compare_<time>/` scratch copies) to the USB backup disk, never `rm`
|
|
198
|
+
them. An explicit `--run-dir DIR` is used as given (it must be empty).
|
|
199
|
+
- **A `REGRESSION` line** names the sample and file whose digest changed. To see
|
|
200
|
+
*what* changed, diff that sample's CSV in the run root's `<suite>/<sample>/<data file>/`
|
|
201
|
+
against a baseline copy. That copy is in `ground_truth/<suite>/<sample>/` if you
|
|
202
|
+
generated on the old commit, or, after a later generate, in the newest
|
|
203
|
+
`backup/deleteable/ground_truth_csv_<time>/`.
|
|
204
|
+
- **`--suite sample` or `--suite sample_pds`** rebuilds one suite and keeps the
|
|
205
|
+
other suite's digests and manifest entries. It refuses (exit 1) unless a
|
|
206
|
+
baseline made on the same data already exists.
|
|
207
|
+
- **Expected noise:** 11 `sample_pds` samples call unseeded `math.random()` and
|
|
208
|
+
differ on every run (expds_022, 037, 039, 043, 045, 081, 091, 092, 104, 114,
|
|
209
|
+
115).
|
|
210
|
+
|
|
211
|
+
The old end-to-end shell wrapper is retired (card #66): it read `ground_truth/samples/`,
|
|
212
|
+
`ground_truth/samples_pds/` and `ground_truth/strategies/`, which no tool writes, so its
|
|
213
|
+
`--quick` mode passed with zero checks. Its default and `--samples` modes are now
|
|
214
|
+
`python tools/compare_ground_truth.py` (`--suite sample`, `sample_pds` or `both`; it reads
|
|
215
|
+
`workdir/data/VN30F1M_15m.ohlcv` by default, see *Test data*); its `--quick` and
|
|
216
|
+
`--strategies` modes are `python tools/ground_truth_check.py capture`, then `compare`.
|
|
217
|
+
|
|
218
|
+
`tools/ground_truth_check.py` (`capture` / `compare`) is a separate, local check of the
|
|
219
|
+
bigtest strategies and the sample suites. It writes only to `ground_truth_check/` at the
|
|
220
|
+
repo root (gitignored), never to `ground_truth/`, so it cannot change the tracked baseline
|
|
221
|
+
above. `capture --force` moves the old `ground_truth_check/` to
|
|
222
|
+
`backup/deleteable/ground_truth_check_<time>/` (nothing is deleted) and touches nothing else. Both actions run the
|
|
223
|
+
bigtest strategies and the two suite harnesses (`--run-dir`) in one fresh run root under
|
|
224
|
+
`backup/deleteable/ground_truth_runs/` (`--run-dir DIR` names an empty one), read only that run's files, and fail the capture when a suite wrote no report
|
|
225
|
+
or a bigtest file did not transpile. `compare` writes its
|
|
226
|
+
scratch copy of the current outputs to `backup/deleteable/gt_compare_<time>/` and never deletes it (card #67); each
|
|
227
|
+
run leaves one such folder, which you move to the backup disk when you want the space back. A capture made
|
|
228
|
+
before card #63 sits in `ground_truth/` (`transpiled/`, `strategy_runs/`, `test_suite_*`);
|
|
229
|
+
`compare` no longer reads it and says so. Run `capture` again.
|
|
230
|
+
|
|
231
|
+
> Regenerate baselines only when you have *confirmed* the new output is correct
|
|
232
|
+
> — otherwise a bug gets frozen in as the expected result.
|
|
233
|
+
|
|
234
|
+
Both tools run every sample on `workdir/data/VN30F1M_15m.ohlcv`, with a
|
|
235
|
+
`<script>.toml` that sets `initial_capital` to 10,000,000,000, and with one
|
|
236
|
+
per-sample timeout (120 s). Both values live in `tools/_ground_truth_common.py`.
|
|
237
|
+
At pynecore's default capital of 1,000,000, one VN30F1M contract costs more than
|
|
238
|
+
equity, so nearly every strategy would record zero trades (card #57).
|
|
239
|
+
|
|
240
|
+
### Test data
|
|
241
|
+
|
|
242
|
+
`workdir/data/` is gitignored, so a fresh clone has none and the sample suites
|
|
243
|
+
abort with `Data file not found`. Restore the exact dataset the recorded results
|
|
244
|
+
were measured against:
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
mkdir -p workdir/data
|
|
248
|
+
# Sample suites: CCXT BTCUSDT 1D, plus the 2D/5D feeds `requires_feeds` names
|
|
249
|
+
git show 227f3d2^:workdir/data/demo.ohlcv > workdir/data/demo.ohlcv
|
|
250
|
+
cp tests/fixtures/data/demo.toml workdir/data/demo.toml
|
|
251
|
+
(cd workdir && pyne data aggregate data/demo.ohlcv -tf 2D && pyne data aggregate data/demo.ohlcv -tf 5D)
|
|
252
|
+
# Ground truth: VN30F1M 15m
|
|
253
|
+
git show 5d63d21:workdir/data/VN30F1M_15m.ohlcv > workdir/data/VN30F1M_15m.ohlcv
|
|
254
|
+
cp tests/fixtures/data/VN30F1M_15m.toml workdir/data/VN30F1M_15m.toml
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
The `.ohlcv` files come from those commits (sha256 `67e827cc1a5f71ed…` for
|
|
258
|
+
demo, `e9ef984b09eaa6d2…` for VN30F1M). Take the `.toml` files from
|
|
259
|
+
`tests/fixtures/data/`, not from the commits. The committed ones number weekdays
|
|
260
|
+
1..7, and pynecore rejects day 7 (it numbers 0 = Monday … 6 = Sunday). They also
|
|
261
|
+
declare one session per week, which leaves session-anchored values such as
|
|
262
|
+
`ta.vwap` NaN or weekly (card #57).
|
|
263
|
+
|
|
264
|
+
(`/sandbox-setup` instead seeds a small *synthetic* fixture — fine for a smoke
|
|
265
|
+
test, but results will not match the recorded rates.)
|
|
266
|
+
|
|
267
|
+
## For Claude Code / LLM Development
|
|
268
|
+
|
|
269
|
+
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.
|
|
270
|
+
|
|
271
|
+
## Optimizing Strategies
|
|
272
|
+
|
|
273
|
+
Quick start: `pyne optimize script.py data.ohlcv params.json -n 20`
|
|
274
|
+
|
|
275
|
+
See [Optimizer.md](./Optimizer.md) for full documentation (parameter JSON format, parallel execution, output files).
|
|
276
|
+
|
|
277
|
+
### Distributed Optimization (multi-machine)
|
|
278
|
+
|
|
279
|
+
Two tools distribute `pyne optimize` across SSH clusters:
|
|
280
|
+
|
|
281
|
+
| Tool | Model | Best for |
|
|
282
|
+
|------|-------|----------|
|
|
283
|
+
| `pyne-dynamic.sh` | Flat-queue (on-demand dispatch) | Multi-variant runs, heterogeneous clusters, long jobs |
|
|
284
|
+
| `pyne-parallel.sh` | Static pre-assignment | Quick jobs on similar-speed machines |
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
# Dynamic (recommended): flat-queue, pre-syncs once, auto-adapts to machine speed
|
|
288
|
+
# Run from workdir/ directory:
|
|
289
|
+
../tools/pyne-dynamic.sh scripts/strategy.py data/data.ohlcv optimize_variants/ \
|
|
290
|
+
-H ../tools/machines.txt -C 24 --name my_run --output-dir runs/output/
|
|
291
|
+
|
|
292
|
+
# Static: pre-assigns chunks by core count
|
|
293
|
+
./tools/pyne-parallel.sh scripts/strategy.py data/data.ohlcv optimize.json \
|
|
294
|
+
-H tools/machines.txt --sync
|
|
295
|
+
|
|
296
|
+
# Check progress / collect results
|
|
297
|
+
../tools/pyne-dynamic.sh --status
|
|
298
|
+
../tools/pyne-dynamic.sh --collect
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Both use the same `machines.txt` format. See `CLAUDE.md` for cluster setup, machine file format, and troubleshooting.
|
|
@@ -0,0 +1,281 @@
|
|
|
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/opencode-pine2pyne-setup/setup.sh # from inside this clone; the skill lives in ~/.claude/skills
|
|
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]>=6.9.1' # 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 former P0 — codegen dropping Pine grouping, so `10 - (5 - 3)` became
|
|
91
|
+
`10 - 5 - 3` — is fixed (card #6) and guarded by
|
|
92
|
+
`tests/test_codegen_operator_grouping.py`. 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
|
+
python tools/test_all_samples.py --run-dir /tmp/run1 # private scripts/, output/ and report files (card #70)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
- Source: `sample/pinescript/`, `sample_pds/` · Output: `workdir/scripts/`
|
|
111
|
+
- `--run-dir DIR` keeps a run's transpiled scripts (`DIR/scripts/`), pyne outputs (`DIR/output/`, passed to `pyne`
|
|
112
|
+
as explicit `--plot/--strat/--trade` paths) and `test_results*.{json,txt}` reports in `DIR`, so two runs, or a run
|
|
113
|
+
and an old report, cannot read each other's files. Without it nothing changes: the shared `workdir/` paths and
|
|
114
|
+
the repo-root reports. `--retry-failed` with `--run-dir` reads `DIR`'s own report.
|
|
115
|
+
- Data: `workdir/data/demo.ohlcv` · Results: `test_results{,_pds}.{json,txt}`
|
|
116
|
+
|
|
117
|
+
**Per-sample expectations** live in `tools/sample_expectations.toml`:
|
|
118
|
+
|
|
119
|
+
```toml
|
|
120
|
+
[expds_156_Out_of_bounds_index]
|
|
121
|
+
expect_error = "index_error" # MUST raise; running clean = FAIL
|
|
122
|
+
|
|
123
|
+
[ex_300_ticker_modify]
|
|
124
|
+
requires_feeds = { D = "demo", "2D" = "demo_2D" } # -> --security D=demo …
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
`expect_error` marks Pine documentation samples that exist to *demonstrate* an
|
|
128
|
+
error — they are reported as `XFAIL` and count as passes. `requires_feeds`
|
|
129
|
+
supplies extra OHLCV feeds for samples calling `request.security()` at a
|
|
130
|
+
timeframe other than the chart's; pynecore deliberately will not resample the
|
|
131
|
+
chart feed, so the feed must be explicit. Generate one with:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
cd workdir && pyne data aggregate data/demo.ohlcv -tf 2D
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Shared logic for both suites lives in `tools/_suite_common.py` — edit it there,
|
|
138
|
+
not in each harness.
|
|
139
|
+
|
|
140
|
+
### 3. Ground truth — numeric regression
|
|
141
|
+
|
|
142
|
+
Checks every freshly generated output file (~1,650 across ~550 samples) against
|
|
143
|
+
its recorded digest. This is the only system that catches *silently wrong
|
|
144
|
+
numbers*: output that runs fine but computes something different.
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
python tools/generate_ground_truth.py # (re)build baselines
|
|
148
|
+
python tools/compare_ground_truth.py # check current output vs baselines
|
|
149
|
+
python tools/compare_ground_truth.py --suite sample_pds -j 12
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**The baseline is `ground_truth/digests.json`** (card #62). It holds a sha256 and a
|
|
153
|
+
line count for each output file, plus the sha256 of the data file. Git tracks
|
|
154
|
+
only it and `ground_truth/manifest.json` (under 1 MB together). The output
|
|
155
|
+
CSVs themselves (~920 MB) are gitignored: the generator still copies them to
|
|
156
|
+
`ground_truth/{sample,sample_pds}/` on your machine for diffing. Before writing a
|
|
157
|
+
new baseline, it moves the previous CSVs to `backup/deleteable/ground_truth_csv_<time>/`
|
|
158
|
+
(nothing is deleted; clear old ones out by hand). `manifest.json` lists its samples sorted by
|
|
159
|
+
suite, then filename (card #68), so a one-suite rebuild (`--suite sample`) and a full rebuild on the same data
|
|
160
|
+
give the same samples in the same order, and a rebuild never reorders the tracked file; only `generated_at`
|
|
161
|
+
changes by design.
|
|
162
|
+
|
|
163
|
+
- **Exit codes.** The compare exits 1 when `digests.json` is missing or empty, and
|
|
164
|
+
2 with `DATA CHANGED` when the data file is not the one the baseline was made
|
|
165
|
+
on (restore it, see *Test data* below, or rebaseline).
|
|
166
|
+
- **Every sample runs in its own directory** (card #70): the generator and the compare make one fresh run
|
|
167
|
+
root, `backup/deleteable/ground_truth_runs/<tool>-<time>/` (or an empty `--run-dir DIR`; a directory that already
|
|
168
|
+
holds files is refused), with `<suite>/<sample>/<data file>/` inside it holding the script, its 10B settings and the three
|
|
169
|
+
CSVs, and collect only from there. An earlier run's file of the same name cannot be read as this run's; a run
|
|
170
|
+
that exits 0 and writes nothing is `no_output` (a failure), never `ok` or `NEW PASS`. Run roots are never
|
|
171
|
+
deleted (see the paragraph below on where they go).
|
|
172
|
+
- **Where run roots go, and the disk they use.** The default run root of the generator, the compare and the
|
|
173
|
+
checker (`ground_truth_check.py capture` and `compare` each make one) is
|
|
174
|
+
`backup/deleteable/ground_truth_runs/<tool>-<time>/`, next to the other disposable output, and each run leaves
|
|
175
|
+
one tree of roughly 0.9 GB there (script, settings and three CSVs per sample). A generate run also copies the
|
|
176
|
+
collected files into `ground_truth/<suite>/` (gitignored). Nothing here is deleted by a tool: when you want the
|
|
177
|
+
space back, move old run roots (and the `gt_compare_<time>/` scratch copies) to the USB backup disk, never `rm`
|
|
178
|
+
them. An explicit `--run-dir DIR` is used as given (it must be empty).
|
|
179
|
+
- **A `REGRESSION` line** names the sample and file whose digest changed. To see
|
|
180
|
+
*what* changed, diff that sample's CSV in the run root's `<suite>/<sample>/<data file>/`
|
|
181
|
+
against a baseline copy. That copy is in `ground_truth/<suite>/<sample>/` if you
|
|
182
|
+
generated on the old commit, or, after a later generate, in the newest
|
|
183
|
+
`backup/deleteable/ground_truth_csv_<time>/`.
|
|
184
|
+
- **`--suite sample` or `--suite sample_pds`** rebuilds one suite and keeps the
|
|
185
|
+
other suite's digests and manifest entries. It refuses (exit 1) unless a
|
|
186
|
+
baseline made on the same data already exists.
|
|
187
|
+
- **Expected noise:** 11 `sample_pds` samples call unseeded `math.random()` and
|
|
188
|
+
differ on every run (expds_022, 037, 039, 043, 045, 081, 091, 092, 104, 114,
|
|
189
|
+
115).
|
|
190
|
+
|
|
191
|
+
The old end-to-end shell wrapper is retired (card #66): it read `ground_truth/samples/`,
|
|
192
|
+
`ground_truth/samples_pds/` and `ground_truth/strategies/`, which no tool writes, so its
|
|
193
|
+
`--quick` mode passed with zero checks. Its default and `--samples` modes are now
|
|
194
|
+
`python tools/compare_ground_truth.py` (`--suite sample`, `sample_pds` or `both`; it reads
|
|
195
|
+
`workdir/data/VN30F1M_15m.ohlcv` by default, see *Test data*); its `--quick` and
|
|
196
|
+
`--strategies` modes are `python tools/ground_truth_check.py capture`, then `compare`.
|
|
197
|
+
|
|
198
|
+
`tools/ground_truth_check.py` (`capture` / `compare`) is a separate, local check of the
|
|
199
|
+
bigtest strategies and the sample suites. It writes only to `ground_truth_check/` at the
|
|
200
|
+
repo root (gitignored), never to `ground_truth/`, so it cannot change the tracked baseline
|
|
201
|
+
above. `capture --force` moves the old `ground_truth_check/` to
|
|
202
|
+
`backup/deleteable/ground_truth_check_<time>/` (nothing is deleted) and touches nothing else. Both actions run the
|
|
203
|
+
bigtest strategies and the two suite harnesses (`--run-dir`) in one fresh run root under
|
|
204
|
+
`backup/deleteable/ground_truth_runs/` (`--run-dir DIR` names an empty one), read only that run's files, and fail the capture when a suite wrote no report
|
|
205
|
+
or a bigtest file did not transpile. `compare` writes its
|
|
206
|
+
scratch copy of the current outputs to `backup/deleteable/gt_compare_<time>/` and never deletes it (card #67); each
|
|
207
|
+
run leaves one such folder, which you move to the backup disk when you want the space back. A capture made
|
|
208
|
+
before card #63 sits in `ground_truth/` (`transpiled/`, `strategy_runs/`, `test_suite_*`);
|
|
209
|
+
`compare` no longer reads it and says so. Run `capture` again.
|
|
210
|
+
|
|
211
|
+
> Regenerate baselines only when you have *confirmed* the new output is correct
|
|
212
|
+
> — otherwise a bug gets frozen in as the expected result.
|
|
213
|
+
|
|
214
|
+
Both tools run every sample on `workdir/data/VN30F1M_15m.ohlcv`, with a
|
|
215
|
+
`<script>.toml` that sets `initial_capital` to 10,000,000,000, and with one
|
|
216
|
+
per-sample timeout (120 s). Both values live in `tools/_ground_truth_common.py`.
|
|
217
|
+
At pynecore's default capital of 1,000,000, one VN30F1M contract costs more than
|
|
218
|
+
equity, so nearly every strategy would record zero trades (card #57).
|
|
219
|
+
|
|
220
|
+
### Test data
|
|
221
|
+
|
|
222
|
+
`workdir/data/` is gitignored, so a fresh clone has none and the sample suites
|
|
223
|
+
abort with `Data file not found`. Restore the exact dataset the recorded results
|
|
224
|
+
were measured against:
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
mkdir -p workdir/data
|
|
228
|
+
# Sample suites: CCXT BTCUSDT 1D, plus the 2D/5D feeds `requires_feeds` names
|
|
229
|
+
git show 227f3d2^:workdir/data/demo.ohlcv > workdir/data/demo.ohlcv
|
|
230
|
+
cp tests/fixtures/data/demo.toml workdir/data/demo.toml
|
|
231
|
+
(cd workdir && pyne data aggregate data/demo.ohlcv -tf 2D && pyne data aggregate data/demo.ohlcv -tf 5D)
|
|
232
|
+
# Ground truth: VN30F1M 15m
|
|
233
|
+
git show 5d63d21:workdir/data/VN30F1M_15m.ohlcv > workdir/data/VN30F1M_15m.ohlcv
|
|
234
|
+
cp tests/fixtures/data/VN30F1M_15m.toml workdir/data/VN30F1M_15m.toml
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
The `.ohlcv` files come from those commits (sha256 `67e827cc1a5f71ed…` for
|
|
238
|
+
demo, `e9ef984b09eaa6d2…` for VN30F1M). Take the `.toml` files from
|
|
239
|
+
`tests/fixtures/data/`, not from the commits. The committed ones number weekdays
|
|
240
|
+
1..7, and pynecore rejects day 7 (it numbers 0 = Monday … 6 = Sunday). They also
|
|
241
|
+
declare one session per week, which leaves session-anchored values such as
|
|
242
|
+
`ta.vwap` NaN or weekly (card #57).
|
|
243
|
+
|
|
244
|
+
(`/sandbox-setup` instead seeds a small *synthetic* fixture — fine for a smoke
|
|
245
|
+
test, but results will not match the recorded rates.)
|
|
246
|
+
|
|
247
|
+
## For Claude Code / LLM Development
|
|
248
|
+
|
|
249
|
+
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.
|
|
250
|
+
|
|
251
|
+
## Optimizing Strategies
|
|
252
|
+
|
|
253
|
+
Quick start: `pyne optimize script.py data.ohlcv params.json -n 20`
|
|
254
|
+
|
|
255
|
+
See [Optimizer.md](./Optimizer.md) for full documentation (parameter JSON format, parallel execution, output files).
|
|
256
|
+
|
|
257
|
+
### Distributed Optimization (multi-machine)
|
|
258
|
+
|
|
259
|
+
Two tools distribute `pyne optimize` across SSH clusters:
|
|
260
|
+
|
|
261
|
+
| Tool | Model | Best for |
|
|
262
|
+
|------|-------|----------|
|
|
263
|
+
| `pyne-dynamic.sh` | Flat-queue (on-demand dispatch) | Multi-variant runs, heterogeneous clusters, long jobs |
|
|
264
|
+
| `pyne-parallel.sh` | Static pre-assignment | Quick jobs on similar-speed machines |
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
# Dynamic (recommended): flat-queue, pre-syncs once, auto-adapts to machine speed
|
|
268
|
+
# Run from workdir/ directory:
|
|
269
|
+
../tools/pyne-dynamic.sh scripts/strategy.py data/data.ohlcv optimize_variants/ \
|
|
270
|
+
-H ../tools/machines.txt -C 24 --name my_run --output-dir runs/output/
|
|
271
|
+
|
|
272
|
+
# Static: pre-assigns chunks by core count
|
|
273
|
+
./tools/pyne-parallel.sh scripts/strategy.py data/data.ohlcv optimize.json \
|
|
274
|
+
-H tools/machines.txt --sync
|
|
275
|
+
|
|
276
|
+
# Check progress / collect results
|
|
277
|
+
../tools/pyne-dynamic.sh --status
|
|
278
|
+
../tools/pyne-dynamic.sh --collect
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
Both use the same `machines.txt` format. See `CLAUDE.md` for cluster setup, machine file format, and troubleshooting.
|