Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
22 changes: 22 additions & 0 deletions pineforge_codegen/codegen/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2115,6 +2115,7 @@ def generate(self) -> str:
# request.security helper-call results read at a history offset
# (``myHelper()[k]``). Maps (sec_id, node-id) -> backing Series metadata.
self._security_expr_hist_by_node: dict[tuple[int, int], dict] = {}
self._prepare_lazy_saturated_roc3_sites()

lines: list[str] = []

Expand Down Expand Up @@ -2176,6 +2177,12 @@ def generate(self) -> str:
)
lines.append("")

# Source-shaped lazy ROC call clocks are generated support types, not
# script state themselves. Their per-callsite instances are declared
# below inside GeneratedStrategy and therefore join the automatic COOF
# checkpoint inventory.
self._emit_lazy_saturated_roc3_helper(lines)

# 2. Open class
lines.append("class GeneratedStrategy : public BacktestEngine {")
lines.append("public:")
Expand Down Expand Up @@ -2293,6 +2300,21 @@ def generate(self) -> str:
lines.append(f" std::vector<{vtype}> _precalc_{site.member_name};")
lines.append(" bool _use_precalc = false;")

for clock_name in self._lazy_saturated_roc3_clock_by_node.values():
lines.append(
f" {self._lazy_saturated_roc3_type_name} {clock_name};"
)
if self._lazy_saturated_roc3_clock_by_node:
# Dedicated eager close[3] fallback. Its fixed four-slot capacity
# is independent of the user's max_bars_back directive, which may
# legitimately be smaller than the offset this generated route
# requires. It is ordinary copyable script state and therefore
# joins the automatic COOF checkpoint below.
lines.append(
" Series<double> "
f"{self._lazy_saturated_roc3_history_name}{{4}};"
)

# Security evaluator TA members (cloned from expression dependencies)
# Skip for user function call expressions — their TA deps are internal to the function
for info in self._security_eval_info:
Expand Down
25 changes: 25 additions & 0 deletions pineforge_codegen/codegen/emit_top.py
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,31 @@ def _emit_history_series_write(
def _emit_on_bar(self, lines: list[str]) -> None:
lines.append(" void on_bar(const Bar& bar) override {")

# A GeneratedStrategy handle may execute multiple batch runs or
# streaming lifecycles. BacktestEngine resets broker/base state, but
# generated members survive. Reset source-shaped lazy ROC clocks and
# their forced eager-fallback close history at the first genuine slot
# of each lifecycle. This lives in on_bar rather than a generated run
# wrapper because stream_begin() enters the base run path directly.
# COOF post-close recalculations have history_advances_new_bar()==false
# and therefore preserve the current committed clock/base.
if self._lazy_saturated_roc3_clock_by_node:
lines.append(
" if (history_advances_new_bar() && bar_index_ == 0) {"
)
for clock_name in self._lazy_saturated_roc3_clock_by_node.values():
lines.append(f" {clock_name}.reset();")
lines.append(
f" {self._lazy_saturated_roc3_history_name}.clear();"
)
lines.append(" }")
self._emit_history_series_write(
lines,
" ",
self._lazy_saturated_roc3_history_name,
"current_bar_.close",
)

# reset_run_state() owns engine/broker state, while these generated
# Series members belong to the strategy object. Clear all of them on
# the first genuine history slot of bar zero, unconditionally: a site
Expand Down
Loading
Loading