
Создаем простой мультивалютный советник с использованием MQL5 (Часть 7): Сигналы индикаторов ZigZag и Awesome Oscillator
Введение
Мультивалютный советник — это автоматизированный торговый советник, который может открывать, закрывать и управлять ордерами на более чем одной паре символов с одного графика.
В этой статье рассмотрена торговле советником на 30 парах и использование индикаторов ZigZag и Awesome Oscillator, фильтрующих сигналы друг друга.
Как было показано в предыдущих статьях, мультивалютная торговля возможна, благодаря инструментам MQL5 как в торговом терминале, так и в тестере стратегий.
Чтобы удовлетворить потребности трейдеров, которым нужна эффективная автоматическая торговля, мы полагаемся на мощь и возможности, предоставляемые MQL5. Наша цель — создать простой мультивалютный советник, используя различные идеи и стратегии. В этой статье основное внимание будет уделено использованию индикаторов ZigZag и Awesome Oscillator, фильтрующих сигналы друг друга.
Особенности
1. Торговые пары.
Советник будет торговать на следующих парах:
Форекс:
EURUSD,GBPUSD,AUDUSD,NZDUSD,USDCAD,USDCHF,USDJPY,EURGBP,EURAUD, EURNZD, EURCAD, EURCHF, EURJPY, GBPAUD, GBPNZD,
GBPCAD,GBPCHF,GBPJPY,AUDNZD,AUDCAD,AUDCHF,AUDJPY,NZDCAD,NZDCHF,NZDJPY, CADCHF, CADJPY, CHFJPY = 28 пар
Плюс 2 пары металлов: XAUUSD (золото) и XAGUSD (серебро)
Всего 30 пар.
Чтобы обеспечить бесперебойную работу советника, обсуждаемого в этой статье, я реализовал функцию, которая автоматически обрабатывает имена символов с префиксами и/или суффиксами.
Однако эта функция работает только для имен форекс-символов и металлов в MetaTrader 5, а не для специальных символов и индексов.
1.1. Дополнительные особенности:
1.1.1. Одновалютный советник
Некоторые пользователи задавались вопросом об использовании этого мультивалютного советника в качестве одновалютного или автономного советника.
Чтобы решить эту проблему, была добавлена функция, позволяющая использовать этот мультивалютный советник в качестве одновалютного.
За это отвечает настройка Select Trading Pairs Mode (Multi or Single) - режим выбора торговых пар (несколько или одна).
1. Single Pair (одна валюта)
2. Multi Pairs (мультивалютный режим)
Если выбрана опция Single Pair:
Советник ограничен торговлей только на той паре, на которой он размещен.
Если выбрана опция Multi Pairs:
Группа настроек Selected Pairs to Trade (выбранные пары для торговли) включает 10 пар.
Одна из возможных опций называется Trader's Wishes Pairs (трейдерские пары). При ее выборе торгуемые пары должны быть введены трейдером вручную в свойствах советника.
Не забывайте, что имя введенной пары уже должно быть в списке из 30 пар.
Опцию Trader's Wishes Pairs можно использовать, чтобы советники торговали только отдельными валютами или работали автономно.
Советник будет торговать только на указанной паре.
Если трейдер введет название пары XAUUSD, советник будет торговать только на ней.
При этом положение советника на одной из 30 доступных пар значения иметь не будет.
Входные параметры советника надо настроить так, как показано на рисунке ниже.
Итак, у советника в этой статье есть два способа торговать одной валютой или работать автономно.
1. Выбрать опции Multi Pairs и Trader's Desired Pairs. После этого необходимо ввести только имя одной пары.
Эта опция позволяет советнику торговать только на паре, указанной в Trader Wishes Pairs.2. В разделе Select Trading Pairs Mode выбрать Single Pair.
Если советник применяется к паре EURUSD, он будет торговать исключительно на ней.
1.1.2. Торговля в определенное время
В группе Trade on Specific Time (торговля в определенное время) предусмотрены опции для трейдеров, которые хотят торговать в определенном часовом поясе.
Возможно, многие трейдеры хотят торговать в соответствии с часовым поясом, чтобы торгуемые пары соответствовали времени торговой сессии, поэтому в этом советнике мы по-прежнему используем опцию торговой сессии (часовой пояс).
2. Сигнальные индикаторы.
2.1. Индикатор ZigZag.
Индикатор ZigZag — это метод измерения движения цены без лишнего шума. Он работает путем определения расстояния между колебаниями цен (максимумами и минимумами). По сути, индикатор рассчитывает уровень отката от очередного максимума/минимума. Если откат превышает определенную ожидаемую величину, движение цены считается завершенным.
Как известно, ZigZag – один из старейших технических индикаторов, пришедший в торговлю валютой с фондового рынка. Он позволяет трейдерам визуализировать структуру рынка.
При попытке оценить изменения цен индикатор ZigZag помогает трейдерам автоматически отфильтровывать незначительные движения цен.
Использование индикатора ZigZag может помочь получить более четкое понимание структуры рынка.
При случайных колебаниях цен индикатор ZigZag можно использовать для определения ценовых трендов и изменений в них.
Технические аналитики отмечают, что:
- Индикатор ZigZag снижает влияние случайных колебаний цен и используется для выявления ценовых трендов и их изменений.
- Индикатор снижает уровень шума, подчеркивая основные тренды.
- Индикатор ZigZag лучше всего работает при сильных трендах.
Ограничения индикатора ZigZag.
Подобно другим индикаторам, следующим за трендом, сигналы покупки и продажи определяются на основе прошлых движений цен, которые могут неточно предсказать будущие движения. Например, большая часть тренда может уже произойти к моменту формирования линии ZigZag.
Трейдеры должны понимать, что самая последняя линия ZigZag может быть непостоянной. Когда цена меняет направление, индикатор начинает рисовать новую линию.
Если она не достигает процентной настройки индикатора и цена ценной бумаги меняет направление, линия удаляется и заменяется расширенной линией ZigZag в изначальном направлении тренда.
Настройки параметров ZigZag:
Индикатор ZigZag располагает тремя входными параметрами:
1. Depth (глубина) со значением по умолчанию 12.
Настройка относится к тому, как далеко в ряду баров графика индикатор будет формироваться.
Достаточная глубина необходима для определения максимумов и минимумов.
Это минимальное количество баров без второго максимального или минимального отклонения бара (например, если у нас есть максимум на свече x, а глубина равна 12, то индикатор не сможет нарисовать следующий максимум до тех пор, пока не наступит хотя бы x +12 свечей).
2. Deviation (отклонение) со значением по умолчанию 5.
Отклонение показывает, какой процент изменения цены необходим, чтобы изменить линию тренда с положительной на отрицательную.
3. Backstep (количество баров на истории) со значением по умолчанию 3.
Параметр определяет минимальное количество баров между максимумами и минимумами колебания
Формула индикатора ZigZag:
ZigZag (HL, %change=X, retrace=FALSE, LastExtreme=TRUE)
Если %change>=X, отобразить ZigZag
где:
HL - серия цен High-Low или цен закрытия
%change - минимальное движение цены в %
Retrace - является ли изменение откатом от предыдущего движения или абсолютным изменением от пика к впадине?
Last Extreme - если экстремальная цена одинакова в течение нескольких периодов, является ли экстремальная цена первым или последним наблюдением?
По моим наблюдениям, для индикатора ZigZag можно использовать как минимум четыре сигнальных алгоритма.
В этом советнике я ввел настройку, позволяющую трейдерам выбрать и опробовать четыре сигнала алгоритма индикатора ZigZag.
2.2. Awesome Oscillator (AO).
Awesome Oscillator (AO) — это индикатор импульса, используемый трейдерами для определения рыночных тенденций и импульса. Он был разработан Биллом Уильямсом, известным техническим аналитиком, и стал популярным инструментом среди трейдеров благодаря своей простоте и надежности.
Awesome Oscillator — это индикатор импульса рынка, который сравнивает недавние движения рынка с историческими движениями.
Он использует нулевую линию в центре, по обе стороны от которой отображаются движения цен в соответствии с результатами сравнения двух скользящих средних.
Awesome Oscillator рассчитывается путем вычитания 34-периодной простой скользящей средней (SMA) из 5-периодной SMA средней точки (H+L)/2 цены финансового инструмента.
Средняя цена считается более точным представлением истинной рыночной цены, чем цены открытия или закрытия, поскольку она учитывает как максимальные, так и минимальные цены данного периода.
АО колеблется между положительными и отрицательными значениями, причем положительные значения указывают на бычий тренд, а отрицательные - на медвежий.
Значения 34-периодной простой скользящей средней (SMA) и 5-периодной SMA постоянно задаются в коде индикатора и остаются неизменными в свойствах входных параметров индикатора.
Awesome Oscillator = 5-периодная SMA (средняя цена, 5-периодная) – 34-периодная SMA (средняя цена, 34-периодная)
Медианная цена равна: (Максимальная цена за сессию + минимальная цена за сессию) / 2.
Для индикатора АО можно использовать как минимум 3 сигнальных алгоритма.
Поэтому в этом советнике я ввел настройку, позволяющую трейдерам выбрать и опробовать три сигнала алгоритма индикатора AO.
На рисунке ниже показаны индикаторы ZigZag и Awesome Oscillator, фильтрующие сигналы друг друга.
Иллюстрации сигналов ниже представляют собой опцию 2 сигнала ZigZag и опцию 2 сигнала AO
3. Управление сделками и ордерами
Есть несколько способов управления сделками с помощью этого мультивалютного советника:
3.1. Ордера стоп-лосс.
Варианты: Use Order Stop Loss (Yes) или (No) - использовать ордер стоп-лосс: да или нет
- При выборе Use Order Stop Loss (No) все ордера будут открываться без стоп-лосса.
Открытие ордера без стоп-лосса будет безопасным, если параметр Close Trade By Opposite Signal (закрыть сделку противоположным сигналом) установлен на Yes. В противном случае существует очень высокий риск для капитала. Поэтому я добавил функцию, позволяющую проверять процентное соотношение между эквити и балансом. В этом случае, если процент эквити баланса меньше (100% - Percent Equity Risk per Trade), советник запустит функцию CheckLoss() (функция для запуска виртуального стоп-лосса) и закроет ордера, у которых есть убыток, превышающий установленное значение стоп-лосса.
//-- if(use_sl==No && CheckVSLTP==Yes) { if(!mc.CheckEquityBalance()) if(mc.CloseAllLoss()) mc.Do_Alerts(symbol,"Close order due stop in loss to secure equity."); } //--
bool MCEA::CheckEquityBalance(void) { //--- bool isgood=false; if((mc_account.Equity()/mc_account.Balance()*100) > (100.00-Risk)) isgood=true; //-- return(isgood); //--- } //-end CheckEquityBalance() //---------//
bool MCEA::CheckLoss(const string symbol,ENUM_POSITION_TYPE intype,double slc=0.0) { //--- Pips(symbol); bool inloss=false; double lossval=slc==0.0 ? (SLval*0.5) : slc; double posloss = mc_symbol.NormalizePrice(slc*pip); int ttlorder=PositionsTotal(); // number of open positions //-- for(int x=0; x<arrsymbx; x++) { string symbol=DIRI[x]; //-- for(int i=ttlorder-1; i>=0; i--) { string position_Symbol = PositionGetSymbol(i); ENUM_POSITION_TYPE type = mc_position.PositionType(); if((position_Symbol==symbol) && (mc_position.Magic()==magicEA)) { double price = mc_position.PriceCurrent(); double pos_open = mc_position.PriceOpen(); double posloss = mc_symbol.NormalizePrice(lossval*pip); double pricegab = mc_symbol.NormalizePrice(fabs(price-pos_open)); //--- if(type==intype && pricegab>posloss) inloss=true; } } } //-- return(inloss); //---- } //-end CheckLoss() //---------//
bool MCEA::CloseAllLoss(void) { //---- ResetLastError(); //-- bool orclose=false; string isloss="due stop in loss."; //-- MqlTradeRequest req={}; MqlTradeResult res={}; MqlTradeCheckResult check={}; //-- int ttlorder=PositionsTotal(); // number of open positions //-- for(int x=0; x<arrsymbx; x++) { string symbol=DIRI[x]; Pips(symbol); double posloss=mc_symbol.NormalizePrice(SLval*pip); orclose=false; //-- for(int i=ttlorder-1; i>=0; i--) { string position_Symbol = PositionGetSymbol(i); ENUM_POSITION_TYPE type = mc_position.PositionType(); if((position_Symbol==symbol) && (mc_position.Magic()==magicEA)) { double price = mc_position.PriceCurrent(); double pos_open = mc_position.PriceOpen(); double posloss = mc_symbol.NormalizePrice(SLval*pip); double pricegab = mc_symbol.NormalizePrice(fabs(price-pos_open)); ulong position_ticket = PositionGetTicket(i); //--- if(type==POSITION_TYPE_BUY && pricegab>posloss) { RefreshTick(position_Symbol); orclose = mc_trade.PositionClose(position_Symbol,slip); //--- output information about the closure PrintFormat("Close Buy %s %s %s",symbol,EnumToString(POSITION_TYPE_BUY),isloss); } if(type==POSITION_TYPE_SELL && pricegab>posloss) { RefreshTick(position_Symbol); orclose = mc_trade.PositionClose(position_Symbol,slip); //--- output information about the closure PrintFormat("Close Sell %s %s %s",symbol,EnumToString(POSITION_TYPE_BUY),isloss); } } } } //-- return(orclose); //---- } //-end CloseAllLoss() //---------//
- При выборе Use Order Stop Loss (Yes) снова появляется выбор: Use Automatic Calculation Stop Loss (Yes) или (No) - использовать автоматически рассчитываемый стоп-лосс: да или нет
- При выборе Automatic Calculation Stop Loss (Yes) стоп-лосс рассчитывается советником.
- При выборе Automatic Calculation Stop Loss (No) трейдеру необходимо ввести значение стоп-лосса в пипсах.
- При выборе Use Order Stop Loss (No) советник будет проверять выполнение условий сигнала. Если они выполняются, ордер сохраняется. Если сигнал ослаб, ордер необходимо закрыть для сохранения прибыли или состояние сигнала изменило направление, и ордер должен быть закрыт, а убыток зафиксирован.
Примечание: Перед закрытием сделки из-за слабого сигнала запрашивается подтверждение пользователя.
В части функции ExpertActionTrade():
//-- if(SaveOnRev==Yes) //-- Close Trade and Save profit due to weak signal (Yes) { mc.CheckOpenPMx(symbol); if(mc.profitb[x]>mc.minprofit && mc.xob[x]>0 && mc.GetCloseInWeakSignal(symbol,mc.Buy)==mc.Sell) { mc.CloseBuyPositions(symbol); mc.Do_Alerts(symbol,"Close BUY order "+symbol+" to save profit due to weak signal."); } if(mc.profits[x]>mc.minprofit && mc.xos[x]>0 && mc.GetCloseInWeakSignal(symbol,mc.Sell)==mc.Buy) { mc.CloseSellPositions(symbol); mc.Do_Alerts(symbol,"Close SELL order "+symbol+" to save profit due to weak signal."); } } //--
Код закрытия сделки и сохранения прибыли из-за слабого сигнала следующий:
int MCEA::GetCloseInWeakSignal(const string symbol,int exis) // Signal Indicator Position Close in profit { //--- int ret=0; int rise=1, down=-1; //-- int AOdir=AOColorSignal(symbol); int ZZDir=ZigZagSignal(symbol); //-- if(exis==down && (AOdir==rise||ZZDir==rise)) ret=rise; if(exis==rise && (AOdir==down||ZZDir==down)) ret=down; //-- return(ret); //--- } //-end GetCloseInWeakSignal() //---------//
- При No, даже несмотря на то, что сигнал ослаб, ордер все равно будет сохранен или не будет закрыт для сохранения прибыли.
- При Yes условия для индикатора ZigZag и AO следующие:
Для закрытия ордеров на покупку:
Когда индикатор АО меняет цвет с зеленого на красный (цвет по умолчанию) или когда индикатор ZigZag достигает крайне высокого положения и цена меняет направление, ордер на покупку будет закрыт.Для закрытия ордеров на продажу:
Когда индикатор АО меняет цвет с красного на зеленый (цвет по умолчанию) или когда индикатор ZigZag достигает крайне низкого положения и цена меняет направление, ордер на продажу будет закрыт.Код для установки стоп-лосса следующий:
double MCEA::OrderSLSet(const string xsymb,ENUM_ORDER_TYPE type,double atprice) { //--- slv=0.0; int x=PairsIdxArray(xsymb); Pips(xsymb); RefreshTick(xsymb); //-- switch(type) { case (ORDER_TYPE_BUY): { if(use_sl==Yes && autosl==Yes) slv=mc_symbol.NormalizePrice(atprice-38*pip); else if(use_sl==Yes && autosl==No) slv=mc_symbol.NormalizePrice(atprice-SLval*pip); else slv=0.0; //-- break; } case (ORDER_TYPE_SELL): { if(use_sl==Yes && autosl==Yes) slv=mc_symbol.NormalizePrice(atprice+38*pip); else if(use_sl==Yes && autosl==No) slv=mc_symbol.NormalizePrice(atprice+SLval*pip); else slv=0.0; } } //--- return(slv); //--- } //-end OrderSLSet() //---------//
3.2. Ордера тейк-профит
Варианты: Use Order Take Profit (Yes) или (No) - использовать тейк-профит ордера: да или нет
- При выборе Use Order Take Profit (No) все ордера будут открываться без тейк-профита.
- При выборе Use Order Take Profit (Yes) снова появляется выбор: Use Automatic Calculation Order Take Profit (Yes) или (No) - использовать автоматически рассчитываемый тейк-профит: да или нет
- При выборе Automatic Calculation Order Take Profit (Yes) тейк-профит рассчитывается советником.
- При выборе Automatic Calculation Order Take Profit (No) трейдеру необходимо ввести значение тейк-профита в пипсах.
Код для установки тейк-профита:
double MCEA::OrderTPSet(const string xsymb,ENUM_ORDER_TYPE type,double atprice) { //--- tpv=0.0; int x=PairsIdxArray(xsymb); Pips(xsymb); RefreshTick(xsymb); //-- switch(type) { case (ORDER_TYPE_BUY): { if(use_tp==Yes && autotp==Yes) tpv=mc_symbol.NormalizePrice(atprice+50*pip); else if(use_tp==Yes && autotp==No) tpv=mc_symbol.NormalizePrice(atprice+TPval*pip); else tpv=0.0; //-- break; } case (ORDER_TYPE_SELL): { if(use_tp==Yes && autotp==Yes) tpv=mc_symbol.NormalizePrice(atprice-50*pip); else if(use_tp==Yes && autotp==No) tpv=mc_symbol.NormalizePrice(atprice-TPval*pip); else tpv=0.0; } } //--- return(tpv); //--- } //-end OrderTPSet() //---------//
3.3. Trailing Stop – трейлинг-стоп
Варианты: Use Trailing Stop Loss (Yes) или (No) - использовать стоп-лосс трейлинга: да или нет
- При Use Trailing SL равном No советник не будет использовать трейлинг стоп-лосса и тейк-профита.
- При Use Trailing SL равном Yes трейдеры могут выбирать между тремя вариантами:
1. Трейлинг цены
Трейлинг-стоп выполняется советником с учетом движений цены и значения входного параметра.
2. Трейлинг индикатора
Трейлинг-стоп выполняется советником с использованием индикатора.
Согласно моим исследованиям и экспериментам, индикатор VIDYA несколько лучше подходит для трейлинг-стопов по сравнению с Parabolic SAR или несколькими вариантами скользящей средней.
По сравнению с индикатором Parabolic SAR индикатор VIDYA находится ближе к движению цены, а по сравнению с индикаторами AMA, DEMA и MA индикатор VIDYA находится еще дальше от движения цены.
Поэтому в этой статье я решил использовать индикатор VIDYA для функции трейлинг-стопа на основе индикатора.
3. Трейлинг-стоп на уровне предыдущего максимального (HIGH) или минимального (LOW) бара
Для ордеров на покупку трейлинг-стоп будет размещен по цене LOW предыдущего бара (LOW[1]).
Для ордеров на продажу трейлинг-стоп будет размещен по цене HIGH предыдущего бара (HIGH[1]).
Функция Trailing Stop Price:
double MCEA::TSPrice(const string xsymb,ENUM_POSITION_TYPE ptype,int TS_type) { //--- int br=2; double pval=0.0; int x=PairsIdxArray(xsymb); Pips(xsymb); //-- switch(TS_type) { case byprice: { RefreshTick(xsymb); if(ptype==POSITION_TYPE_BUY) pval=mc_symbol.NormalizePrice(mc_symbol.Bid()-TSval*pip); if(ptype==POSITION_TYPE_SELL) pval=mc_symbol.NormalizePrice(mc_symbol.Ask()+TSval*pip); break; } case byindi: { double VIDyAv[]; ArrayResize(VIDyAv,br,br); ArraySetAsSeries(VIDyAv,true); CopyBuffer(hVIDyAv[x],0,0,br,VIDyAv); RefreshPrice(xsymb,TFt,br); //-- if(ptype==POSITION_TYPE_BUY && (VIDyAv[0]<mc_symbol.NormalizePrice(mc_symbol.Bid()-TSval*pip))) pval=VIDyAv[0]; if(ptype==POSITION_TYPE_SELL && (VIDyAv[0]>mc_symbol.NormalizePrice(mc_symbol.Ask()+TSval*pip))) pval=VIDyAv[0]; break; } case byHiLo: { UpdatePrice(xsymb,TFt,2); //-- if(ptype==POSITION_TYPE_BUY && (HIGH[0]>HIGH[1])) pval=LOW[1]; if(ptype==POSITION_TYPE_SELL && (LOW[0]<LOW[1])) pval=HIGH[1]; break; } } //-- return(pval); //--- } //-end TSPrice() //---------//
bool MCEA::ModifyOrdersSL(const string symbx,int TS_type) { //--- ResetLastError(); MqlTradeRequest req={}; MqlTradeResult res={}; MqlTradeCheckResult check={}; //-- int TRSP=TS_type; bool modist=false; int x=PairsIdxArray(symbx); Pips(symbx); //-- int total=PositionsTotal(); //-- for(int i=total-1; i>=0; i--) { string symbol=PositionGetSymbol(i); if(symbol==symbx && mc_position.Magic()==magicEA) { ENUM_POSITION_TYPE opstype = mc_position.PositionType(); if(opstype==POSITION_TYPE_BUY) { RefreshTick(symbol); double price = mc_position.PriceCurrent(); double vtrsb = mc_symbol.NormalizePrice(TSPrice(symbx,opstype,TRSP)); double pos_open = mc_position.PriceOpen(); double pos_stop = mc_position.StopLoss(); double pos_tp = mc_position.TakeProfit(); double pos_profit = mc_position.Profit(); double pos_swap = mc_position.Swap(); double pos_comm = mc_position.Commission(); double netp=pos_profit+pos_swap+pos_comm; double modstart=mc_symbol.NormalizePrice(pos_open+TSmin*pip); double modminsl=mc_symbol.NormalizePrice(vtrsb+((TSmin-1.0)*pip)); double modbuysl=vtrsb; bool modbuy = (price>modminsl && modbuysl>modstart && (pos_stop==0.0||modbuysl>pos_stop)); //-- if(modbuy && netp>minprofit) { modist=mc_trade.PositionModify(symbol,modbuysl,pos_tp); } } if(opstype==POSITION_TYPE_SELL) { RefreshTick(symbol); double price = mc_position.PriceCurrent(); double vtrss = mc_symbol.NormalizePrice(TSPrice(symbx,opstype,TRSP)); double pos_open = mc_position.PriceOpen(); double pos_stop = mc_position.StopLoss(); double pos_tp = mc_position.TakeProfit(); double pos_profit = mc_position.Profit(); double pos_swap = mc_position.Swap(); double pos_comm = mc_position.Commission(); double netp=pos_profit+pos_swap+pos_comm; double modstart=mc_symbol.NormalizePrice(pos_open-TSmin*pip); double modminsl=mc_symbol.NormalizePrice(vtrss-((TSmin+1.0)*pip)); double modselsl=vtrss; bool modsel = (price<modminsl && modselsl<modstart && (pos_stop==0.0||modselsl<pos_stop)); //-- if(modsel && netp>minprofit) { modist=mc_trade.PositionModify(symbol,modselsl,pos_tp); } } } } //-- return(modist); //--- } //-end ModifyOrdersSL() //---------//
3.4. Трейлинг тейк-профита
Варианты: Use Trailing Take Profit (Yes) или (No) - использовать трейлинг тейк-профита: да или нет
- При Use Trailing TP равном No советник не будет выполнять трейлинг тейк-профита.
- При Use Trailing TP равном Yes необходимо установить значение Input Trailing Profit Value in Pips - значение скользящей прибыли в пипсах (по умолчанию 25 пипсов). Советник выполняет трейлинг-профит на основе значения переменной TPmin (минимальное значение скользящей прибыли).
bool MCEA::ModifyOrdersTP(const string symbx) { //--- ResetLastError(); MqlTradeRequest req={}; MqlTradeResult res={}; MqlTradeCheckResult check={}; //-- bool modist=false; int x=PairsIdxArray(symbx); Pips(symbx); //-- int total=PositionsTotal(); //-- for(int i=total-1; i>=0; i--) { string symbol=PositionGetSymbol(i); if(symbol==symbx && mc_position.Magic()==magicEA) { ENUM_POSITION_TYPE opstype = mc_position.PositionType(); if(opstype==POSITION_TYPE_BUY) { RefreshTick(symbol); double price = mc_position.PriceCurrent(); double pos_open = mc_position.PriceOpen(); double pos_stop = mc_position.StopLoss(); double pos_tp = mc_position.TakeProfit(); double modbuytp = pos_tp==0.0 ? mc_symbol.NormalizePrice(pos_open+TPmin*pip) : pos_tp; double modpostp = mc_symbol.NormalizePrice(price+TPmin*pip); bool modtpb = (price>pos_open && modbuytp-price<TPmin*pip && pos_tp<modpostp); //-- if(modtpb) { modist=mc_trade.PositionModify(symbol,pos_stop,modpostp); } } if(opstype==POSITION_TYPE_SELL) { RefreshTick(symbol); double price = mc_position.PriceCurrent(); double pos_open = mc_position.PriceOpen(); double pos_stop = mc_position.StopLoss(); double pos_tp = mc_position.TakeProfit(); double modseltp = pos_tp==0.0 ? mc_symbol.NormalizePrice(pos_open-TPmin*pip) : pos_tp; double modpostp = mc_symbol.NormalizePrice(price-TPmin*pip); bool modtps = (price<pos_open && price-modseltp<TPmin*pip && pos_tp>modpostp); //-- if(modtps) { modist=mc_trade.PositionModify(symbol,pos_stop,modpostp); } } } } //-- return(modist); //--- } //-end ModifyOrdersTP() //---------//
3.5. Закрытие сделки по противоположному сигналу
Варианты: Close Trade By Opposite Signal (Yes) or (No) - закрыть сделку по противоположному сигналу: да или нет
- При Close Trade By Opposite Signal равном Yes:
Итак, если ранее был открыт ордер на продажу, а затем сигнал индикатора развернулся, то ордер на продажу будет закрыт, а затем советник откроет ордер на покупку. И наоборот.
В части функции ExpertActionTrade():
if(Close_by_Opps==Yes && mc.xos[x]>0) mc.CloseSellPositions(symbol);bool MCEA::CloseSellPositions(const string symbol) { //--- ResetLastError(); bool sellclose=false; int total=PositionsTotal(); // number of open positions ENUM_POSITION_TYPE closetype = POSITION_TYPE_SELL; ENUM_ORDER_TYPE type_req = ORDER_TYPE_BUY; //-- MqlTradeRequest req={}; MqlTradeResult res={}; MqlTradeCheckResult check={}; //-- int x=PairsIdxArray(symbol); //--- iterate over all open positions for(int i=total-1; i>=0; i--) { if(mc_position.SelectByIndex(i)) { //--- Parameters of the order string position_Symbol = PositionGetSymbol(i); ulong position_ticket = PositionGetTicket(i); ENUM_POSITION_TYPE type = mc_position.PositionType(); //--- if the MagicNumber matches if((position_Symbol==symbol) && (mc_position.Magic()==magicEA)) { //-- if(type==closetype) { RefreshTick(position_Symbol); sellclose=mc_trade.PositionClose(position_Symbol,slip); //--- output information about the closure PrintFormat("Close Sell #%I64d %s %s",position_ticket,position_Symbol,EnumToString(type)); } } } } //--- return(sellclose); //---- } //-end CloseSellPositions() //---------//
В части функции ExpertActionTrade():
if(Close_by_Opps==Yes && mc.xob[x]>0) mc.CloseBuyPositions(symbol);
bool MCEA::CloseBuyPositions(const string symbol) { //--- //-- ResetLastError(); bool buyclose=false; int total=PositionsTotal(); // number of open positions ENUM_POSITION_TYPE closetype = POSITION_TYPE_BUY; ENUM_ORDER_TYPE type_req = ORDER_TYPE_SELL; //-- MqlTradeRequest req={}; MqlTradeResult res={}; MqlTradeCheckResult check={}; //-- int x=PairsIdxArray(symbol); //--- iterate over all open positions for(int i=total-1; i>=0; i--) { if(mc_position.SelectByIndex(i)) { //--- Parameters of the order string position_Symbol = PositionGetSymbol(i); ulong position_ticket = PositionGetTicket(i); ENUM_POSITION_TYPE type = mc_position.PositionType(); //--- if the MagicNumber matches if((position_Symbol==symbol) && (mc_position.Magic()==magicEA)) { //-- if(type==closetype) { RefreshTick(position_Symbol); buyclose=mc_trade.PositionClose(position_Symbol,slip); //--- output information about the closure PrintFormat("Close Buy #%I64d %s %s",position_ticket,position_Symbol,EnumToString(type)); } } } } //--- return(buyclose); //---- } //-end CloseBuyPositions() //---------//
Проблемы возникнут, если Close Trade By Opposite Signal равен Нет:
1. Ордер будет в два раза превышать общую сумму каждой торгуемой пары.
2. В одной паре будут ордера, находящиеся в убытке и в прибыли.
3. Эквити будет падать из-за несбалансированных условий убытков и прибылей.
Чтобы решить эту проблему, я создал несколько функций для определения условий убытка и прибыли.
В части функции ExpertActionTrade():
//-- mc.CheckOpenPMx(symbol); if(Close_by_Opps==No && (mc.xob[x]+mc.xos[x]>1)) { if(mc.CheckProfitLoss(symbol)) mc.Do_Alerts(symbol,"Close order due stop in loss."); } //--
bool MCEA::CheckProfitLoss(const string symbol) { //---- ResetLastError(); //-- bool closeinloss=false; string isloss="due stop in loss."; //-- int xx=PairsIdxArray(symbol); //-- bool BuyProfitSellLoss=(xob[xx]>0 && CheckProfit(symbol,POSITION_TYPE_BUY)) && (xos[xx]>0 && CheckLoss(symbol,POSITION_TYPE_SELL,0.0)); bool SellProfitBuyLoss=(xos[xx]>0 && CheckProfit(symbol,POSITION_TYPE_SELL)) && (xob[xx]>0 && CheckLoss(symbol,POSITION_TYPE_BUY,0.0)); //-- if(BuyProfitSellLoss && !SellProfitBuyLoss) { if(CloseSellPositions(symbol)) { PrintFormat("Close Sell %s %s %s",symbol,EnumToString(POSITION_TYPE_BUY),isloss); closeinloss=true; } } if(SellProfitBuyLoss && !BuyProfitSellLoss) { if(CloseBuyPositions(symbol)) { PrintFormat("Close Buy %s %s %s",symbol,EnumToString(POSITION_TYPE_SELL),isloss); closeinloss=true; } } //-- return(closeinloss); //---- } //-end CheckProfitLoss() //---------//
Функция CheckProfitLoss() вызовет еще две функции, которые будут сравнивать ордер в одной паре с условиями:
Buy Profit и Sell Loss или Buy Loss и Sell Profit.
- При Buy Profit и Sell Loss будет закрыт ордер на продажу.
- При Buy Loss и Sell Profit будет закрыт ордер на покупку.
bool MCEA::CheckProfit(const string symbol,ENUM_POSITION_TYPE intype) { //--- Pips(symbol); double posprofit=mc_symbol.NormalizePrice((TPval*0.5)*pip); bool inprofit=false; //-- int ttlorder=PositionsTotal(); // number of open positions //-- for(int x=0; x<arrsymbx; x++) { string symbol=DIRI[x]; //-- for(int i=ttlorder-1; i>=0; i--) { string position_Symbol = PositionGetSymbol(i); ENUM_POSITION_TYPE type = mc_position.PositionType(); if((position_Symbol==symbol) && (mc_position.Magic()==magicEA)) { double price = mc_position.PriceCurrent(); double pos_open = mc_position.PriceOpen(); double posprofit = mc_symbol.NormalizePrice((TPval*0.5)*pip); double pricegab = mc_symbol.NormalizePrice(fabs(price-pos_open)); //--- if(type==intype && posprofit<pricegab) inprofit=true; } } } //-- return(inprofit); //---- } //-end CheckProfit() //---------//
bool MCEA::CheckLoss(const string symbol,ENUM_POSITION_TYPE intype,double slc=0.0) { //--- Pips(symbol); bool inloss=false; double lossval=slc==0.0 ? (SLval*0.5) : slc; double posloss = mc_symbol.NormalizePrice(slc*pip); int ttlorder=PositionsTotal(); // number of open positions //-- for(int x=0; x<arrsymbx; x++) { string symbol=DIRI[x]; //-- for(int i=ttlorder-1; i>=0; i--) { string position_Symbol = PositionGetSymbol(i); ENUM_POSITION_TYPE type = mc_position.PositionType(); if((position_Symbol==symbol) && (mc_position.Magic()==magicEA)) { double price = mc_position.PriceCurrent(); double pos_open = mc_position.PriceOpen(); double posloss = mc_symbol.NormalizePrice(lossval*pip); double pricegab = mc_symbol.NormalizePrice(fabs(price-pos_open)); //--- if(type==intype && pricegab>posloss) inloss=true; } } } //-- return(inloss); //---- } //-end CheckLoss() //---------//
4. Ручное управление ордерами.
В этом мультивалютном советнике добавлено несколько ручных нажатий кнопок, чтобы обеспечить трейдерам эффективность и результативность при наблюдении за работой советника.
4.1. Set SL / TP All Orders (установить стоп-лосс/тейк-профит для всех ордеров):
Эта кнопка полезна, если трейдер установил Use Order Stop Loss на No и/или Use Order Take Profit на No, а затем хочет использовать стоп-лгосс и тейк-профит для всех ордеров. Одним нажатием на кнопку Set SL/TP All Orders все ордера будут изменены и будет применен стоп-лосс и/или тейк-профит.4.2. Close All Orders (закрыть все ордера):
Если трейдер хочет закрыть все ордера, он может сделать это одним щелчком по кнопке Close All Orders.
4.3. Close All Orders Profit (закрыть все прибыльные ордера):
Чтобы закрыть все прибыльные ордера, достаточно щелчка по кнопке Close All Orders Profit.
5. Управление ордерами и символами.
Очень полезной функцией для мультивалютных советников, торгующих 30 парами с одного графика, будет наличие единой панели кнопок, чтобы трейдеры могли менять графики или символы одним щелчком мыши и видеть точность сигнала индикатора при открытии или закрытии ордера советником.
Реализация планирования в MQL5-программе
1. Заголовок программы и входные свойства.
Включение файла заголовка в MQL5
//+------------------------------------------------------------------+ //| Include | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> #include <Trade\PositionInfo.mqh> #include <Trade\SymbolInfo.mqh> #include <Trade\AccountInfo.mqh> //-- CTrade mc_trade; CSymbolInfo mc_symbol; CPositionInfo mc_position; CAccountInfo mc_account; //---
Перечисление для использования часового пояса
//-- enum tm_zone { Cus_Session, // Trading on Custom Session New_Zealand, // Trading on New Zealand Session Australia, // Trading on Autralia Sydney Session Asia_Tokyo, // Trading on Asia Tokyo Session Europe_London, // Trading on Europe London Session US_New_York // Trading on US New York Session }; //--
Перечисление для выбора часов
//-- enum swhour { hr_00=0, // 00:00 hr_01=1, // 01:00 hr_02=2, // 02:00 hr_03=3, // 03:00 hr_04=4, // 04:00 hr_05=5, // 05:00 hr_06=6, // 06:00 hr_07=7, // 07:00 hr_08=8, // 08:00 hr_09=9, // 09:00 hr_10=10, // 10:00 hr_11=11, // 11:00 hr_12=12, // 12:00 hr_13=13, // 13:00 hr_14=14, // 14:00 hr_15=15, // 15:00 hr_16=16, // 16:00 hr_17=17, // 17:00 hr_18=18, // 18:00 hr_19=19, // 19:00 hr_20=20, // 20:00 hr_21=21, // 21:00 hr_22=22, // 22:00 hr_23=23 // 23:00 }; //--
Перечисление для выбора минут
//-- enum inmnt { mn_00=0, // Minute 0 mn_05=5, // Minute 5 mn_10=10, // Minute 10 mn_15=15, // Minute 15 mn_20=20, // Minute 20 mn_25=25, // Minute 25 mn_30=30, // Minute 30 mn_35=35, // Minute 35 mn_40=40, // Minute 40 mn_45=45, // Minute 45 mn_50=50, // Minute 50 mn_55=55 // Minute 55 }; //--
Перечисление для выбора 10 пар для торговли
//-- enum PairsTrade { All30, // All Forex 30 Pairs TrdWi, // Trader Wishes Pairs Usds, // Forex USD Pairs Eurs, // Forex EUR Pairs Gbps, // Forex GBP Pairs Auds, // Forex AUD Pairs Nzds, // Forex NZD Pairs Cads, // Forex CDD Pairs Chfs, // Forex CHF Pairs Jpys // Forex JPY Pairs }; //--
Перечисление YN используется для опций (Yes) или (No) в параметре советника
//-- enum YN { No, Yes }; //--
Перечисление для использования размера лота в управлении капиталом
//-- enum mmt { FixedLot, // Fixed Lot Size DynamLot // Dynamic Lot Size }; //--
Перечисление для выбора таймфрейма, который будет использоваться в расчете сигнальных индикаторов
//-- enum TFUSE { TFM5, // 00_PERIOD_M5 TFM15, // 01_PERIOD_M15 TFM30, // 02_PERIOD_M30 TFH1, // 03_PERIOD_H1 TFH2, // 04_PERIOD_H2 TFH3, // 05_PERIOD_H3 TFH4, // 06_PERIOD_H4 TFH6, // 07_PERIOD_H6 TFH8, // 08_PERIOD_H8 TFH12, // 09_PERIOD_H12 TFD1 // 10_PERIOD_D1 }; //--
При перечислении TFUSE мы ограничиваем использование расчетов временных рамок для советников только начиная с TF-M5 и заканчивая TF-D1.
Перечисление для выбора типа, который будет использоваться при расчете трейлинг-стопа.
//-- enum TrType { byprice, // Trailing Stop by Price byindi, // Trailing Stop by Indicator byHiLo // Trailing Stop in HIGH or LOW bar }; //--
Перечисление для выбора типа сделки советника: одновалютная или мультивалютная.
//-- enum MS { SP, // Single Pair MP // Multi Pairs }; //--
Перечисление для выбора алгоритма сигнала индикатора ZigZag
//-- enum SignalZZ { SZZ1, // ZigZagSignal 1 SZZ2, // ZigZagSignal 2 SZZ3, // ZigZagSignal 3 SZZ4 // ZigZagSignal 4 }; //--
Перечисление для выбора алгоритма сигнала индикатора AO
//-- enum SignalAO { SAO1, // AOSignal 1 SAO2, // AOSignal 2 SAO3 // AOSignal 3 }; //--
Входные параметры советника
//--- input group "=== Global Strategy EA Parameter ==="; // Global Strategy EA Parameter input TFUSE tfinuse = TFH4; // Select Expert TimeFrame, default PERIOD_H4 //--- input group "=== ZigZag Indicator Input Properties ==="; // ZigZag Indicator Input Properties input int zzDepth = 12; // Input ZigZag Depth, default 12 input int zzDevia = 5; // Input ZigZag Deviation, default 5 input int zzBackS = 3; // Input ZigZag Back Step, default 3 input SignalZZ sigzz = SZZ2; // Select ZigZag Signal to Use input SignalAO sigao = SAO2; // Select AO Signal to Use //--- input group "=== Selected Pairs to Trade ==="; // Selected Pairs to trading input MS trademode = MP; // Select Trading Pairs Mode (Multi or Single) input PairsTrade usepairs = All30; // Select Pairs to Use input string traderwishes = "eg. eurusd,usdchf"; // If Use Trader Wishes Pairs, input pair name here, separate by comma //-- input group "=== Money Management Lot Size Parameter ==="; // Money Management Lot Size Parameter input mmt mmlot = DynamLot; // Money Management Type input double Risk = 10.0; // Percent Equity Risk per Trade (Min=1.0% / Max=10.0%) input double Lots = 0.01; // Input Manual Lot Size FixedLot //--Trade on Specific Time input group "=== Trade on Specific Time ==="; // Trade on Specific Time input YN trd_time_zone = Yes; // Select If You Like to Trade on Specific Time Zone input tm_zone session = Cus_Session; // Select Trading Time Zone input swhour stsescuh = hr_00; // Time Hour to Start Trading Custom Session (0-23) input inmnt stsescum = mn_15; // Time Minute to Start Trading Custom Session (0-55) input swhour clsescuh = hr_23; // Time Hour to Stop Trading Custom Session (0-23) input inmnt clsescum = mn_55; // Time Minute to Stop Trading Custom Session (0-55) //--Day Trading On/Off input group "=== Day Trading On/Off ==="; // Day Trading On/Off input YN ttd0 = No; // Select Trading on Sunday (Yes) or (No) input YN ttd1 = Yes; // Select Trading on Monday (Yes) or (No) input YN ttd2 = Yes; // Select Trading on Tuesday (Yes) or (No) input YN ttd3 = Yes; // Select Trading on Wednesday (Yes) or (No) input YN ttd4 = Yes; // Select Trading on Thursday (Yes) or (No) input YN ttd5 = Yes; // Select Trading on Friday (Yes) or (No) input YN ttd6 = No; // Select Trading on Saturday (Yes) or (No) //--Trade & Order management Parameter input group "=== Trade & Order management Parameter ==="; // Trade & Order management Parameter input YN use_sl = No; // Use Order Stop Loss (Yes) or (No) input YN autosl = Yes; // Use Automatic Calculation Stop Loss (Yes) or (No) input double SLval = 30.0; // If Not Use Automatic SL - Input SL value in Pips input YN use_tp = Yes; // Use Order Take Profit (Yes) or (No) input YN autotp = Yes; // Use Automatic Calculation Take Profit (Yes) or (No) input double TPval = 60.0; // If Not Use Automatic TP - Input TP value in Pips input YN TrailingSL = Yes; // Use Trailing Stop Loss (Yes) or (No) input TrType trlby = byHiLo; // Select Trailing Stop Type input double TSval = 10.0; // If Use Trailing Stop by Price Input value in Pips input double TSmin = 5.0; // Minimum Pips to start Trailing Stop input YN TrailingTP = Yes; // Use Trailing Take Profit (Yes) or (No) input double TPmin = 25.0; // Input Trailing Profit Value in Pips input YN Close_by_Opps = Yes; // Close Trade By Opposite Signal (Yes) or (No) input YN SaveOnRev = Yes; // Close Trade and Save profit due to weak signal (Yes) or (No) input YN CheckVSLTP = Yes; // Check Virtual SL/TP & Close Loss Trade (Yes) or (No) //--Others Expert Advisor Parameter input group "=== Others Expert Advisor Parameter ==="; // Others EA Parameter input YN alerts = Yes; // Display Alerts / Messages (Yes) or (No) input YN UseEmailAlert = No; // Email Alert (Yes) or (No) input YN UseSendnotify = No; // Send Notification (Yes) or (No) input YN trade_info_display = Yes; // Select Display Trading Info on Chart (Yes) or (No) input ulong magicEA = 20240218; // Expert ID (Magic Number) //---
Примечание: Если входной параметр Expert ID (Magic Number) оставить пустым, советник сможет управлять ордерами, открытыми вручную.
В группе входных свойств Global Strategy EA Parameters советника трейдерам предлагается выбрать таймфрейм для расчета сигналов индикатора. Значение по умолчанию - PERIOD_H4.
В группе свойств входных данных Select Pairs to Trade необходимо выбрать пару для торговли из 10 предоставленных вариантов. Значение по умолчанию - All Forex 30 Pairs (все 30 форекс-пар).
Чтобы настроить торгуемую пару, вызовем функцию HandlingSymbolArrays(). С помощью функции HandlingSymbolArrays() мы будем обрабатывать все торгуемые пары.
void MCEA::HandlingSymbolArrays(void) { //--- string All30[]={"EURUSD","GBPUSD","AUDUSD","NZDUSD","USDCAD","USDCHF","USDJPY","EURGBP", "EURAUD","EURNZD","EURCAD","EURCHF","EURJPY","GBPAUD","GBPNZD","GBPCAD", "GBPCHF","GBPJPY","AUDNZD","AUDCAD","AUDCHF","AUDJPY","NZDCAD","NZDCHF", "NZDJPY","CADCHF","CADJPY","CHFJPY","XAUUSD","XAGUSD"}; // 30 pairs string USDs[]={"USDCAD","USDCHF","USDJPY","AUDUSD","EURUSD","GBPUSD","NZDUSD","XAUUSD","XAGUSD"}; // USD pairs string EURs[]={"EURAUD","EURCAD","EURCHF","EURGBP","EURJPY","EURNZD","EURUSD"}; // EUR pairs string GBPs[]={"GBPAUD","GBPCAD","GBPCHF","EURGBP","GBPJPY","GBPNZD","GBPUSD"}; // GBP pairs string AUDs[]={"AUDCAD","AUDCHF","EURAUD","GBPAUD","AUDJPY","AUDNZD","AUDUSD"}; // AUD pairs string NZDs[]={"AUDNZD","NZDCAD","NZDCHF","EURNZD","GBPNZD","NZDJPY","NZDUSD"}; // NZD pairs string CADs[]={"AUDCAD","CADCHF","EURCAD","GBPCAD","CADJPY","NZDCAD","USDCAD"}; // CAD pairs string CHFs[]={"AUDCHF","CADCHF","EURCHF","GBPCHF","NZDCHF","CHFJPY","USDCHF"}; // CHF pairs string JPYs[]={"AUDJPY","CADJPY","CHFJPY","EURJPY","GBPJPY","NZDJPY","USDJPY"}; // JPY pairs //-- sall=ArraySize(All30); arusd=ArraySize(USDs); areur=ArraySize(EURs); aretc=ArraySize(JPYs); ArrayResize(VSym,sall,sall); ArrayCopy(VSym,All30,0,0,WHOLE_ARRAY); //-- if(usepairs==TrdWi && StringFind(traderwishes,"eg.",0)<0) { string to_split=traderwishes; // A string to split into substrings pairs name string sep=","; // A separator as a character ushort u_sep; // The code of the separator character //--- Get the separator code u_sep=StringGetCharacter(sep,0); //--- Split the string to substrings int p=StringSplit(to_split,u_sep,SPC); if(p>0) { for(int i=0; i<p; i++) StringToUpper(SPC[i]); //-- for(int i=0; i<p; i++) { if(ValidatePairs(SPC[i])<0) ArrayRemove(SPC,i,1); } } arspc=ArraySize(SPC); } //-- SetSymbolNamePS(); // With this function we will detect whether the Symbol Name has a prefix and/or suffix //-- if(inpre>0 || insuf>0) { if(usepairs==TrdWi && arspc>0) { for(int t=0; t<arspc; t++) { SPC[t]=pre+SPC[t]+suf; } } //-- for(int t=0; t<sall; t++) { All30[t]=pre+All30[t]+suf; } for(int t=0; t<arusd; t++) { USDs[t]=pre+USDs[t]+suf; } for(int t=0; t<areur; t++) { EURs[t]=pre+EURs[t]+suf; } for(int t=0; t<aretc; t++) { GBPs[t]=pre+GBPs[t]+suf; } for(int t=0; t<aretc; t++) { AUDs[t]=pre+AUDs[t]+suf; } for(int t=0; t<aretc; t++) { NZDs[t]=pre+NZDs[t]+suf; } for(int t=0; t<aretc; t++) { CADs[t]=pre+CADs[t]+suf; } for(int t=0; t<aretc; t++) { CHFs[t]=pre+CHFs[t]+suf; } for(int t=0; t<aretc; t++) { JPYs[t]=pre+JPYs[t]+suf; } } //-- ArrayCopy(VSym,All30,0,0,WHOLE_ARRAY); ArrayResize(AS30,sall,sall); ArrayCopy(AS30,All30,0,0,WHOLE_ARRAY); for(int x=0; x<sall; x++) {SymbolSelect(AS30[x],true);} if(ValidatePairs(Symbol())>=0) symbfix=true; if(!symbfix) { Alert("Expert Advisors will not trade on pairs "+Symbol()); Alert("-- "+expname+" -- ",Symbol()," -- expert advisor will be Remove from the chart."); ExpertRemove(); } //-- switch(usepairs) { case 0: // All Forex & Metal 30 Pairs { ArrayResize(DIRI,sall,sall); arrsymbx=sall; ArraySymbolResize(); ArrayCopy(DIRI,All30,0,0,WHOLE_ARRAY); pairs="Multi Currency "+string(sall)+" Pairs"; //-- break; } case 1: // Trader wishes pairs { ArrayResize(DIRI,arspc,arspc); arrsymbx=arspc; ArraySymbolResize(); ArrayCopy(DIRI,SPC,0,0,WHOLE_ARRAY); pairs="("+string(arspc)+") Trader Wishes Pairs"; //-- break; } case 2: // USD pairs { ArrayResize(DIRI,arusd,arusd); arrsymbx=arusd; ArraySymbolResize(); ArrayCopy(DIRI,USDs,0,0,WHOLE_ARRAY); pairs="("+string(arusd)+") Multi Currency USD Pairs"; //-- break; } case 3: // EUR pairs { ArrayResize(DIRI,areur,areur); arrsymbx=aretc; ArraySymbolResize(); ArrayCopy(DIRI,EURs,0,0,WHOLE_ARRAY); pairs="("+string(aretc)+") Forex EUR Pairs"; //-- break; } case 4: // GBP pairs { ArrayResize(DIRI,aretc,aretc); arrsymbx=aretc; ArraySymbolResize(); ArrayCopy(DIRI,GBPs,0,0,WHOLE_ARRAY); pairs="("+string(aretc)+") Forex GBP Pairs"; //-- break; } case 5: // AUD pairs { ArrayResize(DIRI,aretc,aretc); arrsymbx=aretc; ArraySymbolResize(); ArrayCopy(DIRI,AUDs,0,0,WHOLE_ARRAY); pairs="("+string(aretc)+") Forex AUD Pairs"; //-- break; } case 6: // NZD pairs { ArrayResize(DIRI,aretc,aretc); arrsymbx=aretc; ArraySymbolResize(); ArrayCopy(DIRI,NZDs,0,0,WHOLE_ARRAY); pairs="("+string(aretc)+") Forex NZD Pairs"; //-- break; } case 7: // CAD pairs { ArrayResize(DIRI,aretc,aretc); arrsymbx=aretc; ArraySymbolResize(); ArrayCopy(DIRI,CADs,0,0,WHOLE_ARRAY); pairs="("+string(aretc)+") Forex CAD Pairs"; //-- break; } case 8: // CHF pairs { ArrayResize(DIRI,aretc,aretc); arrsymbx=aretc; ArraySymbolResize(); ArrayCopy(DIRI,CHFs,0,0,WHOLE_ARRAY); pairs="("+string(aretc)+") Forex CHF Pairs"; //-- break; } case 9: // JPY pairs { ArrayResize(DIRI,aretc,aretc); arrsymbx=aretc; ArraySymbolResize(); ArrayCopy(DIRI,JPYs,0,0,WHOLE_ARRAY); pairs="("+string(aretc)+") Forex JPY Pairs"; //-- break; } } //-- return; //--- } //-end HandlingSymbolArrays() //---------//
Внутри функции HandlingSymbolArrays() вызовем функцию SetSymbolNamePS(). Используя SetSymbolNamePS(), мы сможем обрабатывать имена символов с префиксом и/или суффиксом.
void MCEA::SetSymbolNamePS(void) { //--- int sym_Lenpre=0; int sym_Lensuf=0; string sym_pre=""; string sym_suf=""; SymbolSelect(Symbol(),true); string insymbol=Symbol(); int inlen=StringLen(insymbol); int toseek=-1; string dep=""; string bel=""; string sym_use =""; int pairx=-1; string xcur[]={"EUR","GBP","AUD","NZD","USD","CAD","CHF"}; // 7 major currency int xcar=ArraySize(xcur); //-- for(int x=0; x<xcar; x++) { toseek=StringFind(insymbol,xcur[x],0); if(toseek>=0) { pairx=x; break; } } if(pairx>=0) { int awl=toseek-3 <0 ? 0 : toseek-3; int sd=StringFind(insymbol,"SD",0); if(toseek==0 && sd<4) { dep=StringSubstr(insymbol,toseek,3); bel=StringSubstr(insymbol,toseek+3,3); sym_use=dep+bel; } else if(toseek>0) { dep=StringSubstr(insymbol,toseek,3); bel=StringSubstr(insymbol,toseek+3,3); sym_use=dep+bel; } else { dep=StringSubstr(insymbol,awl,3); bel=StringSubstr(insymbol,awl+3,3); sym_use=dep+bel; } } //-- string sym_nmx=sym_use; int lensx=StringLen(sym_nmx); //-- if(inlen>lensx && lensx==6) { sym_Lenpre=StringFind(insymbol,sym_nmx,0); sym_Lensuf=inlen-lensx-sym_Lenpre; //-- if(sym_Lenpre>0) { sym_pre=StringSubstr(insymbol,0,sym_Lenpre); for(int i=0; i<xcar; i++) if(StringFind(sym_pre,xcur[i],0)>=0) sym_pre=""; } if(sym_Lensuf>0) { sym_suf=StringSubstr(insymbol,sym_Lenpre+lensx,sym_Lensuf); for(int i=0; i<xcar; i++) if(StringFind(sym_suf,xcur[i],0)>=0) sym_suf=""; } } //-- pre=sym_pre; suf=sym_suf; inpre=StringLen(pre); insuf=StringLen(suf); posCur1=inpre; posCur2=posCur1+3; //-- return; //--- } //-end SetSymbolNamePS() //---------//
Примечание: Советник проверяет пары. Если трейдер допустил ошибку при вводе названия пары либо проверка пары не удалась, советник выдаст предупреждение и будет удален с графика.
В группе свойств входных данных Trade on Specific Time, трейдеру нужно выбрать Trade on Specific Time Zone (Yes) или (No) - торговать в определенном часовом поясе: да или нет.
При Trade on Specific Time Zone option равном No, советник будет торговать с 00:15 до 23:59.
При Yes необходимо выбрать параметры перечисления:
- Trading on Custom Session (пользовательская сессия)
- Trading on New Zealand Session (новозеландская сессия)
- Trading on Australia Sydney Session (сиднейская сессия)
- Trading on Asia Tokyo Session (токийская сессия)
- Trading on Europe London Session (лондонская сессия)
- Trading on America New York Session (нью-йоркская сессия)
По умолчанию Trading on Specific Time Zones равен Yes и установлен на Trading on Custom Sessions.
При выборе пользовательской сессии:
Трейдеры должны установить время или часы и минуты для начала торговли и часы и минуты для завершения торговли. Таким образом, советник будет выполнять действия только в течение указанного времени.
В остальных случаях время начала и окончания торговли выбирается советником.
2. Класс для работы советника
Чтобы создать и настроить рабочий процесс советника, мы создали класс, который объявляет все необходимые переменные, объекты и функции.
//+------------------------------------------------------------------+ //| Class for working Expert Advisor | //+------------------------------------------------------------------+ class MCEA { //--- private: //---- int x_year; // Year int x_mon; // Month int x_day; // Day of the month int x_hour; // Hour in a day int x_min; // Minutes int x_sec; // Seconds //-- int oBm, oSm, ldig; //--- Variables used in prefix and suffix symbols int posCur1, posCur2; int inpre, insuf; bool symbfix; string pre,suf; string prefix,suffix; //--- Variables are used in Trading Time Zone int ishour, onhour; int tftrlst, tfcinws; datetime rem, znop, zncl, zntm; datetime SesCuOp, SesCuCl, Ses01Op, Ses01Cl, Ses02Op, Ses02Cl, Ses03Op, Ses03Cl, Ses04Op, Ses04Cl, Ses05Op, Ses05Cl, SesNoOp, SesNoCl; //-- string tz_ses, tz_opn, tz_cls; //-- string tmopcu, tmclcu, tmop01, tmcl01, tmop02, tmcl02, tmop03, tmcl03, tmop04, tmcl04, tmop05, tmcl05, tmopno, tmclno; //---------------------- //-- double LotPS; double point; double slv, tpv, pip, xpip; double SARstep, SARmaxi; double floatprofit, fixclprofit; //-- string pairs, hariini, daytrade, trade_mode; //-- double OPEN[], HIGH[], LOW[], CLOSE[]; datetime TIME[]; datetime closetime; //-- //------------ //------------ void SetSymbolNamePS(void); void HandlingSymbolArrays(void); void Set_Time_Zone(void); void Time_Zone(void); bool Trade_session(void); string PosTimeZone(void); int ThisTime(const int reqmode); int ReqTime(datetime reqtime,const int reqmode); //-- int DirectionMove(const string symbol,const ENUM_TIMEFRAMES stf); int GetIndiSignals(const string symbol); int ZigZagSignal(const string symbol); int AOSignal(const string symbol); int AOColorSignal(const string symbol); int PARSAR05(const string symbol); int PARSAR15(const string symbol); int LotDig(const string symbol); //-- double MLots(const string symbx); double NonZeroDiv(double val1,double val2); double OrderSLSet(const string xsymb,ENUM_ORDER_TYPE type,double atprice); double OrderTPSet(const string xsymb,ENUM_ORDER_TYPE type,double atprice); double SetOrderSL(const string xsymb,ENUM_POSITION_TYPE type,double atprice); double SetOrderTP(const string xsymb,ENUM_POSITION_TYPE type,double atprice); double TSPrice(const string xsymb,ENUM_POSITION_TYPE ptype,int TS_type); //-- string ReqDate(int d,int h,int m); string TF2Str(ENUM_TIMEFRAMES period); string timehr(int hr,int mn); string TradingDay(void); string AccountMode(); string GetCommentForOrder(void) { return(expname); } //------------ public: //--- //-- ZigZag_AO_MCEA Config -- string DIRI[], AS30[], VSym[]; string SPC[]; string USD[]; string EUR[]; string GBP[]; string AUD[]; string NZD[]; string CAD[]; string CHF[]; string JPY[]; //-- string expname; string indiname1; //-- //--- Indicators Handle int hZigZag[], hAO[]; int hVIDyAv[]; int hPar05[], hPar15[]; //--- int ALO, dgts, arrsar, arrsymbx; int sall, arusd, areur, aretc, arspc, arper; ulong slip; //-- double profitb[], profits[]; double minprofit; //-- int Buy, Sell; int ccur, psec, xtto, checktml; int OpOr[],xob[],xos[]; //-- int year, // Year mon, // Month day, // Day hour, // Hour min, // Minutes sec, // Seconds dow, // Day of week (0-Sunday, 1-Monday, ... ,6-Saturday) doy; // Day number of the year (January 1st is assigned the number value of zero) //-- ENUM_TIMEFRAMES TFt, TFT05, TFT15; //-- bool PanelExtra; //------------ MCEA(void); ~MCEA(void); //------------ //-- virtual void ZigZag_AO_MCEA_Config(void); virtual void ExpertActionTrade(void); //-- void ArraySymbolResize(void); void CurrentSymbolSet(const string symbol); void Pips(const string symbol); void TradeInfo(void); void Do_Alerts(const string symbx,string msgText); void CheckOpenPMx(const string symbx); void SetSLTPOrders(void); void CloseAllOrders(void); void CheckClose(const string symbx); void TodayOrders(void); void UpdatePrice(const string symbol,ENUM_TIMEFRAMES xtf); void UpdatePrice(const string symbol,ENUM_TIMEFRAMES xtf,int bars); void RefreshPrice(const string symbx,ENUM_TIMEFRAMES xtf,int bars); //-- bool CheckEquityBalance(void); bool RefreshTick(const string symbx); bool TradingToday(void); bool OpenBuy(const string symbol); bool OpenSell(const string symbol); bool ModifyOrderSLTP(double mStop,double ordtp); bool ModifyOrdersSL(const string symbx,int TS_type); bool ModifyOrdersTP(const string symbx); bool CloseAllProfit(void); bool CloseAllLoss(void); bool ManualCloseAllProfit(void); bool CheckProfitLoss(const string symbol); bool CloseBuyPositions(const string symbol); bool CloseSellPositions(const string symbol); bool CheckProfit(const string symbol,ENUM_POSITION_TYPE intype); bool CheckLoss(const string symbol,ENUM_POSITION_TYPE intype,double slc=0.0); //-- int PairsIdxArray(const string symbol); int ValidatePairs(const string symbol); int GetOpenPosition(const string symbol); int GetCloseInWeakSignal(const string symbol,int exis); //-- string getUninitReasonText(int reasonCode); //-- //------------ //--- }; //-end class MCEA //---------// MCEA mc; //---------//
Первая и наиболее важная функция в рабочем процессе мультивалютного советника, вызываемая из OnInit(), — это ZigZag_AO_MCEA_Config().
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(void) { //--- mc.ZigZag_AO_MCEA_Config(); //-- return(INIT_SUCCEEDED); //--- } //-end OnInit() //---------//
В функции ZigZag_AO_MCEA_Config() настраиваются все используемые символы, все таймфреймы, все используемые хэндлы индикаторов и некоторые важные функции заголовка включаемого файла.
Функция ZigZag_AO_MCEA_Config() описывает и реализует способы обработки таймфреймов и создания хэндлв индикаторов для всех индикаторов, используемых в рабочем процессе советника.
//+------------------------------------------------------------------+ //| Expert Configuration | //+------------------------------------------------------------------+ void MCEA::ZigZag_AO_MCEA_Config(void) { //--- //-- HandlingSymbolArrays(); // With this function we will handle all pairs that will be traded //-- TFT05=PERIOD_M5; TFT15=PERIOD_M15; ENUM_TIMEFRAMES TFs[]={PERIOD_M5,PERIOD_M15,PERIOD_M30,PERIOD_H1,PERIOD_H2,PERIOD_H3,PERIOD_H4,PERIOD_H6,PERIOD_H8,PERIOD_H12,PERIOD_D1}; int arTFs=ArraySize(TFs); //-- for(int x=0; x<arTFs; x++) if(tfinuse==x) TFt=TFs[x]; // TF for calculation signal //-- //-- Indicators handle for all symbol for(int x=0; x<arrsymbx; x++) { hZigZag[x] = iCustom(DIRI[x],TFt,indiname1,zzDepth,zzDevia,zzBackS); //-- Handle for the ZigZag indicator hAO[x] = iAO(DIRI[x],TFt); //-- Handle for the Awesome_Oscillator indicator hVIDyAv[x] = iVIDyA(DIRI[x],TFt,9,12,0,PRICE_WEIGHTED); //-- Handle for the VIDYA indicator for Trailing Stop hPar05[x] = iSAR(DIRI[x],TFT05,SARstep,SARmaxi); //-- Handle for the iSAR indicator for M5 Timeframe hPar15[x] = iSAR(DIRI[x],TFT15,SARstep,SARmaxi); //-- Handle for the iSAR indicator for M15 Timeframe //-- } //-- TesterHideIndicators(true); minprofit=NormalizeDouble(TSmin/100.0,2); //-- ALO=(int)mc_account.LimitOrders()>sall ? sall : (int)mc_account.LimitOrders(); if(Close_by_Opps==No) { if((int)mc_account.LimitOrders()>=(sall*2)) ALO=sall*2; else ALO=(int)(mc_account.LimitOrders()/2); } //-- LotPS=(double)ALO; //-- mc_trade.SetExpertMagicNumber(magicEA); mc_trade.SetDeviationInPoints(slip); mc_trade.SetMarginMode(); Set_Time_Zone(); //-- return; //--- } //-end ZigZag_AO_MCEA_Config() //---------//
3. Функция Expert tick и необходимые действия
В функции Expert tick (OnTick()) мы будем вызывать одну из основных функций мультивалютного советника, а именно функцию ExpertActionTrade().
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(void) { //--- mc.ExpertActionTrade(); //-- return; //--- } //-end OnTick() //---------//
В эту функцию включен весь процесс работы советника.
Это означает, что функция ExpertActionTrade() будет выполнять все действия и управлять автоматической торговлей, включая открытие/закрытие ордеров, трейлинг-стоп, трейлинг тейк-профита и другие дополнительные действия.
void MCEA::ExpertActionTrade(void) { //--- //--Check Trading Terminal ResetLastError(); //-- if(!MQLInfoInteger(MQL_TRADE_ALLOWED) && mc.checktml==0) //-- Check whether MT5 Algorithmic trading is Allow or Prohibit { mc.Do_Alerts(Symbol(),"Trading Expert at "+Symbol()+" are NOT Allowed by Setting."); mc.checktml=1; //-- Variable checktml is given a value of 1, so that the alert is only done once. return; } //-- if(!DisplayManualButton("M","C","R")) DisplayManualButton(); //-- Show the expert manual button panel //-- if(trade_info_display==Yes) mc.TradeInfo(); //-- Displayed Trading Info on Chart //--- //-- int mcsec=mc.ThisTime(mc.sec); //-- if(fmod((double)mcsec,5.0)==0) mc.ccur=mcsec; //-- if(mc.ccur!=mc.psec) { string symbol; //-- Here we start with the rotation of the name of all symbol or pairs to be traded for(int x=0; x<mc.arrsymbx && !IsStopped(); x++) { //-- switch(trademode) { case SP: { if(mc.DIRI[x]!=Symbol()) continue; symbol=Symbol(); mc.pairs=mc.pairs+" ("+symbol+")"; break; } case MP: { if(mc.DIRI[x]==Symbol()) symbol=Symbol(); else symbol=mc.DIRI[x]; break; } } //-- mc.CurrentSymbolSet(symbol); //-- if(mc.TradingToday() && mc.Trade_session()) { //-- mc.OpOr[x]=mc.GetOpenPosition(symbol); //-- Get trading signals to open positions //-- //-- and store in the variable OpOr[x] if(mc.OpOr[x]==mc.Buy) //-- If variable OpOr[x] get result of GetOpenPosition(symbol) as "Buy" (value=1) { //-- mc.CheckOpenPMx(symbol); //-- if(Close_by_Opps==Yes && mc.xos[x]>0) mc.CloseSellPositions(symbol); //-- if(mc.xob[x]==0 && mc.xtto<mc.ALO) mc.OpenBuy(symbol); else if(mc.xtto>=mc.ALO) { //-- mc.Do_Alerts(symbol,"Maximum amount of open positions and active pending orders has reached"+ "\n the limit = "+string(mc.ALO)+" Orders "); //-- mc.CheckOpenPMx(symbol); //-- if(mc.xos[x]>0 && mc.profits[x]<-1.02 && mc.xob[x]==0) {mc.CloseSellPositions(symbol); mc.OpenBuy(symbol);} else mc.CloseAllProfit(); //-- if(mc.xos[x]>0 && use_sl==No && CheckVSLTP==Yes) { if(mc.CheckLoss(symbol,POSITION_TYPE_SELL,SLval)) if(mc.CloseSellPositions(symbol)) mc.Do_Alerts(symbol,"Check Profit Trade and Close order due stop in loss."); } } } if(mc.OpOr[x]==mc.Sell) //-- If variable OpOr[x] get result of GetOpenPosition(symbol) as "Sell" (value=-1) { //-- mc.CheckOpenPMx(symbol); //-- if(Close_by_Opps==Yes && mc.xob[x]>0) mc.CloseBuyPositions(symbol); //-- if(mc.xos[x]==0 && mc.xtto<mc.ALO) mc.OpenSell(symbol); else if(mc.xtto>=mc.ALO) { //-- mc.Do_Alerts(symbol,"Maximum amount of open positions and active pending orders has reached"+ "\n the limit = "+string(mc.ALO)+" Orders "); //-- mc.CheckOpenPMx(symbol); //-- if(mc.xob[x]>0 && mc.profitb[x]<-1.02 && mc.xos[x]==0) {mc.CloseBuyPositions(symbol); mc.OpenSell(symbol);} else mc.CloseAllProfit(); //-- if(mc.xob[x]>0 && use_sl==No && CheckVSLTP==Yes) { if(mc.CheckLoss(symbol,POSITION_TYPE_BUY,SLval)) if(mc.CloseBuyPositions(symbol)) mc.Do_Alerts(symbol,"Check Profit Trade and Close order due stop in loss."); } } } } //-- mc.CheckOpenPMx(symbol); //-- if(mc.xtto>0) { //-- if(SaveOnRev==Yes) //-- Close Trade and Save profit due to weak signal (Yes) { mc.CheckOpenPMx(symbol); if(mc.profitb[x]>mc.minprofit && mc.xob[x]>0 && mc.GetCloseInWeakSignal(symbol,mc.Buy)==mc.Sell) { mc.CloseBuyPositions(symbol); mc.Do_Alerts(symbol,"Close BUY order "+symbol+" to save profit due to weak signal."); } if(mc.profits[x]>mc.minprofit && mc.xos[x]>0 && mc.GetCloseInWeakSignal(symbol,mc.Sell)==mc.Buy) { mc.CloseSellPositions(symbol); mc.Do_Alerts(symbol,"Close SELL order "+symbol+" to save profit due to weak signal."); } } //-- if(TrailingSL==Yes) mc.ModifyOrdersSL(symbol,trlby); //-- Use Trailing Stop Loss (Yes) if(TrailingTP==Yes) mc.ModifyOrdersTP(symbol); //-- Use Trailing Take Profit (Yes) } //-- mc.CheckOpenPMx(symbol); if(Close_by_Opps==No && (mc.xob[x]+mc.xos[x]>1)) { if(mc.CheckProfitLoss(symbol)) mc.Do_Alerts(symbol,"Close order due stop in loss."); } //-- if(use_sl==No && CheckVSLTP==Yes) { if(!mc.CheckEquityBalance()) if(mc.CloseAllLoss()) mc.Do_Alerts(symbol,"Close order due stop in loss to secure equity."); } //-- mc.CheckClose(symbol); } //-- mc.psec=mc.ccur; } //-- return; //--- } //-end ExpertActionTrade() //---------//
Между тем, группа свойств Day Trading On/Off позволяет трейдерам торговать в определенные дни с воскресенья по субботу. Это позволяет трейдерам включать или отключать советников в определенный день, используя опции Yes или No.
//--Day Trading On/Off input group "=== Day Trading On/Off ==="; // Day Trading On/Off input YN ttd0 = No; // Select Trading on Sunday (Yes) or (No) input YN ttd1 = Yes; // Select Trading on Monday (Yes) or (No) input YN ttd2 = Yes; // Select Trading on Tuesday (Yes) or (No) input YN ttd3 = Yes; // Select Trading on Wednesday (Yes) or (No) input YN ttd4 = Yes; // Select Trading on Thursday (Yes) or (No) input YN ttd5 = Yes; // Select Trading on Friday (Yes) or (No) input YN ttd6 = No; // Select Trading on Saturday (Yes) or (No)
Порядок выполнения Day Trading On/Off следующий:
bool MCEA::TradingToday(void) { //--- bool tradetoday=false; int trdday=ThisTime(dow); hariini="No"; //-- int ttd[]; ArrayResize(ttd,7); ttd[0]=ttd0; ttd[1]=ttd1; ttd[2]=ttd2; ttd[3]=ttd3; ttd[4]=ttd4; ttd[5]=ttd5; ttd[6]=ttd6; //-- if(ttd[trdday]==Yes) {tradetoday=true; hariini="Yes";} //-- return(tradetoday); //--- } //-end TradingToday() //---------//
Примечание: Условия Day Trading On/Off будут отображаться в торговой информации на графике.
Это работа функции TradingDay():
string MCEA::TradingDay(void) { //--- int trdday=ThisTime(dow); switch(trdday) { case 0: daytrade="Sunday"; break; case 1: daytrade="Monday"; break; case 2: daytrade="Tuesday"; break; case 3: daytrade="Wednesday"; break; case 4: daytrade="Thursday"; break; case 5: daytrade="Friday"; break; case 6: daytrade="Saturday"; break; } return(daytrade); //--- } //-end TradingDay() //---------//
Трейдеры имеют возможность торговать по часовому поясу в группе свойств советника Trade at Specific Time (торговля в определенное время).
Примечание: Как уже упоминалось выше, в остальных случаях время начала и окончания торговли выбирается советником.
Таким образом, в свойствах Expert Entry трейдерам нужно только установить час и минуту начала торговли, а также час и минуту окончания торговли для пользовательской сессии.
Функция ExpertActionTrade() была расширена за счет вызова логической функции Trade_session() специально для торговли в часовых поясах.
При Trade_session() равном true, советник работает до завершения, а при false, советник будет выполнять только задачи Close Trade and Save profit due to weak signal (Yes) (закрыть сделку и сохранить прибыль из-за слабого сигнала (Да)) и Trailing stop (Yes) (трейлинг-стоп (да)).
bool MCEA::Trade_session(void) { //--- bool trd_ses=false; ishour=ThisTime(hour); if(ishour!=onhour) Set_Time_Zone(); datetime tcurr=TimeCurrent(); // Server Time //-- switch(session) { case Cus_Session: { if(tcurr>=SesCuOp && tcurr<=SesCuCl) trd_ses=true; break; } case New_Zealand: { if(tcurr>=Ses01Op && tcurr<=Ses01Cl) trd_ses=true; break; } case Australia: { if(tcurr>=Ses02Op && tcurr<=Ses02Cl) trd_ses=true; break; } case Asia_Tokyo: { if(tcurr>=Ses03Op && tcurr<=Ses03Cl) trd_ses=true; break; } case Europe_London: { if(tcurr>=Ses04Op && tcurr<=Ses04Cl) trd_ses=true; break; } case US_New_York: { if(tcurr>=Ses05Op && tcurr<=Ses05Cl) trd_ses=true; break; } } //-- if(trd_time_zone==No) { if(tcurr>=SesNoOp && tcurr<=SesNoCl) trd_ses=true; } //-- onhour=ishour; //-- return(trd_ses); //--- } //-end Trade_session() //---------//
Процесс торговли в определенное время выглядит следующим образом: сначала функция ExpertActionTrade() вызывает функцию Trade_session(), затем функция Trade_session() вызывает функцию Set_Time_Zone() и, наконец, функция Set_Time_Zone() вызывает функцию Time_Zone().
void MCEA::Set_Time_Zone(void) { //--- //-- Server Time==TimeCurrent() datetime TTS=TimeTradeServer(); datetime GMT=TimeGMT(); //-- MqlDateTime svrtm,gmttm; TimeToStruct(TTS,svrtm); TimeToStruct(GMT,gmttm); int svrhr=svrtm.hour; // Server time hour int gmthr=gmttm.hour; // GMT time hour int difhr=svrhr-gmthr; // Time difference Server time to GMT time //-- int NZSGMT=12; // New Zealand Session GMT/UTC+12 int AUSGMT=10; // Australia Sydney Session GMT/UTC+10 int TOKGMT=9; // Asia Tokyo Session GMT/UTC+9 int EURGMT=0; // Europe London Session GMT/UTC 0 int USNGMT=-5; // US New York Session GMT/UTC-5 //-- int NZSStm=8; // New Zealand Session time start: 08:00 Local Time int NZSCtm=17; // New Zealand Session time close: 17:00 Local Time int AUSStm=7; // Australia Sydney Session time start: 07:00 Local Time int AUSCtm=17; // Australia Sydney Session time close: 17:00 Local Time int TOKStm=9; // Asia Tokyo Session time start: 09:00 Local Time int TOKCtm=18; // Asia Tokyo Session time close: 18:00 Local Time int EURStm=9; // Europe London Session time start: 09:00 Local Time int EURCtm=19; // Europe London Session time close: 19:00 Local Time int USNStm=8; // US New York Session time start: 08:00 Local Time int USNCtm=17; // US New York Session time close: 17:00 Local Time //-- int nzo = (NZSStm+difhr-NZSGMT)<0 ? 24+(NZSStm+difhr-NZSGMT) : (NZSStm+difhr-NZSGMT); int nzc = (NZSCtm+difhr-NZSGMT)<0 ? 24+(NZSCtm+difhr-NZSGMT) : (NZSCtm+difhr-NZSGMT); //-- int auo = (AUSStm+difhr-AUSGMT)<0 ? 24+(AUSStm+difhr-AUSGMT) : (AUSStm+difhr-AUSGMT); int auc = (AUSCtm+difhr-AUSGMT)<0 ? 24+(AUSCtm+difhr-AUSGMT) : (AUSCtm+difhr-AUSGMT); //-- int tko = (TOKStm+difhr-TOKGMT)<0 ? 24+(TOKStm+difhr-TOKGMT) : (TOKStm+difhr-TOKGMT); int tkc = (TOKCtm+difhr-TOKGMT)<0 ? 24+(TOKCtm+difhr-TOKGMT) : (TOKCtm+difhr-TOKGMT); //-- int euo = (EURStm+difhr-EURGMT)<0 ? 24+(EURStm+difhr-EURGMT) : (EURStm+difhr-EURGMT); int euc = (EURCtm+difhr-EURGMT)<0 ? 24+(EURCtm+difhr-EURGMT) : (EURCtm+difhr-EURGMT); //-- int uso = (USNStm+difhr-USNGMT)<0 ? 24+(USNStm+difhr-USNGMT) : (USNStm+difhr-USNGMT); int usc = (USNCtm+difhr-USNGMT)<0 ? 24+(USNCtm+difhr-USNGMT) : (USNCtm+difhr-USNGMT); if(usc==0||usc==24) usc=23; //-- //---Trading on Custom Session int _days00=ThisTime(day); int _days10=ThisTime(day); if(stsescuh>clsescuh) _days10=ThisTime(day)+1; tmopcu=ReqDate(_days00,stsescuh,stsescum); tmclcu=ReqDate(_days10,clsescuh,clsescum); //-- //--Trading on New Zealand Session GMT/UTC+12 int _days01=ThisTime(hour)<nzc ? ThisTime(day)-1 : ThisTime(day); int _days11=ThisTime(hour)<nzc ? ThisTime(day) : ThisTime(day)+1; tmop01=ReqDate(_days01,nzo,0); // start: 08:00 Local Time == 20:00 GMT/UTC tmcl01=ReqDate(_days11,nzc-1,59); // close: 17:00 Local Time == 05:00 GMT/UTC //-- //--Trading on Australia Sydney Session GMT/UTC+10 int _days02=ThisTime(hour)<auc ? ThisTime(day)-1 : ThisTime(day); int _days12=ThisTime(hour)<auc ? ThisTime(day) : ThisTime(day)+1; tmop02=ReqDate(_days02,auo,0); // start: 07:00 Local Time == 21:00 GMT/UTC tmcl02=ReqDate(_days12,auc-1,59); // close: 17:00 Local Time == 07:00 GMT/UTC //-- //--Trading on Asia Tokyo Session GMT/UTC+9 int _days03=ThisTime(hour)<tkc ? ThisTime(day) : ThisTime(day)+1; int _days13=ThisTime(hour)<tkc ? ThisTime(day) : ThisTime(day)+1; tmop03=ReqDate(_days03,tko,0); // start: 09:00 Local Time == 00:00 GMT/UTC tmcl03=ReqDate(_days13,tkc-1,59); // close: 18:00 Local Time == 09:00 GMT/UTC //-- //--Trading on Europe London Session GMT/UTC 00:00 int _days04=ThisTime(hour)<euc ? ThisTime(day) : ThisTime(day)+1; int _days14=ThisTime(hour)<euc ? ThisTime(day) : ThisTime(day)+1; tmop04=ReqDate(_days04,euo,0); // start: 09:00 Local Time == 09:00 GMT/UTC tmcl04=ReqDate(_days14,euc-1,59); // close: 19:00 Local Time == 19:00 GMT/UTC //-- //--Trading on US New York Session GMT/UTC-5 int _days05=ThisTime(hour)<usc ? ThisTime(day) : ThisTime(day)+1; int _days15=ThisTime(hour)<=usc ? ThisTime(day) : ThisTime(day)+1; tmop05=ReqDate(_days05,uso,0); // start: 08:00 Local Time == 13:00 GMT/UTC tmcl05=ReqDate(_days15,usc,59); // close: 17:00 Local Time == 22:00 GMT/UTC //-- //--Not Use Trading Time Zone if(trd_time_zone==No) { tmopno=ReqDate(ThisTime(day),0,15); tmclno=ReqDate(ThisTime(day),23,59); } //-- Time_Zone(); //-- return; //--- } //-end Set_Time_Zone() //---------//
void MCEA::Time_Zone(void) { //--- //-- tz_ses=""; //-- switch(session) { case Cus_Session: { SesCuOp=StringToTime(tmopcu); SesCuCl=StringToTime(tmclcu); zntm=SesCuOp; znop=SesCuOp; zncl=SesCuCl; tz_ses="Custom_Session"; tz_opn=timehr(stsescuh,stsescum); tz_cls=timehr(clsescuh,clsescum); break; } case New_Zealand: { Ses01Op=StringToTime(tmop01); Ses01Cl=StringToTime(tmcl01); zntm=Ses01Op; znop=Ses01Op; zncl=Ses01Cl; tz_ses="New_Zealand/Oceania"; tz_opn=timehr(ReqTime(Ses01Op,hour),ReqTime(Ses01Op,min)); tz_cls=timehr(ReqTime(Ses01Cl,hour),ReqTime(Ses01Cl,min)); break; } case Australia: { Ses02Op=StringToTime(tmop02); Ses02Cl=StringToTime(tmcl02); zntm=Ses02Op; znop=Ses02Op; zncl=Ses02Cl; tz_ses="Australia Sydney"; tz_opn=timehr(ReqTime(Ses02Op,hour),ReqTime(Ses02Op,min)); tz_cls=timehr(ReqTime(Ses02Cl,hour),ReqTime(Ses02Cl,min)); break; } case Asia_Tokyo: { Ses03Op=StringToTime(tmop03); Ses03Cl=StringToTime(tmcl03); zntm=Ses03Op; znop=Ses03Op; zncl=Ses03Cl; tz_ses="Asia/Tokyo"; tz_opn=timehr(ReqTime(Ses03Op,hour),ReqTime(Ses03Op,min)); tz_cls=timehr(ReqTime(Ses03Cl,hour),ReqTime(Ses03Cl,min)); break; } case Europe_London: { Ses04Op=StringToTime(tmop04); Ses04Cl=StringToTime(tmcl04); zntm=Ses04Op; znop=Ses04Op; zncl=Ses04Cl; tz_ses="Europe/London"; tz_opn=timehr(ReqTime(Ses04Op,hour),ReqTime(Ses04Op,min)); tz_cls=timehr(ReqTime(Ses04Cl,hour),ReqTime(Ses04Cl,min)); break; } case US_New_York: { Ses05Op=StringToTime(tmop05); Ses05Cl=StringToTime(tmcl05); zntm=Ses05Op; znop=Ses05Op; zncl=Ses05Cl; tz_ses="US/New_York"; tz_opn=timehr(ReqTime(Ses05Op,hour),ReqTime(Ses05Op,min)); tz_cls=timehr(ReqTime(Ses05Cl,hour),ReqTime(Ses05Cl,min)); break; } } //-- if(trd_time_zone==No) { SesNoOp=StringToTime(tmopno); SesNoCl=StringToTime(tmclno); zntm=SesNoOp; znop=SesNoOp; zncl=SesNoCl; tz_ses="Not Use Time Zone"; tz_opn=timehr(ReqTime(SesNoOp,hour),ReqTime(SesNoOp,min)); tz_cls=timehr(ReqTime(SesNoCl,hour),ReqTime(SesNoCl,min)); } //-- return; //--- } //-end Time_Zone() //---------//
4. Получение торговых сигналов для открытия позиций
Для получения сигнала на открытие позиции функция ExpertActionTrade() вызывает функцию GetOpenPosition().
int MCEA::GetOpenPosition(const string symbol) // Signal Open Position { //--- int ret=0; int rise=1, down=-1; //-- int ZZAOSignal=GetIndiSignals(symbol); int dirmove=DirectionMove(symbol,TFt); int psar15=PARSAR15(symbol); //-- if(ZZAOSignal==rise && dirmove==rise && psar15==rise) ret=rise; if(ZZAOSignal==down && dirmove==down && psar15==down) ret=down; //-- return(ret); //--- } //-end GetOpenPosition() //---------//
Функция GetOpenPosition() вызовет три функции:
- GetIndiSignals(symbol)
- DirectionMove(symbol,TFt)
- PARSAR15(symbol)
4.1. Функция GetIndiSignals(symbol) вызовет две функции:
- ZigZagSignal(symbol)
- AOSignal(symbol)
4.1.1. ZigZag Signal
Внутри функции ZigZagSignal() мы используем и вызываем одну функцию - PairsIdxArray().
int xx=PairsIdxArray(symbol)
int MCEA::PairsIdxArray(const string symbol) { //--- int pidx=-1; //-- for(int x=0; x<arrsymbx; x++) { if(DIRI[x]==symbol) { pidx=x; break; } } //-- return(pidx); //--- } //-end PairsIdxArray() //---------//
Функция PairsIdxArray() используется для получения имени запрошенного символа и хэндлов индикаторов.
Затем вызывается соответствующий хэндл индикатора, чтобы получить значение буфера индикатора ZigZag с этого таймфрейма.
//-- Indicators handle for all symbol for(int x=0; x<arrsymbx; x++) { hZigZag[x] = iCustom(DIRI[x],TFt,indiname1,zzDepth,zzDevia,zzBackS); //-- Handle for the ZigZag indicator hAO[x] = iAO(DIRI[x],TFt); //-- Handle for the Awesome_Oscillator indicator hVIDyAv[x] = iVIDyA(DIRI[x],TFt,9,12,0,PRICE_WEIGHTED); //-- Handle for the VIDYA indicator for Trailing Stop hPar05[x] = iSAR(DIRI[x],TFT05,SARstep,SARmaxi); //-- Handle for the iSAR indicator for M5 Timeframe hPar15[x] = iSAR(DIRI[x],TFT15,SARstep,SARmaxi); //-- Handle for the iSAR indicator for M15 Timeframe //-- } //--
Итак, чтобы получить значение буфера индикатора ZigZag, мы скопируем каждый буфер из хэндла индикатора ZigZag.
Чтобы скопировать буфер ZigZag (буфер 0) из хэндла индикатора ZigZag в целевой массив:
CopyBuffer(hZigZag[x],0,0,barcalc,ZZBuffer);
Кроме того, вызовем функцию UpdatePrice(), чтобы получить значение максимальной и минимальной цен, которые будут использоваться для получения позиций максимального и минимального баров буфера ZigZag.
void MCEA::UpdatePrice(const string symbol,ENUM_TIMEFRAMES xtf) { //--- //-- ArrayFree(OPEN); ArrayFree(HIGH); ArrayFree(LOW); ArrayFree(CLOSE); ArrayFree(TIME); //-- ArrayResize(OPEN,arper,arper); ArrayResize(HIGH,arper,arper); ArrayResize(LOW,arper,arper); ArrayResize(CLOSE,arper,arper); ArrayResize(TIME,arper,arper); //-- ArraySetAsSeries(OPEN,true); ArraySetAsSeries(HIGH,true); ArraySetAsSeries(LOW,true); ArraySetAsSeries(CLOSE,true); ArraySetAsSeries(TIME,true); //-- ArrayInitialize(OPEN,0.0); ArrayInitialize(HIGH,0.0); ArrayInitialize(LOW,0.0); ArrayInitialize(CLOSE,0.0); ArrayInitialize(TIME,0); //-- RefreshPrice(symbol,xtf,arper); //-- int co=CopyOpen(symbol,xtf,0,arper,OPEN); int ch=CopyHigh(symbol,xtf,0,arper,HIGH); int cl=CopyLow(symbol,xtf,0,arper,LOW); int cc=CopyClose(symbol,xtf,0,arper,CLOSE); int ct=CopyTime(symbol,xtf,0,arper,TIME); //-- return; //--- } //-end UpdatePrice() //---------//
И чтобы получить максимальную и минимальную позиции баров ZigZag, перебираем и сравниваем максимальные и минимальные цены.
//-- for(int i=barcalc-1; i>=0; i--) { if(ZZBuffer[i]==HIGH[i]) ZH=i; if(ZZBuffer[i]==LOW[i]) ZL=i; } //--
После получения максимальной (ZH) и минимальной (ZL) позиции баров ZigZag дальнейшие действия зависят от настройки входных параметров сигнального индикатора ZigZag.
Полная функция ZigZagSignal() выглядит так:
int MCEA::ZigZagSignal(const string symbol) // ZigZag Signal for Open Position { //--- int ret=0; int rise=1, down=-1; int ZH=-1, ZL=-1; int barcalc=100; bool ZZrise=false; bool ZZdown=false; //-- double ZZBuffer[]; ArrayResize(ZZBuffer,barcalc,barcalc); ArraySetAsSeries(ZZBuffer,true); //-- int x=PairsIdxArray(symbol); UpdatePrice(symbol,TFt); //-- CopyBuffer(hZigZag[x],0,0,barcalc,ZZBuffer); //-- for(int i=barcalc-1; i>=0; i--) { if(ZZBuffer[i]==HIGH[i]) ZH=i; if(ZZBuffer[i]==LOW[i]) ZL=i; } //-- switch(sigzz) { case SZZ1: { ZZrise=((ZH==0 && HIGH[0]>HIGH[1])||(ZL<ZH && ZL>1)); ZZdown=((ZL==0 && LOW[0]<LOW[1])||(ZH<ZL && ZH>1)); //-- break; } case SZZ2: { ZZrise=(ZL<ZH && ZL>1); ZZdown=(ZH<ZL && ZH>1); //-- break; } case SZZ3: { ZZrise=((ZH==0 && HIGH[0]>HIGH[1])||(ZL<ZH && ZL>0)); ZZdown=((ZL==0 && LOW[0]<LOW[1])||(ZH<ZL && ZH>0)); //-- break; } case SZZ4: { ZZrise=(ZL<ZH && ZL>0); ZZdown=(ZH<ZL && ZH>0); //-- break; } }; //-- if(ZZrise) ret=rise; if(ZZdown) ret=down; //-- return(ret); //--- } //-end ZigZagSignal() //---------//
4.1.2. AO Signal
Как и в функции ZigZagSignal(), в функции AOSignal() нам также необходимо использовать функцию PairsIdxArray(), чтобы получить значение буфера из индикатора AO.
Итак, чтобы получить значение буфера индикатора AO, мы скопируем каждый буфер из хэндла индикатора AO.
Чтобы скопировать буфер AO (буфер 0) из хэндла индикатора AO в целевой массив:
CopyBuffer(hAO[x],0,0,barcalc,AOValue);
Дальнейшие действия зависят от настройки входных параметров сигнального индикатора AO.
Кроме того, для завершения сигнала индикатора АО мы также используем его цвет, чтобы проверить сигнал из значения буфера.
Для этой цели мы создали функцию для получения значения цвета индикатора AO с именем функции AOColorSignal().
В этой функции мы скопируем буфер 1 (буфер индекса цвета индикатора) из индикатора AO.
CopyBuffer(hAO[x],1,0,barcalc,AOColor);
int MCEA::AOColorSignal(const string symbol) { //--- int ret=0; int rise=1, down=-1; int barcalc=9; //-- double AOColor[]; ArrayResize(AOColor,barcalc,barcalc); ArraySetAsSeries(AOColor,true); //-- int x=PairsIdxArray(symbol); UpdatePrice(symbol,TFt,barcalc); //-- CopyBuffer(hAO[x],1,0,barcalc,AOColor); //-- bool AORise=((AOColor[1]==1.0 && AOColor[0]==0.0)||(AOColor[1]==0.0 && AOColor[0]==0.0)); bool AODown=((AOColor[1]==0.0 && AOColor[0]==1.0)||(AOColor[1]==1.0 && AOColor[0]==1.0)); //-- if(AORise) ret=rise; if(AODown) ret=down; //-- return(ret); //--- } //-end AOColorSignal() //---------//
Полная функция AOSignal() выглядит так:
int MCEA::AOSignal(const string symbol) { //--- int ret=0; int rise=1, down=-1; int barcalc=9; bool AORValue=false; bool AODValue=false; //-- double AOValue[]; ArrayResize(AOValue,barcalc,barcalc); ArraySetAsSeries(AOValue,true); //-- int x=PairsIdxArray(symbol); UpdatePrice(symbol,TFt,barcalc); //-- CopyBuffer(hAO[x],0,0,barcalc,AOValue); //-- switch(sigao) { case SAO1: { AORValue=(AOValue[2]<=0.0 && AOValue[1]>0.0 && AOValue[0]>AOValue[1])||(AOValue[1]>AOValue[2] && AOValue[0]>AOValue[1]); AODValue=(AOValue[2]>=0.0 && AOValue[1]<0.0 && AOValue[0]<AOValue[1])||(AOValue[1]<AOValue[2] && AOValue[0]<AOValue[1]); //-- break; } case SAO2: { AORValue=(AOValue[1]<=0.0 && AOValue[0]>0.0)||(AOValue[0]>0.0 && AOValue[0]>AOValue[1]); AODValue=(AOValue[1]>=0.0 && AOValue[0]<0.0)||(AOValue[0]<0.0 && AOValue[0]<AOValue[1]); //-- break; } case SAO3: { AORValue=(AOValue[1]<=0.0 && AOValue[0]>0.0)||(AOValue[0]>AOValue[1]); AODValue=(AOValue[1]>=0.0 && AOValue[0]<0.0)||(AOValue[0]<AOValue[1]); //-- break; } }; //-- bool AORise=(AOColorSignal(symbol)==rise); bool AODown=(AOColorSignal(symbol)==down); //-- if(AORValue && AORise) ret=rise; if(AODValue && AODown) ret=down; //-- return(ret); //--- } //-end AOSignal() //---------//
Функция GetIndiSignals() суммирует возвращаемые значения функций ZigZagSignal() и AOSignal().
int MCEA::GetIndiSignals(const string symbol) // Get Signal for Open Position { //--- int ret=0; int rise=1, down=-1; int sigrise=2; int sigdown=-2; //-- int ZZSignal=ZigZagSignal(symbol); int AwSignal=AOSignal(symbol); //Print(symbol+" = ZZ="+string(ZZSignal)+" AO="+string(AwSignal)+" Signal="+string(ZZSignal+AwSignal)); //-- if(ZZSignal+AwSignal==sigrise) ret=rise; if(ZZSignal+AwSignal==sigdown) ret=down; //-- return(ret); //--- } //-end GetIndiSignals() //---------//
Функция GetIndiSignals() суммирует возвращаемые значения функций ZigZagSignal() и AOSignal().
- Если результат равен 2, это сигнал на покупку.
- Если результат -2, это сигнал на продажу.
4.2. Функция DirectionMove.
Функция DirectionMove() полезна для получения цены закрытия на текущем баре, будь то выше цены открытия (вверх) или ниже цены открытия (вниз).
int MCEA::DirectionMove(const string symbol,const ENUM_TIMEFRAMES stf) // Bar Price Direction { //--- int ret=0; int rise=1, down=-1; //-- Pips(symbol); double difud=mc_symbol.NormalizePrice(1.5*pip); UpdatePrice(symbol,stf,2); //-- if(CLOSE[0]>OPEN[0]+difud) ret=rise; if(CLOSE[0]<OPEN[0]-difud) ret=down; //-- return(ret); //--- } //-end DirectionMove() //---------//
4.3. Функция PARSAR15().
Функция PARSAR15() полезна для согласования движения индикаторов ZigZag и АО с индикаторами Parabolic Stop и Reverse (PSAR/iSAR) на таймфрейме M15.
int MCEA::PARSAR15(const string symbol) // formula Parabolic SAR M15 { //--- int ret=0; int rise=1, down=-1; int br=2; //-- double PSAR[]; ArrayResize(PSAR,br,br); ArraySetAsSeries(PSAR,true); int xx=PairsIdxArray(symbol); CopyBuffer(hPar15[xx],0,0,br,PSAR); //-- UpdatePrice(symbol,TFT15,br); //-- if(PSAR[0]<LOW[0]) ret=rise; if(PSAR[0]>HIGH[0]) ret=down; //-- return(ret); //--- } //-end PARSAR15() //---------//
После выполнения трех основных сигнальных функций и нескольких вспомогательных функций функция GetOpenPosition() предоставит следующие значения:
- 0 - сигнал неизвестен.
- 1 - сигнал на покупку;
- -1 - сигнал на продажу.
Когда функция GetOpenPosition() возвращает значение 1, советник вызывает функцию OpenBuy() для открытия ордера на покупку.
bool MCEA::OpenBuy(const string symbol) { //--- ResetLastError(); //-- bool buyopen = false; string ldComm = GetCommentForOrder()+"_Buy"; double ldLot = MLots(symbol); ENUM_ORDER_TYPE type_req = ORDER_TYPE_BUY; //-- MqlTradeRequest req={}; MqlTradeResult res={}; MqlTradeCheckResult check={}; //-- structure is set to zero ZeroMemory(req); ZeroMemory(res); ZeroMemory(check); //-- CurrentSymbolSet(symbol); double SL=OrderSLSet(symbol,type_req,mc_symbol.Bid()); double TP=OrderTPSet(symbol,type_req,mc_symbol.Ask()); //-- if(RefreshTick(symbol)) buyopen=mc_trade.Buy(ldLot,symbol,mc_symbol.Ask(),SL,TP,ldComm); //-- int error=GetLastError(); if(buyopen||error==0) { string bsopen="Open BUY Order for "+symbol+" ~ Ticket= ["+(string)mc_trade.ResultOrder()+"] successfully..!"; Do_Alerts(symbol,bsopen); } else { mc_trade.CheckResult(check); Do_Alerts(Symbol(),"Open BUY order for "+symbol+" FAILED!!. Return code= "+ (string)mc_trade.ResultRetcode()+". Code description: ["+mc_trade.ResultRetcodeDescription()+"]"); return(false); } //-- return(buyopen); //-- //--- } //-end OpenBuy //---------//
При этом, если функция GetOpenPosition() возвращает значение -1, советник вызовет функцию OpenSell() для открытия ордера на продажу.
bool MCEA::OpenSell(const string symbol) { //--- ResetLastError(); //-- bool selopen = false; string sdComm = GetCommentForOrder()+"_Sell"; double sdLot = MLots(symbol); ENUM_ORDER_TYPE type_req = ORDER_TYPE_SELL; //-- MqlTradeRequest req={}; MqlTradeResult res={}; MqlTradeCheckResult check={}; //-- structure is set to zero ZeroMemory(req); ZeroMemory(res); ZeroMemory(check); //-- CurrentSymbolSet(symbol); double SL=OrderSLSet(symbol,type_req,mc_symbol.Ask()); double TP=OrderTPSet(symbol,type_req,mc_symbol.Bid()); //-- if(RefreshTick(symbol)) selopen=mc_trade.Sell(sdLot,symbol,mc_symbol.Bid(),SL,TP,sdComm); //-- int error=GetLastError(); if(selopen||error==0) { string bsopen="Open SELL Order for "+symbol+" ~ Ticket= ["+(string)mc_trade.ResultOrder()+"] successfully..!"; Do_Alerts(symbol,bsopen); } else { mc_trade.CheckResult(check); Do_Alerts(Symbol(),"Open SELL order for "+symbol+" FAILED!!. Return code= "+ (string)mc_trade.ResultRetcode()+". Code description: ["+mc_trade.ResultRetcodeDescription()+"]"); return(false); } //-- return(selopen); //-- //--- } //-end OpenSell //---------//
5. Функция ChartEvent
Для большей эффективности мультивалютных советников создадим несколько кнопок для управления ордерами и переключения между таймфреймами и символами.
//+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- //--- handling CHARTEVENT_CLICK event ("Clicking the chart") ResetLastError(); //-- ENUM_TIMEFRAMES CCS=mc.TFt; //-- if(id==CHARTEVENT_OBJECT_CLICK) { int lensymbol=StringLen(Symbol()); int lensparam=StringLen(sparam); //-- //--- if "Set SL All Orders" button is click if(sparam=="Set SL/TP All Orders") { mc.SetSLTPOrders(); Alert("-- "+mc.expname+" -- ",Symbol()," -- Set SL/TP All Orders"); //--- unpress the button ObjectSetInteger(0,"Set SL/TP All Orders",OBJPROP_STATE,false); ObjectSetInteger(0,"Set SL/TP All Orders",OBJPROP_ZORDER,0); CreateManualPanel(); } //--- if "Close All Order" button is click if(sparam=="Close All Order") { mc.CloseAllOrders(); Alert("-- "+mc.expname+" -- ",Symbol()," -- Close All Orders"); //--- unpress the button ObjectSetInteger(0,"Close All Order",OBJPROP_STATE,false); ObjectSetInteger(0,"Close All Order",OBJPROP_ZORDER,0); CreateManualPanel(); } //--- if "Close All Profit" button is click if(sparam=="Close All Profit") { mc.ManualCloseAllProfit(); Alert("-- "+mc.expname+" -- ",Symbol()," -- Close All Profit"); //--- unpress the button ObjectSetInteger(0,"Close All Profit",OBJPROP_STATE,false); ObjectSetInteger(0,"Close All Profit",OBJPROP_ZORDER,0); CreateManualPanel(); } //--- if "X" button is click if(sparam=="X") { ObjectsDeleteAll(0,0,OBJ_BUTTON); ObjectsDeleteAll(0,0,OBJ_LABEL); ObjectsDeleteAll(0,0,OBJ_RECTANGLE_LABEL); //--- unpress the button ObjectSetInteger(0,"X",OBJPROP_STATE,false); ObjectSetInteger(0,"X",OBJPROP_ZORDER,0); //-- DeleteButtonX(); mc.PanelExtra=false; DisplayManualButton(); } //--- if "M" button is click if(sparam=="M") { //--- unpress the button ObjectSetInteger(0,"M",OBJPROP_STATE,false); ObjectSetInteger(0,"M",OBJPROP_ZORDER,0); mc.PanelExtra=true; CreateManualPanel(); } //--- if "C" button is click if(sparam=="C") { //--- unpress the button ObjectSetInteger(0,"C",OBJPROP_STATE,false); ObjectSetInteger(0,"C",OBJPROP_ZORDER,0); mc.PanelExtra=true; CreateSymbolPanel(); } //--- if "R" button is click if(sparam=="R") { Alert("-- "+mc.expname+" -- ",Symbol()," -- expert advisor will be Remove from the chart."); ExpertRemove(); //--- unpress the button ObjectSetInteger(0,"R",OBJPROP_STATE,false); ObjectSetInteger(0,"R",OBJPROP_ZORDER,0); if(!ChartSetSymbolPeriod(0,Symbol(),Period())) ChartSetSymbolPeriod(0,Symbol(),Period()); DeletePanelButton(); ChartRedraw(0); } //--- if Symbol button is click if(lensparam==lensymbol) { int sx=mc.ValidatePairs(sparam); ChangeChartSymbol(mc.AS30[sx],CCS); mc.PanelExtra=false; } //-- } //-- return; //--- } //-end OnChartEvent() //---------//
В группе входных свойств Other (другие) трейдеру будет предоставлена возможность выбрать, отображать ли торговую информацию на графике (Yes) или нет (No).
При Yes торговая информация будет отображаться на графике, на котором размещен советник, путем вызова функции TradeInfo().
Мы также добавили функцию для описания времени в соответствии с условиями торгового часового пояса как часть функции TradeInfo().
string MCEA::PosTimeZone(void) { //--- string tzpos=""; //-- if(ReqTime(zntm,day)>ThisTime(day)) { tzpos=tz_opn+ " Next day to " +tz_cls + " Next day"; } else if(TimeCurrent()<znop) { if(ThisTime(day)==ReqTime(znop,day) && ThisTime(day)==ReqTime(zncl,day)) tzpos=tz_opn+" to " +tz_cls+ " Today"; //else if(ThisTime(day)==ReqTime(znop,day) && ThisTime(day)<ReqTime(zncl,day)) tzpos=tz_opn+ " Today to " +tz_cls+ " Next day"; } else if(TimeCurrent()>=znop && TimeCurrent()<zncl) { if(ThisTime(day)<ReqTime(zncl,day)) tzpos=tz_opn+ " Today to " +tz_cls+ " Next day"; else if(ThisTime(day)==ReqTime(zncl,day)) tzpos=tz_opn+" to " +tz_cls+ " Today"; } else if(ThisTime(day)==ReqTime(znop,day) && ThisTime(day)<ReqTime(zncl,day)) { tzpos=tz_opn+" Today to " +tz_cls+ " Next day"; } //-- return(tzpos); //---- } //-end PosTimeZone() //---------//
void MCEA::TradeInfo(void) // function: write comments on the chart { //---- Pips(Symbol()); double spread=SymbolInfoInteger(Symbol(),SYMBOL_SPREAD)/xpip; rem=zntm-TimeCurrent(); string postime=PosTimeZone(); string eawait=" - Waiting for active time..!"; //-- string comm=""; TodayOrders(); //-- comm="\n :: Server Date Time : "+string(ThisTime(year))+"."+string(ThisTime(mon))+"."+string(ThisTime(day))+ " "+TimeToString(TimeCurrent(),TIME_SECONDS)+ "\n ------------------------------------------------------------"+ "\n :: Broker : "+ TerminalInfoString(TERMINAL_COMPANY)+ "\n :: Expert Name : "+ expname+ "\n :: Acc. Name : "+ mc_account.Name()+ "\n :: Acc. Number : "+ (string)mc_account.Login()+ "\n :: Acc. TradeMode : "+ AccountMode()+ "\n :: Acc. Leverage : 1 : "+ (string)mc_account.Leverage()+ "\n :: Acc. Equity : "+ DoubleToString(mc_account.Equity(),2)+ "\n :: Margin Mode : "+ (string)mc_account.MarginModeDescription()+ "\n :: Magic Number : "+ string(magicEA)+ "\n :: Trade on TF : "+ EnumToString(TFt)+ "\n :: Today Trading : "+ TradingDay()+" : "+hariini+ "\n :: Trading Session : "+ tz_ses+ "\n :: Trading Time : "+ postime; if(TimeCurrent()<zntm) { comm=comm+ "\n :: Time Remaining : "+(string)ReqTime(rem,hour)+":"+(string)ReqTime(rem,min)+":"+(string)ReqTime(rem,sec) + eawait; } comm=comm+ "\n ------------------------------------------------------------"+ "\n :: Trading Pairs : "+pairs+ "\n :: BUY Market : "+string(oBm)+ "\n :: SELL Market : "+string(oSm)+ "\n :: Total Order : "+string(oBm+oSm)+ "\n :: Order Profit : "+DoubleToString(floatprofit,2)+ "\n :: Fixed Profit : "+DoubleToString(fixclprofit,2)+ "\n :: Float Money : "+DoubleToString(floatprofit,2)+ "\n :: Nett Profit : "+DoubleToString(floatprofit+fixclprofit,2); //-- Comment(comm); ChartRedraw(0); return; //---- } //-end TradeInfo() //---------//
Интерфейс мультивалютного советника ZigZag_AO_MCEA выглядит следующим образом.
Как видим, под именем советника ZigZag_AO_MCEA расположены кнопки M, C и R.
При нажатии на кнопку М отобразится панель кнопок для ручного нажатия, как показано на рисунке ниже.
Трейдер может управлять ордерами вручную, когда отображается панель кнопок ручного нажатия:
5.1. Set SL/TP All Orders (установить стоп-лосс/тейк-профит для всех ордеров)
Как объяснялось выше, если Use Order Stop Loss равен No и/или Use Order Take Profit равен No, но затем трейдер намеревается использовать стоп-лосс или тейк-профит для всех ордеров, один клик по кнопке Set SL/TP All Orders изменит все ордера и применит стоп-лосс и/или тейк-профит.
void MCEA::SetSLTPOrders(void) { //--- ResetLastError(); MqlTradeRequest req={}; MqlTradeResult res={}; MqlTradeCheckResult check={}; //-- double modbuysl=0; double modselsl=0; double modbuytp=0; double modseltp=0; string position_symbol; int totalorder=PositionsTotal(); //-- for(int i=totalorder-1; i>=0; i--) { string symbol=PositionGetSymbol(i); position_symbol=symbol; if(mc_position.Magic()==magicEA) { ENUM_POSITION_TYPE opstype = mc_position.PositionType(); if(opstype==POSITION_TYPE_BUY) { Pips(symbol); RefreshTick(symbol); double price = mc_position.PriceCurrent(); double pos_open = mc_position.PriceOpen(); double pos_stop = mc_position.StopLoss(); double pos_take = mc_position.TakeProfit(); modbuysl=SetOrderSL(symbol,opstype,pos_open); if(price<modbuysl) modbuysl=mc_symbol.NormalizePrice(price-slip*pip); modbuytp=SetOrderTP(symbol,opstype,pos_open); if(price>modbuytp) modbuytp=mc_symbol.NormalizePrice(price+slip*pip); //-- if(pos_stop==0.0 || pos_take==0.0) { if(!mc_trade.PositionModify(position_symbol,modbuysl,modbuytp)) { mc_trade.CheckResult(check); Do_Alerts(symbol,"Set SL and TP for "+EnumToString(opstype)+" on "+symbol+" FAILED!!. Return code= "+ (string)mc_trade.ResultRetcode()+". Code description: ["+mc_trade.ResultRetcodeDescription()+"]"); } } } if(opstype==POSITION_TYPE_SELL) { Pips(symbol); RefreshTick(symbol); double price = mc_position.PriceCurrent(); double pos_open = mc_position.PriceOpen(); double pos_stop = mc_position.StopLoss(); double pos_take = mc_position.TakeProfit(); modselsl=SetOrderSL(symbol,opstype,pos_open); if(price>modselsl) modselsl=mc_symbol.NormalizePrice(price+slip*pip); modseltp=SetOrderTP(symbol,opstype,pos_open); if(price<modseltp) modseltp=mc_symbol.NormalizePrice(price-slip*pip); //-- if(pos_stop==0.0 || pos_take==0.0) { if(!mc_trade.PositionModify(position_symbol,modselsl,modseltp)) { mc_trade.CheckResult(check); Do_Alerts(symbol,"Set SL and TP for "+EnumToString(opstype)+" on "+symbol+" FAILED!!. Return code= "+ (string)mc_trade.ResultRetcode()+". Code description: ["+mc_trade.ResultRetcodeDescription()+"]"); } } } } } //-- return; //--- } //-end SetSLTPOrders //---------//
5.2. Close All Orders (закрыть все ордера)
Если трейдер хочет закрыть все ордера, он может сделать это одним щелчком по кнопке Close All Orders.
void MCEA::CloseAllOrders(void) //-- function: close all order { //---- ResetLastError(); //-- MqlTradeRequest req={}; MqlTradeResult res={}; MqlTradeCheckResult check={}; //-- int total=PositionsTotal(); // number of open positions //--- iterate over all open positions for(int i=total-1; i>=0; i--) { //--- if the MagicNumber matches if(mc_position.Magic()==magicEA) { //-- string position_Symbol = PositionGetSymbol(i); // symbol of the position ulong position_ticket = PositionGetTicket(i); // ticket of the the opposite position ENUM_POSITION_TYPE type = mc_position.PositionType(); RefreshTick(position_Symbol); bool closepos = mc_trade.PositionClose(position_Symbol,slip); //--- output information about the closure PrintFormat("Close #%I64d %s %s",position_ticket,position_Symbol,EnumToString(type)); //--- } } //--- return; //---- } //-end CloseAllOrders() //---------//
5.3. Close All Profits (закрыть все прибыльные ордера)
Чтобы закрыть все прибыльные ордера, достаточно щелчка по кнопке Close All Profits.
bool MCEA::ManualCloseAllProfit(void) { //---- ResetLastError(); //-- bool orclose=false; //-- MqlTradeRequest req={}; MqlTradeResult res={}; MqlTradeCheckResult check={}; //-- int ttlorder=PositionsTotal(); // number of open positions //-- for(int x=0; x<arrsymbx; x++) { string symbol=DIRI[x]; orclose=false; //-- for(int i=ttlorder-1; i>=0; i--) { string position_Symbol = PositionGetSymbol(i); ENUM_POSITION_TYPE type = mc_position.PositionType(); if((position_Symbol==symbol) && (mc_position.Magic()==magicEA)) { double pos_profit = mc_position.Profit(); double pos_swap = mc_position.Swap(); double pos_comm = mc_position.Commission(); double cur_profit = NormalizeDouble(pos_profit+pos_swap+pos_comm,2); ulong position_ticket = PositionGetTicket(i); //--- if(type==POSITION_TYPE_BUY && cur_profit>0.02) { RefreshTick(position_Symbol); orclose = mc_trade.PositionClose(position_Symbol,slip); //--- output information about the closure PrintFormat("Close #%I64d %s %s",position_ticket,position_Symbol,EnumToString(type)); } if(type==POSITION_TYPE_SELL && cur_profit>0.02) { RefreshTick(position_Symbol); orclose = mc_trade.PositionClose(position_Symbol,slip); //--- output information about the closure PrintFormat("Close #%I64d %s %s",position_ticket,position_Symbol,EnumToString(type)); } } } } //-- return(orclose); //---- } //-end ManualCloseAllProfit() //---------//
При нажатии на C отобразится кнопка панели с 30 именами символов или пар, и трейдеры смогут щелкнуть по одному из названий пар или символов.
Нажатие на одно из названий или символов пары немедленно заменяет символ графика символом, по имени которого был выполнен щелчок.
void CreateSymbolPanel() { //--- //-- ResetLastError(); DeletePanelButton(); int sydis=83; int tsatu=int(mc.sall/2); //-- CreateButtonTemplate(0,"Template",180,367,STYLE_SOLID,5,BORDER_RAISED,clrYellow,clrBurlyWood,clrWhite,CORNER_RIGHT_UPPER,187,45,true); CreateButtonTemplate(0,"TempCCS",167,25,STYLE_SOLID,5,BORDER_RAISED,clrYellow,clrBlue,clrWhite,CORNER_RIGHT_UPPER,181,50,true); CreateButtonClick(0,"X",14,14,"Arial Black",10,BORDER_FLAT,"X",clrWhite,clrWhite,clrRed,ANCHOR_CENTER,CORNER_RIGHT_UPPER,22,48,true,"Close Symbol Panel"); //-- string chsym="Change SYMBOL"; int cspos=int(181/2)+int(StringLen(chsym)/2); CreateButtontLable(0,"CCS","Bodoni MT Black",chsym,11,clrWhite,ANCHOR_CENTER,CORNER_RIGHT_UPPER,cspos,62,true,"Change Chart Symbol"); //-- for(int i=0; i<tsatu; i++) CreateButtonClick(0,mc.AS30[i],80,17,"Bodoni MT Black",8,BORDER_RAISED,mc.AS30[i],clrYellow,clrBlue,clrWhite,ANCHOR_CENTER,CORNER_RIGHT_UPPER,180,sydis+(i*22),true,"Change to "+mc.AS30[i]); //-- for(int i=tsatu; i<mc.sall; i++) CreateButtonClick(0,mc.AS30[i],80,17,"Bodoni MT Black",8,BORDER_RAISED,mc.AS30[i],clrYellow,clrBlue,clrWhite,ANCHOR_CENTER,CORNER_RIGHT_UPPER,94,sydis+((i-tsatu)*22),true,"Change to "+mc.AS30[i]); //-- ChartRedraw(0); //-- return; //--- } //-end CreateSymbolPanel() //---------//
В этом случае функция OnChartEvent() вызывается функцией ChangeChartSymbol() при нажатии на одно из названий символов.
if(id==CHARTEVENT_OBJECT_CLICK) { int lensymbol=StringLen(Symbol()); int lensparam=StringLen(sparam); //--- if Symbol button is click if(lensparam==lensymbol) { int sx=mc.ValidatePairs(sparam); ChangeChartSymbol(mc.AS30[sx],CCS); mc.PanelExtra=false; } //-- }
void ChangeChartSymbol(string c_symbol,ENUM_TIMEFRAMES cstf) { //--- //--- unpress the button ObjectSetInteger(0,c_symbol,OBJPROP_STATE,false); ObjectSetInteger(0,c_symbol,OBJPROP_ZORDER,0); ObjectsDeleteAll(0,0,OBJ_BUTTON); ObjectsDeleteAll(0,0,OBJ_LABEL); ObjectsDeleteAll(0,0,OBJ_RECTANGLE_LABEL); //-- ChartSetSymbolPeriod(0,c_symbol,cstf); //-- ChartRedraw(0); //-- return; //--- } //-end ChangeChartSymbol() //---------//
Наконец, нажатие кнопки R удалит с графика мультивалютный советник ZigZag_AO_MCEA, поэтому трейдерам не придется удалять советники вручную.
if(id==CHARTEVENT_OBJECT_CLICK) { //-- //--- if "R" button is click if(sparam=="R") { Alert("-- "+mc.expname+" -- ",Symbol()," -- Expert Advisor will be Remove from the chart."); ExpertRemove(); //--- unpress the button ObjectSetInteger(0,"R",OBJPROP_STATE,false); ObjectSetInteger(0,"R",OBJPROP_ZORDER,0); if(!ChartSetSymbolPeriod(0,Symbol(),Period())) ChartSetSymbolPeriod(0,Symbol(),Period()); DeletePanelButton(); ChartRedraw(0); } //--- }
Тестер стратегий
Тестер стратегий терминала MetaTrader 5 поддерживает и позволяет тестировать стратегии, торговать на нескольких символах или тестировать автоматическую торговлю для всех доступных символов и на всех доступных таймфреймах. В тестере стратегий MetaTrader 5 мы протестируем мультивалютный советник ZigZag_AO_MCEA.
Установим ZigZag_AO_MCEA на пару XAUUSD H4 с пользовательским периодом 2023.10.01 - 2024.02.17.
Результаты представлены ниже.
Заключение
- Создание мультивалютного советника на MQL5 мало чем отличается от разработки одновалютного. Мультивалютный советник также может использоваться как одновалютный.
- Создание мультивалютного советника повысит эффективность и результативность трейдеров, поскольку им не нужно будет открывать много графиков.
- Правильная торговая стратегия увеличивает вероятность получения прибыли по сравнению с использованием одновалютного советника. Убытки по одной паре будут перекрываться прибылью в других парах.
- Мультивалютный советник ZigZag_AO_MCEA является всего лишь примером для изучения и развития собственных идей. Результаты тестирования в тестере стратегий по-прежнему неудовлетворительны. Экспериментируя и тестируя на разных таймфреймах, рассчитывая разные периоды индикаторов и выбирая разные сигналы, можно получить более прибыльные результаты.
- Сочетание индикаторов ZigZag и AO следует дополнительно изучить, экспериментируя с таймфреймами и значениями дифференциации входных параметров индикатора ZigZag. Возможно, имеет смысл добавить другие сигналы алгоритма к индикаторам ZigZag и AO.
- Удовлетворительные результаты получены только на таймфреймах H1 и выше. На более мелких таймфреймах открывается больше сделок, заканчивающихся убытками.
Спасибо за внимание!
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/14329





- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Уважаемый Роберто Якобс
Приветствую вас и всех остальных.
Большое спасибо за вашу замечательную статью.
Я прогнал коды в тестере стратегий и у нескольких брокеров и, к сожалению, каждый раз получаю ошибку "лимит ордера достигнут". Я буду очень благодарен, если вы поможете мне решить эту проблему.
С уважением и наилучшими пожеланиями
Уважаемый Роберто Джейкобс
Привет вам и всем
Большое спасибо за вашу замечательную статью.
Я прогнал коды в тестере стратегий и у нескольких брокеров и, к сожалению, каждый раз получаю ошибку "лимит ордера достигнут". Я буду очень благодарен, если вы сможете помочь мне решить эту проблему.
С уважением и наилучшими пожеланиями
Пожалуйста, спросите у вашего брокера о AccountInfoInteger(ACCOUNT_LIMIT_ORDERS) вашего счета, или о том, является ли ваш счет в настоящее время ограниченным.