328 lines
7.1 KiB
Plaintext
328 lines
7.1 KiB
Plaintext
// Ðåàë-òàéì èíäèêàòîð ëàãà êîòèðîâîê òåðìèíàëà
|
|
|
|
#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);
|
|
} |