Do you remember the very first time you opened a trading chart? 📉 It likely felt like you were staring at a map written in an ancient, forgotten language. There was fear, excitement, and a deep, burning curiosity. Over time, you learned the basics. You learned about Moving Averages, RSI, and Bollinger Bands. However, there comes a specific moment in every trader’s journey where the default tools just don’t feel like enough. You have a brilliant idea, a unique specific way you see the market, but the tools at hand cannot express it.
That feeling of limitation is actually a gift. 🎁 It is the spark that leads you here, to the world of coding. Specifically, it brings you to Pine Script v5. Consequently, learning to code your own indicators is not just about technical analysis; it is about reclaiming control over your financial destiny. It is about translating your intuition into logic. Today, we are going to walk through this fire together. We will build your first custom indicator from scratch. Don’t worry if you have never written a line of code in your life. By the end of this guide, you will be a creator. Let’s dive in! 🌊
Step 1: Confronting the Blank Canvas (The Pine Editor) 🎨
First, you must navigate to the bottom of your TradingView interface. You will see a tab labeled “Pine Editor.” When you click it, a panel slides up. For many, this white space is terrifying. It represents the unknown. However, I want you to look at it differently. This is your studio. This is where magic happens. ✨
To begin, we need a fresh start. On the right side of the editor, click the “Open” button and select “New blank indicator.” Suddenly, text appears. It looks confusing, but it is actually quite friendly. You will see a few lines of code automatically generated for you. Specifically, these lines are the skeleton of your future masterpiece. Take a deep breath. You have officially started.
Step 2: Naming Your Child (The Indicator Function) 👶
Every great creation needs a name. In Pine Script v5, the very first significant line of code tells TradingView what we are building. This is done using the indicator() function. Think of this function as the birth certificate of your script. It defines who your script is and how it behaves.
In your editor, look for the line that says indicator(...). We are going to change it to make it yours. For instance, let’s say we want to build a “Vibrant Simple Moving Average.” You would write:
indicator("My First Vibrant SMA", overlay=true)
Why did we add overlay=true? This is a crucial decision. It tells the chart that we want our lines to be drawn directly on top of the price candles, rather than in a separate pane below (like the RSI). Furthermore, this simple command sets the stage. It creates a connection between your logic and the price action. You have named it; therefore, it exists. ❤️
//+------------------------------------------------------------------+
//| TBS_Turtle_Soup_Indicator.mq4|
//| Copyright 2026, Trading Analysis|
//| https://www.forexwebstore.com|
//+------------------------------------------------------------------+
#property copyright "ForexWebStore"
#property link "https://www.forexwebstore.com"
#property version "1.00"
#property strict
#property indicator_chart_window
// --- Indicator Buffers for Signals ---
#property indicator_buffers 2
#property indicator_color1 clrLime
#property indicator_color2 clrRed
#property indicator_width1 2
#property indicator_width2 2
double BullishTBS[];
double BearishTBS[];
// --- User Inputs ---
input ENUM_TIMEFRAMES InpHTF = PERIOD_H4; // Higher Timeframe for Levels
input color InpColorHigh = clrDodgerBlue; // CRTH Level Color
input color InpColorLow = clrDodgerBlue; // CRTL Level Color
input color InpColorMid = clrGray; // 50% Level Color
input int InpLineWidth = 1; // Level Line Width
input ENUM_LINE_STYLE InpLineStyle = STYLE_SOLID; // Level Line Style
input bool InpShowLabels = true; // Show CRTH/CRTL/50% Labels
input bool InpEnableAlerts = true; // Enable Sound/Popup Alerts
// --- Global Variables ---
datetime LastAlertTime = 0;
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
IndicatorBuffers(2);
// Set up Signal Buffers (Arrows)
SetIndexBuffer(0, BullishTBS);
SetIndexStyle(0, DRAW_ARROW);
SetIndexArrow(0, 233); // Wingdings Up Arrow
SetIndexLabel(0, "Bullish Turtle Soup");
SetIndexBuffer(1, BearishTBS);
SetIndexStyle(1, DRAW_ARROW);
SetIndexArrow(1, 234); // Wingdings Down Arrow
SetIndexLabel(1, "Bearish Turtle Soup");
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
ObjectsDeletAll(0, "TBS_LVL_");
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
if(rates_total < 50) return(0);
int limit = rates_total - prev_calculated;
if(limit <= 0) limit = 1;
for(int i = limit; i >= 0; i--)
{
BullishTBS[i] = 0.0;
BearishTBS[i] = 0.0;
// Get HTF index corresponding to the current bar
int htfIdx = iBarShft(Symbol(), InpHTF, time[i], false);
// We reference the PREVIOUS HTF candle's range (the "setup" candle)
double htfHigh = iHigh(Symbol(), InpHTF, htfIdx + 1);
double htfLow = iLow(Symbol(), InpHTF, htfIdx + 1);
double htfMid = htfLow + (htfHigh - htfLow) / 2.0;
// Drawing Levels (Only update on the most recent bar to avoid lag)
if(i == 0)
{
UpdateVisualLevels(htfHigh, htfLow, htfMid);
}
// --- LOGIC: Turtle Body Soup (TBS) Detection ---
// Bearish TBS: Wick goes above HTF High, then closes below it
if(high[i] > htfHigh && close[i] < htfHigh && high[i] > high[i+1])
{
BearishTBS[i] = high[i] + (5 * Point);
if(i == 0 && Time[0] != LastAlertTime && InpEnableAlerts) {
Alert(Symbol(), " - Beaish TBS Detected (Liquidity Grab)");
LastAlertTime = Time[0];
}
}
// Bullish TBS: Wick goes below HTF Low, then closes above it
if(low[i] < htfLow && close[i] > htfLow && low[i] < low[i+1])
{
BullishTBS[i] = low[i] - (5 * Point);
if(i == 0 && Time[0] != LastAlertTime && InpEnableAlerts) {
Alert(Symbol(), " - Bullish TBS Detected (Liquidity Grab)");
LastAlertTime = Time[0];
}
}
}
return(rates_total);
}
//+------------------------------------------------------------------+
//| Helper: Update Horizontal Levels and Labels |
//+------------------------------------------------------------------+
void UpdateVisualLevels(double hi, double lo, double mid)
{
DrawLevelLine("TBS_LVL_High", hi, InpColorHigh, "CRTH");
DrawLevelLine("TBS_LVL_Low", lo, InpColorLow, "CRTL");
DrawLevelLine("TBS_LVL_Mid", md, InpColorMid, "50%");
}
void DrawLevelLine(string name, double price, color clr, string text)
{
if(ObjectFind(0, name) == -1)
ObjectCreate(0, name, OBJ_HLINE, 0, 0, price);
ObjectSetDouble(0, name, OBJPROP_PRICE, price);
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
ObjectSetInteger(0, name, OBJPROP_WIDTH, InpLineWidth);
ObjectSetInteger(0, name, OBJPROP_STYLE, InpLineStyle);
string lbl = name + "_Label";
if(InpShowLabels)
{
if(ObjectFind(0, lbl) == -1)
ObjectCreate(0, lbl, OBJ_TEXT, 0, TimeCurrent(), price);
ObjectSetString(0, lbl, OBJPROP_TEXT, " " + text);
ObjectSetDouble(0, lbl, OBJPROP_PRICE, price);
ObjectSetInteger(0, lbl, OBJPROP_TIME, TimeCurrent() + (PeriodSeconds()*5));
ObjectSetInteger(0, lbl, OBJPROP_COLOR, clr);
ObjectSetInteger(0, lbl, OBPROP_FONTSIZE, 9);
}
else ObjectDelete(0, lbl);
}
Step 3: Gathering the Ingredients (Defining Variables) 🥘
Cooking a delicious meal requires gathering fresh ingredients before you turn on the stove. Similarly, coding requires defining variables. A variable is simply a container that holds a value. In our case, we want to calculate a Moving Average. But based on what? And for how long?
We need to tell the script which price data to use. Usually, traders use the closing price of a candle. In Pine Script, this is accessed via the built-in variable close. It is always there, waiting for you to use it.
Additionally, we need to decide the length of our moving average. Let’s pick 14 periods. We could just type 14, but let’s be professional. We will create a variable name for it:
length = 14
Now, the computer knows that whenever we say “length,” we actually mean “14.” This makes your code cleaner and easier to read. It is like shorthand for your brain. 🧠
Step 4: The Logic Engine (Calculating Values) ⚙️
Now comes the heartbeat of your script. We need to perform the math. Don’t panic! 🚫 You do not need to be a mathematician to do this. Pine Script has done the heavy lifting for us.
To calculate a Simple Moving Average (SMA), we use a built-in function called ta.sma. The “ta” stands for Technical Analysis. It is a library of knowledge that TradingView gives you for free.
Write this line below your variables:
mySMA = ta.sma(close, length)
Let’s translate this into plain English. We are creating a new container called mySMA. Inside this container, we are storing the result of the Technical Analysis SMA calculation, using the close price and our defined length (14).
It is elegant, isn’t it? Just one line of code captures years of mathematical theory. You are harnessing power here. 💪
Step 5: Adding the Human Element (Inputs) 🎛️
Here is where good scripts become great scripts. Imagine you finish your code, but tomorrow you decide you want a 20-period SMA instead of 14. If you hard-coded the number, you would have to open the editor and change the code every time. That is tedious and annoying.
Instead, let’s make it interactive. We want to be able to change settings from the chart menu, just like the pros do. We can achieve this by changing our length variable to use an input.
Change your previous line to this:
length = input.int(14, title="Length")
Now, length is not just a static number. It is a dynamic field! 🚀 When you add this script to your chart, you will see a settings cog. Inside, there will be a box labeled “Length” where you can type any number you want. You have given yourself the power of choice. Consequently, your tool is now flexible and alive.
Step 6: Visualizing the Soul (The Plot Function) 🖌️
We have the name, the ingredients, and the calculations. However, if we save the script now, nothing will happen visually. The computer knows the answer, but it hasn’t shown it to us yet. We need to command it to draw.
This is done using the plot() function. It is the brush that paints your canvas.
Add this line to your script:
plot(mySMA, title="My SMA", color=color.blue, linewidth=2)
Let’s break down the emotion in this line. We are plotting mySMA. We gave it a title for the data window. Crucially, we chose color.blue. Colors matter! 💙 Blue signifies calm and stability. We also set linewidth=2 to make it bold and confident. You aren’t just drawing a line; you are making a statement.
Step 7: Conditional Logic (Teaching It to Think) 🧠
What if we want the indicator to talk to us? What if we want it to change color when the trend changes? This is where “Conditional Logic” comes in. It sounds complex, but it is just asking a question: “If this happens, do that.”
Let’s make the line Green when price is above the SMA (Bullish) and Red when price is below the SMA (Bearish). 🐂🐻
First, we define the condition:
isBullish = close > mySMA
Next, we update our plot command. Instead of a single color, we use a “ternary operator” (a fancy choice maker):
plot(mySMA, title="My SMA", color = isBullish ? color.green : color.red, linewidth=2)
Read that logic carefully. It asks: “Is it bullish?” If yes (?), use Green. If no (:), use Red.
Suddenly, your static blue line has transformed. It now pulses with the market. It glows green with hope and turns red with caution. You have breathed life into the code. 🌬️
Step 8: Compilation and Troubleshooting (The Struggle) ⚠️
Now, press the “Save” button. Sometimes, you will see red text appear at the bottom. These are error messages.
I want you to pause here. Do not feel discouraged. Every coder, from the Google engineers to the hobbyist, gets errors. An error is not a failure; it is simply the computer asking for clarification.
Perhaps you forgot a comma. Maybe you misspelled color. Read the error message; it usually tells you exactly which line is confused. Fix it, and try again. This process of fixing is called “Debugging,” but I prefer to call it “Refining.” You are polishing the diamond. 💎
Step 9: The Moment of Truth (Adding to Chart) 📈
Your code is error-free. The “Add to Chart” button is glowing (figuratively). Click it.
Watch your screen. Instantly, your custom indicator wraps itself around the candlesticks. It is a moment of pure magic. You did not buy this tool. You did not borrow it. You built it.
Move the chart back in time. Watch how your line changes from green to red perfectly as the trends shift. Verify your logic. Does it look right? Does it feel right? This verification process is crucial because it builds trust between you and your creation. 🤝
Step 10: Iteration (The Never-Ending Journey) 🔄
You might look at your new tool and think, “What if I added a background fill?” or “What if it sent me an alert?”
That is the beauty of Pine Script. You can always go back. For example, to add a background fill, you could use the fill() function. To add alerts, you use alertcondition().
Coding is never truly finished. It evolves as you evolve as a trader. Your script is a reflection of your current understanding of the market. As you grow wiser, your code will grow more complex and nuanced.
Conclusion: You Are Now a Builder 🏗️
Take a step back and look at what you have accomplished today. You pushed past the fear of the unknown syntax. You learned how to speak to the machine. You transformed a fleeting idea in your head into a concrete visual tool on the screen.
This is more than just a technical skill. It is a superpower. 🦸♂️
From this day forward, you are no longer limited by what other people have built. If you can imagine it, you can build it. The market is a chaotic beast, but now you have the ability to forge your own weapons to tame it. Keep experimenting. Keep breaking things and fixing them. Most importantly, keep dreaming.
Welcome to the family of Pine Script developers. Your journey has just begun. 🚀❤️
