Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
57857a5
feat(cr150): declare report sink ABI and line writer contracts
luisleo526 Oct 18, 2026
c0c879c
feat(cr150): declare global-index TradeLedger contract
luisleo526 Oct 18, 2026
f5d3aba
feat(cr150): declare metric accumulator and point source contracts
luisleo526 Oct 18, 2026
d0fd21c
feat(cr150): declare bar views and document reader inventory
luisleo526 Oct 18, 2026
a9b0866
test(cr150): compile the P00 contract headers
luisleo526 Oct 18, 2026
54acd1c
docs(cr150): satisfy the dangling comment-name guard
luisleo526 Oct 18, 2026
131852f
docs(cr150): anchor the contract map's source citations
luisleo526 Oct 18, 2026
25e4491
test(cr150): keep the CMakeLists line pins valid
luisleo526 Oct 18, 2026
9f01fbc
docs(cr150): cite the final rev 6 lines
luisleo526 Oct 18, 2026
a6abb14
feat(cr150): resolve contract OPEN items S1 and T1
luisleo526 Oct 18, 2026
ee3083d
feat(cr150): resolve contract OPEN items V1 and V2
luisleo526 Oct 18, 2026
72e34d4
cr150 L15a: bar views staging, accessor and guard (no caller yet) (#418)
luisleo526 Oct 18, 2026
0faaf06
cr150 L8a: TradeLedger container swap (no streaming yet) (#419)
luisleo526 Oct 18, 2026
0157156
cr150: .bin bar file contract + streaming run_json.py csv2bin convert…
luisleo526 Oct 18, 2026
d3eab70
cr150 L6a: report sink format in isolation (line writer, f64, escaper…
luisleo526 Oct 18, 2026
23834ff
cr150 L16 (#424)
luisleo526 Oct 18, 2026
9464717
cr150 L8b: three avg_*_percent prefix sums from sealed trade rows (#417)
luisleo526 Oct 18, 2026
2d08085
cr150 L7b (#423)
luisleo526 Oct 18, 2026
69fdd0e
cr150 L7a (#422)
luisleo526 Oct 18, 2026
06734b1
cr150 L0b (#421)
luisleo526 Oct 18, 2026
5be8c54
cr150 CMKD: one CMake file per lane (tests/cr150.d) (#438)
luisleo526 Oct 19, 2026
437bc33
cr150 L19c (#426)
luisleo526 Oct 19, 2026
ace7189
cr150 L19d (#428)
luisleo526 Oct 19, 2026
5e167bd
cr150 L8d (#425)
luisleo526 Oct 19, 2026
abfa69f
cr150 L19a (#427)
luisleo526 Oct 19, 2026
5184ea6
cr150 L3: store-once chart (#434)
luisleo526 Oct 19, 2026
4014671
cr150 L4: chart streaming over the store-once view (#437)
luisleo526 Oct 19, 2026
09a8a45
cr150 CMKD2: fenced per-lane blocks after the cr150.d include loop (#…
luisleo526 Oct 19, 2026
39a718b
cr150 L6b-1: report sink, R and D records (#431)
luisleo526 Oct 19, 2026
4d168c0
CR-150 L17a (#432)
luisleo526 Oct 19, 2026
2fff630
CR-150 BATCH2: L19e + L19h + L15b (#441)
luisleo526 Oct 19, 2026
0d05bfa
CR-150 L9a (#442)
luisleo526 Oct 19, 2026
366489c
CR-150 L19b (#430)
luisleo526 Oct 19, 2026
abe9899
CR-150 L6b-3 (#436)
luisleo526 Oct 19, 2026
76e2310
CR-150 L6b-2 (#435)
luisleo526 Oct 19, 2026
b3c3923
CR-150 L19g (#447)
luisleo526 Oct 19, 2026
e19d1fc
cr150 l19lb: executor result for CR-150 lane l19lb (supervisor commit)
luisleo526 Oct 19, 2026
9a4089e
cr150 l19lc: executor result for CR-150 lane l19lc (supervisor commit)
luisleo526 Oct 19, 2026
5706d2b
cr150 l9c: executor result for CR-150 lane l9c (supervisor commit)
luisleo526 Oct 19, 2026
64e8f37
cr150 l19l: executor result for CR-150 lane l19l (supervisor commit)
luisleo526 Oct 19, 2026
a7f57ca
Merge part B (kernel cohort rosters on disk) into L19l part A
luisleo526 Oct 19, 2026
dd16af0
Merge part C (PlacementTable auxiliary tables on disk) into L19l part…
luisleo526 Oct 19, 2026
bca0a33
cr150 l9c: portable report sink (named temp file, per-reader offsets)…
luisleo526 Oct 19, 2026
1b8cd36
cr150 l19m: exit-phase store on disk, release empty cohort maps and r…
luisleo526 Oct 19, 2026
9b17ce6
cr150 l19m: merge L19l head dd16af0f53 (one id-tables binding, both t…
luisleo526 Oct 19, 2026
30a6e60
cr150 L19f: K5 in script-bar space (F0b) (#433)
luisleo526 Oct 19, 2026
f7ca40f
cr150 l19n: kernel calendar memo and certified-cycle ring reserve the…
luisleo526 Oct 19, 2026
ccbe647
Merge integration 30a6e6062 into the lane head
luisleo526 Oct 19, 2026
cb95997
Merge integration 30a6e6062 into the lane head
luisleo526 Oct 19, 2026
5c3be4c
Merge integration 30a6e6062 into the lane head
luisleo526 Oct 19, 2026
f704339
cr150 l9c: per-run canary log for full and subset sweeps, canary also…
luisleo526 Oct 19, 2026
9224095
cr150 L2: run_json load path (--ohlcv-bin, .bin feeds, chart, --verif…
luisleo526 Oct 19, 2026
e61f785
Merge integration 92240958 into the lane head
luisleo526 Oct 19, 2026
25964e7
Merge integration 92240958 into the lane head
luisleo526 Oct 19, 2026
0d065c3
cr150 l19l+l19m: round-2 fixes: exit-phase store and kernel roster st…
luisleo526 Oct 19, 2026
5300823
Merge integration 92240958 into the lane head
luisleo526 Oct 19, 2026
5022017
cr150 lane proof (squash of the lane branch on the integration head 9…
luisleo526 Oct 20, 2026
1692cce
Merge integration 5022017c into the lane head
luisleo526 Oct 20, 2026
9d0f01f
Merge integration 5022017c into the lane head
luisleo526 Oct 20, 2026
01d568f
Merge integration 5022017c into the lane head
luisleo526 Oct 20, 2026
e203081
docs: re-anchor file:line citations after merging integration 5022017c
luisleo526 Oct 20, 2026
89c7605
cr150 l19l: reversal cancels exits of every known cohort id again, no…
luisleo526 Oct 20, 2026
c12c220
Merge lane l9c into batch-a
luisleo526 Oct 20, 2026
9c2cb12
Merge lane l19o into batch-a
luisleo526 Oct 20, 2026
d7c6729
Merge lane l19n into batch-a
luisleo526 Oct 20, 2026
a35a64c
CR-150 batch A: L9c + L19o + L19n (integration) (#457)
luisleo526 Oct 20, 2026
fcd2b52
scripts: the corpus parity subset step runs a second, sink-on pass an…
luisleo526 Oct 20, 2026
ac01f8b
Merge integration a35a64c2 into the lane head
luisleo526 Oct 20, 2026
a41b26b
Merge core/cr150-canary (CX-CANARY-WIRE fcd2b529b) into the integrati…
luisleo526 Oct 20, 2026
2754739
tests: drop four trailing commas that made test_corpus_subset_sink.py…
luisleo526 Oct 20, 2026
d91c96d
CR-150 route (ii) foundation: the fixed scratch slab, strategy_set_sc…
luisleo526 Oct 20, 2026
c7e6bcb
CR-150 route (ii) host lane: default scratch manifest, run_json jaile…
luisleo526 Oct 20, 2026
cef780f
CR-150 route (ii) id-pair lane: q store removed, fixed-form bounded-m…
luisleo526 Oct 20, 2026
947ab71
CR-150 route (ii) foundation, fix 1: satisfy the preflight guards tha…
luisleo526 Oct 20, 2026
830c8c6
CR-150 route (ii) roots lane: bounded-memory successor-root index, bo…
luisleo526 Oct 20, 2026
519e148
CR-150 route (ii) tree lane: bounded-memory backend for the cohort ta…
luisleo526 Oct 20, 2026
120aacc
CR-150 route (ii) foundation, fix 2: the foundation-owned hunks the b…
luisleo526 Oct 20, 2026
18433f7
Merge branch 'cx/ramroots' into cx/ramint
luisleo526 Oct 20, 2026
65b2329
Merge branch 'cx/ramhost' into cx/ramint
luisleo526 Oct 20, 2026
6defae2
Merge branch 'cx/ramidpair' into cx/ramint
luisleo526 Oct 20, 2026
d1fbc16
Merge branch 'cx/ramtree' into cx/ramint
luisleo526 Oct 20, 2026
d0bece7
CR-150 route (ii) integration, fix 1: run-failure-code guard, declara…
luisleo526 Oct 20, 2026
d64f858
ramint fix round 2: correct the two RED fixtures, not the production …
luisleo526 Oct 20, 2026
6cfcff0
ramint fix round 3: corrupt-cohort witness forces the real spill befo…
luisleo526 Oct 20, 2026
993ff10
Merge pull request #465 from pineforge-4pass/cx/ramint
luisleo526 Oct 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,7 @@ scripts/
tutorial/
.playwright-mcp/
node_modules/

# Frozen Python report producer used only by the L9A comparison tests.
tests/fixtures/run_json_frozen_pre_l9a.py
tests/fixtures/run_json_frozen_pre_l9a.sha256
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,11 @@ set(PINEFORGE_KERNEL_SOURCES
src/timeframe.cpp
src/timezone.cpp
src/native_calendar.cpp
src/report_sink_writer.cpp
src/sha256.cpp
src/native_run_spec.cpp
src/native_fx_curve.cpp
src/native_bar_views.cpp
src/selected_report_view.cpp
src/selected_window_plan.cpp
)
Expand Down
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ The separate PineForge compiler, [`pineforge-codegen`](https://github.com/pinefo
- **Open runtime.** The engine and native live runner are Apache-2.0. The separately distributed [PineForge compiler](https://github.com/pineforge-4pass/pineforge-codegen-oss/blob/main/LICENSE) is source-available under the PineForge Source License 1.2 from its release 1.3.0 (release 1.2.0 keeps the PineForge Source License 1.1, and releases up to 1.1.0 keep the PolyForm Noncommercial terms they shipped with); personal trading is free, and commercial use requires a separate license; the capital in an account a proprietary-trading firm or funded-trader program provides or allocates, including a challenge, evaluation or simulated account, is investment capital. Public reference strategies, benchmarks and validation tooling are available in their respective repositories; the community-script test set is not redistributed.
- **Fast.** In-process, no interpreter: median **36× faster than PyneCore** on the 200 strategies both engines time (100 corpus probes and 100 held-out private strategies), measured at engine `35db01c8` on 2026-09-29 on an AWS c7a.8xlarge with PyneCore timed as a subprocess, its interpreter start-up included (PineForge runs a median 807k bars/s per strategy over its 201 slots with the bar magnifier on; [method](benchmarks/results/speed.md)). Parameter sweeps re-run a loaded `.so` with new inputs — no recompile, no fork.
- **Deterministic to the bit.** Two runs with the same inputs produce identical trade lists. Same on Linux and macOS.
- **Yours to embed.** 145 `PF_API` declarations across five installed headers — `pineforge.h`'s 94 (74 of the 80 runtime exports, the other six being declared in `execution_observer.h` and `selected_window.h`, three each, and up to 20 per-strategy functions a generated module defines), `native_c_api.h`'s 43 native-host declarations and `selected_window_plan.h`'s two planner declarations — an ABI that is append-only within a major version from 1.0 on. Call it from C, Python, Rust, Go, Node, Julia — or let an AI agent drive it over MCP.
- **Yours to embed.** 150 `PF_API` declarations across six installed headers — `pineforge.h`'s 96 (76 of the 85 runtime exports, the other nine being declared in `execution_observer.h`, `selected_window.h` and `report_sink.h`, three each, and up to 20 per-strategy functions a generated module defines), `native_c_api.h`'s 43 native-host declarations and `selected_window_plan.h`'s two planner declarations — an ABI that is append-only within a major version from 1.0 on. Call it from C, Python, Rust, Go, Node, Julia — or let an AI agent drive it over MCP.

Recorded outputs and their additional API declarations described here are
part of **1.5.0**.
Expand Down Expand Up @@ -517,13 +517,13 @@ API, the script ABI epoch and the pairing with codegen — is the
[public contract](docs/pages/public-contract.md); what a 0.x user must act on
is in [CHANGELOG.md](CHANGELOG.md).

The counts below include recorded outputs and describe the released 1.5.0 API.
The counts below describe the current API, including recorded outputs, the report sink and scratch-table selection; recorded outputs are not part of 1.4.0.

A newly generated strategy `.so` exposes 96 compiled-strategy `PF_API` declarations
(80 runtime implementations plus sixteen generated exports) plus 43 native-host
declarations: 139 `PF_API` exports in total; `use_bar_magnifier = true` adds
A newly generated strategy `.so` exposes 101 compiled-strategy `PF_API` declarations
(85 runtime implementations plus sixteen generated exports) plus 43 native-host
declarations: 144 `PF_API` exports in total; `use_bar_magnifier = true` adds
`strategy_declares_bar_magnifier` and a module that records [outputs](docs/outputs.md) its
three outputs exports, bringing the inventories to at most 100 and 143. Older modules lack the six opt-in
three outputs exports, bringing the inventories to at most 105 and 148. Older modules lack the six opt-in
[checked-settings exports](docs/checked-settings.md). Older modules can also lack
the two opt-in [execution-capability exports](docs/strategy-capabilities.md).
In an optimized build `nm -gU` also shows libc++'s
Expand All @@ -548,13 +548,14 @@ inventories are pinned by `scripts/check_c_abi_runtime.py`:
| `strategy_set_chart_timezone` / `strategy_set_syminfo_timezone` / `strategy_set_syminfo_session` | Chart and exchange time |
| `strategy_set_syminfo_mintick` / `_pointvalue` / `_metadata` / `_type` / `_string` | Symbol tick size, point value, numeric metadata, instrument class, string members |
| `strategy_set_native_security_feed` / `strategy_set_aux_security_feed` | Feed `request.security()` from a native higher-timeframe series / an auxiliary bar-aligned feed |
| `strategy_set_symbol_feed` / `_feed_column` / `strategy_set_symbol_facts` / `strategy_set_recorded_series` | Another symbol's data for `request.security()` of that symbol: its own bars (each with its close) and named columns, its `syminfo.*` facts, and recorded request values per chart bar (each behind its own `PINEFORGE_HAS_…_V1` probe; historical runs only) |
| `strategy_set_symbol_feed` / `strategy_set_symbol_feed_borrowed_v1` / `_feed_column` / `strategy_set_symbol_facts` / `strategy_set_recorded_series` | Another symbol's data for `request.security()` of that symbol: its own copied or borrowed bars (each with its close) and named columns, its `syminfo.*` facts, and recorded request values per chart bar (each behind its own `PINEFORGE_HAS_…_V1` probe; historical runs only) |
| `strategy_set_account_currency_fx_series` | Effective-time quote-to-account FX |
| `strategy_get_last_error` | The latest runtime error |
| `strategy_get_last_error_code` / `strategy_get_last_error_args` | The latest failure's stable code from the closed [run-failure vocabulary](docs/pages/run-failure-codes.md) (`""` when the run succeeded) and its typed arguments as canonical JSON; behind `PINEFORGE_HAS_RUN_FAILURE_CODES_V1` |
| `pf_version_get` / `pf_version_string` / `pf_abi_version` | Runtime version, version string, struct-layout version (`PF_ABI_VERSION == 4`) |
| `strategy_execution_contract` / `strategy_configure_native_v1` / `strategy_configure_native_fx_curve_v1` / `_fx_curve_ext_v1` | Query Legacy vs NativeMarketV1; apply the versioned native run specification (an invalid or refused base call returns `-1` and leaves the handle Failed; a cooperative `Aborted` handle may be reused with the same key and a higher run number); stage or clear an immutable native FX curve, `_fx_curve_ext_v1` also writing the typed refusal (`pf_native_fx_curve_error_e`) and the offending point's index |
| `strategy_request_abort` / `strategy_last_run_status` | Cooperative abort of a run in progress; `0`=completed, `1`=aborted |
| `strategy_set_scratch_tables_v1` | Copy disk-scratch or bounded-memory table policy and its eleven admission maxima while idle; NULL restores disk defaults (since 1.7.0) |
| `strategy_set_realtime_tail` | Live-runtime surface (ABI v4): the array's last bar is a still-forming tail — `barstate.islast=false`, `last_bar_index`/`last_bar_time` frozen at the horizon bar, no range-end row |
| `strategy_set_probe_suppress_tail_logic` | ABI v4: the last bar runs only the broker's pre-`on_bar` steps (pending-order settlement, intraday-cap/loss checks) and returns — no `on_bar`, no margin-call / `process_orders_on_close` second pass / bracket-reissue processing (the range-end row is `strategy_set_realtime_tail`'s to skip; the flags are independent) |
| `strategy_set_path_order` / `strategy_last_bar_dual_entry_path` | ABI v4: force the intrabar O→H/L→C leg order (`AUTO`/`HIGH_FIRST`/`LOW_FIRST`) for path-dependent fill probing; read which side won a same-bar dual-entry-stop arbitration |
Expand Down
65 changes: 46 additions & 19 deletions benchmarks/throughput/grid_search_repro.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,14 @@
import ctypes
import csv
import itertools
import sys
from pathlib import Path

# Share the host's descriptor and policy arithmetic, including typed refusals.
sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "docker"))
from run_json import (RunFailure, _bind_scratch_tables, _check_bench_run,
apply_scratch_tables, default_scratch_manifest, failure_line)

class pf_bar_t(ctypes.Structure):
_fields_ = [
("open", ctypes.c_double),
Expand Down Expand Up @@ -192,6 +198,16 @@ def main():
lib.report_free.argtypes = [ctypes.POINTER(pf_report_t)]
lib.report_free.restype = None

_bind_scratch_tables(lib)
for name in ("strategy_get_last_error", "strategy_get_last_error_code",
"strategy_get_last_error_args"):
if hasattr(lib, name):
getattr(lib, name).argtypes = [ctypes.c_void_p]
getattr(lib, name).restype = ctypes.c_char_p
if hasattr(lib, "strategy_last_run_status"):
lib.strategy_last_run_status.argtypes = [ctypes.c_void_p]
lib.strategy_last_run_status.restype = ctypes.c_int

# Load CSV
bars_list = []
print(f"Loading CSV data from {csv_path.name}...")
Expand Down Expand Up @@ -230,29 +246,40 @@ def main():
print("Failed to create strategy instance!")
continue

# Set inputs
lib.strategy_set_input(s, b"Fast EMA", str(fast).encode())
lib.strategy_set_input(s, b"Slow EMA", str(slow).encode())
lib.strategy_set_input(s, b"Take profit (ticks)", str(tp).encode())

report = pf_report_t()
lib.run_backtest(s, bars_array, n, ctypes.byref(report))

profit = report.net_profit
trades = report.total_trades

print(f"Params: Fast_EMA={fast}, Slow_EMA={slow}, TP_ticks={tp} | Net Profit = {profit:.2f} | Trades = {trades}")

if profit > best_profit:
best_profit = profit
best_params = (fast, slow, tp)

lib.report_free(ctypes.byref(report))
lib.strategy_free(s)
try:
lib.strategy_set_input(s, b"Fast EMA", str(fast).encode())
lib.strategy_set_input(s, b"Slow EMA", str(slow).encode())
lib.strategy_set_input(s, b"Take profit (ticks)", str(tp).encode())

# P0 policy for THIS trial, after its overrides/feed selection.
# A fresh handle/run receives fresh occupancy, never its predecessor's.
manifest = default_scratch_manifest(n)
apply_scratch_tables(lib, s, manifest)
lib.run_backtest(s, bars_array, n, ctypes.byref(report))
_check_bench_run(lib, s) # a failed trial cannot enter the ranking

profit = report.net_profit
trades = report.total_trades

print(f"Params: Fast_EMA={fast}, Slow_EMA={slow}, TP_ticks={tp} | Net Profit = {profit:.2f} | Trades = {trades}")

if profit > best_profit:
best_profit = profit
best_params = (fast, slow, tp)
finally:
try:
lib.report_free(ctypes.byref(report))
finally:
lib.strategy_free(s)

print("\n=== OPTIMIZATION COMPLETE ===")
print(f"Best Profit: {best_profit:.2f}")
print(f"Best Parameters: Fast EMA = {best_params[0]}, Slow EMA = {best_params[1]}, Take profit (ticks) = {best_params[2]}")

if __name__ == "__main__":
main()
try:
main()
except RunFailure as failure:
sys.stderr.write(failure_line(str(failure), failure.code, failure.code_args))
sys.exit(failure.exit_status)
Loading
Loading