Skip to content
Module 1 / 2
  1. What Is Edge and Expectancy - The Foundation of a System
  2. TradingView Pine Script from Scratch
  3. Backtesting Without Cheating - Overfitting and Lookahead
  4. Excel as a Trading Laboratory
Algo Systematic Lesson 02

TradingView Pine Script from Scratch

Pine Script is the fastest laboratory for a systematic trader. In 20 lines you can test an idea that would take 2 hours in Excel and would require downloading data in Python. This is not a language for writing HFT bots. It is a language for quick hypothesis validation,...

Introduction0:00 / 7:00
Illustration for lesson: TradingView Pine Script from Scratch
2 / 7
Author
Zordon Intelligence Sp. z o.o.
Updated
7 min read
Structure sources
2

Introduction

Pine Script is the fastest laboratory for a systematic trader. In 20 lines you can test an idea that would take 2 hours in Excel and would require downloading data in Python. This is not a language for writing HFT bots. It is a language for quick hypothesis validation, visualising rules, and building a system prototype before a deeper backtest.

In this lesson you will learn Pine Script v5 from scratch in a systematic way: how to write a strategy instead of an indicator, how to measure R, how to avoid the mistakes that falsify a backtest, and how to build a template you will use in every following lesson.

Pine Script - the fundamentals you should not skip

Pine has 4 modes of operation: indicator (indicator), strategy (strategy), library (library). For a systematic trader the one that matters is strategy. It is the only one with a broker emulator engine, orders, and statistics.

//@version=5
strategy("My First System - Course 21", overlay=true, initial_capital=10000, commission_type=strategy.commission.percent, commission_value=0.05, slippage=1)
//@version=5
strategy("SMA Cross Template", overlay=true, initial_capital=10000, commission_type=strategy.commission.percent, commission_value=0.05)

fastLen = input.int(20, "Fast SMA")
slowLen = input.int(50, "Slow SMA")
riskPct = input.float(1.0, "Risk % per trade", step=0.1)

fast = ta.sma(close, fastLen)
slow = ta.sma(close, slowLen)

longCondition = ta.crossover(fast, slow)
shortCondition = ta.crossunder(fast, slow)

// Risk in R - template
atr = ta.atr(14)
stopDistance = atr * 1.5

if (longCondition)
    strategy.entry("Long", strategy.long)
    strategy.exit("Long Exit", from_entry="Long", stop=close - stopDistance, limit=close + stopDistance*2)

if (shortCondition)
    strategy.entry("Short", strategy.short)
    strategy.exit("Short Exit", from_entry="Short", stop=close + stopDistance, limit=close + stopDistance*2)

Key parameters in strategy():

ParameterWhy it matters
commission_type + commission_valueWithout this the backtest is fiction. Set 0.05% for stocks/ETFs, 0.02-0.04% for futures/crypto
slippage1-2 ticks. Without slippage every system on M1-M5 looks profitable
initial_capitalNeeded for % risk, but do not use it to judge results in PLN (Polish zloty), only in R and %
pyramidingDefault 0. Leave it at 0 until you have control over the position
calc_on_every_tick vs calc_on_order_fillsLeave false at the start, otherwise you introduce lookahead

Entry, stop-loss and exit in R

In Pine most beginners write only strategy.entry. A system without a defined SL and TP is not a system.

The correct template:

// R risk definition
atrLen = input.int(14, "ATR Length")
atrMultSL = input.float(1.5, "ATR SL")
atrMultTP = input.float(3.0, "ATR TP")

atrVal = ta.atr(atrLen)
longSL = close - atrVal*atrMultSL
longTP = close + atrVal*atrMultTP

// Entry condition with context
trendFilter = close > ta.sma(close, 200)
pullback = close < ta.sma(close, 20)

longSignal = trendFilter and pullback and ta.rsi(close,14) < 40

if (longSignal and strategy.position_size == 0)
    strategy.entry("Long", strategy.long)
    // R defined by the SL distance
    strategy.exit("L-Exit", from_entry="Long", stop=longSL, limit=longTP, comment_profit="TP 2R", comment_loss="SL 1R")
R formula in Pine:
R_multiple = (close - entry_price) / (entry_price - stop_price) for a long
Store entry_price = strategy.position_avg_price

Thanks to this, in the Performance Summary you will see not only $ but you can also export R.

How to avoid repaint and lookahead

This is 90% of false backtests in TradingView.

  1. Indicator repaint: the indicator changes a historical value after the fact. For example ZigZag, some Volume Profile.
  2. Strategy repaint: you use request.security with lookahead=barmerge.lookahead_on or without barstate.isconfirmed.
  3. Bar close vs open: By default the strategy calculates on bar close. Entry on close in a backtest is unrealistic on M1 in live trading.

Anti-repaint rules:

MistakeFix
close in the condition and entry on the same barUse barstate.isconfirmed and process_orders_on_close=false or enter on the open of the next bar
request.security(sym, "D", close, lookahead=on)Always lookahead=barmerge.lookahead_off for strategies
ta.highest(high, 10) without an offsetIf the entry is meant to come after a break of high[1], use high[1] not high
The indicator paints the pastTest in Replay Mode bar by bar, watch whether the signal disappears
// Safe higher timeframe without lookahead
htfClose = request.security(syminfo.tickerid, "60", close, gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_off)
htfTrend = htfClose > request.security(syminfo.tickerid, "60", ta.sma(close,50), gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_off)

Measuring the system in Pine - what to export

Pine has strategy.* built in:

Built-in metrics:
strategy.wintrades, strategy.losstrades, strategy.wintrades / strategy.closedtrades
strategy.grossprofit, strategy.grossloss, strategy.netprofit
strategy.max_drawdown, strategy.openprofit

Add your own R logging:

// Logging R after a trade closes
var float totalR = 0.0
if (strategy.closedtrades > strategy.closedtrades[1])
    lastProfit = strategy.closedtrades.profit(strategy.closedtrades - 1)
    lastEntry = strategy.closedtrades.entry_price(strategy.closedtrades - 1)
    // Approximate R if you have a fixed ATR SL; in live trading calculate from the recorded SL
    r = lastProfit / (atrVal*atrMultSL * syminfo.pointvalue) 
    totalR += r

Export: the Performance Summary tab -> Export -> analyse in Excel (Lesson 4) to calculate PF, SQN, EV in R.

When the plan says do not trade

  1. When you use calc_on_every_tick=true and lookahead_on: Your backtest is false. Fix the code, do not trade.
  2. When you test a strategy on < 200 bars on the decision timeframe: Too few to assess the distribution. For H1 that is ~1 month of data - too little.
  3. When commission = 0 and slippage = 0 in the settings: Every strategy looks good. Set real costs before you evaluate.
  4. When a signal appears 5 times on one candle in Replay: You have repaint. Pause until it is fixed.
  5. When you write a strategy without strategy.exit with an SL: That is not a system, just an entry generator. No control over R.

The mistakes that cost the most

  1. Writing an indicator instead of a strategy. An indicator paints arrows, a strategy has a position engine. If you do not use strategy.*, you are not measuring edge.
  2. security without safeguards. One wrong request.security with lookahead can add +30% to the equity curve out of thin air.
  3. Optimising in the Inputs window without OOS. Shifting SMA 20->21->22 so the curve rises is overfitting. Every parameter must have a logical justification outside Pine.
  4. No strategy.position_size == 0 condition. The system opens 10 pyramiding positions and falsifies drawdown and PF.
  5. Trusting the List of Trades instead of exporting to Excel. TradingView calculates differently than a broker. PF in TV is gross, without slippage on stops. Always export + your own audit.

Measurable exercise

Goal: Build an R strategy template and pass the anti-repaint test.

Task 60 min:

  1. Copy the template from the lesson (SMA Cross + ATR SL/TP + SMA200 filter).
  2. In Properties set: commission 0.05%, slippage 2 ticks, initial capital 10000.
  3. On BTCUSD H1 add the strategy. Start TradingView Bar Replay from 2023-01-01. Go through 100 bars manually - check whether the signals move.
  4. Export the Performance Summary to CSV.
  5. Calculate in Excel:
N = number of trades
WinRate = wins / N
AvgR = average R (profit / ATR risk)
PF = sum of profits / |sum of losses|
  1. If PF < 1.2 - do not optimise by force. Write down: “This is not an edge, it is a prototype”. That is the correct result of the exercise.

Check yourself

  1. How does indicator() differ from strategy() and why is only strategy useful for a systematic backtest?
  2. What does lookahead=barmerge.lookahead_on do and why is it forbidden in systems?
  3. Why must you set commission and slippage before you evaluate a system?
  4. Write in 2 lines: how to define an SL as 1.5 ATR in Pine v5 and how to calculate the R-multiple of a position.
  5. You have 3 long signals on the same candle in history - what is the bug in the code and how do you fix it?

Frequently asked questions

Is Pine Script enough to make money? Pine is for prototyping and validating ideas, not for HFT. For swing D1/H1 and simple bots via webhook it is sufficient. For M1 with microstructure you need Python and a dedicated engine.

Can I trade automatically directly from TradingView? TradingView does not send orders to the exchange itself. It sends a webhook alert which you have to capture with a bot, a broker that has a TV integration (e.g. on futures) or a bridge. An arrow alone is not an automation.

How large can the code be in Pine before it slows down? A limit of 64KB of script, effectively 900 lines, max 64 security. For a system with 2-3 filters + ATR + one HTF that is fine. If you write 10 indicators and 5 HTFs - rewrite it in Python.

Can a strategy in Pine use data from other exchanges? Yes, via request.security("BINANCE:BTCUSDT", ...). But then you add data correlation risk and lookahead. Test only on the same ticker you trade.

Where should I keep strategy versions? Use “Save as” with a version number: v01_raw, v02_costs, v03_no_repaint. Export every test to CSV with the date. Without versioning you will not get back to a working version after overfitting.


Advertisement

Key takeaways

  • Pine Script is the fastest laboratory for a systematic trader. In 20 lines you can test an idea that would take 2 hours in Excel and would require downloading data in Python. This is not a language for writing HFT bots. It is a language for quick hypothesis validation,...

Structure sources

The material is based on source documents. Variable values — fees, limits, regulatory figures — are worth checking at the source before making a decision.

  • TradingView — Pine Script reference
  • Python for Finance — Yves Hilpisch

Disclaimer

  • Educational material. Not investment advice or a recommendation. Trading involves the risk of losing capital.
  • The materials are educational. They are not investment advice or a recommendation within the meaning of financial instruments trading regulations. Numbers in examples are illustrative unless a measurement date and source are given.

Who writes this and how we check it

The materials are written and verified by Redakcja Kurs Tradera — the rules are described on about the site: every number has a source, and we show gaps openly. Report a factual error via contact — corrections are described in the content.

Course progress 0 / 7
Next →

Progress is stored only in this browser (localStorage) — there is no account and no copy on the server. Clearing site data will remove it. Manage your data