programgarden 2.6.2__tar.gz → 2.6.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.
Files changed (70) hide show
  1. {programgarden-2.6.2 → programgarden-2.6.3}/PKG-INFO +6 -2
  2. {programgarden-2.6.2 → programgarden-2.6.3}/README.md +4 -0
  3. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/database/broker_evidence.py +5 -1
  4. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/database/broker_snapshot.py +15 -3
  5. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/executor.py +34 -14
  6. programgarden-2.6.3/programgarden/paper_futures_business_date.py +96 -0
  7. {programgarden-2.6.2 → programgarden-2.6.3}/pyproject.toml +2 -2
  8. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/__init__.py +0 -0
  9. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/binding_validator.py +0 -0
  10. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/client.py +0 -0
  11. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/code_worker.py +0 -0
  12. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/context.py +0 -0
  13. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/database/__init__.py +0 -0
  14. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/database/adjustment_delivery.py +0 -0
  15. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/database/broker_order_totals.py +0 -0
  16. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/database/checkpoint_manager.py +0 -0
  17. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/database/db_naming.py +0 -0
  18. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/database/execution_storage.py +0 -0
  19. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/database/fill_reconciler.py +0 -0
  20. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/database/order_cancellation.py +0 -0
  21. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/database/order_recovery.py +0 -0
  22. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/database/owned_order_cancellation.py +0 -0
  23. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/database/position_reconciliation.py +0 -0
  24. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/database/query_builder.py +0 -0
  25. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/database/sqlite_scope.py +0 -0
  26. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/database/workflow_position_tracker.py +0 -0
  27. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/database/workflow_risk_tracker.py +0 -0
  28. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/deep_fixtures.py +0 -0
  29. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/futures_contract_info.py +0 -0
  30. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/futures_history.py +0 -0
  31. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/futures_orderable.py +0 -0
  32. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/futures_pnl.py +0 -0
  33. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/futures_read_evidence.py +0 -0
  34. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/incremental_build.py +0 -0
  35. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/managed_order_control.py +0 -0
  36. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/node_runner.py +0 -0
  37. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/order_lifecycle.py +0 -0
  38. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/plugin/__init__.py +0 -0
  39. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/plugin/sandbox.py +0 -0
  40. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/providers/__init__.py +0 -0
  41. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/providers/llm_errors.py +0 -0
  42. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/providers/llm_provider.py +0 -0
  43. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/reconnect_handler.py +0 -0
  44. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/replay_contracts.py +0 -0
  45. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/replay_events.py +0 -0
  46. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/replay_external.py +0 -0
  47. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/replay_order_adapter.py +0 -0
  48. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/replay_orders.py +0 -0
  49. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/replay_scenarios.py +0 -0
  50. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/replay_semantics.py +0 -0
  51. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/replay_sources.py +0 -0
  52. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/replay_sqlite.py +0 -0
  53. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/replay_triggers.py +0 -0
  54. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/resolver.py +0 -0
  55. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/resource/__init__.py +0 -0
  56. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/resource/context.py +0 -0
  57. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/resource/limiter.py +0 -0
  58. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/resource/monitor.py +0 -0
  59. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/resource/throttle.py +0 -0
  60. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/semantic_rules.py +0 -0
  61. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/tools/__init__.py +0 -0
  62. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/tools/credential_tools.py +0 -0
  63. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/tools/definition_tools.py +0 -0
  64. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/tools/event_tools.py +0 -0
  65. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/tools/job_tools.py +0 -0
  66. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/tools/registry_tools.py +0 -0
  67. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/tools/sqlite_tools.py +0 -0
  68. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/validation_recommender.py +0 -0
  69. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/validation_replay.py +0 -0
  70. {programgarden-2.6.2 → programgarden-2.6.3}/programgarden/valuation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: programgarden
3
- Version: 2.6.2
3
+ Version: 2.6.3
4
4
  Summary: ProgramGarden - 노드 기반 자동매매 DSL 실행 엔진
5
5
  License-Expression: AGPL-3.0-or-later
6
6
  Author: 프로그램동산
@@ -17,7 +17,7 @@ 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
19
  Requires-Dist: programgarden-core (>=2.6.2,<3.0.0)
20
- Requires-Dist: programgarden-finance (>=2.0.1,<3.0.0)
20
+ Requires-Dist: programgarden-finance (>=2.0.2,<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)
23
23
  Requires-Dist: pydantic (>=2.0.1,<3.0.0)
@@ -31,6 +31,10 @@ The 2.6.2 release candidate [futures minute alias correction](../../docs/futures
31
31
 
32
32
  # ProgramGarden
33
33
 
34
+ The 2.6.3 release candidate [paper futures business-date correction](../../docs/paper-futures-business-date.md)
35
+ uses a fresh account-scoped broker date for startup pending orders, OpenOrdersNode
36
+ and legacy fill observation. Missing or mismatched evidence remains unavailable.
37
+
34
38
  An unreleased [stock history interval correction](../../docs/stock-history-interval-contract.md)
35
39
  rejects unsupported intraday periods instead of silently querying daily bars.
36
40
  Supported g3204 periods retain their existing requests and output contract.
@@ -2,6 +2,10 @@ The 2.6.2 release candidate [futures minute alias correction](../../docs/futures
2
2
 
3
3
  # ProgramGarden
4
4
 
5
+ The 2.6.3 release candidate [paper futures business-date correction](../../docs/paper-futures-business-date.md)
6
+ uses a fresh account-scoped broker date for startup pending orders, OpenOrdersNode
7
+ and legacy fill observation. Missing or mismatched evidence remains unavailable.
8
+
5
9
  An unreleased [stock history interval correction](../../docs/stock-history-interval-contract.md)
6
10
  rejects unsupported intraday periods instead of silently querying daily bars.
7
11
  Supported g3204 periods retain their existing requests and output contract.
@@ -40,7 +40,7 @@ class StartupQueryDateMismatch(StartupEvidenceUnavailable):
40
40
  self.business_day = business_day
41
41
 
42
42
 
43
- async def checked_body(response, tr, request=None):
43
+ async def checked_body(response, tr, request=None, *, paper_trading=False):
44
44
  """Read original broker blocks so SDK defaults cannot manufacture success."""
45
45
  if response.status_code != 200 or response.error_msg:
46
46
  raise StartupEvidenceUnavailable(f"{tr}: transport_failure")
@@ -105,6 +105,10 @@ async def checked_body(response, tr, request=None):
105
105
  and echo.get("BnsTpCode") == "0" and echo.get("CrcyCode") == "000")
106
106
  elif tr in {"CIDBQ01500", "CIDBQ02400"} and code == "00707":
107
107
  accepted = (not rows and response.rsp_msg == "모의투자 조회할 내역(자료)이 없습니다.")
108
+ elif paper_trading is True and tr in {"CIDBQ01500", "CIDBQ02400"} and code == "02679":
109
+ # Observed paper response with complete raw blocks, explicit empty rows
110
+ # and terminal continuation. The caller still binds date/account scope.
111
+ accepted = not rows and response.rsp_msg == "조회내역이 없습니다."
108
112
  if not accepted:
109
113
  raise StartupEvidenceUnavailable(f"{tr}: unverified_response_code={code}")
110
114
  if tr in {"t0424", "t0425"}:
@@ -110,12 +110,18 @@ async def read_broker_snapshot(ls, *, execution_key, product, provider, trading_
110
110
  source = "COSOQ00201+COSAQ00102"
111
111
  elif product == "overseas_futures":
112
112
  from programgarden_finance import CIDBQ01500, CIDBQ02400
113
+ from programgarden.paper_futures_business_date import PaperFuturesScope, from_current_positions
113
114
 
114
115
  accno = ls.overseas_futureoption().accno()
116
+ paper_scope = PaperFuturesScope.capture(ls) if trading_mode == "paper" else None
115
117
  query = accno.CIDBQ01500(body=CIDBQ01500.CIDBQ01500InBlock1(
116
118
  RecCnt=1, AcntTpCode="1", FcmAcntNo="", QryDt="", BalTpCode="2"))
117
119
  response = await query.req_async()
118
- await checked_body(response, "CIDBQ01500", query)
120
+ paper_day = None
121
+ if paper_scope is not None:
122
+ paper_day = await from_current_positions(ls, response, query, paper_scope)
123
+ else:
124
+ await checked_body(response, "CIDBQ01500", query)
119
125
  for row in response.block2:
120
126
  symbol = normalized_symbol(str(required(row, "IsuCodeVal")), product)
121
127
  side = str(required(row, "BnsTpCode"))
@@ -127,11 +133,17 @@ async def read_broker_snapshot(ls, *, execution_key, product, provider, trading_
127
133
  if side == "2":
128
134
  positions[symbol] = positions.get(symbol, Decimal(0)) + amount
129
135
  await asyncio.sleep(2)
136
+ if paper_day is not None:
137
+ paper_day.scope.check(ls)
138
+ query_day = paper_day.day if paper_day is not None else ""
130
139
  query = accno.CIDBQ02400(body=CIDBQ02400.CIDBQ02400InBlock1(
131
- RecCnt=1, IsuCodeVal="", QrySrtDt="", QryEndDt="", ThdayTpCode="1",
140
+ RecCnt=1, IsuCodeVal="", QrySrtDt=query_day, QryEndDt=query_day, ThdayTpCode="1",
132
141
  OrdStatCode="2", BnsTpCode="0", QryTpCode="2", OrdPtnCode="00", OvrsDrvtFnoTpCode="A"))
133
142
  response = await query.req_async()
134
- await checked_body(response, "CIDBQ02400", query)
143
+ if paper_day is not None:
144
+ await paper_day.check_orders(ls, response, query)
145
+ else:
146
+ await checked_body(response, "CIDBQ02400", query)
135
147
  pending = [value for row in response.block2 if (value := pending_row(row, product)) is not None]
136
148
  source = "CIDBQ01500+CIDBQ02400"
137
149
  else:
@@ -6659,21 +6659,33 @@ class OpenOrdersNodeExecutor(NodeExecutorBase):
6659
6659
  from datetime import datetime
6660
6660
  from zoneinfo import ZoneInfo
6661
6661
  from programgarden.futures_read_evidence import require_complete_rows, require_pending_identity
6662
+ from programgarden.paper_futures_business_date import is_paper_client, read_current_business_date
6663
+ from programgarden.database.broker_evidence import StartupEvidenceUnavailable
6662
6664
  from programgarden_finance.ls.overseas_futureoption.accno.CIDBQ02400.blocks import CIDBQ02400InBlock1
6663
6665
 
6664
6666
 
6665
- today = datetime.now(ZoneInfo("Asia/Seoul")).strftime("%Y%m%d")
6667
+ paper_day = None
6668
+ try:
6669
+ if is_paper_client(ls):
6670
+ paper_day = await read_current_business_date(ls)
6671
+ except StartupEvidenceUnavailable as exc:
6672
+ return self._empty_result(str(exc))
6673
+ today = paper_day.day if paper_day else datetime.now(ZoneInfo("Asia/Seoul")).strftime("%Y%m%d")
6666
6674
  request = CIDBQ02400InBlock1(
6667
6675
  RecCnt=1, IsuCodeVal="", QrySrtDt=today, QryEndDt=today,
6668
6676
  ThdayTpCode="1", OrdStatCode="2", BnsTpCode="0", QryTpCode="2",
6669
6677
  OrdPtnCode="00", OvrsDrvtFnoTpCode="A",
6670
6678
  )
6671
- response = await ls.overseas_futureoption().accno().CIDBQ02400(body=request).req_async()
6672
6679
  try:
6673
- rows = require_complete_rows(response, request, "CIDBQ02400")
6680
+ if paper_day:
6681
+ paper_day.scope.check(ls)
6682
+ query = ls.overseas_futureoption().accno().CIDBQ02400(body=request)
6683
+ response = await query.req_async()
6684
+ rows = (await paper_day.check_orders(ls, response, query) if paper_day else
6685
+ require_complete_rows(response, request, "CIDBQ02400"))
6674
6686
  for row in rows:
6675
6687
  require_pending_identity(row)
6676
- except ValueError as exc:
6688
+ except (ValueError, StartupEvidenceUnavailable) as exc:
6677
6689
  return self._empty_result(str(exc))
6678
6690
 
6679
6691
  open_orders = []
@@ -16849,17 +16861,21 @@ class NewOrderNodeExecutor(NodeExecutorBase):
16849
16861
  from programgarden_finance.ls.overseas_futureoption.accno.CIDBQ02400.blocks import (
16850
16862
  CIDBQ02400InBlock1,
16851
16863
  )
16852
- # 당일조회(ThdayTpCode="1")에는 날짜를 싣지 않는다 — 브로커의 "당일"은
16853
- # 그쪽 영업일이고, 실행 머신의 달력 날짜와 다를 수 있다. 야간 세션에서
16854
- # 로컬이 자정을 넘기면 브로커는 아직 전 영업일이라(2026-09-10 00:39 KST
16855
- # 접수 주문이 OrdDt/ExecDt=20260909 로 조회됨) 로컬 날짜를 창으로 주면
16856
- # 방금 낸 주문의 체결을 스스로 잘라낸다. 아래 매칭은 주문번호로만 하므로
16857
- # 날짜 창은 필요 없다.
16858
- response = await ls.overseas_futureoption().accno().CIDBQ02400(
16864
+ from programgarden.paper_futures_business_date import is_paper_client, read_current_business_date
16865
+
16866
+ # A paper server can reject the documented blank same-day dates.
16867
+ # Use its fresh account-scoped date, including overnight sessions;
16868
+ # never replace missing evidence with the machine's calendar date.
16869
+ paper_day = await read_current_business_date(ls) if is_paper_client(ls) else None
16870
+ query_day = paper_day.day if paper_day else ""
16871
+ if paper_day:
16872
+ paper_day.scope.check(ls)
16873
+ query = ls.overseas_futureoption().accno().CIDBQ02400(
16859
16874
  body=CIDBQ02400InBlock1(
16875
+ RecCnt=1,
16860
16876
  IsuCodeVal="", # 전체 종목
16861
- QrySrtDt="",
16862
- QryEndDt="",
16877
+ QrySrtDt=query_day,
16878
+ QryEndDt=query_day,
16863
16879
  ThdayTpCode="1", # 1: 당일조회
16864
16880
  OrdStatCode="0", # 0: 전체(체결+미체결) — 상태 판정에 필요
16865
16881
  BnsTpCode="0", # 0: 전체
@@ -16867,7 +16883,11 @@ class NewOrderNodeExecutor(NodeExecutorBase):
16867
16883
  OrdPtnCode="00", # 00: 전체
16868
16884
  OvrsDrvtFnoTpCode="A", # A: 전체(선물+옵션)
16869
16885
  )
16870
- ).req_async()
16886
+ )
16887
+ response = await query.req_async()
16888
+
16889
+ if paper_day:
16890
+ await paper_day.check_orders(ls, response, query)
16871
16891
 
16872
16892
  if getattr(response, "error_msg", None):
16873
16893
  context.log("debug", f"CIDBQ02400 fill query error: {response.error_msg}", node_id)
@@ -0,0 +1,96 @@
1
+ """Use fresh account-scoped broker dates for paper futures order inquiries.
2
+
3
+ The published same-day blank-date contract conflicts with an observed paper
4
+ response. This adapter uses CIDBQ01500's explicit current-date echo, never a
5
+ machine date, and does not cache it across calls or relax unavailable evidence.
6
+ """
7
+ from dataclasses import dataclass, field
8
+ from datetime import datetime
9
+ import hashlib
10
+ import re
11
+ import time
12
+
13
+ from programgarden.database.broker_evidence import StartupEvidenceUnavailable, checked_body
14
+
15
+
16
+ def is_paper_client(ls):
17
+ return getattr(getattr(ls, "token_manager", None), "paper_trading", None) is True
18
+
19
+
20
+ @dataclass(frozen=True)
21
+ class PaperFuturesScope:
22
+ manager: object = field(repr=False)
23
+ key_digest: str = field(repr=False)
24
+ started: float
25
+
26
+ @classmethod
27
+ def capture(cls, ls):
28
+ manager = getattr(ls, "token_manager", None)
29
+ key = getattr(manager, "appkey", None)
30
+ if not is_paper_client(ls) or not isinstance(key, str) or not key:
31
+ raise StartupEvidenceUnavailable("paper_futures_business_date: unavailable_client_scope")
32
+ return cls(manager, hashlib.sha256(key.encode()).hexdigest(), time.monotonic())
33
+
34
+ def check(self, ls):
35
+ current = self.capture(ls)
36
+ if current.manager is not self.manager or current.key_digest != self.key_digest:
37
+ raise StartupEvidenceUnavailable("paper_futures_business_date: client_scope_changed")
38
+ age = time.monotonic() - self.started
39
+ if age < 0 or age > 60:
40
+ raise StartupEvidenceUnavailable("paper_futures_business_date: expired_observation")
41
+
42
+
43
+ @dataclass(frozen=True)
44
+ class PaperFuturesBusinessDate:
45
+ day: str
46
+ account: str = field(repr=False)
47
+ scope: PaperFuturesScope = field(repr=False)
48
+
49
+ async def check_orders(self, ls, response, query):
50
+ self.scope.check(ls)
51
+ body = await checked_body(response, "CIDBQ02400", query, paper_trading=True)
52
+ echo = body["CIDBQ02400OutBlock1"]
53
+ request = next(iter(query.request_data.body.values()))
54
+ expected = request.model_dump(exclude={"RecCnt"})
55
+ if (request.ThdayTpCode != "1" or request.QrySrtDt != self.day
56
+ or request.QryEndDt != self.day or not isinstance(echo, dict)
57
+ or echo.get("AcntNo") != self.account
58
+ or any(name not in echo or echo[name] != value for name, value in expected.items())):
59
+ raise StartupEvidenceUnavailable("CIDBQ02400: business_date_scope_mismatch")
60
+ self.scope.check(ls)
61
+ return response.block2
62
+
63
+
64
+ async def from_current_positions(ls, response, query, scope):
65
+ """Validate a just-fetched positions response without making another call."""
66
+ scope.check(ls)
67
+ body = await checked_body(response, "CIDBQ01500", query, paper_trading=True)
68
+ echo = body["CIDBQ01500OutBlock1"]
69
+ request = next(iter(query.request_data.body.values()))
70
+ expected = {"AcntTpCode": "1", "QryDt": "", "BalTpCode": "2", "FcmAcntNo": ""}
71
+ if any(getattr(request, name) != value for name, value in expected.items()):
72
+ raise StartupEvidenceUnavailable("CIDBQ01500: current_date_query_required")
73
+ if not isinstance(echo, dict) or any(echo.get(name) != value for name, value in expected.items() if name != "QryDt"):
74
+ raise StartupEvidenceUnavailable("CIDBQ01500: business_date_scope_mismatch")
75
+ day, account = echo.get("QryDt"), echo.get("AcntNo")
76
+ if not isinstance(day, str) or not re.fullmatch(r"[0-9]{8}", day):
77
+ raise StartupEvidenceUnavailable("CIDBQ01500: unavailable_business_date")
78
+ try:
79
+ datetime.strptime(day, "%Y%m%d")
80
+ except ValueError:
81
+ raise StartupEvidenceUnavailable("CIDBQ01500: invalid_business_date") from None
82
+ if not isinstance(account, str) or not account.strip():
83
+ raise StartupEvidenceUnavailable("CIDBQ01500: unavailable_account_echo")
84
+ scope.check(ls)
85
+ return PaperFuturesBusinessDate(day, account, scope)
86
+
87
+
88
+ async def read_current_business_date(ls):
89
+ """One bounded read; the SDK retains its existing shared-key rate limiter."""
90
+ from programgarden_finance import CIDBQ01500
91
+
92
+ scope = PaperFuturesScope.capture(ls)
93
+ query = ls.overseas_futureoption().accno().CIDBQ01500(body=CIDBQ01500.CIDBQ01500InBlock1(
94
+ RecCnt=1, AcntTpCode="1", FcmAcntNo="", QryDt="", BalTpCode="2"))
95
+ response = await query.req_async()
96
+ return await from_current_positions(ls, response, query, scope)
@@ -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.2"
8
+ version = "2.6.3"
9
9
  license = "AGPL-3.0-or-later"
10
10
  description = "ProgramGarden - 노드 기반 자동매매 DSL 실행 엔진"
11
11
  readme = "README.md"
@@ -37,7 +37,7 @@ litellm = ">=1.40.0"
37
37
  # (1.26.0 이후로도 context.py 는 WorkflowPnLEvent(**event_data) 에 personal_metrics
38
38
  # 를 무조건 실으므로 그 하한 근거도 이 상한에 포함된다.)
39
39
  programgarden-core = "^2.6.2"
40
- programgarden-finance = "^2.0.1"
40
+ programgarden-finance = "^2.0.2"
41
41
  programgarden-community = "^2.3.1"
42
42
 
43
43
  [tool.poetry.group.dev.dependencies]