fix: align research metadata with live WorldQuant responses
This commit is contained in:
@@ -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):
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user