How to Modify Candle Border Color in TradingView Pine Script?

Modifying visual elements on your charts is crucial for building effective trading tools. While candle body colors often indicate bullish or bearish sentiment based on close vs. open, the border color offers a distinct layer for conveying additional information. This article delves into how to manipulate candle border colors using Pine Script, providing practical examples for immediate application.

Introduction to Candle Border Color Modification in Pine Script

Understanding the Importance of Visual Cues in Trading

Trading is a visually intensive activity. Traders constantly scan charts for patterns, signals, and key price action. Effective use of color and visual elements can significantly improve readability, highlight critical information, and reduce cognitive load, allowing for faster and more accurate decision-making.

Brief Overview of Pine Script and its Capabilities

Pine Script is TradingView’s proprietary scripting language designed for creating custom indicators and strategies. It provides a powerful yet relatively simple syntax for accessing market data, performing calculations, and plotting results directly onto charts. Its capabilities extend beyond plotting lines and shapes to controlling the visual appearance of standard chart elements, including candles.

Why Modify Candle Border Colors? (Highlighting Specific Price Action)

Standard charts typically color candle borders the same as their bodies (or black/grey). However, by conditionally changing the border color, you can draw attention to specific types of candles or particular market conditions without altering the primary body color information. This is useful for:

  • Highlighting specific patterns (Dojis, Engulfing, etc.)
  • Indicating overbought/oversold conditions on a per-candle basis.
  • Signaling shifts in momentum or volatility.
  • Marking candles that meet criteria for entry or exit signals.

Basic Syntax for Modifying Candle Border Color

Using the ‘plotcandle’ Function

The primary function for controlling candle appearance in Pine Script is plotcandle(). This function plots a series of candles based on provided open, high, low, and close values. While it’s often used to plot candles from a different timeframe or synthesized data, you can also use it to overlay or replace the default chart candles specifically to control their appearance.

Its basic signature includes arguments for open, high, low, close, color, bordercolor, and wickcolor.

plotcandle(open, high, low, close, color, bordercolor, wickcolor, editable, show_last, trackprice, display)

Understanding the ‘bordercolor’ Argument

The bordercolor argument in plotcandle() accepts a color value. This can be a predefined color constant (e.g., color.red, color.blue), a hexadecimal color string (e.g., #FF0000), a variable holding a color value, or a series of color values (allowing for conditional coloring, which is where the power lies).

Setting Static Border Colors (e.g., red, green, blue)

To set a static border color for all candles plotted by the function, simply provide a single color value to the bordercolor argument. Note that you typically pass the built-in open, high, low, and close variables to plotcandle if you just want to modify the appearance of the standard candles.

//@version=5
indicator("Static Blue Borders", overlay=true)

// Plot standard candles but force border color to blue
plotcandle(open, high, low, close, color=color.blue, bordercolor=color.blue)

Note: In this basic example, the body color is also set to blue. We will address conditional body coloring shortly, but the focus here is strictly on bordercolor. To control body color and border color separately, you would pass distinct color series to color and bordercolor.

Conditional Border Color Modification

The true utility of border color modification comes from making it conditional. You can change the border color based on price action, indicator values, or any other criteria defined in your script.

Using Conditional Statements (if/else) to Change Border Colors

You can define a variable that holds the desired border color and update it based on conditions using if/else if/else or ternary operators (?:). This color variable is then passed to the bordercolor argument of plotcandle(). Remember that color variables, like most series variables in Pine Script, update on each bar.

Example: Coloring Borders Based on Bullish/Bearish Candles

A common use case is to have bullish candles (close >= open) with one border color and bearish candles (close < open) with another.

//@version=5
indicator("Bull/Bear Borders", overlay=true)

// Determine border color based on bullish or bearish candle
color borderCol = na
if close >= open
    borderCol := color.lime // Bullish border
else
    borderCol := color.red  // Bearish border

// Plot standard candles with conditional border color
plotcandle(open, high, low, close, color=na, bordercolor=borderCol)

Explanation: We initialize borderCol to na (not a number) for clarity. Then, using an if/else structure, we assign color.lime if the current bar is bullish (close >= open) and color.red otherwise. Finally, we pass this dynamic borderCol variable to plotcandle. We set color=na to use TradingView’s default body coloring based on the security’s price.

Example: Coloring Borders Based on Volume or Price Action

You can extend this to other conditions. For instance, highlight candles with exceptionally high volume.

//@version=5
indicator("High Volume Borders", overlay=true)

// Calculate average volume for comparison
float avgVol = ta.sma(volume, 20)

// Determine border color
color borderCol = na
if volume > avgVol * 2
    borderCol := color.yellow // Highlight high volume candles
else if close >= open
    borderCol := color.silver // Default bullish border
else
    borderCol := color.gray   // Default bearish border

// Plot candles
plotcandle(open, high, low, close, color=na, bordercolor=borderCol)

Explanation: This script calculates a 20-period Simple Moving Average of volume. If the current bar’s volume is more than twice the average volume, the border is colored yellow. Otherwise, it uses silver for bullish candles and gray for bearish ones, providing a secondary color scheme alongside the high-volume alert.

Combining Multiple Conditions for Complex Border Color Rules

For more sophisticated analysis, you can combine multiple conditions using logical operators (and, or, not) or nested if statements. This allows you to define specific border colors for various scenarios.

//@version=5
indicator("Complex Borders", overlay=true)

// Define conditions
bool isDoji = math.abs(close - open) <= (high - low) * 0.1 // Simple Doji check
bool isHighVol = volume > ta.sma(volume, 50) * 1.5
bool isNearResistance = high >= ta.highest(high, 20)[1] // Example: new 20-bar high (simple)

// Determine border color based on prioritized conditions
color borderCol = na
if isDoji and isHighVol
    borderCol := color.fuchsia // High-Volume Doji
else if isDoji
    borderCol := color.orange  // Regular Doji
else if isNearResistance and close >= open
    borderCol := color.aqua    // Bullish near recent high
else if close >= open
    borderCol := color.green   // Regular Bullish
else
    borderCol := color.red     // Regular Bearish

// Plot candles
plotcandle(open, high, low, close, color=na, bordercolor=borderCol)

Explanation: This example prioritizes conditions. It first checks for high-volume Dojis, then regular Dojis. If neither, it checks for bullish candles near a recent high before defaulting to standard bullish/bearish coloring. This demonstrates how to build a hierarchy of visual alerts using border colors.

Advanced Techniques and Considerations

Moving beyond simple examples, several techniques can improve the maintainability, flexibility, and performance of your scripts.

Using User-Defined Functions to Simplify Color Logic

As your conditional logic grows, it’s good practice to encapsulate the color determination within a user-defined function. This makes the main body of your script cleaner and the color logic reusable.

//@version5
indicator("Function Based Borders", overlay=true)

// Function to determine border color based on input conditions
getColor(isDoji, isHighVol, isNearResistance, isBullish) =>
    color resultColor = na
    if isDoji and isHighVol
        resultColor := color.fuchsia
    else if isDoji
        resultColor := color.orange
    else if isNearResistance and isBullish
        resultColor := color.aqua
    else if isBullish
        resultColor := color.green
    else
        resultColor := color.red
    resultColor

// Define input conditions (can be based on indicators, etc.)
bool isDoji = math.abs(close - open) <= (high - low) * 0.1
bool isHighVol = volume > ta.sma(volume, 50) * 1.5
bool isNearResistance = high >= ta.highest(high, 20)[1]
bool isBullish = close >= open

// Get border color using the function
color borderCol = getColor(isDoji, isHighVol, isNearResistance, isBullish)

// Plot candles
plotcandle(open, high, low, close, color=na, bordercolor=borderCol)

Implementing Color Palettes for Consistent Visuals

Instead of hardcoding color constants everywhere, define your color scheme at the beginning of your script using constants or input variables. This makes it easy to change the entire color palette without modifying the core logic.

//@version=5
indicator("Palette Borders", overlay=true)

// Define Color Palette as constants
color PALETTE_HIGH_VOLUME_DOJI = color.new(color.fuchsia, 0)
color PALETTE_DOJI = color.new(color.orange, 0)
color PALETTE_BULLISH_NEAR_RES = color.new(color.aqua, 0)
color PALETTE_BULLISH = color.new(color.green, 0)
color PALETTE_BEARISH = color.new(color.red, 0)

// Define conditions (as before)
bool isDoji = math.abs(close - open) <= (high - low) * 0.1
bool isHighVol = volume > ta.sma(volume, 50) * 1.5
bool isNearResistance = high >= ta.highest(high, 20)[1]
bool isBullish = close >= open

// Determine border color using the palette constants
color borderCol = na
if isDoji and isHighVol
    borderCol := PALETTE_HIGH_VOLUME_DOJI
else if isDoji
    borderCol := PALETTE_DOJI
else if isNearResistance and isBullish
    borderCol := PALETTE_BULLISH_NEAR_RES
else if isBullish
    borderCol := PALETTE_BULLISH
else
    borderCol := PALETTE_BEARISH

// Plot candles
plotcandle(open, high, low, close, color=na, bordercolor=borderCol)

This approach makes your script more readable and manageable, especially for complex visual schemes.

Optimizing Code for Performance (especially with complex conditions)

While Pine Script is optimized, excessive complex calculations or function calls within the per-bar logic can impact performance on long history lookbacks. When determining color, ensure your calculations are efficient. Avoid recalculating values that don’t change per bar if possible (though color does change per bar, focus on optimizing the inputs to the color logic). Using var or varip for variables that retain their value across bars can sometimes help, though color calculation is inherently per-bar.

Complex nested if/else or multiple function calls within the color logic can add overhead. For simple conditions, ternary operators (?:) are often more concise and potentially slightly faster. For complex logic, a well-structured if/else if chain or a function is generally clearer and the performance difference negligible unless the conditions themselves involve heavy computations.

Avoiding Common Pitfalls and Errors

  • plotcandle Overlays: Ensure overlay=true in indicator() if you intend to overlay on the main price chart. Without it, your custom candles will appear in a separate pane.
  • color=na vs. Explicit Color: If you want to use TradingView’s default bullish/bearish body coloring, set the color argument of plotcandle to na. If you want to control body color conditionally too, you need to pass a color series to both color and bordercolor.
  • Scope of Variables: Ensure your color variable is updated on each bar. Variables defined inside local blocks (like if) need careful handling if they are intended for use outside that block (e.g., using the := assignment operator). The examples above correctly use a variable defined outside the if/else block.
  • Performance: While usually not a major issue just for border color, be mindful of performance if your conditions require extensive or historical lookback calculations on every bar.

Practical Examples and Use Cases

Let’s put these concepts into practice with specific trading scenarios.

Example 1: Highlighting Doji Candles with a Specific Border Color

Dojis often indicate indecision. Highlighting them can quickly point out potential reversal points or pauses in trend.

//@version=5
indicator("Doji Border Highlighter", overlay=true)

// Define sensitivity for Doji check (percentage of range)
float dojiSensitivity = input.float(0.1, "Doji Body/Range Ratio", minval=0.01, maxval=1.0, step=0.01)
color dojiColor = input.color(color.orange, "Doji Border Color")

// Condition for a Doji (body is small relative to total range)
bool isDoji = math.abs(close - open) <= (high - low) * dojiSensitivity

// Determine border color
color borderCol = isDoji ? dojiColor : na // Use ternary operator for simplicity

// Plot candles with default body color (na) and conditional border
plotcandle(open, high, low, close, color=na, bordercolor=borderCol)

Explanation: This script defines ‘Doji’ based on the body size relative to the candle’s total range, controlled by a user input (dojiSensitivity). It uses a ternary operator to assign the dojiColor if the candle is a Doji, and na otherwise (meaning the default border color will be used if not a Doji). The body color remains default (na).

Example 2: Coloring Candle Borders Based on Trend Strength

Using an indicator like the Average Directional Index (ADX) to color borders can provide a visual cue about whether the current price action is occurring within a strong or weak trend.

//@version=5
indicator("ADX Trend Borders", overlay=true)

int adxLength = input.int(14, "ADX Length", minval=1)
float adxThreshold = input.float(25.0, "ADX Strength Threshold", minval=0)

// Calculate ADX
int dir = ta.direction(adxLength)
float adx = ta.adx(adxLength, dir)

// Determine border color based on ADX
color borderCol = na
if adx > adxThreshold
    borderCol := color.purple // Strong trend color
else
    borderCol := color.gray   // Weak trend color

// Plot candles
plotcandle(open, high, low, close, color=na, bordercolor=borderCol)

// Optional: plot ADX in a separate pane for reference
// plot(adx, title="ADX", color=color.blue)
// plot(adxThreshold, title="ADX Threshold", color=color.red, style=plot.style_hline)

Explanation: This example calculates the ADX. If the ADX is above a user-defined threshold, the candle border is colored purple, indicating a strong trend. Otherwise, it’s colored gray. This visually differentiates price action based on the underlying trend momentum.

Example 3: Creating a Custom Indicator that Uses Border Colors for Signals

Border colors can serve as non-intrusive signals. For example, a simple crossover strategy could use border colors to indicate potential entry points.

//@version=5
indicator("MA Crossover Signal Borders", overlay=true)

int fastMALen = input.int(10, "Fast MA Length", minval=1)
int slowMALen = input.int(30, "Slow MA Length", minval=1)

// Calculate MAs
float fastMA = ta.sma(close, fastMALen)
float slowMA = ta.sma(close, slowMALen)

// Check for crossover signals
bool bullishCrossover = ta.crossover(fastMA, slowMA)
bool bearishCrossover = ta.crossunder(fastMA, slowMA)

// Determine border color based on signals
color borderCol = na
if bullishCrossover
    borderCol := color.green // Bullish signal color
else if bearishCrossover
    borderCol := color.red   // Bearish signal color

// Plot candles with default body color and signal border
plotcandle(open, high, low, close, color=na, bordercolor=borderCol)

// Optional: plot the MAs
// plot(fastMA, title="Fast MA", color=color.blue)
// plot(slowMA, title="Slow MA", color=color.red)

Explanation: This script calculates a fast and a slow Moving Average. It identifies bars where a bullish or bearish crossover occurs. The border color is set to green on a bullish crossover bar and red on a bearish crossover bar, providing a clear visual signal directly on the candle where the event happened. This is less visually cluttering than plotting shapes or text for signals.

By leveraging the bordercolor argument of plotcandle and combining it with Pine Script’s conditional logic, you can add a powerful layer of visual information to your charts, enhancing your ability to identify specific price action and potential trading opportunities.


Leave a Reply