feat: add AI descriptions and platform submission checks
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
"""WorldQuant adapter. Only authentication and explicit backtests allow upstream POST.
|
||||
"""WorldQuant adapter. Explicit descriptions allow PATCH; Alpha submission is not exposed.
|
||||
|
||||
No upstream response body or request headers are included in exceptions: they may
|
||||
contain credentials, cookies, or temporary authentication links.
|
||||
@@ -268,7 +268,7 @@ class WqClient:
|
||||
async def get(self, path: str, params=None, headers=None):
|
||||
return await self._read_json("GET", path, params=params, headers=headers)
|
||||
|
||||
async def _read_json(self, method: str, path: str, *, allow_list=False, poll_attempts=None, **kwargs):
|
||||
async def _read_json(self, method: str, path: str, *, allow_list=False, poll_attempts=None, wait_for_retry_header=False, **kwargs):
|
||||
"""Authenticated read with shared refresh/retry handling; callers use GET or OPTIONS."""
|
||||
if not self.credentials:
|
||||
raise WqError("请先连接 WorldQuant", "disconnected")
|
||||
@@ -292,7 +292,7 @@ class WqClient:
|
||||
# Recordsets may return 200/202 with Retry-After before results exist.
|
||||
if (
|
||||
response.headers.get("Retry-After")
|
||||
and self.retry_delay(response.headers["Retry-After"], 0) > 0
|
||||
and (wait_for_retry_header or self.retry_delay(response.headers["Retry-After"], 0) > 0)
|
||||
):
|
||||
if attempt + 1 == attempts:
|
||||
raise WqError("平台数据仍在准备,请稍后重试", "pending")
|
||||
@@ -390,6 +390,47 @@ class WqClient:
|
||||
async def alpha(self, alpha_id):
|
||||
return await self.get(f"/alphas/{alpha_id}")
|
||||
|
||||
async def patch_descriptions(self, alpha_id, descriptions):
|
||||
"""Write only reviewed descriptions; ambiguous writes require GET reconciliation.
|
||||
|
||||
No blind transport/5xx retry: the first PATCH may already have succeeded.
|
||||
A job retry reads the current Alpha before deciding whether PATCH is needed.
|
||||
"""
|
||||
if not re.fullmatch(r"[A-Za-z0-9_-]{1,100}", alpha_id) or not descriptions or not set(descriptions) <= {"regular", "selection", "combo"}:
|
||||
raise WqError("Description 写入参数无效", "invalid_description")
|
||||
if any(not isinstance(value, dict) or set(value) != {"description"} or not isinstance(value["description"], str) for value in descriptions.values()):
|
||||
raise WqError("仅允许写入 Description", "invalid_description")
|
||||
if not self.credentials:
|
||||
raise WqError("请先连接 WorldQuant", "disconnected")
|
||||
if not self.authenticated:
|
||||
await self.authenticate(*self.credentials)
|
||||
for attempt in range(2):
|
||||
generation = self.auth_generation
|
||||
try:
|
||||
response = await self.client.patch(f"/alphas/{alpha_id}", json=descriptions)
|
||||
except httpx.TransportError:
|
||||
raise WqError("Description 写回结果未知;重试任务将先核对平台内容", "description_unknown") from None
|
||||
if response.status_code == 401 and attempt == 0:
|
||||
await self.authenticate(*self.credentials, stale_generation=generation)
|
||||
continue
|
||||
if response.status_code in (200, 204):
|
||||
return
|
||||
raise WqError(f"Description 写回未确认(HTTP {response.status_code});可重试任务核对平台内容", "description_write_failed")
|
||||
|
||||
async def submission_check(self, alpha_id):
|
||||
"""cnhkmcp /check contract: wait on Retry-After and return only is.checks."""
|
||||
if not re.fullmatch(r"[A-Za-z0-9_-]{1,100}", alpha_id):
|
||||
raise WqError("Alpha ID 无效", "invalid_alpha")
|
||||
raw = await self._read_json(
|
||||
"GET", f"/alphas/{alpha_id}/check", poll_attempts=self.settings.pnl_poll_attempts,
|
||||
wait_for_retry_header=True,
|
||||
)
|
||||
metrics = raw.get("is")
|
||||
checks = metrics.get("checks") if isinstance(metrics, dict) else None
|
||||
if not isinstance(checks, list) or any(not isinstance(item, dict) for item in checks):
|
||||
raise WqError("平台检查尚无有效 is.checks 结果,请稍后重试", "pending")
|
||||
return checks
|
||||
|
||||
async def pnl(self, alpha_id):
|
||||
"""Wait for slow PnL generation separately from transport-error retries.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user