Price_Distribution

Download script
Author: Copyright � 2006, MetaQuotes Software Corp.
Price_Distribution

This script is designed to analyze and visualize price behavior on a trading chart by identifying specific time windows and mapping out where the most trading activity occurred. Instead of just showing you where the price went, it helps you see where the price "spent the most time" or had the highest density of action.

Core Functionality

The script identifies a user-defined time range (for example, from 10:00 AM to 11:59 PM) for each day over a specified number of past days. For every one of these daily boxes, it performs three distinct visual actions:

1. Range Identification (The Box) The script scans the historical data for the high and low prices reached during your chosen time window. It then draws a large rectangular box on your chart to clearly define the boundaries of that day's price action.

2. Extremes Mapping (High/Low Lines) To make the boundaries even clearer, it draws horizontal lines at the highest and lowest points reached during that specific period. This allows you to see at a glance what the "ceiling" and "floor" were for that session.

3. Price Distribution Analysis (The Density Map) This is the most advanced part of the logic. The script takes the space between the High and Low and divides it into 50 equal "buckets" or zones. It then looks at every single minute of data within your time window to see which zones the price entered.

  • Frequency Counting: If the price touched a specific zone, that zone gets a "tick."
  • Visual Density: The script then draws smaller rectangles for each of these 50 buckets. The length of these rectangles is proportional to how many times the price visited that specific area.

How to Read the Results

When you look at the chart, you will see a series of boxes and horizontal lines. Within those boundaries, you will see a "distribution" of smaller bars:

  • Longer Bars: Indicate areas where price spent a significant amount of time or experienced heavy concentration. These are your high-activity zones.
  • Shorter/No Bars: Indicate areas where the price moved through very quickly or rarely visited.

By using this tool, you can visually identify "support" and "resistance" zones based on actual historical density rather than just single price points, helping you understand where the market feels most "crowded."

Price Data Components
Series array that contains the highest prices of each barSeries array that contains the lowest prices of each bar
12 Views
0 Downloads
0 Favorites
Price_Distribution
//+------------------------------------------------------------------+
//|                                           Price_Distribution.mq4 |
//|                      Copyright © 2006, MetaQuotes Software Corp. |
//|                                        http://www.metaquotes.net |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2006, MetaQuotes Software Corp."
#property link      "http://www.metaquotes.net"

#define      SECONDS_IN_MINUTE   60 
#define      SECONDS_IN_HOUR      3600 
#define      SECONDS_IN_DAY      86400 

#define      OBJNAME_PREPEND      "ValueInRange" 

#property indicator_chart_window 


extern string   BoxStart         = "10:00"; 
extern string   BoxEnd            = "23:59"; 
extern string   LineEnd            = "10:00"; 
extern double   PlotMagnification   = 300.0; 
extern color   BoxColor         = C'17,28,28'; 
extern color   LineColor         = DarkSlateGray; 
extern color   PlotColor         = SlateGray; 
extern int      DaysToPlot         = 10; 


int init() { 
} 

int deinit() { 
   DeleteObjects(OBJNAME_PREPEND); 
   return(0); 
} 

int start() { 

   int iPlots = 0; 

   // Determine how far back to iterate 
   // Always recalc last complete bar 
   int iBarsToCalc = Bars - IndicatorCounted(); 
   if (iBarsToCalc < Bars) iBarsToCalc++; 

   // Determine start time for current (or just closed) box 
   datetime dtBoxStart = StrToTime(TimeToStr(TimeCurrent(),TIME_DATE) + " " + BoxStart); 
   if (dtBoxStart > TimeCurrent()) dtBoxStart -= SECONDS_IN_DAY; 
       
   // Determine end time for current (or just closed) box 
   datetime dtBoxEnd   = StrToTime(TimeToStr(dtBoxStart,TIME_DATE) + " " + BoxEnd); 
   if (dtBoxEnd < dtBoxStart) dtBoxEnd += SECONDS_IN_DAY; 

   // Simply loop, there are multiple ways to break 
   // While the box close time is after the oldest bar which needs calculation, 
   // calculate the box then add a day and repeat 
   while (true) { 
    
      // If the box close time is before the oldest bar which needs calculation, exit 
      if (dtBoxEnd < Time[iBarsToCalc]) return(0); 
       
       
      // If max number of iterations reached, exit 
      if (iPlots >= DaysToPlot) return(0); 
       
      // If the range to be plotted has data, plot it 
      if (iBarShift(NULL,PERIOD_M1,dtBoxStart,false) != iBarShift(NULL,PERIOD_M1,dtBoxEnd,false)) { 
         Print(TimeToStr(dtBoxStart), "  ", TimeToStr(dtBoxEnd)); 
         doCalcValueInRange(dtBoxStart); 
         iPlots++; 
      } 
      else { 
         Print("No data for ", TimeToStr(dtBoxStart), "  ", TimeToStr(dtBoxEnd)); 
      } 
       
      // Decrement dates 
      dtBoxStart   -= SECONDS_IN_DAY; 
      dtBoxEnd   -= SECONDS_IN_DAY; 
   } 
} 

void doCalcValueInRange(datetime dtBoxStart) { 

   // Determine box end time based on box start time 
   // Insure it's after box start 
   datetime dtBoxEnd   = StrToTime(TimeToStr(dtBoxStart,TIME_DATE) + " " + BoxEnd); 
   if (dtBoxEnd < dtBoxStart) dtBoxEnd += SECONDS_IN_DAY; 
    
   // Determine line end time based on box end time 
   // Insure it's after box end 
   datetime dtLineEnd   = StrToTime(TimeToStr(dtBoxEnd,TIME_DATE) + " " + LineEnd); 
   if (dtLineEnd < dtBoxEnd) dtLineEnd += SECONDS_IN_DAY; 
       
   // Figure out start and end bars for box on the minute chart 
   // Make sure the timestamp of start bar is >= box start time 
   int iStart   = iBarShift(NULL,PERIOD_M1,dtBoxStart,false); 
   int iEnd   = iBarShift(NULL,PERIOD_M1,dtBoxEnd,false); 
   while (iTime(NULL,PERIOD_M1,iStart) < dtBoxStart) iStart--; 
    
   // Get the high and low prices for the day 
   double dHigh = iHigh(NULL,PERIOD_M1,iHighest(NULL,PERIOD_M1,MODE_HIGH,iStart-iEnd,iEnd)); 
   double dLow  = iLow (NULL,PERIOD_M1,iLowest (NULL,PERIOD_M1,MODE_LOW ,iStart-iEnd,iEnd)); 
    
   // Draw the box 
   doDrawBox(dtBoxStart,dtBoxEnd,dHigh,dLow); 
    
   // Draw the high/low lines 
   doDrawRangeLines(dtBoxStart,dtLineEnd,dHigh,dLow); 
    
   // Draw the price distribution 
   doDrawDistribution(dtBoxStart,dtLineEnd,dHigh,dLow); 

} 

void doDrawBox(datetime dtStart, datetime dtEnd, double dHigh, double dLow) { 

   // Draw the box 
   string sObjName = OBJNAME_PREPEND + " " + TimeToStr(dtStart); 
   ObjectCreate(sObjName,OBJ_RECTANGLE,0,dtStart,dHigh,dtEnd,dLow); 
   ObjectSet(sObjName,OBJPROP_COLOR,BoxColor); 
    
} 

void doDrawRangeLines(datetime dtStart, datetime dtEnd, double dHigh, double dLow) { 

   string sObjName; 
    
   // Draw range high 
   sObjName = OBJNAME_PREPEND + " " + TimeToStr(dtStart) + " " + "High"; 
   ObjectCreate(sObjName,OBJ_TREND,0,dtStart,dHigh,dtEnd,dHigh); 
   ObjectSet(sObjName,OBJPROP_RAY,false); 
   ObjectSet(sObjName,OBJPROP_COLOR,LineColor); 
    
   // Draw range low 
   sObjName = OBJNAME_PREPEND + " " + TimeToStr(dtStart) + " " + "Low"; 
   ObjectCreate(sObjName,OBJ_TREND,0,dtStart,dLow,dtEnd,dLow); 
   ObjectSet(sObjName,OBJPROP_RAY,false); 
   ObjectSet(sObjName,OBJPROP_COLOR,LineColor); 
    
} 

void doDrawDistribution(datetime dtStart, datetime dtEnd, double dHigh, double dLow) { 

   // Buckets to place 'ticks' into. 
   double    dBuckets[50]; 
    
   ArrayInitialize(dBuckets,0.0); 
    
   // Var to tally total 'ticks' 
   int      iTicks   = 0; 
    
   // Figure out the value to use to "split" prices into different buckets 
   double    dSplitter = (dHigh - dLow) / ArraySize(dBuckets); 

   // Get the offset for the first minute bar of the range. 
   double iStartBar = iBarShift(NULL,PERIOD_M1,dtStart,false); 
   while (iTime(NULL,PERIOD_M1,iStartBar) < dtStart) iStartBar--; 
    
   // Get the offset for the last minute bar of the range.    
   double iEndBar = iBarShift(NULL,PERIOD_M1,dtEnd,false); 
    
   // Iterate all minutes of the day and place a 'tick' in each bucket 
   // that price touched 
   for (int i=iStartBar;i>=iEndBar;i--) { 
      for (int j=0;j<=ArraySize(dBuckets)-1;j++) { 
         double dBucketHigh = dHigh - (j)  *dSplitter; 
         double dBucketLow  = dHigh - (j+1)*dSplitter; 
       
         // If price was above bucket range, no tick 
         if (iLow(NULL,PERIOD_M1,i) > dBucketHigh) 
            continue; 
         // If price was below bucket range, no tick 
         if (iHigh(NULL,PERIOD_M1,i) <= dBucketLow) 
            continue; 
          
         // bucket gets a tick 
         dBuckets[j] += 1; 
         iTicks++; 
      } 
   } 
    
   //string sObjName = OBJNAME_PREPEND + " " + TimeToStr(dtStart) + " TickTotal"; 
   //ObjectCreate(sObjName,OBJ_TEXT,0,dtStart,(dHigh+dLow)*0.5); 
   //ObjectSetText(sObjName,"Ticks: " + iTicks); 
    
   // Draw Bucket Lines 
   for (j=0;j<=ArraySize(dBuckets)-1;j++) { 
      dBucketHigh = dHigh - (j)  *dSplitter; 
      dBucketLow  = dHigh - (j+1)*dSplitter; 

      // Create a name for the bucket object 
      string sObjName = OBJNAME_PREPEND + " " + TimeToStr(dtStart) + " Bucket " + j+1; 
      // Determine how many bars long it will be.  Each bar indicates bucket contained 5% of the price action 
      int   iObjLength = 1; 
      if (iTicks > 0)   iObjLength = (dBuckets[j]/iTicks)*PlotMagnification; 
       
      ObjectCreate(sObjName,OBJ_RECTANGLE,0,dtStart,dBucketHigh,dtStart+Period()*60*iObjLength,dBucketLow); 
      ObjectSet(sObjName,OBJPROP_COLOR,PlotColor); 
       
   } 
} 

void DeleteObjects(string sObjNamePrepend) { 

   // delete objects 
   for (int i=ObjectsTotal()-1;i>=0;i--) { 
      string sObjName = ObjectName(i); 
      if (StringFind(sObjName,sObjNamePrepend) == 0) 
         ObjectDelete(sObjName); 
   } 
}

Comments