programgarden 2.6.0__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.
- {programgarden-2.6.0 → programgarden-2.6.2}/PKG-INFO +8 -2
- {programgarden-2.6.0 → programgarden-2.6.2}/README.md +6 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/executor.py +50 -12
- programgarden-2.6.2/programgarden/futures_history.py +23 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/resolver.py +51 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/pyproject.toml +2 -2
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/__init__.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/binding_validator.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/client.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/code_worker.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/context.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/database/__init__.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/database/adjustment_delivery.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/database/broker_evidence.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/database/broker_order_totals.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/database/broker_snapshot.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/database/checkpoint_manager.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/database/db_naming.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/database/execution_storage.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/database/fill_reconciler.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/database/order_cancellation.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/database/order_recovery.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/database/owned_order_cancellation.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/database/position_reconciliation.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/database/query_builder.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/database/sqlite_scope.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/database/workflow_position_tracker.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/database/workflow_risk_tracker.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/deep_fixtures.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/futures_contract_info.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/futures_orderable.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/futures_pnl.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/futures_read_evidence.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/incremental_build.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/managed_order_control.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/node_runner.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/order_lifecycle.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/plugin/__init__.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/plugin/sandbox.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/providers/__init__.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/providers/llm_errors.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/providers/llm_provider.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/reconnect_handler.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/replay_contracts.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/replay_events.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/replay_external.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/replay_order_adapter.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/replay_orders.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/replay_scenarios.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/replay_semantics.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/replay_sources.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/replay_sqlite.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/replay_triggers.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/resource/__init__.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/resource/context.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/resource/limiter.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/resource/monitor.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/resource/throttle.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/semantic_rules.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/tools/__init__.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/tools/credential_tools.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/tools/definition_tools.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/tools/event_tools.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/tools/job_tools.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/tools/registry_tools.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/tools/sqlite_tools.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/validation_recommender.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/validation_replay.py +0 -0
- {programgarden-2.6.0 → programgarden-2.6.2}/programgarden/valuation.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: programgarden
|
|
3
|
-
Version: 2.6.
|
|
3
|
+
Version: 2.6.2
|
|
4
4
|
Summary: ProgramGarden - 노드 기반 자동매매 DSL 실행 엔진
|
|
5
5
|
License-Expression: AGPL-3.0-or-later
|
|
6
6
|
Author: 프로그램동산
|
|
@@ -16,7 +16,7 @@ Requires-Dist: croniter (>=6.0.0,<7.0.0)
|
|
|
16
16
|
Requires-Dist: litellm (>=1.40.0)
|
|
17
17
|
Requires-Dist: lxml (>=6.0.2,<7.0.0)
|
|
18
18
|
Requires-Dist: programgarden-community (>=2.3.1,<3.0.0)
|
|
19
|
-
Requires-Dist: programgarden-core (>=2.6.
|
|
19
|
+
Requires-Dist: programgarden-core (>=2.6.2,<3.0.0)
|
|
20
20
|
Requires-Dist: programgarden-finance (>=2.0.1,<3.0.0)
|
|
21
21
|
Requires-Dist: psutil (>=6.0.0,<7.0.0)
|
|
22
22
|
Requires-Dist: psycopg2-binary (>=2.9.11,<3.0.0)
|
|
@@ -27,8 +27,14 @@ Requires-Dist: tzdata (>=2025.2,<2026.0)
|
|
|
27
27
|
Requires-Dist: yfinance (>=0.2.0,<0.3.0)
|
|
28
28
|
Description-Content-Type: text/markdown
|
|
29
29
|
|
|
30
|
+
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.
|
|
31
|
+
|
|
30
32
|
# ProgramGarden
|
|
31
33
|
|
|
34
|
+
An unreleased [stock history interval correction](../../docs/stock-history-interval-contract.md)
|
|
35
|
+
rejects unsupported intraday periods instead of silently querying daily bars.
|
|
36
|
+
Supported g3204 periods retain their existing requests and output contract.
|
|
37
|
+
|
|
32
38
|
An unreleased [futures detail and durable state candidate](../../docs/futures-contract-details-and-rollover-state.md)
|
|
33
39
|
adds exact o3105 metadata, opt-in execution-scoped SQLite and XHKG calendar
|
|
34
40
|
intersection. Missing evidence remains explicit; this does not authorize orders.
|
|
@@ -1,5 +1,11 @@
|
|
|
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
|
# ProgramGarden
|
|
2
4
|
|
|
5
|
+
An unreleased [stock history interval correction](../../docs/stock-history-interval-contract.md)
|
|
6
|
+
rejects unsupported intraday periods instead of silently querying daily bars.
|
|
7
|
+
Supported g3204 periods retain their existing requests and output contract.
|
|
8
|
+
|
|
3
9
|
An unreleased [futures detail and durable state candidate](../../docs/futures-contract-details-and-rollover-state.md)
|
|
4
10
|
adds exact o3105 metadata, opt-in execution-scoped SQLite and XHKG calendar
|
|
5
11
|
intersection. Missing evidence remains explicit; this does not authorize orders.
|
|
@@ -35,6 +35,12 @@ from programgarden_core import (
|
|
|
35
35
|
OVERSEAS_STOCK_ORDER_MARKET_CODES,
|
|
36
36
|
)
|
|
37
37
|
from programgarden.context import ExecutionContext, WorkflowEvent
|
|
38
|
+
from programgarden.futures_history import (
|
|
39
|
+
DAILY_INTERVALS as FUTURES_DAILY_INTERVALS,
|
|
40
|
+
MINUTE_INTERVALS as FUTURES_MINUTE_INTERVALS,
|
|
41
|
+
normalize_interval as normalize_futures_history_interval,
|
|
42
|
+
validate_adjust as validate_futures_history_adjust,
|
|
43
|
+
)
|
|
38
44
|
from programgarden.order_lifecycle import (
|
|
39
45
|
FUTURES_PRODUCTS, OrderLifecycleMetadata, exact_futures_credential,
|
|
40
46
|
inject_futures_connection, operation_key,
|
|
@@ -12186,7 +12192,7 @@ class HistoricalDataNodeExecutor(NodeExecutorBase):
|
|
|
12186
12192
|
HistoricalDataNode executor - 과거 OHLCV 데이터 조회
|
|
12187
12193
|
|
|
12188
12194
|
LS Finance Chart API를 사용하여 과거 차트 데이터를 조회합니다.
|
|
12189
|
-
-
|
|
12195
|
+
- Overseas stock: g3204 (daily/weekly/monthly/yearly; no intraday)
|
|
12190
12196
|
- 해외선물: o3108 (일봉), o3103 (분봉)
|
|
12191
12197
|
"""
|
|
12192
12198
|
|
|
@@ -12208,6 +12214,20 @@ class HistoricalDataNodeExecutor(NodeExecutorBase):
|
|
|
12208
12214
|
) -> Dict[str, Any]:
|
|
12209
12215
|
"""과거 데이터 조회"""
|
|
12210
12216
|
|
|
12217
|
+
# Reject unsupported stock periods before fixtures can manufacture success.
|
|
12218
|
+
# The broker-specific fetch repeats this guard for direct/legacy callers.
|
|
12219
|
+
connection = config.get("connection")
|
|
12220
|
+
product = connection.get("product") if isinstance(connection, dict) else None
|
|
12221
|
+
if node_type == "OverseasStockHistoricalDataNode" or (
|
|
12222
|
+
node_type == "HistoricalDataNode" and product in (None, "overseas_stock")
|
|
12223
|
+
):
|
|
12224
|
+
self._stock_history_gubun(config.get("interval", "1d"))
|
|
12225
|
+
if node_type == "OverseasFuturesHistoricalDataNode" or (
|
|
12226
|
+
node_type == "HistoricalDataNode" and product in FUTURES_PRODUCTS
|
|
12227
|
+
):
|
|
12228
|
+
normalize_futures_history_interval(config.get("interval", "1d"))
|
|
12229
|
+
validate_futures_history_adjust(config.get("adjust", False))
|
|
12230
|
+
|
|
12211
12231
|
# deep_validate: HistoricalDataNode hits the real LS API. Inject a fixture
|
|
12212
12232
|
# time series so the flow completes without a network call.
|
|
12213
12233
|
if context.is_deep_validate:
|
|
@@ -12451,6 +12471,18 @@ class HistoricalDataNodeExecutor(NodeExecutorBase):
|
|
|
12451
12471
|
# 파싱 실패 시 오늘 날짜 반환
|
|
12452
12472
|
return datetime.now().strftime("%Y%m%d")
|
|
12453
12473
|
|
|
12474
|
+
@staticmethod
|
|
12475
|
+
def _stock_history_gubun(interval: str) -> str:
|
|
12476
|
+
"""Reject unsupported periods instead of relabeling daily bars."""
|
|
12477
|
+
periods = {"1d": "2", "1w": "3", "1M": "4", "1Y": "5"}
|
|
12478
|
+
if not isinstance(interval, str) or interval not in periods:
|
|
12479
|
+
raise ValueError(
|
|
12480
|
+
"UNSUPPORTED_STOCK_HISTORY_INTERVAL: OverseasStockHistoricalDataNode "
|
|
12481
|
+
"supports 1d, 1w, 1M and 1Y through LS g3204. "
|
|
12482
|
+
"Intraday g3203 is not supported; select a supported period explicitly."
|
|
12483
|
+
)
|
|
12484
|
+
return periods[interval]
|
|
12485
|
+
|
|
12454
12486
|
async def _fetch_overseas_stock(
|
|
12455
12487
|
self,
|
|
12456
12488
|
symbols: List[str],
|
|
@@ -12472,6 +12504,9 @@ class HistoricalDataNodeExecutor(NodeExecutorBase):
|
|
|
12472
12504
|
반환: [{symbol, exchange, time_series: [{date, open, high, low, close, volume}, ...]}, ...]
|
|
12473
12505
|
"""
|
|
12474
12506
|
|
|
12507
|
+
# Validate before credential access or login, outside the soft-failure handler.
|
|
12508
|
+
gubun = self._stock_history_gubun(interval)
|
|
12509
|
+
|
|
12475
12510
|
credential = context.get_credential()
|
|
12476
12511
|
if not credential:
|
|
12477
12512
|
context.log("error", "Credential not set for HistoricalDataNode", node_id)
|
|
@@ -12494,10 +12529,6 @@ class HistoricalDataNodeExecutor(NodeExecutorBase):
|
|
|
12494
12529
|
|
|
12495
12530
|
api = ls.overseas_stock()
|
|
12496
12531
|
|
|
12497
|
-
# interval → gubun 변환 (g3204: 2=일, 3=주, 4=월, 5=년)
|
|
12498
|
-
gubun_map = {"1d": "2", "1w": "3", "1M": "4", "1Y": "5"}
|
|
12499
|
-
gubun = gubun_map.get(interval, "2")
|
|
12500
|
-
|
|
12501
12532
|
# 거래소 코드 매핑 (역변환용)
|
|
12502
12533
|
exchcd_to_exchange = {"82": "NASDAQ", "81": "NYSE", "83": "AMEX"}
|
|
12503
12534
|
|
|
@@ -12618,6 +12649,7 @@ class HistoricalDataNodeExecutor(NodeExecutorBase):
|
|
|
12618
12649
|
|
|
12619
12650
|
반환: [{symbol, exchange, time_series: [{date, open, high, low, close, volume}, ...]}, ...]
|
|
12620
12651
|
"""
|
|
12652
|
+
interval = normalize_futures_history_interval(interval)
|
|
12621
12653
|
credential = context.get_credential()
|
|
12622
12654
|
if not credential:
|
|
12623
12655
|
context.log("error", "Credential not set for HistoricalDataNode(futures)", node_id)
|
|
@@ -12649,7 +12681,7 @@ class HistoricalDataNodeExecutor(NodeExecutorBase):
|
|
|
12649
12681
|
symbol_to_exchange[sym] = exch
|
|
12650
12682
|
|
|
12651
12683
|
# interval별 분기
|
|
12652
|
-
is_minute_data = interval in
|
|
12684
|
+
is_minute_data = interval in FUTURES_MINUTE_INTERVALS
|
|
12653
12685
|
|
|
12654
12686
|
for symbol in symbols:
|
|
12655
12687
|
try:
|
|
@@ -12700,8 +12732,10 @@ class HistoricalDataNodeExecutor(NodeExecutorBase):
|
|
|
12700
12732
|
from programgarden_finance.ls.overseas_futureoption.chart.o3108.blocks import O3108InBlock
|
|
12701
12733
|
|
|
12702
12734
|
# interval → gubun 변환 (0=일, 1=주, 2=월)
|
|
12703
|
-
|
|
12704
|
-
|
|
12735
|
+
interval = normalize_futures_history_interval(interval)
|
|
12736
|
+
if interval not in FUTURES_DAILY_INTERVALS:
|
|
12737
|
+
raise ValueError("FUTURES_HISTORY_DAILY_INTERVAL_REQUIRED")
|
|
12738
|
+
gubun = FUTURES_DAILY_INTERVALS[interval]
|
|
12705
12739
|
|
|
12706
12740
|
body = O3108InBlock(
|
|
12707
12741
|
shcode=symbol,
|
|
@@ -12743,17 +12777,21 @@ class HistoricalDataNodeExecutor(NodeExecutorBase):
|
|
|
12743
12777
|
context: ExecutionContext,
|
|
12744
12778
|
node_id: str,
|
|
12745
12779
|
) -> List[Dict[str, Any]]:
|
|
12746
|
-
"""
|
|
12780
|
+
"""Read the initial o3103 page; dates and complete-range coverage are not supplied."""
|
|
12747
12781
|
from programgarden_finance.ls.overseas_futureoption.chart.o3103.blocks import O3103InBlock
|
|
12748
12782
|
|
|
12749
12783
|
# interval → ncnt 변환 (0=30초, 1=1분, 5=5분, 30=30분)
|
|
12750
|
-
|
|
12751
|
-
|
|
12784
|
+
interval = normalize_futures_history_interval(interval)
|
|
12785
|
+
if interval not in FUTURES_MINUTE_INTERVALS:
|
|
12786
|
+
raise ValueError("FUTURES_HISTORY_MINUTE_INTERVAL_REQUIRED")
|
|
12787
|
+
ncnt = FUTURES_MINUTE_INTERVALS[interval]
|
|
12752
12788
|
|
|
12753
12789
|
body = O3103InBlock(
|
|
12754
12790
|
shcode=symbol,
|
|
12755
12791
|
ncnt=ncnt,
|
|
12756
|
-
readcnt=200, #
|
|
12792
|
+
readcnt=200, # Existing application request size, not a documented LS maximum.
|
|
12793
|
+
cts_date="",
|
|
12794
|
+
cts_time="",
|
|
12757
12795
|
)
|
|
12758
12796
|
|
|
12759
12797
|
result = await api.chart().o3103(body=body).req_async()
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Explicit mappings for the existing single-page LS futures chart requests."""
|
|
2
|
+
|
|
3
|
+
MINUTE_INTERVALS = {"30s": 0, "1m": 1, "5m": 5, "15m": 15, "30m": 30}
|
|
4
|
+
DAILY_INTERVALS = {"1d": "0", "1w": "1", "1M": "2"}
|
|
5
|
+
INTERVAL_ALIASES = {"1min": "1m", "5min": "5m", "15min": "15m", "30min": "30m"}
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def normalize_interval(interval):
|
|
9
|
+
"""Preserve known legacy minute names without falling back to daily data."""
|
|
10
|
+
if isinstance(interval, str):
|
|
11
|
+
interval = INTERVAL_ALIASES.get(interval, interval)
|
|
12
|
+
if interval in MINUTE_INTERVALS or interval in DAILY_INTERVALS:
|
|
13
|
+
return interval
|
|
14
|
+
raise ValueError(
|
|
15
|
+
"UNSUPPORTED_FUTURES_HISTORY_INTERVAL: choose 30s, 1m, 5m, 15m, 30m, "
|
|
16
|
+
"1d, 1w or 1M. Hourly and unknown periods are not implemented; no daily fallback."
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def validate_adjust(adjust):
|
|
21
|
+
"""Never describe exact-contract prices as rollover-adjusted history."""
|
|
22
|
+
if adjust is not False:
|
|
23
|
+
raise ValueError("FUTURES_HISTORY_ADJUST_UNSUPPORTED: adjust must be false; rollover adjustment is not implemented")
|
|
@@ -345,6 +345,8 @@ class WorkflowResolver:
|
|
|
345
345
|
|
|
346
346
|
# Order routing choices are literals, never prices or item bindings.
|
|
347
347
|
self._validate_order_enums(workflow, registry, result)
|
|
348
|
+
self._validate_stock_history_interval(workflow, registry, result)
|
|
349
|
+
self._validate_futures_history_contract(workflow, registry, result)
|
|
348
350
|
|
|
349
351
|
# 11. Static recommendations (topology analysis)
|
|
350
352
|
for rec in run_static_recommendation_rules(
|
|
@@ -362,6 +364,55 @@ class WorkflowResolver:
|
|
|
362
364
|
|
|
363
365
|
return result
|
|
364
366
|
|
|
367
|
+
def _validate_stock_history_interval(self, workflow, registry, result) -> None:
|
|
368
|
+
"""Keep static/deep validation aligned with the stock chart executor."""
|
|
369
|
+
node_type = "OverseasStockHistoricalDataNode"
|
|
370
|
+
node_class = registry.get(node_type)
|
|
371
|
+
if node_class is None:
|
|
372
|
+
return
|
|
373
|
+
field = node_class.get_field_schema()["interval"]
|
|
374
|
+
for node in workflow.nodes:
|
|
375
|
+
if node.get("type") != node_type:
|
|
376
|
+
continue
|
|
377
|
+
if node.get("interval", field.default) in field.enum_values:
|
|
378
|
+
continue
|
|
379
|
+
result.add(build_error(
|
|
380
|
+
ErrorCode.INVALID_FIELD_ENUM,
|
|
381
|
+
"Stock historical intervals require a supported LS g3204 period; "
|
|
382
|
+
"intraday g3203 is not supported.",
|
|
383
|
+
location=ErrorLocation(
|
|
384
|
+
node_id=node.get("id"), node_type=node_type, field_path="interval",
|
|
385
|
+
),
|
|
386
|
+
available_values=field.enum_values,
|
|
387
|
+
suggestion="Choose 1d, 1w, 1M or 1Y explicitly; do not substitute daily bars for an intraday strategy.",
|
|
388
|
+
))
|
|
389
|
+
|
|
390
|
+
def _validate_futures_history_contract(self, workflow, registry, result) -> None:
|
|
391
|
+
"""Keep native validation aligned with period and adjustment routing."""
|
|
392
|
+
from programgarden.futures_history import normalize_interval, validate_adjust
|
|
393
|
+
|
|
394
|
+
node_type = "OverseasFuturesHistoricalDataNode"
|
|
395
|
+
node_class = registry.get(node_type)
|
|
396
|
+
if node_class is None:
|
|
397
|
+
return
|
|
398
|
+
field = node_class.get_field_schema()["interval"]
|
|
399
|
+
for node in workflow.nodes:
|
|
400
|
+
if node.get("type") != node_type:
|
|
401
|
+
continue
|
|
402
|
+
for name, check, default, available in (
|
|
403
|
+
("interval", normalize_interval, field.default, field.enum_values),
|
|
404
|
+
("adjust", validate_adjust, False, ["false"]),
|
|
405
|
+
):
|
|
406
|
+
try:
|
|
407
|
+
check(node.get(name, default))
|
|
408
|
+
except ValueError as exc:
|
|
409
|
+
result.add(build_error(
|
|
410
|
+
ErrorCode.INVALID_FIELD_ENUM, str(exc),
|
|
411
|
+
location=ErrorLocation(node_id=node.get("id"), node_type=node_type, field_path=name),
|
|
412
|
+
available_values=available,
|
|
413
|
+
suggestion="Choose a supported period and keep adjust=false; only exact-contract prices are implemented.",
|
|
414
|
+
))
|
|
415
|
+
|
|
365
416
|
def _validate_order_enums(self, workflow, registry, result) -> None:
|
|
366
417
|
"""Enforce fixed price/order types without changing dynamic close sides."""
|
|
367
418
|
from programgarden_core.models.field_binding import ExpressionMode
|
|
@@ -5,7 +5,7 @@ authors = [
|
|
|
5
5
|
homepage = "https://programgarden.com"
|
|
6
6
|
requires-python = ">=3.12"
|
|
7
7
|
name = "programgarden"
|
|
8
|
-
version = "2.6.
|
|
8
|
+
version = "2.6.2"
|
|
9
9
|
license = "AGPL-3.0-or-later"
|
|
10
10
|
description = "ProgramGarden - 노드 기반 자동매매 DSL 실행 엔진"
|
|
11
11
|
readme = "README.md"
|
|
@@ -36,7 +36,7 @@ litellm = ">=1.40.0"
|
|
|
36
36
|
# 로 통째 실패한다(1.35.1 의 조용한 TypeError 보다 강한 모듈 로드 실패).
|
|
37
37
|
# (1.26.0 이후로도 context.py 는 WorkflowPnLEvent(**event_data) 에 personal_metrics
|
|
38
38
|
# 를 무조건 실으므로 그 하한 근거도 이 상한에 포함된다.)
|
|
39
|
-
programgarden-core = "^2.6.
|
|
39
|
+
programgarden-core = "^2.6.2"
|
|
40
40
|
programgarden-finance = "^2.0.1"
|
|
41
41
|
programgarden-community = "^2.3.1"
|
|
42
42
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{programgarden-2.6.0 → programgarden-2.6.2}/programgarden/database/owned_order_cancellation.py
RENAMED
|
File without changes
|
{programgarden-2.6.0 → programgarden-2.6.2}/programgarden/database/position_reconciliation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{programgarden-2.6.0 → programgarden-2.6.2}/programgarden/database/workflow_position_tracker.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|