# Column Resolvers

Parquet-shape-portable helpers: volume_series, delta_series, vwap_series, df_col_or.

<a id="why-these-helpers-exist"></a>
## Why these helpers exist

Different parquets ship different columns. The resolvers let one indicator/strategy work on every shape.

Native OHLC files often carry [`volume`](https://quantchartsllc.com/docs/python/py-data.md#volume) / [`delta`](https://quantchartsllc.com/docs/python/py-data.md#delta) / [`vwap`](https://quantchartsllc.com/docs/python/py-data.md#vwap). TBBO-aggregated bars carry `bid_vol` / `ask_vol` / `tickCount`. Some older files only have `tickCount`. Hard-coding column names breaks portability across data files.

The four helpers below resolve a single concept (the volume signal, the signed delta signal, the VWAP series) with an explicit fallback chain. Use them in `calculate()` instead of writing if/elif chains.

Declare `required_columns=[...]` only for columns your math truly needs (typically [`high`](https://quantchartsllc.com/docs/python/py-data.md#high)/[`low`](https://quantchartsllc.com/docs/python/py-data.md#low)/[`close`](https://quantchartsllc.com/docs/python/py-data.md#close)); leave optional columns out and read them via the resolvers so the validator does not reject parquets that lack the optional columns.

<a id="volume_series"></a>
## volume_series

```python
volume_series(df) -> np.ndarray
```

Per-bar volume with a fallback chain.

**Priority:**

1. `volume` column (any OHLC parquet)
2. `bid_vol` + `ask_vol` (TBBO-aggregated bars)
3. `tickCount` (QC-converted parquets)
4. zeros (nothing available)

NaN entries are coerced to 0. Returns numpy array of length `len(df)`.

### Returns

numpy.ndarray of float64

### Example

```python
from quant_charts import indicator, plot, volume_series, PlotType

@indicator(name="Volume", overlay=False, required_columns=["close"])
class Volume:
    def calculate(self, df):
        vol = volume_series(df)
        plot(vol, "Volume", color="#7AA2F7", plot_type=PlotType.HISTOGRAM)
```

<a id="delta_series"></a>
## delta_series

```python
delta_series(df) -> np.ndarray
```

Per-bar signed delta with a fallback chain.

**Priority:**

1. `delta` column (most OHLC parquets)
2. `ask_vol` - `bid_vol` (TBBO-aggregated bars)
3. `sign(close - open)` (any OHLC parquet)
4. zeros (nothing available)

NaN entries are coerced to 0. Pair with [`cvd(...)`](https://quantchartsllc.com/docs/python/py-signals.md#cvd) for cumulative volume delta.

### Returns

numpy.ndarray of float64

### Example

```python
from quant_charts import delta_series, cvd, plot

delta_arr = delta_series(df)
cvd_arr = cvd(delta_arr)
plot(cvd_arr, "CVD", color="#7AA2F7")
```

<a id="vwap_series"></a>
## vwap_series

```python
vwap_series(df) -> np.ndarray
```

Per-bar VWAP from precomputed column or session-cumulative typical price.

**Priority:**

1. `vwap` column (precomputed)
2. session-cumulative `typical_price * volume_series(df) / cumsum(volume_series(df))` where `typical_price = (high + low + close) / 3`

Falls through `volume_series(df)` so it works regardless of whether `volume`, `bid_vol+ask_vol`, or `tickCount` is present. NaN denominators are handled internally.

### Returns

numpy.ndarray of float64

### Example

```python
from quant_charts import vwap_series, vwap_band, ta, high, low, close

vwap = vwap_series(df)
atr = ta.atr(high, low, close, 14)
upper, lower = vwap_band(vwap, atr, mult=1.5)
```

<a id="df_col_or"></a>
## df_col_or

```python
df_col_or(df, *names, default=None) -> np.ndarray | default
```

First named column that exists, as a numpy array, or `default` if none exist.

Useful for opting into one of several alternative columns the data might or might not ship. Non-strict: returns `default` (None by default) when no name matches, so call sites can branch on `is None`.

### Parameters

- `df` (`DataFrame`, default `required`): pandas DataFrame
- `*names` (`str`, default `required (1+)`): candidate column names in priority order
- `default` (`Any`, default `None`): returned when no name matches

### Returns

numpy.ndarray of float64, or default

### Example

```python
from quant_charts import df_col_or
import numpy as np

# pick the first available size column, fall back to zeros
size = df_col_or(df, "trade_size", "tickCount", default=np.zeros(len(df)))

# returns None when neither column exists; branch on it
vwap = df_col_or(df, "vwap")
if vwap is not None:
    plot(vwap, "VWAP", color="#E0AF68")
```

<a id="raw_ticks"></a>
## raw_ticks

```python
raw_ticksraw_ticks.df  /  raw_ticks[col]  /  raw_ticks.col_name
```

Full raw parquet DataFrame for the current trading day. Use when bar-level data is not enough.

Module-level accessor populated when the strategy or indicator is run with a raw TBBO parquet attached. Three access patterns:

- `raw_ticks.df` -> the full pandas DataFrame for the day. None when no raw parquet is attached.
- `raw_ticks["col"]` -> pandas Series for one column. KeyError when raw_ticks is unavailable.
- `raw_ticks.col_name` -> numpy array for one column (attribute syntax). AttributeError when the column is missing.

Len check via `len(raw_ticks)`; truthiness check via `bool(raw_ticks)`. Use for analyses that need every tick (not the aggregated bar): print-by-print volume, signed trade direction, microstructure imbalance, large-trade filtering.

### Example

```python
from quant_charts import indicator, raw_ticks, log

@indicator(name="Big Prints", overlay=True, required_columns=["close"])
class BigPrints:
    def calculate(self, df):
        if not raw_ticks:
            log("no raw parquet attached; skipping")
            return
        ticks = raw_ticks.df
        big = ticks[ticks["volume"] > ticks["volume"].quantile(0.99)]
        log(f"{len(big)} big prints in the session")
```

### Notes

- Available only when the parent run was launched with a raw parquet path. Backtests and analyzer runs on aggregated OHLC files do not populate it.
- Returns numpy arrays via attribute access, pandas Series via item access. Pick whichever matches your downstream code.
