forked from rosh/MQL5Book
265 lines
9.5 KiB
MQL5
265 lines
9.5 KiB
MQL5
//+------------------------------------------------------------------+
| |||
//| IndSubChart.mq5 |
| |||
//| Copyright (c) 2019-2021, Marketeer |
| |||
//+------------------------------------------------------------------+
| |||
#property copyright "Copyright (c) 2019-2021, Marketeer"
| |||
#property link "https://www.mql5.com/en/users/marketeer"
| |||
#property version "1.1"
| |||
#property description "Display arbitrary symbol quotes (as line/candles/bars) in a subwindow, synchronized by time with current chart."
| |||
| |||
#property indicator_separate_window
| |||
#property indicator_buffers 4
| |||
#property indicator_plots 1
| |||
| |||
#include "..\..\Include\PRTF.mqh"
| |||
#include "..\..\Include\QuoteRefresh.mqh"
| |||
| |||
// inputs
| |||
input string SubSymbol = ""; // Symbol
| |||
input bool Exact = false;
| |||
| |||
// globals
| |||
string symbol;
| |||
ENUM_CHART_MODE mode = 0;
| |||
| |||
// Buffers: OHLC
| |||
double open[];
| |||
double high[];
| |||
double low[];
| |||
double close[];
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Single buffer setup |
| |||
//+------------------------------------------------------------------+
| |||
void InitBuffer(const int index, double &buffer[],
| |||
const ENUM_INDEXBUFFER_TYPE style = INDICATOR_DATA,
| |||
const bool asSeries = false)
| |||
{
| |||
SetIndexBuffer(index, buffer, style);
| |||
ArraySetAsSeries(buffer, asSeries);
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Group of buffers setup |
| |||
//+------------------------------------------------------------------+
| |||
string InitBuffers(const ENUM_CHART_MODE m)
| |||
{
| |||
string title;
| |||
if(m == CHART_LINE)
| |||
{
| |||
InitBuffer(0, close, INDICATOR_DATA, true);
| |||
// hide all other buffers unneeded for line drawing
| |||
InitBuffer(1, high, INDICATOR_CALCULATIONS, true);
| |||
InitBuffer(2, low, INDICATOR_CALCULATIONS, true);
| |||
InitBuffer(3, open, INDICATOR_CALCULATIONS, true);
| |||
title = symbol + " Close";
| |||
}
| |||
else
| |||
{
| |||
InitBuffer(0, open, INDICATOR_DATA, true);
| |||
InitBuffer(1, high, INDICATOR_DATA, true);
| |||
InitBuffer(2, low, INDICATOR_DATA, true);
| |||
InitBuffer(3, close, INDICATOR_DATA, true);
| |||
title = "# Open;# High;# Low;# Close";
| |||
StringReplace(title, "#", symbol);
| |||
}
| |||
return title;
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| All-in-one plot setup |
| |||
//+------------------------------------------------------------------+
| |||
void InitPlot(const int index, const string name, const int style,
| |||
const int width = -1, const int colorx = -1,
| |||
const double empty = EMPTY_VALUE)
| |||
{
| |||
PlotIndexSetInteger(index, PLOT_DRAW_TYPE, style);
| |||
PlotIndexSetString(index, PLOT_LABEL, name);
| |||
PlotIndexSetDouble(index, PLOT_EMPTY_VALUE, empty);
| |||
if(width != -1) PlotIndexSetInteger(index, PLOT_LINE_WIDTH, width);
| |||
if(colorx != -1) PlotIndexSetInteger(index, PLOT_LINE_COLOR, colorx);
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Adjust plot colors according to the chart settings |
| |||
//+------------------------------------------------------------------+
| |||
void SetPlotColors(const int index, const ENUM_CHART_MODE m)
| |||
{
| |||
if(m == CHART_CANDLES)
| |||
{
| |||
PlotIndexSetInteger(index, PLOT_COLOR_INDEXES, 3);
| |||
PlotIndexSetInteger(index, PLOT_LINE_COLOR, 0, (int)ChartGetInteger(0, CHART_COLOR_CHART_LINE)); // rectangle
| |||
PlotIndexSetInteger(index, PLOT_LINE_COLOR, 1, (int)ChartGetInteger(0, CHART_COLOR_CANDLE_BULL)); // up
| |||
PlotIndexSetInteger(index, PLOT_LINE_COLOR, 2, (int)ChartGetInteger(0, CHART_COLOR_CANDLE_BEAR)); // down
| |||
}
| |||
else
| |||
{
| |||
PlotIndexSetInteger(index, PLOT_COLOR_INDEXES, 1);
| |||
PlotIndexSetInteger(index, PLOT_LINE_COLOR, (int)ChartGetInteger(0, CHART_COLOR_CHART_LINE));
| |||
}
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Convert ENUM_CHART_MODE to ENUM_DRAW_TYPE |
| |||
//+------------------------------------------------------------------+
| |||
ENUM_DRAW_TYPE Mode2Style(const ENUM_CHART_MODE m)
| |||
{
| |||
switch(m)
| |||
{
| |||
case CHART_CANDLES: return DRAW_CANDLES;
| |||
case CHART_BARS: return DRAW_BARS;
| |||
case CHART_LINE: return DRAW_LINE;
| |||
}
| |||
return DRAW_NONE;
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Custom indicator initialization function |
| |||
//+------------------------------------------------------------------+
| |||
int OnInit()
| |||
{
| |||
Print("");
| |||
symbol = SubSymbol;
| |||
if(symbol == "") symbol = _Symbol;
| |||
else
| |||
{
| |||
// ensure symbol is available in the market watch
| |||
if(!SymbolSelect(symbol, true))
| |||
{
| |||
return INIT_PARAMETERS_INCORRECT;
| |||
}
| |||
}
| |||
| |||
mode = (ENUM_CHART_MODE)ChartGetInteger(0, CHART_MODE);
| |||
InitPlot(0, InitBuffers(mode), Mode2Style(mode));
| |||
SetPlotColors(0, mode);
| |||
| |||
IndicatorSetString(INDICATOR_SHORTNAME, "SubChart (" + symbol + ")");
| |||
IndicatorSetInteger(INDICATOR_DIGITS, (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS));
| |||
| |||
return INIT_SUCCEEDED;
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Chart event handler function |
| |||
//+------------------------------------------------------------------+
| |||
void OnChartEvent(const int id,
| |||
const long &, const double &, const string &) // unused
| |||
{
| |||
if(id == CHARTEVENT_CHART_CHANGE)
| |||
{
| |||
const ENUM_CHART_MODE newmode = (ENUM_CHART_MODE)ChartGetInteger(0, CHART_MODE);
| |||
if(mode != newmode)
| |||
{
| |||
const ENUM_CHART_MODE oldmode = mode;
| |||
mode = newmode;
| |||
// change buffer mappings and their drawing types for the plot (ad hoc!)
| |||
InitPlot(0, InitBuffers(mode), Mode2Style(mode));
| |||
SetPlotColors(0, mode);
| |||
if(oldmode == CHART_LINE || newmode == CHART_LINE)
| |||
{
| |||
// switching to or from CHART_LINE requires to refresh entire chart
| |||
// including recalculation, because the number of buffers has changed
| |||
Print("Refresh");
| |||
ChartSetSymbolPeriod(0, _Symbol, _Period);
| |||
}
| |||
else
| |||
{
| |||
// while switching between candles and bars it's sufficient to draw
| |||
// the same data in a different manner, because the 4 buffers are the same
| |||
Print("Redraw");
| |||
ChartRedraw();
| |||
}
| |||
}
| |||
}
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Custom indicator iteration function |
| |||
//+------------------------------------------------------------------+
| |||
int OnCalculate(const int rates_total, const int prev_calculated,
| |||
const datetime &time[],
| |||
const double &op[], const double &hi[], const double &lo[], const double &cl[],
| |||
const long &[], const long &[], const int &[]) // unused
| |||
{
| |||
// because we can monitor another symbol we need our own bar count
| |||
// and custom prev_calculated variable
| |||
static bool initialized; // other symbol readiness flag
| |||
static int lastAvailable; // other symbol bar count
| |||
int _prev_calculated = prev_calculated;
| |||
| |||
if(iBars(symbol, _Period) - lastAvailable > 1) // bar gap filled
| |||
{
| |||
_prev_calculated = 0;
| |||
initialized = false;
| |||
lastAvailable = 0;
| |||
}
| |||
| |||
if(_prev_calculated == 0)
| |||
{
| |||
ArrayInitialize(open, EMPTY_VALUE);
| |||
ArrayInitialize(high, EMPTY_VALUE);
| |||
ArrayInitialize(low, EMPTY_VALUE);
| |||
ArrayInitialize(close, EMPTY_VALUE);
| |||
}
| |||
| |||
if(_Symbol != symbol)
| |||
{
| |||
if(!initialized)
| |||
{
| |||
Print("Host ", _Symbol, " ", rates_total, " bars up to ", (string)time[0]);
| |||
Print("Updating ", symbol, " ", lastAvailable, " -> ", iBars(symbol, _Period),
| |||
" / ", (iBars(symbol, _Period) > 0 ? (string)iTime(symbol, _Period, iBars(symbol, _Period) - 1) : "n/a"),
| |||
"... Please wait");
| |||
if(QuoteRefresh(symbol, _Period, time[0]))
| |||
{
| |||
Print("Done");
| |||
initialized = true;
| |||
}
| |||
else
| |||
{
| |||
// OPTION:
| |||
// consider calling EventSetTimer(timeout) to give the system more time
| |||
// to build timeseries and call ChartSetSymbolPeriod in OnTimer handler
| |||
ChartSetSymbolPeriod(0, _Symbol, _Period); // request for async update
| |||
return 0;
| |||
}
| |||
}
| |||
| |||
ArraySetAsSeries(time, true);
| |||
for(int i = 0; i < MathMax(rates_total - _prev_calculated, 1); ++i)
| |||
{
| |||
int x = iBarShift(symbol, _Period, time[i], Exact);
| |||
if(x != -1)
| |||
{
| |||
open[i] = iOpen(symbol, _Period, x);
| |||
high[i] = iHigh(symbol, _Period, x);
| |||
low[i] = iLow(symbol, _Period, x);
| |||
close[i] = iClose(symbol, _Period, x);
| |||
}
| |||
else
| |||
{
| |||
open[i] = high[i] = low[i] = close[i] = EMPTY_VALUE;
| |||
}
| |||
}
| |||
}
| |||
else
| |||
{
| |||
ArraySetAsSeries(op, true);
| |||
ArraySetAsSeries(hi, true);
| |||
ArraySetAsSeries(lo, true);
| |||
ArraySetAsSeries(cl, true);
| |||
for(int i = 0; i < MathMax(rates_total - _prev_calculated, 1); ++i)
| |||
{
| |||
open[i] = op[i];
| |||
high[i] = hi[i];
| |||
low[i] = lo[i];
| |||
close[i] = cl[i];
| |||
}
| |||
}
| |||
| |||
lastAvailable = iBars(symbol, _Period);
| |||
| |||
return rates_total;
| |||
}
| |||
//+------------------------------------------------------------------+
|