Add multi-factor conviction gate to activation
Deploy / lint (push) Successful in 8s
Deploy / test (push) Successful in 35s
Deploy / deploy (push) Successful in 26s

Make "qualified" mean an edge candidate, not just R:R + confidence.
The gate now also requires (all admin-configurable, defaults on):
- high conviction: recommended_action LONG_HIGH / SHORT_HIGH only
- clean read: risk_level Low (no contradicting signals)
- probable primary target: best target probability >= min (default 60)

- Shared predicate: app/services/qualification.py +
  frontend/src/lib/qualification.ts (mirrored)
- Activation config extended (min_target_probability,
  require_high_conviction, exclude_conflicts) with bool-aware
  get/update + validation
- /trades/performance switched to ?qualified_only=true, applying
  the full gate server-side; confidence breakdown stays unfiltered
- Dashboard "Qualified", Signals "Qualified only" toggle, and
  Track Record all use the one gate; Admin gains the new controls

Sentiment provider runtime config (prior change) included.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-13 11:50:42 +02:00
parent 6da65b8d8f
commit d53ed972d1
25 changed files with 924 additions and 110 deletions
+37 -2
View File
@@ -126,13 +126,13 @@ export function useUpdateActivationSettings() {
const { addToast } = useToast();
return useMutation({
mutationFn: (payload: Record<string, number>) =>
mutationFn: (payload: Partial<import('../lib/types').ActivationConfig>) =>
adminApi.updateActivationSettings(payload),
onSuccess: () => {
qc.invalidateQueries({ queryKey: ['admin', 'activation-settings'] });
qc.invalidateQueries({ queryKey: ['activation'] });
qc.invalidateQueries({ queryKey: ['performance'] });
addToast('success', 'Activation thresholds updated');
addToast('success', 'Activation gate updated');
},
onError: (error: Error) => {
addToast('error', error.message || 'Failed to update activation thresholds');
@@ -140,6 +140,41 @@ export function useUpdateActivationSettings() {
});
}
export function useSentimentSettings() {
return useQuery({
queryKey: ['admin', 'sentiment-settings'],
queryFn: () => adminApi.getSentimentSettings(),
});
}
export function useUpdateSentimentSettings() {
const qc = useQueryClient();
const { addToast } = useToast();
return useMutation({
mutationFn: (payload: { provider?: string; model?: string; api_key?: string }) =>
adminApi.updateSentimentSettings(payload),
onSuccess: () => {
qc.invalidateQueries({ queryKey: ['admin', 'sentiment-settings'] });
addToast('success', 'Sentiment provider updated');
},
onError: (error: Error) => {
addToast('error', error.message || 'Failed to update sentiment provider');
},
});
}
export function useTestSentimentProvider() {
const { addToast } = useToast();
return useMutation({
mutationFn: (ticker: string) => adminApi.testSentimentSettings(ticker),
onError: (error: Error) => {
addToast('error', error.message || 'Sentiment test failed');
},
});
}
export function useTickerUniverseSetting() {
return useQuery({
queryKey: ['admin', 'ticker-universe'],