Pon "Me gusta" y sigue las noticias
Deje un enlace a él, ¡qué los demás también lo valoren!
Evalúe su trabajo en el terminal MetaTrader 5
- Visualizaciones:
- 11
- Ranking:
- Publicado:
-
¿Necesita un robot o indicador basado en este código? Solicítelo en la bolsa freelance Pasar a la bolsa
Brechas de valor razonable por inversión: detecta brechas de valor razonable, las señala cuando el cuerpo de una vela cierra atravesándolas (inversión) y, a continuación, indica los rebotes desde la zona invertida. Lógica de cierre de barras, sin repintado. Línea media, eliminación de la zona rellena, colores ajustables y alertas (ventana emergente, sonido, notificación push, correo electrónico, Telegram).
#property copyright "Inversion Fair Value Gaps"
#property version "1.00"
#property description "Inversion fair value gaps with signals. Bar-close, no repaint."
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots 2
#property indicator_type1 DRAW_NONE
#property indicator_label1 "Bounce Up"
#property indicator_type2 DRAW_NONE
#property indicator_label2 "Bounce Down"
enum ENUM_IFVG_BOUNCE
{
BOUNCE_CLOSE = 0, // Close
BOUNCE_WICK = 1 // Wick
};
enum ENUM_IFVG_LABEL
{
LABEL_SMALL = 0, // Small
LABEL_MEDIUM = 1, // Medium
LABEL_LARGE = 2 // Large
};
//---------------------------------------------------------------- inputs (engine first: the dump script passes this prefix; no "input group": it shifts iCustom)
// --- 1. Parameters
input int InpBoxCount = 5; // Show Last
input ENUM_IFVG_BOUNCE InpBounce = BOUNCE_CLOSE; // Detect Bounce
input int InpHistoryBars = 20000; // History bars (loaded and computed)
// --- 2. Filter
input int InpAtrLength = 200; // ATR Length
input double InpAtrMultiplier = 0.25; // ATR Multiplier
input bool InpRemoveFilled = true; // Remove Filled iFVG
// --- 3. Parity
input bool InpWriteParity = false; // Write parity CSV files (MQL5\Files)
// --- 4. Style
input bool InpShowBull = true; // Show Bull IFVG
input bool InpShowBear = true; // Show Bear IFVG
input bool InpShowMidLine = true; // Show MidLine
input bool InpShowSignals = true; // Show Bounce Signals
input int InpExtend = 50; // Extend Active IFVG (bars, 0-100)
input ENUM_IFVG_LABEL InpLabelSize = LABEL_MEDIUM; // Signal Label Size
input color InpBullColor = clrDarkGreen; // Bull Colour
input color InpBearColor = clrMaroon; // Bear Colour
input color InpLineColor = clrGray; // Mid line colour
input int InpOpacity = 40; // Opacity (5-100, blended with the chart background)
// --- 5. Alerts
input bool InpAlertBounce = true; // Alert on bounce
input bool InpAlertInversion = false; // Alert on inversion
input bool InpUsePopup = true; // Popup (Alert)
input bool InpUseSound = true; // Sound
input string InpSoundFile = "alert.wav"; // Sound file (terminal Sounds folder)
input bool InpUsePush = false; // Push notification (MetaQuotes ID)
input bool InpUseMail = false; // E-mail (terminal mail settings)
input string InpTelegramToken = ""; // Telegram: bot token (empty = off, allow api.telegram.org in WebRequest)
input string InpTelegramChat = ""; // Telegram: chat id
input bool InpLogEvents = true; // Log every event (Experts tab)
//---------------------------------------------------------------- engine state (single instance per chart)
#define EV_CREATED 0
#define EV_INVERTED 1
#define EV_UP 2
#define EV_DOWN 3
#define EV_FILLED 4
struct Zone
{
int left;
double top;
int right;
double bot;
double mid;
int dir; // +1 support, -1 resistance (flipped when the gap inverts)
int state; // 0 = just inverted, 1 = active inversion, -1 = filled and kept
int xval; // bar where the gap became an inversion
};
struct Lab { int zone; int x; double y; int dir; };
int gBuffer = 500; //caps each list at 500 entries
double gO[], gH[], gL[], gC[], gCTop[], gCBot[];
datetime gT[];
int gCount = 0; // closed bars fed
int gFirst = 0; // chart index of engine bar 0
double gAtr = 0, gPrevClose = 0;
bool gLive = false;
int gEvents = 0;
Zone gZ[]; // every zone ever created; the lists below hold indexes into it
int gZn = 0;
int gBullFvg[], gBearFvg[]; // open gaps
int gBullInv[], gBearInv[]; // inverted zones, by the list of their origin
Lab gLabs[];
double gBounceSignal = 0, gIsIfvg = 0;
double gUpBuf[], gDnBuf[];
string gDrawn[];
int gBarsFile = INVALID_HANDLE, gEvFile = INVALID_HANDLE;
//---------------------------------------------------------------- helpers
double O(int k) { return gO[gCount - 1 - k]; }
double H(int k) { return gH[gCount - 1 - k]; }
double L(int k) { return gL[gCount - 1 - k]; }
double C(int k) { return gC[gCount - 1 - k]; }
double CTop(int k) { return gCTop[gCount - 1 - k]; }
double CBot(int k) { return gCBot[gCount - 1 - k]; }
string TimeStr(datetime t)
{
MqlDateTime dt; TimeToStruct(t, dt);
return StringFormat("%04d-%02d-%02d %02d:%02d:%02d", dt.year, dt.mon, dt.day, dt.hour, dt.min, dt.sec);
}
string Px(double p) { return DoubleToString(p, _Digits); }
void ListInsert0(int &a[], int v)
{
int n = ArraySize(a);
ArrayResize(a, n + 1, 256);
for(int i = n; i > 0; i--) a[i] = a[i - 1];
a[0] = v;
}
void ListRemoveAt(int &a[], int i)
{
int n = ArraySize(a);
for(int k = i; k < n - 1; k++) a[k] = a[k + 1];
ArrayResize(a, n - 1, 256);
}
void ResetEngine()
{
gCount = 0; gAtr = 0; gPrevClose = 0; gEvents = 0; gZn = 0;
gBounceSignal = 0; gIsIfvg = 0;
ArrayResize(gO, 0); ArrayResize(gH, 0); ArrayResize(gL, 0); ArrayResize(gC, 0);
ArrayResize(gCTop, 0); ArrayResize(gCBot, 0); ArrayResize(gT, 0);
ArrayResize(gZ, 0); ArrayResize(gLabs, 0);
ArrayResize(gBullFvg, 0); ArrayResize(gBearFvg, 0); ArrayResize(gBullInv, 0); ArrayResize(gBearInv, 0);
ObjectsDeleteAll(0, "IFVG_");
ArrayResize(gDrawn, 0);
}
int NewZone(int left, double bot, int right, double top, double mid, int dir)
{
ArrayResize(gZ, gZn + 1, 1024);
gZ[gZn].left = left; gZ[gZn].top = top; gZ[gZn].right = right; gZ[gZn].bot = bot; gZ[gZn].mid = mid;
gZ[gZn].dir = dir; gZ[gZn].state = 0; gZ[gZn].xval = INT_MIN;
return gZn++;
}
void AddLab(int zone, int x, double y, int dir)
{
int k = ArraySize(gLabs);
ArrayResize(gLabs, k + 1, 1024);
gLabs[k].zone = zone; gLabs[k].x = x; gLabs[k].y = y; gLabs[k].dir = dir;
}
//---------------------------------------------------------------- events
string Token(int type)
{
switch(type)
{
case EV_CREATED: return "G";
case EV_INVERTED: return "I";
case EV_UP: return "U";
case EV_DOWN: return "D";
default: return "F";
}
}
string Describe(int type, int n, int z)
{
string when = TimeStr(gT[n]);
string span = Px(gZ[z].bot) + " - " + Px(gZ[z].top);
switch(type)
{
case EV_CREATED: return when + " fair value gap " + span;
case EV_INVERTED: return when + " INVERSION: the zone " + span + " becomes " + (gZ[z].dir == 1 ? "resistance" : "support");
case EV_UP: return when + " BULLISH BOUNCE off " + span + " (close " + Px(gC[n]) + ")";
case EV_DOWN: return when + " BEARISH BOUNCE off " + span + " (close " + Px(gC[n]) + ")";
default: return when + " zone " + span + " filled";
}
}
bool Wanted(int type)
{
if(type == EV_UP || type == EV_DOWN) return InpAlertBounce;
if(type == EV_INVERTED) return InpAlertInversion;
return false;
}
string JsonEscape(string s)
{
string r = s;
StringReplace(r, "\\", "\\\\");
StringReplace(r, "\"", "\\\"");
StringReplace(r, "\n", "\\n");
StringReplace(r, "\r", "");
StringReplace(r, "\t", "\\t");
return r;
}
void SendTelegram(string text)
{
string url = "https://api.telegram.org/bot" + InpTelegramToken + "/sendMessage";
string body = "{\"chat_id\":\"" + JsonEscape(InpTelegramChat) + "\",\"text\":\"" + JsonEscape(text) + "\"}";
char data[]; char result[]; string headers;
int len = StringToCharArray(body, data, 0, WHOLE_ARRAY, CP_UTF8) - 1;
if(len > 0) ArrayResize(data, len);
int code = WebRequest("POST", url, "Content-Type: application/json\r\n", 5000, data, result, headers);
if(code != 200) Print("[IFVG] Telegram KO : ", code, " ", GetLastError());
}
void Notify(int type, string line)
{
if(InpLogEvents) Print("[IFVG] ", line);
if(!Wanted(type) || MQLInfoInteger(MQL_TESTER) != 0) return;
string title = "IFVG " + _Symbol + " : " + (type == EV_INVERTED ? "inversion" : type == EV_UP ? "BOUNCE ▲" : "BOUNCE ▼");
if(InpUsePopup) Alert(title, " | ", line);
if(InpUseSound) PlaySound(InpSoundFile);
if(InpUsePush) SendNotification(title + " | " + line);
if(InpUseMail) SendMail(title, line);
if(StringLen(InpTelegramToken) > 0 && StringLen(InpTelegramChat) > 0) SendTelegram(title + "\n" + line);
}
void Emit(int type, int n, int z)
{
gEvents++;
if(gEvFile != INVALID_HANDLE)
FileWrite(gEvFile, IntegerToString(n) + "," + TimeStr(gT[n]) + "," + Token(type) + "," + DoubleToString(gZ[z].top, 8) + "," + DoubleToString(gZ[z].bot, 8));
if(gLive) Notify(type, Describe(type, n, z));
else if(InpLogEvents && (type == EV_UP || type == EV_DOWN)) Print("[IFVG] ", Describe(type, n, z));
int chart = gFirst + n;
if(type == EV_UP && chart < ArraySize(gUpBuf)) gUpBuf[chart] = gZ[z].bot;
if(type == EV_DOWN && chart < ArraySize(gDnBuf)) gDnBuf[chart] = gZ[z].top;
}
//---------------------------------------------------------------- engine (IfvgEngine.cs)
// NinjaTrader's ATR: seeded with the first range, then a running mean over min(bar+1, period)
void UpdateAtr(double high, double low, double close, int n)
{
if(n == 0) { gAtr = high - low; gPrevClose = close; return; }
double tr = MathMax(high - low, MathMax(MathAbs(high - gPrevClose), MathAbs(low - gPrevClose)));
int p = MathMin(n + 1, MathMax(1, InpAtrLength));
gAtr = ((p - 1) * gAtr + tr) / p;
gPrevClose = close;
}
// a gap whose body is crossed becomes an inversion and changes list
void FvgManage(int &source[], int &inv[], int n)
{
if(ArraySize(source) >= gBuffer) ListRemoveAt(source, ArraySize(source) - 1);
for(int i = ArraySize(source) - 1; i >= 0; i--)
{
int g = source[i];
if(gZ[g].dir == 1 && CBot(0) < gZ[g].bot)
{
gZ[g].xval = n;
ListInsert0(inv, g); ListRemoveAt(source, i);
gIsIfvg = -1;
Emit(EV_INVERTED, n, g);
}
else if(gZ[g].dir == -1 && CTop(0) > gZ[g].top)
{
gZ[g].xval = n;
ListInsert0(inv, g); ListRemoveAt(source, i);
gIsIfvg = 1;
Emit(EV_INVERTED, n, g);
}
}
}
// flip the polarity on the first pass, then look for bounces and for the fill that kills the zone
void InvManage(int &ary[], int n)
{
if(ArraySize(ary) >= gBuffer) ListRemoveAt(ary, ArraySize(ary) - 1);
for(int i = ArraySize(ary) - 1; i >= 0; i--)
{
int z = ary[i];
double top = gZ[z].top, bot = gZ[z].bot;
int dirBefore = gZ[z].dir, state = gZ[z].state;
if(state == 0 && dirBefore == 1) { gZ[z].state = 1; gZ[z].dir = -1; }
else if(state == 0 && dirBefore == -1) { gZ[z].state = 1; gZ[z].dir = 1; }
int prevDir = dirBefore; // keeps the direction as it was before the flip
int dir = gZ[z].dir; state = gZ[z].state; // and re-reads the current one
if(state >= 1) gZ[z].right = n;
double refPrice = InpBounce == BOUNCE_WICK ? H(0) : C(1);
double refPriceLow = InpBounce == BOUNCE_WICK ? L(0) : C(1);
if(dir == -1 && prevDir == -1 && state == 1 && C(0) < bot && refPrice >= bot && refPrice < top)
{
AddLab(z, n, top, -1);
gBounceSignal = -1;
Emit(EV_DOWN, n, z);
}
else if(dir == 1 && prevDir == 1 && state == 1 && C(0) > top && refPriceLow <= top && refPriceLow > bot)
{
AddLab(z, n, bot, 1);
gBounceSignal = 1;
Emit(EV_UP, n, z);
}
if(state >= 1 && ((gZ[z].dir == -1 && CTop(0) > top && C(0) > O(0)) || (gZ[z].dir == 1 && CBot(0) < bot && O(0) > C(0))))
{
Emit(EV_FILLED, n, z);
if(!InpRemoveFilled) { gZ[z].state = -1; gZ[z].right = n; }
else ListRemoveAt(ary, i);
}
}
}
void FeedBar(datetime t, double o, double h, double l, double c)
{
int n = gCount;
ArrayResize(gO, n + 1, 4096); ArrayResize(gH, n + 1, 4096); ArrayResize(gL, n + 1, 4096); ArrayResize(gC, n + 1, 4096);
ArrayResize(gCTop, n + 1, 4096); ArrayResize(gCBot, n + 1, 4096); ArrayResize(gT, n + 1, 4096);
gO[n] = o; gH[n] = h; gL[n] = l; gC[n] = c; gCTop[n] = MathMax(o, c); gCBot[n] = MathMin(o, c); gT[n] = t;
gCount = n + 1;
if(gBarsFile != INVALID_HANDLE)
FileWrite(gBarsFile, IntegerToString(n) + "," + TimeStr(t) + "," + DoubleToString(o, _Digits) + "," + DoubleToString(h, _Digits) + "," + DoubleToString(l, _Digits) + "," + DoubleToString(c, _Digits));
UpdateAtr(h, l, c, n);
gBounceSignal = 0;
if(n < 3) return;
double filter = gAtr * InpAtrMultiplier;
gIsIfvg = 0;
bool bullGap = L(0) > H(2) && C(1) > H(2);
bool bearGap = H(0) < L(2) && C(1) < L(2);
if(bullGap && MathAbs(L(0) - H(2)) > filter)
{
int g = NewZone(n - 1, MathMin(L(0), H(2)), n, MathMax(L(0), H(2)), (L(0) + H(2)) / 2.0, 1);
ListInsert0(gBullFvg, g);
Emit(EV_CREATED, n, g);
}
if(bearGap && MathAbs(L(2) - H(0)) > filter)
{
int g = NewZone(n - 1, MathMin(L(2), H(0)), n, MathMax(L(2), H(0)), (H(0) + L(2)) / 2.0, -1);
ListInsert0(gBearFvg, g);
Emit(EV_CREATED, n, g);
}
FvgManage(gBullFvg, gBullInv, n);
FvgManage(gBearFvg, gBearInv, n);
InvManage(gBullInv, n);
InvManage(gBearInv, n);
}
//---------------------------------------------------------------- drawing
// engine index -> time; past the last closed bar the time is extrapolated one period per bar, like a bar index
datetime XT(int x)
{
int last = gCount - 1;
if(x <= last) return gT[MathMax(0, x)];
return (datetime)((long)gT[last] + (long)(x - last) * PeriodSeconds(_Period));
}
color Blend(color c, int alpha)
{
long bg = ChartGetInteger(0, CHART_COLOR_BACKGROUND);
int br = (int)(bg & 0xFF), bgg = (int)((bg >> 8) & 0xFF), bb = (int)((bg >> 16) & 0xFF);
int cr = (int)(c & 0xFF), cg = (int)((c >> 8) & 0xFF), cb = (int)((c >> 16) & 0xFF);
int r = br + (cr - br) * alpha / 255, g = bgg + (cg - bgg) * alpha / 255, b = bb + (cb - bb) * alpha / 255;
return (color)((b << 16) | (g << 8) | r);
}
void Mark(string name, string &live[])
{
int k = ArraySize(live);
ArrayResize(live, k + 1, 64);
live[k] = name;
for(int i = 0; i < ArraySize(gDrawn); i++) if(gDrawn[i] == name) return;
k = ArraySize(gDrawn);
ArrayResize(gDrawn, k + 1, 64);
gDrawn[k] = name;
}
void Rect(string name, int x1, double y1, int x2, double y2, color c, string &live[])
{
if(y1 == 0 || y2 == 0) return;
if(x2 <= x1) x2 = x1 + 1;
datetime t1 = XT(x1), t2 = XT(x2);
if(ObjectFind(0, name) < 0)
{
ObjectCreate(0, name, OBJ_RECTANGLE, 0, t1, y1, t2, y2);
ObjectSetInteger(0, name, OBJPROP_FILL, true);
ObjectSetInteger(0, name, OBJPROP_BACK, true);
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
}
else
{
ObjectMove(0, name, 0, t1, y1);
ObjectMove(0, name, 1, t2, y2);
}
ObjectSetInteger(0, name, OBJPROP_COLOR, Blend(c, MathMax(10, InpOpacity * 255 / 100)));
Mark(name, live);
}
void Seg(string name, int x1, double y, int x2, color c, string &live[])
{
if(y == 0) return;
if(x2 <= x1) x2 = x1 + 1;
datetime t1 = XT(x1), t2 = XT(x2);
if(ObjectFind(0, name) < 0)
{
ObjectCreate(0, name, OBJ_TREND, 0, t1, y, t2, y);
ObjectSetInteger(0, name, OBJPROP_RAY_RIGHT, false);
ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_DOT);
ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
}
else
{
ObjectMove(0, name, 0, t1, y);
ObjectMove(0, name, 1, t2, y);
}
ObjectSetInteger(0, name, OBJPROP_COLOR, Blend(c, 200));
Mark(name, live);
}
void Arrow(string name, int x, double y, int dir, string &live[])
{
if(ObjectFind(0, name) < 0)
{
ObjectCreate(0, name, OBJ_TEXT, 0, XT(x), y);
ObjectSetString(0, name, OBJPROP_FONT, "Arial");
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
}
else ObjectMove(0, name, 0, XT(x), y);
ObjectSetString(0, name, OBJPROP_TEXT, dir == 1 ? "▲" : "▼");
ObjectSetInteger(0, name, OBJPROP_COLOR, dir == 1 ? InpBullColor : InpBearColor);
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, InpLabelSize == LABEL_LARGE ? 16 : InpLabelSize == LABEL_MEDIUM ? 12 : 9);
// below the zone for a bullish bounce, above it for a bearish one
ObjectSetInteger(0, name, OBJPROP_ANCHOR, dir == 1 ? ANCHOR_UPPER : ANCHOR_LOWER);
Mark(name, live);
}
void DrawList(int &ary[], string prefix, bool showBox, string &live[], int now)
{
int extend = MathMax(0, MathMin(100, InpExtend));
for(int i = 0; i < ArraySize(ary) && i < InpBoxCount; i++)
{
int id = ary[i];
if(gZ[id].xval == INT_MIN || gZ[id].left == INT_MIN) continue;
color first = gZ[id].dir == -1 ? InpBullColor : InpBearColor;
color second = gZ[id].dir == -1 ? InpBearColor : InpBullColor;
string p = "IFVG_" + prefix + IntegerToString(gZ[id].left) + "_";
int xEnd = gZ[id].state < 1 ? gZ[id].right : now;
if(showBox)
{
Rect(p + "M", gZ[id].left, gZ[id].top, gZ[id].xval, gZ[id].bot, first, live);
Rect(p + "O", gZ[id].xval, gZ[id].top, xEnd, gZ[id].bot, second, live);
if(gZ[id].state >= 1 && extend > 0) Rect(p + "E", now, gZ[id].top, now + extend, gZ[id].bot, second, live);
}
if(InpShowMidLine)
{
Seg(p + "L1", gZ[id].left, gZ[id].mid, xEnd, InpLineColor, live);
if(gZ[id].state >= 1 && extend > 0) Seg(p + "L2", now, gZ[id].mid, now + extend, InpLineColor, live);
}
if(!InpShowSignals) continue;
for(int k = 0; k < ArraySize(gLabs); k++)
{
if(gLabs[k].zone != id) continue;
Arrow("IFVG_lab" + IntegerToString(gLabs[k].x) + "_" + IntegerToString(gLabs[k].dir), gLabs[k].x, gLabs[k].y, gLabs[k].dir, live);
}
}
}
void Redraw()
{
if(gCount < 1) return;
string live[];
int now = gCount - 1;
DrawList(gBullInv, "FVGP", InpShowBear, live, now); // zones born bullish, now resistance
DrawList(gBearInv, "FVGN", InpShowBull, live, now);
for(int i = ArraySize(gDrawn) - 1; i >= 0; i--)
{
bool keep = false;
for(int k = 0; k < ArraySize(live); k++) if(live[k] == gDrawn[i]) { keep = true; break; }
if(keep) continue;
ObjectDelete(0, gDrawn[i]);
int n = ArraySize(gDrawn);
gDrawn[i] = gDrawn[n - 1];
ArrayResize(gDrawn, n - 1, 64);
}
ChartRedraw(0);
}
//---------------------------------------------------------------- parity files
void OpenParityFiles(int total)
{
string stem = "ifvg_" + _Symbol + "_" + EnumToString(_Period);
StringReplace(stem, "PERIOD_", "");
gBarsFile = FileOpen(stem + "_bars.csv", FILE_WRITE | FILE_TXT | FILE_ANSI);
gEvFile = FileOpen(stem + "_events.csv", FILE_WRITE | FILE_TXT | FILE_ANSI);
if(gBarsFile == INVALID_HANDLE || gEvFile == INVALID_HANDLE) { Print("[IFVG] parity files KO : ", GetLastError()); return; }
FileWrite(gBarsFile, "# count=" + IntegerToString(total) + " atrlen=" + IntegerToString(InpAtrLength) + " atrmult=" + DoubleToString(InpAtrMultiplier, 4) +
" bounce=" + (InpBounce == BOUNCE_WICK ? "wick" : "close") + " removefilled=" + (InpRemoveFilled ? "1" : "0") + " history=" + IntegerToString(InpHistoryBars));
FileWrite(gBarsFile, "index,time,open,high,low,close");
FileWrite(gEvFile, "index,time,token,top,bot");
Print("[IFVG] parity files: MQL5\\Files\\", stem, "_bars.csv / _events.csv");
}
void CloseParityFiles()
{
if(gBarsFile != INVALID_HANDLE) { FileClose(gBarsFile); gBarsFile = INVALID_HANDLE; }
if(gEvFile != INVALID_HANDLE) { FileClose(gEvFile); gEvFile = INVALID_HANDLE; }
}
//---------------------------------------------------------------- MQL5 entry points
int OnInit()
{
SetIndexBuffer(0, gUpBuf, INDICATOR_DATA);
SetIndexBuffer(1, gDnBuf, INDICATOR_DATA);
ArraySetAsSeries(gUpBuf, false);
ArraySetAsSeries(gDnBuf, false);
PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);
IndicatorSetString(INDICATOR_SHORTNAME, "IFVG");
ResetEngine();
Print("[IFVG] Inversion Fair Value Gaps, show last ", InpBoxCount, ", bounce on ", (InpBounce == BOUNCE_WICK ? "Wick" : "Close"),
", ATR ", InpAtrLength, " x ", DoubleToString(InpAtrMultiplier, 2), ", remove filled ", InpRemoveFilled,
", history ", InpHistoryBars, " bars, parity ", InpWriteParity);
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
CloseParityFiles();
ObjectsDeleteAll(0, "IFVG_");
ChartRedraw(0);
}
void FullPass(const int rates_total, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[])
{
CloseParityFiles();
ResetEngine();
gLive = false;
ArrayInitialize(gUpBuf, EMPTY_VALUE);
ArrayInitialize(gDnBuf, EMPTY_VALUE);
gFirst = MathMax(0, rates_total - InpHistoryBars);
if(InpWriteParity) OpenParityFiles(rates_total - 1 - gFirst);
for(int i = gFirst; i < rates_total - 1; i++) FeedBar(time[i], open[i], high[i], low[i], close[i]);
CloseParityFiles();
Print("[IFVG] history loaded: ", gCount, " bars (from chart index ", gFirst, "), ", gEvents, " events, ",
ArraySize(gBullInv), " active zones born bullish, ", ArraySize(gBearInv), " born bearish.");
}
int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[],
const double &open[], const double &high[], const double &low[], const double &close[],
const long &tick_volume[], const long &volume[], const int &spread[])
{
if(rates_total < 2) return 0;
// the engine maps bar gFirst + k to its bar k
bool shifted = gCount > 0 && (gFirst + gCount - 1 >= rates_total || time[gFirst + gCount - 1] != gT[gCount - 1]);
if(prev_calculated == 0 || shifted)
{
FullPass(rates_total, time, open, high, low, close);
Redraw();
return rates_total;
}
gLive = true;
for(int i = MathMax(0, prev_calculated - 1); i < rates_total; i++) { gUpBuf[i] = EMPTY_VALUE; gDnBuf[i] = EMPTY_VALUE; }
int before = gCount;
for(int i = gFirst + gCount; i < rates_total - 1; i++) FeedBar(time[i], open[i], high[i], low[i], close[i]);
if(gCount != before) Redraw();
return rates_total;
}
//+------------------------------------------------------------------+
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/code/77726
ZoneUS30 — Reversión y swap positivo a tu favor en el US30
ZoneUS30 es un sencillo asesor experto para el US30 centrado en oportunidades de VENTA tras subidas prolongadas de los precios. La estrategia combina el concepto de reversión a la media con la idea del carry trade: cuando las posiciones cortas obtienen un swap nocturno positivo, el EA puede beneficiarse tanto de una corrección del precio como de mantener la posición a lo largo del tiempo. Diseñado para el US30 y destinado a un uso experimental y educativo. Operar en contra de la tendencia conlleva un riesgo significativo, especialmente con lotes de gran tamaño.
EMA Cross Demo EA with ATR Stops
Un pequeño asesor experto de demostración: entradas por cruce de EMA rápida/lenta (evaluadas únicamente en barras cerradas, sin repintado), stop-loss y take-profit basados en el ATR, filtro de spread y gestión de posiciones que funciona tanto en cuentas de cobertura como de compensación. Incluye una implementación de referencia en Python para realizar pruebas de paridad. Se trata de un ejemplo ilustrativo, no de una recomendación de inversión: la curva de capital adjunta procede de datos sintéticos y muestra el método, no una ventaja competitiva.
SPREAD CALCULADORA (Script)
1. INTRODUCCIÓN Y UTILIDAD -------------------------------------------------------------------------------- En el trading cuantitativo y algorítmico (Expert Advisors), uno de los mayores factores de discrepancia entre los resultados teóricos de un backtest y los resultados reales en cuenta viva (live/forward testing) es el SPREAD FLOTANTE y el DESLIZAMIENTO (slippage) durante las fases de alta volatilidad. Eventos como la campana de apertura de Wall Street (09:30 NY), la apertura de Londres (08:00 Londres) o la publicación de noticias macroeconómicas provocan frecuentemente ensanchamientos extremos del spread (spread widening) que duran desde unas pocas décimas de segundo hasta varios minutos.
Accelerator Oscillator (AC)
El indicador Acceleration/Deceleration (AC, Aceleración/Desaceleración) mide la aceleración y la desaceleración de la fuerza impulsora del mercado.
