programgarden 1.37.2__tar.gz → 1.37.3__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 (39) hide show
  1. {programgarden-1.37.2 → programgarden-1.37.3}/PKG-INFO +1 -1
  2. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/executor.py +64 -6
  3. {programgarden-1.37.2 → programgarden-1.37.3}/pyproject.toml +1 -1
  4. {programgarden-1.37.2 → programgarden-1.37.3}/README.md +0 -0
  5. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/__init__.py +0 -0
  6. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/binding_validator.py +0 -0
  7. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/client.py +0 -0
  8. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/code_worker.py +0 -0
  9. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/context.py +0 -0
  10. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/database/__init__.py +0 -0
  11. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/database/checkpoint_manager.py +0 -0
  12. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/database/query_builder.py +0 -0
  13. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/database/workflow_position_tracker.py +0 -0
  14. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/database/workflow_risk_tracker.py +0 -0
  15. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/deep_fixtures.py +0 -0
  16. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/futures_pnl.py +0 -0
  17. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/node_runner.py +0 -0
  18. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/order_lifecycle.py +0 -0
  19. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/plugin/__init__.py +0 -0
  20. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/plugin/sandbox.py +0 -0
  21. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/providers/__init__.py +0 -0
  22. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/providers/llm_errors.py +0 -0
  23. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/providers/llm_provider.py +0 -0
  24. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/reconnect_handler.py +0 -0
  25. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/resolver.py +0 -0
  26. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/resource/__init__.py +0 -0
  27. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/resource/context.py +0 -0
  28. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/resource/limiter.py +0 -0
  29. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/resource/monitor.py +0 -0
  30. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/resource/throttle.py +0 -0
  31. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/semantic_rules.py +0 -0
  32. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/tools/__init__.py +0 -0
  33. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/tools/credential_tools.py +0 -0
  34. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/tools/definition_tools.py +0 -0
  35. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/tools/event_tools.py +0 -0
  36. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/tools/job_tools.py +0 -0
  37. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/tools/registry_tools.py +0 -0
  38. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/tools/sqlite_tools.py +0 -0
  39. {programgarden-1.37.2 → programgarden-1.37.3}/programgarden/validation_recommender.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: programgarden
3
- Version: 1.37.2
3
+ Version: 1.37.3
4
4
  Summary: ProgramGarden - 노드 기반 자동매매 DSL 실행 엔진
5
5
  License-Expression: AGPL-3.0-or-later
6
6
  Author: 프로그램동산
@@ -15523,7 +15523,7 @@ class NewOrderNodeExecutor(NodeExecutorBase):
15523
15523
  # 을 낸 정상 무신호 날이 매 실행 노드 FAILED + 사용자 알림으로 승격된다
15524
15524
  # (D2, 벤치 2026-09-06 이 없애려던 바로 그 증상). 그래서 분류를 먼저
15525
15525
  # 돌리고, **확실한 배선 고장**(`hard`)일 때만 NO_SYMBOL 로 승격한다.
15526
- unfilled = self._describe_unfilled_order_fields(order, context, node_type)
15526
+ unfilled = self._describe_unfilled_order_fields(order, context, node_type, node_id)
15527
15527
  reason, detail = self._diagnose_empty_reason(
15528
15528
  order, config, raw_order_expr, context, node_id=node_id
15529
15529
  )
@@ -15912,6 +15912,7 @@ class NewOrderNodeExecutor(NodeExecutorBase):
15912
15912
  order: Any,
15913
15913
  context: Optional["ExecutionContext"] = None,
15914
15914
  node_type: str = "",
15915
+ node_id: Optional[str] = None,
15915
15916
  ) -> Optional[Dict[str, Any]]:
15916
15917
  """주문 입력이 **채워지지 않아서** 주문을 못 만든 경우의 구체 사유.
15917
15918
 
@@ -15990,9 +15991,42 @@ class NewOrderNodeExecutor(NodeExecutorBase):
15990
15991
  # 수량이 0/음수인 것만으로는 승격하지 않는다 — 자금 부족·소수점 잔량 등
15991
15992
  # **정상적으로 오늘 못 사는 날**이 있고, 그건 `_diagnose_empty_reason` 이
15992
15993
  # no_signal / fractional_only 로 더 정확히 분류한다.
15993
- hard = item_shape is not None or any(
15994
- kind in ("unresolved_template", "invalid") for kind in kinds.values()
15995
- )
15994
+ # 🔴 2026-09-14 prod 실관측(엔진 1.37.2 첫 판단) — 거짓 경보 교정.
15995
+ # 매수 노드가 `{{ item.symbol }}` 을 쓰는데 상류(SymbolFilter)가 **정상적으로**
15996
+ # 빈 목록을 냈다(관심종목을 이미 보유 → 오늘 살 것 없음). 반복이 아예 일어나지
15997
+ # 않아 템플릿이 리터럴로 남았는데, 그걸 `unresolved_template` = 배선 고장으로
15998
+ # 승격해 **매 실행 노드 FAILED + 사용자 알림**이 됐다. "오늘 살 게 없다" 는
15999
+ # 정상이다.
16000
+ # → 반복 중이 아닐 때 남은 **아이템 바인딩**(`{{ item… }}` / `{{ index }}`)은
16001
+ # 승격하지 않고 `_diagnose_empty_reason` 에 넘긴다. 그쪽은 상류 리스트 포트로
16002
+ # "상류가 비어 반복이 안 됨(no_signal)" 과 "배열 소스 없이 item 을 씀(unbound)"
16003
+ # 을 이미 가른다 — 이 함수의 계약대로 분류는 그쪽 몫이다.
16004
+ # 반복 **중인데도** 안 풀린 템플릿은 종전대로 고장이다(항목이 있는데 못 읽었다).
16005
+ def _is_item_binding(value: Any) -> bool:
16006
+ if not isinstance(value, str):
16007
+ return False
16008
+ return bool(re.search(r"\{\{\s*(item|index|total)\b", value))
16009
+
16010
+ # 판정 축은 "반복 중인가" 가 아니라 **상류 배열이 있는데 비었는가** 다.
16011
+ # · 상류 리스트 포트가 있고 비었다 → 오늘 줄 게 없어 반복이 안 된 것(정상)
16012
+ # · 리스트 포트가 아예 없다 → 배열 소스 없이 item 을 쓴 배선 고장
16013
+ # `_upstream_array_is_empty` 가 이미 그 셋(True/False/None)을 가른다.
16014
+ upstream_empty = False
16015
+ if not iterating and context is not None and node_id:
16016
+ try:
16017
+ upstream_empty = _upstream_array_is_empty(_input_namespace(context, node_id)) is True
16018
+ except Exception: # noqa: BLE001 — 진단이 실행을 막지 않는다
16019
+ upstream_empty = False
16020
+ hard_kinds = {
16021
+ key
16022
+ for key, kind in kinds.items()
16023
+ if kind == "invalid"
16024
+ or (
16025
+ kind == "unresolved_template"
16026
+ and not (upstream_empty and _is_item_binding(order.get(key)))
16027
+ )
16028
+ }
16029
+ hard = item_shape is not None or bool(hard_kinds)
15996
16030
 
15997
16031
  labels = NewOrderNodeExecutor._ORDER_FIELD_LABELS
15998
16032
  kind_ko = {
@@ -16299,8 +16333,16 @@ class NewOrderNodeExecutor(NodeExecutorBase):
16299
16333
 
16300
16334
  ord_mkt_code = self.STOCK_MARKET_CODES.get(exchange, "82")
16301
16335
 
16302
- # 매수 지정가 주문인데 가격이 없으면 현재가 조회
16303
- if is_buy and ordprc_ptn_code == "00" and price <= 0:
16336
+ # 지정가 주문인데 가격이 없으면 현재가 조회.
16337
+ # 🔴 2026-09-14 — 종전엔 **매수만** 이 경로를 탔다. 그래서 매도를 지정가로 두고 가격을
16338
+ # 비우면 price=0 이 그대로 나가 거부됐다. 미국주식 **주간거래(Blue Ocean) 세션은
16339
+ # 지정가만 받는다**(rsp_cd 00891 "주간거래는 지정가로만 주문이 가능합니다", 실계좌
16340
+ # 관측) — 그 시간대에 손절·익절을 쓰려면 매도도 지정가여야 하는데, 사람이 종목마다
16341
+ # 가격을 적을 수는 없다(손절 대상은 조건이 고른다). 매수와 같은 규칙으로 맞춘다.
16342
+ # ⚠️ 지정가는 **체결을 보장하지 않는다** — 손절인데 안 팔릴 수 있다. 그건 워크플로우가
16343
+ # 지정가를 선택한 결과이고, 노드 설정에 그대로 드러난다(엔진이 몰래 유형을 바꾸지
16344
+ # 않는다 — 오너 지시 2026-09-14: "다시 보낼 때는 노드로 워크플로우에 있어야 사용자가 안다").
16345
+ if ordprc_ptn_code == "00" and price <= 0:
16304
16346
  try:
16305
16347
  current_price = await self._get_current_price(ls, symbol, ord_mkt_code, context, node_id)
16306
16348
  if current_price and current_price > 0:
@@ -16334,6 +16376,14 @@ class NewOrderNodeExecutor(NodeExecutorBase):
16334
16376
 
16335
16377
  # 디버그: 응답 전체 출력
16336
16378
  context.log("debug", f"COSAT00301 response: rsp_cd={response.rsp_cd}, rsp_msg={response.rsp_msg}", node_id)
16379
+ # 🔴 2026-09-14 — `context.log` 는 리스너 통지 전용이라 **stdout 에 안 나온다**.
16380
+ # 그래서 파드 로그만 보면 접수됐는지 거부됐는지 구분이 안 됐다(주문 결과 0줄).
16381
+ # 증권사 응답 코드는 사고 조사의 1차 근거라 모듈 로거로도 남긴다
16382
+ # (`kubectl logs | grep broker_order_response`).
16383
+ logger.info(
16384
+ "broker_order_response | node=%s tr=COSAT00301 symbol=%s rsp_cd=%s rsp_msg=%s",
16385
+ node_id, symbol, response.rsp_cd, response.rsp_msg,
16386
+ )
16337
16387
 
16338
16388
  if response.error_msg:
16339
16389
  reject = map_reject_code(
@@ -16344,6 +16394,10 @@ class NewOrderNodeExecutor(NodeExecutorBase):
16344
16394
  f"Order failed: {symbol} - {response.error_msg} ({reject.cause})",
16345
16395
  node_id,
16346
16396
  )
16397
+ logger.warning(
16398
+ "broker_order_rejected | node=%s symbol=%s rsp_cd=%s cause=%s msg=%s",
16399
+ node_id, symbol, getattr(response, "rsp_cd", None), reject.cause, response.error_msg,
16400
+ )
16347
16401
  await self._notify_order_reject(context, node_id, symbol, reject)
16348
16402
  return self._order_result(
16349
16403
  False, symbol, exchange, side, qty, price,
@@ -16373,6 +16427,10 @@ class NewOrderNodeExecutor(NodeExecutorBase):
16373
16427
  )
16374
16428
 
16375
16429
  context.log("info", f"Order submitted: {symbol} {side} {qty}@{price} → order_id={order_no}", node_id)
16430
+ logger.info(
16431
+ "broker_order_submitted | node=%s symbol=%s side=%s qty=%s price=%s order_id=%s",
16432
+ node_id, symbol, side, qty, price, order_no,
16433
+ )
16376
16434
 
16377
16435
  # Record workflow order for FIFO tracking (OrderNo가 있는 경우만)
16378
16436
  context.record_workflow_order(
@@ -5,7 +5,7 @@ authors = [
5
5
  homepage = "https://programgarden.com"
6
6
  requires-python = ">=3.12"
7
7
  name = "programgarden"
8
- version = "1.37.2"
8
+ version = "1.37.3"
9
9
  license = "AGPL-3.0-or-later"
10
10
  description = "ProgramGarden - 노드 기반 자동매매 DSL 실행 엔진"
11
11
  readme = "README.md"
File without changes