mirror of
https://ghfast.top/https://github.com/aeroxw/tick-stock-panel.git
synced 2026-09-12 14:24:15 +08:00
feat(mining): 环境数据覆盖不足时弹窗补算并自动重跑挖矿
This commit is contained in:
@@ -58,6 +58,23 @@ const PROFILE_LABELS: Record<MiningBudgetProfile, string> = {
|
||||
balanced: '均衡档',
|
||||
strict: '严格档',
|
||||
}
|
||||
// 市场环境覆盖不足的失败文案 (regime_alignment 校验抛出), 命中时弹「补算并重跑」确认。
|
||||
const REGIME_COVERAGE_ERROR_RE = /市场环境数据覆盖不完整|市场环境数据为空/
|
||||
const REGIME_FIRST_MISSING_RE = /缺少前一交易日环境\s*(\d{4}-\d{2}-\d{2})/
|
||||
|
||||
/** run.request 带 auto/auto_screening 附加字段, 而 start 接口 extra="forbid", 重跑前剥回纯 MiningRequestV1。 */
|
||||
function stripMiningRequestExtras(request: MiningRun['request']): MiningRequestV1 {
|
||||
const {
|
||||
factor_names, strategy_ids, symbols, asset_type, start, end, budget_profile,
|
||||
commission_pct, stamp_tax_pct, slippage_bps, correlation_threshold,
|
||||
max_combination_factors, beam_width, max_finalists, force,
|
||||
} = request
|
||||
return {
|
||||
factor_names, strategy_ids, symbols, asset_type, start, end, budget_profile,
|
||||
commission_pct, stamp_tax_pct, slippage_bps, correlation_threshold,
|
||||
max_combination_factors, beam_width, max_finalists, force,
|
||||
}
|
||||
}
|
||||
|
||||
interface MiningDraft {
|
||||
assetType: 'stock' | 'etf'
|
||||
@@ -606,6 +623,43 @@ export function MiningWorkbench() {
|
||||
onError: error => toast(`保存失败 · ${String((error as Error).message || error)}`, 'error'),
|
||||
})
|
||||
|
||||
// 市场环境覆盖不足: 挖矿内部 T-1 环境校验 (fail-closed) 失败时弹「补算并重跑」确认。
|
||||
// 确定 → 调 regime/recompute 补算缺失区间后自动重跑同一 payload; 取消 → 仅关闭不动作。
|
||||
const [regimeBackfill, setRegimeBackfill] = useState<{ payload: MiningRequestV1; start?: string } | null>(null)
|
||||
const regimeErrorHandled = useRef('')
|
||||
useEffect(() => {
|
||||
if (task.isPending) {
|
||||
regimeErrorHandled.current = ''
|
||||
return
|
||||
}
|
||||
if (!task.error || !REGIME_COVERAGE_ERROR_RE.test(task.error)) return
|
||||
if (regimeErrorHandled.current === task.error) return
|
||||
const request = task.run?.request
|
||||
if (!request) return
|
||||
regimeErrorHandled.current = task.error
|
||||
setRegimeBackfill({
|
||||
payload: stripMiningRequestExtras(request),
|
||||
start: REGIME_FIRST_MISSING_RE.exec(task.error)?.[1],
|
||||
})
|
||||
}, [task.error, task.isPending, task.run])
|
||||
const regimeRecomputeAndRun = useMutation({
|
||||
mutationFn: async (job: { payload: MiningRequestV1; start?: string }) => {
|
||||
// start 缺省(环境数据为空)时后端从 enriched 最早日全量重算; 有 start 则只补缺口区间
|
||||
const recomputed = await api.regimeRecompute(job.start)
|
||||
return { recomputed, job }
|
||||
},
|
||||
onSuccess: ({ recomputed, job }) => {
|
||||
for (const key of [QK.regimeLatest, QK.regimeCoverage, QK.regimeHistory()]) {
|
||||
queryClient.invalidateQueries({ queryKey: key })
|
||||
}
|
||||
toast(recomputed.computed > 0 ? `环境补算完成 · 新增 ${recomputed.computed} 天,重新开始挖掘` : '环境数据已是最新,重新开始挖掘', 'success')
|
||||
setRegimeBackfill(null)
|
||||
submitMining(job.payload)
|
||||
},
|
||||
onError: error => toast(`环境补算失败 · ${String((error as Error).message || error)}`, 'error'),
|
||||
})
|
||||
const regimeBackdrop = useDialogBackdrop(() => setRegimeBackfill(null), () => !regimeRecomputeAndRun.isPending)
|
||||
|
||||
const attachRun = (run: MiningRun) => {
|
||||
const params = new URLSearchParams(searchParams)
|
||||
params.set('run', run.run_id)
|
||||
@@ -814,6 +868,30 @@ export function MiningWorkbench() {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 市场环境覆盖不足的补救确认: 补算缺失区间后自动重跑, 取消则不做任何操作。 */}
|
||||
{regimeBackfill && (
|
||||
<div {...regimeBackdrop} className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4">
|
||||
<div onClick={e => e.stopPropagation()} className="w-full max-w-sm rounded-2xl border border-border bg-surface p-5 shadow-2xl">
|
||||
<div className="flex items-center gap-2 text-sm font-semibold text-foreground">
|
||||
<Database className="h-4 w-4 shrink-0 text-warning" />
|
||||
市场环境数据不完整
|
||||
</div>
|
||||
<p className="mt-2 text-xs leading-5 text-secondary">
|
||||
{regimeBackfill.start
|
||||
? <>环境数据缺少 <span className="font-mono text-foreground">{regimeBackfill.start}</span> 起的部分交易日,挖掘的 T-1 环境校验未通过。点击确定将自动补算该区间(结束至今日),完成后重新开始挖掘。</>
|
||||
: '尚未计算市场环境数据。点击确定将执行全量计算(历史较长时耗时较久),完成后重新开始挖掘。'}
|
||||
</p>
|
||||
<div className="mt-4 flex justify-end gap-2">
|
||||
<button type="button" disabled={regimeRecomputeAndRun.isPending} onClick={() => setRegimeBackfill(null)} className="h-8 rounded-btn border border-border px-3 text-xs text-secondary hover:bg-elevated disabled:opacity-50">取消</button>
|
||||
<button type="button" disabled={regimeRecomputeAndRun.isPending} onClick={() => regimeRecomputeAndRun.mutate(regimeBackfill)} className="inline-flex h-8 items-center gap-1.5 rounded-btn bg-accent px-3 text-xs font-semibold text-white hover:opacity-90 disabled:opacity-50">
|
||||
{regimeRecomputeAndRun.isPending ? <LoaderCircle className="h-3.5 w-3.5 animate-spin" /> : <Database className="h-3.5 w-3.5" />}
|
||||
补算并重新挖掘
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user