programgarden 2.7.0__tar.gz → 2.7.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.7.0 → programgarden-2.7.2}/PKG-INFO +1 -1
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/database/broker_snapshot.py +6 -3
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/paper_futures_business_date.py +66 -11
- {programgarden-2.7.0 → programgarden-2.7.2}/pyproject.toml +1 -1
- {programgarden-2.7.0 → programgarden-2.7.2}/README.md +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/__init__.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/binding_provenance.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/binding_validator.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/client.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/code_worker.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/context.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/database/__init__.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/database/adjustment_delivery.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/database/broker_evidence.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/database/broker_order_totals.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/database/checkpoint_manager.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/database/db_naming.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/database/execution_storage.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/database/fill_reconciler.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/database/order_cancellation.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/database/order_recovery.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/database/owned_order_cancellation.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/database/position_reconciliation.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/database/query_builder.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/database/sqlite_scope.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/database/workflow_position_tracker.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/database/workflow_risk_tracker.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/deep_fixtures.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/executor.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/futures_contract_info.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/futures_exchange.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/futures_history.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/futures_orderable.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/futures_pnl.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/futures_read_evidence.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/incremental_build.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/managed_order_control.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/node_runner.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/order_lifecycle.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/plugin/__init__.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/plugin/sandbox.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/providers/__init__.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/providers/llm_errors.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/providers/llm_provider.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/ranking_nodes.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/reconnect_handler.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/replay_contracts.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/replay_events.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/replay_external.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/replay_order_adapter.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/replay_orders.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/replay_responders.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/replay_scenarios.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/replay_semantics.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/replay_sources.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/replay_sqlite.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/replay_tape.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/replay_triggers.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/resolver.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/resource/__init__.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/resource/context.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/resource/limiter.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/resource/monitor.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/resource/throttle.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/semantic_rules.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/tools/__init__.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/tools/credential_tools.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/tools/definition_tools.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/tools/event_tools.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/tools/job_tools.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/tools/registry_tools.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/tools/sqlite_tools.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/validation_recommender.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/validation_replay.py +0 -0
- {programgarden-2.7.0 → programgarden-2.7.2}/programgarden/valuation.py +0 -0
|
@@ -61,19 +61,22 @@ async def read_broker_snapshot(ls, *, execution_key, product, provider, trading_
|
|
|
61
61
|
from programgarden_finance.ls.korea_stock.extension.valuation import collect_domestic_trading_positions
|
|
62
62
|
from programgarden_finance.ls.korea_stock.extension.account_queries import collect_domestic_open_orders
|
|
63
63
|
|
|
64
|
+
# The collector returns t0424 rows (trade-basis balance, chegb=2), not
|
|
65
|
+
# CSPAQ12300 rows: the symbol is expcode and the held quantity janqty,
|
|
66
|
+
# the same fields the server startup gate reads.
|
|
64
67
|
_, rows = await collect_domestic_trading_positions(ls.korea_stock().accno())
|
|
65
68
|
for row in rows:
|
|
66
|
-
symbol = normalized_symbol(str(required(row, "
|
|
69
|
+
symbol = normalized_symbol(str(required(row, "expcode")), product)
|
|
67
70
|
if symbol in positions:
|
|
68
71
|
raise ReconciliationUnavailable("ambiguous_position_rows")
|
|
69
|
-
positions[symbol] = quantity(required(row, "
|
|
72
|
+
positions[symbol] = quantity(required(row, "janqty"))
|
|
70
73
|
await asyncio.sleep(2)
|
|
71
74
|
pending = await collect_domestic_open_orders(ls.korea_stock().accno())
|
|
72
75
|
# t0425 is the documented current-day inquiry. Retain that observation
|
|
73
76
|
# date for exact ledger matching; never widen it to yesterday's order.
|
|
74
77
|
for row in pending:
|
|
75
78
|
row["order_date"] = day
|
|
76
|
-
source = "
|
|
79
|
+
source = "t0424+t0425"
|
|
77
80
|
elif product == "overseas_stock":
|
|
78
81
|
from programgarden_finance import COSOQ00201, COSAQ00102
|
|
79
82
|
|
|
@@ -3,14 +3,46 @@
|
|
|
3
3
|
The published same-day blank-date contract conflicts with an observed paper
|
|
4
4
|
response. This adapter uses CIDBQ01500's explicit current-date echo, never a
|
|
5
5
|
machine date, and does not cache it across calls or relax unavailable evidence.
|
|
6
|
+
|
|
7
|
+
When the paper server returns no business date at all (observed on weekends,
|
|
8
|
+
when its overseas futures day is closed), the order inquiry uses the published
|
|
9
|
+
blank-date same-day form instead, with the same response checks. This is the
|
|
10
|
+
form the server startup gate already uses for that answer, so a start the
|
|
11
|
+
server admits is not refused here.
|
|
6
12
|
"""
|
|
7
13
|
from dataclasses import dataclass, field
|
|
8
14
|
from datetime import datetime
|
|
9
15
|
import hashlib
|
|
16
|
+
import logging
|
|
10
17
|
import re
|
|
11
18
|
import time
|
|
12
19
|
|
|
13
|
-
from programgarden.database.broker_evidence import StartupEvidenceUnavailable, checked_body
|
|
20
|
+
from programgarden.database.broker_evidence import StartupEvidenceUnavailable, broker_message, checked_body
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
_RESPONSE_CODE_REASON = "CIDBQ02400: unverified_response_code="
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def broker_business_day(value):
|
|
28
|
+
"""The broker's explicit YYYYMMDD business date, or "" when none was given.
|
|
29
|
+
|
|
30
|
+
A blank, missing, padded or impossible value is "no date". It is never
|
|
31
|
+
replaced by a local, previous or cached date.
|
|
32
|
+
"""
|
|
33
|
+
if not isinstance(value, str) or not re.fullmatch(r"[0-9]{8}", value):
|
|
34
|
+
return ""
|
|
35
|
+
try:
|
|
36
|
+
datetime.strptime(value, "%Y%m%d")
|
|
37
|
+
except ValueError:
|
|
38
|
+
return ""
|
|
39
|
+
return value
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def blank_date_unverified(response):
|
|
43
|
+
"""No business date, and the blank-date answer's code was not accepted."""
|
|
44
|
+
return (f"CIDBQ02400: blank_date_unverified rsp_cd={getattr(response, 'rsp_cd', None)} "
|
|
45
|
+
f"rsp_msg={broker_message(getattr(response, 'rsp_msg', None))}")
|
|
14
46
|
|
|
15
47
|
|
|
16
48
|
def is_paper_client(ls):
|
|
@@ -42,19 +74,41 @@ class PaperFuturesScope:
|
|
|
42
74
|
|
|
43
75
|
@dataclass(frozen=True)
|
|
44
76
|
class PaperFuturesBusinessDate:
|
|
77
|
+
"""One fresh CIDBQ01500 observation for the following CIDBQ02400 read.
|
|
78
|
+
|
|
79
|
+
``day`` is the broker's explicit business date, or "" when the paper server
|
|
80
|
+
returned none; "" selects the published blank-date same-day query (both
|
|
81
|
+
dates blank). ``account`` is the positions echo; it is always present with
|
|
82
|
+
a date and may be "" only on a blank-date answer.
|
|
83
|
+
"""
|
|
45
84
|
day: str
|
|
46
85
|
account: str = field(repr=False)
|
|
47
86
|
scope: PaperFuturesScope = field(repr=False)
|
|
48
87
|
|
|
88
|
+
@property
|
|
89
|
+
def blank_date(self):
|
|
90
|
+
return not self.day
|
|
91
|
+
|
|
49
92
|
async def check_orders(self, ls, response, query):
|
|
50
93
|
self.scope.check(ls)
|
|
51
|
-
|
|
94
|
+
try:
|
|
95
|
+
body = await checked_body(response, "CIDBQ02400", query, paper_trading=True)
|
|
96
|
+
except StartupEvidenceUnavailable as exc:
|
|
97
|
+
# Without a business date the published blank-date form is the only
|
|
98
|
+
# query left; name that case, with the broker's answer, when its
|
|
99
|
+
# response code is not accepted. Other failures keep their reasons.
|
|
100
|
+
if self.blank_date and str(exc).startswith(_RESPONSE_CODE_REASON):
|
|
101
|
+
raise StartupEvidenceUnavailable(blank_date_unverified(response)) from None
|
|
102
|
+
raise
|
|
52
103
|
echo = body["CIDBQ02400OutBlock1"]
|
|
53
104
|
request = next(iter(query.request_data.body.values()))
|
|
54
105
|
expected = request.model_dump(exclude={"RecCnt"})
|
|
106
|
+
# A dated observation always carries its account; a blank-date one binds
|
|
107
|
+
# the account only when LS echoed it (the credential scope check below
|
|
108
|
+
# still proves both reads used the same token, key and paper mode).
|
|
55
109
|
if (request.ThdayTpCode != "1" or request.QrySrtDt != self.day
|
|
56
110
|
or request.QryEndDt != self.day or not isinstance(echo, dict)
|
|
57
|
-
or echo.get("AcntNo") != self.account
|
|
111
|
+
or (self.account and echo.get("AcntNo") != self.account)
|
|
58
112
|
or any(name not in echo or echo[name] != value for name, value in expected.items())):
|
|
59
113
|
raise StartupEvidenceUnavailable("CIDBQ02400: business_date_scope_mismatch")
|
|
60
114
|
self.scope.check(ls)
|
|
@@ -72,15 +126,16 @@ async def from_current_positions(ls, response, query, scope):
|
|
|
72
126
|
raise StartupEvidenceUnavailable("CIDBQ01500: current_date_query_required")
|
|
73
127
|
if not isinstance(echo, dict) or any(echo.get(name) != value for name, value in expected.items() if name != "QryDt"):
|
|
74
128
|
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
|
|
129
|
+
day, account = broker_business_day(echo.get("QryDt")), echo.get("AcntNo")
|
|
82
130
|
if not isinstance(account, str) or not account.strip():
|
|
83
|
-
|
|
131
|
+
if day:
|
|
132
|
+
raise StartupEvidenceUnavailable("CIDBQ01500: unavailable_account_echo")
|
|
133
|
+
account = ""
|
|
134
|
+
if not day:
|
|
135
|
+
# Weekend paper answers carry no business date. The server startup gate
|
|
136
|
+
# uses the blank-date order form for this answer; so does the engine.
|
|
137
|
+
logger.info("CIDBQ01500: paper server returned no business date; "
|
|
138
|
+
"using the published blank-date CIDBQ02400 same-day form")
|
|
84
139
|
scope.check(ls)
|
|
85
140
|
return PaperFuturesBusinessDate(day, account, scope)
|
|
86
141
|
|
|
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
|
{programgarden-2.7.0 → programgarden-2.7.2}/programgarden/database/owned_order_cancellation.py
RENAMED
|
File without changes
|
{programgarden-2.7.0 → programgarden-2.7.2}/programgarden/database/position_reconciliation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{programgarden-2.7.0 → programgarden-2.7.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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|