Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Implementing Entry and Exit Rules in Pine Script

Welcome to the next lesson in our journey through systematic trading workflows. Last time, we used Python to develop an objective method for classifying a market's state as either "trending" or "range-bound" using the Average Directional Index (ADX). This classification is a crucial analytical step.

Today, we translate analysis into action. We'll move from the Python environment to TradingView's Pine Script, a language specifically designed for creating and testing trading signals directly on charts. Your objective is to take a trading idea and implement it as a complete signal, defining both the conditions for entering a trade and the rules for exiting it. This is the practical step that turns a market observation into a testable hypothesis.

We will build a simple strategy from the ground up, covering the essential functions for placing entry orders and, critically, managing exits through fixed price targets, conditional logic, and dynamic trailing stops. Given your extensive development experience, we will focus on the specific syntax and execution model of Pine Script, treating it as a new API to master.

1. From Analysis to Strategy: Introduction to Pine Script

So far, our work in Python has been purely analytical. To backtest a trading idea and see how it would have performed historically, we need a "strategy" that can simulate placing orders. Pine Script, the language of the TradingView platform, is built for exactly this.

In Pine Script, there's a fundamental distinction between an indicator and a strategy.

  • An indicator performs calculations and displays information on a chart (e.g., a moving average, the ADX value).
  • A strategy does everything an indicator can do, but it can also simulate trades, manage a virtual portfolio, and generate a performance report.

Converting an indicator into a strategy is straightforward. You simply change the script's declaration from indicator() to strategy(). This unlocks a powerful set of strategy.* functions for order management.

Strategies

This document provides a concise overview of how to get started with strategies. Focus on the first section, which demonstrates the basic conversion of an RSI indicator into a strategy.

Please read the section titled "How can I turn my indicator into a strategy?". Pay attention to how the indicator() function is replaced by strategy() and how the strategy.entry() function is used to trigger trades based on the indicator's logic.

2. Defining the Entry Rule

Let's build a simple trend-following strategy based on a moving average (MA) crossover, a concept we touched on in the previous lesson. The rules are:

  • Entry Condition (Long): Enter a long position when a short-term MA crosses above a long-term MA.
  • Entry Condition (Short): Enter a short position when a short-term MA crosses below a long-term MA.

To implement this, we use the strategy.entry() function. This is the primary command for opening a new position or reversing an existing one.

Concepts / Strategies

This is the official documentation for Pine Script strategies. It provides the most precise definitions and examples.

Read the section "A simple strategy example". This code block is a perfect, self-contained implementation of our moving average crossover strategy. Note the key components: the strategy() declaration, the calculation of the two MAs, the if conditions with ta.crossover and ta.crossunder, and the strategy.entry() calls.

The core logic resides in these lines:

if ta.crossover(fastMA, slowMA)
    strategy.entry("buy", strategy.long)

if ta.crossunder(fastMA, slowMA)
    strategy.entry("sell", strategy.short)

Here's a breakdown:

  • strategy.entry("buy", strategy.long): This command places an order.
    • "buy" is the order ID. It's a unique name you give to this specific entry rule.
    • strategy.long is the direction. It tells the strategy to open a long position. If a short position is already open, strategy.entry will close it and then open the new long position.

The Execution Model

It is crucial to understand that Pine Script strategies, by default, execute on the close of each historical bar. Any orders generated are then filled at the open of the next bar. This behavior ensures that your strategy doesn't act on information from the future (i.e., the close of a bar before it has actually closed), preventing a common form of look-ahead bias.

3. Defining Exit Rules

Entering a trade is only half the story. A complete signal requires explicit rules for when to exit. We'll explore three fundamental types of exits.

A. Fixed Exits: Stop-Loss and Take-Profit

The most common exit strategy is a "bracket order," where you pre-define a price for taking profit and a price for cutting your loss. The strategy.exit() function is designed for this.

This function can be confusing at first, as it has parameters for both absolute price levels (limit, stop) and relative tick distances (profit, loss).

  • For taking profit:
    • limit = <price>: Exits at a specific price level.
    • profit = <ticks>: Exits after price has moved a certain number of ticks in your favor.
  • For stopping loss:
    • stop = <price>: Exits at a specific price level.
    • loss = <ticks>: Exits after price has moved a certain number of ticks against you.

To implement price-based exits, we need to calculate the target prices when we enter the trade and store them. Because a script re-executes on every bar, we must use the var keyword to create persistent variables that hold their value between bars.

The following video provides an excellent, clear walkthrough of implementing a complete strategy with fixed price exits.

Complete Guide to STRATEGY ORDERS in Pine Script

This video from The Art of Trading channel explains how to implement price-based stop-losses and take-profits.

Watch the section from the beginning to the end of the first example. Focus on these key concepts: Using var to declare persistent variables for tradeTakeProfitPrice and tradeStopLossPrice. Calculating the stop and profit levels at the moment the entry condition is met. Calling strategy.exit() on every bar while a trade is active, passing in the stored price levels to the limit and stop parameters.

Here's the core logic for exiting a long position, adapted from the video:

// When our entry condition is met...
if (longCondition)
    // ...calculate and store the exit prices
    tradeStopLossPrice := close - (atr * stopLossMultiplier)
    tradeTakeProfitPrice := close + (atr * riskRewardRatio * stopLossMultiplier)
    strategy.entry("Long", strategy.long)

// On every bar, if a long position is open...
if (strategy.position_size > 0)
    // ...maintain the exit orders
    strategy.exit("Exit Long", from_entry="Long", stop=tradeStopLossPrice, limit=tradeTakeProfitPrice)

The from_entry="Long" argument is crucial; it links the exit order to the entry order that had the ID "Long".

B. Conditional Exits

Sometimes, you want to exit not at a fixed price, but when the market tells you the reason for your trade is no longer valid. For our MA crossover system, a logical exit condition would be when the moving averages cross back in the opposite direction.

For this, we use the strategy.close() or strategy.close_all() functions. These issue a market order to close the position on the next bar's open.

Basic strategy order functions guide for Pine Script

This video, also from The Art of Trading, demonstrates how to use conditional logic to exit a trade.

Watch the segment from the start of the conditional exit example. The presenter modifies a strategy to exit based on a price pattern (a bar closing higher/lower than the previous bar) instead of a fixed target, using strategy.close().

Applying this concept to our MA crossover strategy, we can define exit conditions:

// Define exit conditions
longExitCondition = ta.crossunder(fastMA, slowMA)
shortExitCondition = ta.crossover(fastMA, slowMA)

// If in a long position and the exit condition is met...
if strategy.position_size > 0 and longExitCondition
    strategy.close("Long", comment="Close Long")

// If in a short position and the exit condition is met...
if strategy.position_size < 0 and shortExitCondition
    strategy.close("Short", comment="Close Short")

strategy.close("Long", ...) will close the position that was opened with the entry ID "Long". strategy.close_all() is a simpler alternative if you only ever have one position open at a time.

C. Dynamic Exits: Trailing Stops

A trailing stop is a powerful tool that attempts to "let profits run" while still protecting against reversals. It's a stop-loss order that automatically moves up as a long trade moves in your favor, or down as a short trade moves in your favor.

Pine Script's strategy.exit() function has built-in parameters for a simple trailing stop.

Concepts / Strategies

This section of the documentation explains the built-in parameters for creating a trailing stop.

Read the section "Trailing stops". Focus on the three key parameters: trail_price (the price that must be hit to activate the trail), trail_points (an alternative way to specify activation, as a distance), and trail_offset (the distance the stop will trail behind the price).

Here is how you would add a trailing stop-loss to an entry. This example activates the trail after the price has moved 100 ticks in your favor (trail_points) and then trails the price by 200 ticks (trail_offset).

// When our entry condition is met...
if (longCondition)
    strategy.entry("Long", strategy.long)
    // Place an exit order that includes a trailing stop
    strategy.exit("Exit Long", from_entry="Long", trail_points=100, trail_offset=200)

This is a much simpler way to implement a trend-following exit compared to coding the logic manually, and it's a great feature to be aware of.

4. A Complete Strategy Script

Let's combine these concepts into a single, complete strategy script. This script will enter on a moving average crossover and exit with a fixed stop-loss and take-profit based on the Average True Range (ATR), a measure of volatility.

//@version=5
strategy("MA Crossover Strategy", overlay=true)

// === INPUTS ===
// Allow the user to change the parameters from the settings menu
int fastLength = input.int(10, title="Fast MA Length")
int slowLength = input.int(50, title="Slow MA Length")
float atrMultiplier = input.float(1.5, title="ATR Stop Multiplier")
float riskRewardRatio = input.float(2.0, title="Risk/Reward Ratio")

// === INDICATORS ===
float fastMA = ta.sma(close, fastLength)
float slowMA = ta.sma(close, slowLength)
float atr = ta.atr(14)

// === STATE MANAGEMENT ===
// Persistent variables to store our exit prices for an open trade
var float tradeStopPrice = na
var float tradeProfitPrice = na

// === STRATEGY LOGIC ===
// Entry conditions
bool longCondition = ta.crossover(fastMA, slowMA)
bool shortCondition = ta.crossunder(fastMA, slowMA)

// --- ENTRY ---
if (longCondition)
    // Calculate and store exit prices at the moment of entry
    tradeStopPrice := close - (atr * atrMultiplier)
    tradeProfitPrice := close + (atr * atrMultiplier * riskRewardRatio)
    // Submit the entry order
    strategy.entry("Long", strategy.long)

if (shortCondition)
    // Calculate and store exit prices
    tradeStopPrice := close + (atr * atrMultiplier)
    tradeProfitPrice := close - (atr * atrMultiplier * riskRewardRatio)
    // Submit the entry order
    strategy.entry("Short", strategy.short)

// --- EXIT ---
// On every bar, if we are in a position, maintain the exit orders
if (strategy.position_size > 0) // We are in a long position
    strategy.exit("Exit", from_entry="Long", stop=tradeStopPrice, limit=tradeProfitPrice)

if (strategy.position_size < 0) // We are in a short position
    strategy.exit("Exit", from_entry="Short", stop=tradeStopPrice, limit=tradeProfitPrice)

// === PLOTTING ===
// Draw the MAs on the chart
plot(fastMA, color=color.aqua, title="Fast MA")
plot(slowMA, color=color.orange, title="Slow MA")

// Plot the stop/profit levels only when in a trade
plot(strategy.position_size != 0 ? tradeStopPrice : na, "Stop Loss", color.red, style=plot.style_linebr)
plot(strategy.position_size != 0 ? tradeProfitPrice : na, "Take Profit", color.green, style=plot.style_linebr)

To use this script:

  1. Open a chart on TradingView.
  2. Click the "Pine Editor" tab at the bottom.
  3. Paste the code into the editor.
  4. Click "Add to Chart".

The strategy's trades will appear on your chart. You can see the performance summary by clicking the "Strategy Tester" tab.

Conclusion

In this lesson, you've learned how to translate a trading idea into a fully specified, testable strategy using Pine Script. This is a fundamental skill for any systematic trader, bridging the gap between abstract analysis and concrete implementation.

Key takeaways:

  • Strategy vs. Indicator: A strategy in Pine Script can simulate trades, whereas an indicator only displays data.
  • Entry and Exit Commands: strategy.entry() is the primary function for initiating trades. strategy.exit() is used for price-based exits (stops and limits), while strategy.close() is used for conditional, market-order exits.
  • Persistent State: The var keyword is essential for storing information, like calculated stop-loss prices, that needs to persist across multiple bars.
  • Exit Rule Variety: You can define exits using fixed prices (stop/limit), conditional logic (strategy.close), or dynamic trailing stops (trail_offset).

You now have a template for coding your own signals. However, building a strategy is just the first step. The results from the Strategy Tester can be misleading if not interpreted correctly.

In our next lesson, we will focus on how to evaluate a signal using chronological training and test periods while checking for look-ahead bias, transaction costs, and parameter overfitting. This will equip you with the critical skills needed to rigorously and honestly assess whether a trading idea has a genuine edge.

Can't find a good explanation? Sign up and we'll make it for you

Sign up