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:
@@ -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'] }),
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user