programgarden-core 2.6.1__tar.gz → 2.6.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 (87) hide show
  1. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/PKG-INFO +3 -1
  2. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/README.md +2 -0
  3. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/i18n/locales/en.json +8 -6
  4. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/i18n/locales/ko.json +8 -6
  5. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/backtest_futures.py +34 -24
  6. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/pyproject.toml +1 -1
  7. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/__init__.py +0 -0
  8. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/bases/__init__.py +0 -0
  9. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/bases/client.py +0 -0
  10. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/bases/components.py +0 -0
  11. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/bases/listener.py +0 -0
  12. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/bases/mixins.py +0 -0
  13. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/bases/products.py +0 -0
  14. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/bases/sql.py +0 -0
  15. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/bases/storage.py +0 -0
  16. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/code_node.py +0 -0
  17. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/exceptions.py +0 -0
  18. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/expression/__init__.py +0 -0
  19. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/expression/evaluator.py +0 -0
  20. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/expression/reference.py +0 -0
  21. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/i18n/__init__.py +0 -0
  22. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/i18n/translator.py +0 -0
  23. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/korea_alias.py +0 -0
  24. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/market_calendar.py +0 -0
  25. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/models/__init__.py +0 -0
  26. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/models/connection_rule.py +0 -0
  27. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/models/credential.py +0 -0
  28. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/models/edge.py +0 -0
  29. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/models/event.py +0 -0
  30. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/models/exchange.py +0 -0
  31. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/models/field_binding.py +0 -0
  32. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/models/job.py +0 -0
  33. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/models/order_diagnostics.py +0 -0
  34. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/models/plugin_resource.py +0 -0
  35. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/models/resilience.py +0 -0
  36. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/models/resource.py +0 -0
  37. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/models/validation.py +0 -0
  38. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/models/workflow.py +0 -0
  39. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/__init__.py +0 -0
  40. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/account_futures.py +0 -0
  41. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/account_korea_stock.py +0 -0
  42. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/account_stock.py +0 -0
  43. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/ai.py +0 -0
  44. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/backtest.py +0 -0
  45. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/backtest_korea_stock.py +0 -0
  46. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/backtest_stock.py +0 -0
  47. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/base.py +0 -0
  48. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/broker.py +0 -0
  49. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/calculation.py +0 -0
  50. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/code.py +0 -0
  51. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/condition.py +0 -0
  52. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/contract_info_futures.py +0 -0
  53. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/data.py +0 -0
  54. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/data_futures.py +0 -0
  55. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/data_korea_stock.py +0 -0
  56. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/data_stock.py +0 -0
  57. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/display.py +0 -0
  58. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/event.py +0 -0
  59. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/fundamental_korea_stock.py +0 -0
  60. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/fundamental_stock.py +0 -0
  61. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/infra.py +0 -0
  62. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/open_orders_futures.py +0 -0
  63. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/open_orders_korea_stock.py +0 -0
  64. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/open_orders_stock.py +0 -0
  65. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/order.py +0 -0
  66. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/orderable_quantity_futures.py +0 -0
  67. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/portfolio.py +0 -0
  68. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/realtime_futures.py +0 -0
  69. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/realtime_korea_stock.py +0 -0
  70. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/realtime_stock.py +0 -0
  71. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/risk.py +0 -0
  72. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/session_gate.py +0 -0
  73. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/symbol.py +0 -0
  74. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/symbol_futures.py +0 -0
  75. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/symbol_korea_stock.py +0 -0
  76. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/symbol_stock.py +0 -0
  77. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/nodes/trigger.py +0 -0
  78. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/presets/__init__.py +0 -0
  79. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/presets/news_analyst.json +0 -0
  80. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/presets/risk_manager.json +0 -0
  81. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/presets/strategist.json +0 -0
  82. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/presets/technical_analyst.json +0 -0
  83. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/registry/__init__.py +0 -0
  84. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/registry/credential_registry.py +0 -0
  85. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/registry/node_registry.py +0 -0
  86. {programgarden_core-2.6.1 → programgarden_core-2.6.2}/programgarden_core/registry/plugin_registry.py +0 -0
  87. {programgarden_core-2.6.1 → programgarden_core-2.6.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: 2.6.1
3
+ Version: 2.6.2
4
4
  Summary: ProgramGarden Core - 노드 기반 DSL 핵심 타입 정의
5
5
  License-Expression: AGPL-3.0-or-later
6
6
  Author: 프로그램동산
@@ -15,6 +15,8 @@ Requires-Dist: pydantic (>=2.0.1,<3.0.0)
15
15
  Requires-Dist: tzdata (>=2025.2,<2026.0)
16
16
  Description-Content-Type: text/markdown
17
17
 
18
+ 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.
19
+
18
20
  US regular-session requests can opt into SessionGateNode.exchange_calendar (XNYS/XNAS) for scheduled holidays and early closes. See [session gates](../../docs/session-gates-and-guarded-split.md).
19
21
 
20
22
  # ProgramGarden Core
@@ -1,3 +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
+
1
3
  US regular-session requests can opt into SessionGateNode.exchange_calendar (XNYS/XNAS) for scheduled holidays and early closes. See [session gates](../../docs/session-gates-and-guarded-split.md).
2
4
 
3
5
  # ProgramGarden Core
@@ -116,7 +116,7 @@
116
116
  "nodes.OverseasFuturesBrokerNode.name": "Futures Broker",
117
117
  "nodes.OverseasFuturesCancelOrderNode.description": "Cancels a pending overseas futures order by its original order ID. Requires original_order_id from FuturesOpenOrdersNode or FuturesRealOrderEventNode. Outputs cancel result and cancelled order ID. Rate-limited to 5-second minimum interval. Typical connection: FuturesOpenOrdersNode → FuturesCancelOrderNode.",
118
118
  "nodes.OverseasFuturesCancelOrderNode.name": "Futures Cancel Order",
119
- "nodes.OverseasFuturesHistoricalDataNode.description": "Fetches historical OHLCV chart data for overseas futures over a specified date range. Supports bar intervals from 1-minute to monthly. Enable 'adjust' for rollover-adjusted prices (recommended for backtesting). Auto-iterates when previous node outputs multiple symbols. Requires an upstream OverseasFuturesBrokerNode. Typical connection: FuturesSymbolQueryNode → FuturesHistoricalDataNode → ConditionNode or → BacktestEngineNode. Supported exchanges: CME, EUREX, SGX, HKEX. Can be used as an AI Agent tool.",
119
+ "nodes.OverseasFuturesHistoricalDataNode.description": "Fetches exact-contract overseas-futures OHLCV through an upstream broker. Minute periods use one available o3103 page (200 rows requested); start/end dates are not sent and the requested period is not certified coverage. Completed bars, time zone, retention and sufficient lookback require separate verification. Daily/weekly/monthly use o3108. Rollover adjustment is not implemented; keep adjust=false.",
120
120
  "nodes.OverseasFuturesHistoricalDataNode.name": "Futures Historical Data",
121
121
  "nodes.OverseasFuturesMarketDataNode.description": "Fetches the current price, volume, and market data for a single overseas futures symbol as a one-time REST API call. Auto-iterates when the previous node outputs an array of symbols. Outputs market data including last price, change, volume, and bid/ask. Requires an upstream OverseasFuturesBrokerNode. Supported exchanges: CME, EUREX, SGX, HKEX. For continuous updates, use OverseasFuturesRealMarketDataNode. Can be used as an AI Agent tool.",
122
122
  "nodes.OverseasFuturesMarketDataNode.name": "Futures Market Data",
@@ -371,13 +371,13 @@
371
371
  "fields.OverseasFuturesCancelOrderNode.exchange": "Select the exchange.",
372
372
  "fields.OverseasFuturesCancelOrderNode.original_order_id": "Original order ID to cancel.",
373
373
  "fields.OverseasFuturesCancelOrderNode.symbol": "Symbol code.",
374
- "fields.OverseasFuturesHistoricalDataNode.adjust.detail": "Uses adjusted prices reflecting rollovers, etc. Recommended for backtesting.",
375
- "fields.OverseasFuturesHistoricalDataNode.adjust.short": "Apply adjusted price",
374
+ "fields.OverseasFuturesHistoricalDataNode.adjust.detail": "Must remain false. Rollover adjustment and cross-contract stitching are not implemented; true is rejected before broker access.",
375
+ "fields.OverseasFuturesHistoricalDataNode.adjust.short": "Exact-contract prices only (false)",
376
376
  "fields.OverseasFuturesHistoricalDataNode.end_date": "End date (YYYYMMDD)",
377
- "fields.OverseasFuturesHistoricalDataNode.end_date.help_text": "e.g., 20241231. With binding: {{ date.today(format='yyyymmdd') }}",
378
- "fields.OverseasFuturesHistoricalDataNode.interval": "Bar interval (1m, 5m, 15m, 1h, 1d, 1w, 1M)",
377
+ "fields.OverseasFuturesHistoricalDataNode.end_date.help_text": "e.g., 20241231. With binding: {{ date.today(format='yyyymmdd') }}. Minute queries do not send this date; the requested period is not actual coverage.",
378
+ "fields.OverseasFuturesHistoricalDataNode.interval": "Implemented periods: 30s, 1m, 5m, 15m, 30m, 1d, 1w, 1M. Hourly and unknown periods are rejected.",
379
379
  "fields.OverseasFuturesHistoricalDataNode.start_date": "Start date (YYYYMMDD)",
380
- "fields.OverseasFuturesHistoricalDataNode.start_date.help_text": "e.g., 20240101. With binding: {{ date.ago(30, format='yyyymmdd') }}",
380
+ "fields.OverseasFuturesHistoricalDataNode.start_date.help_text": "e.g., 20240101. With binding: {{ date.ago(30, format='yyyymmdd') }}. Used by daily/weekly/monthly requests; minute queries do not send this date and return one available page.",
381
381
  "fields.OverseasFuturesHistoricalDataNode.symbol": "Enter the futures symbol code to query",
382
382
  "fields.OverseasFuturesHistoricalDataNode.symbol.exchange": "Exchange",
383
383
  "fields.OverseasFuturesHistoricalDataNode.symbol.help_text": "Direct input or binding available (fx toggle)",
@@ -1035,6 +1035,8 @@
1035
1035
  "enums.interval.1Y": "1 year",
1036
1036
  "enums.interval.1d": "1 Day",
1037
1037
  "enums.interval.1h": "1 Hour",
1038
+ "enums.interval.30s": "30 seconds",
1039
+ "enums.interval.30m": "30 minutes",
1038
1040
  "enums.interval.1m": "1 Minute",
1039
1041
  "enums.interval.1w": "1 Week",
1040
1042
  "enums.interval.5m": "5 Minutes",
@@ -116,7 +116,7 @@
116
116
  "nodes.OverseasFuturesBrokerNode.name": "해외선물 브로커",
117
117
  "nodes.OverseasFuturesCancelOrderNode.description": "해외선물 미체결 주문을 원래 주문 ID로 취소합니다. FuturesOpenOrdersNode 또는 FuturesRealOrderEventNode에서 original_order_id를 받아야 합니다. 출력: 취소 결과, 취소된 주문 ID. 최소 5초 간격 제한 적용. 일반적인 연결: FuturesOpenOrdersNode → FuturesCancelOrderNode.",
118
118
  "nodes.OverseasFuturesCancelOrderNode.name": "해외선물 취소주문",
119
- "nodes.OverseasFuturesHistoricalDataNode.description": "지정 기간의 해외선물 과거 OHLCV 차트 데이터를 조회합니다. 1분봉부터 월봉까지 지원합니다. 롤오버 수정가(adjust) 활성화 권장(백테스트용). 이전 노드가 여러 종목을 출력하면 자동 반복 실행. 상위 OverseasFuturesBrokerNode 연결 필수. 일반적인 연결: FuturesSymbolQueryNode → FuturesHistoricalDataNode → ConditionNode 또는 → BacktestEngineNode. 지원 거래소: CME, EUREX, SGX, HKEX. AI Agent 도구로 사용 가능.",
119
+ "nodes.OverseasFuturesHistoricalDataNode.description": "연결한 증권 계좌로 지정 월물의 OHLCV를 조회합니다. 분봉은 o3103의 첫 페이지를 한 번 조회하며 200건을 요청합니다. 분봉 요청에는 시작·종료 날짜가 전송되지 않으며, 표시된 요청 기간 전체를 조회했다는 뜻이 아닙니다. 완료봉·시간대·보존 범위·필요 봉 수는 별도로 확인해야 합니다. 일·주·월봉은 o3108을 사용합니다. 롤오버 가격 보정은 구현되어 있지 않아 adjust는 false로 유지합니다.",
120
120
  "nodes.OverseasFuturesHistoricalDataNode.name": "해외선물 과거데이터",
121
121
  "nodes.OverseasFuturesMarketDataNode.description": "해외선물 종목의 현재가, 거래량, 시세 데이터를 REST API 1회 호출로 조회합니다. 이전 노드가 종목 배열을 출력하면 자동 반복 실행합니다. 출력: 최근 체결가, 등락폭, 거래량, 매수/매도 호가. 상위 OverseasFuturesBrokerNode 연결 필수. 지원 거래소: CME, EUREX, SGX, HKEX. 연속 업데이트가 필요하면 OverseasFuturesRealMarketDataNode를 사용하세요. AI Agent 도구로 사용 가능.",
122
122
  "nodes.OverseasFuturesMarketDataNode.name": "해외선물 현재가",
@@ -371,13 +371,13 @@
371
371
  "fields.OverseasFuturesCancelOrderNode.exchange": "거래소를 선택합니다.",
372
372
  "fields.OverseasFuturesCancelOrderNode.original_order_id": "취소할 원주문번호입니다.",
373
373
  "fields.OverseasFuturesCancelOrderNode.symbol": "종목 코드입니다.",
374
- "fields.OverseasFuturesHistoricalDataNode.adjust.detail": "롤오버 등 반영된 수정 가격을 사용합니다. 백테스트 시 권장합니다.",
375
- "fields.OverseasFuturesHistoricalDataNode.adjust.short": "수정주가 적용 여부",
374
+ "fields.OverseasFuturesHistoricalDataNode.adjust.detail": "false로 유지합니다. 롤오버 가격 보정과 월물 연결은 구현되어 있지 않으며, true는 증권사 접근 전에 거절됩니다.",
375
+ "fields.OverseasFuturesHistoricalDataNode.adjust.short": "지정 월물 가격만 사용 (false)",
376
376
  "fields.OverseasFuturesHistoricalDataNode.end_date": "조회 종료일 (YYYYMMDD)",
377
- "fields.OverseasFuturesHistoricalDataNode.end_date.help_text": "예: 20241231. 바인딩 시 {{ date.today(format='yyyymmdd') }} 사용 가능",
378
- "fields.OverseasFuturesHistoricalDataNode.interval": "봉 종류 (1m, 5m, 15m, 1h, 1d, 1w, 1M)",
377
+ "fields.OverseasFuturesHistoricalDataNode.end_date.help_text": "예: 20241231. 바인딩: {{ date.today(format='yyyymmdd') }}. 분봉은 이 날짜를 전송하지 않으며, 표시된 요청 기간이 실제 조회 범위를 보장하지 않습니다.",
378
+ "fields.OverseasFuturesHistoricalDataNode.interval": "지원 주기: 30s, 1m, 5m, 15m, 30m, 1d, 1w, 1M. 1h와 알 수 없는 주기는 거절됩니다.",
379
379
  "fields.OverseasFuturesHistoricalDataNode.start_date": "조회 시작일 (YYYYMMDD)",
380
- "fields.OverseasFuturesHistoricalDataNode.start_date.help_text": "예: 20240101. 바인딩 시 {{ date.ago(30, format='yyyymmdd') }} 사용 가능",
380
+ "fields.OverseasFuturesHistoricalDataNode.start_date.help_text": "예: 20240101. 바인딩: {{ date.ago(30, format='yyyymmdd') }}. 일·주·월봉 요청에 사용합니다. 분봉은 이 날짜를 전송하지 않고 첫 페이지를 한 번 조회합니다.",
381
381
  "fields.OverseasFuturesHistoricalDataNode.symbol": "조회할 선물 종목 코드를 입력하세요",
382
382
  "fields.OverseasFuturesHistoricalDataNode.symbol.exchange": "거래소",
383
383
  "fields.OverseasFuturesHistoricalDataNode.symbol.help_text": "직접 입력 또는 바인딩 가능 (fx 토글)",
@@ -1035,6 +1035,8 @@
1035
1035
  "enums.interval.1Y": "1년",
1036
1036
  "enums.interval.1d": "1일",
1037
1037
  "enums.interval.1h": "1시간",
1038
+ "enums.interval.30s": "30초",
1039
+ "enums.interval.30m": "30분",
1038
1040
  "enums.interval.1m": "1분",
1039
1041
  "enums.interval.1w": "1주",
1040
1042
  "enums.interval.5m": "5분",
@@ -10,7 +10,7 @@ Item-based execution:
10
10
  """
11
11
 
12
12
  from typing import Any, Optional, List, Literal, Dict, ClassVar, TYPE_CHECKING
13
- from pydantic import Field
13
+ from pydantic import Field, field_validator
14
14
 
15
15
  if TYPE_CHECKING:
16
16
  from programgarden_core.models.field_binding import FieldSchema
@@ -59,21 +59,27 @@ class OverseasFuturesHistoricalDataNode(BaseNode):
59
59
  )
60
60
  start_date: str = Field(
61
61
  default="{{ date.months_ago(3, format='yyyymmdd') }}",
62
- description="Start date (YYYY-MM-DD or {{ date.months_ago(N, format='yyyymmdd') }})",
62
+ description="Requested start date (YYYYMMDD or expression); not sent for minute periods.",
63
63
  )
64
64
  end_date: str = Field(
65
65
  default="{{ date.today(format='yyyymmdd') }}",
66
- description="End date (YYYY-MM-DD or {{ date.today(format='yyyymmdd') }})",
66
+ description="Requested end date (YYYYMMDD or expression); not sent for minute periods.",
67
67
  )
68
- interval: str = Field(
68
+ interval: Literal["30s", "1m", "5m", "15m", "30m", "1d", "1w", "1M"] = Field(
69
69
  default="1d",
70
- description="Data interval (1m, 5m, 15m, 1h, 1d)",
70
+ description="Implemented chart periods: 30s, 1m, 5m, 15m, 30m, 1d, 1w, 1M; hourly and unknown periods are rejected.",
71
71
  )
72
- adjust: bool = Field(
72
+ adjust: Literal[False] = Field(
73
73
  default=False,
74
- description="Apply adjusted prices",
74
+ description="Must remain false; only exact-contract prices are implemented, without rollover adjustment.",
75
75
  )
76
76
 
77
+ @field_validator("interval", mode="before")
78
+ @classmethod
79
+ def normalize_legacy_interval(cls, value):
80
+ aliases = {"1min": "1m", "5min": "5m", "15min": "15m", "30min": "30m"}
81
+ return aliases.get(value, value) if isinstance(value, str) else value
82
+
77
83
  @classmethod
78
84
  def is_tool_enabled(cls) -> bool:
79
85
  return True
@@ -82,7 +88,7 @@ class OverseasFuturesHistoricalDataNode(BaseNode):
82
88
  "when_to_use": [
83
89
  "Retrieve daily (or intraday) OHLCV history for an overseas futures contract (CME, EUREX, SGX, HKEX) for technical analysis or backtesting",
84
90
  "Feed historical candles into ConditionNode plugins (MACD, Bollinger, etc.) that operate on futures price series",
85
- "Use `{{ date.ago(N, format='yyyymmdd') }}` for start_date to maintain a rolling lookback window",
91
+ "Use date expressions for daily/weekly/monthly request dates; minute requests do not send dates or guarantee a lookback window",
86
92
  ],
87
93
  "when_not_to_use": [
88
94
  "For overseas stock historical data — use OverseasStockHistoricalDataNode",
@@ -97,9 +103,11 @@ class OverseasFuturesHistoricalDataNode(BaseNode):
97
103
  }
98
104
  _features: ClassVar[List[str]] = [
99
105
  "Returns time_series list of OHLCV candles: [{date, open, high, low, close, volume}, ...] ordered oldest-first",
100
- "Supports intervals: 1m, 5m, 15m, 1h, 1d, 1w, 1M — daily is the default for swing/position strategies",
106
+ "Supports implemented periods: 30s, 1m, 5m, 15m, 30m, 1d, 1w, 1M; legacy minute aliases are accepted; 1h and unknown periods are rejected",
101
107
  "start_date / end_date accept YYYYMMDD strings or `{{ date.ago(N, format='yyyymmdd') }}` expressions",
102
- "adjust=True applies roll-adjusted prices for continuous contract backtesting",
108
+ "adjust must remain false: exact-contract prices only, without rollover adjustment or month stitching",
109
+ "Minute history returns the available initial o3103 page (200 rows requested), not a complete requested range or a guaranteed latest-N set",
110
+ "Minute start/end dates are not sent to o3103; period echoes requested dates, not actual coverage. Bar finality, time zone, retention and sufficient lookback require separate evidence",
103
111
  "is_tool_enabled=True — AI Agent can fetch futures history for technical analysis autonomously",
104
112
  "Item-based execution: pair with SplitNode to fetch OHLCV for multiple contracts",
105
113
  "Replay resolves this node's request with the upstream broker's connection identity (provider, product, paper_trading, broker_node_id, and credential_id when the account is linked); a recording whose request omits that connection does not match",
@@ -117,9 +125,9 @@ class OverseasFuturesHistoricalDataNode(BaseNode):
117
125
  "alternative": "Bind `{{ nodes.historical.value.time_series }}` to the ConditionNode data field.",
118
126
  },
119
127
  {
120
- "pattern": "Using adjust=False for a continuous contract strategy that spans roll periods",
121
- "reason": "Unadjusted prices have discontinuities at contract roll dates, which distort momentum and mean-reversion signals.",
122
- "alternative": "Set adjust=True for strategies that look back across multiple roll periods.",
128
+ "pattern": "Treating exact-contract observations as continuous or rollover-adjusted data",
129
+ "reason": "Rollover adjustment and cross-contract stitching are not implemented.",
130
+ "alternative": "Keep adjust=False and verify this contract has the needed data; do not substitute internal history.",
123
131
  },
124
132
  ]
125
133
  _examples: ClassVar[List[Dict[str, Any]]] = [
@@ -158,7 +166,7 @@ class OverseasFuturesHistoricalDataNode(BaseNode):
158
166
  },
159
167
  {
160
168
  "title": "HKEX mini-futures intraday scan",
161
- "description": "Fetch 1-hour bars for the past 10 days for HKEX mini-futures and apply MACD.",
169
+ "description": "Request the available initial 30-minute page for HKEX mini-futures; verify completed bars and lookback before MACD.",
162
170
  "workflow_snippet": {
163
171
  "id": "overseas_futures_historical_hkex_macd",
164
172
  "name": "HKEX Intraday MACD",
@@ -166,7 +174,7 @@ class OverseasFuturesHistoricalDataNode(BaseNode):
166
174
  {"id": "start", "type": "StartNode"},
167
175
  {"id": "broker", "type": "OverseasFuturesBrokerNode", "credential_id": "broker_cred", "paper_trading": False},
168
176
  {'id': 'split', 'type': 'SplitNode', 'array': [{'symbol': 'MHIH26', 'exchange': 'HKEX'}]},
169
- {"id": "historical", "type": "OverseasFuturesHistoricalDataNode", "symbol": "{{ nodes.split.item }}", "start_date": "{{ date.ago(10, format='yyyymmdd') }}", "end_date": "{{ date.today(format='yyyymmdd') }}", "interval": "1h", "adjust": False},
177
+ {"id": "historical", "type": "OverseasFuturesHistoricalDataNode", "symbol": "{{ nodes.split.item }}", "start_date": "{{ date.ago(10, format='yyyymmdd') }}", "end_date": "{{ date.today(format='yyyymmdd') }}", "interval": "30m", "adjust": False},
170
178
  {"id": "condition", "type": "ConditionNode", "plugin": "MACD", "items": {"from": "{{ item.time_series }}", "extract": {"symbol": "{{ item.symbol }}", "exchange": "{{ item.exchange }}", "date": "{{ row.date }}", "close": "{{ row.close }}"}}, "fields": {"fast": 12, "slow": 26, "signal": 9, "direction": "bullish_cross"}},
171
179
  {'id': 'split_results', 'type': 'AggregateNode', 'mode': 'collect'}],
172
180
  "edges": [
@@ -187,7 +195,7 @@ class OverseasFuturesHistoricalDataNode(BaseNode):
187
195
  }
188
196
  ],
189
197
  },
190
- "expected_output": "MACD crossover signal computed from 1-hour HKEX mini-futures bars; result port emits buy/sell signal.",
198
+ "expected_output": "Available 30-minute observations only; complete range, finality and sufficient lookback are not certified.",
191
199
  },
192
200
  ]
193
201
  _node_guide: ClassVar[Dict[str, Any]] = {
@@ -195,7 +203,7 @@ class OverseasFuturesHistoricalDataNode(BaseNode):
195
203
  "The `symbol` field takes a single {exchange, symbol} dict — bind to `{{ nodes.split.item }}` for multi-contract iteration. "
196
204
  "Exchange values: CME, EUREX, SGX, HKEX. Symbol includes contract month code (e.g., ESH26). "
197
205
  "start_date / end_date: use `{{ date.ago(N, format='yyyymmdd') }}` for rolling lookback. "
198
- "interval options: 1m, 5m, 15m, 1h, 1d, 1w, 1M."
206
+ "interval options: 30s, 1m, 5m, 15m, 30m, 1d, 1w, 1M. Minute queries return one available page, not a complete requested date range."
199
207
  ),
200
208
  "output_consumption": (
201
209
  "The `value` port emits {symbol, exchange, time_series: [{date, open, high, low, close, volume}, ...]}. "
@@ -221,9 +229,9 @@ class OverseasFuturesHistoricalDataNode(BaseNode):
221
229
  OutputPort(name="value", type="ohlcv_data", description="i18n:ports.ohlcv_value", fields=HISTORICAL_VALUE_FIELDS),
222
230
  ]
223
231
 
224
- _version: ClassVar[str] = "1.0.0"
225
- _updated_at: ClassVar[str] = "2026-05-19"
226
- _change_note: ClassVar[Optional[str]] = None
232
+ _version: ClassVar[str] = "1.1.0"
233
+ _updated_at: ClassVar[str] = "2026-10-14"
234
+ _change_note: ClassVar[Optional[str]] = "Align minute aliases with one-page chart routing; reject unsupported periods and rollover adjustment."
227
235
 
228
236
  @classmethod
229
237
  def get_field_schema(cls) -> Dict[str, "FieldSchema"]:
@@ -296,12 +304,13 @@ class OverseasFuturesHistoricalDataNode(BaseNode):
296
304
  description="i18n:fields.OverseasFuturesHistoricalDataNode.interval",
297
305
  default="1d",
298
306
  required=True,
299
- enum_values=["1m", "5m", "15m", "1h", "1d", "1w", "1M"],
307
+ enum_values=["30s", "1m", "5m", "15m", "30m", "1d", "1w", "1M"],
300
308
  enum_labels={
309
+ "30s": "i18n:enums.interval.30s",
301
310
  "1m": "i18n:enums.interval.1m",
302
311
  "5m": "i18n:enums.interval.5m",
303
312
  "15m": "i18n:enums.interval.15m",
304
- "1h": "i18n:enums.interval.1h",
313
+ "30m": "i18n:enums.interval.30m",
305
314
  "1d": "i18n:enums.interval.1d",
306
315
  "1w": "i18n:enums.interval.1w",
307
316
  "1M": "i18n:enums.interval.1M"
@@ -319,8 +328,9 @@ class OverseasFuturesHistoricalDataNode(BaseNode):
319
328
  help_text="i18n:fields.OverseasFuturesHistoricalDataNode.adjust.detail",
320
329
  default=False,
321
330
  category=FieldCategory.PARAMETERS,
322
- expression_mode=ExpressionMode.BOTH,
331
+ expression_mode=ExpressionMode.FIXED_ONLY,
323
332
  ui_component=UIComponent.CHECKBOX,
324
- example=True,
333
+ example=False,
334
+ expected_type="bool",
325
335
  ),
326
336
  }
@@ -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.6.1"
8
+ version = "2.6.2"
9
9
  license = "AGPL-3.0-or-later"
10
10
  description = "ProgramGarden Core - 노드 기반 DSL 핵심 타입 정의"
11
11
  readme = "README.md"