Styling & Tags

Bar/wick/border coloring with 8-char hex alpha, plotshape markers, region shading, and define_tag for signal classification.

bar_color

bar_color(colors)

Set candle body color per bar.

Pass an array of hex color strings, same length as bars. Use None for bars that should keep their default color.

Accepts 6-char (#rrggbb) or 8-char (#rrggbbaa) hex. The 8-char form encodes per-bar alpha, so you can mark a bar 30% transparent without touching the global candle opacity setting.

Parameters

  • colors (array-like, default required): Array of hex color strings (6- or 8-char) or None values

Returns

None

Example

import numpy as np
from quant_charts import bar_color, ta, close

sma = ta.sma(close, 20)
# 8-char hex makes high-vol bars translucent green, default elsewhere
colors = np.where(np.array(close) > sma, "#22c55ecc", None)
bar_color(colors)

wick_color

wick_color(colors)

Set candle wick color per bar.

Same format as bar_color: array of hex strings or None.

Parameters

  • colors (array-like, default required): Array of hex color strings or None values

Returns

None

Example

wick_color(np.where(volume > avg_vol, "#ffffff", None))

border_color

border_color(colors)

Set candle border color per bar (independent from body).

Sets the border color independently from bar_color. With body color unchanged and a contrasting border, candles render in the TradingView "hollow candle" style. Useful for highlighting trades that hit TP vs SL, regime transitions, or volume outliers without changing the up/down body color.

Parameters

  • colors (array-like, default required): Array of hex color strings or None values, same length as bars. None entries use the body color (default LWC behavior).

Returns

None

Example

import numpy as np
from quant_charts import border_color, volume_series

vol = volume_series(df)
avg = np.nanmean(vol)
# white outline on high-volume bars, default border elsewhere
border_color(np.where(vol > 2 * avg, "#ffffff", None))

Notes

  • 8-char hex (#rrggbbaa) encodes per-bar alpha, e.g. "#ffffff80" for 50% white.
  • Pair with bar_color() for the hollow-candle look (body translucent, border solid).
  • Mismatched array length raises ValueError.

set_bar_color

set_bar_color(condition, color, wick?, border?)

Apply color where condition is True.

Convenience wrapper. Multiple calls compose (last-writer-wins per bar). Easier than building a full color array. Optional wick overrides the wick color and border overrides the candle outline color independently.

Parameters

  • condition (bool array, default required): Boolean array, color applied where True
  • color (str, default required): Hex color for candle body
  • wick (str, default None): Optional hex color for wick
  • border (str, default None): Optional hex color for candle border / outline

Returns

None

Example

from quant_charts import set_bar_color, ta, close

rsi = ta.rsi(close, 14)
set_bar_color(rsi > 70, "#ef4444")                              # Red when overbought
set_bar_color(rsi < 30, "#22c55e")                              # Green when oversold
set_bar_color(rsi > 70, "#ef4444", wick="#ff6666")              # Body + wick
set_bar_color(rsi > 70, "#ef4444", wick="#ff6666", border="#aa0000")  # Body + wick + border

plotshape

plotshape(condition, shape?, location?, color?, size?, text?)

Plot shape markers on the chart where condition is True.

Draws visual markers at specific bars. Useful for marking entry/exit signals, divergences, or pattern detections.

Parameters

  • condition (bool array, default required): Boolean array, shapes placed where True
  • shape (str, default "triangle_up"): triangle_up, triangle_down, circle, diamond, square, arrow_up, arrow_down, cross
  • location (str, default "above"): "above" (above high), "below" (below low), "at" (at close)
  • color (str, default "#00ff00"): Hex color string
  • size (str, default "small"): "small", "medium", "large"
  • text (str, default None): Optional text label next to shape

Returns

None

Example

from quant_charts import plotshape, cross_above, cross_below

plotshape(cross_above(fast, slow),
    shape="triangle_up", color="#22c55e")
plotshape(cross_below(fast, slow),
    shape="triangle_down", location="below",
    color="#ef4444")
plotshape(vol_spike, shape="diamond",
    color="#e0af68", text="VOL")

draw_box

draw_box(start_index, end_index, top, bottom, color?, opacity?, extend_right?)

Draw a single rectangle at exact bar indices and exact price bounds.

Unlike box() which colors regions where a boolean condition is True, draw_box() places one rectangle at specific coordinates. Use for precise control: FVG (fair value gap) zones, order blocks, marked-up support / resistance bands, archived value-area boxes.

Parameters

  • start_index (int, default required): Bar index where the box starts
  • end_index (int, default required): Bar index where the box ends. Ignored when extend_right=True.
  • top (float, default required): Price level for the top edge
  • bottom (float, default required): Price level for the bottom edge
  • color (str, default "#7aa2f7"): Hex color string
  • opacity (int, default 20): Opacity 0-100
  • extend_right (bool, default False): If True, the box extends to the right edge of the chart regardless of end_index.

Returns

None

Example

from quant_charts import draw_box

# Mark a fair value gap zone between bars 100 and 140
draw_box(100, 140, top=4250.50, bottom=4248.75, color="#7aa2f7", opacity=15)

# Open-ended order block that extends right until invalidated
draw_box(200, 0, top=4255.0, bottom=4253.0, color="#f7768e", opacity=20, extend_right=True)

bgcolor

bgcolor(condition, color?, opacity?)

Draw full-height colored background on bars where condition is True.

Creates colored bands behind candles for session highlighting, zone marking, or any condition-based background.

Opacity is 0-100 (default 20). Color is a hex string.

Parameters

  • condition (array-like, default required): Boolean array, True bars get the background
  • color (str, default "#7aa2f7"): Hex color string
  • opacity (int, default 20): Opacity 0-100

Returns

None

Example

from quant_charts import indicator, bgcolor, hour, minute

@indicator("Session Colors", overlay=True)
class SessionColors:
    def calculate(self, df):
        asian = (hour >= 19) | (hour < 1)
        european = (hour >= 1) & (hour < 9)
        american = (hour >= 9) & (hour < 16)

        bgcolor(asian, color="#FF5722", opacity=10)
        bgcolor(european, color="#4CAF50", opacity=10)
        bgcolor(american, color="#2196F3", opacity=10)
        return {}

box

box(start_condition, end_condition, color?, opacity?, top?, bottom?)

Draw rectangular regions from start to end bars.

Each True in start_condition begins a box. The next True in end_condition closes it.

If top/bottom are None, the box spans full chart height. Set them to price levels for bounded boxes.

Parameters

  • start_condition (array-like, default required): Boolean, True starts a box
  • end_condition (array-like, default required): Boolean, True ends a box
  • color (str, default "#7aa2f7"): Hex color string
  • opacity (int, default 20): Opacity 0-100
  • top (float, default None): Price level for top edge (None = chart top)
  • bottom (float, default None): Price level for bottom edge (None = chart bottom)

Returns

None

Example

from quant_charts import indicator, box, hour, minute

@indicator("Session Box", overlay=True)
class SessionBox:
    def calculate(self, df):
        session_open = (hour == 9) & (minute == 30)
        session_close = (hour == 16) & (minute == 0)
        box(session_open, session_close, color="#7aa2f7", opacity=15)
        return {}

define_tag

define_tag(name, description, label?, color?)

Declare a tag with display metadata for the UI.

Tags are boolean arrays that mark conditions on each bar. Strategies use them to filter trades (e.g., only trade during "morning" bars). define_tag() sets the label, description, and color shown in the tag dropdown.

You can also skip define_tag(). Returning a dict from calculate() auto-creates tags from the dict keys.

Tags drive preventive trade filtering in the analyzer: with a tag selected, the Rust engine receives a trading_mask and refuses to open trades where the tag is False on that bar.

Parameters

  • name (str, default required): Tag identifier (matches return dict key)
  • description (str, default required): Tooltip description shown in UI
  • label (str, default name): Short display label
  • color (str, default auto): Hex color for the tag badge

Returns

None

Example

from quant_charts import indicator, input, plot, ta, define_tag, close

@indicator("RSI", overlay=False)
class RSI:
    period = input.int(14, "Period")

    def calculate(self, df):
        rsi = ta.rsi(close, self.period)
        plot(rsi, "RSI", color="#9C27B0")

        define_tag("overbought", f"RSI > 70", color="#DC2626")
        define_tag("oversold", f"RSI < 30", color="#16A34A")

        return {
            "overbought": rsi > 70,
            "oversold": rsi < 30,
        }

Notes

  • Tags returned from calculate() are boolean arrays. True marks bars where the condition holds.
  • Tags enable preventive trade filtering in the backtester. Only allow trades during tagged periods.
  • If you skip define_tag(), tags are auto-generated from the return dict keys with default colors.

block_entries

block_entriesreturn {..., 'block_entries': bool_array}

Per-bar entry gate returned from calculate(). A truthy value blocks NEW entries on that bar.

Return a block_entries boolean array (same length as the data) alongside your signals. On bars where it is truthy, the engine refuses to open a new position; bars that are absent or falsy are allowed. Open positions and exit signals are unaffected. This is the array-shaped replacement for the old disable_entries action: gate entries declaratively instead of dispatching a runtime action.

For stop-modification patterns, return the SL/TP you want directly in sl_long/tp_long/sl_short/tp_short (the engine ratchets them favorably so a trailing stop just works), or build them with the breakeven_when / shift_levels helpers. To close a position, set exit_long/exit_short.

Parameters

  • block_entries (bool[], default absent (all allowed)): Truthy bar = block new entries; absent/falsy = allowed

Returns

part of the calculate() return dict

Example

from quant_charts import strategy, use_indicator, cross_above, cross_below

@strategy(name="Gated MA", overlay=True)
class GatedMA:
    def calculate(self, df):
        fast = use_indicator('sma', period=10)
        slow = use_indicator('sma', period=30)
        atr  = use_indicator('atr', period=14)

        # block entries when volatility is too low to be worth trading
        low_vol = atr < atr.rolling(50).mean() * 0.5

        return {
            'entry_long': cross_above(fast, slow),
            'exit_long':  cross_below(fast, slow),
            'block_entries': low_vol,
        }

Notes

  • block_entries only gates NEW entries. It never force-closes an open position and never suppresses exits.
  • Replaces the removed disable_entries trigger. For breakeven/tick-shift stops use breakeven_when() / shift_levels().
  • Rust strategies use the symmetric .with_trading_mask(vec) builder where true = allowed.

breakeven_when

breakeven_when(entries, entry_price, tag, offset_ticks=0, tick_size=0.25)

Build an SL-shaped array that snaps the stop to the entry price (plus an optional tick offset) on every bar where tag is True.

Authoring-time helper. Forward-fills the most recent entry price since the last entries signal, then writes entry_price ± offset_ticks*tick_size wherever tag is True (NaN elsewhere). Return the result as sl_long (or sl_short). It cannot see engine-side fills/exits, but the favorable-only SL ratchet makes a stale breakeven harmless (a value that would loosen the stop is rejected). Replaces the old set_sl_breakeven trigger.

Parameters

  • entries (bool[], default required): Entry signal array (where positions open)
  • entry_price (float[], default required): Price series to read the entry price from (e.g. df["close"])
  • tag (bool[], default required): Bars where the stop should move to breakeven
  • offset_ticks (int, default 0): Ticks above/below entry (signed)
  • tick_size (float, default 0.25): Instrument tick size

Returns

np.ndarray (sl-shaped, NaN where inactive)

Example

from quant_charts import breakeven_when

out['sl_long'] = breakeven_when(entry_long, df['close'], regime_flip, offset_ticks=2)

Notes

  • Best-effort replacement for the stateful set_sl_breakeven; documented limitation: no live fill/exit feedback.

shift_levels

shift_levels(levels, tag, ticks, tick_size=0.25)

Return a copy of an SL/TP array shifted by ticks*tick_size from each True in tag onward (sticky-forward).

Authoring-time helper. From each True in tag, every non-NaN value in levels is shifted by ticks*tick_size (negative tightens). The shift is sticky-forward, mirroring the old shift_sl_ticks/shift_tp_ticks triggers which moved the stop until the position closed.

Parameters

  • levels (float[], default required): SL or TP array to transform
  • tag (bool[], default required): Bars from which the shift arms
  • ticks (int, default required): Signed tick delta to apply
  • tick_size (float, default 0.25): Instrument tick size

Returns

np.ndarray (copy of levels)

Example

from quant_charts import shift_levels

out['sl_long'] = shift_levels(sl_long, tighten_now, ticks=-4)

Notes

  • Replaces the removed shift_sl_ticks / shift_tp_ticks triggers.