add track-record reset; drop dead distance_penalty_factor knob
Deploy / lint (push) Successful in 6s
Deploy / test (push) Successful in 36s
Deploy / deploy (push) Successful in 24s

Track Record: new "Reset" action (POST /admin/track-record/reset) deletes all
trade setups so stats start fresh after material scoring/setup changes — live
setups regenerate on the next scan. Guarded by a confirm dialog.

Recommendation config: remove distance_penalty_factor, which was exposed in the
admin UI but consumed nowhere (the touch-probability model superseded it). A
knob that silently does nothing is worse than no knob. Remaining defaults are
left as-is — they're reasonable, and the honest way to tune them is backtesting
against accumulated outcomes, not invented "researched" numbers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-14 14:44:02 +02:00
parent 6e06f51bb6
commit 90618d186f
9 changed files with 62 additions and 13 deletions
+7
View File
@@ -180,3 +180,10 @@ export function cleanupData(olderThanDays: number) {
})
.then((r) => r.data);
}
// Track record
export function resetTrackRecord() {
return apiClient
.post<{ trade_setups: number }>('admin/track-record/reset')
.then((r) => r.data);
}
@@ -9,7 +9,6 @@ const DEFAULTS: RecommendationConfig = {
confidence_diff_threshold: 20,
signal_alignment_weight: 0.15,
sr_strength_weight: 0.2,
distance_penalty_factor: 0.1,
momentum_technical_divergence_threshold: 30,
fundamental_technical_divergence_threshold: 40,
};
@@ -82,7 +81,6 @@ export function RecommendationSettings() {
<NumberInput label="Signal Alignment Weight" value={form.signal_alignment_weight} min={0} max={1} step={0.01} onChange={(v) => setField('signal_alignment_weight', v)} />
<NumberInput label="S/R Strength Weight" value={form.sr_strength_weight} min={0} max={1} step={0.01} onChange={(v) => setField('sr_strength_weight', v)} />
<NumberInput label="Distance Penalty Factor" value={form.distance_penalty_factor} min={0} max={1} step={0.01} onChange={(v) => setField('distance_penalty_factor', v)} />
<NumberInput label="Momentum-Technical Divergence Threshold" value={form.momentum_technical_divergence_threshold} min={0} max={100} onChange={(v) => setField('momentum_technical_divergence_threshold', v)} />
<NumberInput label="Fundamental-Technical Divergence Threshold" value={form.fundamental_technical_divergence_threshold} min={0} max={100} onChange={(v) => setField('fundamental_technical_divergence_threshold', v)} />
@@ -3,7 +3,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useActivation } from '../../hooks/useActivation';
import { activationSummary } from '../../lib/qualification';
import { usePerformance } from '../../hooks/usePerformance';
import { triggerJob } from '../../api/admin';
import { triggerJob, resetTrackRecord } from '../../api/admin';
import { Button } from '../ui/Button';
import { Callout } from '../ui/Callout';
import { Disclosure } from '../ui/Disclosure';
@@ -112,6 +112,29 @@ export function TrackRecordPanel() {
},
});
const resetMutation = useMutation({
mutationFn: () => resetTrackRecord(),
onSuccess: (data) => {
toast.addToast('success', `Track record reset — ${data.trade_setups} setups cleared. Run the scanner to rebuild.`);
queryClient.invalidateQueries({ queryKey: ['performance'] });
queryClient.invalidateQueries({ queryKey: ['trades'] });
},
onError: () => {
toast.addToast('error', 'Failed to reset track record');
},
});
const onReset = () => {
if (
window.confirm(
'Reset the track record? This permanently deletes ALL trade setups and their outcomes. ' +
'Live setups will regenerate on the next R:R scan. This cannot be undone.',
)
) {
resetMutation.mutate();
}
};
return (
<div className="space-y-6">
<div className="glass-sm flex flex-wrap items-center justify-between gap-3 px-4 py-3">
@@ -145,9 +168,14 @@ export function TrackRecordPanel() {
evaluator runs nightly after OHLCV collection.
</p>
</Disclosure>
<Button onClick={() => evaluateMutation.mutate()} loading={evaluateMutation.isPending} className="shrink-0">
{evaluateMutation.isPending ? 'Evaluating…' : 'Evaluate Now'}
</Button>
<div className="flex shrink-0 items-center gap-2">
<Button onClick={() => evaluateMutation.mutate()} loading={evaluateMutation.isPending}>
{evaluateMutation.isPending ? 'Evaluating…' : 'Evaluate Now'}
</Button>
<Button variant="danger" onClick={onReset} loading={resetMutation.isPending}>
{resetMutation.isPending ? 'Resetting…' : 'Reset'}
</Button>
</div>
</div>
{isLoading && (
-1
View File
@@ -307,7 +307,6 @@ export interface RecommendationConfig {
confidence_diff_threshold: number;
signal_alignment_weight: number;
sr_strength_weight: number;
distance_penalty_factor: number;
momentum_technical_divergence_threshold: number;
fundamental_technical_divergence_threshold: number;
}