programgarden 1.22.2__tar.gz → 1.22.3__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-1.22.2 → programgarden-1.22.3}/PKG-INFO +5 -4
- {programgarden-1.22.2 → programgarden-1.22.3}/programgarden/executor.py +140 -21
- {programgarden-1.22.2 → programgarden-1.22.3}/pyproject.toml +4 -9
- {programgarden-1.22.2 → programgarden-1.22.3}/README.md +0 -0
- {programgarden-1.22.2 → programgarden-1.22.3}/programgarden/__init__.py +0 -0
- {programgarden-1.22.2 → programgarden-1.22.3}/programgarden/binding_validator.py +0 -0
- {programgarden-1.22.2 → programgarden-1.22.3}/programgarden/client.py +0 -0
- {programgarden-1.22.2 → programgarden-1.22.3}/programgarden/context.py +0 -0
- {programgarden-1.22.2 → programgarden-1.22.3}/programgarden/database/__init__.py +0 -0
- {programgarden-1.22.2 → programgarden-1.22.3}/programgarden/database/checkpoint_manager.py +0 -0
- {programgarden-1.22.2 → programgarden-1.22.3}/programgarden/database/query_builder.py +0 -0
- {programgarden-1.22.2 → programgarden-1.22.3}/programgarden/database/workflow_position_tracker.py +0 -0
- {programgarden-1.22.2 → programgarden-1.22.3}/programgarden/database/workflow_risk_tracker.py +0 -0
- {programgarden-1.22.2 → programgarden-1.22.3}/programgarden/node_runner.py +0 -0
- {programgarden-1.22.2 → programgarden-1.22.3}/programgarden/plugin/__init__.py +0 -0
- {programgarden-1.22.2 → programgarden-1.22.3}/programgarden/plugin/sandbox.py +0 -0
- {programgarden-1.22.2 → programgarden-1.22.3}/programgarden/providers/__init__.py +0 -0
- {programgarden-1.22.2 → programgarden-1.22.3}/programgarden/providers/llm_errors.py +0 -0
- {programgarden-1.22.2 → programgarden-1.22.3}/programgarden/providers/llm_provider.py +0 -0
- {programgarden-1.22.2 → programgarden-1.22.3}/programgarden/reconnect_handler.py +0 -0
- {programgarden-1.22.2 → programgarden-1.22.3}/programgarden/resolver.py +0 -0
- {programgarden-1.22.2 → programgarden-1.22.3}/programgarden/resource/__init__.py +0 -0
- {programgarden-1.22.2 → programgarden-1.22.3}/programgarden/resource/context.py +0 -0
- {programgarden-1.22.2 → programgarden-1.22.3}/programgarden/resource/limiter.py +0 -0
- {programgarden-1.22.2 → programgarden-1.22.3}/programgarden/resource/monitor.py +0 -0
- {programgarden-1.22.2 → programgarden-1.22.3}/programgarden/resource/throttle.py +0 -0
- {programgarden-1.22.2 → programgarden-1.22.3}/programgarden/tools/__init__.py +0 -0
- {programgarden-1.22.2 → programgarden-1.22.3}/programgarden/tools/credential_tools.py +0 -0
- {programgarden-1.22.2 → programgarden-1.22.3}/programgarden/tools/definition_tools.py +0 -0
- {programgarden-1.22.2 → programgarden-1.22.3}/programgarden/tools/event_tools.py +0 -0
- {programgarden-1.22.2 → programgarden-1.22.3}/programgarden/tools/job_tools.py +0 -0
- {programgarden-1.22.2 → programgarden-1.22.3}/programgarden/tools/registry_tools.py +0 -0
- {programgarden-1.22.2 → programgarden-1.22.3}/programgarden/tools/sqlite_tools.py +0 -0
- {programgarden-1.22.2 → programgarden-1.22.3}/programgarden/validation_recommender.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: programgarden
|
|
3
|
-
Version: 1.22.
|
|
3
|
+
Version: 1.22.3
|
|
4
4
|
Summary: ProgramGarden - 노드 기반 자동매매 DSL 실행 엔진
|
|
5
5
|
Author: 프로그램동산
|
|
6
6
|
Author-email: coding@programgarden.com
|
|
@@ -8,13 +8,14 @@ Requires-Python: >=3.12
|
|
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
|
9
9
|
Classifier: Programming Language :: Python :: 3.12
|
|
10
10
|
Classifier: Programming Language :: Python :: 3.13
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
11
12
|
Requires-Dist: aiohttp (>=3.9.0,<4.0.0)
|
|
12
13
|
Requires-Dist: aiosqlite (>=0.20.0,<0.21.0)
|
|
13
14
|
Requires-Dist: croniter (>=6.0.0,<7.0.0)
|
|
14
15
|
Requires-Dist: litellm (>=1.40.0)
|
|
15
16
|
Requires-Dist: lxml (>=6.0.2,<7.0.0)
|
|
16
|
-
Requires-Dist: programgarden-community (>=1.13.
|
|
17
|
-
Requires-Dist: programgarden-core (>=1.14.
|
|
17
|
+
Requires-Dist: programgarden-community (>=1.13.8,<2.0.0)
|
|
18
|
+
Requires-Dist: programgarden-core (>=1.14.3,<2.0.0)
|
|
18
19
|
Requires-Dist: programgarden-finance (>=1.6.9,<2.0.0)
|
|
19
20
|
Requires-Dist: psutil (>=6.0.0,<7.0.0)
|
|
20
21
|
Requires-Dist: psycopg2-binary (>=2.9.11,<3.0.0)
|
|
@@ -8364,39 +8364,48 @@ class LogicNodeExecutor(NodeExecutorBase):
|
|
|
8364
8364
|
condition_results: List[Dict[str, Any]] = []
|
|
8365
8365
|
all_passed_symbols: List[List[str]] = []
|
|
8366
8366
|
weights: Dict[int, float] = {} # index -> weight
|
|
8367
|
-
|
|
8367
|
+
|
|
8368
8368
|
for idx, cond in enumerate(conditions):
|
|
8369
8369
|
# 조건 객체 검증
|
|
8370
8370
|
if not isinstance(cond, dict):
|
|
8371
8371
|
context.log("warning", f"Condition at index {idx} is not a dict, skipping", node_id)
|
|
8372
8372
|
continue
|
|
8373
|
-
|
|
8373
|
+
|
|
8374
8374
|
# is_condition_met 필수
|
|
8375
|
+
# 다종목 auto-iterate 시 is_condition_met 바인딩이 병합 리스트
|
|
8376
|
+
# ([False, False, ...]) 로 해석될 수 있다. 리스트는 "통과 여부"가
|
|
8377
|
+
# 아니라 "실행 여부"를 뜻하게 되므로, any() 로 스칼라화해 per-condition
|
|
8378
|
+
# 통과 여부로 환원한다. (None 은 미제공 → 기존처럼 False + 경고)
|
|
8375
8379
|
is_met = cond.get("is_condition_met")
|
|
8376
8380
|
if is_met is None:
|
|
8377
8381
|
context.log("warning", f"Condition at index {idx} missing 'is_condition_met', treating as False", node_id)
|
|
8378
|
-
|
|
8379
|
-
|
|
8380
|
-
|
|
8381
|
-
|
|
8382
|
-
|
|
8383
|
-
|
|
8384
|
-
|
|
8385
|
-
|
|
8386
|
-
|
|
8382
|
+
is_met_scalar = False
|
|
8383
|
+
elif isinstance(is_met, list):
|
|
8384
|
+
is_met_scalar = any(bool(x) for x in is_met)
|
|
8385
|
+
else:
|
|
8386
|
+
is_met_scalar = bool(is_met)
|
|
8387
|
+
|
|
8388
|
+
# passed_symbols: None(미제공) 과 [](명시 빈 리스트) 를 구분한다.
|
|
8389
|
+
# - 명시 리스트(빈 리스트 포함) → "symbol-bearing" 조건 (교집합 참여)
|
|
8390
|
+
# - 미제공/None/비리스트 → "boolean-gate" 조건 (bool 게이팅만)
|
|
8391
|
+
raw_passed = cond.get("passed_symbols")
|
|
8392
|
+
symbols_provided = isinstance(raw_passed, list)
|
|
8393
|
+
passed_symbols = raw_passed if symbols_provided else []
|
|
8394
|
+
|
|
8387
8395
|
# weight (optional, default 1.0)
|
|
8388
8396
|
weight = cond.get("weight", 1.0)
|
|
8389
8397
|
if not isinstance(weight, (int, float)):
|
|
8390
8398
|
weight = 1.0
|
|
8391
8399
|
weights[idx] = float(weight)
|
|
8392
|
-
|
|
8400
|
+
|
|
8393
8401
|
condition_results.append({
|
|
8394
8402
|
"index": idx,
|
|
8395
|
-
"result":
|
|
8403
|
+
"result": is_met_scalar,
|
|
8396
8404
|
"passed_symbols": passed_symbols,
|
|
8405
|
+
"symbols_provided": symbols_provided,
|
|
8397
8406
|
"weight": weight,
|
|
8398
8407
|
})
|
|
8399
|
-
all_passed_symbols.append(passed_symbols
|
|
8408
|
+
all_passed_symbols.append(passed_symbols)
|
|
8400
8409
|
|
|
8401
8410
|
if not condition_results:
|
|
8402
8411
|
context.log("warning", "No valid condition results to combine", node_id)
|
|
@@ -8486,8 +8495,16 @@ class LogicNodeExecutor(NodeExecutorBase):
|
|
|
8486
8495
|
elif isinstance(s, str):
|
|
8487
8496
|
codes.add(s)
|
|
8488
8497
|
return codes
|
|
8489
|
-
|
|
8490
|
-
|
|
8498
|
+
|
|
8499
|
+
# symbol-bearing 조건(passed_symbols 명시 제공)만 교집합에 참여한다.
|
|
8500
|
+
# 빈 리스트도 명시 제공이면 포함 → AND 교집합을 [] 로 영점화한다.
|
|
8501
|
+
# boolean-gate 조건(미제공)은 심볼 의미가 없으므로 교집합에서 제외하고
|
|
8502
|
+
# bool(result) 로만 게이팅된다.
|
|
8503
|
+
sets = [
|
|
8504
|
+
extract_codes(r["passed_symbols"])
|
|
8505
|
+
for r in results
|
|
8506
|
+
if r.get("symbols_provided")
|
|
8507
|
+
]
|
|
8491
8508
|
if not sets:
|
|
8492
8509
|
return []
|
|
8493
8510
|
common = sets[0]
|
|
@@ -9968,11 +9985,18 @@ class BacktestEngineNodeExecutor(NodeExecutorBase):
|
|
|
9968
9985
|
context.log("warning", f"External binding evaluation failed for {target_field}: {e}", node_id)
|
|
9969
9986
|
external_values[target_field] = source_expr
|
|
9970
9987
|
|
|
9988
|
+
# 기본 컨텍스트 dict (row 제외) — ExpressionContext 는 mapping 이 아니므로
|
|
9989
|
+
# 반드시 to_dict() 로 평탄화해야 한다. 이전엔 {**expr_context} 로 객체를
|
|
9990
|
+
# 직접 언팩하려다 비어있지 않은 from_data 의 첫 row 에서 TypeError 로
|
|
9991
|
+
# 크래시했다 (dry_run 은 mock 이 빈 time_series 를 줘 루프가 안 돌아 은폐됨).
|
|
9992
|
+
base_dict = expr_context.to_dict()
|
|
9993
|
+
|
|
9971
9994
|
# 각 row 처리
|
|
9972
9995
|
for row in from_data:
|
|
9973
9996
|
record = {}
|
|
9974
|
-
|
|
9975
|
-
|
|
9997
|
+
row_ctx = ExpressionContext()
|
|
9998
|
+
row_ctx.variables = {**base_dict, "row": row}
|
|
9999
|
+
row_evaluator = ExpressionEvaluator(row_ctx)
|
|
9976
10000
|
|
|
9977
10001
|
for target_field, source_expr in extract.items():
|
|
9978
10002
|
if target_field in external_values:
|
|
@@ -12891,6 +12915,7 @@ class ModifyOrderNodeExecutor(NodeExecutorBase):
|
|
|
12891
12915
|
)
|
|
12892
12916
|
return {
|
|
12893
12917
|
"order_id": order_id,
|
|
12918
|
+
"modified_order_id": order_id,
|
|
12894
12919
|
"status": "simulated",
|
|
12895
12920
|
"dry_run": True,
|
|
12896
12921
|
"requested": config,
|
|
@@ -13038,13 +13063,33 @@ class ModifyOrderNodeExecutor(NodeExecutorBase):
|
|
|
13038
13063
|
"original_order_id": original_order_id,
|
|
13039
13064
|
"product": "overseas_stock",
|
|
13040
13065
|
},
|
|
13066
|
+
"modified_order_id": "",
|
|
13041
13067
|
"modified_order": None,
|
|
13042
13068
|
}
|
|
13043
13069
|
|
|
13044
13070
|
new_order_no = ""
|
|
13045
13071
|
if response.block2:
|
|
13046
13072
|
new_order_no = str(response.block2.OrdNo) if response.block2.OrdNo else ""
|
|
13047
|
-
|
|
13073
|
+
|
|
13074
|
+
# 정정 빈-주문번호 가드 (거래시간 외/정정 불가 상태 silent no-op 차단)
|
|
13075
|
+
if not new_order_no:
|
|
13076
|
+
msg = response.rsp_msg or "정정 미반영"
|
|
13077
|
+
context.log(
|
|
13078
|
+
"warning",
|
|
13079
|
+
f"Modify order returned no OrderNo: {symbol} - {msg}",
|
|
13080
|
+
node_id,
|
|
13081
|
+
)
|
|
13082
|
+
return {
|
|
13083
|
+
"modify_result": {
|
|
13084
|
+
"success": False,
|
|
13085
|
+
"error": f"Empty modify order number: {msg} (거래시간 외/정정 불가 상태 가능)",
|
|
13086
|
+
"original_order_id": original_order_id,
|
|
13087
|
+
"product": "overseas_stock",
|
|
13088
|
+
},
|
|
13089
|
+
"modified_order_id": "",
|
|
13090
|
+
"modified_order": None,
|
|
13091
|
+
}
|
|
13092
|
+
|
|
13048
13093
|
context.log(
|
|
13049
13094
|
"info",
|
|
13050
13095
|
f"Order modified: {symbol} original={original_order_id} → new={new_order_no}",
|
|
@@ -13058,6 +13103,7 @@ class ModifyOrderNodeExecutor(NodeExecutorBase):
|
|
|
13058
13103
|
"new_order_id": new_order_no,
|
|
13059
13104
|
"product": "overseas_stock",
|
|
13060
13105
|
},
|
|
13106
|
+
"modified_order_id": new_order_no,
|
|
13061
13107
|
"modified_order": {
|
|
13062
13108
|
"symbol": symbol,
|
|
13063
13109
|
"exchange": exchange,
|
|
@@ -13137,14 +13183,34 @@ class ModifyOrderNodeExecutor(NodeExecutorBase):
|
|
|
13137
13183
|
"original_order_id": original_order_id,
|
|
13138
13184
|
"product": "overseas_futures",
|
|
13139
13185
|
},
|
|
13186
|
+
"modified_order_id": "",
|
|
13140
13187
|
"modified_order": None,
|
|
13141
13188
|
}
|
|
13142
|
-
|
|
13189
|
+
|
|
13143
13190
|
new_order_no = ""
|
|
13144
13191
|
if response.block2:
|
|
13145
13192
|
# 해외선물 정정 주문번호 필드: OvrsFutsOrdNo
|
|
13146
13193
|
new_order_no = str(response.block2.OvrsFutsOrdNo) if hasattr(response.block2, "OvrsFutsOrdNo") and response.block2.OvrsFutsOrdNo else ""
|
|
13147
13194
|
|
|
13195
|
+
# 정정 빈-주문번호 가드 (거래시간 외/정정 불가 상태 silent no-op 차단)
|
|
13196
|
+
if not new_order_no:
|
|
13197
|
+
msg = response.rsp_msg or "정정 미반영"
|
|
13198
|
+
context.log(
|
|
13199
|
+
"warning",
|
|
13200
|
+
f"Modify futures order returned no OrderNo: {symbol} - {msg}",
|
|
13201
|
+
node_id,
|
|
13202
|
+
)
|
|
13203
|
+
return {
|
|
13204
|
+
"modify_result": {
|
|
13205
|
+
"success": False,
|
|
13206
|
+
"error": f"Empty modify order number: {msg} (거래시간 외/정정 불가 상태 가능)",
|
|
13207
|
+
"original_order_id": original_order_id,
|
|
13208
|
+
"product": "overseas_futures",
|
|
13209
|
+
},
|
|
13210
|
+
"modified_order_id": "",
|
|
13211
|
+
"modified_order": None,
|
|
13212
|
+
}
|
|
13213
|
+
|
|
13148
13214
|
context.log(
|
|
13149
13215
|
"info",
|
|
13150
13216
|
f"Futures order modified: {symbol} original={original_order_id} → new={new_order_no}",
|
|
@@ -13158,6 +13224,7 @@ class ModifyOrderNodeExecutor(NodeExecutorBase):
|
|
|
13158
13224
|
"new_order_id": new_order_no,
|
|
13159
13225
|
"product": "overseas_futures",
|
|
13160
13226
|
},
|
|
13227
|
+
"modified_order_id": new_order_no,
|
|
13161
13228
|
"modified_order": {
|
|
13162
13229
|
"symbol": symbol,
|
|
13163
13230
|
"exchange": exchange_code,
|
|
@@ -13220,6 +13287,7 @@ class ModifyOrderNodeExecutor(NodeExecutorBase):
|
|
|
13220
13287
|
"original_order_id": original_order_id,
|
|
13221
13288
|
"product": "korea_stock",
|
|
13222
13289
|
},
|
|
13290
|
+
"modified_order_id": "",
|
|
13223
13291
|
"modified_order": None,
|
|
13224
13292
|
}
|
|
13225
13293
|
|
|
@@ -13227,6 +13295,25 @@ class ModifyOrderNodeExecutor(NodeExecutorBase):
|
|
|
13227
13295
|
if response.block2:
|
|
13228
13296
|
new_order_no = str(response.block2.OrdNo) if response.block2.OrdNo else ""
|
|
13229
13297
|
|
|
13298
|
+
# 정정 빈-주문번호 가드 (거래시간 외/정정 불가 상태 silent no-op 차단)
|
|
13299
|
+
if not new_order_no:
|
|
13300
|
+
msg = response.rsp_msg or "정정 미반영"
|
|
13301
|
+
context.log(
|
|
13302
|
+
"warning",
|
|
13303
|
+
f"Korea stock modify order returned no OrderNo: {symbol} - {msg}",
|
|
13304
|
+
node_id,
|
|
13305
|
+
)
|
|
13306
|
+
return {
|
|
13307
|
+
"modify_result": {
|
|
13308
|
+
"success": False,
|
|
13309
|
+
"error": f"Empty modify order number: {msg} (거래시간 외/정정 불가 상태 가능)",
|
|
13310
|
+
"original_order_id": original_order_id,
|
|
13311
|
+
"product": "korea_stock",
|
|
13312
|
+
},
|
|
13313
|
+
"modified_order_id": "",
|
|
13314
|
+
"modified_order": None,
|
|
13315
|
+
}
|
|
13316
|
+
|
|
13230
13317
|
context.log(
|
|
13231
13318
|
"info",
|
|
13232
13319
|
f"Korea stock order modified: {symbol} original={original_order_id} → new={new_order_no}",
|
|
@@ -13240,6 +13327,7 @@ class ModifyOrderNodeExecutor(NodeExecutorBase):
|
|
|
13240
13327
|
"new_order_id": new_order_no,
|
|
13241
13328
|
"product": "korea_stock",
|
|
13242
13329
|
},
|
|
13330
|
+
"modified_order_id": new_order_no,
|
|
13243
13331
|
"modified_order": {
|
|
13244
13332
|
"symbol": symbol,
|
|
13245
13333
|
"exchange": "KRX",
|
|
@@ -13270,6 +13358,7 @@ class ModifyOrderNodeExecutor(NodeExecutorBase):
|
|
|
13270
13358
|
"success": False,
|
|
13271
13359
|
"error": error_msg,
|
|
13272
13360
|
},
|
|
13361
|
+
"modified_order_id": "",
|
|
13273
13362
|
"modified_order": None,
|
|
13274
13363
|
}
|
|
13275
13364
|
|
|
@@ -13286,6 +13375,8 @@ class CancelOrderNodeExecutor(NodeExecutorBase):
|
|
|
13286
13375
|
- original_order_id: 취소할 주문번호 (필수)
|
|
13287
13376
|
- symbol: 종목코드
|
|
13288
13377
|
- exchange: 거래소
|
|
13378
|
+
|
|
13379
|
+
주의: LS 가 error_msg 없이 취소 미반영(거래시간 외 등)을 반환할 수 있다. 취소 성공은 사후 OpenOrders 재조회로 확인 권장.
|
|
13289
13380
|
"""
|
|
13290
13381
|
|
|
13291
13382
|
# 해외주식 시장 코드 매핑
|
|
@@ -13316,6 +13407,7 @@ class CancelOrderNodeExecutor(NodeExecutorBase):
|
|
|
13316
13407
|
)
|
|
13317
13408
|
return {
|
|
13318
13409
|
"order_id": order_id,
|
|
13410
|
+
"cancelled_order_id": order_id,
|
|
13319
13411
|
"status": "simulated",
|
|
13320
13412
|
"dry_run": True,
|
|
13321
13413
|
"requested": config,
|
|
@@ -13449,9 +13541,10 @@ class CancelOrderNodeExecutor(NodeExecutorBase):
|
|
|
13449
13541
|
"order_id": order_id,
|
|
13450
13542
|
"product": "overseas_stock",
|
|
13451
13543
|
},
|
|
13544
|
+
"cancelled_order_id": "",
|
|
13452
13545
|
"cancelled_order": None,
|
|
13453
13546
|
}
|
|
13454
|
-
|
|
13547
|
+
|
|
13455
13548
|
context.log(
|
|
13456
13549
|
"info",
|
|
13457
13550
|
f"Order cancelled: {symbol} order_id={order_id}",
|
|
@@ -13464,6 +13557,7 @@ class CancelOrderNodeExecutor(NodeExecutorBase):
|
|
|
13464
13557
|
"order_id": order_id,
|
|
13465
13558
|
"product": "overseas_stock",
|
|
13466
13559
|
},
|
|
13560
|
+
"cancelled_order_id": order_id,
|
|
13467
13561
|
"cancelled_order": {
|
|
13468
13562
|
"symbol": symbol,
|
|
13469
13563
|
"exchange": exchange,
|
|
@@ -13481,6 +13575,7 @@ class CancelOrderNodeExecutor(NodeExecutorBase):
|
|
|
13481
13575
|
"order_id": order_id,
|
|
13482
13576
|
"product": "overseas_stock",
|
|
13483
13577
|
},
|
|
13578
|
+
"cancelled_order_id": "",
|
|
13484
13579
|
"cancelled_order": None,
|
|
13485
13580
|
}
|
|
13486
13581
|
|
|
@@ -13527,6 +13622,7 @@ class CancelOrderNodeExecutor(NodeExecutorBase):
|
|
|
13527
13622
|
"order_id": order_id,
|
|
13528
13623
|
"product": "overseas_futures",
|
|
13529
13624
|
},
|
|
13625
|
+
"cancelled_order_id": "",
|
|
13530
13626
|
"cancelled_order": None,
|
|
13531
13627
|
}
|
|
13532
13628
|
|
|
@@ -13542,6 +13638,7 @@ class CancelOrderNodeExecutor(NodeExecutorBase):
|
|
|
13542
13638
|
"order_id": order_id,
|
|
13543
13639
|
"product": "overseas_futures",
|
|
13544
13640
|
},
|
|
13641
|
+
"cancelled_order_id": order_id,
|
|
13545
13642
|
"cancelled_order": {
|
|
13546
13643
|
"symbol": symbol,
|
|
13547
13644
|
"exchange": exchange_code,
|
|
@@ -13559,6 +13656,7 @@ class CancelOrderNodeExecutor(NodeExecutorBase):
|
|
|
13559
13656
|
"order_id": order_id,
|
|
13560
13657
|
"product": "overseas_futures",
|
|
13561
13658
|
},
|
|
13659
|
+
"cancelled_order_id": "",
|
|
13562
13660
|
"cancelled_order": None,
|
|
13563
13661
|
}
|
|
13564
13662
|
|
|
@@ -13600,6 +13698,7 @@ class CancelOrderNodeExecutor(NodeExecutorBase):
|
|
|
13600
13698
|
"order_id": order_id,
|
|
13601
13699
|
"product": "korea_stock",
|
|
13602
13700
|
},
|
|
13701
|
+
"cancelled_order_id": "",
|
|
13603
13702
|
"cancelled_order": None,
|
|
13604
13703
|
}
|
|
13605
13704
|
|
|
@@ -13615,6 +13714,7 @@ class CancelOrderNodeExecutor(NodeExecutorBase):
|
|
|
13615
13714
|
"order_id": order_id,
|
|
13616
13715
|
"product": "korea_stock",
|
|
13617
13716
|
},
|
|
13717
|
+
"cancelled_order_id": order_id,
|
|
13618
13718
|
"cancelled_order": {
|
|
13619
13719
|
"symbol": symbol,
|
|
13620
13720
|
"exchange": "KRX",
|
|
@@ -13632,6 +13732,7 @@ class CancelOrderNodeExecutor(NodeExecutorBase):
|
|
|
13632
13732
|
"order_id": order_id,
|
|
13633
13733
|
"product": "korea_stock",
|
|
13634
13734
|
},
|
|
13735
|
+
"cancelled_order_id": "",
|
|
13635
13736
|
"cancelled_order": None,
|
|
13636
13737
|
}
|
|
13637
13738
|
|
|
@@ -13642,6 +13743,7 @@ class CancelOrderNodeExecutor(NodeExecutorBase):
|
|
|
13642
13743
|
"success": False,
|
|
13643
13744
|
"error": error_msg,
|
|
13644
13745
|
},
|
|
13746
|
+
"cancelled_order_id": "",
|
|
13645
13747
|
"cancelled_order": None,
|
|
13646
13748
|
}
|
|
13647
13749
|
|
|
@@ -16264,6 +16366,23 @@ class WorkflowJob:
|
|
|
16264
16366
|
input_data = None
|
|
16265
16367
|
for edge in self.workflow.edges:
|
|
16266
16368
|
if edge.to_node_id == node_id:
|
|
16369
|
+
# 명시적 from_port 우선 (예: ExclusionListNode.filtered).
|
|
16370
|
+
# 이를 지정하지 않으면 auto-iterate 는 소스 노드의 첫 출력
|
|
16371
|
+
# 포트를 집어버린다 — ExclusionListNode 는 첫 포트가
|
|
16372
|
+
# `excluded`(블랙리스트)라서, from_port 없이는 제외 종목을
|
|
16373
|
+
# 순회하게 되는 silent 버그가 생긴다. IfNode 분기 포트
|
|
16374
|
+
# (true/false/result)는 별도 라우팅 의미라 여기서 제외.
|
|
16375
|
+
explicit_port = getattr(edge, "from_port", None)
|
|
16376
|
+
if explicit_port and explicit_port not in (
|
|
16377
|
+
"output", "true", "false", "result",
|
|
16378
|
+
):
|
|
16379
|
+
port_data = self.context.get_output(
|
|
16380
|
+
edge.from_node_id, explicit_port
|
|
16381
|
+
)
|
|
16382
|
+
if port_data is not None:
|
|
16383
|
+
input_data = port_data
|
|
16384
|
+
break
|
|
16385
|
+
|
|
16267
16386
|
# symbols 포트 우선 확인 (WatchlistNode 출력)
|
|
16268
16387
|
input_data = self.context.get_output(edge.from_node_id, "symbols")
|
|
16269
16388
|
|
|
@@ -5,7 +5,7 @@ authors = [
|
|
|
5
5
|
homepage = "https://programgarden.com"
|
|
6
6
|
requires-python = ">=3.12"
|
|
7
7
|
name = "programgarden"
|
|
8
|
-
version = "1.22.
|
|
8
|
+
version = "1.22.3"
|
|
9
9
|
description = "ProgramGarden - 노드 기반 자동매매 DSL 실행 엔진"
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
|
|
@@ -28,14 +28,9 @@ lxml = "^6.0.2"
|
|
|
28
28
|
pytickersymbols = {version = ">=1.17.5", python = ">=3.12,<4.0"}
|
|
29
29
|
aiosqlite = "^0.20.0"
|
|
30
30
|
litellm = ">=1.40.0"
|
|
31
|
-
programgarden-core =
|
|
32
|
-
programgarden-finance =
|
|
33
|
-
programgarden-community =
|
|
34
|
-
|
|
35
|
-
[[tool.poetry.source]]
|
|
36
|
-
name = "testpypi"
|
|
37
|
-
url = "https://test.pypi.org/simple/"
|
|
38
|
-
priority = "supplemental"
|
|
31
|
+
programgarden-core = "^1.14.3"
|
|
32
|
+
programgarden-finance = "^1.6.9"
|
|
33
|
+
programgarden-community = "^1.13.8"
|
|
39
34
|
|
|
40
35
|
[tool.poetry.group.dev.dependencies]
|
|
41
36
|
pytest = "^8.0.0"
|
|
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-1.22.2 → programgarden-1.22.3}/programgarden/database/workflow_position_tracker.py
RENAMED
|
File without changes
|
{programgarden-1.22.2 → programgarden-1.22.3}/programgarden/database/workflow_risk_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
|