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? После подтверждения оплаты файл сохранится в кабинете на этот email. Если аккаунта ещё нет, мы создадим его и пришлём ссылку для задания пароля. Если аккаунт уже есть, покупка добавится в него. Укажите адрес, к которому у вас есть доступ.
Чтобы оформить заказ, введите email и перейдите к оплате.