Add activation thresholds: qualified-signal defaults and views
Admin-configurable thresholds (min R:R, default 2.0; min confidence, default 70%) defining what counts as an actionable signal: - Admin Settings: new Activation Thresholds panel (GET/PUT /admin/settings/activation) - GET /trades/activation exposes values to all users with access - Signals/Setups: filters initialize from activation values - Track Record: "Qualified signals only" toggle (default on) via min_rr/min_confidence params on /trades/performance; the confidence breakdown always covers the full population so the thresholds can be validated against outcomes - Dashboard: "Qualified" metric and qualified-first Top Setups - Outcome evaluator unchanged: every setup is still evaluated Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import { useState } from 'react';
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { useActivation } from '../../hooks/useActivation';
|
||||
import { usePerformance } from '../../hooks/usePerformance';
|
||||
import { triggerJob } from '../../api/admin';
|
||||
import { Button } from '../ui/Button';
|
||||
@@ -89,7 +91,14 @@ function BreakdownTable({ rows, labelHeader, mapLabel }: {
|
||||
}
|
||||
|
||||
export function TrackRecordPanel() {
|
||||
const { data, isLoading, isError, error } = usePerformance();
|
||||
const [qualifiedOnly, setQualifiedOnly] = useState(true);
|
||||
const activation = useActivation();
|
||||
|
||||
const params = qualifiedOnly && activation.data
|
||||
? { min_rr: activation.data.min_rr, min_confidence: activation.data.min_confidence }
|
||||
: undefined;
|
||||
|
||||
const { data, isLoading, isError, error } = usePerformance(params);
|
||||
const queryClient = useQueryClient();
|
||||
const toast = useToast();
|
||||
|
||||
@@ -106,6 +115,26 @@ export function TrackRecordPanel() {
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="glass-sm flex flex-wrap items-center justify-between gap-3 px-4 py-3">
|
||||
<label className="flex cursor-pointer items-center gap-2.5 text-sm text-gray-300">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={qualifiedOnly}
|
||||
onChange={(e) => setQualifiedOnly(e.target.checked)}
|
||||
className="h-4 w-4 cursor-pointer accent-blue-400"
|
||||
/>
|
||||
<span>
|
||||
Qualified signals only
|
||||
{activation.data && (
|
||||
<span className="num ml-2 text-xs text-gray-500">
|
||||
R:R ≥ {activation.data.min_rr.toFixed(1)} · conf ≥ {activation.data.min_confidence.toFixed(0)}%
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
</label>
|
||||
<p className="text-xs text-gray-500">Confidence breakdown always covers all setups.</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<Disclosure summary="How outcomes are measured">
|
||||
<p className="text-xs text-gray-400">
|
||||
@@ -138,8 +167,9 @@ export function TrackRecordPanel() {
|
||||
|
||||
{data && data.overall.total === 0 && (
|
||||
<Callout variant="empty">
|
||||
No evaluated setups yet. Outcomes appear once setups are old enough for their stop or
|
||||
target to be hit — the evaluator runs nightly, or click Evaluate Now.
|
||||
{qualifiedOnly
|
||||
? 'No evaluated setups meet the activation thresholds yet. Untick "Qualified signals only" to see all evaluated setups, or wait for more outcomes.'
|
||||
: 'No evaluated setups yet. Outcomes appear once setups are old enough for their stop or target to be hit — the evaluator runs nightly, or click Evaluate Now.'}
|
||||
{data.pending > 0 && ` ${data.pending} setup${data.pending === 1 ? '' : 's'} pending evaluation.`}
|
||||
</Callout>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user