293 行
8.7 KiB
MQL5
293 行
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");
| |||
}
| |||
}
| |||
//+------------------------------------------------------------------+
|