programgarden 2.7.3__tar.gz → 3.0.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.
Files changed (83) hide show
  1. {programgarden-2.7.3 → programgarden-3.0.0}/PKG-INFO +5 -134
  2. {programgarden-2.7.3 → programgarden-3.0.0}/README.md +1 -130
  3. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/__init__.py +5 -0
  4. programgarden-3.0.0/programgarden/_build_info.py +3 -0
  5. programgarden-3.0.0/programgarden/code_worker.py +3 -0
  6. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/context.py +4 -4
  7. programgarden-3.0.0/programgarden/database/broker_evidence.py +52 -0
  8. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/workflow_position_tracker.py +2 -2
  9. programgarden-3.0.0/programgarden/deep_fixtures.py +46 -0
  10. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/executor.py +325 -45
  11. programgarden-3.0.0/programgarden/replay_contracts.py +7 -0
  12. programgarden-3.0.0/programgarden/replay_semantics.py +3 -0
  13. programgarden-3.0.0/programgarden/replay_triggers.py +3 -0
  14. programgarden-3.0.0/programgarden/semantic_rules.py +12 -0
  15. {programgarden-2.7.3 → programgarden-3.0.0}/pyproject.toml +4 -4
  16. programgarden-2.7.3/programgarden/code_worker.py +0 -499
  17. programgarden-2.7.3/programgarden/database/broker_evidence.py +0 -189
  18. programgarden-2.7.3/programgarden/deep_fixtures.py +0 -561
  19. programgarden-2.7.3/programgarden/incremental_build.py +0 -430
  20. programgarden-2.7.3/programgarden/replay_contracts.py +0 -261
  21. programgarden-2.7.3/programgarden/replay_events.py +0 -196
  22. programgarden-2.7.3/programgarden/replay_external.py +0 -279
  23. programgarden-2.7.3/programgarden/replay_order_adapter.py +0 -291
  24. programgarden-2.7.3/programgarden/replay_orders.py +0 -359
  25. programgarden-2.7.3/programgarden/replay_responders.py +0 -148
  26. programgarden-2.7.3/programgarden/replay_scenarios.py +0 -127
  27. programgarden-2.7.3/programgarden/replay_semantics.py +0 -674
  28. programgarden-2.7.3/programgarden/replay_sources.py +0 -161
  29. programgarden-2.7.3/programgarden/replay_sqlite.py +0 -53
  30. programgarden-2.7.3/programgarden/replay_tape.py +0 -182
  31. programgarden-2.7.3/programgarden/replay_triggers.py +0 -76
  32. programgarden-2.7.3/programgarden/semantic_rules.py +0 -501
  33. programgarden-2.7.3/programgarden/validation_replay.py +0 -492
  34. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/binding_provenance.py +0 -0
  35. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/binding_validator.py +0 -0
  36. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/client.py +0 -0
  37. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/__init__.py +0 -0
  38. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/adjustment_delivery.py +0 -0
  39. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/broker_order_totals.py +0 -0
  40. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/broker_snapshot.py +0 -0
  41. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/checkpoint_manager.py +0 -0
  42. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/db_naming.py +0 -0
  43. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/execution_storage.py +0 -0
  44. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/fill_reconciler.py +0 -0
  45. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/order_cancellation.py +0 -0
  46. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/order_recovery.py +0 -0
  47. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/owned_order_cancellation.py +0 -0
  48. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/position_reconciliation.py +0 -0
  49. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/query_builder.py +0 -0
  50. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/sqlite_scope.py +0 -0
  51. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/workflow_risk_tracker.py +0 -0
  52. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/futures_contract_info.py +0 -0
  53. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/futures_exchange.py +0 -0
  54. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/futures_history.py +0 -0
  55. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/futures_orderable.py +0 -0
  56. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/futures_pnl.py +0 -0
  57. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/futures_read_evidence.py +0 -0
  58. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/managed_order_control.py +0 -0
  59. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/node_runner.py +0 -0
  60. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/order_lifecycle.py +0 -0
  61. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/paper_futures_business_date.py +0 -0
  62. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/plugin/__init__.py +0 -0
  63. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/plugin/sandbox.py +0 -0
  64. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/providers/__init__.py +0 -0
  65. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/providers/llm_errors.py +0 -0
  66. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/providers/llm_provider.py +0 -0
  67. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/ranking_nodes.py +0 -0
  68. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/reconnect_handler.py +0 -0
  69. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/resolver.py +0 -0
  70. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/resource/__init__.py +0 -0
  71. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/resource/context.py +0 -0
  72. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/resource/limiter.py +0 -0
  73. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/resource/monitor.py +0 -0
  74. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/resource/throttle.py +0 -0
  75. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/tools/__init__.py +0 -0
  76. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/tools/credential_tools.py +0 -0
  77. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/tools/definition_tools.py +0 -0
  78. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/tools/event_tools.py +0 -0
  79. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/tools/job_tools.py +0 -0
  80. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/tools/registry_tools.py +0 -0
  81. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/tools/sqlite_tools.py +0 -0
  82. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/validation_recommender.py +0 -0
  83. {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/valuation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: programgarden
3
- Version: 2.7.3
3
+ Version: 3.0.0
4
4
  Summary: ProgramGarden - 노드 기반 자동매매 DSL 실행 엔진
5
5
  License-Expression: AGPL-3.0-or-later
6
6
  Author: 프로그램동산
@@ -15,9 +15,9 @@ Requires-Dist: aiosqlite (>=0.20.0,<0.21.0)
15
15
  Requires-Dist: croniter (>=6.0.0,<7.0.0)
16
16
  Requires-Dist: litellm (>=1.40.0)
17
17
  Requires-Dist: lxml (>=6.0.2,<7.0.0)
18
- Requires-Dist: programgarden-community (>=2.3.2,<3.0.0)
19
- Requires-Dist: programgarden-core (>=2.7.0,<3.0.0)
20
- Requires-Dist: programgarden-finance (>=2.0.6,<3.0.0)
18
+ Requires-Dist: programgarden-community (==3.0.0)
19
+ Requires-Dist: programgarden-core (==3.0.0)
20
+ Requires-Dist: programgarden-finance (==3.0.0)
21
21
  Requires-Dist: psutil (>=6.0.0,<7.0.0)
22
22
  Requires-Dist: psycopg2-binary (>=2.9.11,<3.0.0)
23
23
  Requires-Dist: pydantic (>=2.0.1,<3.0.0)
@@ -27,58 +27,8 @@ Requires-Dist: tzdata (>=2025.2,<2026.0)
27
27
  Requires-Dist: yfinance (>=0.2.0,<0.3.0)
28
28
  Description-Content-Type: text/markdown
29
29
 
30
- The 2.6.2 release candidate [futures minute alias correction](../../docs/futures-minute-alias-contract.md) aligns canonical minute periods with the existing one-page o3103 path and rejects unsupported hourly/adjustment claims. Complete-range coverage and consumer adoption are not implied.
31
-
32
30
  # ProgramGarden
33
31
 
34
- The 2.6.7 release [futures TC2 confirmation handling](../../docs/release-2.6.7-validation.md)
35
- requires programgarden-finance 2.0.5, which reads overseas futures modify and
36
- cancel confirmations (TC2) as LS sends them and re-registers account events
37
- after a reconnect. The engine finds the workflow's order through
38
- `orgn_ordr_no`, uses the guide's side codes and skips frames the SDK could
39
- not read.
40
-
41
- The 2.6.6 release candidate [ranking nodes and runtime-values-as-data correction](../../docs/release-2.6.6-validation.md)
42
- adds KoreaStockRankingNode and OverseasStockRankingNode, evaluates only
43
- expression templates authored in the workflow, routes Gemini and DeepSeek
44
- credentials to their own APIs, and reads an unresolved futures position
45
- exchange again at most once per 5 minutes per contract instead of keeping it
46
- empty for the whole run.
47
-
48
- The 2.6.5 release candidate [futures position exchange correction](../../docs/futures-position-exchange.md)
49
- reads each futures position's and pending order's exchange from o3105 instead
50
- of a fixed `HKEX`; an unconfirmed exchange stays empty.
51
-
52
- The 2.6.4 release candidate [paper futures account correction](../../docs/paper-futures-business-date.md#account-node-and-rejection-messages-264-candidate)
53
- returns an empty futures account for the paper empty-account answer and names
54
- the broker message in unaccepted response errors. Live behavior is unchanged.
55
-
56
- The 2.6.3 release candidate [paper futures business-date correction](../../docs/paper-futures-business-date.md)
57
- uses a fresh account-scoped broker date for startup pending orders, OpenOrdersNode
58
- and legacy fill observation. Missing or mismatched evidence remains unavailable.
59
-
60
- An unreleased [stock history interval correction](../../docs/stock-history-interval-contract.md)
61
- rejects unsupported intraday periods instead of silently querying daily bars.
62
- Supported g3204 periods retain their existing requests and output contract.
63
-
64
- An unreleased [futures detail and durable state candidate](../../docs/futures-contract-details-and-rollover-state.md)
65
- adds exact o3105 metadata, opt-in execution-scoped SQLite and XHKG calendar
66
- intersection. Missing evidence remains explicit; this does not authorize orders.
67
-
68
- An unreleased [offline temporal replay extension](../../docs/offline-temporal-replay.md)
69
- supports explicit bounded multi-day acceptance without changing the default
70
- 32-event worker/save path. Public package release remains pending; the server
71
- team has validated a private DEV candidate with the same runtime changes.
72
-
73
- The REST overseas-stock account output preserves broker-reported `sellable_qty`
74
- separately from holdings; absent capacity remains null. See
75
- [the sell-capacity contract](../../docs/overseas-stock-sellable-quantity.md).
76
-
77
- Unreleased execution reconciliation work is documented in
78
- [execution-reconciliation.md](../../docs/execution-reconciliation.md). Its
79
- optional host storage identity is not yet enabled in deployed worker/tray hosts;
80
- historical recovery policy and host migration remain open.
81
-
82
32
  ProgramGarden은 AI 시대에 맞춰 파이썬을 모르는 투자자도 개인화된 시스템 트레이딩을 자동으로 수행할 수 있게 돕는 노드 기반 자동매매 DSL(Domain Specific Language) 오픈소스입니다.
83
33
 
84
34
  노드를 조합하여 워크플로우를 정의하고, 실행 엔진이 이를 자동으로 처리합니다. LS증권 OpenAPI를 메인으로 해외 주식/선물 거래 자동화를 지원합니다.
@@ -155,14 +105,6 @@ pending count and cancels those tasks, allowing another 0.25 seconds for cancell
155
105
  A listener that suppresses cancellation is reported and remains tracked; it cannot
156
106
  hold job shutdown indefinitely. Delivery beyond this bounded grace is not guaranteed.
157
107
 
158
- Overseas-stock OpenOrdersNode treats the observed terminal COSAQ00102 `02679`
159
- empty envelope as `open_orders=[]` / `count=0` without an error. The echoed date,
160
- market and filters must match its current-day pending query, the empty detail
161
- array must be explicitly present, and continuation must be terminal. This follows
162
- the finance package's observed-response reference and StockAccountTracker;
163
- `02679` is not a generic success code. Missing, malformed, mismatched and failed
164
- reads retain `error` / `reason=fetch_failed` so workflows can block new entries.
165
-
166
108
  Overseas-stock historical queries preserve the exchange supplied by each symbol,
167
109
  including when the account has no holdings. Item-bound consumers of an explicitly
168
110
  empty selected upstream list are skipped before expression evaluation; this also
@@ -185,7 +127,7 @@ the catalog's `result` port as a list of outcome rows: `{{ nodes.order.result }}
185
127
  can feed TableDisplayNode directly. Each row copies the final `order_result`
186
128
  and includes `order_id` from the existing top-level or nested order number.
187
129
  Legacy `order_result` and `order_id` outputs are unchanged. The projection occurs
188
- after fill confirmation, fractional-remainder annotation, and replay marking;
130
+ after fill confirmation, and fractional-remainder annotation;
189
131
  an accepted order is not promoted to a fill without confirmation. Rejections and
190
132
  `no_signal` keep their original diagnostics/reason as an outcome row, with no
191
133
  fabricated order number. Dry runs add a `simulated` row while retaining the legacy
@@ -195,73 +137,6 @@ of a new-order result retains its legacy `order_result` row shape; explicit
195
137
  `nodes.order.result` bindings always read the list. Modify/cancel nodes retain their
196
138
  separately declared `modify_result`/`cancel_result` ports and are outside this change.
197
139
 
198
- ## Explicit execution identities in local ledgers
199
-
200
- `WorkflowPositionTracker.record_fill(..., execution_id=...)` can preserve a
201
- broker-provided execution identity. Within one ledger/product/provider/mode,
202
- order date and normalized order number, an identical replay returns the first
203
- classification without changing FIFO or history. Conflicting facts raise
204
- `ExecutionIdentityConflictError`. Positive numeric identifiers ignore padding;
205
- opaque identifiers retain case. Missing, blank and zero identifiers retain legacy
206
- behavior and are never inferred from time, price or quantity. Every pending
207
- partial fill is retained while its order acknowledgement is being recorded.
208
-
209
- The migration is additive and does not invent identities for existing rows.
210
- Callers must establish consistent identifier and timestamp semantics first;
211
- exchange execution numbers and broker execution numbers are not interchangeable.
212
- This ledger API alone does not reconcile finite workflows after their shutdown.
213
-
214
- Standalone futures TC3 callbacks interpret `s_b_ccd="1"` as sell and `"2"` as buy,
215
- matching the SDK contract. TC2 confirmations use the same codes and find the
216
- workflow's order through `orgn_ordr_no`: a futures modify or cancel gets its own
217
- new order number (LS paper, 2026-10-15), so the cancel confirmation cancels the
218
- original order in the ledger and a modify confirmation updates the successor row
219
- recorded by ModifyOrderNodeExecutor. Unknown or blank side codes are logged and skipped
220
- before any inventory write is scheduled. With an app order lifecycle handler,
221
- TC3 still bypasses this legacy fill path: canonical REST reconciliation remains
222
- the sole writer. This side correction does not establish an alias between TC3
223
- and REST execution identities, dates, or times.
224
-
225
- ## Futures monetary evidence
226
-
227
- Futures workflow PnL events preserve native gross estimates separately from
228
- accounting results. `workflow_*`, `other_*`, `total_*`, account and competition
229
- monetary/rate scalars remain null when accounting evidence is unavailable.
230
- `pnl_by_currency` contains gross price-change subtotals by contract currency;
231
- `monetary_positions` retains their basis/status and unmodified, unconfirmed
232
- `broker_pnl_amount`. No fee, FX, margin/equity return or verified contest score
233
- is inferred from these estimates. Actual zero and negative amounts are retained.
234
- `currency` is null for mixed or unavailable currencies. Consumers must handle
235
- nullable monetary fields and must not coerce them to zero.
236
-
237
- ## Personal workflow execution metrics
238
-
239
- `WorkflowPositionTracker.personal_metrics()` reads retained SQLite `trade_history`
240
- for the current product, provider and paper/live mode. Its independent
241
- `WorkflowPnLEvent.personal_metrics` envelope retains stock realized PnL after all
242
- positions close and counts distinct positive executed orders by valid stored
243
- order date and normalized order number. Partial fills of one dated order count
244
- once. This is the reporting runtime's retained workflow ledger, not a complete
245
- account history or verified contest score. No credential/account generation is
246
- claimed by this legacy local storage.
247
-
248
- Stored stock realized PnL is gross long-only FIFO before fees. The reader
249
- validates sufficient recorded buys and consistency with stored results; mixed
250
- manual/product/provider/exchange FIFO ownership makes the affected symbol
251
- unavailable. Amounts remain separate by symbol/exchange, with currency null
252
- because the historical table has no currency evidence. No FX conversion or return
253
- denominator is invented. Futures executed-order counts are available under the
254
- same identity rules, but futures monetary results and portfolio MDD remain null:
255
- the retained FIFO lacks contract accounting and risk price windows are not equity
256
- curves. Invalid order identities also yield a null count with its reason.
257
-
258
- The listener refreshes this evidence at most once per 10 seconds to avoid scanning
259
- retained history on every price tick; reused observations retain their original
260
- `as_of`. The cache is isolated to the actual tracker/product/provider/mode, and
261
- other broker-node callbacks do not receive it. A newly recorded fill may appear
262
- at the next refresh. Existing open-position PnL fields are unchanged. Core and
263
- engine consumers must ship together to support the additive event field.
264
-
265
140
  ## Stock read failures
266
141
 
267
142
  Stock open-order queries include current-day orders (`ThdayBnsAppYn="1"`).
@@ -365,7 +240,3 @@ job = await pg.run_async(
365
240
 
366
241
  자세한 변경 사항은 `CHANGELOG.md`를 참고하세요.
367
242
 
368
- See [Session gates and guarded Split](../../docs/session-gates-and-guarded-split.md)
369
- for immediate timezone decisions, per-item conditional routing, effective sequential
370
- execution of guarded/order branches, and finite CodeNode input bindings.
371
-
@@ -1,55 +1,5 @@
1
- The 2.6.2 release candidate [futures minute alias correction](../../docs/futures-minute-alias-contract.md) aligns canonical minute periods with the existing one-page o3103 path and rejects unsupported hourly/adjustment claims. Complete-range coverage and consumer adoption are not implied.
2
-
3
1
  # ProgramGarden
4
2
 
5
- The 2.6.7 release [futures TC2 confirmation handling](../../docs/release-2.6.7-validation.md)
6
- requires programgarden-finance 2.0.5, which reads overseas futures modify and
7
- cancel confirmations (TC2) as LS sends them and re-registers account events
8
- after a reconnect. The engine finds the workflow's order through
9
- `orgn_ordr_no`, uses the guide's side codes and skips frames the SDK could
10
- not read.
11
-
12
- The 2.6.6 release candidate [ranking nodes and runtime-values-as-data correction](../../docs/release-2.6.6-validation.md)
13
- adds KoreaStockRankingNode and OverseasStockRankingNode, evaluates only
14
- expression templates authored in the workflow, routes Gemini and DeepSeek
15
- credentials to their own APIs, and reads an unresolved futures position
16
- exchange again at most once per 5 minutes per contract instead of keeping it
17
- empty for the whole run.
18
-
19
- The 2.6.5 release candidate [futures position exchange correction](../../docs/futures-position-exchange.md)
20
- reads each futures position's and pending order's exchange from o3105 instead
21
- of a fixed `HKEX`; an unconfirmed exchange stays empty.
22
-
23
- The 2.6.4 release candidate [paper futures account correction](../../docs/paper-futures-business-date.md#account-node-and-rejection-messages-264-candidate)
24
- returns an empty futures account for the paper empty-account answer and names
25
- the broker message in unaccepted response errors. Live behavior is unchanged.
26
-
27
- The 2.6.3 release candidate [paper futures business-date correction](../../docs/paper-futures-business-date.md)
28
- uses a fresh account-scoped broker date for startup pending orders, OpenOrdersNode
29
- and legacy fill observation. Missing or mismatched evidence remains unavailable.
30
-
31
- An unreleased [stock history interval correction](../../docs/stock-history-interval-contract.md)
32
- rejects unsupported intraday periods instead of silently querying daily bars.
33
- Supported g3204 periods retain their existing requests and output contract.
34
-
35
- An unreleased [futures detail and durable state candidate](../../docs/futures-contract-details-and-rollover-state.md)
36
- adds exact o3105 metadata, opt-in execution-scoped SQLite and XHKG calendar
37
- intersection. Missing evidence remains explicit; this does not authorize orders.
38
-
39
- An unreleased [offline temporal replay extension](../../docs/offline-temporal-replay.md)
40
- supports explicit bounded multi-day acceptance without changing the default
41
- 32-event worker/save path. Public package release remains pending; the server
42
- team has validated a private DEV candidate with the same runtime changes.
43
-
44
- The REST overseas-stock account output preserves broker-reported `sellable_qty`
45
- separately from holdings; absent capacity remains null. See
46
- [the sell-capacity contract](../../docs/overseas-stock-sellable-quantity.md).
47
-
48
- Unreleased execution reconciliation work is documented in
49
- [execution-reconciliation.md](../../docs/execution-reconciliation.md). Its
50
- optional host storage identity is not yet enabled in deployed worker/tray hosts;
51
- historical recovery policy and host migration remain open.
52
-
53
3
  ProgramGarden은 AI 시대에 맞춰 파이썬을 모르는 투자자도 개인화된 시스템 트레이딩을 자동으로 수행할 수 있게 돕는 노드 기반 자동매매 DSL(Domain Specific Language) 오픈소스입니다.
54
4
 
55
5
  노드를 조합하여 워크플로우를 정의하고, 실행 엔진이 이를 자동으로 처리합니다. LS증권 OpenAPI를 메인으로 해외 주식/선물 거래 자동화를 지원합니다.
@@ -126,14 +76,6 @@ pending count and cancels those tasks, allowing another 0.25 seconds for cancell
126
76
  A listener that suppresses cancellation is reported and remains tracked; it cannot
127
77
  hold job shutdown indefinitely. Delivery beyond this bounded grace is not guaranteed.
128
78
 
129
- Overseas-stock OpenOrdersNode treats the observed terminal COSAQ00102 `02679`
130
- empty envelope as `open_orders=[]` / `count=0` without an error. The echoed date,
131
- market and filters must match its current-day pending query, the empty detail
132
- array must be explicitly present, and continuation must be terminal. This follows
133
- the finance package's observed-response reference and StockAccountTracker;
134
- `02679` is not a generic success code. Missing, malformed, mismatched and failed
135
- reads retain `error` / `reason=fetch_failed` so workflows can block new entries.
136
-
137
79
  Overseas-stock historical queries preserve the exchange supplied by each symbol,
138
80
  including when the account has no holdings. Item-bound consumers of an explicitly
139
81
  empty selected upstream list are skipped before expression evaluation; this also
@@ -156,7 +98,7 @@ the catalog's `result` port as a list of outcome rows: `{{ nodes.order.result }}
156
98
  can feed TableDisplayNode directly. Each row copies the final `order_result`
157
99
  and includes `order_id` from the existing top-level or nested order number.
158
100
  Legacy `order_result` and `order_id` outputs are unchanged. The projection occurs
159
- after fill confirmation, fractional-remainder annotation, and replay marking;
101
+ after fill confirmation, and fractional-remainder annotation;
160
102
  an accepted order is not promoted to a fill without confirmation. Rejections and
161
103
  `no_signal` keep their original diagnostics/reason as an outcome row, with no
162
104
  fabricated order number. Dry runs add a `simulated` row while retaining the legacy
@@ -166,73 +108,6 @@ of a new-order result retains its legacy `order_result` row shape; explicit
166
108
  `nodes.order.result` bindings always read the list. Modify/cancel nodes retain their
167
109
  separately declared `modify_result`/`cancel_result` ports and are outside this change.
168
110
 
169
- ## Explicit execution identities in local ledgers
170
-
171
- `WorkflowPositionTracker.record_fill(..., execution_id=...)` can preserve a
172
- broker-provided execution identity. Within one ledger/product/provider/mode,
173
- order date and normalized order number, an identical replay returns the first
174
- classification without changing FIFO or history. Conflicting facts raise
175
- `ExecutionIdentityConflictError`. Positive numeric identifiers ignore padding;
176
- opaque identifiers retain case. Missing, blank and zero identifiers retain legacy
177
- behavior and are never inferred from time, price or quantity. Every pending
178
- partial fill is retained while its order acknowledgement is being recorded.
179
-
180
- The migration is additive and does not invent identities for existing rows.
181
- Callers must establish consistent identifier and timestamp semantics first;
182
- exchange execution numbers and broker execution numbers are not interchangeable.
183
- This ledger API alone does not reconcile finite workflows after their shutdown.
184
-
185
- Standalone futures TC3 callbacks interpret `s_b_ccd="1"` as sell and `"2"` as buy,
186
- matching the SDK contract. TC2 confirmations use the same codes and find the
187
- workflow's order through `orgn_ordr_no`: a futures modify or cancel gets its own
188
- new order number (LS paper, 2026-10-15), so the cancel confirmation cancels the
189
- original order in the ledger and a modify confirmation updates the successor row
190
- recorded by ModifyOrderNodeExecutor. Unknown or blank side codes are logged and skipped
191
- before any inventory write is scheduled. With an app order lifecycle handler,
192
- TC3 still bypasses this legacy fill path: canonical REST reconciliation remains
193
- the sole writer. This side correction does not establish an alias between TC3
194
- and REST execution identities, dates, or times.
195
-
196
- ## Futures monetary evidence
197
-
198
- Futures workflow PnL events preserve native gross estimates separately from
199
- accounting results. `workflow_*`, `other_*`, `total_*`, account and competition
200
- monetary/rate scalars remain null when accounting evidence is unavailable.
201
- `pnl_by_currency` contains gross price-change subtotals by contract currency;
202
- `monetary_positions` retains their basis/status and unmodified, unconfirmed
203
- `broker_pnl_amount`. No fee, FX, margin/equity return or verified contest score
204
- is inferred from these estimates. Actual zero and negative amounts are retained.
205
- `currency` is null for mixed or unavailable currencies. Consumers must handle
206
- nullable monetary fields and must not coerce them to zero.
207
-
208
- ## Personal workflow execution metrics
209
-
210
- `WorkflowPositionTracker.personal_metrics()` reads retained SQLite `trade_history`
211
- for the current product, provider and paper/live mode. Its independent
212
- `WorkflowPnLEvent.personal_metrics` envelope retains stock realized PnL after all
213
- positions close and counts distinct positive executed orders by valid stored
214
- order date and normalized order number. Partial fills of one dated order count
215
- once. This is the reporting runtime's retained workflow ledger, not a complete
216
- account history or verified contest score. No credential/account generation is
217
- claimed by this legacy local storage.
218
-
219
- Stored stock realized PnL is gross long-only FIFO before fees. The reader
220
- validates sufficient recorded buys and consistency with stored results; mixed
221
- manual/product/provider/exchange FIFO ownership makes the affected symbol
222
- unavailable. Amounts remain separate by symbol/exchange, with currency null
223
- because the historical table has no currency evidence. No FX conversion or return
224
- denominator is invented. Futures executed-order counts are available under the
225
- same identity rules, but futures monetary results and portfolio MDD remain null:
226
- the retained FIFO lacks contract accounting and risk price windows are not equity
227
- curves. Invalid order identities also yield a null count with its reason.
228
-
229
- The listener refreshes this evidence at most once per 10 seconds to avoid scanning
230
- retained history on every price tick; reused observations retain their original
231
- `as_of`. The cache is isolated to the actual tracker/product/provider/mode, and
232
- other broker-node callbacks do not receive it. A newly recorded fill may appear
233
- at the next refresh. Existing open-position PnL fields are unchanged. Core and
234
- engine consumers must ship together to support the additive event field.
235
-
236
111
  ## Stock read failures
237
112
 
238
113
  Stock open-order queries include current-day orders (`ThdayBnsAppYn="1"`).
@@ -335,7 +210,3 @@ job = await pg.run_async(
335
210
  ## 변경 로그
336
211
 
337
212
  자세한 변경 사항은 `CHANGELOG.md`를 참고하세요.
338
-
339
- See [Session gates and guarded Split](../../docs/session-gates-and-guarded-split.md)
340
- for immediate timezone decisions, per-item conditional routing, effective sequential
341
- execution of guarded/order branches, and finite CodeNode input bindings.
@@ -99,7 +99,12 @@ try: # 설치된 배포판
99
99
  __version__ = _pkg_version("programgarden")
100
100
  except Exception: # 소스 트리에서 직접 import (미설치)
101
101
  __version__ = "0.0.0+source"
102
+
103
+ from programgarden._build_info import build_info # noqa: E402
104
+
102
105
  __all__ = [
106
+ # Build provenance
107
+ "build_info",
103
108
  # Core
104
109
  "ProgramGarden",
105
110
  "WorkflowResolver",
@@ -0,0 +1,3 @@
1
+ """Public stub of the build-provenance module (no internal build stamp)."""
2
+ def build_info():
3
+ return {"version": "3.0.0", "build": "public"}
@@ -0,0 +1,3 @@
1
+ """Public stub: the CodeNode sandbox worker is platform-only."""
2
+ def run_code_node_sandboxed(*args, **kwargs):
3
+ raise RuntimeError("CodeNode runs on the ProgramGarden platform only.")
@@ -48,7 +48,7 @@ logger = logging.getLogger("programgarden.context")
48
48
  # 체결시각을 브로커 프레임이 싣지 않았을 때 **원장에만** 쓰는 표식.
49
49
  #
50
50
  # 서버로 나가는 OrderFillEvent.fill_time 은 이때 빈 문자열이어야 한다 — 소비자
51
- # (pg-worker app/order_fill_updates.py)가 `order_date + fill_time` 을 시장 세션
51
+ # (호스팅 플랫폼의 체결 소비자)가 `order_date + fill_time` 을 시장 세션
52
52
  # 타임존으로 읽어 executed_at 을 **계산된 체결시각**으로 확정하기 때문이다. 값이
53
53
  # 없는데 무언가를 채우면 그게 체결시각으로 승격된다(엔진 <= v1.36.0 이
54
54
  # `datetime.now().strftime('%H%M%S000')` 를 합성하던 자리가 정확히 그 사고였다).
@@ -214,7 +214,7 @@ class ExecutionContext:
214
214
  self.position_adjustments: List[Dict[str, Any]] = []
215
215
  self.context_params = context_params or {}
216
216
 
217
- # Opt-in LS token provider (Verified League §3.2.3). When set, broker
217
+ # Opt-in LS token provider. When set, broker
218
218
  # logins consume a server-issued token instead of self-issuing.
219
219
  self.ls_token_provider = ls_token_provider
220
220
  # Runtime-only capabilities and acceptance guards never enter DSL state
@@ -2958,7 +2958,7 @@ class ExecutionContext:
2958
2958
  quantity: 체결 수량
2959
2959
  price: 체결 가격
2960
2960
  fill_time: 체결시각 (HHMMSSsss). 프레임이 체결시각을 싣지 않았으면
2961
- **빈 문자열**을 넘긴다 — 값을 합성하면 하류(pg-worker)가 그걸
2961
+ **빈 문자열**을 넘긴다 — 값을 합성하면 하류(호스팅 플랫폼)가 그걸
2962
2962
  거래소 체결시각으로 승격시킨다. 원장 쪽 표기는 이 메서드가
2963
2963
  UNKNOWN_FILL_TIME 으로 바꿔 넣고, 서버로 나가는 이벤트에서는
2964
2964
  다시 빈 문자열로 되돌린다(_on_tracker_fill_classified).
@@ -3137,7 +3137,7 @@ class ExecutionContext:
3137
3137
  quantity=fill.quantity,
3138
3138
  price=fill.price,
3139
3139
  # 원장 전용 표식은 서버로 내보내지 않는다 — 빈 fill_time 이어야
3140
- # pg-worker 의 `and fill_time` 가드가 걸려 executed_at 이 정직한
3140
+ # 호스팅 플랫폼의 `and fill_time` 가드가 걸려 executed_at 이 정직한
3141
3141
  # received_at 폴백(+ executed_at_source 태그)이 된다.
3142
3142
  fill_time=("" if fill.fill_time == UNKNOWN_FILL_TIME else fill.fill_time),
3143
3143
  product=tracker.product,
@@ -0,0 +1,52 @@
1
+ """Public skeleton of the broker startup-evidence gate. Performs the generic checks
2
+ only: transport success, a complete (not continued) response, and a standard success
3
+ response code. The platform's hardened evidence checks and its special-case handling of
4
+ edge responses are not part of the open-source build; such responses fall back to the
5
+ generic startup-evidence error."""
6
+ import inspect
7
+
8
+ PAPER_FUTURES_NO_RESULT_MESSAGES = frozenset()
9
+ ORDDT_ECHO_WINDOW_DAYS = 7
10
+ _SUCCESS_CODES = frozenset({"00000", "00136"})
11
+
12
+
13
+ class StartupEvidenceUnavailable(RuntimeError):
14
+ pass
15
+
16
+
17
+ class StartupQueryDateMismatch(StartupEvidenceUnavailable):
18
+ pass
19
+
20
+
21
+ def broker_message(value):
22
+ return "" if value is None else str(value)[:80]
23
+
24
+
25
+ def unverified_response_code(tr, code, message):
26
+ return f"{tr}: unverified_response_code {code} {broker_message(message)}".rstrip()
27
+
28
+
29
+ def _header_matches(tr, tr_cd):
30
+ tr_cd = str(tr_cd or "")
31
+ return tr_cd == tr or (len(tr_cd) >= 5 and tr.startswith(tr_cd))
32
+
33
+
34
+ async def checked_body(response, tr, request=None, *, paper_trading=False):
35
+ if getattr(response, "status_code", None) != 200 or getattr(response, "error_msg", None):
36
+ raise StartupEvidenceUnavailable(f"{tr}: transport_failure")
37
+ header = getattr(response, "header", None)
38
+ if (header is None or not _header_matches(tr, getattr(header, "tr_cd", None))
39
+ or getattr(header, "tr_cont", None) not in {"N", "0"}
40
+ or str(getattr(header, "tr_cont_key", "") or "").strip()):
41
+ raise StartupEvidenceUnavailable(f"{tr}: incomplete_continuation")
42
+ raw = getattr(response, "raw_data", None)
43
+ body = raw.json() if raw is not None else None
44
+ if inspect.isawaitable(body):
45
+ body = await body
46
+ if not isinstance(body, dict):
47
+ raise StartupEvidenceUnavailable(f"{tr}: missing_response_blocks")
48
+ code = getattr(response, "rsp_cd", None)
49
+ if code not in _SUCCESS_CODES:
50
+ raise StartupEvidenceUnavailable(
51
+ unverified_response_code(tr, code, getattr(response, "rsp_msg", None)))
52
+ return body
@@ -1041,7 +1041,7 @@ class WorkflowPositionTracker:
1041
1041
  list) instead of being emitted here — the caller emits it via
1042
1042
  ``_emit_fill_notifications`` AFTER releasing ``_buffer_lock``. Emitting
1043
1043
  under the lock would await the listener chain (a network POST in the
1044
- pg-worker forwarder, ``request_timeout`` seconds) while holding
1044
+ hosting platform's forwarder, ``request_timeout`` seconds) while holding
1045
1045
  ``_buffer_lock``, serializing every other broker fill push behind it.
1046
1046
  Replays return early and queue nothing (no re-fire)."""
1047
1047
  fill_datetime = f"{fill.order_date}_{fill.fill_time}"
@@ -2128,7 +2128,7 @@ class WorkflowPositionTracker:
2128
2128
  "workflow_pnl_rate": wf_rate,
2129
2129
  # 비율을 못 낸 **이유**. 값을 비우는 것만으로는 "안 샀다" 와 "증권사가 평단을
2130
2130
  # 안 보냈다" 가 구분되지 않는다 — 서버는 이 사유를 raw_event 에 그대로 싣는다.
2131
- # 어휘는 dsl-api `app/utils/pnl_rate_evidence.RATE_UNAVAILABLE_REASONS` 와 공용.
2131
+ # 어휘는 플랫폼의 수익률 미산출 사유 어휘와 공용.
2132
2132
  "workflow_rate_unavailable_reason": wf_rate_reason,
2133
2133
  "workflow_eval_amount": wf_eval,
2134
2134
  "workflow_buy_amount": wf_buy,
@@ -0,0 +1,46 @@
1
+ """Public fixture helpers for dry-run. Deep validation is platform-only; this module
2
+ provides only minimal, generic, shape-correct placeholder data so a public dry-run
3
+ completes without broker calls. It contains no recorded broker responses and no
4
+ curated fixture content."""
5
+
6
+
7
+ def apply_override(default, override=None):
8
+ merged = dict(default or {})
9
+ if isinstance(override, dict):
10
+ merged.update(override)
11
+ return merged
12
+
13
+
14
+ def futures_contract_fixture(config, *, now=None):
15
+ cfg = config or {}
16
+ exchange = str(cfg.get("exchange") or "")
17
+ contracts = []
18
+ for product in cfg.get("base_products") or []:
19
+ code = str(product)
20
+ if code:
21
+ contracts.append({"symbol": code, "exchange": exchange, "base_product": code,
22
+ "base_product_name": code, "name": code, "contract_month": ""})
23
+ return {"symbols": [{"exchange": c["exchange"], "symbol": c["symbol"]} for c in contracts],
24
+ "contracts": contracts, "count": len(contracts)}
25
+
26
+
27
+ def _placeholder(schema):
28
+ if not isinstance(schema, dict):
29
+ return None
30
+ kind = schema.get("type")
31
+ if kind == "object":
32
+ return {key: _placeholder(sub) for key, sub in (schema.get("properties") or {}).items()}
33
+ return {"array": [], "string": "", "integer": 0, "number": 0.0, "boolean": False}.get(kind)
34
+
35
+
36
+ def ai_agent_fixture(config):
37
+ cfg = config or {}
38
+ if cfg.get("output_format") == "structured":
39
+ return {"response": _placeholder(cfg.get("output_schema")) or {}}
40
+ return {"response": ""}
41
+
42
+
43
+ def __getattr__(name):
44
+ def _unavailable(*args, **kwargs):
45
+ raise NotImplementedError("Deep validation runs on the ProgramGarden platform only.")
46
+ return _unavailable