programgarden-core 1.15.0__tar.gz → 1.15.2__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_core-1.15.0 → programgarden_core-1.15.2}/PKG-INFO +2 -1
  2. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/__init__.py +7 -0
  3. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/bases/listener.py +1 -0
  4. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/models/__init__.py +15 -0
  5. programgarden_core-1.15.2/programgarden_core/models/order_diagnostics.py +155 -0
  6. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/pyproject.toml +2 -1
  7. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/README.md +0 -0
  8. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/bases/__init__.py +0 -0
  9. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/bases/client.py +0 -0
  10. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/bases/components.py +0 -0
  11. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/bases/mixins.py +0 -0
  12. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/bases/products.py +0 -0
  13. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/bases/sql.py +0 -0
  14. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/bases/storage.py +0 -0
  15. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/exceptions.py +0 -0
  16. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/expression/__init__.py +0 -0
  17. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/expression/evaluator.py +0 -0
  18. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/i18n/__init__.py +0 -0
  19. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/i18n/locales/en.json +0 -0
  20. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/i18n/locales/ko.json +0 -0
  21. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/i18n/translator.py +0 -0
  22. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/korea_alias.py +0 -0
  23. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/models/connection_rule.py +0 -0
  24. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/models/credential.py +0 -0
  25. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/models/edge.py +0 -0
  26. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/models/event.py +0 -0
  27. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/models/exchange.py +0 -0
  28. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/models/field_binding.py +0 -0
  29. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/models/job.py +0 -0
  30. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/models/plugin_resource.py +0 -0
  31. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/models/resilience.py +0 -0
  32. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/models/resource.py +0 -0
  33. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/models/validation.py +0 -0
  34. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/models/workflow.py +0 -0
  35. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/__init__.py +0 -0
  36. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/account_futures.py +0 -0
  37. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/account_korea_stock.py +0 -0
  38. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/account_stock.py +0 -0
  39. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/ai.py +0 -0
  40. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/backtest.py +0 -0
  41. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/backtest_futures.py +0 -0
  42. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/backtest_korea_stock.py +0 -0
  43. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/backtest_stock.py +0 -0
  44. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/base.py +0 -0
  45. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/broker.py +0 -0
  46. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/calculation.py +0 -0
  47. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/condition.py +0 -0
  48. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/data.py +0 -0
  49. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/data_futures.py +0 -0
  50. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/data_korea_stock.py +0 -0
  51. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/data_stock.py +0 -0
  52. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/display.py +0 -0
  53. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/event.py +0 -0
  54. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/fundamental_korea_stock.py +0 -0
  55. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/fundamental_stock.py +0 -0
  56. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/infra.py +0 -0
  57. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/market_external.py +0 -0
  58. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/market_status.py +0 -0
  59. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/open_orders_futures.py +0 -0
  60. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/open_orders_korea_stock.py +0 -0
  61. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/open_orders_stock.py +0 -0
  62. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/order.py +0 -0
  63. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/portfolio.py +0 -0
  64. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/realtime_futures.py +0 -0
  65. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/realtime_korea_stock.py +0 -0
  66. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/realtime_stock.py +0 -0
  67. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/risk.py +0 -0
  68. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/symbol.py +0 -0
  69. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/symbol_futures.py +0 -0
  70. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/symbol_korea_stock.py +0 -0
  71. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/symbol_stock.py +0 -0
  72. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/nodes/trigger.py +0 -0
  73. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/presets/__init__.py +0 -0
  74. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/presets/news_analyst.json +0 -0
  75. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/presets/risk_manager.json +0 -0
  76. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/presets/strategist.json +0 -0
  77. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/presets/technical_analyst.json +0 -0
  78. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/registry/__init__.py +0 -0
  79. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/registry/credential_registry.py +0 -0
  80. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/registry/dynamic_node_registry.py +0 -0
  81. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/registry/node_registry.py +0 -0
  82. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/registry/plugin_registry.py +0 -0
  83. {programgarden_core-1.15.0 → programgarden_core-1.15.2}/programgarden_core/retry_executor.py +0 -0
@@ -1,7 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: programgarden-core
3
- Version: 1.15.0
3
+ Version: 1.15.2
4
4
  Summary: ProgramGarden Core - 노드 기반 DSL 핵심 타입 정의
5
+ License-Expression: AGPL-3.0-or-later
5
6
  Author: 프로그램동산
6
7
  Author-email: coding@programgarden.com
7
8
  Requires-Python: >=3.12
@@ -110,6 +110,13 @@ __all__ = [
110
110
  "default_severity_for",
111
111
  "suggest_close_match",
112
112
  "build_error",
113
+ # Order Diagnostics (runtime order reject + empty-order reason)
114
+ "OrderRejectInfo",
115
+ "EmptyOrderReason",
116
+ "map_reject_code",
117
+ "OVERSEAS_STOCK_REJECT_CODES",
118
+ "OVERSEAS_FUTURES_REJECT_CODES",
119
+ "KOREA_STOCK_REJECT_CODES",
113
120
  # Registry
114
121
  "NodeTypeRegistry",
115
122
  "PluginRegistry",
@@ -510,6 +510,7 @@ class NotificationCategory(str, Enum):
510
510
  CONNECTION_LOST = "connection_lost" # 실시간 WebSocket 끊김, 재연결 시도 중
511
511
  CONNECTION_RESTORED = "connection_restored" # 재연결 성공 (끊김 시간 + reconcile 결과 동봉)
512
512
  CONNECTION_FAILED = "connection_failed" # 재연결 최종 실패 (최대 시도 소진)
513
+ ORDER_REJECTED = "order_rejected" # 주문 거부 (브로커 reject, rsp_cd + 진단 동봉)
513
514
 
514
515
 
515
516
  class NotificationSeverity(str, Enum):
@@ -93,6 +93,14 @@ from programgarden_core.models.validation import (
93
93
  suggest_close_match,
94
94
  build_error,
95
95
  )
96
+ from programgarden_core.models.order_diagnostics import (
97
+ OrderRejectInfo,
98
+ EmptyOrderReason,
99
+ map_reject_code,
100
+ OVERSEAS_STOCK_REJECT_CODES,
101
+ OVERSEAS_FUTURES_REJECT_CODES,
102
+ KOREA_STOCK_REJECT_CODES,
103
+ )
96
104
 
97
105
  __all__ = [
98
106
  # Edge
@@ -179,4 +187,11 @@ __all__ = [
179
187
  "default_severity_for",
180
188
  "suggest_close_match",
181
189
  "build_error",
190
+ # Order Diagnostics (runtime order reject + empty-order reason)
191
+ "OrderRejectInfo",
192
+ "EmptyOrderReason",
193
+ "map_reject_code",
194
+ "OVERSEAS_STOCK_REJECT_CODES",
195
+ "OVERSEAS_FUTURES_REJECT_CODES",
196
+ "KOREA_STOCK_REJECT_CODES",
182
197
  ]
@@ -0,0 +1,155 @@
1
+ """Runtime order diagnostics for AI-chatbot-friendly live trading callbacks.
2
+
3
+ Unlike the pre-trade ``validation`` module (which produces ``ErrorInfo`` for
4
+ ``validate()`` / ``dry_run`` paths), this module covers *live runtime* order
5
+ outcomes:
6
+
7
+ 1. **Order rejection** — maps an LS Securities response code (``rsp_cd``) to a
8
+ structured ``OrderRejectInfo`` (English cause + remediation tip) so the
9
+ consuming chatbot can reason about *why* an order was rejected and *what to
10
+ fix*, instead of passing through the raw broker message only.
11
+ 2. **Empty order result** — the ``EmptyOrderReason`` enum lets a consumer
12
+ deterministically distinguish "no trading signal today (normal)" from
13
+ "upstream data pipeline failed (broken)".
14
+
15
+ This is a pure, dependency-free data layer (no imports from other packages).
16
+ All user-facing strings here follow the ``ErrorInfo`` convention: ``cause`` is a
17
+ single English sentence (like ``ErrorInfo.message``) and ``tip`` is a single
18
+ English line (like ``ErrorInfo.suggestion``).
19
+
20
+ The ``rsp_cd`` mapping tables intentionally start **empty**. Entries are added
21
+ only after a code is observed and confirmed in live trading — never guessed
22
+ from incomplete LS specs. Until a code is registered, ``map_reject_code``
23
+ falls back to the raw broker message with ``known=False``.
24
+ """
25
+ from __future__ import annotations
26
+
27
+ from enum import Enum
28
+ from typing import Dict, Optional
29
+
30
+ from pydantic import BaseModel, ConfigDict, Field
31
+
32
+
33
+ class OrderRejectInfo(BaseModel):
34
+ """Structured diagnostic for a broker-rejected order.
35
+
36
+ Always carries the raw broker message (``raw_msg``) as a fallback so the
37
+ consumer never loses the original text, even for unmapped codes.
38
+ """
39
+
40
+ rsp_cd: str = Field(
41
+ description="LS Securities response code, e.g. '40570'. '00000' means success.",
42
+ )
43
+ cause: str = Field(
44
+ description=(
45
+ "One-sentence English explanation of why the order was rejected. "
46
+ "Falls back to raw_msg when the code is not in the mapping table."
47
+ ),
48
+ )
49
+ tip: Optional[str] = Field(
50
+ default=None,
51
+ description=(
52
+ "Single-line English remediation hint. None when the code is "
53
+ "unmapped (no known fix to suggest)."
54
+ ),
55
+ )
56
+ raw_msg: str = Field(
57
+ default="",
58
+ description="Original LS broker message (rsp_msg / error_msg), always included.",
59
+ )
60
+ known: bool = Field(
61
+ description=(
62
+ "True when a structured cause/tip is available (from the "
63
+ "reject-code table OR a recognized order-lifecycle case); False "
64
+ "when only the raw broker message is available."
65
+ ),
66
+ )
67
+
68
+ model_config = ConfigDict(extra="forbid")
69
+
70
+
71
+ class EmptyOrderReason(str, Enum):
72
+ """Why an order node produced no order.
73
+
74
+ Lets a consumer distinguish a normal no-op from a pipeline failure so that
75
+ "no order today" is never silently treated the same as "data fetch broke".
76
+ """
77
+
78
+ NO_SIGNAL = "no_signal"
79
+ """Upstream produced an empty result normally (no trading signal today)."""
80
+
81
+ FETCH_FAILED = "fetch_failed"
82
+ """Upstream data lookup itself failed (broken pipeline)."""
83
+
84
+ NO_SYMBOL = "no_symbol"
85
+ """No symbol was specified (missing configuration)."""
86
+
87
+
88
+ # ---------------------------------------------------------------------------
89
+ # Market-specific reject-code tables.
90
+ #
91
+ # Structure: rsp_cd -> {"cause": <english one-sentence>, "tip": <english one-liner>}
92
+ #
93
+ # Only live-verified, real rsp_cd values are registered here. Do NOT guess
94
+ # codes or causes from incomplete LS specifications — register a code only
95
+ # after it is observed and confirmed in live trading.
96
+ # ---------------------------------------------------------------------------
97
+
98
+ OVERSEAS_STOCK_REJECT_CODES: Dict[str, Dict[str, str]] = {}
99
+ OVERSEAS_FUTURES_REJECT_CODES: Dict[str, Dict[str, str]] = {}
100
+ KOREA_STOCK_REJECT_CODES: Dict[str, Dict[str, str]] = {}
101
+
102
+ _MARKET_TABLES: Dict[str, Dict[str, Dict[str, str]]] = {
103
+ "overseas_stock": OVERSEAS_STOCK_REJECT_CODES,
104
+ "overseas_futures": OVERSEAS_FUTURES_REJECT_CODES,
105
+ "korea_stock": KOREA_STOCK_REJECT_CODES,
106
+ }
107
+
108
+
109
+ def map_reject_code(market: str, rsp_cd: str, raw_msg: str = "") -> OrderRejectInfo:
110
+ """Map an LS reject ``rsp_cd`` to a structured ``OrderRejectInfo``.
111
+
112
+ Looks up ``rsp_cd`` in the table for ``market``. If found, returns a mapped
113
+ diagnostic (``known=True``); if the code's mapped ``cause`` is empty, falls
114
+ back to ``raw_msg``. If the market or code is unknown, returns a raw
115
+ fallback (``known=False``, ``tip=None``).
116
+
117
+ Args:
118
+ market: One of "overseas_stock", "overseas_futures", "korea_stock".
119
+ An unknown market string is handled defensively (raw fallback).
120
+ rsp_cd: LS Securities response code. Success codes ("00000") are
121
+ filtered by callers, but are handled defensively here.
122
+ raw_msg: Original LS broker message, used as the fallback cause.
123
+
124
+ Returns:
125
+ OrderRejectInfo with cause/tip/known populated.
126
+ """
127
+ # Defensive casts: callers may pass non-str rsp_cd (e.g. int OrdNo) or a
128
+ # None raw_msg from a mocked LS response — never let that raise.
129
+ rsp_cd = str(rsp_cd or "")
130
+ raw_msg = str(raw_msg or "")
131
+
132
+ table = _MARKET_TABLES.get(market, {})
133
+ entry = table.get(rsp_cd)
134
+
135
+ if entry is not None:
136
+ cause = entry.get("cause") or raw_msg or (
137
+ f"Order rejected by broker (code {rsp_cd}, no diagnostic mapping)"
138
+ )
139
+ tip = entry.get("tip") or None
140
+ return OrderRejectInfo(
141
+ rsp_cd=rsp_cd,
142
+ cause=cause,
143
+ tip=tip,
144
+ raw_msg=raw_msg,
145
+ known=True,
146
+ )
147
+
148
+ cause = raw_msg or f"Order rejected by broker (code {rsp_cd}, no diagnostic mapping)"
149
+ return OrderRejectInfo(
150
+ rsp_cd=rsp_cd,
151
+ cause=cause,
152
+ tip=None,
153
+ raw_msg=raw_msg,
154
+ known=False,
155
+ )
@@ -5,7 +5,8 @@ authors = [
5
5
  homepage = "https://programgarden.com"
6
6
  requires-python = ">=3.12"
7
7
  name = "programgarden-core"
8
- version = "1.15.0"
8
+ version = "1.15.2"
9
+ license = "AGPL-3.0-or-later"
9
10
  description = "ProgramGarden Core - 노드 기반 DSL 핵심 타입 정의"
10
11
  readme = "README.md"
11
12