fix: align research metadata with live WorldQuant responses

This commit is contained in:
yuxuanhui
2026-09-08 23:32:52 +08:00
parent a6e36e50ec
commit c67031a7cb
16 changed files with 900 additions and 12 deletions
+1
View File
@@ -23,6 +23,7 @@ class SimulationSettings(Contract):
language: Literal["FASTEXPR"] = "FASTEXPR"
visualization: bool = False
maxTrade: Literal["ON", "OFF"] = "OFF"
maxPosition: Literal["ON", "OFF"] = "OFF"
class Candidate(Contract):
+13 -3
View File
@@ -70,8 +70,16 @@ def setting_rows(data):
raise HTTPException(502, "平台设置结构无法识别,未发布新快照") from None
def normalize_availability(data):
def normalize_availability(data, *, instrument_type=None):
"""Use the request's instrument only for the platform field-detail `data` form.
Legacy availability rows must still state their own instrument. Missing market,
delay or universe never inherits the requested scope.
"""
raw = data.get("availability")
detail_form = raw is None and isinstance(data.get("data"), list)
if detail_form:
raw = data["data"]
if not isinstance(raw, list):
return {"status": "needs_review", "items": [], "reason": "平台未提供可识别的 availability 列表"}
rows, malformed = [], False
@@ -83,7 +91,7 @@ def normalize_availability(data):
universes = universes if isinstance(universes, list) else [universes]
for universe in universes:
if (
item.get("instrumentType") == "EQUITY"
item.get("instrumentType", instrument_type if detail_form else None) == "EQUITY"
and type(item.get("delay")) is int
and item["delay"] in (0, 1)
and isinstance(item.get("region"), str)
@@ -227,8 +235,10 @@ class ResearchMetadata:
async def refresh_availability(self, body):
data = await upstream(self.client.field_availability(body.field_id, body.scope))
if data.get("id") is not None and data["id"] != body.field_id:
raise HTTPException(502, "平台返回字段与请求不一致,保留原可用性快照")
content = {
**normalize_availability(data),
**normalize_availability(data, instrument_type=body.scope.instrument_type),
"field_id": body.field_id,
"scope": body.scope.model_dump(),
}
+12 -1
View File
@@ -27,6 +27,17 @@ def scope_of(settings):
}
def seed_settings(snapshot):
"""Decode executable settings, retaining returned historical dates in the parent snapshot.
startDate/endDate are result window metadata absent from POST settings. Unknown
execution parameters still fail strict validation rather than being discarded.
"""
return SimulationSettings.model_validate(
{k: v for k, v in snapshot.items() if k not in ("startDate", "endDate")}
)
class Experiments:
def __init__(self, db):
self.db = db
@@ -315,7 +326,7 @@ class Experiments:
[parent_snapshot] if parent_snapshot is not None else await self.parents([body.alpha_id], [])
)
original = parents[0]
base = SimulationSettings.model_validate(original["settings"])
base = seed_settings(original["settings"])
expression = original["expression"]
snapshots, _ = await self.inputs(body.input_ids)
groups = defaultdict(list)
+9 -6
View File
@@ -619,12 +619,15 @@ class ResearchRuntime:
):
raise HTTPException(403, "候选不属于此研究运行的固定输入范围")
if "backtest" not in run.authorization["methods"] or any(
c["settings"]
not in (
run.authorization.get("allowed_settings", [run.authorization["settings"]])
if experiment["evidence"].get("method") == "settings"
else [run.authorization["settings"]]
)
SimulationSettings.model_validate(c["settings"]).model_dump(mode="json")
not in [
SimulationSettings.model_validate(value).model_dump(mode="json")
for value in (
run.authorization.get("allowed_settings", [run.authorization["settings"]])
if experiment["evidence"].get("method") == "settings"
else [run.authorization["settings"]]
)
]
or not c.get("input_ids")
or any(
not any(
+2 -2
View File
@@ -9,7 +9,7 @@ from ..backtests.contracts import SimulationSettings, fingerprint
from ..backtests.service import uid
from ..models import Account, ResearchFlowRun, ResearchStepRun
from .assets import Assets
from .experiments import Experiments, scope_of
from .experiments import Experiments, scope_of, seed_settings
from .serialization import encode_snapshot as jsonable_encoder
from .workspace_contracts import WorkflowSpec
@@ -195,7 +195,7 @@ class Workflows:
parents = await experiments.parents(body.parent_alpha_ids, [])
allowed_settings = [body.settings.model_dump(mode="json")]
if settings_variant:
base = SimulationSettings.model_validate(parents[0]["settings"])
base = seed_settings(parents[0]["settings"])
for snapshot in inputs:
scope = snapshot["scope"]
target = SimulationSettings.model_validate(