forked from airat77786/MQL5Book
210 lines
6.9 KiB
MQL5
210 lines
6.9 KiB
MQL5
//+------------------------------------------------------------------+
| |||
//| UseM1MA.mq5 |
| |||
//| Copyright (c) 2020-2021, Marketeer |
| |||
//| https://www.mql5.com/en/users/marketeer |
| |||
//| |
| |||
//| For simplified version look at UseM1MASimple.mq5 |
| |||
//+------------------------------------------------------------------+
| |||
#property copyright "Copyright (c) 2020-2021 Marketeer"
| |||
#property link "https://www.mql5.com/en/users/marketeer"
| |||
#property version "1.1"
| |||
#property description "M1-based Moving Average provides more adequate estimation of average price per bar compared to standard price types (close, open, median, typical, weighted, etc)."
| |||
| |||
// drawing settings
| |||
#property indicator_chart_window
| |||
#property indicator_buffers 1
| |||
#property indicator_plots 1
| |||
| |||
#property indicator_type1 DRAW_LINE
| |||
#property indicator_color1 clrDodgerBlue
| |||
#property indicator_width1 2
| |||
#property indicator_style1 STYLE_SOLID
| |||
| |||
// inputs
| |||
input uint _BarLimit = 100; // BarLimit
| |||
input uint BarPeriod = 1;
| |||
input ENUM_APPLIED_PRICE M1Price = PRICE_CLOSE;
| |||
| |||
#include "..\..\Include\IndCommon.mqh"
| |||
| |||
// indicator buffer
| |||
double Buffer[];
| |||
| |||
// globals
| |||
int Handle;
| |||
int BarLimit;
| |||
bool PendingRefresh;
| |||
| |||
const string MyName = "M1MA (" + StringSubstr(EnumToString(M1Price), 6)
| |||
+ "," + (string)BarPeriod + "[" + (string)(PeriodSeconds() / 60) + "])";
| |||
const uint P = PeriodSeconds() / 60 * BarPeriod;
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Custom indicator initialization function |
| |||
//+------------------------------------------------------------------+
| |||
int OnInit()
| |||
{
| |||
IndicatorSetString(INDICATOR_SHORTNAME, MyName);
| |||
IndicatorSetInteger(INDICATOR_DIGITS, _Digits);
| |||
| |||
SetIndexBuffer(0, Buffer);
| |||
| |||
// only simple MA makes sense to get average price of M1's within single bar of a higher timeframe
| |||
Handle = iMA(_Symbol, PERIOD_M1, P, 0, MODE_SMA, M1Price);
| |||
| |||
return Handle != INVALID_HANDLE ? INIT_SUCCEEDED : INIT_FAILED;
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Custom indicator iteration function |
| |||
//+------------------------------------------------------------------+
| |||
int OnCalculate(ON_CALCULATE_STD_FULL_PARAM_LIST)
| |||
{
| |||
if(prev_calculated == 0)
| |||
{
| |||
Print("Start");
| |||
// fresh start or history update
| |||
ArrayInitialize(Buffer, EMPTY_VALUE);
| |||
if(_BarLimit == 0
| |||
|| _BarLimit > (uint)rates_total)
| |||
{
| |||
BarLimit = rates_total;
| |||
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, 0);
| |||
}
| |||
else
| |||
{
| |||
BarLimit = (int)_BarLimit;
| |||
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, rates_total - BarLimit);
| |||
}
| |||
}
| |||
else
| |||
{
| |||
// new bars initialization
| |||
for(int i = fmax(prev_calculated - 1, (int)(rates_total - BarLimit)); i < rates_total; ++i)
| |||
{
| |||
Buffer[i] = EMPTY_VALUE;
| |||
}
| |||
}
| |||
| |||
// wait for M1-indicator to get ready
| |||
if(BarsCalculated(Handle) != iBars(_Symbol, PERIOD_M1))
| |||
{
| |||
if(prev_calculated == 0)
| |||
{
| |||
// if not, initiate a self-refersh
| |||
Print("Requesting refresh");
| |||
EventSetTimer(1);
| |||
PendingRefresh = true;
| |||
}
| |||
return prev_calculated;
| |||
}
| |||
| |||
// clear pending requests for self-refresh (if any)
| |||
PendingRefresh = false;
| |||
| |||
// keep track of M1 data available history
| |||
int noDataBar = -1;
| |||
int trackDataBar = -1;
| |||
| |||
// main calculation loop
| |||
for(int i = fmax(prev_calculated - 1, (int)(rates_total - BarLimit)); i < rates_total; ++i)
| |||
{
| |||
static double result[1];
| |||
static double exact[1];
| |||
| |||
// get latest M1 bar corresponding to i-th bar of current timeframe
| |||
const datetime dt = time[i] + PeriodSeconds() - 60;
| |||
const int bar = iBarShift(_Symbol, PERIOD_M1, dt);
| |||
| |||
// check if M1 timeseries exist on this bar of current timeframe
| |||
ResetLastError();
| |||
datetime x = iTime(_Symbol, PERIOD_M1, bar);
| |||
| |||
// if M1 data is missing, remember bar number to prevent drawing
| |||
if(bar == -1 || _LastError != 0 || x > dt)
| |||
{
| |||
if(trackDataBar != -1) // hole in near history (need a refresh)
| |||
{
| |||
Print("Hole detected, refresh");
| |||
EventSetTimer(1);
| |||
PendingRefresh = true;
| |||
return prev_calculated;
| |||
}
| |||
noDataBar = i; // update on old bars without data
| |||
continue;
| |||
}
| |||
else if(noDataBar != -1)
| |||
{
| |||
trackDataBar = i; // update on latest bars with data
| |||
}
| |||
| |||
// find the number of M1 bars mapped into unfinished bar 0
| |||
// it's used to adjust value on the latest bar
| |||
int prev = 0;
| |||
if(bar == 0)
| |||
{
| |||
prev = iBarShift(_Symbol, PERIOD_M1, time[i]);
| |||
if(CopyBuffer(Handle, 0, prev, 1, exact) != 1)
| |||
{
| |||
prev = 0;
| |||
}
| |||
}
| |||
| |||
// request MA from M1
| |||
if(CopyBuffer(Handle, 0, bar, 1, result) == 1)
| |||
{
| |||
// for all bars except 0-th use values intact
| |||
if(bar > 0 || prev == 0)
| |||
{
| |||
Buffer[i] = result[0];
| |||
}
| |||
else
| |||
{
| |||
// for 0-th bar adjust the value for the part
| |||
// of M1 bars extended into 1-st bar;
| |||
// for example, with P=60 (H1 chart) the averaging at 15:15
| |||
// is made of M1 bars from 14:15 to 15:15
| |||
/*
| |||
now
| |||
V
| |||
<-- exact --> V
| |||
<-- result -->.
| |||
| 1 | . 0 }
| |||
unfinished bar
| |||
*/
| |||
| |||
// (P - prev) elements are common
| |||
const double fix = (result[0] * P + exact[0] * prev * BarPeriod - exact[0] * P) / prev / BarPeriod;
| |||
Buffer[i] = (fix * prev * BarPeriod + result[0] * (P - prev * BarPeriod)) / P;
| |||
}
| |||
}
| |||
else
| |||
{
| |||
Print("CopyBuffer failed: ", _LastError);
| |||
return prev_calculated;
| |||
}
| |||
}
| |||
| |||
// if M1 history is shorter than current timeframe, limit drawing
| |||
if(noDataBar != -1)
| |||
{
| |||
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, noDataBar + 1 + BarPeriod);
| |||
Print("Limits: ", noDataBar, " ", rates_total);
| |||
}
| |||
| |||
return rates_total;
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Timer handler |
| |||
//+------------------------------------------------------------------+
| |||
void OnTimer()
| |||
{
| |||
EventKillTimer();
| |||
if(PendingRefresh)
| |||
{
| |||
Print("Refreshing");
| |||
ChartSetSymbolPeriod(0, _Symbol, _Period);
| |||
}
| |||
}
| |||
//+------------------------------------------------------------------+
|