0 Views
0 Downloads
0 Favorites
Ping
// Ðåàë-òàéì èíäèêàòîð ëàãà êîòèðîâîê òåðìèíàëà

#define HISTOGRAM_VIEW // Çàêîììåíòèðîâàòü, ÷òîáû ñìåíèòü âèä èíäèêàòîðà ñ ãèñòîãðàìì íà ñâå÷è

#property indicator_separate_window

const bool Init = IndicatorSetDouble(INDICATOR_MINIMUM, 0) && IndicatorSetInteger(INDICATOR_DIGITS, 3) && IndicatorSetString(INDICATOR_SHORTNAME, "PING");

class BUFFER
{
private:
  static int BuffersTotal;
  const int Index;
  int Pos;

public:
  double Buffer[];

  BUFFER( void ) : Index(BUFFER::BuffersTotal++), Pos(0)
  {
    ::SetIndexBuffer(this.Index, this.Buffer);
  }

  // Øèðèíà ãèñòîãðàììû
  bool SetScale( void ) const
  {
    const int Width = 9 / (1 << (5 - (int)::ChartGetInteger(0, CHART_SCALE)));

    return((Width != ::PlotIndexGetInteger(this.Index, PLOT_LINE_WIDTH)) && ::PlotIndexSetInteger(this.Index, PLOT_LINE_WIDTH, Width));
  }

  void Fill( const double Value = EMPTY_VALUE )
  {
    ::ArrayInitialize(this.Buffer, Value);
  }

  BUFFER* operator[]( const int iPos )
  {
    this.Pos = iPos;

    return(&this);
  }

  void operator =( const double Value )
  {
    this.Buffer[this.Pos] = Value;
  }

  bool operator >( const double Value ) const
  {
    return(this.Buffer[this.Pos] > Value);
  }

  bool operator <( const double Value ) const
  {
    return(this.Buffer[this.Pos] < Value);
  }
};

static int BUFFER::BuffersTotal = 0;

class INDICATOR
{
protected:
  const int Amount;
  BUFFER Buffers[];

  int PrevPos;

  bool IsNewBar( int &Pos )
  {
    if (!PrevPos)
      this.Fill();

    Pos = ::ArraySize(this.Buffers[0].Buffer) - 1;

    const bool Res = (Pos != this.PrevPos);

    this.PrevPos = Pos;

    return(Res);
  }

public:
  INDICATOR( const int iAmount ) : PrevPos(0), Amount(iAmount) {}

  void Fill( const double Value = EMPTY_VALUE )
  {
    for (int i = 0; i < this.Amount; i++)
      this.Buffers[i].Fill(Value);
  }

  void SetScale( void ) const
  {
    bool Res = false;

    for (int i = 0; i < this.Amount; i++)
      Res |= this.Buffers[i].SetScale();

    if (Res)
      ::ChartRedraw();
  }

  virtual void Add( const double Value );
};

class CANDLES : public INDICATOR
{
public:
  CANDLES( void ) : INDICATOR(::ArrayResize(this.Buffers, 4)) {}

#define OPEN  0
#define HIGH  1
#define LOW   2
#define CLOSE 3

  virtual void Add( const double Value )
  {
    int Pos;

    if (IsNewBar(Pos))
    {
      this.Buffers[OPEN][Pos] = Value;
      this.Buffers[HIGH][Pos] = Value;
      this.Buffers[LOW][Pos] = Value;
    }
    else if (this.Buffers[HIGH][Pos] < Value)
      this.Buffers[HIGH][Pos] = Value;
    else if (this.Buffers[LOW][Pos] > Value)
      this.Buffers[LOW][Pos] = Value;

    this.Buffers[CLOSE][Pos] = Value;
  }
};

class AVERAGE : public INDICATOR
{
protected:
  double Sum;
  int Count;

public:
  AVERAGE( void ) : INDICATOR(::ArrayResize(this.Buffers, 1)) {}

  virtual void Add( const double Value )
  {
    int Pos;

    if (this.IsNewBar(Pos))
    {
      this.Sum = 0;
      this.Count = 0;
    }

    this.Sum += Value;
    this.Count++;

    this.Buffers[0][Pos] = this.Sum / this.Count;

    return;
  }
};

class HISTOGRAM : public INDICATOR
{
protected:
  double Sum;
  int Count;

public:
  HISTOGRAM( void ) : INDICATOR(::ArrayResize(this.Buffers, 3)) {}

#define MAX 0
#define AVG 1
#define MIN 2

  virtual void Add( const double Value )
  {
    int Pos;

    if (IsNewBar(Pos))
    {
      this.Buffers[MAX][Pos] = Value;
      this.Buffers[MIN][Pos] = Value;

      this.Sum = 0;
      this.Count = 0;
    }
    else if (this.Buffers[MAX][Pos] < Value)
      this.Buffers[MAX][Pos] = Value;
    else if (this.Buffers[MIN][Pos] > Value)
      this.Buffers[MIN][Pos] = Value;

    this.Sum += Value;
    this.Count++;

    this.Buffers[AVG][Pos] = this.Sum / this.Count;
  }
};

#ifdef HISTOGRAM_VIEW
  #property indicator_buffers 3
  #property indicator_plots   3

  #property indicator_label1 "Ping Max"
  #property indicator_type1  DRAW_HISTOGRAM
  #property indicator_color1 Blue

  #property indicator_label2  "Ping Average"
  #property indicator_type2  DRAW_HISTOGRAM
  #property indicator_color2 Lime

  #property indicator_label3 "Ping Min"
  #property indicator_type3  DRAW_HISTOGRAM
  #property indicator_color3 Red

  HISTOGRAM Histogram;

  void OnChartEvent( const int id, const long&, const double&, const string& )
  {
    if (id == CHARTEVENT_CHART_CHANGE)
      Histogram.SetScale();
  }
#else // HISTOGRAM_VIEW
  #property indicator_buffers 5
  #property indicator_plots   2

  #property indicator_label1 "Ping Open;Ping High;Ping Low;Ping Close"
  #property indicator_type1  DRAW_CANDLES
  #property indicator_color1 clrGreen, clrWhite, clrRed

  #property indicator_label2 "Ping Average"
  #property indicator_type2  DRAW_LINE
  #property indicator_color2 clrBlue
  #property indicator_width2 2

  CANDLES Candles;
  AVERAGE Average;
#endif // HISTOGRAM_VIEW

#include <TypeToBytes.mqh> // https://www.mql5.com/ru/code/16280

// Ïîëó÷åíèå ñâåæåãî òèêà ñ âåëè÷èíîé âíóòðåííåãî ëàãà êîòèðîâêè (â ìèëëèñåêóíäàõ)
bool SymbolInfoTick( const string &Symb, MqlTick &Tick, double &Ping )
{
  static const bool IsTester = (MQLInfoInteger(MQL_TESTER) || MQLInfoInteger(MQL_OPTIMIZATION));

  static MqlTick PrevTick = {0};
  static ulong PrevTime = 0;

  const ulong NowTime = IsTester ? 0 : GetMicrosecondCount();
  const bool Res = SymbolInfoTick(Symb, Tick);

  if ((!IsTester) && Res)
  {
    if (_R(Tick) != PrevTick)
    {
      Ping = PrevTime ? MathAbs(Tick.time_msc - PrevTick.time_msc - (NowTime - PrevTime) / 1e3) : 0;

      PrevTime = NowTime;
      PrevTick = Tick;
    }
    else
      Ping = 0;
  }

  return(Res);
}

string GetTickFlag( uint tickflag )
{
  string flag = "";

#define TICKFLAG_MACRO(A) flag += ((bool)(tickflag & TICK_FLAG_##A)) ? " TICK_FLAG_" + #A : "";
  TICKFLAG_MACRO(BID)
  TICKFLAG_MACRO(ASK)
  TICKFLAG_MACRO(LAST)
  TICKFLAG_MACRO(VOLUME)
  TICKFLAG_MACRO(BUY)
  TICKFLAG_MACRO(SELL)
#undef TICKFLAG_MACRO

  if (flag == "")
    flag = " FLAG_UNKNOWN (" + (string)tickflag + ")";

  return(flag);
}

#define TOSTRING(A) " " + #A + " = " + (string)Tick.A

string TickToString( const MqlTick &Tick )
{
  return(TOSTRING(time) + "." + (string)IntegerToString(Tick.time_msc %1000, 3, '0') +
         TOSTRING(bid) + TOSTRING(ask) + TOSTRING(last)+ TOSTRING(volume) + GetTickFlag(Tick.flags));
}

#undef TOSTRING

#define TOSTRING(A) "\n" + #A + " = " + (string)(A)

int OnCalculate( const int rates_total, const int prev_calculated, const int, const double &[] )
// void OnTick()
{
  static MqlTick Tick;
  static double Ping;

  static const bool IsIndicator = (MQLInfoInteger(MQL_PROGRAM_TYPE) == PROGRAM_INDICATOR);
  static int Count = 0;

  if (SymbolInfoTick(_Symbol, Tick, Ping) &&
      (!IsIndicator || (Count++ > 1))) // SymbolInfoTick â èíäèêàòîðàõ òðåáóåò "ðàçîãðåâà" â âèäå íåñêîëüêèõ ïåðâûõ Calculate-ñîáûòèé
  {
#ifdef HISTOGRAM_VIEW
    Histogram.Add(Ping);
#else // HISTOGRAM_VIEW
    Candles.Add(Ping);

    Average.Add(Ping);
#endif // HISTOGRAM_VIEW

    Comment(__FILE__ + TOSTRING(AccountInfoString(ACCOUNT_SERVER)) + TOSTRING(TerminalInfoInteger(TERMINAL_PING_LAST)) +
            "\nLast Ping = " + DoubleToString(Ping, 3) + "\nLast Tick =" + TickToString(Tick));

//    Print(TickToString(Tick) + TOSTRING(Ping));
  }

  return(rates_total);
}

Comments

Markdown supported. Formatting help

Markdown Formatting Guide

Element Markdown Syntax
Heading # H1
## H2
### H3
Bold **bold text**
Italic *italicized text*
Link [title](https://www.example.com)
Image ![alt text](image.jpg)
Code `code`
Code Block ```
code block
```
Quote > blockquote
Unordered List - Item 1
- Item 2
Ordered List 1. First item
2. Second item
Horizontal Rule ---