diff --git a/app/src/components/ConfluenceChart.tsx b/app/src/components/ConfluenceChart.tsx index 240ad2a..aab1844 100644 --- a/app/src/components/ConfluenceChart.tsx +++ b/app/src/components/ConfluenceChart.tsx @@ -83,15 +83,22 @@ function ChartTooltip({ active, payload }: { active?: boolean; payload?: Array<{ if (!active || !payload?.length) return null; const point = payload[0].payload; const allFires = [...point.bullFires, ...point.bearFires]; + // Deduplicate by slot name (a slot can fire multiple times on the same day) + const seen = new Set(); + const uniqueFires = allFires.filter((f) => { + if (seen.has(f.slotName)) return false; + seen.add(f.slotName); + return true; + }); return (
{formatDateFull(point.date)}
Close: ${point.close.toFixed(2)}
- {allFires.length > 0 && ( + {uniqueFires.length > 0 && (
- {allFires.map((f, i) => ( + {uniqueFires.map((f, i) => (
{f.slotName} @@ -185,12 +192,19 @@ export function ConfluenceChart({ symbol }: ConfluenceChartProps) { const data: ChartPoint[] = candles.map((c) => { const key = toDateKey(c.ts); const dayFires = firesByDate.get(key) ?? []; + // Deduplicate by slot name per day (a slot can fire multiple times) + const seenSlots = new Set(); + const uniqueDayFires = dayFires.filter((f) => { + if (seenSlots.has(f.slotId)) return false; + seenSlots.add(f.slotId); + return true; + }); return { date: c.ts, dateShort: formatDate(c.ts), close: c.c, - bullFires: dayFires.filter((f) => f.body === 'bull'), - bearFires: dayFires.filter((f) => f.body === 'bear' || f.body === 'exit'), + bullFires: uniqueDayFires.filter((f) => f.body === 'bull'), + bearFires: uniqueDayFires.filter((f) => f.body === 'bear' || f.body === 'exit'), }; }); diff --git a/app/src/components/ConfluencePanel.tsx b/app/src/components/ConfluencePanel.tsx index a374fb5..dcad813 100644 --- a/app/src/components/ConfluencePanel.tsx +++ b/app/src/components/ConfluencePanel.tsx @@ -119,19 +119,21 @@ export function ConfluencePanel() { {/* Header */}
-
- -
- pickSymbol(raw.toUpperCase())} - placeholder="Search symbol…" - ariaLabel="Select symbol for signal confluence" - inputClassName="w-full bg-surface-sunken border border-line rounded-lg px-3 py-1.5 text-xs text-fg placeholder:text-fg-muted focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent" - /> +
+
+ +
+ pickSymbol(raw.toUpperCase())} + placeholder="Search symbol…" + ariaLabel="Select symbol for signal confluence" + inputClassName="w-full bg-surface-sunken border border-line rounded-lg px-3 py-1.5 text-xs text-fg placeholder:text-fg-muted focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent" + /> +
{symbol && ( - + {symbol} )} @@ -187,6 +189,13 @@ export function ConfluencePanel() {
) : null} + {/* Signal Timeline chart */} + {symbol && ( + + + + )} + {/* Slot evidence grid */} {assessments.length > 0 && ( @@ -211,13 +220,6 @@ export function ConfluencePanel() { )} - {/* Signal Timeline chart */} - {symbol && ( - - - - )} - {/* Scorecard */} {scorecard && scorecard.slots.length > 0 && (