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.
1Copy the scriptPress Copy script above, or the copy button on the code below.
2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
3Paste over the templateCreate a new indicator, select all of the template text and paste. Press Save and give it a name.
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.
The inputs you can change from the gear icon on the chart. Defaults are the author's.
Setting
Type
Default
Variable
Display Mode
string
DISPLAY_DESCRIPTION
displayMode
Ticker & Timeframe
color
color.new(#808080, 30)
tickerColor
Description / Sector
color
color.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.
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
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
This indicator classifies each bar as bull, bear, or sideways from a rolling log return compared with adjustable percentage thresholds. It paints a regime ribbon on the price chart, shows a current-regime banner, and labels state changes after a minimum hold.
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
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
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