programgarden 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.
- {programgarden-2.6.1 → programgarden-2.6.2}/PKG-INFO +4 -2
- {programgarden-2.6.1 → programgarden-2.6.2}/README.md +2 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/executor.py +25 -7
- programgarden-2.6.2/programgarden/futures_history.py +23 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/resolver.py +27 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/pyproject.toml +2 -2
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/__init__.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/binding_validator.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/client.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/code_worker.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/context.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/database/__init__.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/database/adjustment_delivery.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/database/broker_evidence.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/database/broker_order_totals.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/database/broker_snapshot.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/database/checkpoint_manager.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/database/db_naming.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/database/execution_storage.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/database/fill_reconciler.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/database/order_cancellation.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/database/order_recovery.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/database/owned_order_cancellation.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/database/position_reconciliation.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/database/query_builder.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/database/sqlite_scope.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/database/workflow_position_tracker.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/database/workflow_risk_tracker.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/deep_fixtures.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/futures_contract_info.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/futures_orderable.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/futures_pnl.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/futures_read_evidence.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/incremental_build.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/managed_order_control.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/node_runner.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/order_lifecycle.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/plugin/__init__.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/plugin/sandbox.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/providers/__init__.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/providers/llm_errors.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/providers/llm_provider.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/reconnect_handler.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/replay_contracts.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/replay_events.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/replay_external.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/replay_order_adapter.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/replay_orders.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/replay_scenarios.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/replay_semantics.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/replay_sources.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/replay_sqlite.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/replay_triggers.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/resource/__init__.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/resource/context.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/resource/limiter.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/resource/monitor.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/resource/throttle.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/semantic_rules.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/tools/__init__.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/tools/credential_tools.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/tools/definition_tools.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/tools/event_tools.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/tools/job_tools.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/tools/registry_tools.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/tools/sqlite_tools.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/validation_recommender.py +0 -0
- {programgarden-2.6.1 → programgarden-2.6.2}/programgarden/validation_replay.py +0 -0
- {programgarden-2.6.1 → 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,6 +27,8 @@ 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
|
|
|
32
34
|
An unreleased [stock history interval correction](../../docs/stock-history-interval-contract.md)
|
|
@@ -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
|
# ProgramGarden
|
|
2
4
|
|
|
3
5
|
An unreleased [stock history interval correction](../../docs/stock-history-interval-contract.md)
|
|
@@ -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,
|
|
@@ -12216,6 +12222,11 @@ class HistoricalDataNodeExecutor(NodeExecutorBase):
|
|
|
12216
12222
|
node_type == "HistoricalDataNode" and product in (None, "overseas_stock")
|
|
12217
12223
|
):
|
|
12218
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))
|
|
12219
12230
|
|
|
12220
12231
|
# deep_validate: HistoricalDataNode hits the real LS API. Inject a fixture
|
|
12221
12232
|
# time series so the flow completes without a network call.
|
|
@@ -12638,6 +12649,7 @@ class HistoricalDataNodeExecutor(NodeExecutorBase):
|
|
|
12638
12649
|
|
|
12639
12650
|
반환: [{symbol, exchange, time_series: [{date, open, high, low, close, volume}, ...]}, ...]
|
|
12640
12651
|
"""
|
|
12652
|
+
interval = normalize_futures_history_interval(interval)
|
|
12641
12653
|
credential = context.get_credential()
|
|
12642
12654
|
if not credential:
|
|
12643
12655
|
context.log("error", "Credential not set for HistoricalDataNode(futures)", node_id)
|
|
@@ -12669,7 +12681,7 @@ class HistoricalDataNodeExecutor(NodeExecutorBase):
|
|
|
12669
12681
|
symbol_to_exchange[sym] = exch
|
|
12670
12682
|
|
|
12671
12683
|
# interval별 분기
|
|
12672
|
-
is_minute_data = interval in
|
|
12684
|
+
is_minute_data = interval in FUTURES_MINUTE_INTERVALS
|
|
12673
12685
|
|
|
12674
12686
|
for symbol in symbols:
|
|
12675
12687
|
try:
|
|
@@ -12720,8 +12732,10 @@ class HistoricalDataNodeExecutor(NodeExecutorBase):
|
|
|
12720
12732
|
from programgarden_finance.ls.overseas_futureoption.chart.o3108.blocks import O3108InBlock
|
|
12721
12733
|
|
|
12722
12734
|
# interval → gubun 변환 (0=일, 1=주, 2=월)
|
|
12723
|
-
|
|
12724
|
-
|
|
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]
|
|
12725
12739
|
|
|
12726
12740
|
body = O3108InBlock(
|
|
12727
12741
|
shcode=symbol,
|
|
@@ -12763,17 +12777,21 @@ class HistoricalDataNodeExecutor(NodeExecutorBase):
|
|
|
12763
12777
|
context: ExecutionContext,
|
|
12764
12778
|
node_id: str,
|
|
12765
12779
|
) -> List[Dict[str, Any]]:
|
|
12766
|
-
"""
|
|
12780
|
+
"""Read the initial o3103 page; dates and complete-range coverage are not supplied."""
|
|
12767
12781
|
from programgarden_finance.ls.overseas_futureoption.chart.o3103.blocks import O3103InBlock
|
|
12768
12782
|
|
|
12769
12783
|
# interval → ncnt 변환 (0=30초, 1=1분, 5=5분, 30=30분)
|
|
12770
|
-
|
|
12771
|
-
|
|
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]
|
|
12772
12788
|
|
|
12773
12789
|
body = O3103InBlock(
|
|
12774
12790
|
shcode=symbol,
|
|
12775
12791
|
ncnt=ncnt,
|
|
12776
|
-
readcnt=200, #
|
|
12792
|
+
readcnt=200, # Existing application request size, not a documented LS maximum.
|
|
12793
|
+
cts_date="",
|
|
12794
|
+
cts_time="",
|
|
12777
12795
|
)
|
|
12778
12796
|
|
|
12779
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")
|
|
@@ -346,6 +346,7 @@ class WorkflowResolver:
|
|
|
346
346
|
# Order routing choices are literals, never prices or item bindings.
|
|
347
347
|
self._validate_order_enums(workflow, registry, result)
|
|
348
348
|
self._validate_stock_history_interval(workflow, registry, result)
|
|
349
|
+
self._validate_futures_history_contract(workflow, registry, result)
|
|
349
350
|
|
|
350
351
|
# 11. Static recommendations (topology analysis)
|
|
351
352
|
for rec in run_static_recommendation_rules(
|
|
@@ -386,6 +387,32 @@ class WorkflowResolver:
|
|
|
386
387
|
suggestion="Choose 1d, 1w, 1M or 1Y explicitly; do not substitute daily bars for an intraday strategy.",
|
|
387
388
|
))
|
|
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
|
+
|
|
389
416
|
def _validate_order_enums(self, workflow, registry, result) -> None:
|
|
390
417
|
"""Enforce fixed price/order types without changing dynamic close sides."""
|
|
391
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.1 → programgarden-2.6.2}/programgarden/database/owned_order_cancellation.py
RENAMED
|
File without changes
|
{programgarden-2.6.1 → programgarden-2.6.2}/programgarden/database/position_reconciliation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{programgarden-2.6.1 → 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
|