Covert MT4 SmoothStep

mikeytrader13

New member
Adopted from work done by Kenneth H. Perlin, who is a professor at New York University in the Computer Science Department. His work centered around something called “Sigmoid Function” having a characteristic “S”-shaped curve or sigmoid curve which eventually levels off. The MT4 version was coded in 2022 by a prolific Coder named Mladen Rakic there’s a lot of math going on behind the scenes.

More about this indicator settings and what it does can be found here.
More about the Sigmoid Function here.

IY7tRjq.jpg


Code:
//---------------------------------------------------------------------------------------------------------------------
#property copyright   "© mladen, 2022"
#property link        "[email protected]"
//---------------------------------------------------------------------------------------------------------------------
#property indicator_separate_window
#property indicator_buffers 3
#property indicator_plots   3
#property indicator_label1  "SmoothStep up"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDodgerBlue
#property indicator_width1  2
#property indicator_label2  "SmoothStep down"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrCoral
#property indicator_width2  2
#property indicator_label3  "SmoothStep down"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrCoral
#property indicator_width3  2
#property indicator_level1  0.2
#property indicator_level2  0.8
#property strict

//
//
//

input int             inpPeriod  = 32;        // Period
   enum enPrices
         {
            pr_close,      // Close
            pr_open,       // Open
            pr_high,       // High
            pr_low,        // Low
            pr_median,     // Median
            pr_typical,    // Typical
            pr_weighted,   // Weighted
            pr_lowhigh,    // Low/High/Close
         };
input enPrices         inpPrice   = pr_close; // Price        

//
//
//

double val[],valda[],valdb[],valc[];
struct sGlobalStruct
{
   int period;
};
sGlobalStruct global;

//---------------------------------------------------------------------------------------------------------------------
//                                                                
//---------------------------------------------------------------------------------------------------------------------
//
//
//

int OnInit()
{
   IndicatorBuffers(4);
   SetIndexBuffer(0,val  ,INDICATOR_DATA);
   SetIndexBuffer(1,valda,INDICATOR_DATA);
   SetIndexBuffer(2,valdb,INDICATOR_DATA);
   SetIndexBuffer(3,valc ,INDICATOR_CALCULATIONS);
 
      //
      //
      //

      global.period  = MathMax(inpPeriod,1);
   IndicatorSetString(INDICATOR_SHORTNAME,StringFormat("Smooth step (%i)",global.period));
   return (INIT_SUCCEEDED);
}
void OnDeinit(const int reason) { return;}

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//

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[] )
{
   int limit = (prev_calculated>0) ? rates_total-prev_calculated : rates_total-1;
     
   //
   //
   //

         static double m_lows[];
         static int    m_lowsSize  = -1;
                   if (m_lowsSize <rates_total) m_lowsSize = ArrayResize(m_lows ,rates_total+500,2000);
         static double m_highs[];
         static int    m_highsSize = -1;
                   if (m_highsSize<rates_total) m_highsSize = ArrayResize(m_highs,rates_total+500,2000);
         struct sWorkStruct  
            {
                  int    prevBar;
                  double prevmin;
                  double prevmax;
            };
         static sWorkStruct m_work[];
         static int         m_workSize= -1;
                        if (m_workSize<=rates_total) m_workSize = ArrayResize(m_work,rates_total+500,2000);
     
      //
      //
      //
     
      if (valc[limit]==2) iCleanPoint(limit,rates_total,valda,valdb);
      for(int i=limit, r=rates_total-i-1; i>=0; i--,r++)
         {
            double _price  = close[i];
            double _priceh = close[i];
            double _pricel = close[i];
           
            //
            //
            //
           
               switch (inpPrice)
                  {
                     case pr_close    : _price  = _priceh = _pricel = close[i]; break;
                     case pr_open     : _price  = _priceh = _pricel = open[i];  break;
                     case pr_high     : _price  = _priceh = _pricel = high[i];  break;
                     case pr_low      : _price  = _priceh = _pricel = low[i];   break;
                     case pr_median   : _price  = _priceh = _pricel = (high[i]+low[i])/2.0; break;
                     case pr_typical  : _price  = _priceh = _pricel = (high[i]+low[i]+close[i])/3.0; break;
                     case pr_weighted : _price  = _priceh = _pricel = (high[i]+low[i]+close[i]*2.0)/4.0; ; break;
                     case pr_lowhigh  : _price  = close[i];
                                        _priceh = high[i];
                                        _pricel = low[i];
                                        break;
                  }
                  m_lows[r]  = _pricel;    
                  m_highs[r] = _priceh;    

                  //
                  //
                  //
     
                  if (m_work[r].prevBar!=i)
                     {
                        m_work[r  ].prevBar = i;
                        m_work[r+1].prevBar =-1;
     
                        if (r>0 && global.period>1)
                              {
                                 int _minMaxPeriod = global.period-1;
                                 int _start        = r-global.period+1; if(_start<0) { _start = 0; _minMaxPeriod = r+1; }
                                          m_work[r].prevmin = m_lows [ArrayMinimum(m_lows ,_minMaxPeriod,_start)];
                                          m_work[r].prevmax = m_highs[ArrayMaximum(m_highs,_minMaxPeriod,_start)];
                              }
                        else
                              {
                                          m_work[r].prevmin = m_lows[i];
                                          m_work[r].prevmax = m_highs[i];
                              }
                     }
       
                  double min = (m_work[r].prevmin < _pricel) ? m_work[r].prevmin : _pricel;
                  double max = (m_work[r].prevmax > _priceh) ? m_work[r].prevmax : _priceh;
                  double raw = (max!=min) ? (_price - min)/(max- min) : 0;
            //
            //
            //
           
            val[i]  = raw * raw * (3.0 - 2.0 * raw);
            valc[i] = (r>0) ? (val[i]>val[i+1]) ? 1 : (val[i]<val[i+1]) ? 2 : valc[i+1] : 0;
            if (valc[i]==2) iPlotPoint(i,rates_total,valda,valdb,val); else valda[i] = valdb[i] = EMPTY_VALUE;
         }
 
   //
   //
   //
 
   return (rates_total);
}

//-------------------------------------------------------------------
//                                                                
//-------------------------------------------------------------------
//
//
//

void iCleanPoint(int i,int bars, double& first[],double& second[])
{
   if (i>=bars-3) return;
   if ((second[i]  != EMPTY_VALUE) && (second[i+1] != EMPTY_VALUE))
        second[i+1] = EMPTY_VALUE;
   else
      if ((first[i] != EMPTY_VALUE) && (first[i+1] != EMPTY_VALUE) && (first[i+2] == EMPTY_VALUE))
          first[i+1] = EMPTY_VALUE;
}

void iPlotPoint(int i, int bars, double& first[],double& second[],double& from[])
{
   if (i>=bars-2) return;
   if (first[i+1] == EMPTY_VALUE)
      if (first[i+2] == EMPTY_VALUE)
            { first[i]  = from[i];  first[i+1]  = from[i+1]; second[i] = EMPTY_VALUE; }
      else  { second[i] =  from[i]; second[i+1] = from[i+1]; first[i]  = EMPTY_VALUE; }
   else     { first[i]  = from[i];                           second[i] = EMPTY_VALUE; }
}

Hope this indicator can be converted and be useful in your trading journey! Thank you.

@mashume @samer800 if anyone has time plz take a look at this if can be done or be helpful in your trading journey, much appreciated!(y)
 
Last edited:
Solution
So this is NOT a conversion of the above given MT4 indicator. It is an implementation of the SmoothStep function (and Smoother Step, and a 7th order version as well -- just for fun) that I whipped up. It is not necessarily comparable to the original, and has far fewer bells and whistles. There are very few checks on anything, and in general it is quite rough around the edges.

Options:
method
1. close
2. HLC
this chooses whether the value for x is calculated using just close values, or high, low, and close values.

type
1. Smooth
2. Smoother
3. Seventh
this option allows you to choose the method of calculating the smoothstep function.

Code:
#  SmoothStep implementation for ThinkOrSwim
#  by mashume for...
So this is NOT a conversion of the above given MT4 indicator. It is an implementation of the SmoothStep function (and Smoother Step, and a 7th order version as well -- just for fun) that I whipped up. It is not necessarily comparable to the original, and has far fewer bells and whistles. There are very few checks on anything, and in general it is quite rough around the edges.

Options:
method
1. close
2. HLC
this chooses whether the value for x is calculated using just close values, or high, low, and close values.

type
1. Smooth
2. Smoother
3. Seventh
this option allows you to choose the method of calculating the smoothstep function.

Code:
#  SmoothStep implementation for ThinkOrSwim
#  by mashume for UseThinkScript.com community
# 2023-01-28

declare lower;

def c = close;
def h = high;
def l = low;

input length = 32;
input method = {default close, HLC};
input type = {default smooth, smoother, seventh};
def x;

switch (method) {
case "close":
    x = if c < lowest(c[1], length) then 0 else if c >= highest(c[1], length) then 1 else (c - lowest(c[1], length)) / (highest(c[1], length) - lowest(c[1], length));
case "HLC":
    x = if c < lowest(l[1], length) then 0 else if c >= highest(h[1], length) then 1 else (c - lowest(c[1], length)) / (highest(h[1], length) - lowest(l[1], length));
}

def val;

switch (type) {
case "smooth":
    val  = x * x * (3.0 - 2.0 * x);
case "smoother":
    val = x * x * x * (x * (x * 6 - 15) + 10);
case "seventh":
    val = -20 * power(x, 7) + 70 * power(x, 6) - 84 * power(x, 5) + 35 * power(x, 4);
}

plot SmoothStep = val;
SmoothStep.SetDefaultColor(getcolor(3));
SmoothStep.SetLineWeight(2);
plot midline = 0.5;
midline.SetDefaultColor(getColor(5));

happy trading,
mashume
 
Last edited:
Solution
okay so I tried to convert the MT4 version, my first attempt at coding and using the help of ChatGPT since don`t have coding experience but its actually cool to use lol. Also not sure if the code is doing what it supposed to do right or if it have converted everything.

I used @mashume SmoothStep implementation for levels comparisons in a ranged chart but don`t think its very smooth lol.

BL8D9uW.jpg


I think your version of the smooth function is the closest, however if this can be improved please take a look;
Here is code,
Code:
# declare lower to allow referencing of daily and lower timeframes
declare lower;

input inpPeriod  = 32;
input priceType = CLOSE;
# determine previous bar to handle bar change conditions
def prevBar = if IsNaN(close[-1]) then 1 else if BarNumber() != BarNumber()[1] then 1 else prevBar[1] + 1;
#calculate previous minimum based on aggregation period
def prevmin = if prevBar[1] != prevBar then
                   if prevBar > 0 and GetAggregationPeriod() > AggregationPeriod.MIN then
                       fold i = 0 to Min(inpPeriod - 1, prevBar - 1) with s do
                           Min(s, low[(inpPeriod  - 1) - 1])
                          # Min(s, low[i + (prevBar - Min(inpPeriod - 1, prevBar - 1) - 1)])
                   else low
               else prevmin[1];
# calculate previous maximum based on aggregation period              
def prevmax = if prevBar[1] != prevBar then
                   if prevBar > 0 and GetAggregationPeriod() > AggregationPeriod.MIN then
                       fold t = 0 to Min(inpPeriod - 1, prevBar - 1) with a do
                           #Max(s, high[i + (prevBar - Min(inpPeriod - 1, prevBar - 1) - 1)])
                            Max(a, high[(inpPeriod - 1)- 1])
                   else high
               else prevmax[1];
               
# determine current minimum and maximum values              
def min = if prevmin < low then prevmin else low;
def max = if prevmax > high then prevmax else high;
# calculate raw SmoothStep value
def raw = if max != min then (priceType - min) / (max - min) else 0;

def m_lows = Lowest(close, inpPeriod);
def m_highs = Highest(high, inpPeriod);
# determine start point for calculations
def start = if IsNaN(m_lows[-1]) then 0 else inpPeriod-1;
# determine highest and lowest values for entire chart
def maxVal = HighestAll(m_highs);
def minVal = LowestAll(m_lows);
def range = maxVal - minVal;
def midVal = minVal + range * 0.5;
# calculate SmoothStep value based on type and method
def SmoothStepVal = (Max(minVal[0], prevmax) + Min(maxVal[0], prevmin)) / 2;
def SmoothStepNorm = if range > 0 then (SmoothStepVal - minVal) / range else midVal;

plot SmoothStepUp = CompoundValue(1, if SmoothStepVal > SmoothStepVal[1] then SmoothStepVal[1] + raw else SmoothStepVal[1] - raw, raw);
plot SmoothStepDown = CompoundValue(1, if SmoothStepNorm > SmoothStepNorm[1] then SmoothStepNorm[1] - raw else SmoothStepNorm[1] + raw, raw);
plot ZeroLine = 0.5;

SmoothStepUp.SetDefaultColor(Color.CYAN);
SmoothStepUp.SetStyle(Curve.FIRM);
SmoothStepDown.SetDefaultColor(Color.ORANGE);
SmoothStepDown.SetStyle(Curve.FIRM);
 
Last edited:

Join useThinkScript to post your question to a community of 21,000+ developers and traders.

Similar threads

Not the exact question you're looking for?

Start a new thread and receive assistance from our community.

87k+ Posts
254 Online
Create Post

Similar threads

Similar threads

The Market Trading Game Changer

Join 2,500+ subscribers inside the useThinkScript VIP Membership Club
  • Exclusive indicators
  • Proven strategies & setups
  • Private Discord community
  • ‘Buy The Dip’ signal alerts
  • Exclusive members-only content
  • Add-ons and resources
  • 1 full year of unlimited support

Frequently Asked Questions

What is useThinkScript?

useThinkScript is the #1 community of stock market investors using indicators and other tools to power their trading strategies. Traders of all skill levels use our forums to learn about scripting and indicators, help each other, and discover new ways to gain an edge in the markets.

How do I get started?

We get it. Our forum can be intimidating, if not overwhelming. With thousands of topics, tens of thousands of posts, our community has created an incredibly deep knowledge base for stock traders. No one can ever exhaust every resource provided on our site.

If you are new, or just looking for guidance, here are some helpful links to get you started.

What are the benefits of VIP Membership?
VIP members get exclusive access to these proven and tested premium indicators: Buy the Dip, Advanced Market Moves 2.0, Take Profit, and Volatility Trading Range. In addition, VIP members get access to over 50 VIP-only custom indicators, add-ons, and strategies, private VIP-only forums, private Discord channel to discuss trades and strategies in real-time, customer support, trade alerts, and much more. Learn all about VIP membership here.
How can I access the premium indicators?
To access the premium indicators, which are plug and play ready, sign up for VIP membership here.
Back
Top