visual: redesign bottom panel, price axis cleanup and bid/ask indicator

- Bottom panel: dual bid/ask bars per cluster (red=sell, blue=buy) with white volume label inside and delta block below
- Price axis: labels every 10 rows (~7.5pts) instead of every row — cleaner, closer to original
- Price axis: bid/ask spread boxes with arrow indicator (shows on live market open)
- Price axis zoom: inverted so drag-up = zoom in, matching original behavior
- Current price line rendered last (always on top of all bars)
- Bar colors: corrected — ask (buy) = blue, bid (sell) = pink
- Backend: close_time field added to cluster JSON; bar_volume M1 annotation (triggers after replay)
- Backend: last_bid/ask tracked and injected into active cluster WS message

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
rufinomec-afk
2026-06-06 15:53:05 -03:00
co-authored by Claude Sonnet 4.6
parent 11a0921e20
commit 54f269b0ce
4 changed files with 224 additions and 90 deletions
+4
View File
@@ -18,6 +18,7 @@ class FootprintCluster:
self.total_delta: float = 0.0
self.total_volume: float = 0.0
self.total_ticks: int = 0
self.close_time: Optional[int] = None
# levels: price_float -> { 'ask': float, 'bid': float }
# internally we keep float keys to make sorting and arithmetic easy
@@ -40,6 +41,7 @@ class FootprintCluster:
self.open_price = price
self.close_price = price
self.close_time = timestamp_msc
self.total_ticks += 1
# Round price to the nearest tick_size to avoid float precision issues
@@ -316,9 +318,11 @@ class FootprintCluster:
"high": float(self.high) if self.high is not None else None,
"low": float(self.low) if self.low is not None else None,
"poc": float(self.poc) if self.poc is not None else None,
"close_time": int(self.close_time) if self.close_time is not None else None,
"total_delta": float(self.total_delta),
"total_volume": float(self.total_volume),
"total_ticks": int(self.total_ticks),
"bar_volume": None,
"levels": levels_str,
"stacked": {
"buy": bool(self.stacked.get("buy", False)),