Advertisement IC Markets

Why 1-Minute Charts Aren't for Everyone

Discuss 1-minute to 15-minute price action setups, fading intraday momentum, key support/resistance zones, and proven short-term trading methodologies.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Why 1-Minute Charts Aren't for Everyone

Post by PTScalper »

Pine Script: M1 vs M5 Noise Visualizer

This Pine Script (v5) mathematically proves your point about M1 noise. It calculates a "Choppiness/Noise Ratio" by taking the Total Distance Traveled (the sum of all high-to-low ranges) and dividing it by the Net Displacement (the actual highest high minus lowest low) over a rolling 1-hour window.

A perfectly clean move with no pullbacks scores a 1. The higher the number, the more chaotic and noisy the price action is. When you apply this, the M1 line will consistently chart much higher than M5, visualizing exactly why M1 is a "slot machine."

Code: Select all

//@version=5
indicator("Timeframe Noise Visualizer (M1 vs M5)", shorttitle="TF Noise", overlay=false)

// Function to calculate the Noise Ratio
// Total distance traveled divided by the net structural displacement
get_noise(len) =>
    total_movement = math.sum(high - low, len)
    net_displacement = ta.highest(high, len) - ta.lowest(low, len)
    
    // Avoid division by zero on flat markets
    noise = net_displacement == 0 ? na : total_movement / net_displacement
    noise

// Define a 60-minute rolling window for both timeframes
// 60 bars on a 1-minute chart = 1 hour
// 12 bars on a 5-minute chart = 1 hour
noise_m1 = request.security(syminfo.tickerid, "1", get_noise(60))
noise_m5 = request.security(syminfo.tickerid, "5", get_noise(12))

// Plot the comparisons
plot(noise_m1, color=color.new(color.red, 0), title="M1 Noise Ratio", linewidth=2)
plot(noise_m5, color=color.new(color.blue, 0), title="M5 Noise Ratio", linewidth=2)

// Baseline representing a perfectly clean, frictionless directional move
hline(1, "Absolute Minimum (No Noise)", color=color.gray, linestyle=hline.style_dotted)

// Optional: Fill the gap to highlight the excess noise generated purely by dropping to M1
fill_color = noise_m1 > noise_m5 ? color.new(color.red, 85) : color.new(color.blue, 85)
p1 = plot(noise_m1, display=display.none)
p2 = plot(noise_m5, display=display.none)
fill(p1, p2, color=fill_color, title="Noise Delta")
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Recommended broker for automated trading & scalping IC Markets
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Why 1-Minute Charts Aren't for Everyone

Post by PTScalper »

Here is the updated Pine Script. It now applies a moving average (you can toggle between EMA and SMA in the settings) to the raw noise ratio.

To make it instantly actionable, I also added a Chaos Threshold. When the smoothed M1 noise crosses above this threshold, the indicator paints the background orange—signaling that the 1-minute chart has degraded into a messy, structureless range where you're just paying the spread.

Code: Select all

//@version=5
indicator("Timeframe Noise Visualizer + Smoothing", shorttitle="TF Noise + Chaos", overlay=false)

// --- Settings ---
ma_length       = input.int(20, title="Smoothing Length", minval=1, tooltip="Length for the moving average applied to the noise ratio.")
ma_type         = input.string("EMA", title="MA Type", options=["SMA", "EMA"])
chaos_threshold = input.float(5.0, title="High-Chaos Threshold (M1)", step=0.5, tooltip="If the smoothed M1 noise goes above this, the background highlights to warn of severe chop.")

// --- Core Noise Calculation ---
// Total distance traveled divided by the net structural displacement
get_noise(len) =>
    total_movement = math.sum(high - low, len)
    net_displacement = ta.highest(high, len) - ta.lowest(low, len)
    
    // Avoid division by zero on flat markets
    noise = net_displacement == 0 ? na : total_movement / net_displacement
    noise

// Define a 60-minute rolling window for both timeframes
// 60 bars on a 1-minute chart = 1 hour
// 12 bars on a 5-minute chart = 1 hour
noise_m1 = request.security(syminfo.tickerid, "1", get_noise(60))
noise_m5 = request.security(syminfo.tickerid, "5", get_noise(12))

// --- Smoothing (Moving Average) ---
get_ma(src, len, type) =>
    type == "SMA" ? ta.sma(src, len) : ta.ema(src, len)

smoothed_m1 = get_ma(noise_m1, ma_length, ma_type)
smoothed_m5 = get_ma(noise_m5, ma_length, ma_type)

// --- Plotting ---
// Raw noise (faded to the background to reduce visual clutter)
plot(noise_m1, color=color.new(color.red, 75), title="Raw M1 Noise", linewidth=1)
plot(noise_m5, color=color.new(color.blue, 75), title="Raw M5 Noise", linewidth=1)

// Smoothed noise (the primary focus)
plot(smoothed_m1, color=color.red, title="Smoothed M1 Noise", linewidth=2)
plot(smoothed_m5, color=color.blue, title="Smoothed M5 Noise", linewidth=2)

// Baselines and Thresholds
hline(1, "Absolute Minimum (No Noise)", color=color.gray, linestyle=hline.style_dotted)
hline(chaos_threshold, "Chaos Threshold Line", color=color.new(color.orange, 40), linestyle=hline.style_dashed)

// --- Visual Alerts ---
// Highlight the background when the smoothed M1 noise breaks the chaos threshold
is_chaos = smoothed_m1 > chaos_threshold
bgcolor(is_chaos ? color.new(color.orange, 90) : na, title="High Chaos State Warning")
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Why 1-Minute Charts Aren't for Everyone

Post by PTScalper »

How to use this practically:

The Baseline (1.0): If price moves perfectly from A to B with zero wicks or pullbacks, the ratio is 1.

Visualizing the Trap: You will see the smoothed red line (M1) consistently run far above the blue line (M5). This mathematical delta is exactly what you are avoiding when you filter through D1/M15 charts.

The Chaos Alert: The default threshold is set to 5.0 (meaning the total M1 movement is 5 times larger than the actual structural displacement for that hour). When the background turns orange, it confirms that diving into the M1 chart is statistically equivalent to spinning a slot machine in a messy range.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Why 1-Minute Charts Aren't for Everyone

Post by PTScalper »

The script now includes alertcondition triggers for both exiting and entering the chaos state. You can set TradingView to ping you the moment the smoothed M1 noise crosses under your threshold, signaling a potential return to clean market structure.

Pine script version 3.0

Code: Select all

//@version=5
indicator("Timeframe Noise Visualizer + Smoothing & Alerts", shorttitle="TF Noise + Alerts", overlay=false)

// --- Settings ---
ma_length       = input.int(20, title="Smoothing Length", minval=1, tooltip="Length for the moving average applied to the noise ratio.")
ma_type         = input.string("EMA", title="MA Type", options=["SMA", "EMA"])
chaos_threshold = input.float(5.0, title="High-Chaos Threshold (M1)", step=0.5, tooltip="If the smoothed M1 noise goes above this, the background highlights to warn of severe chop.")

// --- Core Noise Calculation ---
// Total distance traveled divided by the net structural displacement
get_noise(len) =>
    total_movement = math.sum(high - low, len)
    net_displacement = ta.highest(high, len) - ta.lowest(low, len)
    
    // Avoid division by zero on flat markets
    noise = net_displacement == 0 ? na : total_movement / net_displacement
    noise

// Define a 60-minute rolling window for both timeframes
// 60 bars on a 1-minute chart = 1 hour
// 12 bars on a 5-minute chart = 1 hour
noise_m1 = request.security(syminfo.tickerid, "1", get_noise(60))
noise_m5 = request.security(syminfo.tickerid, "5", get_noise(12))

// --- Smoothing (Moving Average) ---
get_ma(src, len, type) =>
    type == "SMA" ? ta.sma(src, len) : ta.ema(src, len)

smoothed_m1 = get_ma(noise_m1, ma_length, ma_type)
smoothed_m5 = get_ma(noise_m5, ma_length, ma_type)

// --- Plotting ---
// Raw noise (faded to the background to reduce visual clutter)
plot(noise_m1, color=color.new(color.red, 75), title="Raw M1 Noise", linewidth=1)
plot(noise_m5, color=color.new(color.blue, 75), title="Raw M5 Noise", linewidth=1)

// Smoothed noise (the primary focus)
plot(smoothed_m1, color=color.red, title="Smoothed M1 Noise", linewidth=2)
plot(smoothed_m5, color=color.blue, title="Smoothed M5 Noise", linewidth=2)

// Baselines and Thresholds
hline(1, "Absolute Minimum (No Noise)", color=color.gray, linestyle=hline.style_dotted)
hline(chaos_threshold, "Chaos Threshold Line", color=color.new(color.orange, 40), linestyle=hline.style_dashed)

// --- Visual Alerts ---
// Highlight the background when the smoothed M1 noise breaks the chaos threshold
is_chaos = smoothed_m1 > chaos_threshold
bgcolor(is_chaos ? color.new(color.orange, 90) : na, title="High Chaos State Warning")

// --- Alert Conditions ---
// Trigger when M1 noise drops below the threshold (cleaner price action returning)
exit_chaos = ta.crossunder(smoothed_m1, chaos_threshold)
alertcondition(exit_chaos, title="Exiting Chaos State (M1)", message="M1 Noise dropped below the chaos threshold. Price action is cleaning up.")

// Trigger when M1 enters chaos (optional warning)
enter_chaos = ta.crossover(smoothed_m1, chaos_threshold)
alertcondition(enter_chaos, title="Entering Chaos State (M1)", message="M1 Noise exceeded the chaos threshold. Messy range detected.")
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Why 1-Minute Charts Aren't for Everyone

Post by PTScalper »

To activate the notification:

1.) Add the updated indicator to your chart.

2.) Press Alt + A (or click the Alerts icon in TradingView).

3.) Set the Condition drop-down to this script (TF Noise + Alerts).

4.) Select Exiting Chaos State (M1) from the secondary drop-down.

5.) Set the trigger frequency to Once Per Bar Close to ensure the alert only fires when the 1-minute candle actually closes under the threshold, preventing false positives from mid-candle wicks.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Why 1-Minute Charts Aren't for Everyone

Post by PTScalper »

Moving this from TradingView to MetaTrader requires handling multi-timeframe synchronization explicitly, as MT4 and MT5 do not have Pine Script’s request.security abstraction.

Both scripts below use iBarShift to align the M1 and M5 timeframes perfectly to whatever chart timeframe you drop the indicator on. They use hidden buffers for the raw noise calculation and plot the smoothed averages. The alerts strictly trigger on the close of the candle (index 1) to prevent false intra-bar signals.

MT4 Implementation (MQL4)

Save this as TF_Noise_Visualizer.mq4 in your MQL4/Indicators folder.

Code: Select all

//+------------------------------------------------------------------+
//|                                        TF_Noise_Visualizer.mq4   |
//+------------------------------------------------------------------+
#property copyright "Pavel Tucek"
#property indicator_separate_window
#property indicator_buffers 4
#property indicator_color1 clrRed
#property indicator_color2 clrDodgerBlue
#property indicator_width1 2
#property indicator_width2 2

// Visual Levels
#property indicator_level1 1.0
#property indicator_level2 5.0
#property indicator_levelcolor clrDarkGray
#property indicator_levelstyle STYLE_DOT

//--- Inputs
input int    InpM1Bars         = 60;       // M1 Rolling Window (60 = 1 Hour)
input int    InpM5Bars         = 12;       // M5 Rolling Window (12 = 1 Hour)
input int    InpMAPeriod       = 20;       // Smoothing MA Period
input int    InpMAMethod       = 1;        // 0: SMA, 1: EMA
input double InpChaosThreshold = 5.0;      // High-Chaos Threshold (M1)
input bool   InpUseAlerts      = true;     // Enable Push/Popup Alerts

//--- Buffers
double ExtSmoothedM1[];
double ExtSmoothedM5[];
double ExtRawM1[];
double ExtRawM5[];

//--- Global Variables
datetime lastAlertTime = 0;

//+------------------------------------------------------------------+
int OnInit()
  {
   SetIndexBuffer(0, ExtSmoothedM1);
   SetIndexStyle(0, DRAW_LINE);
   SetIndexLabel(0, "Smoothed M1 Noise");

   SetIndexBuffer(1, ExtSmoothedM5);
   SetIndexStyle(1, DRAW_LINE);
   SetIndexLabel(1, "Smoothed M5 Noise");

   // Hidden buffers for raw calculations
   SetIndexBuffer(2, ExtRawM1);
   SetIndexStyle(2, DRAW_NONE);
   SetIndexBuffer(3, ExtRawM5);
   SetIndexStyle(3, DRAW_NONE);

   IndicatorShortName("TF Noise [" + IntegerToString(InpMAPeriod) + "]");
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
double CalculateNoise(int timeframe, datetime time, int lookback)
  {
   int shift = iBarShift(NULL, timeframe, time, false);
   if(shift < 0) return 0.0;

   double total_movement = 0;
   int highest_idx = iHighest(NULL, timeframe, MODE_HIGH, lookback, shift);
   int lowest_idx  = iLowest(NULL, timeframe, MODE_LOW, lookback, shift);

   if(highest_idx < 0 || lowest_idx < 0) return 0.0;

   double highest_high = iHigh(NULL, timeframe, highest_idx);
   double lowest_low   = iLow(NULL, timeframe, lowest_idx);
   double net_displacement = highest_high - lowest_low;

   for(int i = 0; i < lookback; i++)
     {
      total_movement += (iHigh(NULL, timeframe, shift + i) - iLow(NULL, timeframe, shift + i));
     }

   if(net_displacement <= 0) return 1.0; 
   return total_movement / net_displacement;
  }

//+------------------------------------------------------------------+
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 < InpMAPeriod) return(0);
   int limit = rates_total - prev_calculated;
   if(limit > 0) limit++; 
   if(prev_calculated == 0) limit = rates_total - 1;

   // 1. Calculate Raw Noise
   for(int i = limit; i >= 0; i--)
     {
      ExtRawM1[i] = CalculateNoise(PERIOD_M1, time[i], InpM1Bars);
      ExtRawM5[i] = CalculateNoise(PERIOD_M5, time[i], InpM5Bars);
     }

   // 2. Apply Smoothing
   double alpha = 2.0 / (InpMAPeriod + 1.0);
   for(int i = limit; i >= 0; i--)
     {
      if(InpMAMethod == 0) // SMA
        {
         double sumM1 = 0, sumM5 = 0;
         for(int k = 0; k < InpMAPeriod; k++)
           {
            sumM1 += ExtRawM1[i + k];
            sumM5 += ExtRawM5[i + k];
           }
         ExtSmoothedM1[i] = sumM1 / InpMAPeriod;
         ExtSmoothedM5[i] = sumM5 / InpMAPeriod;
        }
      else // EMA
        {
         if(i == rates_total - 1) 
           {
            ExtSmoothedM1[i] = ExtRawM1[i];
            ExtSmoothedM5[i] = ExtRawM5[i];
           }
         else 
           {
            ExtSmoothedM1[i] = alpha * ExtRawM1[i] + (1 - alpha) * ExtSmoothedM1[i + 1];
            ExtSmoothedM5[i] = alpha * ExtRawM5[i] + (1 - alpha) * ExtSmoothedM5[i + 1];
           }
        }
     }

   // 3. Process Alerts (Only strictly on closed bar [1])
   if(InpUseAlerts && time[0] != lastAlertTime)
     {
      bool crossUnder = (ExtSmoothedM1[1] < InpChaosThreshold && ExtSmoothedM1[2] >= InpChaosThreshold);
      bool crossOver  = (ExtSmoothedM1[1] > InpChaosThreshold && ExtSmoothedM1[2] <= InpChaosThreshold);

      if(crossUnder)
        {
         Alert(Symbol(), " TF Noise: M1 dropped below chaos threshold. Clean structure returning.");
         lastAlertTime = time[0];
        }
      else if(crossOver)
        {
         Alert(Symbol(), " TF Noise: M1 exceeded chaos threshold. Messy range detected.");
         lastAlertTime = time[0];
        }
     }

   return(rates_total);
  }
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Why 1-Minute Charts Aren't for Everyone

Post by PTScalper »

MT5 Implementation (MQL5)

Save this as TF_Noise_Visualizer.mq5 in your MQL5/Indicators folder. MQL5 relies heavily on CopyHigh/CopyLow, so this version retrieves the exact arrays for the timeframes to perform the math efficiently.

Code: Select all

//+------------------------------------------------------------------+
//|                                        TF_Noise_Visualizer.mq5   |
//+------------------------------------------------------------------+
#property copyright "Pavel Tucek"
#property indicator_separate_window
#property indicator_buffers 4
#property indicator_plots   2

#property indicator_label1  "Smoothed M1"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2

#property indicator_label2  "Smoothed M5"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrDodgerBlue
#property indicator_style2  STYLE_SOLID
#property indicator_width2  2

#property indicator_level1  1.0
#property indicator_level2  5.0
#property indicator_levelcolor clrDarkGray
#property indicator_levelstyle STYLE_DOT

enum ENUM_MA_TYPE { MA_SMA=0, MA_EMA=1 };

//--- Inputs
input int          InpM1Bars         = 60;       // M1 Rolling Window (60 = 1 Hour)
input int          InpM5Bars         = 12;       // M5 Rolling Window (12 = 1 Hour)
input int          InpMAPeriod       = 20;       // Smoothing MA Period
input ENUM_MA_TYPE InpMAMethod       = MA_EMA;   // Smoothing Type
input double       InpChaosThreshold = 5.0;      // High-Chaos Threshold (M1)
input bool         InpUseAlerts      = true;     // Enable Alerts

//--- Buffers
double ExtSmoothedM1[];
double ExtSmoothedM5[];
double ExtRawM1[];
double ExtRawM5[];

//--- Global Variables
datetime lastAlertTime = 0;

//+------------------------------------------------------------------+
int OnInit()
  {
   SetIndexBuffer(0, ExtSmoothedM1, INDICATOR_DATA);
   SetIndexBuffer(1, ExtSmoothedM5, INDICATOR_DATA);
   SetIndexBuffer(2, ExtRawM1, INDICATOR_CALCULATIONS);
   SetIndexBuffer(3, ExtRawM5, INDICATOR_CALCULATIONS);

   ArraySetAsSeries(ExtSmoothedM1, true);
   ArraySetAsSeries(ExtSmoothedM5, true);
   ArraySetAsSeries(ExtRawM1, true);
   ArraySetAsSeries(ExtRawM5, true);

   IndicatorSetString(INDICATOR_SHORTNAME, "TF Noise [" + IntegerToString(InpMAPeriod) + "]");
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
double CalculateNoise(ENUM_TIMEFRAMES tf, datetime targetTime, int lookback)
  {
   int shift = iBarShift(_Symbol, tf, targetTime, false);
   if(shift < 0) return 0.0;

   double high_arr[], low_arr[];
   if(CopyHigh(_Symbol, tf, shift, lookback, high_arr) <= 0) return 0.0;
   if(CopyLow(_Symbol, tf, shift, lookback, low_arr) <= 0) return 0.0;

   double highest_high = high_arr[ArrayMaximum(high_arr)];
   double lowest_low   = low_arr[ArrayMinimum(low_arr)];
   double net_displacement = highest_high - lowest_low;
   
   double total_movement = 0;
   for(int i = 0; i < lookback; i++)
     {
      total_movement += (high_arr[i] - low_arr[i]);
     }

   if(net_displacement <= 0) return 1.0;
   return total_movement / net_displacement;
  }

//+------------------------------------------------------------------+
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 < InpMAPeriod) return(0);

   ArraySetAsSeries(time, true);
   
   int limit = rates_total - prev_calculated;
   if(limit > 0) limit++; 
   if(prev_calculated == 0) limit = rates_total - 1;

   // 1. Raw Noise Array
   for(int i = limit; i >= 0 && !IsStopped(); i--)
     {
      ExtRawM1[i] = CalculateNoise(PERIOD_M1, time[i], InpM1Bars);
      ExtRawM5[i] = CalculateNoise(PERIOD_M5, time[i], InpM5Bars);
     }

   // 2. Smoothing Array
   double alpha = 2.0 / (InpMAPeriod + 1.0);
   for(int i = limit; i >= 0 && !IsStopped(); i--)
     {
      if(InpMAMethod == MA_SMA)
        {
         double sumM1 = 0, sumM5 = 0;
         for(int k = 0; k < InpMAPeriod; k++)
           {
            if((i + k) < rates_total)
              {
               sumM1 += ExtRawM1[i + k];
               sumM5 += ExtRawM5[i + k];
              }
           }
         ExtSmoothedM1[i] = sumM1 / InpMAPeriod;
         ExtSmoothedM5[i] = sumM5 / InpMAPeriod;
        }
      else // MA_EMA
        {
         if(i == rates_total - 1)
           {
            ExtSmoothedM1[i] = ExtRawM1[i];
            ExtSmoothedM5[i] = ExtRawM5[i];
           }
         else
           {
            ExtSmoothedM1[i] = alpha * ExtRawM1[i] + (1 - alpha) * ExtSmoothedM1[i + 1];
            ExtSmoothedM5[i] = alpha * ExtRawM5[i] + (1 - alpha) * ExtSmoothedM5[i + 1];
           }
        }
     }

   // 3. Alerts processing (Index 1 is the most recently closed candle)
   if(InpUseAlerts && time[0] != lastAlertTime && rates_total > 2)
     {
      bool crossUnder = (ExtSmoothedM1[1] < InpChaosThreshold && ExtSmoothedM1[2] >= InpChaosThreshold);
      bool crossOver  = (ExtSmoothedM1[1] > InpChaosThreshold && ExtSmoothedM1[2] <= InpChaosThreshold);

      if(crossUnder)
        {
         Alert(_Symbol, " TF Noise: M1 dropped below chaos threshold. Clean structure returning.");
         lastAlertTime = time[0];
        }
      else if(crossOver)
        {
         Alert(_Symbol, " TF Noise: M1 exceeded chaos threshold. Messy range detected.");
         lastAlertTime = time[0];
        }
     }

   return(rates_total);
  }
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Why 1-Minute Charts Aren't for Everyone

Post by PTScalper »

Here is the complete C# implementation for cTrader. It fetches the M1 and M5 bars directly, calculates the raw noise into hidden IndicatorDataSeries, and then pipes that data through cTrader's native MovingAverage engine.

cTrader Implementation (C#)

1.) Open cTrader, go to the Automate tab.

2.) Click New Indicator and name it TFNoiseVisualizer.

3.) Replace the default code with this:

Code: Select all

using System;
using cAlgo.API;
using cAlgo.API.Indicators;

namespace cAlgo
{
    [Indicator(IsOverlay = false, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class TFNoiseVisualizer : Indicator
    {
        // --- Parameters ---
        [Parameter("M1 Bars (60 = 1h)", DefaultValue = 60)]
        public int M1Bars { get; set; }

        [Parameter("M5 Bars (12 = 1h)", DefaultValue = 12)]
        public int M5Bars { get; set; }

        [Parameter("Smoothing Period", DefaultValue = 20)]
        public int MAPeriod { get; set; }

        [Parameter("MA Type", DefaultValue = MovingAverageType.Exponential)]
        public MovingAverageType MaType { get; set; }

        [Parameter("Chaos Threshold (M1)", DefaultValue = 5.0)]
        public double ChaosThreshold { get; set; }

        [Parameter("Use Log Alerts", DefaultValue = true)]
        public bool UseAlerts { get; set; }

        // --- Outputs ---
        [Output("Smoothed M1 Noise", LineColor = "Red", Thickness = 2)]
        public IndicatorDataSeries SmoothedM1 { get; set; }

        [Output("Smoothed M5 Noise", LineColor = "DodgerBlue", Thickness = 2)]
        public IndicatorDataSeries SmoothedM5 { get; set; }

        [Output("Chaos Threshold Line", LineColor = "Orange", LineStyle = LineStyle.Lines)]
        public IndicatorDataSeries ThresholdLine { get; set; }

        [Output("Baseline", LineColor = "Gray", LineStyle = LineStyle.Dots)]
        public IndicatorDataSeries BaselineLine { get; set; }

        // --- Internal Variables ---
        private Bars _m1Bars;
        private Bars _m5Bars;

        private IndicatorDataSeries _rawM1;
        private IndicatorDataSeries _rawM5;

        private MovingAverage _maM1;
        private MovingAverage _maM5;

        private DateTime _lastAlertTime;

        protected override void Initialize()
        {
            // Fetch the external timeframes natively
            _m1Bars = MarketData.GetBars(TimeFrame.Minute);
            _m5Bars = MarketData.GetBars(TimeFrame.Minute5);

            // Initialize hidden series for raw calculation
            _rawM1 = CreateDataSeries();
            _rawM5 = CreateDataSeries();

            // Pipe the raw series through native MA engines
            _maM1 = Indicators.MovingAverage(_rawM1, MAPeriod, MaType);
            _maM5 = Indicators.MovingAverage(_rawM5, MAPeriod, MaType);
        }

        public override void Calculate(int index)
        {
            // Sync the current chart's time to the MTF series indices
            var currentTime = Bars.OpenTimes[index];
            int idxM1 = _m1Bars.OpenTimes.GetIndexByTime(currentTime);
            int idxM5 = _m5Bars.OpenTimes.GetIndexByTime(currentTime);

            // Calculate raw noise
            _rawM1[index] = CalculateNoise(_m1Bars, idxM1, M1Bars);
            _rawM5[index] = CalculateNoise(_m5Bars, idxM5, M5Bars);

            // Output smoothed noise
            SmoothedM1[index] = _maM1.Result[index];
            SmoothedM5[index] = _maM5.Result[index];

            // Draw static lines
            ThresholdLine[index] = ChaosThreshold;
            BaselineLine[index] = 1.0;

            // Process alerts only for the most recently closed candle
            if (UseAlerts && IsLastBar && index > 2)
            {
                CheckAlerts(index);
            }
        }

        private double CalculateNoise(Bars tfBars, int tfIndex, int lookback)
        {
            if (tfIndex < lookback || tfIndex == -1)
                return double.NaN;

            double totalMovement = 0;
            double highestHigh = double.MinValue;
            double lowestLow = double.MaxValue;

            for (int i = 0; i < lookback; i++)
            {
                // Traverse backward through the target timeframe
                int currentIndex = tfIndex - i;
                double high = tfBars.HighPrices[currentIndex];
                double low = tfBars.LowPrices[currentIndex];

                totalMovement += (high - low);
                if (high > highestHigh) highestHigh = high;
                if (low < lowestLow) lowestLow = low;
            }

            double netDisplacement = highestHigh - lowestLow;
            
            // Prevent division by zero if the market is entirely flat
            if (netDisplacement == 0) return 1.0; 

            return totalMovement / netDisplacement;
        }

        private void CheckAlerts(int index)
        {
            // Index - 1 is the candle that just closed. Index - 2 is the one before it.
            var lastClosedBarTime = Bars.OpenTimes[index - 1];

            // Ensure we only process a closed candle once
            if (_lastAlertTime == lastClosedBarTime) return;

            double currentM1 = SmoothedM1[index - 1];
            double prevM1 = SmoothedM1[index - 2];

            bool crossedUnder = prevM1 >= ChaosThreshold && currentM1 < ChaosThreshold;
            bool crossedOver = prevM1 <= ChaosThreshold && currentM1 > ChaosThreshold;

            if (crossedUnder)
            {
                Print($"{Symbol.Name}: M1 Noise dropped below chaos threshold. Clean structure returning.");
                // Draw a visual cue directly on the indicator panel
                Chart.DrawText($"alert_{lastClosedBarTime.Ticks}", " ↓ Clean", index - 1, currentM1, Color.Lime);
                _lastAlertTime = lastClosedBarTime;
            }
            else if (crossedOver)
            {
                Print($"{Symbol.Name}: M1 Noise exceeded chaos threshold. Messy range detected.");
                // Draw a visual cue directly on the indicator panel
                Chart.DrawText($"alert_{lastClosedBarTime.Ticks}", " ↑ Chaos", index - 1, currentM1, Color.Orange);
                _lastAlertTime = lastClosedBarTime;
            }
        }
    }
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Why 1-Minute Charts Aren't for Everyone

Post by PTScalper »

cTrader Specifics to Note:

Memory Efficiency: cTrader calculates IsLastBar on every tick. The script restricts alert evaluations strictly to index - 1 (the last closed candle) and uses _lastAlertTime as a lock. This ensures you aren't spammed with alerts from intra-candle M1 ticks jumping over and under the threshold.

Visual Cues: Since cTrader's native pop-ups interrupt workflow, this script prints to the cTrader Log and uses Chart.DrawText to draw "↑ Chaos" or "↓ Clean" text directly onto the indicator window exactly at the crossover point.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PropScalpDesk
Posts: 364
Joined: Sat Sep 19, 2026 7:50 pm

Re: Why 1-Minute Charts Aren't for Everyone

Post by PropScalpDesk »

PTScalper wrote:From my perspective, M1 is simply too close, too fast, and overwhelmingly full of noise. The higher-timeframe filter isn't just a brake for me—it is the entire mechanism. I rely on the Daily and M15 charts for raw price action and market structure.
That is a cleaner statement of the same philosophy. If D1/M15 is not presenting structure, dropping to M1 is just paying spread to trade static — I have lived that version and it is expensive entertainment. From Frankfurt I still keep M1 as a timing tool after permission, not as a hunting ground. Your choice to live on M15 for execution granularity without the whip-saw is coherent; I only disagree on one operational detail: I still want a written M1 ticket cap on prop accounts even when noise "naturally" keeps me away, because a soft morning can invent reasons to zoom in.

Desk rule: no M1 order ticket unless H1 bias and M15 location are written in one sentence first; max two M1 attempts per hour on funded. Frequency goals are how overtrading dresses up as work, and more M1 tickets means more chances to nick a firm rule.

When D1/M15 are clear but the first M15 pullback fails, do you refuse M1 entirely for the rest of that session, or allow one timed attempt after a second structural hold?
Post Reply