Indicator · Pine Script v6 · On the chart

Chart Watermark

by major· code credited to //

This overlay indicator draws a watermark at the top of the price chart showing the ticker and timeframe, plus an optional instrument description, sector, or industry.

View on GitHub

GitHub stars2on the repository
Pine versionv6current
Lines of code230
Settings3inputs
LicenceApache-2.0
Last update3 months ago29 Jul 2026

Add It to Your TradingView Chart

  1. 1Copy the scriptPress Copy script above, or the copy button on the code below.
  2. 2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
  3. 3Paste over the templateCreate a new indicator, select all of the template text and paste. Press Save and give it a name.
  4. 4Add it to the chartPress Add to chart, then adjust the settings with the gear icon next to its name.

Source Code

230 lines of Pine Script v6 from chart_watermark/chart_watermark.pine, published under Apache-2.0. Keep the author's credit and licence lines when you reuse it.

chart_watermark.pine
// Copyright 2025
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//@version=6
indicator('Chart Watermark', overlay = true)

// ═══════════════════════════════════════════════════════════════════════════
// CHART WATERMARK
// ═══════════════════════════════════════════════════════════════════════════
// Displays ticker, timeframe, and optional description/sector/industry info
// as a watermark at the top of the chart. Properly handles ratio/spread charts
// (e.g., AMD/SPY) by fetching info for both symbols.
//
// Display modes:
//   - Description: Shows company name/instrument description
//   - Sector/Industry: Shows sector and industry (stocks only)
//   - Both: Shows description on line 2, sector/industry on line 3
//
// Note: Sector/Industry data is only available for stocks. For ETFs, crypto,
// forex, and futures, the indicator will fall back to showing description.
// ═══════════════════════════════════════════════════════════════════════════

// ═══════════════════════════════════════════════════════════════════════════
// INPUTS
// ═══════════════════════════════════════════════════════════════════════════

// Display mode options
const string DISPLAY_DESCRIPTION = "Description"
const string DISPLAY_SECTOR = "Sector/Industry"
const string DISPLAY_BOTH = "Both"

displayMode = input.string(DISPLAY_DESCRIPTION, title = 'Display Mode', options = [DISPLAY_DESCRIPTION, DISPLAY_SECTOR, DISPLAY_BOTH], group = 'Watermark Settings')
tickerColor = input.color(color.new(#808080, 30), title = 'Ticker & Timeframe', group = 'Watermark Colors')
descriptionColor = input.color(color.new(#808080, 50), title = 'Description / Sector', group = 'Watermark Colors')

// ═══════════════════════════════════════════════════════════════════════════
// TIMEFRAME FORMATTING
// ═══════════════════════════════════════════════════════════════════════════
// Convert timeframe.period to a compact, readable format like TradingView's default

formatTimeframe() =>
    tf = timeframe.period
    if timeframe.isseconds
        str.tostring(timeframe.multiplier) + "s"
    else if timeframe.isminutes
        mins = timeframe.multiplier
        if mins < 60
            str.tostring(mins) + "m"
        else if mins % 60 == 0
            // Evenly divisible by 60 (e.g., 60, 120, 240)
            str.tostring(mins / 60) + "H"
        else
            // Non-standard like 65m - just show as minutes
            str.tostring(mins) + "m"
    else if timeframe.isdaily
        str.tostring(timeframe.multiplier) + "D"
    else if timeframe.isweekly
        str.tostring(timeframe.multiplier) + "W"
    else if timeframe.ismonthly
        multiplier = timeframe.multiplier
        if multiplier == 12
            "1Y"
        else if multiplier == 3
            "1Q"
        else
            str.tostring(multiplier) + "M"
    else
        tf

// ═══════════════════════════════════════════════════════════════════════════
// HELPER FUNCTIONS
// ═══════════════════════════════════════════════════════════════════════════

// Strip exchange prefix from a symbol (e.g., "AMEX:SPY" -> "SPY")
stripExchange(sym) =>
    if str.contains(sym, ":")
        parts = str.split(sym, ":")
        array.size(parts) >= 2 ? array.get(parts, 1) : sym
    else
        sym

// ═══════════════════════════════════════════════════════════════════════════
// SPREAD/RATIO CHART DETECTION AND DESCRIPTION HANDLING
// ═══════════════════════════════════════════════════════════════════════════
// For spread charts like AMD/SPY, syminfo.description just returns the expression.
// We need to parse the ticker and fetch descriptions for each symbol individually.

// Detect if this is a spread chart
isSpread = syminfo.type == "spread"

// Detect spread operator
detectSpreadOperator(ticker) =>
    string result = na
    if str.contains(ticker, "/")
        result := "/"
    else if str.contains(ticker, "*")
        result := "*"
    else if str.contains(ticker, "-")
        result := "-"
    else if str.contains(ticker, "+")
        result := "+"
    result

// Get clean ticker for spread charts (strip exchange prefixes)
getCleanSpreadTicker() =>
    ticker = syminfo.ticker
    operator = detectSpreadOperator(ticker)
    if na(operator)
        ticker
    else
        parts = str.split(ticker, operator)
        if array.size(parts) >= 2
            sym1 = stripExchange(array.get(parts, 0))
            sym2 = stripExchange(array.get(parts, 1))
            sym1 + operator + sym2
        else
            ticker

// Parse spread ticker and get combined description
getSpreadDescription() =>
    ticker = syminfo.ticker
    operator = detectSpreadOperator(ticker)
    if na(operator)
        syminfo.description
    else
        // Split the ticker by the operator
        parts = str.split(ticker, operator)
        if array.size(parts) >= 2
            sym1 = array.get(parts, 0)
            sym2 = array.get(parts, 1)
            // Fetch descriptions for each symbol
            desc1 = request.security(sym1, timeframe.period, syminfo.description, ignore_invalid_symbol = true)
            desc2 = request.security(sym2, timeframe.period, syminfo.description, ignore_invalid_symbol = true)
            // Combine with the same operator for clarity
            cleanSym1 = stripExchange(sym1)
            cleanSym2 = stripExchange(sym2)
            validDesc1 = not na(desc1) and desc1 != "" ? desc1 : cleanSym1
            validDesc2 = not na(desc2) and desc2 != "" ? desc2 : cleanSym2
            validDesc1 + " " + operator + " " + validDesc2
        else
            syminfo.description

// Format sector and industry into a single string
formatSectorIndustry(sector, industry) =>
    hasSector = not na(sector) and sector != ""
    hasIndustry = not na(industry) and industry != ""
    if hasSector and hasIndustry
        sector + " | " + industry
    else if hasSector
        sector
    else if hasIndustry
        industry
    else
        ""

// Get sector/industry for spread charts
getSpreadSectorIndustry() =>
    ticker = syminfo.ticker
    operator = detectSpreadOperator(ticker)
    if na(operator)
        formatSectorIndustry(syminfo.sector, syminfo.industry)
    else
        parts = str.split(ticker, operator)
        if array.size(parts) >= 2
            sym1 = array.get(parts, 0)
            sym2 = array.get(parts, 1)
            sector1 = request.security(sym1, timeframe.period, syminfo.sector, ignore_invalid_symbol = true)
            sector2 = request.security(sym2, timeframe.period, syminfo.sector, ignore_invalid_symbol = true)
            cleanSym1 = stripExchange(sym1)
            cleanSym2 = stripExchange(sym2)
            validSector1 = not na(sector1) and sector1 != "" ? sector1 : cleanSym1
            validSector2 = not na(sector2) and sector2 != "" ? sector2 : cleanSym2
            validSector1 + " " + operator + " " + validSector2
        else
            formatSectorIndustry(syminfo.sector, syminfo.industry)

// Get the appropriate ticker (clean for spreads)
displayTicker = isSpread ? getCleanSpreadTicker() : syminfo.ticker

// Get the appropriate description
description = isSpread ? getSpreadDescription() : syminfo.description

// Get sector/industry info
sectorIndustry = isSpread ? getSpreadSectorIndustry() : formatSectorIndustry(syminfo.sector, syminfo.industry)

// ═══════════════════════════════════════════════════════════════════════════
// WATERMARK DISPLAY
// ═══════════════════════════════════════════════════════════════════════════

// Build the display text
tickerLine = displayTicker + ", " + formatTimeframe()

// Determine what to display based on mode and availability
// If sector/industry is empty, fall back to description
hasSectorIndustry = sectorIndustry != ""
showDescription = displayMode == DISPLAY_DESCRIPTION or displayMode == DISPLAY_BOTH
showSector = (displayMode == DISPLAY_SECTOR or displayMode == DISPLAY_BOTH) and hasSectorIndustry

// Calculate number of rows needed (1 for ticker + optional description + optional sector)
numRows = 1 + (showDescription ? 1 : 0) + (showSector ? 1 : 0)

// If user wants sector but it's not available, show description as fallback
fallbackToDescription = displayMode == DISPLAY_SECTOR and not hasSectorIndustry

// Create the table only on the last bar
if barstate.islast
    var table watermark = table.new(position.top_center, 1, 3, bgcolor = color.new(color.white, 100), border_width = 0)
    // Row 0: Ticker and timeframe
    table.cell(watermark, 0, 0, tickerLine, text_color = tickerColor, text_size = size.huge, text_halign = text.align_center)
    // Row 1: Description (if enabled) or fallback
    if showDescription or fallbackToDescription
        table.cell(watermark, 0, 1, description, text_color = descriptionColor, text_size = size.large, text_halign = text.align_center)
    else
        table.cell(watermark, 0, 1, "", text_color = descriptionColor, text_size = size.large, text_halign = text.align_center)
    // Row 2: Sector/Industry (if enabled and available)
    if showSector
        table.cell(watermark, 0, 2, sectorIndustry, text_color = descriptionColor, text_size = size.large, text_halign = text.align_center)
    else
        table.cell(watermark, 0, 2, "", text_color = descriptionColor, text_size = size.large, text_halign = text.align_center)

Overview

Display mode chooses description only, sector and industry, or both. Separate colors set the ticker line and the description line. On ratio or spread charts it looks up information for both symbols. Sector and industry data apply to stocks; for ETFs, crypto, forex, and futures it falls back to the description. It does not generate trade signals.

Details

Published by

major on GitHub

Code credit

//, as written in the script's header

Type

Indicator, Pine Script v6

Draws

On the price chart

Markets

Any symbol

Timeframes

Any timeframe

Risk Notes

No repainting, lookahead or stacked entries were found in its code or README. That says nothing about how well it works: test it first.

Settings

The inputs you can change from the gear icon on the chart. Defaults are the author's.

SettingTypeDefaultVariable
Display ModestringDISPLAY_DESCRIPTIONdisplayMode
Ticker & Timeframecolorcolor.new(#808080, 30)tickerColor
Description / Sectorcolorcolor.new(#808080, 50)descriptionColor
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.

Similar Pine Scripts

948 stars

The Adaptive Laguerre Filter calculates a smoothed, dynamically adjusted trendline plotted directly on the main price chart. It dynamically adjusts its damping factor (alpha) based on recent price range variations and median percentile calculations over a spec

Indicator v4 TrendMoving averages
Updated 10 days ago · GPL-3.0 GitHub repository of Adaptive Laguerre Filter
575 stars

The indicator plots ICT displacement candles on the price chart and can switch to retracement scanning. Displacement requires a move larger than a set share of the 14-bar average high-low range, lasting at least a chosen number of bars, with optional close, pr

Indicator v6 SMC / ICT / price actionCandlestick patterns
Updated 1 year ago · MPL-2.0 GitHub repository of ICT Displacement Candles Indicator
330 stars

This script plots three moving averages on the price chart: a fast EMA or SMA with 10 periods, a slow EMA or SMA with 20 periods, and a trend EMA or SMA with 100 periods. It signals trend changes by drawing a white cross when the fast moving average crosses ab

Indicator v4 TrendMoving averages
Updated 2 months ago · None stated GitHub repository of 3 EMA/SMA + Cross [Alorse]
318 stars

This indicator calculates an ATR trailing stop line derived from the highest high minus an average true range multiple over a specified lookback period. It plots the trailing stop line directly on the price chart and colors price bars based on whether the clos

Indicator v5 TrendVolatility & bands
Updated 2 months ago · MIT GitHub repository of ATR Trailing Stoploss

1833 Tracker

by Allen1212
303 stars

1833 Tracker is an overlay indicator that visualizes historical buy and sell transactions executed by ARK Invest ETFs for the stock on the main price chart. It displays trade dates using green buy and red sell labels positioned relative to price bars, indicati

Indicator v4
Updated 2 years ago · None stated GitHub repository of 1833 Tracker

More indicators

Trade Your Charts With a Regulated Broker

Compare brokers that connect to TradingView, with spreads, minimum deposits and leverage side by side.

Compare brokers