programgarden-core 1.25.0__tar.gz → 1.25.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 (84) hide show
  1. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/PKG-INFO +1 -1
  2. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/bases/listener.py +1 -1
  3. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/models/order_diagnostics.py +15 -0
  4. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/backtest_futures.py +1 -0
  5. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/backtest_korea_stock.py +1 -0
  6. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/backtest_stock.py +1 -0
  7. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/data_futures.py +1 -0
  8. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/data_korea_stock.py +1 -0
  9. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/data_stock.py +1 -0
  10. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/risk.py +2 -0
  11. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/pyproject.toml +1 -1
  12. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/README.md +0 -0
  13. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/__init__.py +0 -0
  14. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/bases/__init__.py +0 -0
  15. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/bases/client.py +0 -0
  16. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/bases/components.py +0 -0
  17. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/bases/mixins.py +0 -0
  18. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/bases/products.py +0 -0
  19. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/bases/sql.py +0 -0
  20. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/bases/storage.py +0 -0
  21. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/code_node.py +0 -0
  22. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/exceptions.py +0 -0
  23. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/expression/__init__.py +0 -0
  24. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/expression/evaluator.py +0 -0
  25. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/i18n/__init__.py +0 -0
  26. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/i18n/locales/en.json +0 -0
  27. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/i18n/locales/ko.json +0 -0
  28. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/i18n/translator.py +0 -0
  29. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/korea_alias.py +0 -0
  30. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/models/__init__.py +0 -0
  31. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/models/connection_rule.py +0 -0
  32. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/models/credential.py +0 -0
  33. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/models/edge.py +0 -0
  34. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/models/event.py +0 -0
  35. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/models/exchange.py +0 -0
  36. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/models/field_binding.py +0 -0
  37. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/models/job.py +0 -0
  38. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/models/plugin_resource.py +0 -0
  39. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/models/resilience.py +0 -0
  40. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/models/resource.py +0 -0
  41. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/models/validation.py +0 -0
  42. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/models/workflow.py +0 -0
  43. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/__init__.py +0 -0
  44. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/account_futures.py +0 -0
  45. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/account_korea_stock.py +0 -0
  46. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/account_stock.py +0 -0
  47. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/ai.py +0 -0
  48. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/backtest.py +0 -0
  49. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/base.py +0 -0
  50. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/broker.py +0 -0
  51. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/calculation.py +0 -0
  52. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/code.py +0 -0
  53. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/condition.py +0 -0
  54. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/data.py +0 -0
  55. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/display.py +0 -0
  56. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/event.py +0 -0
  57. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/fundamental_korea_stock.py +0 -0
  58. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/fundamental_stock.py +0 -0
  59. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/infra.py +0 -0
  60. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/market_external.py +0 -0
  61. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/market_status.py +0 -0
  62. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/open_orders_futures.py +0 -0
  63. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/open_orders_korea_stock.py +0 -0
  64. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/open_orders_stock.py +0 -0
  65. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/order.py +0 -0
  66. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/portfolio.py +0 -0
  67. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/realtime_futures.py +0 -0
  68. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/realtime_korea_stock.py +0 -0
  69. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/realtime_stock.py +0 -0
  70. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/symbol.py +0 -0
  71. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/symbol_futures.py +0 -0
  72. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/symbol_korea_stock.py +0 -0
  73. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/symbol_stock.py +0 -0
  74. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/nodes/trigger.py +0 -0
  75. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/presets/__init__.py +0 -0
  76. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/presets/news_analyst.json +0 -0
  77. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/presets/risk_manager.json +0 -0
  78. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/presets/strategist.json +0 -0
  79. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/presets/technical_analyst.json +0 -0
  80. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/registry/__init__.py +0 -0
  81. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/registry/credential_registry.py +0 -0
  82. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/registry/node_registry.py +0 -0
  83. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/registry/plugin_registry.py +0 -0
  84. {programgarden_core-1.25.0 → programgarden_core-1.25.2}/programgarden_core/retry_executor.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: programgarden-core
3
- Version: 1.25.0
3
+ Version: 1.25.2
4
4
  Summary: ProgramGarden Core - 노드 기반 DSL 핵심 타입 정의
5
5
  License-Expression: AGPL-3.0-or-later
6
6
  Author: 프로그램동산
@@ -189,7 +189,7 @@ class PositionDetail:
189
189
  """
190
190
  symbol: str
191
191
  exchange: str
192
- quantity: int
192
+ quantity: Union[Decimal, float, int] # 소수점(fractional) 잔고 대응
193
193
  avg_price: Union[Decimal, float]
194
194
  current_price: Union[Decimal, float]
195
195
  pnl_amount: Union[Decimal, float]
@@ -117,6 +117,12 @@ class EmptyOrderReason(str, Enum):
117
117
  NO_SYMBOL = "no_symbol"
118
118
  """No symbol was specified (missing configuration)."""
119
119
 
120
+ FRACTIONAL_ONLY = "fractional_only"
121
+ """The position quantity is fractional-only (e.g. 0.847972 shares) and the
122
+ order pipeline submits integer quantities, so flooring left nothing to
123
+ order. Distinct from NO_SIGNAL so the holder learns why nothing happened
124
+ (fractional balances measured on a real LS account, prod 2026-08-24)."""
125
+
120
126
 
121
127
  # ---------------------------------------------------------------------------
122
128
  # Market-specific reject-code tables.
@@ -132,6 +138,15 @@ class EmptyOrderReason(str, Enum):
132
138
  # Each entry was produced by deliberately triggering the rejection and reading
133
139
  # the returned rsp_cd/rsp_msg — none of these are inferred from the LS spec.
134
140
  OVERSEAS_STOCK_REJECT_CODES: Dict[str, Dict[str, str]] = {
141
+ # Live-verified 2026-08-24: deliberately submitted OrdQty=0.1 (COSAT00301,
142
+ # limit buy far below market). The LS gateway rejected at its type-validation
143
+ # layer with HTTP 500 + rsp_cd IGW40011 "주문수량(OrdQty) data type을
144
+ # 확인하세요." — fractional order quantities are not accepted by this TR.
145
+ "IGW40011": {
146
+ "retry": RetryAdvice.RETRY_AFTER_FIX.value,
147
+ "cause": "The order quantity failed the LS gateway's data-type validation (non-integer quantity).",
148
+ "tip": "Order quantities must be integers; floor fractional quantities before ordering.",
149
+ },
135
150
  "02201": {
136
151
  "retry": RetryAdvice.RETRY_AFTER_FIX.value,
137
152
  "cause": "The account does not have enough cash to place this order.",
@@ -200,6 +200,7 @@ class OverseasFuturesHistoricalDataNode(BaseNode):
200
200
  "OverseasFuturesHistoricalDataNode.value → LineChartNode (price history display)",
201
201
  ],
202
202
  "pitfalls": [
203
+ "`symbol: {{ item }}` / `{{ nodes.<split>.item }}` resolves only inside a per-symbol loop (auto-iterate over an upstream array, or a SplitNode branch). If the upstream array is empty for this run the node logs `No symbols to fetch` and returns an empty series — normal no-signal, not a defect. A `No symbols provided` WARNING means the symbol source is missing or the binding did not resolve",
203
204
  "Bind `.time_series` not `.value` to ConditionNode data — the value port wraps the list in a dict",
204
205
  "Use OverseasFuturesBrokerNode (not OverseasStockBrokerNode) upstream",
205
206
  "Contract month codes change every quarter — update symbol (e.g., ESH26 → ESM26) when rolling",
@@ -199,6 +199,7 @@ class KoreaStockHistoricalDataNode(BaseNode):
199
199
  "KoreaStockHistoricalDataNode.value → LineChartNode (domestic price history chart)",
200
200
  ],
201
201
  "pitfalls": [
202
+ "`symbol: {{ item }}` / `{{ nodes.<split>.item }}` resolves only inside a per-symbol loop (auto-iterate over an upstream array, or a SplitNode branch). If the upstream array is empty for this run the node logs `No symbols to fetch` and returns an empty series — normal no-signal, not a defect. A `No symbols provided` WARNING means the symbol source is missing or the binding did not resolve",
202
203
  "Access time_series via `{{ nodes.historical.value.time_series }}` — the value port wraps the list in a dict",
203
204
  "Intraday intervals (1m/5m/15m) not supported for Korean domestic stocks — use 1d minimum",
204
205
  "adjust=True is the default; set False only if you specifically need unadjusted prices",
@@ -201,6 +201,7 @@ class OverseasStockHistoricalDataNode(BaseNode):
201
201
  "OverseasStockHistoricalDataNode.value → LineChartNode (price chart display)",
202
202
  ],
203
203
  "pitfalls": [
204
+ "`symbol: {{ item }}` / `{{ nodes.<split>.item }}` resolves only inside a per-symbol loop (auto-iterate over an upstream array, or a SplitNode branch). If the upstream array is empty for this run the node logs `No symbols to fetch` and returns an empty series — normal no-signal, not a defect. A `No symbols provided` WARNING means the symbol source is missing or the binding did not resolve",
204
205
  "Access time_series via `{{ nodes.historical.value.time_series }}` — the value port is a dict, not a plain list",
205
206
  "Intraday intervals (1m/5m/15m/1h) may have limited history depth depending on LS Securities API availability",
206
207
  "adjust=False (default) returns unadjusted prices; set True when comparing long-term price levels across splits",
@@ -178,6 +178,7 @@ class OverseasFuturesMarketDataNode(BaseNode):
178
178
  "OverseasFuturesMarketDataNode.values → PositionSizingNode.market_data (with symbols + balance)",
179
179
  ],
180
180
  "pitfalls": [
181
+ "If the bound symbol source (ConditionNode.passed_symbols / SymbolFilterNode.symbols) is empty for this run the node returns values=[] with no error (normal no-signal). The `symbols 필드가 필수입니다` error is raised only when no symbol source is configured or the binding did not resolve",
181
182
  "Symbol must include contract month code (e.g., ESH26 not ES) — check OverseasFuturesSymbolQueryNode for valid codes",
182
183
  "Use OverseasFuturesBrokerNode (not OverseasStockBrokerNode) as the upstream broker",
183
184
  "REST polling returns a snapshot; for tick-level streaming use OverseasFuturesRealMarketDataNode",
@@ -183,6 +183,7 @@ class KoreaStockMarketDataNode(BaseNode):
183
183
  "KoreaStockSymbolQueryNode/ConditionNode.passed_symbols → PositionSizingNode.symbols (canonical symbol list)",
184
184
  ],
185
185
  "pitfalls": [
186
+ "If the bound symbol source (ConditionNode.passed_symbols / SymbolFilterNode.symbols) is empty for this run the node returns values=[] with no error (normal no-signal). The `symbols 필드가 필수입니다` error is raised only when no symbol source is configured or the binding did not resolve",
186
187
  "KoreaStock does not support paper trading — KoreaStockBrokerNode always uses a live session",
187
188
  "Symbol must be a 6-digit KRX code without exchange field — do not include exchange key",
188
189
  "REST polling returns a snapshot; for tick-level streaming use KoreaStockRealMarketDataNode",
@@ -184,6 +184,7 @@ class OverseasStockMarketDataNode(BaseNode):
184
184
  "OverseasStockMarketDataNode.values → PositionSizingNode.market_data (with symbols + balance)",
185
185
  ],
186
186
  "pitfalls": [
187
+ "If the bound symbol source (ConditionNode.passed_symbols / SymbolFilterNode.symbols) is empty for this run the node returns values=[] with no error (normal no-signal). The `symbols 필드가 필수입니다` error is raised only when no symbol source is configured or the binding did not resolve",
187
188
  "The symbol field must be a single dict — not a list. Use SplitNode for multi-symbol scenarios",
188
189
  "REST polling returns a snapshot at call time; for tick-level streaming use OverseasStockRealMarketDataNode",
189
190
  "OverseasStockBrokerNode must be upstream via a main edge for connection auto-injection to work",
@@ -143,6 +143,7 @@ class PositionSizingNode(BaseNode):
143
143
  "Standardize risk exposure across different strategies (share the same PositionSizingNode config)",
144
144
  ],
145
145
  "when_not_to_use": [
146
+ "Buying/selling a FIXED quantity of every symbol that passed a ConditionNode / SymbolFilterNode — you do not need sizing at all: wire the condition/filter straight into NewOrderNode (it auto-iterates the symbol list) and set `quantity` on the order node",
146
147
  "Fixed-quantity simple tests — set `quantity` directly on NewOrderNode and skip sizing entirely",
147
148
  "Portfolio-level rebalancing — use PortfolioNode to compute allocations first, then size each leg",
148
149
  "Per-leg position-management signals (stop-loss / trailing) — that lives in ConditionNode with position-management plugins",
@@ -292,6 +293,7 @@ class PositionSizingNode(BaseNode):
292
293
  "AccountNode + MarketDataNode → PositionSizingNode(atr_based) → NewOrderNode",
293
294
  ],
294
295
  "pitfalls": [
296
+ "`symbols` bound to ConditionNode.passed_symbols / SymbolFilterNode.symbols may legitimately be EMPTY on a given run (no symbol met the condition today). The node then emits orders=[] with reason='no_signal' and logs an info line — that is normal runtime behaviour, not a wiring defect, and it does not block saving. A `No symbols provided for position sizing` WARNING means the symbol source itself is missing or the binding did not resolve (unknown node id / output port, or `{{ item }}` used outside a per-symbol loop) — fix the wiring in that case only",
295
297
  "`symbol` must be the object form, not a plain string",
296
298
  "method='kelly' needs kelly_fraction <= 1.0 (quarter-Kelly = 0.25 is a sane default)",
297
299
  "method='atr_based' needs `market_data` bound — otherwise the fallback produces a zero / tiny quantity",
@@ -5,7 +5,7 @@ authors = [
5
5
  homepage = "https://programgarden.com"
6
6
  requires-python = ">=3.12"
7
7
  name = "programgarden-core"
8
- version = "1.25.0"
8
+ version = "1.25.2"
9
9
  license = "AGPL-3.0-or-later"
10
10
  description = "ProgramGarden Core - 노드 기반 DSL 핵심 타입 정의"
11
11
  readme = "README.md"