Indicator · Pine Script v5 · On the chart

Farid Makram Custom Levels

by g-moe· code credited to goodgains

Farid Makram Custom Levels is a multi-signal indicator for TradingView that combines RSI momentum analysis with MOBO oscillator signals based on the DPO.

View on GitHub

GitHub stars3on the repository
Pine versionv5runs as is
Lines of code299
Settings24inputs
LicenceNone statedfile: MPL-2.0
Last update5 months ago27 May 2026

Add It to Your TradingView Chart

  1. 1Copy the scriptPress Copy script above, or the copy button on the code below.
  2. 2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
  3. 3Paste over the templateCreate a new indicator, select all of the template text and paste. Press Save and give it a name.
  4. 4Add it to the chartPress Add to chart, then adjust the settings with the gear icon next to its name.

Source Code

299 lines of Pine Script v5 from Tradingview/farid-makram-custom-levels.pine, published under MPL-2.0 (file header). Keep the author's credit and licence lines when you reuse it.

farid-makram-custom-levels.pine
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © goodgains


//@version=5
indicator("Farid Makram Custom Levels", overlay = true)

// @keywords: buy-sell-signals, rsi-crossover, mobo-dpo, knn-prediction, machine-learning, dpo-oscillator, signal-filter, alert-conditions, plot-labels, rsi-ma-cross, nearest-neighbors, cci-features, roc-features, volatility-filter, confluence-signals, confirmed-bars, short-long-trades, prediction-engine, feature-normalization, multi-filter

// signal settings
bars_between_same_signals = input.int(10, title = 'Bars Between Same Side Signals', group = 'Settings')



// rsi code
ma(source, length, type) =>
    switch type
        "SMA" => ta.sma(source, length)
        "EMA" => ta.ema(source, length)
        "SMMA (RMA)" => ta.rma(source, length)
        "WMA" => ta.wma(source, length)
        "VWMA" => ta.vwma(source, length)

use_rsi_logic = input.bool(true, title = 'Use RSI Logic', group = 'RSI Settings')
rsi_signal_within = input.int(2, title = 'RSI Signal Within', group = 'RSI Settings')
rsiLengthInput = input.int(14, minval=1, title="RSI Length", group="RSI Settings")
rsiSourceInput = input.source(close, "Source", group="RSI Settings")
maTypeInput = input.string("SMA", title="MA Type", options=["SMA", "EMA", "SMMA (RMA)", "WMA", "VWMA"], group="RSI Settings")
maLengthInput = input.int(14, title="MA Length", group="RSI Settings")

up = ta.rma(math.max(ta.change(rsiSourceInput), 0), rsiLengthInput)
down = ta.rma(-math.min(ta.change(rsiSourceInput), 0), rsiLengthInput)
rsi = down == 0 ? 100 : up == 0 ? 0 : 100 - (100 / (1 + up / down))
rsiMA = ma(rsi, maLengthInput, maTypeInput)

var bool rsi_buy = na
var bool rsi_sell = na
var int barssince_rsi_buy = 0
var int barssince_rsi_sell = 0

rsi_buy_cross   = ta.crossover(rsi, rsiMA)
rsi_sell_cross  = ta.crossunder(rsi, rsiMA)

barssince_rsi_buy := rsi_buy_cross ? 0 : barssince_rsi_buy[1] + 1 
barssince_rsi_sell := rsi_sell_cross ? 0 : barssince_rsi_sell[1] + 1 

rsi_buy := (barssince_rsi_buy <= rsi_signal_within) or not use_rsi_logic
rsi_sell := (barssince_rsi_sell <= rsi_signal_within) or not use_rsi_logic


// mobo code
use_mobo_logic = input.bool(true, title = 'Use MOBO Logic', group = 'Mobo Settings')
mb_signal_within = input.int(1, title = 'MOBO Signal Within', group = 'Mobo Settings')
price           = input.source(hl2, "Price", group = 'Mobo Settings')
colorNormLength = input.int(3, "colorNormLength", group = 'Mobo Settings')
dpoLength       = input.int(13, "dpoLength", group = 'Mobo Settings')
moboDisplace    = input.int(0, "moboDisplace", group = 'Mobo Settings')
moboLength      = input.int(10, "moboLength", group = 'Mobo Settings')

numDevDn  = input.float(-0.8, "numDevDn", group = 'Mobo Settings')
numDevUp  = input.float(0.8, "numDevUp", group = 'Mobo Settings')

xsma = ta.sma(price[dpoLength / 2 + 1], dpoLength) 
DPO = price - xsma

var bool mobo_buy = na
var bool mobo_sell = na
var int barssince_mobo_buy = 0
var int barssince_mobo_sell = 0

mobo_buy_cross   = ta.crossover(DPO, 0)
mobo_sell_cross  = ta.crossunder(DPO, 0)

barssince_mobo_buy := mobo_buy_cross ? 0 : barssince_mobo_buy[1] + 1 
barssince_mobo_sell := mobo_sell_cross ? 0 : barssince_mobo_sell[1] + 1 

mobo_buy := (barssince_mobo_buy <= mb_signal_within) or not use_mobo_logic
mobo_sell := (barssince_mobo_sell <= mb_signal_within) or not use_mobo_logic



// knn
// kNN-based Strategy (FX and Crypto)
// Description: 
// This strategy uses a classic machine learning algorithm - k Nearest Neighbours (kNN) - 
// to let you find a prediction for the next (tomorrow's, next month's, etc.) market move. 
// Being an unsupervised machine learning algorithm, kNN is one of the most simple learning algorithms. 

// To do a prediction of the next market move, the kNN algorithm uses the historic data, 
// collected in 3 arrays - feature1, feature2 and directions, - and finds the k-nearest 
// neighbours of the current indicator(s) values. 

// The two dimensional kNN algorithm just has a look on what has happened in the past when 
// the two indicators had a similar level. It then looks at the k nearest neighbours, 
// sees their state and thus classifies the current point.

// The kNN algorithm offers a framework to test all kinds of indicators easily to see if they 
// have got any *predictive value*. One can easily add cog, wpr and others.
// Note: TradingViews's playback feature helps to see this strategy in action.
// Warning: Signals ARE repainting.

// Style tags: Trend Following, Trend Analysis
// Asset class: Equities, Futures, ETFs, Currencies and Commodities
// Dataset: FX Minutes/Hours+++/Days

//-- Preset Dates

int startdate = timestamp('01 Jan 2000 00:00:00 GMT+10')
int stopdate  = timestamp('31 Dec 2025 23:45:00 GMT+10')

//-- Inputs

StartDate  = input.time  (startdate, 'Start Date', group = 'KNN Settings')
StopDate   = input.time  (stopdate,  'Stop Date', group = 'KNN Settings')
Indicator  = input.string('All',     'Indicator',   ['RSI','ROC','CCI','Volume','All'], group = 'KNN Settings')
ShortWinow = input.int   (14,        'Short Period [1..n]', 1, group = 'KNN Settings')
LongWindow = input.int   (28,        'Long Period [2..n]',  2, group = 'KNN Settings')
BaseK      = input.int   (252,       'Base No. of Neighbours (K) [5..n]', 5, group = 'KNN Settings')
Filter     = input.bool  (false,     'Volatility Filter', group = 'KNN Settings')
Bars       = input.int   (300,       'Bar Threshold [2..5000]', 2, 5000, group = 'KNN Settings')

//-- Constants

var int BUY   = 1
var int SELL  =-1
var int CLEAR = 0

var int k     = math.floor(math.sqrt(BaseK))  // k Value for kNN algo

//-- Variable

// Training data, normalized to the range of [0,...,100]
var array<float> feature1   = array.new_float(0)  // [0,...,100]
var array<float> feature2   = array.new_float(0)  //    ...
var array<int>   directions = array.new_int(0)    // [-1; +1]

// Result data
var array<int>   predictions = array.new_int(0)
var float        prediction  = 0.0
var array<int>   bars        = array.new<int>(1, 0) // array used as a container for inter-bar variables

// Signals
var int          signal      = CLEAR

//-- Functions

minimax(float x, int p, float min, float max) => 
    float hi = ta.highest(x, p), float lo = ta.lowest(x, p)
    (max - min) * (x - lo)/(hi - lo) + min

cAqua(int g) => g>9?#0080FFff:g>8?#0080FFe5:g>7?#0080FFcc:g>6?#0080FFb2:g>5?#0080FF99:g>4?#0080FF7f:g>3?#0080FF66:g>2?#0080FF4c:g>1?#0080FF33:#00C0FF19
cPink(int g) => g>9?#FF0080ff:g>8?#FF0080e5:g>7?#FF0080cc:g>6?#FF0080b2:g>5?#FF008099:g>4?#FF00807f:g>3?#FF008066:g>2?#FF00804c:g>1?#FF008033:#FF008019

inside_window(float start, float stop) =>  
    time >= start and time <= stop ? true : false

//-- Logic

bool window = inside_window(StartDate, StopDate)

// 3 pairs of predictor indicators, long and short each
float rs = ta.rsi(close,   LongWindow),        float rf = ta.rsi(close,   ShortWinow)
float cs = ta.cci(close,   LongWindow),        float cf = ta.cci(close,   ShortWinow)
float os = ta.roc(close,   LongWindow),        float of = ta.roc(close,   ShortWinow)
float vs = minimax(volume, LongWindow, 0, 99), float vf = minimax(volume, ShortWinow, 0, 99)

// TOADD or TOTRYOUT:
//    ta.cmo(close, LongWindow), ta.cmo(close, ShortWinow)
//    ta.mfi(close, LongWindow), ta.mfi(close, ShortWinow)
//    ta.mom(close, LongWindow), ta.mom(close, ShortWinow)

float f1 = switch Indicator
    'RSI'    => rs 
    'CCI'    => cs 
    'ROC'    => os 
    'Volume' => vs 
    => math.avg(rs, cs, os, vs)

float f2 = switch Indicator
    'RSI'    => rf 
    'CCI'    => cf
    'ROC'    => of
    'Volume' => vf 
    => math.avg(rf, cf, of, vf)

// Classification data, what happens on the next bar
int class_label = int(math.sign(close[1] - close[0])) // eq. close[1]<close[0] ? SELL: close[1]>close[0] ? BUY : CLEAR

// Use particular training period
if window
    // Store everything in arrays. Features represent a square 100 x 100 matrix,
    // whose row-colum intersections represent class labels, showing historic directions
    array.push(feature1, f1)
    array.push(feature2, f2)
    array.push(directions, class_label)

// Ucomment the followng statement (if barstate.islast) and tab everything below
// between BOBlock and EOBlock marks to see just the recent several signals gradually 
// showing up, rather than all the preceding signals

//if barstate.islast   

//==BOBlock    

// Core logic of the algorithm
int   size    = array.size(directions)
float maxdist = -999.0
// Loop through the training arrays, getting distances and corresponding directions.
for i=0 to size-1
    // Calculate the euclidean distance of current point to all historic points,
    // here the metric used might as well be a manhattan distance or any other.
    float d = math.sqrt(math.pow(f1 - array.get(feature1, i), 2) + math.pow(f2 - array.get(feature2, i), 2))
    
    if d > maxdist
        maxdist := d
        if array.size(predictions) >= k
            array.shift(predictions)
        array.push(predictions, array.get(directions, i))
        
//==EOBlock    

// Note: in this setup there's no need for distances array (i.e. array.push(distances, d)),
//       but the drawback is that a sudden max value may shadow all the subsequent values.
// One of the ways to bypass this is to:
// 1) store d in distances array,
// 2) calculate newdirs = bubbleSort(distances, directions), and then 
// 3) take a slice with array.slice(newdirs) from the end
        
// Get the overall prediction of k nearest neighbours
prediction := array.sum(predictions)   

bool filter = Filter ? ta.atr(10) > ta.atr(40) : true // filter out by volatility or ex. ta.atr(1) > ta.atr(10)...

// Now that we got a prediction for the next market move, we need to make use of this prediction and 
// trade it. The returns then will show if everything works as predicted.
// Over here is a simple long/short interpretation of the prediction, 
// but of course one could also use the quality of the prediction (+5 or +1) in some sort of way,
// ex. for position sizing.

bool long  = prediction > 0 and filter
bool short = prediction < 0 and filter
bool clear = not(long and short)

if array.get(bars, 0)==Bars    // stop by trade duration
    signal := CLEAR
    array.set(bars, 0, 0)
else
    array.set(bars, 0, array.get(bars, 0) + 1)

signal := long ? BUY : short ? SELL : clear ? CLEAR : nz(signal[1])

int  changed         = ta.change(signal)
bool startLongTrade  = changed and signal==BUY 
bool startShortTrade = changed and signal==SELL 

bool clear_condition = changed and signal==CLEAR //or (changed and signal==SELL) or (changed and signal==BUY)

float maxpos = ta.highest(high, 10)
float minpos = ta.lowest (low,  10)

//-- Visuals
// plotshape(startLongTrade  ? minpos : na, 'Buy',      shape.labelup,   location.belowbar, cAqua(int(prediction*5)),  size=size.small) 
// plotshape(startShortTrade ? maxpos : na, 'Sell',     shape.labeldown, location.abovebar, cPink(int(-prediction*5)), size=size.small)
// plot(clear_condition      ? close  : na, 'ClearPos', color.yellow, 4, plot.style_cross)






// create signals
var bool buy_signal = na
var bool sell_signal = na

var int last_signal = 0

var int bars_since_buy = 999
var int bars_since_sell = 999


buy_signal := barstate.isconfirmed and nz(bars_since_buy[1],999) >= bars_between_same_signals
 and rsi_buy and mobo_buy and startLongTrade 

sell_signal := barstate.isconfirmed and nz(bars_since_sell[1],999) >= bars_between_same_signals
 and rsi_sell and mobo_sell and startShortTrade


last_signal := buy_signal ? 1 : sell_signal ? -1 : last_signal[1] 

bars_since_buy := buy_signal[1] ? 1 : bars_since_buy[1] + 1
bars_since_sell := sell_signal[1] ? 1 : bars_since_sell[1] + 1

plotshape(buy_signal, style=shape.labelup, editable=true,
 text= "BUY", textcolor=color.white, location= location.belowbar, color=color.new(color.green, 30), size=size.tiny)
plotshape(sell_signal, style=shape.labeldown, editable=true,
 text='SELL', textcolor=color.white, location=location.abovebar, color=color.new(color.red, 30), size=size.tiny)

alertcondition(buy_signal, "Buy Signal", "Buy Signal")
alertcondition(sell_signal, "Sell Signal", "Sell Signal")

Overview

Farid Makram Custom Levels generates buy and sell signals when RSI crosses its moving average or when DPO crosses zero, with configurable within-bar filtering and minimum bars between same-side signals. The script can operate on price chart or be toggled with volatility filters and KNN-based prediction features. Main settings allow customization of RSI and MOBO parameters including lengths and source. Signals are plotted as labels on the chart for user alerts.

Details

Published by

g-moe on GitHub

Code credit

goodgains, as written in the script's header

Type

Indicator, Pine Script v5

Draws

On the price chart

Category

Oscillators & momentum, Moving averages, Volatility & bands, AI / machine learning

Markets

Forex, Crypto

Timeframes

Any timeframe

Risk Notes

No repainting, lookahead or stacked entries were found in its code or README. That says nothing about how well it works: test it first.

Settings

The inputs you can change from the gear icon on the chart. Defaults are the author's.

SettingTypeDefaultVariable
Bars Between Same Side Signalsint10bars_between_same_signals
Use RSI Logicbooltrueuse_rsi_logic
RSI Signal Withinint2rsi_signal_within
RSI Lengthint14rsiLengthInput
SourcesourceclosersiSourceInput
MA TypestringSMAmaTypeInput
MA Lengthint14maLengthInput
Use MOBO Logicbooltrueuse_mobo_logic
MOBO Signal Withinint1mb_signal_within
Pricesourcehl2price
colorNormLengthint3colorNormLength
dpoLengthint13dpoLength
Show all 24 settings
SettingTypeDefaultVariable
moboDisplaceint0moboDisplace
moboLengthint10moboLength
numDevDnfloat-0.8numDevDn
numDevUpfloat0.8numDevUp
Start DatetimestartdateStartDate
Stop DatetimestopdateStopDate
IndicatorstringAllIndicator
Short Period [1..n]int14ShortWinow
Long Period [2..n]int28LongWindow
Base No. of Neighbours (K) [5..n]int252BaseK
Volatility FilterboolfalseFilter
Bar Threshold [2..5000]int300Bars
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.

Similar Pine Scripts

2 stars

The ANN MACD Future Forecast is an experimental indicator serving as a true deep learning sample on TradingView. It calculates Bollinger Bands and Squeeze Momentum using custom functions for SMA, EMA, standard deviation, highest and lowest values. The script p

Indicator v4 AI / machine learningVolatility & bands
Updated 7 years ago · MIT GitHub repository of ANN MACD Future Forecast (SPY 1D)

This overlay indicator plots a Supertrend from an ATR length and factor, four exponential moving averages, and a selectable VWAP or TWAP taken from a chosen timeframe on the price chart. It also shows MACD and stochastic with adjustable lengths, smoothing, ver

Indicator v5 Oscillators & momentumMoving averages
Updated 1 year ago · MPL-2.0 GitHub repository of Momentum Setup: Vijay Thakare Option Buying Scalping Setup

This indicator plots trade entry and exit signals on the price chart by combining Bollinger Bands with optional filter conditions. It calculates standard Bollinger Bands along with RSI, Aroon, and moving average filters. Reversal signals trigger when price bre

Indicator v4 Volatility & bandsOscillators & momentum
Updated 2 months ago · None stated GitHub repository of Bollinger Bands Winner IND [Alorse]

LiqSpikeBounceLong Feb2026

by agutinbaigo28
147 stars

LiqSpikeBounceLong Feb2026 is an indicator designed to identify long bounce entries following high-volume liquidation cascades in bearish market contexts. It checks if price is below an exponential moving average, then detects large single-bar drops exceeding

Indicator v5 Volatility & bandsVolume
Updated 6 weeks ago · MIT GitHub repository of LiqSpikeBounceLong Feb2026

8zz反指標v2

by hansai-art
121 stars

The 8zz Contrarian Indicator plots reverse sentiment trading signals directly on the price chart based on tracked retail post activity. It generates bullish signals during retail panic and bearish signals during market euphoria. The indicator features adjustab

Indicator v6 Moving averagesOscillators & momentum
Updated 3 months ago · None stated GitHub repository of 8zz反指標v2
114 stars

ADX Strategy (original) is an overlay indicator that identifies trend-following trade signals using the Average Directional Index (ADX) combined with a Simple Moving Average filter. A long signal is generated when the ADX rises above a user-defined trend level

Indicator v4 TrendOscillators & momentum
Updated 1 year ago · None stated GitHub repository of ADX Strategy (original)

More indicators

Trade Your Charts With a Regulated Broker

Compare brokers that connect to TradingView, with spreads, minimum deposits and leverage side by side.

Compare brokers