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, defaultrequired): 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, defaultrequired): 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, defaultrequired): 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, defaultrequired): Boolean array, color applied where Truecolor(str, defaultrequired): Hex color for candle bodywick(str, defaultNone): Optional hex color for wickborder(str, defaultNone): 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, defaultrequired): Boolean array, shapes placed where Trueshape(str, default"triangle_up"): triangle_up, triangle_down, circle, diamond, square, arrow_up, arrow_down, crosslocation(str, default"above"):"above"(above high),"below"(below low),"at"(at close)color(str, default"#00ff00"): Hex color stringsize(str, default"small"):"small","medium","large"text(str, defaultNone): 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, defaultrequired): Bar index where the box startsend_index(int, defaultrequired): Bar index where the box ends. Ignored when extend_right=True.top(float, defaultrequired): Price level for the top edgebottom(float, defaultrequired): Price level for the bottom edgecolor(str, default"#7aa2f7"): Hex color stringopacity(int, default20): Opacity 0-100extend_right(bool, defaultFalse): 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, defaultrequired): Boolean array, True bars get the backgroundcolor(str, default"#7aa2f7"): Hex color stringopacity(int, default20): 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, defaultrequired): Boolean, True starts a boxend_condition(array-like, defaultrequired): Boolean, True ends a boxcolor(str, default"#7aa2f7"): Hex color stringopacity(int, default20): Opacity 0-100top(float, defaultNone): Price level for top edge (None = chart top)bottom(float, defaultNone): 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, defaultrequired): Tag identifier (matches return dict key)description(str, defaultrequired): Tooltip description shown in UIlabel(str, defaultname): Short display labelcolor(str, defaultauto): 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.Truemarks 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[], defaultabsent (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_entriesonly gates NEW entries. It never force-closes an open position and never suppresses exits.- Replaces the removed
disable_entriestrigger. For breakeven/tick-shift stops usebreakeven_when()/shift_levels(). - Rust strategies use the symmetric
.with_trading_mask(vec)builder wheretrue= 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[], defaultrequired): Entry signal array (where positions open)entry_price(float[], defaultrequired): Price series to read the entry price from (e.g. df["close"])tag(bool[], defaultrequired): Bars where the stop should move to breakevenoffset_ticks(int, default0): Ticks above/below entry (signed)tick_size(float, default0.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[], defaultrequired): SL or TP array to transformtag(bool[], defaultrequired): Bars from which the shift armsticks(int, defaultrequired): Signed tick delta to applytick_size(float, default0.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_tickstriggers.