Cross-Exchange Symbol Matching

Fact checked by
Mike Christensen, CFOA
March 11, 2026
How to use Pine Script syminfo.isin to match securities across exchanges, compare dual-listed stocks, and build cross-market analysis tools.

Bottom Line

  • syminfo.isin provides a universal identifier to determine if two different ticker symbols refer to the same underlying security
  • Build cross-exchange comparison tools that automatically detect matching securities regardless of exchange naming
  • Use request.security() with syminfo.isin to compare price spreads between the same stock on different exchanges
  • ISIN codes follow the ISO 6166 standard with a 2-letter country code 9-character identifier and 1 check digit

The same stock can trade under completely different ticker symbols on different exchanges. Apple trades as AAPL on NASDAQ and APC on GETTEX. Royal Dutch Shell trades as SHEL on NYSE and RDSB on LSE. If you are building multi-exchange analysis tools or scanning for arbitrage opportunities in Pine Script, you need a way to match these symbols programmatically.

Pine Script's syminfo.isin variable solves this problem1. It returns the International Securities Identification Number for the current symbol, a universal code that stays the same regardless of which exchange lists the security. This guide shows you how to use ISIN codes to build cross-exchange matching tools, price spread analyzers, and dual-listed stock comparisons.

The Problem with Ticker Symbols

Ticker symbols are exchange-specific identifiers. Each exchange assigns its own symbol to a listed security, and there is no requirement for consistency across exchanges. This creates several problems for traders working across multiple markets.

A stock listed on both the NYSE and the Frankfurt Stock Exchange has two completely different tickers. You cannot compare them by name. Even within the same country, a company may have different symbols on different exchanges. The ticker alone tells you nothing about whether two symbols represent the same underlying instrument.

For Pine Script developers, this means you cannot simply compare syminfo.ticker values to determine if two securities are the same. You need a globally unique identifier, and that is where ISIN comes in.

How ISIN Codes Work

The International Securities Identification Number follows the ISO 6166 standard2. Every ISIN is a 12-character alphanumeric code with three components:

  • A 2-letter country code identifying the country of the issuing entity (for example, US for the United States, GB for Great Britain, DE for Germany)2
  • A 9-character alphanumeric identifier assigned by the national numbering agency2
  • A single check digit calculated using the Luhn algorithm2

For example, Apple Inc.'s common stock has the ISIN US0378331005. The US prefix indicates the security was issued in the United States. This ISIN is the same whether you look up NASDAQ:AAPL or GETTEX:APC, because both symbols represent the same underlying shares of Apple Inc.

There is an important distinction to understand. The Apple CDR (Canadian Depositary Receipt) trading on the TSX as AAPL has a different ISIN: CA03785Y1007. Even though the TSX ticker is AAPL, the ISIN differs because the CDR is a different financial instrument from the underlying common stock. The ISIN identifies the instrument, not just the company.

Using syminfo.isin in Pine Script

The syminfo.isin variable is a simple string that returns the ISIN for the symbol currently loaded on the chart1. If no ISIN information is available, it returns an empty string1. This typically happens with crypto pairs, forex pairs, and some derivative contracts that do not have assigned ISINs.

Here is a basic indicator that displays the current symbol's ISIN on the chart:

//@version=6
indicator("ISIN Display", overlay = true)

if barstate.islastconfirmedhistory
    string isinText = syminfo.isin != "" ? syminfo.isin : "No ISIN available"
    label.new(
         bar_index, high,
         str.format("Symbol: {0}\nISIN: {1}", syminfo.tickerid, isinText),
         style = label.style_label_lower,
         color = color.blue,
         textcolor = color.white,
         size = size.normal
     )

Load this indicator on any stock chart to see its ISIN. Try switching between NASDAQ:AAPL and GETTEX:APC to confirm they share the same ISIN.

Building a Symbol Matcher

The real power of syminfo.isin emerges when you combine it with request.security() to compare the current chart symbol against another symbol. You can build an indicator that automatically detects whether two symbols represent the same underlying security.

//@version=6
indicator("Cross-Exchange Symbol Matcher", overlay = true)

string compareSymbol = input.symbol("GETTEX:APC", "Compare Symbol")

string currentISIN = syminfo.isin
string compareISIN = request.security(compareSymbol, timeframe.period, syminfo.isin)

bool isMatch = currentISIN != "" and compareISIN != "" and currentISIN == compareISIN

var table infoTable = table.new(position.top_right, 2, 4, bgcolor = color.new(color.gray, 85))

if barstate.islastconfirmedhistory
    table.cell(infoTable, 0, 0, "Current Symbol", text_color = color.white)
    table.cell(infoTable, 1, 0, syminfo.tickerid, text_color = color.white)
    table.cell(infoTable, 0, 1, "Current ISIN", text_color = color.white)
    table.cell(infoTable, 1, 1, currentISIN != "" ? currentISIN : "N/A", text_color = color.white)
    table.cell(infoTable, 0, 2, "Compare Symbol", text_color = color.white)
    table.cell(infoTable, 1, 2, compareSymbol, text_color = color.white)
    table.cell(infoTable, 0, 3, "Match Status", text_color = color.white)
    table.cell(infoTable, 1, 3, isMatch ? "SAME SECURITY" : "DIFFERENT",
         text_color = isMatch ? color.green : color.red)

This indicator requests the ISIN from the comparison symbol using request.security()1. Since syminfo.isin is a simple string, it works seamlessly as the expression argument. If both ISINs match and neither is empty, the indicator confirms the symbols represent the same security.

Price Spread Analyzer

Once you can confirm two symbols are the same security, you can build a meaningful price spread comparison. Comparing prices between the same stock on different exchanges reveals arbitrage opportunities and helps you understand market microstructure.

//@version=6
indicator("Cross-Exchange Spread", overlay = false)

string compareSymbol = input.symbol("GETTEX:APC", "Compare Symbol")

string currentISIN = syminfo.isin
string compareISIN = request.security(compareSymbol, timeframe.period, syminfo.isin)

bool isMatch = currentISIN != "" and compareISIN != "" and currentISIN == compareISIN

float compareClose = request.security(compareSymbol, timeframe.period, close)

float spread = isMatch ? close - compareClose : na
float spreadPct = isMatch ? (close - compareClose) / close * 100 : na

plot(spreadPct, "Spread %", color.blue, 2)
hline(0, "Zero Line", color.gray, hline.style_dashed)

bgcolor(spreadPct > 0.5 ? color.new(color.green, 90) :
     spreadPct < -0.5 ? color.new(color.red, 90) : na)

This indicator calculates the percentage price difference between the current symbol and the comparison symbol. It only shows the spread when the ISIN match confirms both symbols are the same security. The background highlights when the spread exceeds 0.5% in either direction, which can indicate temporary pricing inefficiencies across exchanges.

Currency Considerations

When comparing prices across exchanges in different countries, the raw price difference includes the effect of currency conversion. AAPL on NASDAQ trades in USD, while APC on GETTEX trades in EUR. A meaningful spread comparison requires normalizing both prices to the same currency.

You can use request.security() with a currency pair to convert prices before calculating the spread:

//@version=6
indicator("Currency-Adjusted Spread", overlay = false)

string compareSymbol = input.symbol("GETTEX:APC", "Compare Symbol")
string fxPair = input.symbol("FX:EURUSD", "FX Rate Symbol")

float compareClose = request.security(compareSymbol, timeframe.period, close)
float fxRate = request.security(fxPair, timeframe.period, close)

float adjustedCompare = compareClose * fxRate
float spreadPct = (close - adjustedCompare) / close * 100

plot(spreadPct, "Adjusted Spread %", color.purple, 2)
hline(0, "Zero Line", color.gray, hline.style_dashed)

This version converts the comparison price from EUR to USD using the EUR/USD exchange rate before calculating the spread. The result is a true measure of pricing difference between the two exchanges, with currency effects removed.

ADR and GDR Analysis

American Depositary Receipts and Global Depositary Receipts are common use cases for cross-exchange analysis. An ADR represents shares of a foreign company traded on a US exchange. The ADR and the underlying foreign shares have different ISINs because they are technically different instruments, but their prices are closely linked.

For depositary receipt analysis, you cannot rely on ISIN matching alone. Instead, you compare the prices directly and use the known conversion ratio between the ADR and the underlying shares.

//@version=6
indicator("ADR Premium/Discount", overlay = false)

string foreignSymbol = input.symbol("LSE:SHEL", "Foreign Shares")
string fxPair = input.symbol("FX:GBPUSD", "FX Rate")
float adrRatio = input.float(1.0, "ADR Ratio (shares per ADR)", minval = 0.01)

float foreignClose = request.security(foreignSymbol, timeframe.period, close)
float fxRate = request.security(fxPair, timeframe.period, close)

float foreignInUSD = foreignClose * fxRate * adrRatio
float premiumPct = (close - foreignInUSD) / foreignInUSD * 100

plot(premiumPct, "ADR Premium %", color.orange, 2)
hline(0, "Parity", color.gray, hline.style_dashed)
hline(1, "+1%", color.new(color.green, 70))
hline(-1, "-1%", color.new(color.red, 70))

This indicator shows whether an ADR is trading at a premium or discount relative to the underlying foreign shares. When the premium is positive, the ADR costs more than the equivalent foreign shares converted to USD. Negative values indicate a discount. Persistent premiums or discounts can signal sentiment differences between US and foreign investors.

Dual-Listed Stock Dashboard

For a comprehensive view of a security across multiple exchanges, you can build a dashboard that pulls data from several listings and compares them in a table.

//@version=6
indicator("Multi-Exchange Dashboard", overlay = true)

string sym1 = input.symbol("NASDAQ:AAPL", "Exchange 1")
string sym2 = input.symbol("GETTEX:APC", "Exchange 2")
string sym3 = input.symbol("LSE:0R2T", "Exchange 3")

float close1 = request.security(sym1, timeframe.period, close)
float close2 = request.security(sym2, timeframe.period, close)
float close3 = request.security(sym3, timeframe.period, close)

float vol1 = request.security(sym1, timeframe.period, volume)
float vol2 = request.security(sym2, timeframe.period, volume)
float vol3 = request.security(sym3, timeframe.period, volume)

string isin1 = request.security(sym1, timeframe.period, syminfo.isin)
string isin2 = request.security(sym2, timeframe.period, syminfo.isin)
string isin3 = request.security(sym3, timeframe.period, syminfo.isin)

var table dash = table.new(position.bottom_right, 4, 5,
     bgcolor = color.new(color.gray, 85), frame_width = 1, frame_color = color.gray)

if barstate.islastconfirmedhistory
    table.cell(dash, 0, 0, "Metric", text_color = color.white)
    table.cell(dash, 1, 0, sym1, text_color = color.white)
    table.cell(dash, 2, 0, sym2, text_color = color.white)
    table.cell(dash, 3, 0, sym3, text_color = color.white)

    table.cell(dash, 0, 1, "Close", text_color = color.white)
    table.cell(dash, 1, 1, str.tostring(close1, format.mintick), text_color = color.white)
    table.cell(dash, 2, 1, str.tostring(close2, format.mintick), text_color = color.white)
    table.cell(dash, 3, 1, str.tostring(close3, format.mintick), text_color = color.white)

    table.cell(dash, 0, 2, "Volume", text_color = color.white)
    table.cell(dash, 1, 2, str.tostring(vol1, "#,###"), text_color = color.white)
    table.cell(dash, 2, 2, str.tostring(vol2, "#,###"), text_color = color.white)
    table.cell(dash, 3, 2, str.tostring(vol3, "#,###"), text_color = color.white)

    table.cell(dash, 0, 3, "ISIN", text_color = color.white)
    table.cell(dash, 1, 3, isin1, text_color = color.white)
    table.cell(dash, 2, 3, isin2, text_color = color.white)
    table.cell(dash, 3, 3, isin3, text_color = color.white)

    bool allMatch = isin1 == isin2 and isin2 == isin3 and isin1 != ""
    table.cell(dash, 0, 4, "Match", text_color = color.white)
    table.cell(dash, 1, 4, allMatch ? "ALL MATCH" : "MISMATCH",
         text_color = allMatch ? color.green : color.red)
    table.merge_cells(dash, 1, 4, 3, 4)

This dashboard displays the close price, volume, and ISIN for up to three exchange listings side by side. The bottom row confirms whether all three symbols share the same ISIN, verifying they represent the same security.

Limitations of ISIN Matching

While ISIN codes are powerful for cross-exchange identification, they have several limitations you should understand before relying on them in production tools.

No ISIN for Crypto and Forex

Cryptocurrencies and forex pairs do not have ISINs. The syminfo.isin variable returns an empty string for these instruments1. If you build tools that depend on ISIN matching, they will not work for crypto or forex markets. You will need alternative matching logic for these asset classes, such as comparing syminfo.basecurrency and syminfo.currency values1.

Futures and Options

Futures contracts and options may or may not have ISINs depending on the exchange and clearing house2. Even when ISINs are available, each contract expiration has its own ISIN. A March ES futures contract has a different ISIN than a June ES futures contract, even though they track the same underlying index.

Depositary Receipts

As mentioned earlier, an ADR or GDR has a different ISIN from the underlying foreign shares because it is a separate financial instrument. An ISIN match tells you two symbols are the exact same instrument, not just related instruments from the same company.

Data Availability

TradingView's ISIN data coverage depends on the data provider for each exchange. Some exchanges or less liquid securities may not have ISIN data available. Always check for empty strings before comparing ISINs in your scripts.

Cross-Exchange Analysis and Automation

Cross-exchange analysis can feed directly into automated trading strategies. If you detect a significant price spread between two listings of the same security, you can generate alerts and route them to your broker through TradersPost4.

For example, a pairs trading strategy that monitors the spread between an ADR and its underlying foreign shares can send a webhook to TradersPost when the spread exceeds a threshold4. TradersPost routes the order to your connected broker account for execution, whether you trade through Alpaca, Interactive Brokers, Tradovate, or another supported platform.

The v6 dynamic request feature makes cross-exchange strategies more practical3. Because request.security() now accepts series string arguments for the symbol parameter, you can build strategies that switch between different exchange listings dynamically based on conditions like volume, spread, or time of day5. This was not possible in v5 where the symbol parameter required a simple string3.

By combining syminfo.isin for security identification, request.security() for cross-exchange data, and TradersPost for automated execution, you can build sophisticated multi-market strategies entirely within the TradingView ecosystem.

References

1 Pine Script v6 Language Reference
2 Pine Script User Manual
3 Pine Script v5 to v6 Migration Guide
4 TradersPost - Automated Trading Platform
5 Pine Script Release Notes

Ready to automate your trading? Try a free 7-day account:
Try it for free ->