Build your own

How To Build A Signal Generator.

The PSStrategyX tool can be used on almost any indicator, even closed-source ones (though many developers choose to hide their outputs to avoid this type of testing). The key is to create a single plot in the indicator script that plots an integer output of the trading signals.

Here are the steps:

Step One: Great Signals

The first step of building a signal generator is finding great signals. Buy Low, Sell High right?

You will need to find/build a script that has clear buy/sell conditions.

If you're looking to modify an existing script, you might have to do some digging for this.

Step Two: Indicators Only

The second step is making sure those signals are coming from an Indicator.

TradingView has 3 types of scripts. Indicators, Strategies, and Libraries.

  • Every Indicator can become a signal generator.

  • Every Library can be used in an Indicator or Strategy.

  • Strategies cannot generate signals, but can be converted to Indicators.

Whats the difference between an indicator and a strategy?

In the context of TradingView and technical analysis, an indicator and a strategy are two different types of tools that are used to analyze and make decisions about the market. Here are the key differences between indicators and strategies:

  • Purpose: Indicators are tools that are used to analyze the market and identify trends, patterns, and other characteristics of the data. They typically use mathematical formulas and algorithms to process the data and generate signals or outputs that can be used by traders to make decisions. Strategies, on the other hand, are tools that are used to automate the process of trading. They typically use a set of rules and logic to determine when to enter and exit trades, as well as how to manage the trades once they are open.

  • Functionality: Indicators and strategies have different functionalities and capabilities. Indicators are typically designed to provide a specific type of analysis or signal, such as identifying trend direction, overbought/oversold conditions, or support/resistance levels. Strategies, on the other hand, are designed to execute trades and manage positions based on a set of rules and logic. This means that strategies can include indicators as part of their logic, but indicators cannot execute trades or manage positions on their own.

  • Implementation: Indicators and strategies are implemented differently in Pine Script. Indicators are typically implemented as functions that take the data series as input and return the output (e.g. the signal) as output. Strategies, on the other hand, are implemented as collections of rules and logic that are executed by the Pine Script runtime environment. This means that strategies can include multiple indicators and other components, whereas indicators are typically standalone functions.

In summary, indicators and strategies are both tools used for trading and technical analysis, but they serve distinct purposes. Indicators are used to analyze market data and generate signals, while strategies automate the process of trading. Indicators do not inherently provide backtesting results, making them more resource-efficient and faster to run. As a result, it can be beneficial to use indicators whenever possible.

How Do I Know If My Script Is An Indicator?

You can check if your script is an indicator by looking at the pine editor. The indicator("")declaration will always be somewhere near the top.

Step Three: Plotting Your Signal

Finally, you will need to make sure that your indicator is plotting your buy/sell/exit conditions correctly so that we can Connect those Trading Signals To the PSStrategyX

All you have to do is plot the buy/sell signals as integers in a single plot. You can use positive integers for buy signals and negative integers for sell signals. For example:

  • Buy signals: 1

  • Sell signals: -1

  • Exit signals: 0 (You can also add a third output for exit signals, but it is not necessary, as exit signals are usually developed in the PSStrategyX.)

It should look something like this when your done.

// *Script Code* //

signal = buyCondition ? 1 : sellCondition ? -1 : exitCondition ? 0 : na
plot(signal, 'My Custom Signal')

The only thing you have to do, is identify the buy/sell/exit conditions of the indicator in question and then plot them in a single plot.

Let's look at some examples in the next section so you can get a better understanding of how this works.

Modified Script Examples

Example 1: Built-in MA Cross

Here is an example using the built-in MA Cross indicator in Pine Script:

//@version=5
indicator(title="MA Cross", overlay=true, timeframe="", timeframe_gaps=true)
shortlen = input.int(9, "Short MA Length", minval=1)
longlen = input.int(21, "Long MA Length", minval=1)
short = ta.sma(close, shortlen)
long = ta.sma(close, longlen)
plot(short, color = #FF6D00)
plot(long, color = #43A047)
plot(ta.cross(short, long) ? short : na, color=#2962FF, style = plot.style_cross, linewidth = 4)

In this code, there is a slow moving average and a fast moving average. A cross is plotted every time the moving averages crosses, either up or down.

plot(ta.cross(short, long) ? short : na, color=#2962FF, style = plot.style_cross, linewidth = 4)

We need to define clear buy/sell signals where this cross occurs, unfortunately we will have to write a little more code to do this, but still much less than converting this to a strategy.

Here is the modified script ready for the PSStrategyX:

//@version=5
indicator(title="MA Cross", overlay=true, timeframe="", timeframe_gaps=true)
shortlen = input.int(9, "Short MA Length", minval=1)
longlen = input.int(21, "Long MA Length", minval=1)
short = ta.sma(close, shortlen)
long = ta.sma(close, longlen)
plot(short, color = #FF6D00)
plot(long, color = #43A047)
plot(ta.cross(short, long) ? short : na, color=#2962FF, style = plot.style_cross, linewidth = 4)

buy = ta.crossover(short, long)
sell = ta.crossunder(short, long)
signal = buy ? 1 : sell ? -1 : na

plot(signal, 'Signal', display = display.data_window)

We defined the buy and sell signals using the crossover and crossunder functions, respectively.

We then combined the buy and sell signals into one signal using a ternary operator (? :) to return a 1 when the buy signal is triggered, a -1 when the sell signal is triggered and a na (not available) otherwise.

Finally, we plot the signal on the chart using the plot function.

We also specify a title for the signal (So it's easy to find later), and a display parameter (So it doesn't show up in our chart and make a mess).

Example 2: Built-in Bollinger Bands

This example uses the built-in Bollinger Bands indicator in Pine Script:

//@version=5
indicator(shorttitle="BB", title="Bollinger Bands", overlay=true, timeframe="", timeframe_gaps=true)
length = input.int(20, minval=1)
src = input(close, title="Source")
mult = input.float(2.0, minval=0.001, maxval=50, title="StdDev")
basis = ta.sma(src, length)
dev = mult * ta.stdev(src, length)
upper = basis + dev
lower = basis - dev
offset = input.int(0, "Offset", minval = -500, maxval = 500)
plot(basis, "Basis", color=#FF6D00, offset = offset)
p1 = plot(upper, "Upper", color=#2962FF, offset = offset)
p2 = plot(lower, "Lower", color=#2962FF, offset = offset)
fill(p1, p2, title = "Background", color=color.rgb(33, 150, 243, 95))

To turn this indicator into a strategy, we need to define clear buy and sell signals based on the Bollinger Bands.

Here is the modified script, ready for the PSStrategyX:

//@version=5
indicator(shorttitle="BB", title="Bollinger Bands", overlay=true, timeframe="", timeframe_gaps=true)
length = input.int(20, minval=1)
src = input(close, title="Source")
mult = input.float(2.0, minval=0.001, maxval=50, title="StdDev")
basis = ta.sma(src, length)
dev = mult * ta.stdev(src, length)
upper = basis + dev
lower = basis - dev
offset = input.int(0, "Offset", minval = -500, maxval = 500)
plot(basis, "Basis", color=#FF6D00, offset = offset)
p1 = plot(upper, "Upper", color=#2962FF, offset = offset)
p2 = plot(lower, "Lower", color=#2962FF, offset = offset)
fill(p1, p2, title = "Background", color=color.rgb(33, 150, 243, 95))

// Signal for optimizer
buy = close < lower
sell = close > upper
signal = buy ? 1 : sell ? -1 : na

plot(signal, 'Signal', display = display.data_window)

We defined the buy and sell signals using the < and > operators, respectively. We check if the close price is below the lower Bollinger Band (buy signal) or above the upper Bollinger Band (sell signal).

We then combined the buy and sell signals into one signal using a ternary operator (? :) to return a 1 when the buy signal is triggered, a -1 when the sell signal is triggered and a na (not available) otherwise.

Finally, we plot the signal on the chart using the plot function.

We also specify a title for the signal (So it's easy to find later), and a display parameter (So it doesn't show up in our chart and make a mess).

Example 3: Built-In Williams %R

In the next example, we will use the PSStrategyX with a built-in oscillator called the "Williams %R." It is a Wave Oscillator, similar to the RSI.

//@version=5
indicator("Williams Percent Range", shorttitle="Williams %R", format=format.price, precision=2, timeframe="", timeframe_gaps=true)
length = input(title="Length", defval=14)
src = input(close, "Source")
_pr(length) =>
	max = ta.highest(length)
	min = ta.lowest(length)
	100 * (src - max) / (max - min)
percentR = _pr(length)
obPlot = hline(-20, title="Upper Band", color=#0d0e13)
hline(-50, title="Middle Level", linestyle=hline.style_dotted, color=#787B86)
osPlot = hline(-80, title="Lower Band", color=#787B86)
fill(obPlot, osPlot, title="Background", color=color.rgb(126, 87, 194, 90))
plot(percentR, title="%R", color=#7E57C2)

To use this script with the PSStrategyX, we will need to add buy and sell signals and plot them as a single signal.

//@version=5
indicator("Williams Percent Range", shorttitle="Williams %R", format=format.price, precision=2, timeframe="", timeframe_gaps=true)
length = input(title="Length", defval=14)
src = input(close, "Source")
_pr(length) =>
	max = ta.highest(length)
	min = ta.lowest(length)
	100 * (src - max) / (max - min)
percentR = _pr(length)
obPlot = hline(-20, title="Upper Band", color=#0d0e13)
hline(-50, title="Middle Level", linestyle=hline.style_dotted, color=#787B86)
osPlot = hline(-80, title="Lower Band", color=#787B86)
fill(obPlot, osPlot, title="Background", color=color.rgb(126, 87, 194, 90))
plot(percentR, title="%R", color=#7E57C2)

buy = percentR < -80
sell = percentR > -20
signal = buy ? 1 : sell ? -1 : na

plot(signal, 'Signal', display = display.data_window)

We defined the buy and sell signals using the > and < operators, respectively.

We then combined the buy and sell signals into one signal using a ternary operator (? :) to return a 1 when the buy signal is triggered, a -1 when the sell signal is triggered and a na (not available) otherwise.

Finally, we plot the signal on the chart using the plot function.

We also specify a title for the signal (So it's easy to find later), and a display parameter (So it doesn't show up in our chart and make a mess).

Example 4: Custom Squeeze Momentum Indicator [LazyBear]

This is the top indicator on TV as of December 2022, based on likes.

It's possible that its popularity is due to its history as one of the first scripts published on TradingView, rather than its effectiveness as an indicator. However, we won't know for sure until we put it to the test. Regardless of the reason, this indicator is a classic, and currently uses Version 1 of Pine Script, while we are now on Version 5.

Normally, if you wanted to convert this to a strategy the traditional way, you would have to convert it to version 5 first and then add your strategy logic!

Fortunately, the PSStrategyX allows us to use this script without worrying about version compatibility issues, as long as we make some minor adjustments.

Here's the original code

//
// @author LazyBear
//

study(shorttitle = "SQZMOM_LB", title="Squeeze Momentum Indicator [LazyBear]", overlay=false)
length = input(20, title="BB Length")
mult = input(2.0,title="BB MultFactor")
lengthKC=input(20, title="KC Length")
multKC = input(1.5, title="KC MultFactor")

useTrueRange = input(true, title="Use TrueRange (KC)", type=bool)

// Calculate BB
source = close
basis = sma(source, length)
dev = multKC * stdev(source, length)
upperBB = basis + dev
lowerBB = basis - dev

// Calculate KC
ma = sma(source, lengthKC)
range = useTrueRange ? tr : (high - low)
rangema = sma(range, lengthKC)
upperKC = ma + rangema * multKC
lowerKC = ma - rangema * multKC

sqzOn = (lowerBB > lowerKC) and (upperBB < upperKC)
sqzOff = (lowerBB < lowerKC) and (upperBB > upperKC)
noSqz = (sqzOn == false) and (sqzOff == false)

val = linreg(source - avg(avg(highest(high, lengthKC), lowest(low,lengthKC)),sma(close,lengthKC)),lengthKC,0)

bcolor = iff( val > 0,iff( val > nz(val[1]), lime, green),iff( val < nz(val[1]), red, maroon))
scolor = noSqz ? blue : sqzOn ? black : gray
plot(val, color=bcolor, style=histogram, linewidth=4)
plot(0, color=scolor, style=cross, linewidth=2)

To use this script with the PSStrategyX, we will need to add buy and sell signals and plot them as a single signal.

//
// @author LazyBear
//

study(shorttitle = "SQZMOM_LB", title="Squeeze Momentum Indicator [LazyBear]", overlay=false)
length = input(20, title="BB Length")
mult = input(2.0,title="BB MultFactor")
lengthKC=input(20, title="KC Length")
multKC = input(1.5, title="KC MultFactor")

useTrueRange = input(true, title="Use TrueRange (KC)", type=bool)

// Calculate BB
source = close
basis = sma(source, length)
dev = multKC * stdev(source, length)
upperBB = basis + dev
lowerBB = basis - dev

// Calculate KC
ma = sma(source, lengthKC)
range = useTrueRange ? tr : (high - low)
rangema = sma(range, lengthKC)
upperKC = ma + rangema * multKC
lowerKC = ma - rangema * multKC

sqzOn = (lowerBB > lowerKC) and (upperBB < upperKC)
sqzOff = (lowerBB < lowerKC) and (upperBB > upperKC)
noSqz = (sqzOn == false) and (sqzOff == false)

val = linreg(source - avg(avg(highest(high, lengthKC), lowest(low,lengthKC)),sma(close,lengthKC)),lengthKC,0)

bcolor = iff( val > 0,iff( val > nz(val[1]), lime, green),iff( val < nz(val[1]), red, maroon))
scolor = noSqz ? blue : sqzOn ? black : gray
plot(val, color=bcolor, style=histogram, linewidth=4)
plot(0, color=scolor, style=cross, linewidth=2)

// Added for strategy optimizer
buy = sqzOn[1] and sqzOff and val > 0
sell = sqzOn[1] and sqzOff and val < 0
exit = noSqz
signal = buy ? 1 : sell ? -1 : exit ? 0: na

plot(signal, 'Signal')

We defined the buy and sell signals using logic from the indicator visual cues. These can be identified from just looking at the scripts output or learning more from the script author.

A buy happens when the indicator transitions from black to gray crosses and the oscillator is green buy = sqzOn[1] and sqzOff and val > 0 A sell happens similarly, except we wait for the oscillator to be red. sell = sqzOn[1] and sqzOff and val < 0 This way we capitalize on a squeeze in any direction.

We've also include and exit symbol. exit = noSqz

We then combined the buy, sell and exit signals into one signal using a ternary operator (? :) to return a 1 when the buy signal is triggered, a -1 when the sell signal is triggered, a 0 when the exit signal is triggered and a na (not available) otherwise.

Finally, we plot the signal on the chart using the plot function.

We also specify a title for the signal (So it's easy to find later); however, we were unable to hide the signal from the chart, because that feature is not available in version 1.

Testing Closed Source Scripts

⚠️ A Warning On Closed Source Scripts For Sale ⚠️

It is important to be aware that not all indicators and strategies available on the market are of high quality. Some vendors may sell indicators that do not perform as advertised or that are not suitable for certain market conditions. In some cases, the only people who benefit from these indicators are the vendors themselves, as they are able to sell them to unsuspecting traders who may not realize that they are not effective.

To avoid falling victim to such scams, it is important to do your due diligence and thoroughly research any indicator or strategy before purchasing it. Look for reviews and testimonials from real users, and consider trying out a free or trial version of the tool to see if it meets your expectations. It is also a good idea to use caution when dealing with vendors who make overly-optimistic or unrealistic claims about their products. Remember that no indicator or strategy is guaranteed to be profitable, and it is always important to manage your risk carefully and use good judgment when making trading decisions.

If you have any doubts about the fecundity of an indicator, ask the vendor to supply you with a buy/sell signal output similar to the one we are using. You can even show them this tool as an example. If they are confident in the results of their indicator, they should be ok with this.

If they object, I highly advise reconsidering your purchase, unless they can provide an alternative to real verified trading results.

Many of the paid for indicators sold by vendors are not worth a penny because...

You cannot backtest them.

I mean, its definitely possible to backtest any indicator, but most vendors don't want you to backtest their indicator so they don't include that as a feature.

This tool can help you backtest some closed source indicators, but due to the hidden code and smart developers, you will often find that vendors hide their plot outputs so they cannot be backtested.

But... if you want to give it a shot here's how I would do it.

Since we don't have access to the codebase, we need to look at the Data Window inside TradingView and determine if there is a signal we can feed into PSStrategyX.

Now that the data window is open, lets add a random closed source indicator and see if we can test it. I found this script called "The Phoenix 1.0 🔥" -- It's closed source and has buy/sell signals so that's good enough for this test.

Here it is on the chart.

You can see after hovering over a green up arrow (what I am assuming is a buy or something indicating upward pressure) a 1 is represented in the data window. When the triangle is not present, a 0 takes its place. We would love to test these. We can see them in the Data window, but they might not still be accessible in the PSStrategyX if the script author doesn't allow it.

In this case, the author did decide to hide the Chars signals. If they were available, we would test the long and short signals separately, but we can't even do that!

Last updated