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.
- {programgarden-2.7.3 → programgarden-3.0.0}/PKG-INFO +5 -134
- {programgarden-2.7.3 → programgarden-3.0.0}/README.md +1 -130
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/__init__.py +5 -0
- programgarden-3.0.0/programgarden/_build_info.py +3 -0
- programgarden-3.0.0/programgarden/code_worker.py +3 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/context.py +4 -4
- programgarden-3.0.0/programgarden/database/broker_evidence.py +52 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/workflow_position_tracker.py +2 -2
- programgarden-3.0.0/programgarden/deep_fixtures.py +46 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/executor.py +325 -45
- programgarden-3.0.0/programgarden/replay_contracts.py +7 -0
- programgarden-3.0.0/programgarden/replay_semantics.py +3 -0
- programgarden-3.0.0/programgarden/replay_triggers.py +3 -0
- programgarden-3.0.0/programgarden/semantic_rules.py +12 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/pyproject.toml +4 -4
- programgarden-2.7.3/programgarden/code_worker.py +0 -499
- programgarden-2.7.3/programgarden/database/broker_evidence.py +0 -189
- programgarden-2.7.3/programgarden/deep_fixtures.py +0 -561
- programgarden-2.7.3/programgarden/incremental_build.py +0 -430
- programgarden-2.7.3/programgarden/replay_contracts.py +0 -261
- programgarden-2.7.3/programgarden/replay_events.py +0 -196
- programgarden-2.7.3/programgarden/replay_external.py +0 -279
- programgarden-2.7.3/programgarden/replay_order_adapter.py +0 -291
- programgarden-2.7.3/programgarden/replay_orders.py +0 -359
- programgarden-2.7.3/programgarden/replay_responders.py +0 -148
- programgarden-2.7.3/programgarden/replay_scenarios.py +0 -127
- programgarden-2.7.3/programgarden/replay_semantics.py +0 -674
- programgarden-2.7.3/programgarden/replay_sources.py +0 -161
- programgarden-2.7.3/programgarden/replay_sqlite.py +0 -53
- programgarden-2.7.3/programgarden/replay_tape.py +0 -182
- programgarden-2.7.3/programgarden/replay_triggers.py +0 -76
- programgarden-2.7.3/programgarden/semantic_rules.py +0 -501
- programgarden-2.7.3/programgarden/validation_replay.py +0 -492
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/binding_provenance.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/binding_validator.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/client.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/__init__.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/adjustment_delivery.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/broker_order_totals.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/broker_snapshot.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/checkpoint_manager.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/db_naming.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/execution_storage.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/fill_reconciler.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/order_cancellation.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/order_recovery.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/owned_order_cancellation.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/position_reconciliation.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/query_builder.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/sqlite_scope.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/workflow_risk_tracker.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/futures_contract_info.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/futures_exchange.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/futures_history.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/futures_orderable.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/futures_pnl.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/futures_read_evidence.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/managed_order_control.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/node_runner.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/order_lifecycle.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/paper_futures_business_date.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/plugin/__init__.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/plugin/sandbox.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/providers/__init__.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/providers/llm_errors.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/providers/llm_provider.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/ranking_nodes.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/reconnect_handler.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/resolver.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/resource/__init__.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/resource/context.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/resource/limiter.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/resource/monitor.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/resource/throttle.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/tools/__init__.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/tools/credential_tools.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/tools/definition_tools.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/tools/event_tools.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/tools/job_tools.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/tools/registry_tools.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/tools/sqlite_tools.py +0 -0
- {programgarden-2.7.3 → programgarden-3.0.0}/programgarden/validation_recommender.py +0 -0
- {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:
|
|
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 (
|
|
19
|
-
Requires-Dist: programgarden-core (
|
|
20
|
-
Requires-Dist: programgarden-finance (
|
|
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
|
|
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
|
|
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",
|
|
@@ -48,7 +48,7 @@ logger = logging.getLogger("programgarden.context")
|
|
|
48
48
|
# 체결시각을 브로커 프레임이 싣지 않았을 때 **원장에만** 쓰는 표식.
|
|
49
49
|
#
|
|
50
50
|
# 서버로 나가는 OrderFillEvent.fill_time 은 이때 빈 문자열이어야 한다 — 소비자
|
|
51
|
-
# (
|
|
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
|
|
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
|
-
**빈 문자열**을 넘긴다 — 값을 합성하면 하류(
|
|
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
|
-
#
|
|
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
|
{programgarden-2.7.3 → programgarden-3.0.0}/programgarden/database/workflow_position_tracker.py
RENAMED
|
@@ -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
|
-
|
|
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
|
-
# 어휘는
|
|
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
|