@@ -284,6 +284,37 @@ def _annotate_metadata(ax, card_meta: dict) -> None:
284284 )
285285
286286
287+ def _short_fib (fib_id : str ) -> str :
288+ """Compact member id: drop the ``fib_BTC-USD_<tf>_`` prefix, keep the timestamp tail."""
289+ return fib_id .split ("_" )[- 1 ]
290+
291+
292+ def _annotate_member_table (ax , band : list [LevelRow ]) -> None :
293+ """Top-left member table (TF / ratio / price / short fib id) — replaces stacked labels.
294+
295+ Because the four members sit within ~$36 their per-line labels would overlap and hide
296+ each other; a compact table keeps every member individually source-traceable while the
297+ level lines stay clean.
298+ """
299+ lines = ["member levels (fixed-band)" , "TF ratio price fib" ]
300+ for r in band :
301+ lines .append (
302+ f"{ r .timeframe :<3} { r .ratio :<5g} { r .level_price :>7,.0f} { _short_fib (r .fib_id )} "
303+ )
304+ ax .text (
305+ 0.012 ,
306+ 0.985 ,
307+ "\n " .join (lines ),
308+ transform = ax .transAxes ,
309+ fontsize = 8 ,
310+ family = "monospace" ,
311+ va = "top" ,
312+ ha = "left" ,
313+ bbox = dict (boxstyle = "round,pad=0.35" , fc = "white" , ec = "#888888" , alpha = 0.9 ),
314+ zorder = 7 ,
315+ )
316+
317+
287318def _draw_card (
288319 df : pd .DataFrame ,
289320 band : list [LevelRow ],
@@ -299,7 +330,9 @@ def _draw_card(
299330
300331 ``show_members=False`` (clean): candle backdrop + shaded ``[min,max]`` confluence band
301332 + representative-price line + metadata box. ``show_members=True`` (levels): adds one
302- horizontal line per member level, coloured by timeframe, labelled ``TF ratio price id``.
333+ horizontal line per member level, coloured by timeframe, plus a top-left member table
334+ (TF / ratio / price / short fib id) so every member stays source-traceable without the
335+ near-coincident per-line labels overlapping.
303336 """
304337 fig , ax = _candle_backdrop (df , title , fig_w )
305338
@@ -314,38 +347,19 @@ def _draw_card(
314347 ls = "-" ,
315348 alpha = 0.85 ,
316349 zorder = 3 ,
317- label = f"repr { cluster .representative_price :,.0f} " ,
318350 )
319351
320352 if show_members :
321- x_right = len (df ) - 1
322- seen_tf : set [str ] = set ()
323353 for r in band :
324- color = _TF_COLORS .get (r .timeframe , "#555555" )
325354 ax .axhline (
326355 r .level_price ,
327- color = color ,
356+ color = _TF_COLORS . get ( r . timeframe , "#555555" ) ,
328357 lw = 1.2 ,
329358 ls = "--" ,
330359 alpha = 0.9 ,
331360 zorder = 4 ,
332- label = r .timeframe if r .timeframe not in seen_tf else None ,
333361 )
334- seen_tf .add (r .timeframe )
335- ax .annotate (
336- f"{ r .timeframe } { r .ratio :g} @ { r .level_price :,.0f} { r .fib_id } " ,
337- xy = (x_right , r .level_price ),
338- xytext = (- 4 , 3 ),
339- textcoords = "offset points" ,
340- fontsize = 7 ,
341- fontweight = "bold" ,
342- color = color ,
343- va = "bottom" ,
344- ha = "right" ,
345- bbox = dict (boxstyle = "round,pad=0.2" , fc = "white" , ec = color , alpha = 0.85 ),
346- zorder = 6 ,
347- )
348- ax .legend (loc = "upper left" , fontsize = 7 , framealpha = 0.85 , title = "member TF" )
362+ _annotate_member_table (ax , band )
349363
350364 _annotate_metadata (ax , card_meta )
351365 fig .savefig (out_path , dpi = 120 , bbox_inches = "tight" )
@@ -419,8 +433,15 @@ def render_confluence_card(
419433 fig_w = max (16 , min (n // 2 , 36 ))
420434 a_str = f"{ df .index [0 ]:%Y-%m-%d} "
421435 b_str = f"{ df .index [- 1 ]:%Y-%m-%d} "
436+ # Show the cluster id once; only append the resolved id when it differs from the
437+ # signature label (avoids the "c001 (c001)" duplication when they coincide).
438+ id_part = (
439+ signature .label
440+ if signature .label == cluster .cluster_id
441+ else f"{ signature .label } ({ cluster .cluster_id } )"
442+ )
422443 base_title = (
423- f"{ symbol } MTF confluence { signature . label } ( { cluster . cluster_id } ) | "
444+ f"{ symbol } MTF confluence { id_part } | "
424445 f"{ ',' .join (cluster .timeframes )} { backdrop_tf } backdrop { a_str } → { b_str } (log)"
425446 )
426447 _draw_card (
0 commit comments