Files
Bell-PriceActionWithEma-EA/Experts/MotherBarBreakout.mq5
T
2026-05-16 03:33:17 +07:00

166 lines
5.3 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//+------------------------------------------------------------------+
//| MotherBarBreakout.mq5 |
//| M15: thân nến >= K × TB thân N nến liền trước |
//+------------------------------------------------------------------+
#property copyright "MotherBarBreakout"
#property version "2.01"
#property description "M15 large body: body >= K × average of N prior bodies. Marks on chart."
input ENUM_TIMEFRAMES InpTf = PERIOD_M15;
input int InpAvgPriorBodies = 10; // TB của 10 thân nến liền trước
input double InpBodyVsAvgMult = 3.0; // thân hiện tại >= hệ số này × TB (vd 3 = gấp 3)
input int InpScanBarsOnInit = 300;
input bool InpClearMarksOnInit = true;
input color InpClrBullBar = clrDodgerBlue; // nến tăng (chỉ phân biệt hướng nến, không lọc trend)
input color InpClrBearBar = clrDarkOrange;
input int InpArrowSize = 2;
input int InpArrowOffsetPts = 20;
const string OBJ_PFX = "MBB_";
datetime g_lastBarTime = 0;
//+------------------------------------------------------------------+
double BodyAbs(const string sym, const ENUM_TIMEFRAMES tf, const int sh)
{
return MathAbs(iClose(sym, tf, sh) - iOpen(sym, tf, sh));
}
//+------------------------------------------------------------------+
//| Thân nến sh >= InpBodyVsAvgMult × TB thân sh+1 .. sh+n //
//+------------------------------------------------------------------+
bool IsBodyLargerThanPriorAverage(const string sym, const ENUM_TIMEFRAMES tf,
const int sh, const int nAvg)
{
if(nAvg < 1)
return false;
if(InpBodyVsAvgMult <= 0.0)
return false;
const int need = sh + nAvg + 1;
if(Bars(sym, tf) < need)
return false;
double sum = 0.0;
for(int k = 1; k <= nAvg; k++)
sum += BodyAbs(sym, tf, sh + k);
const double avg = sum / (double)nAvg;
const double body = BodyAbs(sym, tf, sh);
const double threshold = InpBodyVsAvgMult * avg;
return (body >= threshold);
}
//+------------------------------------------------------------------+
void DeleteMarkersByPrefix()
{
const long chart_id = ChartID();
const int n = ObjectsTotal(chart_id, 0, -1);
for(int i = n - 1; i >= 0; i--)
{
const string name = ObjectName(chart_id, i, 0, -1);
if(StringFind(name, OBJ_PFX) == 0)
ObjectDelete(chart_id, name);
}
}
//+------------------------------------------------------------------+
void PlaceMarker(const string sym, const ENUM_TIMEFRAMES tf, const int sh)
{
const datetime t = iTime(sym, tf, sh);
const string name = OBJ_PFX + IntegerToString((long)t);
const long chart_id = ChartID();
if(ObjectFind(chart_id, name) >= 0)
return;
const bool bull = (iClose(sym, tf, sh) >= iOpen(sym, tf, sh));
const int dig = (int)SymbolInfoInteger(sym, SYMBOL_DIGITS);
const double pt = SymbolInfoDouble(sym, SYMBOL_POINT);
double price = 0.0;
if(bull)
price = iLow(sym, tf, sh) - InpArrowOffsetPts * pt;
else
price = iHigh(sym, tf, sh) + InpArrowOffsetPts * pt;
price = NormalizeDouble(price, dig);
if(!ObjectCreate(chart_id, name, OBJ_ARROW, 0, t, price))
return;
ObjectSetInteger(chart_id, name, OBJPROP_ARROWCODE, bull ? 233 : 234);
ObjectSetInteger(chart_id, name, OBJPROP_COLOR, bull ? InpClrBullBar : InpClrBearBar);
ObjectSetInteger(chart_id, name, OBJPROP_WIDTH, InpArrowSize);
ObjectSetInteger(chart_id, name, OBJPROP_ANCHOR, bull ? ANCHOR_TOP : ANCHOR_BOTTOM);
ObjectSetInteger(chart_id, name, OBJPROP_BACK, false);
ObjectSetInteger(chart_id, name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(chart_id, name, OBJPROP_HIDDEN, false);
}
//+------------------------------------------------------------------+
void TryMarkAtShift(const string sym, const ENUM_TIMEFRAMES tf, const int sh)
{
if(IsBodyLargerThanPriorAverage(sym, tf, sh, InpAvgPriorBodies))
PlaceMarker(sym, tf, sh);
}
//+------------------------------------------------------------------+
void ScanHistory(const string sym, const ENUM_TIMEFRAMES tf, const int maxBars)
{
if(maxBars <= 0)
return;
const int n = MathMin(maxBars, Bars(sym, tf) - InpAvgPriorBodies - 2);
for(int sh = 1; sh <= n; sh++)
TryMarkAtShift(sym, tf, sh);
}
//+------------------------------------------------------------------+
int OnInit()
{
if(InpAvgPriorBodies < 1)
{
Print("[MBB] InpAvgPriorBodies phải >= 1");
return INIT_FAILED;
}
if(InpBodyVsAvgMult <= 0.0)
{
Print("[MBB] InpBodyVsAvgMult phải > 0");
return INIT_FAILED;
}
if(InpClearMarksOnInit)
DeleteMarkersByPrefix();
ScanHistory(_Symbol, InpTf, InpScanBarsOnInit);
g_lastBarTime = iTime(_Symbol, InpTf, 0);
ChartRedraw();
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
DeleteMarkersByPrefix();
ChartRedraw();
}
//+------------------------------------------------------------------+
void OnTick()
{
const datetime t0 = iTime(_Symbol, InpTf, 0);
if(t0 == 0 || t0 == g_lastBarTime)
return;
g_lastBarTime = t0;
TryMarkAtShift(_Symbol, InpTf, 1);
ChartRedraw();
}
//+------------------------------------------------------------------+