feat: Pattern Lab — historical backtest engine for pattern discovery

- Remove all built-in patterns (no proof of legitimacy); seed_builtin_patterns is now a no-op
- DB: add backtest_lab_runs table + backtest_hits/runs_count columns on patterns
- services/pattern_lab.py: build_historical_context (yfinance + RSI/MA200),
  run_ai_backtest (GPT-4o as historical analyst), evaluate_outcomes (actual moves at T+horizon)
- routers/pattern_lab.py: POST /run, POST /evaluate/{id}, GET /runs, DELETE /runs/{id},
  POST /save-pattern (promotes hit pattern to library with reliability counters)
- PatternLab.tsx: 34 preset events 2015-2025 (macro/geo/credit/fx/commodities/volatility/tech),
  3-panel layout — preset selector + wizard + run history, market data table,
  AI pattern cards with hit/miss outcome display, Save to Library button
- useApi.ts: usePatternLabRuns, useRunPatternLab, useEvaluatePatternLab, useSaveLabPattern, useDeleteLabRun
- Sidebar + App.tsx: /pattern-lab route + FlaskConical nav link

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OpenSquared
2026-06-22 17:55:48 +02:00
parent a68a08d9af
commit cbf989502c
8 changed files with 1148 additions and 31 deletions

View File

@@ -958,3 +958,52 @@ export const useRefreshInstitutionalReports = () =>
mutationFn: (report_type?: string) =>
api.post('/institutional/refresh', null, { params: report_type ? { report_type } : {} }).then(r => r.data),
})
// ── Pattern Lab ───────────────────────────────────────────────────────────────
export const usePatternLabRuns = () =>
useQuery({
queryKey: ['pattern-lab-runs'],
queryFn: () => api.get('/pattern-lab/runs').then(r => r.data),
staleTime: 30_000,
})
export const useRunPatternLab = () => {
const qc = useQueryClient()
return useMutation({
mutationFn: (body: {
preset_id?: string; theme: string; analysis_date: string;
horizon_days: number; assets: string[]; theme_hint: string;
}) => api.post('/pattern-lab/run', body).then(r => r.data),
onSuccess: () => qc.invalidateQueries({ queryKey: ['pattern-lab-runs'] }),
})
}
export const useEvaluatePatternLab = () => {
const qc = useQueryClient()
return useMutation({
mutationFn: (run_id: string) => api.post(`/pattern-lab/evaluate/${run_id}`, {}).then(r => r.data),
onSuccess: () => qc.invalidateQueries({ queryKey: ['pattern-lab-runs'] }),
})
}
export const useSaveLabPattern = () => {
const qc = useQueryClient()
return useMutation({
mutationFn: (body: {
run_id: string; pattern_index: number; name?: string;
category?: string; signal_direction?: string; asset_class?: string;
}) => api.post('/pattern-lab/save-pattern', body).then(r => r.data),
onSuccess: () => {
qc.invalidateQueries({ queryKey: ['all-patterns'] })
qc.invalidateQueries({ queryKey: ['pattern-lab-runs'] })
},
})
}
export const useDeleteLabRun = () => {
const qc = useQueryClient()
return useMutation({
mutationFn: (run_id: string) => api.delete(`/pattern-lab/runs/${run_id}`).then(r => r.data),
onSuccess: () => qc.invalidateQueries({ queryKey: ['pattern-lab-runs'] }),
})
}