feat: expose local self-correlation tools through MCP
Deploy production / deploy (push) Successful in 57s
Deploy production / deploy (push) Successful in 57s
This commit is contained in:
@@ -16,6 +16,7 @@ from ..catalog.contracts import CatalogJobInput
|
||||
from ..catalog.platform import platform_options, validate_platform_scope
|
||||
from ..catalog.research_metadata import ResearchMetadata, availability_key
|
||||
from ..catalog.service import Catalog
|
||||
from ..correlation import MIN_SAMPLES, THRESHOLD, WINDOW_YEARS
|
||||
from ..models import Account, Alpha, BacktestItem, Job, JobItem, ResearchRequest, SimulationAttempt, now
|
||||
from ..research.serialization import encode_snapshot
|
||||
from ..research.workspace_contracts import FieldAvailabilityInput
|
||||
@@ -48,7 +49,15 @@ class ResearchAccess:
|
||||
"settings_schema": DirectCandidate.model_json_schema(),
|
||||
"confirmation": "调用者须已获本批执行授权;直接提交后返回稳定运行 ID",
|
||||
"duplicate_policies": ["reject", "rerun"], "permissions": sorted(self.principal.scopes),
|
||||
"metadata_only": True, "actual_platform_allowance": None}
|
||||
"metadata_only": True, "actual_platform_allowance": None,
|
||||
"self_correlation": {
|
||||
"check_with": "check_self_correlation", "read_with": "get_self_correlation",
|
||||
"job_with": "get_refresh_job", "max_targets": 100, "source": "local",
|
||||
"reference_scope": "本地已同步的同地区已提交 Alpha,排除自身",
|
||||
"method": "累计 PnL 日变化的 Pearson 相关系数,取带符号最大值",
|
||||
"threshold": THRESHOLD, "min_samples": MIN_SAMPLES, "window_years": WINDOW_YEARS,
|
||||
"platform_check": False,
|
||||
}}
|
||||
|
||||
async def catalog(self, args):
|
||||
data = await Catalog(self.db).search(args.filters, args.dataset_id)
|
||||
@@ -101,7 +110,7 @@ class ResearchAccess:
|
||||
|
||||
async def refresh_job(self, args):
|
||||
job = await self.db.get(Job, args.job_id)
|
||||
if not job or job.kind not in {"catalog_sync", "field_sync", "pnl_refresh"}:
|
||||
if not job or job.kind not in {"catalog_sync", "field_sync", "pnl_refresh", "self_correlation"}:
|
||||
raise ResearchError("NOT_FOUND", "研究刷新任务不存在")
|
||||
result = await self.business.get_job_status(args.job_id)
|
||||
query = select(JobItem).where(JobItem.job_id == job.id, JobItem.error.is_not(None))
|
||||
@@ -111,6 +120,27 @@ class ResearchAccess:
|
||||
return {**result, "job_id": job.id, "artifact_reference": job.payload,
|
||||
"errors": page([{"alpha_id": e.alpha_id, "error": e.error} for e in errors], total, args.limit, args.offset)}
|
||||
|
||||
async def check_self_correlation(self, args):
|
||||
"""Queue local checks for synced IDs; caller commits before waking the runner.
|
||||
|
||||
The shared job service deduplicates active batches. Missing PnL is fetched
|
||||
by the durable runner, so slow upstream reads do not hold the MCP call.
|
||||
"""
|
||||
ids = sorted(set(args.alpha_ids))
|
||||
existing = set(await self.db.scalars(select(Alpha.id).where(Alpha.id.in_(ids))))
|
||||
if existing != set(ids):
|
||||
raise ResearchError("NOT_FOUND", "部分 Alpha 尚未同步,请先导入", affected_items=sorted(set(ids)-existing))
|
||||
job = await self.business.create_sync_job(JobInput(kind="self_correlation", alpha_ids=ids))
|
||||
self.wake = "jobs"
|
||||
return {"job_id": job["id"], "status": job["status"], "alpha_ids": ids,
|
||||
"source": "local", "job_with": "get_refresh_job", "read_with": "get_self_correlation"}
|
||||
|
||||
async def self_correlation(self, args):
|
||||
"""Read the latest local result without fetching PnL or starting a check."""
|
||||
data = await self.business.get_self_correlation(args.alpha_id)
|
||||
status = "not_cached" if not data["cached"] else "stale" if data["result"]["stale"] else "available"
|
||||
return {"alpha_id": args.alpha_id, "source": "local", "status": status, **data}
|
||||
|
||||
async def history(self, args):
|
||||
return await self.evidence.history(args)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user