feat: Find Similar + Merge in Pattern Library

Backend (patterns.py):
- POST /api/patterns/find-similar — GPT-4o-mini compares a library pattern
  against all others; returns merge_as_instance | counter_scenario | new_pattern
- POST /api/patterns/merge — full transactional merge: remaps pattern_id in
  pattern_score_history, trade_entry_prices, ai_reasoning_traces, ai_call_logs,
  skipped_trades; unions historical_instances (dedup); sums backtest counters;
  deletes the discarded pattern

Frontend (PatternExplorer.tsx + useApi.ts):
- ScanSearch button on each non-builtin card triggers find-similar
- Inline result panel: duplicate → merge CTA with confirmation + destructive warning
  counter_scenario → apply regime_tag CTA; new_pattern → green "unique" badge
- useFindSimilarPattern + useMergePatterns hooks invalidate all-patterns on success

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OpenSquared
2026-06-23 12:13:33 +02:00
parent 8d257adf3d
commit a630cdc708
3 changed files with 372 additions and 13 deletions

View File

@@ -363,6 +363,33 @@ export const useTogglePattern = () => {
})
}
export interface SimilarityResult {
recommendation: 'merge_as_instance' | 'counter_scenario' | 'new_pattern'
match_id: string | null
match_name: string | null
confidence: number
reasoning: string
suggested_regime_tag: string
}
export const useFindSimilarPattern = () =>
useMutation({
mutationFn: (pattern_id: string) =>
api.post('/patterns/find-similar', { pattern_id }).then(r => r.data as SimilarityResult),
})
export const useMergePatterns = () => {
const qc = useQueryClient()
return useMutation({
mutationFn: (body: { keep_id: string; discard_id: string }) =>
api.post('/patterns/merge', body).then(r => r.data),
onSuccess: () => {
qc.invalidateQueries({ queryKey: ['all-patterns'] })
qc.invalidateQueries({ queryKey: ['last-scores'] })
},
})
}
// ── Auto-Cycle ───────────────────────────────────────────────────────────────
export const useCycleStatus = () =>