← К каталогу
Исходный код · ID 4509

Point & Figure

point and figure.mq5
Вы автор или правообладатель?

Сообщите нам, если считаете, что материал размещён без разрешения.

Краткий обзор

О программе

Point & Figure — индикатор для MetaTrader 5, реализующий классический метод технического анализа Point & Figure. Он строит графическую модель цены в виде квадратов X (рост) и O (падение) в отдельном окне индикатора и отображает подробную информационную панель на основном графике. Работает на любом символе и таймфрейме.

Индикатор автоматически рассчитывает столбцы P&F на основе заданного размера коробки — фиксированного в пунктах или адаптивного на основе ATR. Генерируются сигналы входа BUY (+1) и SELL (-1) при пробое уровней предыдущих столбцов. Доступны фильтры: по структуре цен (более высокий минимум / более низкий максимум), по тренду (EMA), по силе тренда (ADX) и по торговой сессии.

Информационная панель показывает план сделки (вход, стоп-лосс, тейк-профит, соотношение риск/прибыль, размер лота), текущие условия входа, состояние фильтров, структуру P&F и уровни поддержки/сопротивления. Поддерживаются оповещения: всплывающие окна, звуковые сигналы, push-уведомления и e-mail.

Индикатор также определяет классические паттерны P&F (двойной/тройной верх и низ, катапульта, треугольник) и отображает их названия в окне. Линии поддержки и сопротивления R1/R2/S1/S2 рисуются на основном графике с настраиваемым цветом и стилем.

Настройки программы

НастройкаОписание
InpBoxMode Режим определения размера коробки: фиксированный (в пунктах) или адаптивный (на основе ATR).
InpBoxPoints Фиксированный размер коробки в пунктах. Используется при выборе фиксированного режима.
InpATRPeriod Период расчёта индикатора ATR для автоматического определения размера коробки.
InpATRTf Таймфрейм для расчёта ATR при использовании адаптивного режима размера коробки.
InpATRMult Множитель ATR: размер коробки в адаптивном режиме рассчитывается как кратное значение ATR.
InpReversal Количество коробок, необходимое для разворота направления столбца P&F.
InpSource Источник цены для построения P&F: цена закрытия или диапазон высокий/низкий.
InpHistoryBars Максимальное количество баров истории, используемых для расчёта P&F.
InpClosedOnly Использование только закрытых баров для расчёта P&F, что обеспечивает стабильность сигналов.
InpColorX Цвет квадратов X, обозначающих рост цены в P&F.
InpColorO Цвет квадратов O, обозначающих падение цены в P&F.
InpMaxColumns Максимальное количество отображаемых столбцов P&F в окне индикатора.
InpColWidth Ширина столбца P&F в барах основного графика.
InpLeftShift Количество баров сдвига от последнего бара для отображения P&F в отдельном окне.
InpGapPct Процент зазора между квадратами P&F относительно размера коробки.
InpShowBorder Отображение границы вокруг квадратов P&F.
InpBorderColor Цвет границы вокруг квадратов P&F (при включённом отображении границ).
InpBorderWidth Ширина границы квадратов в пикселях. Автоматически уменьшается в маленьких окнах.
InpMaxObjects Максимальное количество графических объектов (квадраты и границы) на графике.
InpWindowHeight Высота окна Point & Figure в пикселях. 0 — высота остаётся по умолчанию.
InpShowPrice Отображение линии текущей цены в отдельном окне P&F.
InpShowPanel Отображение информационной панели на основном графике.
InpPanelPos Расположение информационной панели на графике: верхний/нижний левый или правый угол.
InpPanelMode Начальный режим отображения панели: полный (Full), компактный (Compact) или минимальный (Minimal).
InpPanelX Горизонтальное расстояние панели от края графика в пикселях.
InpPanelY Вертикальное расстояние панели от края графика в пикселях.
InpPanelBg Цвет фона информационной панели на основном графике.
InpSectionColor Цвет заголовков секций на информационной панели.
InpPanelRefreshMs Интервал обновления данных панели в миллисекундах.
InpPanelScale Масштаб размера панели: 1.0 — нормальный, 1.5 — увеличен на 50%, 2.0 — удвоен.
InpTextScale Масштаб размера текста на панели: 1.0 — нормальный, 1.3 — +30%, 1.6 — +60%.
InpRequireHL Фильтрация сигналов по структуре: BUY требует более высокого минимума, SELL — более низкого максимума.
InpOnlyFlips Генерация сигнала только при смене направления по сравнению с предыдущим сигналом.
InpRR Соотношение риска к прибыли (Risk/Reward) для расчёта уровня тейк-профита.
InpSLBuffer Дополнительный буфер стоп-лосса в пунктах. 0 — автоматический (20% от размера коробки).
InpShowPatterns Отображение названий паттернов P&F в окне индикатора.
InpPatternColor Цвет текста названий паттернов P&F в окне индикатора.
InpRiskPct Процент баланса, используемый как допустимый риск для расчёта размера лота.
InpMaxRiskPips Предупреждение, если стоп-лосс превышает указанное количество пипсов. 0 — автоматический расчёт (6 коробок).
InpMaxChasePips Предупреждение, если цена сместилась дальше пробоя на указанное количество пипсов. 0 — автоматический расчёт (1 коробка).
InpMaxSpreadPips Предупреждение, если спред превышает указанное количество пипсов. 0 — автоматический расчёт (0.3 коробки).
InpUseTrendMA Включение фильтра тренда EMA: BUY только выше EMA, SELL только ниже EMA.
InpMAPeriod Период экспоненциальной скользящей средней (EMA) для фильтрации тренда.
InpUseADX Включение фильтра ADX: сигналы игнорируются при боковом рынке (ADX ниже минимального значения).
InpADXPeriod Период расчёта индекса ADX (сила тренда) для фильтрации сигналов.
InpADXMin Минимальное значение ADX: сигналы игнорируются, если индекс силы тренда ниже этого порога. Используется для фильтрации бокового рынка.
InpUseSession Включение фильтрации сигналов по времени торговой сессии (серверное время).
InpStartHour Час начала торговой сессии по серверному времени для фильтрации сигналов.
InpEndHour Час окончания торговой сессии по серверному времени для фильтрации сигналов.
InpSRLookback Количество столбцов P&F, анализируемых для определения уровней поддержки и сопротивления.
InpShowSRLines Отрисовка линий R1/R2/S1/S2 и их подписей на основном графике.
InpResistColor Цвет линий сопротивления R1/R2 на основном графике.
InpSupportColor Цвет линий поддержки S1/S2 на основном графике.
InpResistLabelBg Цвет фона подписей уровней сопротивления на основном графике.
InpSupportLabelBg Цвет фона подписей уровней поддержки на основном графике.
InpSRLabelText Цвет текста подписей уровней поддержки и сопротивления.
InpSRLineWidth Толщина линий поддержки и сопротивления на основном графике (от 1 до 5).
InpSRLabelSize Размер шрифта подписей уровней поддержки и сопротивления.
InpSRLabelRight Расстояние подписей уровней поддержки/сопротивления от правого края графика в пикселях.
InpAlertSignals Отправка оповещения при появлении сигнала BUY или SELL.
InpAlertPatterns Отправка оповещения при обнаружении паттерна P&F (двойной/тройной верх или низ, катапульта, треугольник).
InpAlertColumn Отправка оповещения при формировании нового столбца X/O на графике P&F.
InpAlertPopup Отображение всплывающего окна при срабатывании оповещений.
InpAlertSound Воспроизведение звукового сигнала при срабатывании оповещений.
InpSoundFile Путь к звуковому файлу, воспроизводимому при оповещениях.
InpAlertPush Отправка push-уведомления на мобильное устройство при оповещениях.
InpAlertEmail Отправка оповещения по электронной почте при появлении сигналов, паттернов или столбцов.
Получить полный код

Поиск по коду файла

#property version     "4.10"
#property description "Point & Figure: lime squares = X (rising), red squares = O (falling), navy border."
#property description "Navy panel in the main window: signal, trade plan, entry conditions, P&F structure."
#property description "Works on any symbol / timeframe. Signal buffer: +1 BUY, -1 SELL, 0 none."
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_plots   1
#property indicator_type1   DRAW_NONE
#property indicator_label1  "PF Signal"

enum ENUM_BOX_MODE
  {
   BOX_FIXED_POINTS = 0,   // Fixed (points)
   BOX_ATR          = 1    // ATR based (adapts to symbol / timeframe)
  };

enum ENUM_PF_SOURCE
  {
   PF_CLOSE    = 0,        // Close price
   PF_HIGHLOW  = 1         // High / Low
  };

enum ENUM_PF_PATTERN
  {
   PAT_NONE = 0,
   PAT_DTB,                // Double top breakout
   PAT_DBB,                // Double bottom breakdown
   PAT_TTB,                // Triple top breakout
   PAT_TBB,                // Triple bottom breakdown
   PAT_BULL_CAT,           // Bullish catapult
   PAT_BEAR_CAT,           // Bearish catapult
   PAT_TRIANGLE            // Triangle (consolidation)
  };

enum ENUM_PANEL_POS
  {
   PNL_LEFT_TOP     = 0,   // Top left
   PNL_RIGHT_TOP    = 1,   // Top right
   PNL_LEFT_BOTTOM  = 2,   // Bottom left
   PNL_RIGHT_BOTTOM = 3    // Bottom right
  };

enum ENUM_PANEL_MODE
  {
   PMODE_FULL    = 0,      // Full
   PMODE_COMPACT = 1,      // Compact
   PMODE_MIN     = 2       // Minimal
  };

enum ENUM_SECTION_CLR
  {
   SC_SKY     = 0,   // Sky blue
   SC_GOLD    = 1,   // Gold
   SC_ORANGE  = 2,   // Orange
   SC_LIME    = 3,   // Lime green
   SC_AQUA    = 4,   // Aqua
   SC_PINK    = 5,   // Pink
   SC_MAGENTA = 6,   // Magenta
   SC_VIOLET  = 7,   // Violet
   SC_SILVER  = 8,   // Silver
   SC_WHITE   = 9    // White
  };

input group "=== Point & Figure ==="
input ENUM_BOX_MODE   InpBoxMode      = BOX_ATR;          // Box size mode
input int             InpBoxPoints    = 50;               // Box size (points) - fixed mode
input int             InpATRPeriod    = 14;               // ATR period - ATR mode
input ENUM_TIMEFRAMES InpATRTf        = PERIOD_CURRENT;   // ATR timeframe - ATR mode
input double          InpATRMult      = 1.0;              // ATR multiplier - ATR mode
input int             InpReversal     = 3;                // Reversal (number of boxes)
input ENUM_PF_SOURCE  InpSource       = PF_CLOSE;         // Price source
input int             InpHistoryBars  = 3000;             // Max. history bars used
input bool            InpClosedOnly   = true;             // Closed bars only (stable signals)

input group "=== Display (P&F window) ==="
input color           InpColorX       = clrLime;          // X squares color (rising)
input color           InpColorO       = clrRed;           // O squares color (falling)
input int             InpMaxColumns   = 40;               // Max. columns displayed
input int             InpColWidth     = 3;                // Column width (chart bars)
input int             InpLeftShift    = 8;                // Shift to the left (bars) from the last bar
input double          InpGapPct       = 20.0;             // Gap between squares (% of box)
input bool            InpShowBorder   = true;             // Border around squares
input color           InpBorderColor  = clrNavy;          // Border color
input int             InpBorderWidth  = 2;                // Border width (pixels, reduced automatically in small windows)
input int             InpMaxObjects   = 5000;             // Object limit (squares + borders)
input int             InpWindowHeight = 0;                // P&F window height (pixels), 0 = leave as is
input bool            InpShowPrice    = true;             // Current price line

input group "=== Info panel (main window) ==="
input bool            InpShowPanel      = true;           // Show panel
input ENUM_PANEL_POS  InpPanelPos       = PNL_LEFT_TOP;   // Panel position
input ENUM_PANEL_MODE InpPanelMode      = PMODE_FULL;     // Initial mode (panel button: Full/Compact/Minimal)
input int             InpPanelX         = 12;             // Horizontal margin (px)
input int             InpPanelY         = 28;             // Vertical margin (px)
input color           InpPanelBg        = clrNavy;        // Panel background color
input ENUM_SECTION_CLR InpSectionColor  = SC_SKY;         // Section title color
input int             InpPanelRefreshMs = 250;            // Panel refresh (ms)
input double          InpPanelScale     = 1.0;            // Panel size (1.0 = normal, 1.5 = +50%, 2.0 = double)
input double          InpTextScale      = 1.0;            // Text size (1.0 = normal, 1.3 = +30%, 1.6 = +60%)

input group "=== Entry signals ==="
input bool            InpRequireHL    = true;             // Filter: higher low (BUY) / lower high (SELL)
input bool            InpOnlyFlips    = true;             // Signal only when direction changes
input double          InpRR           = 1.5;              // Risk/Reward ratio for TP
input int             InpSLBuffer     = 0;                // SL buffer (points), 0 = auto (20% of box)
input bool            InpShowPatterns = true;             // Show P&F pattern names (P&F window)
input color           InpPatternColor = clrWhite;         // Pattern text color

input group "=== Risk management (panel + alerts) ==="
input double          InpRiskPct        = 1.0;            // Risk per trade (% of balance) for lot size
input double          InpMaxRiskPips    = 0.0;            // Warn: SL larger than (pips), 0 = auto (6 boxes)
input double          InpMaxChasePips   = 0.0;            // Warn: price ran past breakout by (pips), 0 = auto (1 box)
input double          InpMaxSpreadPips  = 0.0;            // Warn: spread larger than (pips), 0 = auto (0.3 box)

input group "=== Filters for cleaner signals ==="
input bool            InpUseTrendMA   = true;             // Trend filter: BUY only above EMA, SELL only below EMA
input int             InpMAPeriod     = 200;              // EMA period (on current timeframe)
input bool            InpUseADX       = true;             // ADX filter: ignore sideways market
input int             InpADXPeriod    = 14;               // ADX period
input double          InpADXMin       = 20.0;             // Minimum ADX for a signal
input bool            InpUseSession   = true;             // Session filter (server time)
input int             InpStartHour    = 9;                // Start hour (server time)
input int             InpEndHour      = 21;               // End hour (server time)

input group "=== Support / Resistance ==="
input int             InpSRLookback   = 40;               // Number of columns analysed
input bool            InpShowSRLines  = true;             // Draw R1/R2/S1/S2 lines + labels on the MAIN chart
input color           InpResistColor  = clrRed;           // Resistance line color
input color           InpSupportColor = C'0,150,60';      // Support line color
input color           InpResistLabelBg  = C'200,30,30';   // Resistance label background (red)
input color           InpSupportLabelBg = C'0,150,60';    // Support label background (green)
input color           InpSRLabelText    = clrWhite;       // Label text color
input int             InpSRLineWidth  = 3;                // Line width (1-5)
input int             InpSRLabelSize  = 10;               // Label font size
input int             InpSRLabelRight = 8;                // Label distance from the right edge (px)

input group "=== Alerts ==="
input bool            InpAlertSignals  = true;            // Alert on BUY/SELL signal
input bool            InpAlertPatterns = false;           // Alert on pattern
input bool            InpAlertColumn   = false;           // Alert on new column (X/O)
input bool            InpAlertPopup    = true;            // Popup window
input bool            InpAlertSound    = false;           // Sound
input string          InpSoundFile     = "alert.wav";     // Sound file
input bool            InpAlertPush     = false;           // Push notification (mobile)
input bool            InpAlertEmail    = false;           // E-mail

color SectionColor()
  {
   switch(InpSectionColor)
     {
      case SC_SKY:     return C'80,210,255';
      case SC_GOLD:    return C'255,210,64';
      case SC_ORANGE:  return C'255,150,50';
      case SC_LIME:    return C'120,235,90';
      case SC_AQUA:    return C'0,230,230';
      case SC_PINK:    return C'255,140,190';
      case SC_MAGENTA: return C'255,80,255';
      case SC_VIOLET:  return C'185,140,255';
      case SC_SILVER:  return C'200,200,210';
      case SC_WHITE:   return clrWhite;
     }
   return C'80,210,255';
  }

#define PREFIX "PFB_"        // objects in the P&F window
#define PNL    "PFP_"        // panel objects (main window)
#define SRL    "PFSR_"       // support/resistance lines + labels (main chart)

#define BLK_HL    1
#define BLK_EMA   2
#define BLK_ADX   4
#define BLK_SES   8
#define BLK_FLIP  16

#define P_W       470
#define P_H_FULL  420
#define P_H_COMP  234
#define P_H_MIN   82
#define P_X1      12
#define P_X2      242
#define P_CW      216

enum PV_SLOT
  {
   PV_HDR = 0, PV_BANNER, PV_STATUS, PV_VERDICT, PV_SUB,
   PV_ENTRY, PV_SL, PV_TP, PV_RR, PV_LOT,
   PV_EMA, PV_ADX, PV_SES, PV_SPR, PV_ATR,
   PV_BOX, PV_COL, PV_EXT, PV_REV, PV_BOB, PV_BOS, PV_STR,
   PV_R2, PV_R1, PV_S1, PV_S2,
   PV_H0, PV_H1, PV_H2, PV_HR0, PV_HR1, PV_HR2,
   PV_FL, PV_FR,
   PV_COUNT
  };

struct PFCol
  {
   int      dir;     // +1 = X, -1 = O
   int      top;     // top box level
   int      bot;     // bottom box level
   int      sig;     // raw breakout: +1 buy, -1 sell, 0 none
   int      eff;     // effective ENTRY signal (after filters)
   int      pat;     // pattern (ENUM_PF_PATTERN)
   int      blk;     // filters that blocked the breakout (BLK_*)
   int      trig;    // index of the bar where the breakout happened (-1 = none)
   int      b0;      // index of the bar where the column started
   datetime t0;      // time of the bar where the column started
   datetime tTrig;   // time of the breakout bar
   double   pTrig;   // price level of the breakout
  };

PFCol    g_cols[];
int      g_n          = 0;
int      g_boxPts     = 50;
int      g_boxAuto    = 0;       // last ATR-derived box (hysteresis)
double   g_pip        = 0.0001;
int      g_hATR       = INVALID_HANDLE;   // ATR for the box (ATR mode)
int      g_hATRc      = INVALID_HANDLE;   // ATR on the current timeframe (informative)
int      g_hMA        = INVALID_HANDLE;
int      g_hADX       = INVALID_HANDLE;
double   g_sigBuf[];
double   g_maBuf[];
double   g_adxBuf[];
int      g_fOff       = 0;      // index (non-series) of the first element of g_maBuf / g_adxBuf
double   g_emaV       = 0.0;    // informative values for the panel
double   g_adxV       = 0.0;
double   g_atrV       = 0.0;

int      g_rev        = 3;
int      g_W          = 3;
int      g_shift      = 8;
int      g_maxCols    = 40;
int      g_maxObj     = 5000;
int      g_hist       = 3000;
double   g_gap        = 20.0;
int      g_maP        = 200;
int      g_adxP       = 14;
int      g_srLook     = 40;

int      g_win        = -1;     // separate window of the indicator
string   g_lastSig    = "";
bool     g_needRecalc = true;
int      g_lastTotal  = 0;
datetime g_lastBarT   = 0;
int      g_drawn      = 0;      // drawn columns
int      g_subH       = 0;      // height (px) of the P&F window at the last redraw
bool     g_hSet       = false;  // window height already applied
double   g_lastPrice  = 0.0;
double   g_scLo       = 0.0;    // fixed vertical scale of the P&F window
double   g_scHi       = 0.0;
double   g_res[];
double   g_sup[];
int      g_nr         = 0;
int      g_ns         = 0;

bool     g_firstRun   = true;
string   g_alSig      = "";
string   g_alPat      = "";
string   g_alCol      = "";
string   g_srKey      = "";     // last drawn S/R state (avoids useless object updates)

bool     g_panelBuilt = false;
bool     g_chartDirty = false;
int      g_pmode      = 0;
double   g_scale      = 1.0;
int      g_pcount     = 0;
string   g_pn[];
string   g_pt[];
color    g_pc[];
int      g_pl[];
int      g_pk[];
int      g_px[];
int      g_py[];
int      g_pw[];
int      g_ph[];
int      g_v[PV_COUNT];
int      g_bg         = -1;
int      g_btn        = -1;
int      g_cw         = 0;
int      g_chh        = 0;
uint     g_lastUiMs   = 0;
string   g_fT         = "Arial Bold";
string   g_fB         = "Arial Bold";
string   g_up, g_dn, g_dot, g_dia, g_mid;

int OnInit()
  {
      // код доступен только после оплаты
   }

void OnDeinit(const int reason)
  {
   if(g_hATR != INVALID_HANDLE)
      IndicatorRelease(g_hATR);
   if(g_hATRc != INVALID_HANDLE)
      IndicatorRelease(g_hATRc);
   if(g_hMA != INVALID_HANDLE)
      IndicatorRelease(g_hMA);
   if(g_hADX != INVALID_HANDLE)
      IndicatorRelease(g_hADX);
   ObjectsDeleteAll(0, PREFIX);
   ObjectsDeleteAll(0, PNL);
   ObjectsDeleteAll(0, SRL);
   ChartRedraw();
  }

int Sc(const int v)
  {
   return (int)MathRound(v * g_scale);
  }

int Fs(const int size)
  {
   return (int)MathMax(6, MathRound(size * MathMax(0.5, MathMin(3.0, InpTextScale))));
  }

color Mix(const color a, const color b, const double t)
  {
   int ra = (int)a & 0xFF, ga = ((int)a >> 8) & 0xFF, ba = ((int)a >> 16) & 0xFF;
   int rb = (int)b & 0xFF, gb = ((int)b >> 8) & 0xFF, bb = ((int)b >> 16) & 0xFF;
   int r  = (int)MathRound(ra + (rb - ra) * t);
   int g  = (int)MathRound(ga + (gb - ga) * t);
   int bl = (int)MathRound(ba + (bb - ba) * t);
   return (color)(r | (g << 8) | (bl << 16));
  }

string Px(const double p)
  {
   return DoubleToString(p, _Digits);
  }

string SP(const double pips)
  {
   double v = MathRound(pips * 10.0) / 10.0;
   string s = (v >= 0.0) ? "+" : "-";
   return s + DoubleToString(MathAbs(v), 1) + "p";
  }

string TStr(const datetime t)
  {
   MqlDateTime d;
   TimeToStruct(t, d);
   return StringFormat("%02d.%02d %02d:%02d", d.day, d.mon, d.hour, d.min);
  }

string TfName()
  {
   string s = EnumToString((ENUM_TIMEFRAMES)_Period);
   return StringSubstr(s, 7);   // strips "PERIOD_"
  }

bool SessionApplies()
  {
   return (InpUseSession && PeriodSeconds() <= 14400);
  }

bool InSession(const int hour)
  {
   if(InpStartHour == InpEndHour)
      return true;
   if(InpStartHour < InpEndHour)
      return (hour >= InpStartHour && hour < InpEndHour);
   return (hour >= InpStartHour || hour < InpEndHour);
  }

int LotDigits()
  {
   double st = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
   int    d  = 0;
   if(st <= 0.0)
      return 2;
   while(d < 8 && MathAbs(st - MathRound(st)) > 1e-8)
     {
      st *= 10.0;
      d++;
     }
   return d;
  }

string AgeText(const int bars)
  {
   if(bars <= 0)
      return "on current bar";
   long   sec = (long)bars * (long)PeriodSeconds();
   string d;
   if(sec < 7200)
      d = IntegerToString(sec / 60) + " min";
   else
      if(sec < 172800)
         d = DoubleToString((double)sec / 3600.0, 1) + " h";
      else
         d = DoubleToString((double)sec / 86400.0, 1) + " d";
   return IntegerToString(bars) + (bars == 1 ? " bar" : " bars") + " ago (" + d + ")";
  }

double BoxPips()
  {
   return g_boxPts * _Point / g_pip;
  }

double MaxRiskP()
  {
   return (InpMaxRiskPips > 0.0) ? InpMaxRiskPips : 6.0 * BoxPips();
  }

double MaxChaseP()
  {
   return (InpMaxChasePips > 0.0) ? InpMaxChasePips : 1.0 * BoxPips();
  }

double MaxSpreadP()
  {
   return (InpMaxSpreadPips > 0.0) ? InpMaxSpreadPips : 0.3 * BoxPips();
  }

double SLBufferPrice()
  {
   int pts = (InpSLBuffer > 0) ? InpSLBuffer : (int)MathMax(1, MathRound(0.2 * g_boxPts));
   return pts * _Point;
  }

int Level(const double price)
  {
   long pts = (long)MathRound(price / _Point);
   return (int)MathFloor((double)pts / (double)g_boxPts);
  }

int NiceBoxPts(const double rawPts)
  {
   double pipPts = g_pip / _Point;
   double pips   = rawPts / pipPts;
   if(pips <= 0.0)
      return 1;
   double m[10] = {1.0, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0};
   double base  = MathPow(10.0, MathFloor(MathLog10(pips)));
   double mant  = pips / base;
   int    best  = 0;
   double bd    = DBL_MAX;
   for(int i = 0; i < 10; i++)
     {
      double d = MathAbs(MathLog(mant / m[i]));
      if(d < bd)
        {
         bd   = d;
         best = i;
        }
     }
   return (int)MathMax(1, MathRound(m[best] * base * pipPts));
  }

void AddCol(const int dir, const int bot, const int top, const int bar, const datetime t)
  {
   ArrayResize(g_cols, g_n + 1, 2048);
   g_cols[g_n].dir   = dir;
   g_cols[g_n].bot   = bot;
   g_cols[g_n].top   = top;
   g_cols[g_n].sig   = 0;
   g_cols[g_n].eff   = 0;
   g_cols[g_n].pat   = PAT_NONE;
   g_cols[g_n].blk   = 0;
   g_cols[g_n].trig  = -1;
   g_cols[g_n].b0    = bar;
   g_cols[g_n].t0    = t;
   g_cols[g_n].tTrig = 0;
   g_cols[g_n].pTrig = 0.0;
   g_n++;
  }

void BuildPF(const datetime &time[], const double &high[], const double &low[],
             const double &close[], const int total, const int startRaw)
  {
   g_n = 0;
   ArrayResize(g_cols, 0, 2048);

   int lastBar = InpClosedOnly ? total - 2 : total - 1;
   int start   = startRaw;

   if(start > 0)
     {
      int lim = MathMin(lastBar - 40, start + 1500);
      for(int s = start; s <= lim; s++)
         if((long)time[s] / 86400 != (long)time[s - 1] / 86400)
           {
            start = s;
            break;
           }
     }
   if(lastBar <= start)
      return;

   int rev  = g_rev;
   int base = Level(close[start]);

   for(int i = start + 1; i <= lastBar; i++)
     {
      double h = (InpSource == PF_HIGHLOW) ? high[i] : close[i];
      double l = (InpSource == PF_HIGHLOW) ? low[i]  : close[i];
      int hL = Level(h);
      int lL = Level(l);

      if(g_n == 0)
        {
         if(hL > base)
            AddCol(1, base, hL, i, time[i]);
         else
            if(lL < base)
               AddCol(-1, lL, base, i, time[i]);
         continue;
        }

      int j = g_n - 1;
      if(g_cols[j].dir == 1)
        {
         if(hL > g_cols[j].top)
            g_cols[j].top = hL;                                   // X extension
         else
            if(lL <= g_cols[j].top - rev)
               AddCol(-1, lL, g_cols[j].top - 1, i, time[i]);     // reversal -> O
        }
      else
        {
         if(lL < g_cols[j].bot)
            g_cols[j].bot = lL;                                   // O extension
         else
            if(hL >= g_cols[j].bot + rev)
               AddCol(1, g_cols[j].bot + 1, hL, i, time[i]);      // reversal -> X
        }

      j = g_n - 1;
      if(j >= 2 && g_cols[j].trig < 0)
        {
         double bp = g_boxPts * _Point;
         if(g_cols[j].dir == 1 && g_cols[j].top > g_cols[j - 2].top)
           {
            g_cols[j].trig  = i;
            g_cols[j].tTrig = time[i];
            g_cols[j].pTrig = (g_cols[j - 2].top + 1) * bp;
           }
         else
            if(g_cols[j].dir == -1 && g_cols[j].bot < g_cols[j - 2].bot)
              {
               g_cols[j].trig  = i;
               g_cols[j].tTrig = time[i];
               g_cols[j].pTrig = g_cols[j - 2].bot * bp;
              }
        }
     }
  }

int FilterMask(const int dir, const int bar, const datetime &time[], const double &close[], const int total)
  {
   if(bar < 0 || bar >= total)
      return (BLK_HL | BLK_EMA | BLK_ADX | BLK_SES);

   int m  = 0;
   int ix = bar - g_fOff;

   if(InpUseTrendMA && g_hMA != INVALID_HANDLE)
     {
      if(ix < 0 || ix >= ArraySize(g_maBuf))
         m |= BLK_EMA;
      else
        {
         if(dir > 0 && !(close[bar] > g_maBuf[ix]))
            m |= BLK_EMA;
         if(dir < 0 && !(close[bar] < g_maBuf[ix]))
            m |= BLK_EMA;
        }
     }
   if(InpUseADX && g_hADX != INVALID_HANDLE)
     {
      if(ix < 0 || ix >= ArraySize(g_adxBuf))
         m |= BLK_ADX;
      else
         if(g_adxBuf[ix] < InpADXMin)
            m |= BLK_ADX;
     }
   if(SessionApplies())
     {
      MqlDateTime dt;
      TimeToStruct(time[bar], dt);
      if(!InSession(dt.hour))
         m |= BLK_SES;
     }
   return m;
  }

void Analyze(const datetime &time[], const double &close[], const int total)
  {
   int lastSig = 0;
   for(int j = 0; j < g_n; j++)
     {
      g_cols[j].sig = 0;
      g_cols[j].eff = 0;
      g_cols[j].pat = PAT_NONE;
      g_cols[j].blk = 0;

      if(j >= 2)
        {
         if(g_cols[j].dir == 1 && g_cols[j].top > g_cols[j - 2].top)
           {
            g_cols[j].sig = 1;
            g_cols[j].pat = PAT_DTB;
            if(j >= 4 && g_cols[j - 2].top == g_cols[j - 4].top)
               g_cols[j].pat = PAT_TTB;

            if(g_cols[j - 2].pat == PAT_TTB && g_cols[j - 1].bot >= g_cols[j - 4].top)
               g_cols[j].pat = PAT_BULL_CAT;
           }
         else
            if(g_cols[j].dir == -1 && g_cols[j].bot < g_cols[j - 2].bot)
              {
               g_cols[j].sig = -1;
               g_cols[j].pat = PAT_DBB;
               if(j >= 4 && g_cols[j - 2].bot == g_cols[j - 4].bot)
                  g_cols[j].pat = PAT_TBB;
               if(g_cols[j - 2].pat == PAT_TBB && g_cols[j - 1].top <= g_cols[j - 4].bot)
                  g_cols[j].pat = PAT_BEAR_CAT;
              }
        }

      if(g_cols[j].pat == PAT_NONE && j >= 4)
        {
         bool tri = true;
         int  lt = INT_MAX, lb = INT_MIN;
         for(int k = j - 4; k <= j; k++)
           {
            if(g_cols[k].dir == 1)
              {
               if(g_cols[k].top >= lt) { tri = false; break; }
               lt = g_cols[k].top;
              }
            else
              {
               if(g_cols[k].bot <= lb) { tri = false; break; }
               lb = g_cols[k].bot;
              }
           }
         if(tri)
            g_cols[j].pat = PAT_TRIANGLE;
        }

      if(g_cols[j].sig != 0)
        {
         int sg  = g_cols[j].sig;
         int blk = 0;
         if(InpRequireHL)
           {
            bool okHL;
            if(j < 3)
               okHL = false;
            else
               if(sg == 1)
                  okHL = (g_cols[j - 1].bot > g_cols[j - 3].bot);   // pullback with a higher low
               else
                  okHL = (g_cols[j - 1].top < g_cols[j - 3].top);   // pullback with a lower high
            if(!okHL)
               blk |= BLK_HL;
         }
         blk |= FilterMask(sg, g_cols[j].trig, time, close, total);
         if(blk == 0)
           {
            if(InpOnlyFlips && sg == lastSig)
               blk = BLK_FLIP;
            else
               g_cols[j].eff = sg;
            lastSig = sg;
           }
         g_cols[j].blk = blk;
        }
     }
  }

string PatShort(const int p)
  {
   switch(p)
     {
      case PAT_DTB:      return "Double top breakout";
      case PAT_DBB:      return "Double bottom breakdown";
      case PAT_TTB:      return "Triple top breakout";
      case PAT_TBB:      return "Triple bottom breakdown";
      case PAT_BULL_CAT: return "Bullish catapult";
      case PAT_BEAR_CAT: return "Bearish catapult";
      case PAT_TRIANGLE: return "Triangle";
     }
   return "";
  }

string PatName(const int p)
  {
   switch(p)
     {
      case PAT_DTB:
      case PAT_TTB:
      case PAT_BULL_CAT: return PatShort(p) + " (BUY)";
      case PAT_DBB:
      case PAT_TBB:
      case PAT_BEAR_CAT: return PatShort(p) + " (SELL)";
      case PAT_TRIANGLE: return PatShort(p) + " (neutral)";
     }
   return "";
  }

void JoinAdd(string &r, const string s)
  {
   if(r != "")
      r += ", ";
   r += s;
  }

string BlkText(const int m)
  {
   string r = "";
   if((m & BLK_HL) != 0)   JoinAdd(r, "structure (higher low / lower high)");
   if((m & BLK_EMA) != 0)  JoinAdd(r, "EMA");
   if((m & BLK_ADX) != 0)  JoinAdd(r, "ADX");
   if((m & BLK_SES) != 0)  JoinAdd(r, "session");
   if((m & BLK_FLIP) != 0) JoinAdd(r, "same direction as previous signal");
   if(r == "")
      r = "-";
   return r;
  }

datetime SlotTime(const datetime &time[], const int total, const int idx)
  {
   long ps   = (long)PeriodSeconds();
   int  last = total - 1;
   if(idx < 0)
      return (datetime)((long)time[0] + (long)idx * ps);
   if(idx <= last)
      return time[idx];
   return (datetime)((long)time[last] + (long)(idx - last) * ps);
  }

datetime SlotTimeF(const datetime &time[], const int total, const double idx)
  {
   double fl = MathFloor(idx);
   int    i0 = (int)fl;
   double fr = idx - fl;
   long   t  = (long)SlotTime(time, total, i0);
   if(fr > 0.0)
     {
      t += (long)MathRound(fr * (double)PeriodSeconds());
      if(i0 >= 0 && i0 < total - 1)
        {
         long tn = (long)time[i0 + 1];
         if(t >= tn)
            t = tn - 1;      // never cross into the next bar (e.g. short months on MN1)
        }
     }
   return (datetime)t;
  }

double CalcLot(const double riskDist, double &riskMoney)
  {
   riskMoney = 0.0;
   double bal = AccountInfoDouble(ACCOUNT_BALANCE);
   if(bal <= 0.0 || InpRiskPct <= 0.0 || riskDist <= 0.0)
      return 0.0;
   riskMoney = bal * InpRiskPct / 100.0;
   double tv = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE_LOSS);
   if(tv <= 0.0)
      tv = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
   double ts = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
   if(tv <= 0.0 || ts <= 0.0)
      return 0.0;
   double lossPerLot = riskDist / ts * tv;
   if(lossPerLot <= 0.0)
      return 0.0;
   double lot = riskMoney / lossPerLot;
   double st  = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
   double mn  = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   double mx  = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
   if(st > 0.0)
      lot = MathFloor(lot / st + 1e-9) * st;
   if(lot < mn)
      return 0.0;
   if(mx > 0.0 && lot > mx)
      lot = mx;
   return NormalizeDouble(lot, LotDigits());
  }

bool Plan(const int j, const double bid, const double ask,
          double &entry, double &sl, double &tp, double &riskP, double &lot, double &riskMoney)
  {
   if(j < 1 || j >= g_n || g_cols[j].eff == 0)
      return false;
   bool   buy = (g_cols[j].eff > 0);
   double bp  = g_boxPts * _Point;
   double buf = SLBufferPrice();
   entry = buy ? ask : bid;
   sl    = buy ? g_cols[j - 1].bot * bp - buf
               : (g_cols[j - 1].top + 1) * bp + buf;
   double risk = MathAbs(entry - sl);
   if(risk <= 0.0)
      return false;
   tp    = buy ? entry + risk * InpRR : entry - risk * InpRR;
   sl    = NormalizeDouble(sl, _Digits);
   tp    = NormalizeDouble(tp, _Digits);
   riskP = risk / g_pip;
   lot   = CalcLot(risk, riskMoney);
   return true;
  }

void AddUnique(double &arr[], int &n, const double v, const double tol)
  {
   for(int i = 0; i < n; i++)
      if(MathAbs(arr[i] - v) < tol * 0.5)
         return;
   ArrayResize(arr, n + 1);
   arr[n] = v;
   n++;
  }

void CalcSR(const double cur)
  {
   g_nr = 0;
   g_ns = 0;
   if(g_n < 1)
      return;

   double bp = g_boxPts * _Point;
   double r[];
   double s[];
   int nr = 0, ns = 0;
   ArrayResize(r, 0);
   ArrayResize(s, 0);

   int from = MathMax(0, g_n - g_srLook);
   for(int j = from; j < g_n; j++)
     {
      if(g_cols[j].dir == 1)
        {
         double p = (g_cols[j].top + 1) * bp;       // upper edge of the highest X
         if(p > cur)
            AddUnique(r, nr, p, bp);
        }
      else
        {
         double p = g_cols[j].bot * bp;             // lower edge of the lowest O
         if(p < cur)
            AddUnique(s, ns, p, bp);
        }
     }
   if(nr > 1) ArraySort(r);
   if(ns > 1) ArraySort(s);

   g_nr = MathMin(2, nr);
   g_ns = MathMin(2, ns);
   ArrayResize(g_res, g_nr);
   ArrayResize(g_sup, g_ns);
   for(int q = 0; q < g_nr; q++)
      g_res[q] = r[q];                  // nearest resistance first
   for(int q2 = 0; q2 < g_ns; q2++)
      g_sup[q2] = s[ns - 1 - q2];       // nearest support first
  }

void DrawRect(const string name, datetime t1, double p1, datetime t2, double p2,
              color clr, bool fill = true, int width = 1)
  {
   if(!ObjectCreate(0, name, OBJ_RECTANGLE, g_win, t1, p1, t2, p2))
      return;
   ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
   ObjectSetInteger(0, name, OBJPROP_FILL, fill);
   ObjectSetInteger(0, name, OBJPROP_BACK, false);
   ObjectSetInteger(0, name, OBJPROP_WIDTH, width);
   ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
  }

void DrawText(const string name, datetime t, double p, const string txt, color clr,
              ENUM_ANCHOR_POINT anchor, int size = 8)
  {
   if(!ObjectCreate(0, name, OBJ_TEXT, g_win, t, p))
      return;
   ObjectSetString(0, name, OBJPROP_TEXT, txt);
   ObjectSetString(0, name, OBJPROP_FONT, "Arial Bold");
   ObjectSetInteger(0, name, OBJPROP_FONTSIZE, size);
   ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
   ObjectSetInteger(0, name, OBJPROP_ANCHOR, anchor);
   ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
  }

void DrawPriceLine(const string name, datetime t1, datetime t2, double p, color clr, const string txt)
  {
   if(ObjectCreate(0, name, OBJ_TREND, g_win, t1, p, t2, p))
     {
      ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
      ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_DASH);
      ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
      ObjectSetInteger(0, name, OBJPROP_RAY_RIGHT, true);
      ObjectSetInteger(0, name, OBJPROP_BACK, false);
      ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
      ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
     }
   DrawText(name + "_t", t1, p, txt, clr, ANCHOR_LEFT_LOWER, 8);
  }

void UpdatePriceLine(const double p)
  {
   if(!InpShowPrice || g_win < 0)
      return;
   if(MathAbs(p - g_lastPrice) < _Point * 0.5)
      return;
   if(ObjectFind(0, PREFIX + "PRICE") < 0)
      return;

   double bp = g_boxPts * _Point;
   if(g_scHi > g_scLo && (p < g_scLo + bp || p > g_scHi - bp))
     {
      g_lastSig    = "";
      g_needRecalc = true;
      return;
     }

   ObjectSetDouble(0, PREFIX + "PRICE", OBJPROP_PRICE, 0, p);
   ObjectSetDouble(0, PREFIX + "PRICE", OBJPROP_PRICE, 1, p);
   ObjectSetDouble(0, PREFIX + "PRICE_t", OBJPROP_PRICE, p);
   ObjectSetString(0, PREFIX + "PRICE_t", OBJPROP_TEXT, Px(p));
   g_lastPrice  = p;
   g_chartDirty = true;
  }

void SetSRObject(const string nm, const double price, const color clr, const string txt, const datetime t)
  {
   int w = (int)MathMax(1, MathMin(5, InpSRLineWidth));

   if(ObjectFind(0, nm) < 0)
     {
      if(!ObjectCreate(0, nm, OBJ_HLINE, 0, 0, price))
         return;
      ObjectSetInteger(0, nm, OBJPROP_STYLE, STYLE_SOLID);
      ObjectSetInteger(0, nm, OBJPROP_BACK, true);          // behind the candles
      ObjectSetInteger(0, nm, OBJPROP_SELECTABLE, false);
      ObjectSetInteger(0, nm, OBJPROP_HIDDEN, true);
      ObjectSetInteger(0, nm, OBJPROP_TIMEFRAMES, OBJ_ALL_PERIODS);
     }
   ObjectSetInteger(0, nm, OBJPROP_COLOR, clr);
   ObjectSetInteger(0, nm, OBJPROP_WIDTH, w);
   ObjectSetDouble(0, nm, OBJPROP_PRICE, 0, price);

   string tn = nm + "_t";
   bool   isRes = (StringFind(nm, "_R") >= 0);
   if(ObjectFind(0, tn) < 0)
     {
      if(!ObjectCreate(0, tn, OBJ_EDIT, 0, 0, 0))
         return;
      ObjectSetString(0, tn, OBJPROP_FONT, "Arial Bold");
      ObjectSetInteger(0, tn, OBJPROP_CORNER, CORNER_LEFT_UPPER);
      ObjectSetInteger(0, tn, OBJPROP_ALIGN, ALIGN_CENTER);
      ObjectSetInteger(0, tn, OBJPROP_READONLY, true);
      ObjectSetInteger(0, tn, OBJPROP_SELECTABLE, false);
      ObjectSetInteger(0, tn, OBJPROP_HIDDEN, true);
      ObjectSetInteger(0, tn, OBJPROP_BACK, false);
      ObjectSetInteger(0, tn, OBJPROP_ZORDER, 10);
      ObjectSetInteger(0, tn, OBJPROP_TIMEFRAMES, OBJ_ALL_PERIODS);
     }
   color bg = isRes ? InpResistLabelBg : InpSupportLabelBg;
   ObjectSetInteger(0, tn, OBJPROP_FONTSIZE, (int)MathMax(6, InpSRLabelSize));
   ObjectSetInteger(0, tn, OBJPROP_COLOR, InpSRLabelText);
   ObjectSetInteger(0, tn, OBJPROP_BGCOLOR, bg);
   ObjectSetInteger(0, tn, OBJPROP_BORDER_COLOR, bg);
   ObjectSetString(0, tn, OBJPROP_TEXT, txt);
  }

void PlaceSRLabels()
  {
   if(!InpShowSRLines)
      return;
   double pmax = ChartGetDouble(0, CHART_PRICE_MAX, 0);
   double pmin = ChartGetDouble(0, CHART_PRICE_MIN, 0);
   int    cw   = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0);
   int    ch   = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0);
   if(pmax <= pmin || cw <= 0 || ch <= 0)
      return;
   double dp = (double)TerminalInfoInteger(TERMINAL_SCREEN_DPI);
   dp = (dp >= 96.0) ? MathMin(3.0, dp / 96.0) : 1.0;       // DPI only (not the panel scale input)
   int fs   = (int)MathMax(6, InpSRLabelSize);
   int boxH = (int)MathRound((fs * 2 + 6) * dp);
   int right = cw - (int)MathRound(MathMax(0, InpSRLabelRight) * dp);
   if(ChartGetInteger(0, CHART_SHOW_PRICE_SCALE) != 0)
      right -= (i

Показано первые 50% кода (символов: 47985)

Открыть по тарифу

Купите тариф с пакетом открытий или войдите, если тариф уже есть. Открытый файл останется в вашем кабинете навсегда.

Разовая покупка файла

На этой странице откроется полный исходный код файла вместо ознакомительного фрагмента.

Чтобы оформить заказ, введите email и перейдите к оплате.

Стоимость полного кода
4 798,50₽ ≈ $58.65