forked from zjwzjw20062007/MQL5Book
		
	
		
			
	
	
		
			293 lines
		
	
	
	
		
			8.7 KiB
		
	
	
	
		
			MQL5
		
	
	
	
	
	
		
		
			
		
	
	
			293 lines
		
	
	
	
		
			8.7 KiB
		
	
	
	
		
			MQL5
		
	
	
	
	
	
| 
								 | 
							
								//+------------------------------------------------------------------+
							 | 
						||
| 
								 | 
							
								//|                                            SymbolBidAskChart.mq5 |
							 | 
						||
| 
								 | 
							
								//|                                  Copyright 2021, MetaQuotes Ltd. |
							 | 
						||
| 
								 | 
							
								//|                                             https://www.mql5.com |
							 | 
						||
| 
								 | 
							
								//+------------------------------------------------------------------+
							 | 
						||
| 
								 | 
							
								// indicator settings
							 | 
						||
| 
								 | 
							
								#property indicator_chart_window
							 | 
						||
| 
								 | 
							
								#property indicator_buffers 4
							 | 
						||
| 
								 | 
							
								#property indicator_plots   1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// plot settings
							 | 
						||
| 
								 | 
							
								#property indicator_type1   DRAW_BARS
							 | 
						||
| 
								 | 
							
								#property indicator_color1  clrDodgerBlue
							 | 
						||
| 
								 | 
							
								#property indicator_width1  2
							 | 
						||
| 
								 | 
							
								#property indicator_label1  "Open;High;Low;Close;"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// includes
							 | 
						||
| 
								 | 
							
								#include "..\..\Include\IndCommon.mqh"
							 | 
						||
| 
								 | 
							
								#include "..\..\Include\TickEnum.mqh"
							 | 
						||
| 
								 | 
							
								#include "..\..\Include\MqlError.mqh"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								enum ENUM_SYMBOL_CHART_MODE_EXTENDED
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								   _SYMBOL_CHART_MODE_BID,  // SYMBOL_CHART_MODE_BID
							 | 
						||
| 
								 | 
							
								   _SYMBOL_CHART_MODE_LAST, // SYMBOL_CHART_MODE_LAST
							 | 
						||
| 
								 | 
							
								   _SYMBOL_CHART_MODE_ASK,  // SYMBOL_CHART_MODE_ASK*
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// inputs
							 | 
						||
| 
								 | 
							
								input int BarCount = 100;
							 | 
						||
| 
								 | 
							
								input COPY_TICKS TickType = INFO_TICKS;
							 | 
						||
| 
								 | 
							
								input ENUM_SYMBOL_CHART_MODE_EXTENDED ChartMode = _SYMBOL_CHART_MODE_BID;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//+------------------------------------------------------------------+
							 | 
						||
| 
								 | 
							
								//| Class for calculation of bars based on specific price type       |
							 | 
						||
| 
								 | 
							
								//+------------------------------------------------------------------+
							 | 
						||
| 
								 | 
							
								class CalcCustomBars
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								   const int limit;
							 | 
						||
| 
								 | 
							
								   const COPY_TICKS tickType;
							 | 
						||
| 
								 | 
							
								   const ENUM_SYMBOL_CHART_MODE_EXTENDED chartMode;
							 | 
						||
| 
								 | 
							
								   
							 | 
						||
| 
								 | 
							
								   ulong lasttime; // millisecond mark of the last online tick processed
							 | 
						||
| 
								 | 
							
								   int lastcount;  // number of online ticks with the same mark
							 | 
						||
| 
								 | 
							
								   
							 | 
						||
| 
								 | 
							
								   // indicator buffers
							 | 
						||
| 
								 | 
							
								   double open[];
							 | 
						||
| 
								 | 
							
								   double high[];
							 | 
						||
| 
								 | 
							
								   double low[];
							 | 
						||
| 
								 | 
							
								   double close[];
							 | 
						||
| 
								 | 
							
								   
							 | 
						||
| 
								 | 
							
								protected:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								   double price(const MqlTick &t) const
							 | 
						||
| 
								 | 
							
								   {
							 | 
						||
| 
								 | 
							
								      switch(chartMode)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								      case _SYMBOL_CHART_MODE_BID:
							 | 
						||
| 
								 | 
							
								         return t.bid;
							 | 
						||
| 
								 | 
							
								      case _SYMBOL_CHART_MODE_LAST:
							 | 
						||
| 
								 | 
							
								         return t.last;
							 | 
						||
| 
								 | 
							
								      case _SYMBOL_CHART_MODE_ASK:
							 | 
						||
| 
								 | 
							
								         return t.ask;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      return 0; // error
							 | 
						||
| 
								 | 
							
								   }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								   // main tick processing on history and online (common part)
							 | 
						||
| 
								 | 
							
								   void calc(const int i, const MqlTick &ticks[], const int skip = 0)
							 | 
						||
| 
								 | 
							
								   {
							 | 
						||
| 
								 | 
							
								      const int n = ArraySize(ticks);
							 | 
						||
| 
								 | 
							
								      for(int j = skip; j < n; ++j)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								         const double p = price(ticks[j]);
							 | 
						||
| 
								 | 
							
								         if(open[i] == EMPTY_VALUE)
							 | 
						||
| 
								 | 
							
								         {
							 | 
						||
| 
								 | 
							
								            open[i] = p;
							 | 
						||
| 
								 | 
							
								         }
							 | 
						||
| 
								 | 
							
								         
							 | 
						||
| 
								 | 
							
								         if(p > high[i] || high[i] == EMPTY_VALUE)
							 | 
						||
| 
								 | 
							
								         {
							 | 
						||
| 
								 | 
							
								            high[i] = p;
							 | 
						||
| 
								 | 
							
								         }
							 | 
						||
| 
								 | 
							
								         
							 | 
						||
| 
								 | 
							
								         if(p < low[i])
							 | 
						||
| 
								 | 
							
								         {
							 | 
						||
| 
								 | 
							
								            low[i] = p;
							 | 
						||
| 
								 | 
							
								         }
							 | 
						||
| 
								 | 
							
								         
							 | 
						||
| 
								 | 
							
								         close[i] = p;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								   }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								   // remember moment of recent online tick processing
							 | 
						||
| 
								 | 
							
								   void updateLastTime(const int n, const MqlTick &ticks[])
							 | 
						||
| 
								 | 
							
								   {
							 | 
						||
| 
								 | 
							
								      lasttime = ticks[n - 1].time_msc;
							 | 
						||
| 
								 | 
							
								      lastcount = 0;
							 | 
						||
| 
								 | 
							
								      for(int k = n - 1; k >= 0; --k)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								         if(ticks[k].time_msc == ticks[n - 1].time_msc) ++lastcount;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								   }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								public:
							 | 
						||
| 
								 | 
							
								   CalcCustomBars(
							 | 
						||
| 
								 | 
							
								      const int bars,
							 | 
						||
| 
								 | 
							
								      const COPY_TICKS type,
							 | 
						||
| 
								 | 
							
								      const ENUM_SYMBOL_CHART_MODE_EXTENDED mode)
							 | 
						||
| 
								 | 
							
								      : limit(bars), tickType(type), chartMode(mode), lasttime(0), lastcount(0)
							 | 
						||
| 
								 | 
							
								   {
							 | 
						||
| 
								 | 
							
								      // register the internal arrays as indicator buffers
							 | 
						||
| 
								 | 
							
								      SetIndexBuffer(0, open);
							 | 
						||
| 
								 | 
							
								      SetIndexBuffer(1, high);
							 | 
						||
| 
								 | 
							
								      SetIndexBuffer(2, low);
							 | 
						||
| 
								 | 
							
								      SetIndexBuffer(3, close);
							 | 
						||
| 
								 | 
							
								      const static string defTitle[] = {"Open;High;Low;Close;"}; // use array for compiler bugfix
							 | 
						||
| 
								 | 
							
								      const static string types[] = {"Bid", "Last", "Ask"};
							 | 
						||
| 
								 | 
							
								      string name = defTitle[0];
							 | 
						||
| 
								 | 
							
								      StringReplace(name, ";", types[chartMode] + ";");
							 | 
						||
| 
								 | 
							
								      PlotIndexSetString(0, PLOT_LABEL, name);
							 | 
						||
| 
								 | 
							
								      IndicatorSetInteger(INDICATOR_DIGITS, _Digits);
							 | 
						||
| 
								 | 
							
								   }
							 | 
						||
| 
								 | 
							
								   
							 | 
						||
| 
								 | 
							
								   // complete initialization
							 | 
						||
| 
								 | 
							
								   void reset()
							 | 
						||
| 
								 | 
							
								   {
							 | 
						||
| 
								 | 
							
								      // most of array is feeded with empty value
							 | 
						||
| 
								 | 
							
								      // except for given number of recent bars for calculations
							 | 
						||
| 
								 | 
							
								      ArrayInitialize(open, EMPTY_VALUE);
							 | 
						||
| 
								 | 
							
								      ArrayFill(open, ArraySize(open) - limit, limit, 0);
							 | 
						||
| 
								 | 
							
								      
							 | 
						||
| 
								 | 
							
								      // replicate this initial state to other buffers
							 | 
						||
| 
								 | 
							
								      ArrayCopy(high, open);
							 | 
						||
| 
								 | 
							
								      ArrayCopy(low, open);
							 | 
						||
| 
								 | 
							
								      ArrayCopy(close, open);
							 | 
						||
| 
								 | 
							
								      
							 | 
						||
| 
								 | 
							
								      // prepare variables for online tick monitoring
							 | 
						||
| 
								 | 
							
								      lasttime = 0;
							 | 
						||
| 
								 | 
							
								      lastcount = 0;
							 | 
						||
| 
								 | 
							
								   }
							 | 
						||
| 
								 | 
							
								   
							 | 
						||
| 
								 | 
							
								   
							 | 
						||
| 
								 | 
							
								   // get ticks for specific bar on the history
							 | 
						||
| 
								 | 
							
								   int createBar(const int i, const datetime &time[])
							 | 
						||
| 
								 | 
							
								   {
							 | 
						||
| 
								 | 
							
								      const int size = ArraySize(time);
							 | 
						||
| 
								 | 
							
								      if(i < 0 || i >= size)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								         return -1; // do nothing: self-protection from out of bound requests
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      
							 | 
						||
| 
								 | 
							
								      open[i] = high[i] = low[i] = close[i] = EMPTY_VALUE;
							 | 
						||
| 
								 | 
							
								      
							 | 
						||
| 
								 | 
							
								      MqlTick ticks[];
							 | 
						||
| 
								 | 
							
								      // prev and next are timestamps of the bar boundaries,
							 | 
						||
| 
								 | 
							
								      // new function PeriodSeconds() will be covered in the chapter about charts
							 | 
						||
| 
								 | 
							
								      const datetime prev = time[i];
							 | 
						||
| 
								 | 
							
								      const datetime next = prev + PeriodSeconds();
							 | 
						||
| 
								 | 
							
								      ResetLastError();
							 | 
						||
| 
								 | 
							
								      const int n = CopyTicksRange(_Symbol, ticks, COPY_TICKS_ALL, prev * 1000, next * 1000 - 1);
							 | 
						||
| 
								 | 
							
								      if(n > -1 && _LastError == 0)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								         if(i == size - 1) // last bar
							 | 
						||
| 
								 | 
							
								         {
							 | 
						||
| 
								 | 
							
								            updateLastTime(n, ticks);
							 | 
						||
| 
								 | 
							
								         }
							 | 
						||
| 
								 | 
							
								         calc(i, ticks);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      else
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								         return -_LastError;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      
							 | 
						||
| 
								 | 
							
								      return n;
							 | 
						||
| 
								 | 
							
								   }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								   // get online ticks on the latest bar, which are not yet processed
							 | 
						||
| 
								 | 
							
								   int updateLastBar(const int total)
							 | 
						||
| 
								 | 
							
								   {
							 | 
						||
| 
								 | 
							
								      MqlTick ticks[];
							 | 
						||
| 
								 | 
							
								      ResetLastError();
							 | 
						||
| 
								 | 
							
								      const int n = CopyTicksRange(_Symbol, ticks, COPY_TICKS_ALL, lasttime);
							 | 
						||
| 
								 | 
							
								      if(n > -1 && _LastError == 0)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								         const int skip = lastcount;
							 | 
						||
| 
								 | 
							
								         updateLastTime(n, ticks);
							 | 
						||
| 
								 | 
							
								         calc(total - 1, ticks, skip);
							 | 
						||
| 
								 | 
							
								         return n - skip;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      return -_LastError;
							 | 
						||
| 
								 | 
							
								   }
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//+------------------------------------------------------------------+
							 | 
						||
| 
								 | 
							
								//| Global variables                                                 |
							 | 
						||
| 
								 | 
							
								//+------------------------------------------------------------------+
							 | 
						||
| 
								 | 
							
								CalcCustomBars chart(BarCount, TickType, ChartMode);
							 | 
						||
| 
								 | 
							
								bool calcDone = false;
							 | 
						||
| 
								 | 
							
								bool selfRefresh = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//+------------------------------------------------------------------+
							 | 
						||
| 
								 | 
							
								//| Custom indicator initialization function                         |
							 | 
						||
| 
								 | 
							
								//+------------------------------------------------------------------+
							 | 
						||
| 
								 | 
							
								int OnInit()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								   if(_Period >= PERIOD_D1)
							 | 
						||
| 
								 | 
							
								   {
							 | 
						||
| 
								 | 
							
								      Alert("Use intraday timeframe, please");
							 | 
						||
| 
								 | 
							
								      return INIT_FAILED;
							 | 
						||
| 
								 | 
							
								   }
							 | 
						||
| 
								 | 
							
								   
							 | 
						||
| 
								 | 
							
								   ENUM_SYMBOL_CHART_MODE mode =
							 | 
						||
| 
								 | 
							
								      (ENUM_SYMBOL_CHART_MODE)SymbolInfoInteger(_Symbol, SYMBOL_CHART_MODE);
							 | 
						||
| 
								 | 
							
								   Print("Chart mode: ", EnumToString(mode));
							 | 
						||
| 
								 | 
							
								   
							 | 
						||
| 
								 | 
							
								   if(mode == SYMBOL_CHART_MODE_BID
							 | 
						||
| 
								 | 
							
								      && ChartMode == _SYMBOL_CHART_MODE_LAST)
							 | 
						||
| 
								 | 
							
								   {
							 | 
						||
| 
								 | 
							
								      Alert("Last price is not available for ", _Symbol);
							 | 
						||
| 
								 | 
							
								   }
							 | 
						||
| 
								 | 
							
								   
							 | 
						||
| 
								 | 
							
								   return INIT_SUCCEEDED;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//+------------------------------------------------------------------+
							 | 
						||
| 
								 | 
							
								//| Custom indicator iteration function                              |
							 | 
						||
| 
								 | 
							
								//+------------------------------------------------------------------+
							 | 
						||
| 
								 | 
							
								int OnCalculate(ON_CALCULATE_STD_FULL_PARAM_LIST)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								   if(prev_calculated == 0)
							 | 
						||
| 
								 | 
							
								   {
							 | 
						||
| 
								 | 
							
								      chart.reset();
							 | 
						||
| 
								 | 
							
								   }
							 | 
						||
| 
								 | 
							
								   
							 | 
						||
| 
								 | 
							
								   calcDone = false;
							 | 
						||
| 
								 | 
							
								   
							 | 
						||
| 
								 | 
							
								   // on every new bar or many new bars (including first event)
							 | 
						||
| 
								 | 
							
								   if(prev_calculated != rates_total)
							 | 
						||
| 
								 | 
							
								   {
							 | 
						||
| 
								 | 
							
								      // process all or new bars
							 | 
						||
| 
								 | 
							
								      for(int i = fmax(prev_calculated, fmax(1, rates_total - BarCount));
							 | 
						||
| 
								 | 
							
								         i < rates_total && !IsStopped(); ++i)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								         const int e = chart.createBar(i, time);
							 | 
						||
| 
								 | 
							
								         if(e <= 0)
							 | 
						||
| 
								 | 
							
								         {
							 | 
						||
| 
								 | 
							
								            PrintFormat("No data on bar %d at %s %s (%d). Refreshing...",
							 | 
						||
| 
								 | 
							
								               i, TimeToString(time[i]), E2S(-e), -e);
							 | 
						||
| 
								 | 
							
								            EventSetTimer(1); // ask terminal to call us in 1 second
							 | 
						||
| 
								 | 
							
								            return 0; // no ticks, can't show
							 | 
						||
| 
								 | 
							
								         }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								   }
							 | 
						||
| 
								 | 
							
								   else // ticks on current bar
							 | 
						||
| 
								 | 
							
								   {
							 | 
						||
| 
								 | 
							
								      // update the latest bar
							 | 
						||
| 
								 | 
							
								      const int e = chart.updateLastBar(rates_total);
							 | 
						||
| 
								 | 
							
								      if(e < 0)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								         PrintFormat("Error, no ticks, %s (%d)", E2S(-e), -e);
							 | 
						||
| 
								 | 
							
								         return 0; // error/warning
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								   }
							 | 
						||
| 
								 | 
							
								   
							 | 
						||
| 
								 | 
							
								   calcDone = true;
							 | 
						||
| 
								 | 
							
								   if(selfRefresh)
							 | 
						||
| 
								 | 
							
								   {
							 | 
						||
| 
								 | 
							
								      Print("Refresh done");
							 | 
						||
| 
								 | 
							
								      selfRefresh = false;
							 | 
						||
| 
								 | 
							
								   }
							 | 
						||
| 
								 | 
							
								   
							 | 
						||
| 
								 | 
							
								   return rates_total; // report number of processed bars for next call
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//+------------------------------------------------------------------+
							 | 
						||
| 
								 | 
							
								//| Timer callback function                                          |
							 | 
						||
| 
								 | 
							
								//+------------------------------------------------------------------+
							 | 
						||
| 
								 | 
							
								void OnTimer()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								   EventKillTimer();
							 | 
						||
| 
								 | 
							
								   if(!calcDone)
							 | 
						||
| 
								 | 
							
								   {
							 | 
						||
| 
								 | 
							
								      Print("Refreshing...");
							 | 
						||
| 
								 | 
							
								      selfRefresh = true;
							 | 
						||
| 
								 | 
							
								      ChartSetSymbolPeriod(0, _Symbol, _Period); // refresh myself
							 | 
						||
| 
								 | 
							
								   }
							 | 
						||
| 
								 | 
							
								   else
							 | 
						||
| 
								 | 
							
								   {
							 | 
						||
| 
								 | 
							
								      Print("Ready before timer");
							 | 
						||
| 
								 | 
							
								   }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								//+------------------------------------------------------------------+
							 |