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.
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
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
Was this helpful?