Summary
This incident examines why a seemingly simple request—plotting a weighted average of two ETF percent‑change series in TradingView Supercharts—fails when implemented naively. Although the user expected Pine Script to directly combine percent‑scaled overlays, the underlying data model of TradingView makes this non‑trivial. The postmortem breaks down what went wrong, why it happens, and how senior engineers typically solve it.
Root Cause
The failure stemmed from a misunderstanding of how TradingView handles percent‑scaled charts:
- Percent‑scaled charts do not expose the percent‑change values to Pine Script.
- Pine Script always receives raw price series, not the transformed percent‑scale values shown on the chart.
- Attempting to compute
0.3 * ETF1 + 0.7 * ETF2using raw prices produces a meaningless result. - The user expected Pine Script to operate on the visualized percent‑change curves, but Pine Script only operates on data, not chart transformations.
Why This Happens in Real Systems
This is a classic example of UI transformations not matching backend data:
- Charting platforms often apply visual transformations (percent scale, log scale, normalization) that are not part of the underlying data pipeline.
- Engineers assume the plotted values are the same values available to scripts.
- TradingView’s architecture separates:
- Data series (raw OHLC)
- Visual transforms (percent, log, compare mode)
- Pine Script only sees the former.
Real-World Impact
This mismatch leads to:
- Incorrect portfolio‑weight calculations
- Confusing or misleading overlays
- Scripts that “look right” but compute the wrong thing
- Hours wasted debugging a problem caused by a hidden architectural constraint
Example or Code (if necessary and relevant)
Below is the correct pattern: manually compute percent‑change yourself, then compute the weighted average.
//@version=5
indicator("Weighted ETF Portfolio", overlay=true)
// Input tickers
etf1 = input.symbol("SPY", "ETF 1")
etf2 = input.symbol("QQQ", "ETF 2")
// Weights
w1 = input.float(0.3, "Weight 1")
w2 = input.float(0.7, "Weight 2")
// Request close prices
c1 = request.security(etf1, timeframe.period, close)
c2 = request.security(etf2, timeframe.period, close)
// Compute percent change from 1 year ago
c1_start = c1[252]
c2_start = c2[252]
pct1 = (c1 - c1_start) / c1_start
pct2 = (c2 - c2_start) / c2_start
// Weighted portfolio percent change
portfolio = w1 * pct1 + w2 * pct2
plot(portfolio, color=color.orange, title="Weighted Portfolio")
How Senior Engineers Fix It
Experienced engineers recognize the architectural constraint and work with the data model, not against it:
- They recreate percent‑change manually instead of relying on the chart’s percent‑scale transform.
- They compute the weighted portfolio using normalized series, not raw prices.
- They validate the output by comparing:
- Raw price series
- Normalized series
- Weighted composite
- They avoid assumptions about what the UI exposes to scripts.
Why Juniors Miss It
Juniors often fall into predictable traps:
- They assume what they see is what the script sees.
- They expect Pine Script to access visual transformations.
- They don’t yet recognize the separation between data pipelines and rendering pipelines.
- They try to combine series without normalizing them first.
- They underestimate how often charting tools hide complexity behind simple UI features.
This incident is a perfect reminder that visualization ≠ data, and scripts must explicitly compute what the UI only displays.