programgarden-core 2.3.0__tar.gz → 2.4.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 (85) hide show
  1. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/PKG-INFO +1 -1
  2. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/trigger.py +32 -17
  3. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/pyproject.toml +1 -1
  4. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/README.md +0 -0
  5. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/__init__.py +0 -0
  6. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/bases/__init__.py +0 -0
  7. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/bases/client.py +0 -0
  8. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/bases/components.py +0 -0
  9. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/bases/listener.py +0 -0
  10. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/bases/mixins.py +0 -0
  11. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/bases/products.py +0 -0
  12. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/bases/sql.py +0 -0
  13. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/bases/storage.py +0 -0
  14. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/code_node.py +0 -0
  15. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/exceptions.py +0 -0
  16. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/expression/__init__.py +0 -0
  17. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/expression/evaluator.py +0 -0
  18. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/expression/reference.py +0 -0
  19. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/i18n/__init__.py +0 -0
  20. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/i18n/locales/en.json +0 -0
  21. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/i18n/locales/ko.json +0 -0
  22. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/i18n/translator.py +0 -0
  23. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/korea_alias.py +0 -0
  24. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/models/__init__.py +0 -0
  25. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/models/connection_rule.py +0 -0
  26. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/models/credential.py +0 -0
  27. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/models/edge.py +0 -0
  28. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/models/event.py +0 -0
  29. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/models/exchange.py +0 -0
  30. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/models/field_binding.py +0 -0
  31. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/models/job.py +0 -0
  32. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/models/order_diagnostics.py +0 -0
  33. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/models/plugin_resource.py +0 -0
  34. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/models/resilience.py +0 -0
  35. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/models/resource.py +0 -0
  36. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/models/validation.py +0 -0
  37. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/models/workflow.py +0 -0
  38. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/__init__.py +0 -0
  39. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/account_futures.py +0 -0
  40. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/account_korea_stock.py +0 -0
  41. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/account_stock.py +0 -0
  42. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/ai.py +0 -0
  43. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/backtest.py +0 -0
  44. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/backtest_futures.py +0 -0
  45. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/backtest_korea_stock.py +0 -0
  46. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/backtest_stock.py +0 -0
  47. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/base.py +0 -0
  48. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/broker.py +0 -0
  49. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/calculation.py +0 -0
  50. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/code.py +0 -0
  51. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/condition.py +0 -0
  52. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/data.py +0 -0
  53. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/data_futures.py +0 -0
  54. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/data_korea_stock.py +0 -0
  55. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/data_stock.py +0 -0
  56. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/display.py +0 -0
  57. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/event.py +0 -0
  58. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/fundamental_korea_stock.py +0 -0
  59. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/fundamental_stock.py +0 -0
  60. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/infra.py +0 -0
  61. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/open_orders_futures.py +0 -0
  62. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/open_orders_korea_stock.py +0 -0
  63. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/open_orders_stock.py +0 -0
  64. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/order.py +0 -0
  65. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/orderable_quantity_futures.py +0 -0
  66. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/portfolio.py +0 -0
  67. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/realtime_futures.py +0 -0
  68. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/realtime_korea_stock.py +0 -0
  69. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/realtime_stock.py +0 -0
  70. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/risk.py +0 -0
  71. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/session_gate.py +0 -0
  72. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/symbol.py +0 -0
  73. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/symbol_futures.py +0 -0
  74. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/symbol_korea_stock.py +0 -0
  75. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/nodes/symbol_stock.py +0 -0
  76. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/presets/__init__.py +0 -0
  77. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/presets/news_analyst.json +0 -0
  78. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/presets/risk_manager.json +0 -0
  79. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/presets/strategist.json +0 -0
  80. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/presets/technical_analyst.json +0 -0
  81. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/registry/__init__.py +0 -0
  82. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/registry/credential_registry.py +0 -0
  83. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/registry/node_registry.py +0 -0
  84. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/registry/plugin_registry.py +0 -0
  85. {programgarden_core-2.3.0 → programgarden_core-2.4.0}/programgarden_core/retry_executor.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: programgarden-core
3
- Version: 2.3.0
3
+ Version: 2.4.0
4
4
  Summary: ProgramGarden Core - 노드 기반 DSL 핵심 타입 정의
5
5
  License-Expression: AGPL-3.0-or-later
6
6
  Author: 프로그램동산
@@ -50,17 +50,29 @@ class ScheduleNode(BaseNode):
50
50
  default="America/New_York", description="Timezone (e.g., America/New_York, Asia/Seoul)"
51
51
  )
52
52
  enabled: bool = Field(default=True, description="Schedule enabled")
53
- max_duration_hours: float = Field(
54
- default=24.0,
55
- description="최대 실행 시간 (시간). 초과 시 스케줄 자동 종료.",
53
+ # None (omitted) = unbounded: run until the workflow is stopped. Owner
54
+ # decision 2026-09-26: when the investor names no duration, the bot keeps
55
+ # running on its cron until the user stops the workflow. A provided value
56
+ # is still enforced (> 0) as a cap.
57
+ max_duration_hours: Optional[float] = Field(
58
+ default=None,
59
+ description=(
60
+ "최대 실행 시간 (시간). 생략(None)하면 사용자가 워크플로우를 멈출 때까지 "
61
+ "계속 실행한다. 값을 지정하면 초과 시 스케줄 자동 종료 (지정 시 반드시 > 0)."
62
+ ),
56
63
  )
57
64
  # ⑭ count was read by the executor (config.get("count", 1000)) but not
58
65
  # declared on the model — that schema/executor duality meant a canonical
59
- # field was invisible to validation. Declared here (default matches the
60
- # executor's safety cap) so the model is the single source of truth.
61
- count: int = Field(
62
- default=1000,
63
- description="Max number of schedule cycles before the scheduler exits (safety cap alongside max_duration_hours).",
66
+ # field was invisible to validation. Now declared here. None (omitted) =
67
+ # unbounded: run until the workflow is stopped (owner 2026-09-26). A
68
+ # provided value is enforced (>= 1) as a cycle cap.
69
+ count: Optional[int] = Field(
70
+ default=None,
71
+ description=(
72
+ "Max number of schedule cycles before the scheduler exits. Omit "
73
+ "(None) to run until the workflow is stopped; set only when the "
74
+ "investor named a fixed number of cycles (>= 1)."
75
+ ),
64
76
  )
65
77
 
66
78
  _inputs: List[InputPort] = []
@@ -76,7 +88,7 @@ class ScheduleNode(BaseNode):
76
88
  _usage: ClassVar[Dict[str, Any]] = {
77
89
  "when_to_use": [
78
90
  "Run the main flow on a cron schedule (every N minutes, daily at 09:30 ET, weekly market close, …)",
79
- "Bound long-running workflows with max_duration_hours to avoid runaway schedulers",
91
+ "Run indefinitely until the user stops the workflow — omit max_duration_hours and count; set them only when the investor named a duration or cycle count",
80
92
  "Combine with TradingHoursFilterNode to fire only on weekdays within market hours",
81
93
  ],
82
94
  "when_not_to_use": [
@@ -93,12 +105,12 @@ class ScheduleNode(BaseNode):
93
105
  _features: ClassVar[List[str]] = [
94
106
  "Standard 5-field cron expression — minute / hour / day / month / weekday",
95
107
  "Timezone-aware (IANA names) — 'America/New_York', 'Asia/Seoul', 'UTC'",
96
- "max_duration_hours caps total runtime; the scheduler exits cleanly at the limit",
108
+ "max_duration_hours and count are optional bounds: omit both and the schedule runs until the workflow is stopped; set one and the scheduler exits cleanly at that limit",
97
109
  "enabled=False freezes the trigger without removing the node from the DAG",
98
110
  "A tick re-executes the whole main flow: the ScheduleNode returns {trigger: true} without re-registering, so every node downstream of it runs again on each tick",
99
111
  "Startup account and open-order snapshots are not retained across schedule ticks; they are retained only across realtime events",
100
112
  "In replay a schedule_tick must fall on the cron's next firing instant after the previous frame, evaluated in this node's timezone (default America/New_York)",
101
- "enabled=false emits no tick; ticks past count or max_duration_hours are refused; an invalid timezone is rejected at startup",
113
+ "enabled=false emits no tick; when count / max_duration_hours are set, ticks past them are refused; when both are omitted the schedule is unbounded (runs until stopped); an invalid timezone is rejected at startup",
102
114
  "Emits exactly one subsequent event type: schedule_tick",
103
115
  "Requires exactly one StartNode upstream; the ScheduleNode is never the workflow root",
104
116
  ]
@@ -177,7 +189,7 @@ class ScheduleNode(BaseNode):
177
189
  },
178
190
  ]
179
191
  _node_guide: ClassVar[Dict[str, Any]] = {
180
- "input_handling": "No data inputs. All behavior is configured via `cron`, `timezone`, `enabled`, `max_duration_hours`.",
192
+ "input_handling": "No data inputs. All behavior is configured via `cron`, `timezone`, `enabled`, and the optional bounds `max_duration_hours` / `count` (omit both to run until the workflow is stopped).",
181
193
  "output_consumption": "`trigger` output carries `{fired_at, cycle_index}`. Downstream nodes usually just need an incoming edge; explicit binding is optional.",
182
194
  "common_combinations": [
183
195
  "StartNode → ScheduleNode → trading body (plain cron workflow)",
@@ -187,7 +199,7 @@ class ScheduleNode(BaseNode):
187
199
  "pitfalls": [
188
200
  "Always set `timezone` explicitly — server defaults are not portable",
189
201
  "Pair with TradingHoursFilterNode when the cron expression does not already encode market hours",
190
- "max_duration_hours caps the total runtime; for indefinite bots set it generously (up to 720h)",
202
+ "For a bot that should run until the user stops it, omit max_duration_hours and count; set them only when the investor named a duration (max_duration_hours > 0) or a cycle count (count >= 1)",
191
203
  ],
192
204
  }
193
205
 
@@ -237,9 +249,11 @@ class ScheduleNode(BaseNode):
237
249
  name="max_duration_hours",
238
250
  type=FieldType.NUMBER,
239
251
  description="i18n:fields.ScheduleNode.max_duration_hours",
240
- default=24.0,
252
+ # No default and NO max_value: omit for an unbounded schedule
253
+ # that runs until the workflow is stopped (owner 2026-09-26).
254
+ # Only a provided value is bounded (> 0).
255
+ default=None,
241
256
  min_value=0.1,
242
- max_value=720.0,
243
257
  expression_mode=ExpressionMode.FIXED_ONLY,
244
258
  category=FieldCategory.SETTINGS,
245
259
  expected_type="float",
@@ -248,8 +262,9 @@ class ScheduleNode(BaseNode):
248
262
  "count": FieldSchema(
249
263
  name="count",
250
264
  type=FieldType.INTEGER,
251
- description="Max number of schedule cycles before the scheduler exits (safety cap alongside max_duration_hours).",
252
- default=1000,
265
+ description="Max number of schedule cycles before the scheduler exits. Omit for an unbounded schedule that runs until the workflow is stopped; set only when a fixed number of cycles is intended.",
266
+ # No default: omitted = unbounded (owner 2026-09-26).
267
+ default=None,
253
268
  min_value=1,
254
269
  expression_mode=ExpressionMode.FIXED_ONLY,
255
270
  category=FieldCategory.SETTINGS,
@@ -5,7 +5,7 @@ authors = [
5
5
  homepage = "https://programgarden.com"
6
6
  requires-python = ">=3.12"
7
7
  name = "programgarden-core"
8
- version = "2.3.0"
8
+ version = "2.4.0"
9
9
  license = "AGPL-3.0-or-later"
10
10
  description = "ProgramGarden Core - 노드 기반 DSL 핵심 타입 정의"
11
11
  readme = "README.md"