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

Smart Gold Hunter v2.0

smart_gold_hunter v2.0 decomp by fixcode.mq5
Вы автор или правообладатель?

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

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

О программе

Smart Gold Hunter v2.0 — это торговый советник для MetaTrader 5, работающий на языке MQL5. Он анализирует ценовые данные инструмента, строит разметку экстремумов по типу ZigZag и на её основе формирует отложенные стоп-заявки Buy Stop и Sell Stop. Советник обрабатывает рыночные события через OnTick и управляет позициями и отложенными ордерами с учётом заданных защитных параметров.

В советнике предусмотрены несколько режимов работы: Scalper, Swinger, Positive RR, Prop Scalper и Custom. Каждый режим задаёт собственный таймфрейм сигнала и набор параметров защиты (тейк-профит, стоп-лосс, трейлинг, смещение входа, пауза). Режим Custom позволяет задавать эти значения вручную через входные параметры.

Реализованы механизмы управления рисками: расчёт объёма по проценту риска от эквити или фиксированным лотом, ограничение по максимальному спреду, защита от дублирующих цен, а также защитные окна по времени (ежедневное закрытие и защита перед выходными). Дополнительно доступны ограничения по дневной прибыли, дневному убытку и просадке эквити, а также фильтр новостей.

Советник поддерживает трейлинг-стоп, активируемый после достижения заданного порога прибыли, и специальную логику для режима Prop Scalper с временным расширением защитных уровней. Управление ордерами и позициями выполняется через торговые API MetaTrader 5.

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

НастройкаОписание
inp_Mode Выбор режима работы советника: Scalper, Swinger, Positive RR, Prop Scalper или Custom.
inp_MM Включает расчёт торгового объёма на основе процента риска от эквити вместо фиксированного лота.
inp_RiskPercent Процент риска от эквити, используемый для расчёта торгового объёма при включённом управлении капиталом.
inp_Lots Фиксированный торговый объём в лотах, используемый при отключённом управлении капиталом.
inp_MaxSpread Максимально допустимый спред в пунктах. При превышении блокируется открытие новых сделок. Значение 0 отключает проверку.
inp_UseHighSpreadDeletePending Определяет, удалять ли отложенные ордера при превышении максимального спреда.
inp_TP Размер тейк-профита в пунктах для режима Custom.
inp_SL Размер стоп-лосса в пунктах для режима Custom.
inp_TrailStart Порог прибыли в пунктах, после достижения которого активируется трейлинг-стоп (для режима Custom).
inp_TrailStop Расстояние в пунктах от текущей цены до уровня трейлинг-стопа (для режима Custom).
inp_Pause Пауза в секундах после закрытия сделки, в течение которой новые сигналы игнорируются (для режима Custom).
inp_CustomPointsAway Смещение точки входа в пунктах для режима Custom. Используется при расчёте цены отложенного ордера относительно найденного экстремума.
inp_UsePropFirmMode Главный переключатель группы защитных функций для проп-фирм. При выключении дочерние защиты неактивны.
inp_UseDailyProfitTarget Включает ограничение по дневной прибыли. При достижении цели торговля блокируется.
inp_DailyProfitTargetUSD Целевая дневная прибыль в долларах. При достижении блокируется торговля и закрываются позиции.
inp_UseDailyLossLimit Включает ограничение дневного убытка. При достижении лимита торговля блокируется.
inp_DailyLossLimitUSD Максимально допустимый дневной убыток в долларах. При достижении блокируется торговля и закрываются позиции.
inp_UseEquityProtection Включает защиту эквити по проценту просадки от выбранной базы.
inp_DailyEquityProtectionPercent Процент просадки от выбранной базы, при достижении которого срабатывает защита эквити.
inp_DailyDDReference Выбор базы для расчёта просадки: баланс на начало дня или эквити на начало дня.
inp_UseNewsFilter Включает фильтр новостей, блокирующий торговлю в заданном окне вокруг новостных событий.
inp_NewsMinutesBefore Количество минут до новостного события, в течение которых торговля блокируется.
inp_NewsMinutesAfter Количество минут после новостного события, в течение которых торговля блокируется.
inp_UseManualDailyCloseTime Включает ручное задание времени ежедневного закрытия и возобновления торговли.
inp_DailyCloseHour Час серверного времени, в который начинается ежедневное окно закрытия.
inp_DailyCloseMinute Минуты серверного времени, в которые начинается ежедневное окно закрытия.
inp_DailyReopenHour Час серверного времени, в который торговля возобновляется после ежедневного закрытия.
inp_DailyReopenMinute Минуты серверного времени, в которые торговля возобновляется после ежедневного закрытия.
inp_DailyCloseDeletePendings Определяет, удалять ли отложенные ордера при наступлении ежедневного окна закрытия.
inp_DailyCloseClosePositions Определяет, закрывать ли открытые позиции при наступлении ежедневного окна закрытия.
inp_UseFridayCloseProtection Включает защиту перед выходными: закрытие позиций и удаление отложенных ордеров в пятницу.
inp_UseManualFridayCloseTime Включает ручное задание времени пятничного закрытия.
inp_FridayCloseHour Час серверного времени, в который начинается пятничное окно закрытия.
inp_FridayCloseMinute Минуты серверного времени, в которые начинается пятничное окно закрытия.
inp_FridayCloseDeletePendings Определяет, удалять ли отложенные ордера при наступлении пятничного окна закрытия.
inp_FridayCloseClosePositions Определяет, закрывать ли открытые позиции при наступлении пятничного окна закрытия.
inp_ShowPanel Включает отображение информационной панели на графике.
inp_MagicNo Уникальный идентификатор (Magic Number) для распознавания ордеров и позиций, принадлежащих советнику.
inp_TradeComment Текстовый комментарий, добавляемый к торговым ордерам советника.
Получить полный код

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

#property description "Smart Gold Hunter v2.0 decompiled by FixcodeURL"

#property version   "2.00"
#property strict

#ifndef SGH_SIGNAL_ENGINE_MQH
#define SGH_SIGNAL_ENGINE_MQH

#define SGH_COPY_RATES_COUNT          300
#define SGH_EXTREMA_SCAN_LIMIT        100
#define SGH_ZIGZAG_DEVIATION_POINTS    10

#define SGH_ZIGZAG_BACKSTEP             3
#define SGH_TRIGGER_DISTANCE_POINTS     10

enum ENUM_SGH_PROFILE_MODE
  {
   SGH_MODE_SCALPER     = 0,
   SGH_MODE_SWINGER     = 1,
   SGH_MODE_POSITIVE_RR = 2,
   SGH_MODE_PROP_SCALPER= 3,
   SGH_MODE_CUSTOM      = 4
  };

enum ENUM_SGH_EXTREMUM_TYPE
  {
   SGH_EXTREMUM_NONE = 0,
   SGH_EXTREMUM_HIGH = 1,
   SGH_EXTREMUM_LOW  =-1
  };

enum ENUM_SGH_PENDING_DIRECTION
  {
   SGH_DIRECTION_NONE      = 0,
   SGH_DIRECTION_BUY_STOP  = 1,
   SGH_DIRECTION_SELL_STOP =-1
  };

enum ENUM_SGH_SIGNAL_STATUS
  {
   SGH_STATUS_ERROR             =-1,
   SGH_STATUS_NO_SETUP          = 0,
   SGH_STATUS_READY             = 1,
   SGH_STATUS_NOT_ENOUGH_RATES  = 2,
   SGH_STATUS_NOT_ENOUGH_SWINGS = 3,
   SGH_STATUS_NO_TICK           = 4,
   SGH_STATUS_BAD_SYMBOL        = 5
  };

struct SGHCustomValues
  {
   double takeProfitPoints;
   double stopLossPoints;
   double trailStartPoints;
   double trailDistancePoints;
   double entryOffsetPoints;
   int    pauseSeconds;
  };

struct SGHEffectiveProfile
  {
   int              requestedMode;
   ENUM_SGH_PROFILE_MODE effectiveMode;
   string           profileName;
   ENUM_TIMEFRAMES  signalTimeframe;
   int              swingDepth;
   int              zigzagDeviationPoints;
   int              zigzagBackstep;
   int              copyRatesCount;
   int              extremaScanLimit;
   long             pendingExpiryHours;
   double           takeProfitPoints;
   double           stopLossPoints;
   double           trailStartPoints;
   double           trailDistancePoints;
   double           entryOffsetPoints;
   int              pauseSeconds;
   bool             isCustom;
   bool             isPropScalper;
   int              propHoldSeconds;
   double           initialTakeProfitPoints;
   double           initialStopLossPoints;
  };

struct SGHExtremum
  {
   ENUM_SGH_EXTREMUM_TYPE type;
   double                 price;
   datetime               time;
   int                    shift;
  };

struct SGHSignal
  {
   ENUM_SGH_SIGNAL_STATUS      status;
   ENUM_SGH_PENDING_DIRECTION direction;
   ENUM_ORDER_TYPE             orderType;
   string                      symbol;
   ENUM_TIMEFRAMES             timeframe;
   double                      bid;
   double                      ask;
   double                      rawPoint;
   double                      normalizedPoint;
   double                      triggerDistance;
   double                      entryPrice;
   double                      stopLossPrice;
   double                      takeProfitPrice;
   datetime                    expiration;
   SGHExtremum                 latestExtremum;
   SGHExtremum                 previousExtremum;
   int                         copiedRates;
   int                         errorCode;
  };

void SGHSetCapturedCustomDefaults(SGHCustomValues &values)
  {

   values.takeProfitPoints    =250.0;
   values.stopLossPoints      =1500.0;
   values.trailStartPoints    =100.0;
   values.trailDistancePoints =50.0;
   values.entryOffsetPoints   =0.0;
   values.pauseSeconds        =300;
  }

void SGHResetExtremum(SGHExtremum &extremum)
  {
   extremum.type =SGH_EXTREMUM_NONE;
   extremum.price=0.0;
   extremum.time =0;
   extremum.shift=-1;
  }

void SGHResetSignal(SGHSignal &signal,const string symbol,
                    const ENUM_TIMEFRAMES timeframe)
  {
   signal.status           =SGH_STATUS_NO_SETUP;
   signal.direction        =SGH_DIRECTION_NONE;
   signal.orderType        =ORDER_TYPE_BUY_STOP;
   signal.symbol           =symbol;
   signal.timeframe        =timeframe;
   signal.bid              =0.0;
   signal.ask              =0.0;
   signal.rawPoint         =0.0;
   signal.normalizedPoint  =0.0;
   signal.triggerDistance  =0.0;
   signal.entryPrice       =0.0;
   signal.stopLossPrice    =0.0;
   signal.takeProfitPrice  =0.0;
   signal.expiration       =0;
   signal.copiedRates      =0;
   signal.errorCode        =0;
   SGHResetExtremum(signal.latestExtremum);
   SGHResetExtremum(signal.previousExtremum);
  }

void SGHSetProfileCommon(const int requested_mode,
                         SGHEffectiveProfile &profile)
  {
   profile.requestedMode             =requested_mode;
   profile.effectiveMode             =SGH_MODE_CUSTOM;
   profile.profileName               ="Custom";
   profile.signalTimeframe           =PERIOD_CURRENT;
   profile.swingDepth                =12;
   profile.zigzagDeviationPoints     =SGH_ZIGZAG_DEVIATION_POINTS;
   profile.zigzagBackstep            =SGH_ZIGZAG_BACKSTEP;
   profile.copyRatesCount            =SGH_COPY_RATES_COUNT;
   profile.extremaScanLimit          =SGH_EXTREMA_SCAN_LIMIT;
   profile.pendingExpiryHours        =27000;
   profile.takeProfitPoints          =0.0;
   profile.stopLossPoints            =0.0;
   profile.trailStartPoints          =0.0;
   profile.trailDistancePoints       =0.0;
   profile.entryOffsetPoints         =0.0;
   profile.pauseSeconds              =0;
   profile.isCustom                  =true;
   profile.isPropScalper             =false;
   profile.propHoldSeconds           =0;
   profile.initialTakeProfitPoints   =0.0;
   profile.initialStopLossPoints     =0.0;
  }

bool SGHResolveProfile(const int mode,const SGHCustomValues &custom_values,
                       SGHEffectiveProfile &profile)
  {
   SGHSetProfileCommon(mode,profile);

   switch(mode)
     {
      case SGH_MODE_SCALPER:
         profile.effectiveMode           =SGH_MODE_SCALPER;
         profile.profileName             ="Scalper";
         profile.signalTimeframe         =PERIOD_M15;
         profile.swingDepth              =18;
         profile.pendingExpiryHours      =12;
         profile.takeProfitPoints        =1500.0;
         profile.stopLossPoints          =300.0;
         profile.trailStartPoints        =140.0;
         profile.trailDistancePoints     =40.0;
         profile.entryOffsetPoints       =-160.0;
         profile.pauseSeconds            =60;
         profile.isCustom                =false;
         break;

      case SGH_MODE_SWINGER:
         profile.effectiveMode           =SGH_MODE_SWINGER;
         profile.profileName             ="Swinger";
         profile.signalTimeframe         =PERIOD_M15;
         profile.swingDepth              =12;
         profile.pendingExpiryHours      =27000;
         profile.takeProfitPoints        =4500.0;
         profile.stopLossPoints          =2500.0;
         profile.trailStartPoints        =1500.0;
         profile.trailDistancePoints     =500.0;
         profile.entryOffsetPoints       =-50.0;
         profile.pauseSeconds            =60;
         profile.isCustom                =false;
         break;

      case SGH_MODE_POSITIVE_RR:
         profile.effectiveMode           =SGH_MODE_POSITIVE_RR;
         profile.profileName             ="Positive RR";
         profile.signalTimeframe         =PERIOD_M30;
         profile.swingDepth              =18;
         profile.pendingExpiryHours      =12;
         profile.takeProfitPoints        =2250.0;
         profile.stopLossPoints          =325.0;
         profile.trailStartPoints        =575.0;
         profile.trailDistancePoints     =165.0;
         profile.entryOffsetPoints       =-175.0;
         profile.pauseSeconds            =60;
         profile.isCustom                =false;
         break;

      case SGH_MODE_PROP_SCALPER:
         profile.effectiveMode           =SGH_MODE_PROP_SCALPER;
         profile.profileName             ="Prop Scalper";
         profile.signalTimeframe         =PERIOD_M15;
         profile.swingDepth              =18;
         profile.pendingExpiryHours      =12;
         profile.takeProfitPoints        =2500.0;
         profile.stopLossPoints          =350.0;
         profile.trailStartPoints        =165.0;
         profile.trailDistancePoints     =40.0;
         profile.entryOffsetPoints       =-150.0;
         profile.pauseSeconds            =60;
         profile.isCustom                =false;
         profile.isPropScalper           =true;
         profile.propHoldSeconds         =120;
         profile.initialTakeProfitPoints =5000.0;
         profile.initialStopLossPoints   =700.0;
         break;

      default:

         profile.effectiveMode        =SGH_MODE_CUSTOM;
         profile.profileName          ="Custom";
         profile.signalTimeframe      =PERIOD_CURRENT;
         profile.swingDepth           =12;
         profile.pendingExpiryHours   =27000;
         profile.takeProfitPoints     =custom_values.takeProfitPoints;
         profile.stopLossPoints       =custom_values.stopLossPoints;
         profile.trailStartPoints     =custom_values.trailStartPoints;
         profile.trailDistancePoints  =custom_values.trailDistancePoints;
         profile.entryOffsetPoints    =custom_values.entryOffsetPoints;
         profile.pauseSeconds         =custom_values.pauseSeconds;
         profile.isCustom             =true;
         break;
     }

   if(!profile.isPropScalper)
     {
      profile.initialTakeProfitPoints=profile.takeProfitPoints;
      profile.initialStopLossPoints  =profile.stopLossPoints;
     }

   return(true);
  }

bool SGHResolveProfile(const int mode,
                       const double custom_take_profit_points,
                       const double custom_stop_loss_points,
                       const double custom_trail_start_points,
                       const double custom_trail_distance_points,
                       const int custom_pause_seconds,
                       const double custom_entry_offset_points,
                       SGHEffectiveProfile &profile)
  {
   SGHCustomValues values;
   values.takeProfitPoints    =custom_take_profit_points;
   values.stopLossPoints      =custom_stop_loss_points;
   values.trailStartPoints    =custom_trail_start_points;
   values.trailDistancePoints =custom_trail_distance_points;
   values.entryOffsetPoints   =custom_entry_offset_points;
   values.pauseSeconds        =custom_pause_seconds;
   return(SGHResolveProfile(mode,values,profile));
  }

bool SGHResolveProfile(const int mode,SGHEffectiveProfile &profile)
  {
   SGHCustomValues values;
   SGHSetCapturedCustomDefaults(values);
   return(SGHResolveProfile(mode,values,profile));
  }

double SGHRawPoint(const string symbol)
  {
   return(SymbolInfoDouble(symbol,SYMBOL_POINT));
  }

double SGHNormalizedPoint(const string symbol)
  {
   const double point=SGHRawPoint(symbol);
   const int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
   if(digits==3 || digits==5)
      return(point*10.0);
   return(point);
  }

int SGHWindowLowest(const MqlRates &rates[],const int start,const int count)
  {
   const int bars=ArraySize(rates);
   if(start<0 || start>=bars || count<=0)
      return(-1);

   int end=start+count;
   if(end>bars)
      end=bars;

   int result=start;
   double value=rates[start].low;
   for(int i=start+1;i<end;i++)
     {
      if(rates[i].low<value)
        {
         value=rates[i].low;
         result=i;
        }
     }
   return(result);
  }

int SGHWindowHighest(const MqlRates &rates[],const int start,const int count)
  {
   const int bars=ArraySize(rates);
   if(start<0 || start>=bars || count<=0)
      return(-1);

   int end=start+count;
   if(end>bars)
      end=bars;

   int result=start;
   double value=rates[start].high;
   for(int i=start+1;i<end;i++)
     {
      if(rates[i].high>value)
        {
         value=rates[i].high;
         result=i;
        }
     }
   return(result);
  }

bool SGHBuildZigZag(const MqlRates &rates[],const int depth,
                    const double deviation_price,const int backstep,
                    double &high_buffer[],double &low_buffer[],
                    double &zigzag_buffer[])
  {
   const int bars=ArraySize(rates);
   if(depth<2 || backstep<0 || bars<depth+2)
      return(false);

   ArrayResize(high_buffer,bars);
   ArrayResize(low_buffer,bars);
   ArrayResize(zigzag_buffer,bars);
   ArraySetAsSeries(high_buffer,true);
   ArraySetAsSeries(low_buffer,true);
   ArraySetAsSeries(zigzag_buffer,true);
   ArrayInitialize(high_buffer,0.0);
   ArrayInitialize(low_buffer,0.0);
   ArrayInitialize(zigzag_buffer,0.0);

   double last_low =0.0;
   double last_high=0.0;
   const int oldest_calculable=bars-depth;

   for(int shift=oldest_calculable;shift>=0;shift--)
     {
      int index=SGHWindowLowest(rates,shift,depth);
      double value=(index>=0 ? rates[index].low : 0.0);

      if(value==last_low)
         value=0.0;
      else
        {
         last_low=value;
         if((rates[shift].low-value)>deviation_price)
            value=0.0;
         else
           {
            for(int back=1;back<=backstep;back++)
              {
               const int old_shift=shift+back;
               if(old_shift>=bars)
                  break;
               const double old_value=low_buffer[old_shift];
               if(old_value!=0.0 && old_value>value)
                  low_buffer[old_shift]=0.0;
              }
           }
        }
      if(value!=0.0 && rates[shift].low==value)
         low_buffer[shift]=value;

      index=SGHWindowHighest(rates,shift,depth);
      value=(index>=0 ? rates[index].high : 0.0);

      if(value==last_high)
         value=0.0;
      else
        {
         last_high=value;
         if((value-rates[shift].high)>deviation_price)
            value=0.0;
         else
           {
            for(int back=1;back<=backstep;back++)
              {
               const int old_shift=shift+back;
               if(old_shift>=bars)
                  break;
               const double old_value=high_buffer[old_shift];
               if(old_value!=0.0 && old_value<value)
                  high_buffer[old_shift]=0.0;
              }
           }
        }
      if(value!=0.0 && rates[shift].high==value)
         high_buffer[shift]=value;
     }

   int what_to_find=0; // 0=first point, 1=high next, -1=low next
   int last_low_position =-1;
   int last_high_position=-1;
   last_low =0.0;
   last_high=0.0;

   for(int shift=oldest_calculable;shift>=0;shift--)
     {
      const double high=high_buffer[shift];
      const double low =low_buffer[shift];

      if(what_to_find==0)
        {
         if(high!=0.0)
           {
            last_high=high;
            last_high_position=shift;
            zigzag_buffer[shift]=high;
            what_to_find=-1;
           }
         if(low!=0.0)
           {
            last_low=low;
            last_low_position=shift;
            zigzag_buffer[shift]=low;
            what_to_find=1;
           }
         continue;
        }

      if(what_to_find==1)
        {
         if(low!=0.0 && low<last_low && high==0.0)
           {
            if(last_low_position>=0)
               zigzag_buffer[last_low_position]=0.0;
            last_low=low;
            last_low_position=shift;
            zigzag_buffer[shift]=low;
           }
         if(high!=0.0 && low==0.0)
           {
            last_high=high;
            last_high_position=shift;
            zigzag_buffer[shift]=high;
            what_to_find=-1;
           }
         continue;
        }

      if(high!=0.0 && high>last_high && low==0.0)
        {
         if(last_high_position>=0)
            zigzag_buffer[last_high_position]=0.0;
         last_high=high;
         last_high_position=shift;
         zigzag_buffer[shift]=high;
        }
      if(low!=0.0 && high==0.0)
        {
         last_low=low;
         last_low_position=shift;
         zigzag_buffer[shift]=low;
         what_to_find=1;
        }
     }

   return(true);
  }

bool SGHFindLatestTwoExtrema(const MqlRates &rates[],
                             const double &high_buffer[],
                             const double &low_buffer[],
                             const double &zigzag_buffer[],
                             const int scan_limit,
                             SGHExtremum &latest,
                             SGHExtremum &previous)
  {
   SGHResetExtremum(latest);
   SGHResetExtremum(previous);

   int limit=scan_limit;
   if(limit>ArraySize(zigzag_buffer))
      limit=ArraySize(zigzag_buffer);
   if(limit>ArraySize(rates))
      limit=ArraySize(rates);

   for(int shift=1;shift<limit;shift++)
     {
      const double value=zigzag_buffer[shift];
      if(value==0.0)
         continue;

      ENUM_SGH_EXTREMUM_TYPE type=SGH_EXTREMUM_NONE;
      if(high_buffer[shift]!=0.0 && value==high_buffer[shift])
         type=SGH_EXTREMUM_HIGH;
      else if(low_buffer[shift]!=0.0 && value==low_buffer[shift])
         type=SGH_EXTREMUM_LOW;

      if(latest.shift<0)
        {
         latest.type =type;
         latest.price=value;
         latest.time =rates[shift].time;
         latest.shift=shift;
         continue;
        }

      previous.type =type;
      previous.price=value;
      previous.time =rates[shift].time;
      previous.shift=shift;
      return(true);
     }

   return(false);
  }

double SGHNormalizePrice(const string symbol,const double price)
  {
   const int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
   return(NormalizeDouble(price,digits));
  }

bool SGHGenerateSignal(const string symbol,const SGHEffectiveProfile &profile,
                       SGHSignal &out)
  {
   SGHResetSignal(out,symbol,profile.signalTimeframe);

   const double point=SGHRawPoint(symbol);
   const double normalized_point=SGHNormalizedPoint(symbol);
   if(point<=0.0 || normalized_point<=0.0)
     {
      out.status=SGH_STATUS_BAD_SYMBOL;
      out.errorCode=GetLastError();
      return(false);
     }

   out.rawPoint=point;
   out.normalizedPoint=normalized_point;

   MqlRates rates[];
   ArrayResize(rates,profile.copyRatesCount);
   ArraySetAsSeries(rates,true);
   ResetLastError();
   const int copied=CopyRates(symbol,profile.signalTimeframe,0,
                              profile.copyRatesCount,rates);
   out.copiedRates=copied;
   if(copied!=profile.copyRatesCount)
     {
      out.status=SGH_STATUS_NOT_ENOUGH_RATES;
      out.errorCode=GetLastError();
      return(false);
     }

   double high_buffer[];
   double low_buffer[];
   double zigzag_buffer[];

   const double deviation_price=
      profile.zigzagDeviationPoints*normalized_point;

   if(!SGHBuildZigZag(rates,profile.swingDepth,deviation_price,
                      profile.zigzagBackstep,high_buffer,low_buffer,
                      zigzag_buffer))
     {
      out.status=SGH_STATUS_ERROR;
      return(false);
     }

   SGHExtremum latest;
   SGHExtremum previous;
   if(!SGHFindLatestTwoExtrema(rates,high_buffer,low_buffer,zigzag_buffer,
                               profile.extremaScanLimit,latest,previous))
     {
      out.status=SGH_STATUS_NOT_ENOUGH_SWINGS;
      return(false);
     }
   out.latestExtremum=latest;
   out.previousExtremum=previous;

   MqlTick tick;
   ResetLastError();
   if(!SymbolInfoTick(symbol,tick))
     {
      out.status=SGH_STATUS_NO_TICK;
      out.errorCode=GetLastError();
      return(false);
     }

   out.bid=tick.bid;
   out.ask=tick.ask;

   out.triggerDistance=SGH_TRIGGER_DISTANCE_POINTS*normalized_point;

   const SGHExtremum actionable=previous;

   if(previous.price>latest.price)
     {
      out.direction=SGH_DIRECTION_BUY_STOP;
      out.orderType=ORDER_TYPE_BUY_STOP;

      if((actionable.price-tick.ask)<=out.triggerDistance)
        {
         out.status=SGH_STATUS_NO_SETUP;
         return(false);
        }

      out.entryPrice=SGHNormalizePrice(symbol,
         actionable.price+profile.entryOffsetPoints*point);
      out.stopLossPrice=SGHNormalizePrice(symbol,
         out.entryPrice-profile.initialStopLossPoints*point);
      out.takeProfitPrice=SGHNormalizePrice(symbol,
         out.entryPrice+profile.initialTakeProfitPoints*point);
     }
   else if(latest.price>previous.price)
     {
      out.direction=SGH_DIRECTION_SELL_STOP;
      out.orderType=ORDER_TYPE_SELL_STOP;

      if((tick.bid-actionable.price)<=out.triggerDistance)
        {
         out.status=SGH_STATUS_NO_SETUP;
         return(false);
        }

      out.entryPrice=SGHNormalizePrice(symbol,
         actionable.price-profile.entryOffsetPoints*point);
      out.stopLossPrice=SGHNormalizePrice(symbol,
         out.entryPrice+profile.initialStopLossPoints*point);
      out.takeProfitPrice=SGHNormalizePrice(symbol,
         out.entryPrice-profile.initialTakeProfitPoints*point);
     }
   else
     {
      out.status=SGH_STATUS_NO_SETUP;
      return(false);
     }

   out.expiration=(datetime)(TimeCurrent()+profile.pendingExpiryHours*3600);
   out.status=SGH_STATUS_READY;
   return(true);
  }

#endif // SGH_SIGNAL_ENGINE_MQH

#ifndef __SGH_TRADE_ENGINE_MQH__
#define __SGH_TRADE_ENGINE_MQH__

enum ENUM_SGH_DD_REFERENCE
  {
   SGH_DD_DAY_START_BALANCE = 0,
   SGH_DD_DAY_START_EQUITY  = 1
  };

struct SGHTradeEngineConfig
  {
   string                symbol;
   long                  magic;
   string                comment;
   bool                  verbose;
   int                   deviationPoints;

   bool                  useRiskPercent;
   double                riskPercent;
   double                fixedLots;

   bool                  oneShot;
   double                duplicateTolerancePoints;

   double                stopLossPoints;
   double                takeProfitPoints;
   int                   pendingExpirySeconds;

   double                trailStartPoints;
   double                trailDistancePoints;

   bool                  propScalperMode;
   int                   propHoldSeconds;
   double                propInitialProtectionMultiplier;

   int                   maxSpreadPoints;
   bool                  highSpreadDeletePendings;

   bool                  useDailyProfitTarget;
   double                dailyProfitTargetMoney;
   bool                  useDailyLossLimit;
   double                dailyLossLimitMoney;
   bool                  useEquityProtection;
   double                dailyEquityProtectionPercent;
   ENUM_SGH_DD_REFERENCE ddReference;
   bool                  dailyTargetUsesEquity;
   bool                  protectionsAccountWide;

   bool                  useNewsFilter;

   bool                  useManualDailyCloseTime;
   int                   dailyCloseHour;
   int                   dailyCloseMinute;
   int                   dailyReopenHour;
   int                   dailyReopenMinute;
   bool                  dailyCloseDeletePendings;
   bool                  dailyCloseClosePositions;

   bool                  useFridayCloseProtection;
   bool                  useManualFridayCloseTime;
   int                   fridayCloseHour;
   int                   fridayCloseMinute;
   bool                  fridayCloseDeletePendings;
   bool                  fridayCloseClosePositions;
  };

struct SGHProtectionDecision
  {
   bool   blockEntries;
   bool   deletePendings;
   bool   closePositions;
   string reason;
  };

void SGHTradeEngineDefaults(SGHTradeEngineConfig &cfg)
  {
   cfg.symbol                          = _Symbol;
   cfg.magic                           = 878787;
   cfg.comment                         = "SGH clean-room";
   cfg.verbose                         = false;
   cfg.deviationPoints                 = 20;

   cfg.useRiskPercent                  = false;
   cfg.riskPercent                     = 1.0;
   cfg.fixedLots                       = 0.01;
   cfg.oneShot                         = true;
   cfg.duplicateTolerancePoints        = 1.0;

   cfg.stopLossPoints                  = 0.0;
   cfg.takeProfitPoints                = 0.0;
   cfg.pendingExpirySeconds            = 0;
   cfg.trailStartPoints                = 0.0;
   cfg.trailDistancePoints             = 0.0;

   cfg.propScalperMode                 = false;
   cfg.propHoldSeconds                 = 120;
   cfg.propInitialProtectionMultiplier = 2.0;

   cfg.maxSpreadPoints                 = 0;
   cfg.highSpreadDeletePendings        = false;

   cfg.useDailyProfitTarget            = false;
   cfg.dailyProfitTargetMoney          = 200.0;
   cfg.useDailyLossLimit               = false;
   cfg.dailyLossLimitMoney             = 100.0;
   cfg.useEquityProtection             = false;
   cfg.dailyEquityProtectionPercent    = 3.0;
   cfg.ddReference                     = SGH_DD_DAY_START_BALANCE;
   cfg.dailyTargetUsesEquity           = true;
   cfg.protectionsAccountWide          = true;
   cfg.useNewsFilter                   = false;

   cfg.useManualDailyCloseTime         = false;
   cfg.dailyCloseHour                  = 22;
   cfg.dailyCloseMinute                = 15;
   cfg.dailyReopenHour                 = 1;
   cfg.dailyReopenMinute               = 15;
   cfg.dailyCloseDeletePendings        = true;
   cfg.dailyCloseClosePositions        = true;

   cfg.useFridayCloseProtection        = false;
   cfg.useManualFridayCloseTime        = false;
   cfg.fridayCloseHour                 = 22;
   cfg.fridayCloseMinute               = 15;
   cfg.fridayCloseDeletePendings       = true;
   cfg.fridayCloseClosePositions       = true;
  }

class CSGHTradeEngine
  {
private:
   SGHTradeEngineConfig m_cfg;
   string               m_symbol;
   int                  m_digits;
   double               m_point;
   datetime             m_dayAnchor;
   double               m_dayStartBalance;
   double               m_dayStartEquity;
   bool                 m_newsStateKnown;
   bool                 m_newsBlocked;
   bool                 m_warnedUnknownNews;
   string               m_lastReason;
   ulong                m_propNormalizedTickets[];

   void Log(const string text)
     {
      if(m_cfg.verbose)
         Print("SGH TradeEngine | ",text);
     }

   void SetReason(const string text)
     {
      m_lastReason=text;
      Log(text);
     }

   void AddDecisionReason(SGHProtectionDecision &decision,const string text)
     {
      if(decision.reason=="")
         decision.reason=text;
      else if(StringFind(decision.reason,text)<0)
         decision.reason=decision.reason+"; "+text;
     }

   bool IsOurOrderSelected()
     {
      if(OrderGetString(ORDER_SYMBOL)!=m_symbol)
         return(false);
      if((long)OrderGetInteger(ORDER_MAGIC)!=m_cfg.magic)
         return(false);
      return(true);
     }

   bool IsOurPositionSelected()
     {
      if(PositionGetString(POSITION_SYMBOL)!=m_symbol)
         return(false);
      if((long)PositionGetInteger(POSITION_MAGIC)!=m_cfg.magic)
         return(false);
      return(true);
     }

   bool IsPendingType(const ENUM_ORDER_TYPE type)
     {
      return(type==ORDER_TYPE_BUY_LIMIT || type==ORDER_TYPE_SELL_LIMIT ||
             type==ORDER_TYPE_BUY_STOP  || type==ORDER_TYPE_SELL_STOP  ||
             type==ORDER_TYPE_BUY_STOP_LIMIT || type==ORDER_TYPE_SELL_STOP_LIMIT);
     }

   bool IsBuyPendingType(const ENUM_ORDER_TYPE type)
     {
      return(type==ORDER_TYPE_BUY_LIMIT || type==ORDER_TYPE_BUY_STOP ||
             type==ORDER_TYPE_BUY_STOP_LIMIT);
     }

   bool IsSellPendingType(const ENUM_ORDER_TYPE type)
     {
      return(type==ORDER_TYPE_SELL_LIMIT || type==ORDER_TYPE_SELL_STOP ||
             type==ORDER_TYPE_SELL_STOP_LIMIT);
     }

   int VolumeDigits(const double step)
     {
      if(step<=0.0)
         return(2);
      for(int digits=0; digits<=8; digits++)
        {
         const double scaled=step*MathPow(10.0,digits);
         if(MathAbs(scaled-MathRound(scaled))<1.0e-8)
            return(digits);
        }
      return(8);
     }

   ENUM_ORDER_TYPE_FILLING MarketFillingMode()
     {
      const long modes=SymbolInfoInteger(m_symbol,SYMBOL_FILLING_MODE);
      if((modes & SYMBOL_FILLING_FOK)==SYMBOL_FILLING_FOK)
         return(ORDER_FILLING_FOK);
      if((modes & SYMBOL_FILLING_IOC)==SYMBOL_FILLING_IOC)
         return(ORDER_FILLING_IOC);
      return(ORDER_FILLING_RETURN);
     }

   double BrokerMinimumDistancePrice()
     {
      const long stopLevel=SymbolInfoInteger(m_symbol,SYMBOL_TRADE_STOPS_LEVEL);
      const long freezeLevel=SymbolInfoInteger(m_symbol,SYMBOL_TRADE_FREEZE_LEVEL);
      long distanceLevel=stopLevel;
      if(freezeLevel>distanceLevel)
         distanceLevel=freezeLevel;
      return((double)distanceLevel*m_point);
     }

   ulong Deviation()
     {
      if(m_cfg.deviationPoints<=0)
         return(0);
      return((ulong)m_cfg.deviationPoints);
     }

   bool TradeEnvironmentReady()
     {
      if(!MQLInfoInteger(MQL_TESTER) && !TerminalInfoInteger(TERMINAL_CONNECTED))
        {
         SetReason("Terminal is not connected");
         return(false);
        }
      if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) ||
         !MQLInfoInteger(MQL_TRADE_ALLOWED) ||
         !AccountInfoInteger(ACCOUNT_TRADE_ALLOWED))
        {
         SetReason("Trading is not allowed");
         return(false);
        }
      return(true);
     }

   bool TradeResultSucceeded(const MqlTradeResult &result,const bool pendingRequest)
     {
      if(pendingRequest)
         return(result.retcode==TRADE_RETCODE_PLACED ||
                result.retcode==TRADE_RETCODE_DONE ||
                result.retcode==TRADE_RETCODE_DONE_PARTIAL);
      return(result.retcode==TRADE_RETCODE_DONE ||
             result.retcode==TRADE_RETCODE_DONE_PARTIAL ||
             result.retcode==TRADE_RETCODE_PLACED);
     }

   datetime ServerDayAnchor(const datetime when)
     {
      MqlDateTime tm;
      TimeToStruct(when,tm);
      tm.hour=0;
      tm.min=0;
      tm.sec=0;
      return(StructToTime(tm));
     }

   double RealizedTradingPnl(const datetime from,const datetime to)
     {
      if(!HistorySelect(from,to))
         return(0.0);

      double pnl=0.0;
      const int count=HistoryDealsTotal();
      for(int i=0;i<count;i++)
        {
         const ulong ticket=HistoryDealGetTicket(i);
         if(ticket==0)
            continue;

         const ENUM_DEAL_TYPE dealType=(ENUM_DEAL_TYPE)HistoryDealGetInteger(ticket,DEAL_TYPE);
         if(dealType!=DEAL_TYPE_BUY && dealType!=DEAL_TYPE_SELL)
            continue;

         if(!m_cfg.protectionsAccountWide)
           {
            if(HistoryDealGetString(ticket,DEAL_SYMBOL)!=m_symbol)
               continue;
            if((long)HistoryDealGetInteger(ticket,DEAL_MAGIC)!=m_cfg.magic)
               continue;
           }

         pnl+=HistoryDealGetDouble(ticket,DEAL_PROFIT);
         pnl+=HistoryDealGetDouble(ticket,DEAL_SWAP);
         pnl+=HistoryDealGetDouble(ticket,DEAL_COMMISSION);
         pnl+=HistoryDealGetDouble(ticket,DEAL_FEE);
        }
      return(pnl);
     }

   void SyncDayState(const datetime now)
     {
      const datetime anchor=ServerDayAnchor(now);
      if(anchor==m_dayAnchor)
         return;

      m_dayAnchor=anchor;
      const double realized=RealizedTradingPnl(anchor,now);

      m_dayStartBalance=AccountInfoDouble(ACCOUNT_BALANCE)-realized;

      m_dayStartEquity=AccountInfoDouble(ACCOUNT_EQUITY);
      m_warnedUnknownNews=false;
     }

   bool IsInDailyClosedWindow(const datetime now)
     {
      if(!m_cfg.useManualDailyCloseTime)
         return(false);

      MqlDateTime tm;
      TimeToStruct(now,tm);
      const int current=tm.hour*60+tm.min;
      const int closeAt=m_cfg.dailyCloseHour*60+m_cfg.dailyCloseMinute;

       const int reopenAt=m_cfg.dailyReopenHour*60;

      if(closeAt==reopenAt)
         return(false);
      if(closeAt>reopenAt)
         return(current>=closeAt || current<reopenAt);
      return(current>=closeAt && current<reopenAt);
     }

   bool IsFridayClosed(const datetime now)
     {
      if(!m_cfg.useFridayCloseProtection || !m_cfg.useManualFridayCloseTime)
         return(false);

      MqlDateTime tm;
      TimeToStruct(now,tm);
      if(tm.day_of_week==0 || tm.day_of_week==6)
         return(true);
      if(tm.day_of_week!=5)
         return(false);

      const int current=tm.hour*60+tm.min;
      const int closeAt=m_cfg.fridayCloseHour*60+m_cfg.fridayCloseMinute;
      return(current>=closeAt);
     }

   bool IsSymbolTradeSessionOpen(const string symbol)
     {

      datetime now=TimeTradeServer();
      if(now<=0)
         now=TimeCurrent();

      MqlDateTime current;
      if(now<=0 || !TimeToStruct(now,current))
         return(false);
      const int currentSeconds=current.hour*3600+current.min*60+current.sec;

      for(uint index=0;index<10;index++)
        {
         datetime from=0;
         datetime to=0;
         if(!SymbolInfoSessionTrade(symbol,
                                    (ENUM_DAY_OF_WEEK)current.day_of_week,
                                    index,from,to))
           {
            if(index==0)
              {
               long mode=SYMBOL_TRADE_MODE_DISABLED;
               if(!SymbolInfoInteger(symbol,SYMBOL_TRADE_MODE,mode))
                  return(false);

               return(mode>=SYMBOL_TRADE_MODE_LONGONLY &&
                      mode<=SYMBOL_TRADE_MODE_FULL);
              }
            break;
           }

         MqlDateTime startParts;
         MqlDateTime endParts;
         if(!TimeToStruct(from,startParts) || !TimeToStruct(to,endParts))
            continue;
         const int startSeconds=
            startParts.hour*3600+startParts.min*60+startParts.sec;
         const int endSeconds=
            endParts.hour*3600+endParts.min*60+endParts.sec;

         if(startSeconds<=endSeconds)
           {
            if(currentSeconds>=startSeconds && currentSeconds<endSeconds)
               return(true);
           }
         else if(currentSeconds>=startSeconds || currentSeconds<endSeconds)
            return(true);
        }
      return(false);
     }

   bool PropTicketProcessed(const ulong ticket)
     {
      const int size=ArraySize(m_propNormalizedTickets);
      for(int i=0;i<size;i++)
         if(m_propNormalizedTickets[i]==ticket)
            return(true);
      return(false);
     }

   void MarkPropTicketProcessed(const ulong ticket)
     {
      if(PropTicketProcessed(ticket))
         return;
      const int size=ArraySize(m_propNormalizedTickets);
      ArrayResize(m_propNormalizedTickets,size+1);
      m_propNormalizedTickets[size]=ticket;
     }

   void PrunePropTickets()
     {
      int writeIndex=0;
      const int size=ArraySize(m_propNormalizedTickets);
      for(int i=0;i<size;i++)
        {
         if(PositionSelectByTicket(m_propNormalizedTickets[i]))
           {
            m_propNormalizedTickets[writeIndex]=m_propNormalizedTickets[i];
            writeIndex++;
           }
        }
      if(writeIndex!=size)
         ArrayResize(m_propNormalizedTickets,writeIndex);
     }

   bool BuildProtectionPrices(const ENUM_ORDER_TYPE orderType,
                              const double entryPrice,
                              const bool initialPropProtection,
                              double &sl,double &tp)
     {
      double multiplier=1.0;
      if(m_cfg.propScalperMode && initialPropProtection)
         multiplier=MathMax(1.0,m_cfg.propInitialProtectionMultiplier);

      const double slDistance=m_cfg.stopLossPoints*m_point*multiplier;
      const double tpDistance=m_cfg.takeProfitPoints*m_point*multiplier;
      sl=0.0;
      tp=0.0;

      if(orderType==ORDER_TYPE_BUY || orderType==ORDER_TYPE_BUY_STOP)
        {
         if(slDistance>0.0)
            sl=NormalizePrice(entryPrice-slDistance);
         if(tpDistance>0.0)
            tp=NormalizePrice(entryPrice+tpDistance);
        }
      else if(orderType==ORDER_TYPE_SELL || orderType==ORDER_TYPE_SELL_STOP)
        {
         if(slDistance>0.0)
            sl=NormalizePrice(entryPrice+slDistance);
         if(tpDistance>0.0)
            tp=NormalizePrice(entryPrice-tpDistance);
        }
      else
         return(false);
      return(true);
     }

   bool ValidatePendingGeometry(const ENUM_ORDER_TYPE orderType,
                                const double entryPrice,
                                const double sl,const double tp)
     {
      MqlTick tick;
      if(!SymbolInfoTick(m_symbol,tick))
        {
         SetReason("No current tick");
         return(false);
        }

      const double minDistance=BrokerMinimumDistancePrice();
      const double epsilon=m_point*0.1;

      if(orderType==ORDER_TYPE_BUY_STOP)
        {
         if(entryPrice+epsilon<tick.ask+minDistance)
           {
            

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

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

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

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

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

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

Стоимость полного кода
5 137,10₽ ≈ $62.79