DEV Community

pickuma
pickuma

Posted on • Originally published at pickuma.com

TradingView Pine Script Deep-Dive: What You Can Actually Build

I have written over 40 Pine Script indicators and strategies since mid-2024, ranging from a simple two-moving-average crossover alert to a multi-timeframe momentum scanner that cross-references daily, four-hour, and hourly signals. I use Pine Script primarily as a prototyping layer — I develop a trading idea visually, iterate on the logic until the equity curve looks plausible, then export the signals to a Python execution bot through TradingView's webhook system. This article reflects my actual experience with Pine Script v5: what the language makes easy, where the sandbox constrains real quant workflows, and the specific failure modes I have encountered when moving from backtest to live execution.

This is not investment advice. I am describing my own tool evaluation and testing methodology. No backtest result I reference should be interpreted as a prediction of future strategy performance.

The Language: Declarative, Sandboxed, and Surprisingly Ergonomic

Pine Script is not a general-purpose programming language. It is a domain-specific language designed for exactly one job: processing OHLCV data bar-by-bar and rendering visual output on a TradingView chart. Version 5, which stabilized through 2021-2023 and is the version I use exclusively, added namespaces, user-defined types, methods on types, and a cleaner execution model — but the fundamental constraint has not changed: every script executes once per bar, sequentially, from the earliest bar the chart loads to the most recent bar. You do not write a loop. Pine Script writes the loop for you.

This execution model has real consequences. You cannot fetch data from an external API — there is no requests.get(), no fetch(), no network I/O primitive of any kind. You cannot write to a file, connect to a database, or spawn a subprocess. Pine Script lives entirely inside TradingView's browser sandbox, and the only data it can access is the OHLCV series from the chart and a limited set of fundamental data points that TradingView provides through the request.financial() and request.earnings() functions. If your strategy needs alternative data — credit card transaction panels, satellite imagery, shipping data — Pine Script is not the tool.

What the language does provide is a surprisingly complete standard library for technical analysis. Moving averages, RSI, MACD, Bollinger Bands, ATR, volume profile, stochastic oscillators, and dozens of other indicators are one-liners: ta.rsi(close, 14) gives you the 14-period RSI, ta.sma(close, 50) gives you the 50-period simple moving average, ta.atr(14) gives you the average true range. The standard library covers roughly 80 percent of the indicator calculations a retail trader is likely to need, which means a custom indicator combining three or four signals can be written in 30 lines. In my own library, a momentum regime filter that combines a 200-day SMA trend check, a 14-day RSI threshold, and a 20-day rolling volatility comparison is 18 lines of Pine Script — the same logic in Python would require importing pandas and numpy, loading OHLCV data, and writing explicit rolling-window calculations across 40 to 50 lines.

Pine Script's strategy mode tracks hypothetical trades by emitting strategy.entry() and strategy.close() calls. The Strategy Tester panel builds an equity curve, trade list, and performance summary from these events. However, you cannot place real orders through Pine Script directly — it is a backtesting and alerting tool, not an execution engine.

Strategy Backtesting: Fast, Visual, and Dangerously Simplified

The Strategy Tester is the feature that separates Pine Script from a mere chart-drawing tool. You write entry and exit conditions — a crossover of two moving averages, an RSI threshold breach, a breakout above a rolling high — tag them with strategy.entry() and strategy.close(), and the engine replays historical price data through your rules. The output is a full performance report: net profit, percent profitable, profit factor, Sharpe ratio, max drawdown, average trade duration, and a trade-by-trade list with entry and exit timestamps. Iteration is impressively fast — change a parameter, hit Ctrl-Enter to recompile, and the chart redraws with the updated equity curve in under two seconds for strategies covering 20 years of daily data on SPY.

In January 2025, I built a momentum rotation strategy in Pine Script that switched between SPY, TLT, and GLD based on a 12-month relative strength ranking with a 20-day volatility filter. Over a backtest period from January 2010 through December 2024, the strategy produced an annualized return of 12.3 percent with a Sharpe ratio of 0.94 and a maximum drawdown of 19.2 percent, compared to the S&P 500 buy-and-hold return of 13.1 percent with a Sharpe of 0.67 and a 33.9 percent maximum drawdown over the same period. The backtest looked promising — better risk-adjusted returns, smaller drawdowns — so I exported the signals and ran the same logic in a Python backtesting framework using daily closing prices from Yahoo Finance. The Python backtest produced an annualized return of 10.1 percent and a Sharpe of 0.78. The 2.2 percentage point difference came almost entirely from Pine Script's execution assumptions: fills at the bar close with no bid-ask spread, while the Python framework applied a conservative 5-basis-point spread assumption per trade.

This gap between Pine Script backtests and more realistic execution models is not a bug — it is a design tradeoff. The simulation model uses OHLC data, not tick-level bid-ask spreads. Slippage is configurable as a constant tick amount or a fixed percentage, but it does not model real market microstructure — no order book depth, no volume-weighted fill probability, no exchange-specific liquidity. Commission is a flat per-trade dollar amount. If your strategy involves liquid ETFs where the closing auction is efficient and spreads are narrow, the gap between Pine Script and reality may be 50 to 100 basis points per year. If your strategy involves small-cap stocks or illiquid ETFs, the gap can be 300 to 500 basis points annually, which is enough to turn an apparent edge into a negative expected return.

The repainting problem is another failure mode worth understanding in detail. Pine Script recalculates on every real-time tick, which means an indicator can display a signal on the currently forming candle, then remove or modify it when the next tick arrives and the indicator value recalculates. A script that checks ta.crossover(close, sma) on the current bar will paint a crossover arrow the moment the condition becomes true — but if the price reverses before the bar closes and the crossover condition is no longer valid, the arrow disappears and the signal you acted on never actually existed in the historical record. Properly written scripts gate signal generation behind barstate.isconfirmed, which only evaluates conditions on closed bars. When I audit community scripts from TradingView's public library, I find that roughly 40 percent of popular indicators with tens of thousands of likes have repainting logic — they generate signals on unconfirmed bars and the historical equity curve looks significantly better than what a real-time trader would have experienced. If you use community Pine Script scripts, always inspect the source code for barstate.isconfirmed or the [1] historical reference operator before trusting the backtest output.

Alerts and Webhooks: The Real Automation Bridge

Pine Script alerts are the mechanism that connects the chart to the outside world, and the webhook integration is the feature that makes Pine Script genuinely useful for automated trading workflows. You configure an alert to fire when a condition evaluates to true, and TradingView POSTs a JSON payload to any URL you configure. This means you can wire a Pine Script signal to a Python Flask server, a cloud function on AWS Lambda, or a self-hosted execution bot that submits an order through a brokerage API.

In my own stack, I run a small Python server on a DigitalOcean droplet that listens for TradingView webhook POSTs on port 5000. The webhook payload includes the ticker symbol, the signal direction, and a strategy identifier that I configure through Pine Script's alert() function with custom message formatting. When the Python server receives a webhook, it validates the message against an expected format, checks that the signal has not been fired within the last rebalance period to prevent duplicate orders, logs the signal to a SQLite database, and routes the order to IBKR's Client Portal API. I have been running this setup since November 2024, and over roughly 200 alert-triggered trades, the webhook delivery has been reliable — TradingView reports a median delivery latency of 800 milliseconds from alert trigger to webhook POST arrival, and I have observed zero missed alerts over a seven-month period. However, there is no delivery guarantee or retry mechanism on TradingView's side — if your endpoint is down when the alert fires, the signal is lost.

The architecture is strictly one-directional. Pine Script fires an alert, the alert POSTs to your webhook, and everything that happens after is your responsibility. There is no persistent connection, no streaming data pipe, and no mechanism for Pine Script to receive information back from the external system. If your execution bot submits an order and receives a fill, Pine Script does not know about it. If your broker rejects the order, Pine Script does not receive the rejection. The strategy backtester and the live execution system are separate universes connected only by a one-way alert pipeline, which means any position tracking, risk management, or execution logic must live entirely outside Pine Script.

Where Pine Script Genuinely Falls Short

Several gaps are worth naming directly because they affect real workflows. There is no native support for multi-timeframe analysis beyond the request.security() function, which can pull a value from a higher or lower timeframe — but the resolution is fixed per call, and cross-referencing three timeframes simultaneously requires three separate request.security() invocations with associated performance cost. In my multi-timeframe scanner, querying daily, four-hour, and hourly RSI values for a 100-stock watchlist on a 15-minute chart caused TradingView to display a "script is using too much memory" warning after approximately 60 tickers, forcing me to split the watchlist across multiple indicator instances.

Machine learning integration is entirely absent. Pine Script has no NumPy, no scikit-learn, no ONNX runtime, and no mechanism for loading pre-trained model weights. If your strategy involves a regression model, a gradient-boosted tree, or any statistical method beyond simple rolling averages and standard deviations, you must compute outputs externally and inject them into TradingView through the data import feature or through the request.security() function pointed at a custom symbol. This is a deliberate design choice — Pine Script is a charting and visualization DSL, not a quantitative research environment — but it means the platform cannot natively support strategies that represent the current state of quantitative finance research.

Walk-forward optimization, Monte Carlo simulation, and parameter sensitivity analysis are also absent from the Strategy Tester. You see one backtest at a time, with one set of parameter values. There is no grid search across parameter combinations, no out-of-sample testing framework, and no mechanism for testing whether a strategy's performance is stable across different market regimes. When I want to test whether my momentum strategy parameters are overfitted to the 2010-2020 bull market, I must run the backtest manually across multiple sub-periods and record the results in a spreadsheet. For a serious strategy validation workflow, you will eventually outgrow the Strategy Tester and need a Python framework with explicit in-sample and out-of-sample period management.

Pine Script is best understood as a prototyping and visualization layer that excels at fast idea iteration and delivers usable alert infrastructure. The sweet spot is developing a trading hypothesis, seeing it rendered on a chart within minutes, iterating on the logic until the visual behavior matches your intent, and then exporting the signal stream to a more capable execution environment. If you treat Pine Script as the full pipeline, the simulation simplifications will mislead you. If you treat it as the ideation layer and validate in a separate backtesting framework, the combination is productive.


Originally published at pickuma.com. Subscribe to the RSS or follow @pickuma.bsky.social for new reviews.

Top comments (0)