User

TAB CHART : ISKO AUR ACCURATE AUR EFFECTIVE KAISE BANAU ? \"\"\" tab_chart.py — SIA Live Candlestick-Chart module. ⚠️ DESIGN-PRINCIPLE (jaisa poore-project mein hai): Yeh function KOI NAYI- GANANA NAHI karta — sirf EXISTING data (jo `run_technical_analys

Asked by CNI Follower · 2 hrs ago · 12-09-2026

Context: Python/Streamlit candlestick module for Indian equities; objective is to make the chart more accurate (no subtle bugs / mis‑alignments) and more effective visually, without adding any new indicators.

Below are concrete, code‑level suggestions that respect your design principle: “no new calculations here, only plotting of existing outputs”.

---

1. Immediate correctness fixes

1. Missing `pandas` import in this file

You are using `pd.notna` in `render_lightweight_chart`, but `pandas` is only imported in `_self_test()`.

```python

TOP OF FILE

import pandas as pd # ADD THIS

```

Without this, `render_lightweight_chart` will raise `NameError: name 'pd' is not defined`.

---

2. Ensure index is sorted before `tail()`

If `df` ever comes unsorted (common when merging / concatenating), `tail(lookback_days)` can pick a non‑contiguous window. Make both chart functions robust:

```python

def render_price_chart(...):

if df is None or df.empty:

raise ValueError("render_price_chart: df khaali hai — pehle data fetch karein.")

plot_df = df.sort_index().tail(lookback_days).copy()

...

```

```python

def render_lightweight_chart(...):

if df is None or df.empty:

raise ValueError("render_lightweight_chart: df khaali hai.")

plot_df = df.sort_index().tail(lookback_days).copy()

...

```

This keeps all overlays (DMA, SAR, ADX, zones, VP) aligned with the same clean time window.

---

3. Normalise date types for zones / SAR / ADX

Right now you compare a string `plot_end_str` with `z["confirmed_date"]`. If `confirmed_date` is a `datetime.date` or `Timestamp`, Python will raise `TypeError`.

Make it deterministic by normalising to string (or date) at the point of use:

```python

from datetime import datetime

def _to_date_str(d):

if isinstance(d, str):

return d # assume 'YYYY-MM-DD'

return d.strftime("%Y-%m-%d")

...

plot_start_str = plot_df.index[0].strftime("%Y-%m-%d")

plot_end_str = plot_df.index[-1].strftime("%Y-%m-%d")

...

for zi, z in enumerate(_zones_to_plot):

z_conf_str = _to_date_str(z["confirmed_date"])

if z_conf_str > plot_end_str:

continue

_zone_start = max(z_conf_str, plot_start_str)

...

```

Similarly, in SAR/ADX filters you already convert `p["date"]` via `strftime`, which is fine.

---

4. Guard volume / VP data against bad or empty input

You already handle empty `bins` via `max(..., default=1)`. Add a defensive check so bad structures don’t break chart render:

```python

if volume_profile_data and volume_profile_data.get("available"):

bins = volume_profile_data.get("bins") or []

if not isinstance(bins, list) or not bins:

volume_profile_js = "" # skip VP gracefully

else:

...

```

This keeps chart rendering even if an upstream function returns malformed `volume_profile_data`.

---

2. Visual accuracy & consistency

5. Use consistent up/down colours across Plotly & Lightweight

Currently:

- Plotly candles: `#27AE60` (up), `#E74C3C` (down)

- Lightweight candles: `#26a69a` (up), `#ef5350` (down)

Not a bug, but visually less consistent. Pick one palette and reuse:

```python

_CANDLE_UP = "#26a69a"

_CANDLE_DOWN = "#ef5350"

```

Use these in both `render_price_chart` and `render_lightweight_chart`.

---

6. Avoid extra rounding in JS candle data if not needed

You’re doing `round(..., 2)` for OHLC in `candle_data`. Indian equities are quoted to 2 decimals currently, but if you ever plot derivatives / indices with more precision, this truncates information.

You can safely send full floats; the browser will display fewer decimals as per tooltip/formatting:

```python

"open": float(row["Open"]),

"high": float(row["High"]),

"low": float(row["Low"]),

"close": float(row["Close"]),

```

Same for DMA series.

---

7. Align TradingView symbol and your df symbol

In `render_dual_chart`, TradingView and your custom chart must always refer to the same instrument. You already have `yf_symbol` passed in; you can optionally echo it in the decision panel (you already support `symbol` there). That keeps user mental model consistent: both panes clearly show the same stock/index.

---

3. Effectiveness: clarity and signal‑to‑noise

8. Config flags to control overlays (no new math, only visibility)

Right now everything plots together (DMA, S/R, breakout, zones, SAR, ADX, VP). For some users, this is visually heavy.

Add optional flags to `render_lightweight_chart` / `render_price_chart`:

```python

def render_lightweight_chart(...,

show_dmas: bool = True,

show_zones: bool = True,

show_sar: bool = True,

show_adx: bool = True,

show_volume_profile: bool = True,

):

...

if show_dmas:

dma_series_js = ...

if show_zones and demand_supply_zones:

...

if show_sar and sar_data:

...

if show_adx and adx_data:

...

if show_volume_profile and volume_profile_data:

...

```

In Streamlit you can control these with checkboxes, but the module itself remains pure‑plotting.

---

9. Zone and VP overlays: ensure they never hide candles

You already create VP overlay as a full‑width absolute layer anchored to the right. To guarantee that candles / DMAs always stay visually dominant:

- Keep VP colours low‑opacity (`0.2–0.45`) as you are doing.

- Explicitly ensure overlay container doesn’t extend left too aggressively. Instead of `width:100%`, you can give it a limited width, say `120px`, so it only hugs the right side:

```javascript

style="position:absolute;top:0;right:0;width:120px;height:{height}px;

pointer-events:none;overflow:hidden;">

```

Then keep `right:0` in bars; they will stay in that band without entering the core candle area.

No data change, only layout fix.

---

10. Zone labels: avoid overlapping heavily with each other

Your `positionZoneLabels()` can end up stacking labels on top of each other if multiple zones are near in price and time.

A very light post‑processing (still no new financial logic) is to nudge labels vertically if they are too close in pixel space:

- Keep an array of `y` positions you’ve already used.

- If new `y` is within, say, 18 px of any existing one, shift it down by 18 px until it is unique.

That keeps labels readable without changing the zone logic.

---

11. Decision panel text: keep to 2–3 highly interpretable lines

You’re already doing a good job. To keep it “effective” for an equity trader:

- Line 1: `symbol` + `CURRENT price`

- Line 2: `Trend | Regime | ADX`

- Line 3: `RS vs Nifty | Vol (x avg) | ATR`

You’re exactly structured this way; the main improvement is to ensure you don’t overflow horizontally on smaller screens. You can force line breaks in long texts via `
` if strings become long (e.g., very detailed `regime` descriptions).

---

4. Robustness under Streamlit re‑rendering

12. Avoid inline `window.addEventListener("resize"...` duplicates

Each re‑render of the component adds another `resize` listener. On an active Streamlit app with many tab switches, this can accumulate.

Before adding a new listener, you can:

- Either rely on a simple flag on `window`:

```javascript

if (!window._siaLwResizeAttached) {

window.addEventListener('resize', () => { ... });

window._siaLwResizeAttached = true;

}

```

- Or wrap the entire script in an IIFE that checks by `container.dataset.bound === '1'` before binding.

This is purely a runtime safety; again, no new data logic.

---

13. Explicitly clean overlays on every re‑render

You’re already doing `overlay.innerHTML = ''` in `positionZoneLabels` and `positionVolumeProfile`. That’s good. Just ensure these functions are always called after any `setData` changes you might add later (e.g., if you ever support timeframe switches in the same container).

---

5. For the Plotly chart (`render_price_chart`)

14. Make S/R & breakout labels non‑overlapping

Right now: last candle’s `x` for annotation and close price for `y`. When many levels are close, labels can overlap.

You can slightly offset the annotation `y`:

```python

y=r["level"],

yshift=6, # small vertical offset

```

And for breakout level, you already use `yshift=12`. This small visual separation helps traders distinguish lines in a dense zone cluster without any change to underlying S/R computation.

---

15. Subplot heights and margins tuned for Indian daily charts

Your `row_heights=[0.78, 0.22]` is reasonable. If users are on laptops (typical), you can slightly increase the main panel share:

```python

row_heights=[0.82, 0.18]

```

And reduce top margin when `pattern_annotation` is not present (you already do conditional `t`).

This gives a bit more vertical space to daily candles, which Indian traders usually focus on vs. volume pane.

---

These changes will make the module more robust (no silent bugs), visually consistent across Plotly and Lightweight versions, and easier to interpret for an equity trader—without adding any new indicator or doing any fresh calculation inside `tab_chart.py`.

If you have any further queries, please connect with us on 022-6290-10141 (Timings : 09.00 AM to 05.00 PM) or you can email us on info@cniinfoxchange.com