Skip to main content
On this page

Pine Script for Beginners: Build Your First TradingView Indicator

· 9 min read
Pineify Team
Pine Script and AI trading workflow research team

Pine Script is TradingView's language for indicators, strategy simulations, and alert conditions. A useful first project is a moving-average crossover: calculate two series, draw them, define an event, and inspect when it becomes known. An indicator signal and a simulated order are different outputs, so this guide provides a separate example for each.

Learn Pine Script TradingView: A Beginner's Guide to Custom Trading Tools

Getting to Know Pine Script​

The examples use Pine Script v6. TradingView's v6 migration guide describes changes including dynamic requests and lazy evaluation of logical expressions. You do not need to memorize the history of every language version to write a first indicator.

Pine executes across the chart's dataset and updates on realtime bars according to the script type and settings. A historical bar's close is known; the current realtime bar's close can still change. That distinction affects signals, alerts, and comparisons with backtests.

How to Get Started​

Open a TradingView chart, open Pine Editor, and create a new indicator. Paste the complete indicator example below, save it, and choose Add to chart. Review compiler messages before interpreting its plots. Editor placement can change with TradingView's interface; use its Pine Editor control rather than assuming it is always a bottom tab.

What You Can Build​

Indicators display calculations and events. Strategies submit hypothetical orders to TradingView's broker emulator and produce a Strategy Tester report. Alerts can notify you or send webhook messages to a separately configured service. A Pine strategy does not directly submit its simulated orders to a connected live brokerage account.

Pineify Website

Pineify can help configure or draft Pine Script, but generated output still needs rule review and testing. For learning, compare the generated source with a small specification whose expected behavior you can explain.

Core Syntax and Data Concepts​

  • Series: values calculated over successive bars, such as close and a moving average.
  • Inputs: settings users can change without editing the calculation.
  • History: the bracket operator accesses earlier values; an unavailable earlier value can be na.
  • Events: a crossover occurs when one series changes from at or below another to above it; staying above is a different condition.
  • Logical operators: and and or combine Boolean expressions. They are operators, not function calls.

Plotting calls such as plot() belong in global scope. To display a conditional event, pass its Boolean condition to plotshape() rather than placing a plotting call inside a local if block.

Build a Complete Moving-Average Indicator​

This educational example uses simple moving averages with default lengths 9 and 21. The defaults are not optimized settings. It marks an upward crossing only after the chart bar is confirmed.

//@version=6
indicator("Learning MA Cross", overlay=true)
fastLength = input.int(9, "Fast SMA", minval=1)
slowLength = input.int(21, "Slow SMA", minval=1)
fastMA = ta.sma(close, fastLength)
slowMA = ta.sma(close, slowLength)
crossUp = ta.crossover(fastMA, slowMA)
buyEvent = crossUp and barstate.isconfirmed
plot(fastMA, "Fast SMA", color=color.blue)
plot(slowMA, "Slow SMA", color=color.orange)
plotshape(buyEvent, title="Confirmed cross", style=shape.triangleup, location=location.belowbar, color=color.green)
alertcondition(buyEvent, "Confirmed bullish cross", "Fast SMA crossed above slow SMA on a completed bar")

Read it in order: declare the program, configure lengths, calculate both averages on every bar, calculate the crossing, then draw the lines and event. The chart needs enough history for both averages to be available.

The alert declaration exposes a condition; it does not create a running alert. Open TradingView's alert dialog, select the condition, and choose timing consistent with a completed-bar rule. TradingView's alert documentation explains the server-side snapshot: changing the script or its inputs does not change an existing alert automatically.

Inspect several crossings manually. A marker should appear on the decision bar; it is not a record of a trade fill. Try a different timeframe while keeping the same lengths and observe how the bar duration changes the underlying calculation window.

Turn the Idea into a Strategy​

Create a separate strategy script for the next example. It enters long after an upward crossing and closes after a downward crossing. It has no protective stop or target: this is an order-timing baseline, not a complete trading plan.

//@version=6
strategy("Learning MA Cross Strategy", overlay=true, pyramiding=0, default_qty_type=strategy.percent_of_equity, default_qty_value=10, commission_type=strategy.commission.percent, commission_value=0.1, slippage=1)
fastLength = input.int(9, "Fast SMA", minval=1)
slowLength = input.int(21, "Slow SMA", minval=1)
fastMA = ta.sma(close, fastLength)
slowMA = ta.sma(close, slowLength)
crossUp = ta.crossover(fastMA, slowMA)
crossDown = ta.crossunder(fastMA, slowMA)
if crossUp and strategy.position_size == 0
strategy.entry("Long", strategy.long)
if crossDown and strategy.position_size > 0
strategy.close("Long")
plot(fastMA, "Fast SMA", color=color.blue)
plot(slowMA, "Slow SMA", color=color.orange)

The 10% setting sizes each entry as a percentage of equity. It does not mean the maximum loss is 10% or that a stop exists. The example's 0.1% commission and one-tick slippage are illustrative assumptions; replace them with assumptions appropriate to the intended instrument and execution environment.

With these default calculation settings, historical decisions occur after a bar is processed and market orders normally fill on the next available tick, commonly the next bar's open. Check the trade list against the marker bars. Consult the strategy documentation before changing order timing, intrabar calculation, or fill assumptions.

Add Risk Rules Deliberately​

A stop needs a defined reference and update policy. A price recalculated as a percentage of each current close moves every time it is evaluated; it is not automatically a fixed percentage below the entry. If the intended stop is entry-based, define when the actual entry becomes known and how the strategy handles the period before a protective order is active.

Separate position allocation from loss-at-stop sizing. Record the entry-to-stop distance, quantity, instrument point value, costs, and possible gap execution. No risk-sizing formula guarantees a loss cap during discontinuous prices.

Read a Backtest as a Simulation​

Record the symbol, data feed, timeframe, chart type, date range, script version, inputs, and strategy properties. Inspect trades around gaps and repeated signals before relying on summary statistics. Report trade count and drawdown alongside returns and win rate.

Do not assume Strategy Tester provides a general built-in parameter-grid optimizer. You can compare saved parameter runs, or evaluate a separate optimization workflow with its own limits. Reserve later data before choosing settings; repeatedly selecting the best result on the same history creates selection bias.

Taking the Next Step​

Arrays, Matrices, and Functions​

Arrays store collections of values; they are not the same as a series' historical timeline. A persistent array appended on every bar grows until you remove elements or reach a limit. Decide whether you need all observations or a bounded rolling sample before using it.

Matrices organize data in rows and columns. Custom functions package repeated calculations. Add these structures when they make the rule easier to express, and check the language documentation for their lifecycle and limits.

Multiple Timeframes​

Pine timeframe strings express four hours as 240, not 4H. A higher-timeframe close can remain unconfirmed while lower-timeframe bars complete. A simple request for its current value can therefore behave differently after a reload.

Read the other-timeframe data guide before introducing a filter. Specify whether you need the current developing higher-timeframe value or the previous confirmed one, and when it becomes available. Confirming the chart bar alone does not confirm a different timeframe's bar.

Debugging and Performance​

Use plots, the Data Window, and Pine Logs to inspect intermediate values. Check na values during warm-up and compare the signal predicate with the actual entry predicate. Avoid repeated expensive loops where a built-in calculation expresses the same rule.

Keep a small test case and change one behavior at a time. A successful syntax check does not verify repainting, order semantics, profitability, or every runtime data condition.

Common Pitfalls​

A history reference such as the previous close is useful but does not by itself prevent lookahead bias. Incorrect higher-timeframe requests or backdated pivot interpretation can still introduce unavailable information. Review decision time separately from where a marker is drawn.

Avoid treating a smooth chart or a high win rate as proof of an edge. Compare average realized gains and losses, costs, exposure, and sample size. The same signal can produce different outcomes under different exits.

Save source versions and settings alongside reports. If you change a rule, begin a new experiment record rather than merging incompatible results into one track record.

Frequently Asked Questions​

How does an indicator differ from a strategy? An indicator calculates and displays information. A strategy also creates simulated orders and a performance report. Neither example here directly executes live broker trades.

Do I need a paid plan to write Pine? Check TradingView's current plan table for editor access, script limits, alerts, data history, and publication permissions. Do not assume one old plan name or one limit applies to every feature.

Can alerts automate execution? A separate webhook receiver or bridge may translate messages into broker requests. Its authentication, duplicate handling, sizing, latency, and failure behavior require their own tests; creating an alert is not sufficient.

Does a confirmed bar eliminate repainting? It resolves one source of intrabar change. Other-timeframe data, future-dependent pivots, and script logic require separate review.

What should I practice next? Explain every marker and simulated trade in the two examples, then change one input. Only after the baseline is understood should you add a filter or exit and compare the results under the same costs.