CME Gap Tracker
Сообщите нам, если считаете, что материал размещён без разрешения.
Сообщить о материале
Обращение по товару «cmegaptracker.mq5» отправится на рассмотрение. Укажите контакт для ответа и опишите причину.
О программе
CME Gap Tracker — индикатор для MetaTrader 5, который находит уикенд-пропуски цен CME (пятничное закрытие и воскресное открытие фьючерсов) на графиках Bitcoin и золота. Он сохраняет все незакрытые пропуски на графике в виде цветных прямоугольников и отслеживает статистику их заполнения.
Индикатор также включает табло-скоркард, показывающее количество пропусков, процент закрытия, медианное время заполнения, величину «тепла» перед заполнением и результаты тестирования классической стратегии «продавать пропуск» (fade the gap). Результаты рассчитываются на заданном количестве недель истории.
Поддерживаются уведомления: оповещение при открытии нового пропуска и при его заполнении, в том числе push-уведомления. Данные для чтения из советников доступны через 10 индикаторных буферов, которые можно получить с помощью iCustom(). Индикатор рекомендуется использовать на таймфреймах H1 и ниже.
Настройки программы
| Настройка | Описание |
|---|---|
| InpGMTWinter | Часовой сдвиг сервера GMT зимой (в часах), используемый для перевода времени CME в серверное время. |
| InpGMTSummer | Часовой сдвиг сервера GMT летом (в часах), используемый для перевода времени CME в серверное время. |
| InpMinGapPct | Минимальный размер ценового пропуска в процентах от цены, пропуски меньше этого порога игнорируются. |
| InpWeeksBack | Количество недель истории, участвующих в расчёте статистики и скоркарда пропусков. |
| InpDrawWeeks | Количество недель истории, для которых на графике отрисовываются прямоугольники ценовых пропусков. |
| InpStopGapMult | Множитель размера пропуска, определяющий расстояние стоп-лосса при тестировании стратегии fade the gap. |
| InpMaxHoldDays | Максимальное количество дней удержания позиции при тестировании стратегии fade the gap. |
| InpShowPanel | Включает или отключает отображение табло скоркарда с статистикой пропусков на графике. |
| InpPanelCorner | Угол графика, в котором отображается табло скоркарда. |
| InpOpenGapColor | Цвет заливки прямоугольника открытого (незакрытого) ценового пропуска. |
| InpFilledGapColor | Цвет заливки прямоугольника уже заполненного ценового пропуска. |
| InpAlertNewGap | Включает или отключает оповещение при открытии нового ценового пропуска. |
| InpAlertFill | Включает или отключает оповещение при заполнении открытого ценового пропуска. |
| InpAlertPush | Дополнительно отправляет push-уведомления при срабатывании оповещений о пропусках. |
Поиск по коду файла
#property version "1.00"
#property description "CME weekend gaps for Bitcoin and gold: open gaps kept on the chart, fill statistics and a fade-trade scorecard."
#property indicator_chart_window
#property indicator_buffers 10
#property indicator_plots 2
#property indicator_label1 "Gap down (fade buy)"
#property indicator_type1 DRAW_ARROW
#property indicator_color1 clrDodgerBlue
#property indicator_width1 2
#property indicator_label2 "Gap up (fade sell)"
#property indicator_type2 DRAW_ARROW
#property indicator_color2 clrOrangeRed
#property indicator_width2 2
enum ENUM_SERVER_DST
{
DST_NONE = 0, // Fixed offset all year
DST_EU = 1, // Europe (last Sunday of March to last Sunday of October)
DST_US = 2 // USA (second Sunday of March to first Sunday of November)
};
input int InpGMTWinter = 2; // Server GMT offset in winter (hours)
input int InpGMTSummer = 3; // Server GMT offset in summer (hours)
input ENUM_SERVER_DST InpServerDST = DST_EU; // Server daylight saving rule
input double InpMinGapPct = 0.30; // Minimum gap size, % of price
input int InpWeeksBack = 104; // Weeks of history scored
input int InpDrawWeeks = 26; // Weeks with gap boxes drawn
input double InpStopGapMult = 1.0; // Fade trade: stop distance as a multiple of the gap
input int InpMaxHoldDays = 5; // Fade trade: exit after this many days
input bool InpShowPanel = true; // Show the scorecard panel
input ENUM_BASE_CORNER InpPanelCorner = CORNER_LEFT_UPPER; // Panel corner
input color InpOpenGapColor = C'255,214,170'; // Unfilled gap colour
input color InpFilledGapColor = clrGainsboro; // Filled gap colour
input bool InpAlertNewGap = true; // Alert when a new gap opens
input bool InpAlertFill = true; // Alert when an open gap is filled
input bool InpAlertPush = false; // Also send push notifications
double BufFadeBuy[]; // 0: gap down, entry price on the CME open bar
double BufFadeSell[]; // 1: gap up, entry price on the CME open bar
double BufTarget[]; // 2: Friday close (fill level) on that bar
double BufStop[]; // 3: fade-trade stop on that bar
double BufOpenAbove[]; // 4: nearest unfilled gap level above the close
double BufOpenBelow[]; // 5: nearest unfilled gap level below the close
double BufScN[]; // 6: scorecard, closed fade trades (last closed bar)
double BufScWins[]; // 7: scorecard, winning fade trades
double BufScSumR[]; // 8: scorecard, total result in R
double BufScLossR[]; // 9: scorecard, sum of losing results in R (positive)
struct GapInfo
{
datetime openTime; // bar time of the CME open
int openBar; // bar index of the CME open
double friClose; // price at the Friday close (fill level)
double sunOpen; // price at the CME open
int fillBar; // bar that filled it, -1 if still open
datetime fillTime; // open time of that bar
double heat; // move against the fill before it filled, in gap sizes
double r; // fade trade result in R
bool tradeOpen; // fade trade still running
bool scored; // inside the scored window
};
const string PFX = "CGT_"; // chart object prefix
GapInfo g_gaps[]; // gaps found in the loaded history
int g_small = 0; // weekends below the minimum size
datetime g_lastBarTime = 0; // last bar the scan ran on
datetime g_lastGapAlert = 0; // gap already alerted
datetime g_lastFillAlert = 0; // fill already alerted
string g_rows[10]; // last panel text
int OnInit()
{
// код доступен только после оплаты
}
void OnDeinit(const int reason)
{
ObjectsDeleteAll(0, PFX);
ChartRedraw();
}
datetime NthSunday(const int year, const int month, const int n)
{
MqlDateTime dt = {};
dt.year = year;
dt.mon = month;
dt.day = 1;
datetime first = StructToTime(dt);
TimeToStruct(first, dt);
int shift = (7 - dt.day_of_week) % 7;
return(first + (shift + 7 * (n - 1)) * 86400);
}
datetime LastSunday(const int year, const int month)
{
MqlDateTime dt = {};
dt.year = (month == 12) ? year + 1 : year;
dt.mon = (month == 12) ? 1 : month + 1;
dt.day = 1;
datetime nextFirst = StructToTime(dt);
TimeToStruct(nextFirst - 86400, dt);
return(nextFirst - 86400 - dt.day_of_week * 86400);
}
bool UsDst(const datetime utc)
{
MqlDateTime dt;
TimeToStruct(utc, dt);
datetime on = NthSunday(dt.year, 3, 2) + 8 * 3600; // 02:00 CST = 08:00 UTC
datetime off = NthSunday(dt.year, 11, 1) + 7 * 3600; // 02:00 CDT = 07:00 UTC
return(utc >= on && utc < off);
}
bool EuDst(const datetime utc)
{
MqlDateTime dt;
TimeToStruct(utc, dt);
datetime on = LastSunday(dt.year, 3) + 3600; // 01:00 UTC
datetime off = LastSunday(dt.year, 10) + 3600;
return(utc >= on && utc < off);
}
datetime UtcToServer(const datetime utc)
{
bool summer = false;
if(InpServerDST == DST_EU)
summer = EuDst(utc);
else
if(InpServerDST == DST_US)
summer = UsDst(utc);
return(utc + (summer ? InpGMTSummer : InpGMTWinter) * 3600);
}
int LastBarEndingBy(const datetime &time[], const int total, const datetime t)
{
int lo = 0, hi = total - 1, ans = -1, ps = PeriodSeconds();
while(lo <= hi)
{
int mid = (lo + hi) / 2;
if(time[mid] + ps <= t)
{
ans = mid;
lo = mid + 1;
}
else
hi = mid - 1;
}
return(ans);
}
int FirstBarFrom(const datetime &time[], const int total, const datetime t)
{
int lo = 0, hi = total - 1, ans = -1;
while(lo <= hi)
{
int mid = (lo + hi) / 2;
if(time[mid] >= t)
{
ans = mid;
hi = mid - 1;
}
else
lo = mid + 1;
}
return(ans);
}
void FindGaps(const int rates_total, const datetime &time[], const double &open[], const double &close[],
const datetime scoreFrom)
{
ArrayResize(g_gaps, 0);
g_small = 0;
datetime day = time[0] - time[0] % 86400;
MqlDateTime dt;
TimeToStruct(day, dt);
day += ((5 - dt.day_of_week + 7) % 7) * 86400;
for(; day < time[rates_total - 1]; day += 7 * 86400)
{
int ct = UsDst(day + 12 * 3600) ? 5 : 6;
datetime closeUtc = day + (16 + ct) * 3600;
int ct2 = UsDst(day + 2 * 86400 + 12 * 3600) ? 5 : 6;
datetime openUtc = day + 2 * 86400 + (17 + ct2) * 3600;
datetime closeSrv = UtcToServer(closeUtc);
datetime openSrv = UtcToServer(openUtc);
int a = LastBarEndingBy(time, rates_total, closeSrv);
int b = FirstBarFrom(time, rates_total, openSrv);
if(a < 0 || b < 0 || b <= a)
continue;
if(closeSrv - time[a] > 3 * 86400 || time[b] - openSrv > 2 * 86400)
continue;
double fc = close[a], so = open[b];
if(fc <= 0.0 || MathAbs(so - fc) / fc * 100.0 < InpMinGapPct)
{
if(time[b] >= scoreFrom)
g_small++; // counted in the scored window only
continue;
}
int n = ArraySize(g_gaps);
ArrayResize(g_gaps, n + 1);
g_gaps[n].openTime = time[b];
g_gaps[n].openBar = b;
g_gaps[n].friClose = fc;
g_gaps[n].sunOpen = so;
g_gaps[n].fillBar = -1;
g_gaps[n].fillTime = 0;
g_gaps[n].heat = 0.0;
g_gaps[n].r = 0.0;
g_gaps[n].tradeOpen = true;
g_gaps[n].scored = false;
}
}
void ScoreGap(GapInfo &g, const int rates_total, const datetime &time[], const double &high[],
const double &low[], const double &close[], const int &spread[])
{
bool up = g.sunOpen > g.friClose;
double size = MathAbs(g.sunOpen - g.friClose);
double entry = g.sunOpen;
double stop = up ? entry + InpStopGapMult * size : entry - InpStopGapMult * size;
double risk = InpStopGapMult * size;
double cost = spread[g.openBar] * _Point;
datetime lim = g.openTime + InpMaxHoldDays * 86400;
double worst = entry;
for(int k = g.openBar; k < rates_total - 1; k++)
{
if(g.tradeOpen)
{
bool hitSL = up ? high[k] >= stop : low[k] <= stop;
bool hitTP = up ? low[k] <= g.friClose : high[k] >= g.friClose;
if(hitSL)
{
g.r = (-risk - cost) / risk;
g.tradeOpen = false;
}
else
if(hitTP)
{
g.r = (size - cost) / risk;
g.tradeOpen = false;
}
else
if(time[k] >= lim)
{
g.r = ((up ? entry - close[k] : close[k] - entry) - cost) / risk;
g.tradeOpen = false;
}
}
if(g.fillBar < 0)
{
worst = up ? MathMax(worst, high[k]) : MathMin(worst, low[k]);
if(up ? low[k] <= g.friClose : high[k] >= g.friClose)
{
g.fillBar = k;
g.fillTime = time[k];
g.heat = MathAbs(worst - entry) / size;
}
}
if(!g.tradeOpen && g.fillBar >= 0)
break;
}
}
double Percentile(double &v[], const double p)
{
int n = ArraySize(vПоказано первые 50% кода (символов: 15990)
Открыть по тарифу
Купите тариф с пакетом открытий или войдите, если тариф уже есть. Открытый файл останется в вашем кабинете навсегда.
Разовая покупка файла
На этой странице откроется полный исходный код файла вместо ознакомительного фрагмента.
Зачем нужен email? После подтверждения оплаты файл сохранится в кабинете на этот email. Если аккаунта ещё нет, мы создадим его и пришлём ссылку для задания пароля. Если аккаунт уже есть, покупка добавится в него. Укажите адрес, к которому у вас есть доступ.
Чтобы оформить заказ, введите email и перейдите к оплате.