Создаем простой мультивалютный советник с использованием MQL5 (Часть 5): Полосы Боллинджера на канале Кельтнера — Сигналы индикаторов
Введение
Под мультивалютным советником в этой статье понимается советник, или торговый робот, который может торговать (открывать/закрывать ордера, управлять ордерами, например, трейлинг-стоп-лоссом и трейлинг-профитом) более чем одной парой символов с одного графика. В этой статье советник будет торговать по 30 парам.
В этой статье мы будем использовать сигналы двух индикаторов - полосы Боллинджера (Bollinger Bands®) на канале Кельтнера.
В MetaTrader 4 использование подобных сигналов осуществлялось с помощью функции iBandsOnArray.
В справочнике MQL4 в пояснении к функции iBandsOnArray указано, что, в отличие от iBands(...), функция iBandsOnArray() не берет данные по имени символа, таймфрейму и применяемой цене. Данные о ценах должны быть предварительно подготовлены...
В обсуждении на форуме MQL5 мне попадались мнения о том, что iBandOnArray() в MQL5 не существует.
Действительно, iBandOnArray() нет в списке функций MQL5, но с помощью хэндла индикатора iBands() мы можем легко создать iBandOnArray() в MQL5. На мой взгляд, использовать хэндл индикатора в MQL5 проще и удобнее, чем использовать функцию iBandsOnArray() в MetaTrader 4.
Как уже доказывалось в предыдущих статьях, мультивалютная торговля как в торговом терминале, так и в тестере стратегий, возможна благодаря MQL5.
Таким образом, цель состоит в том, чтобы удовлетворить основные потребности трейдеров, которым нужны эффективные и действенные торговые роботы. Полагаясь на сильные стороны и возможности MQL5, мы можем создать простой мультивалютный советник, который в этой статье использует сигналы двух индикаторов для открытия ордеров: полосы Боллинджера на канале Кельтнера. Полосы Боллинджера будут использовать ценовые данные канала Кельтнера. При этом для трейлинг-стопов мы по-прежнему будем использовать индикатор Parabolic SAR (iSAR).
Особенности
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 пар.
Как и в предыдущей статье, мы упростили задачу, добавив специальные входные свойства для префикса и суффикса имени пары. Затем с помощью простой функции мы обрабатываем префикс и/или суффикс имени пары в сочетании с 30 зарегистрированными именами пар, чтобы советник работал нормально, если брокер использует специальные имена символов.
Недостаток функции обнаружения имен символов, имеющих префиксы и суффиксы, заключается в том, что она работает только с парами или именами символов для форекс и металлов в MetaTrader 5, но не работает со специальными символами и индексами. Кроме того, необходимо избегать опечаток (необходимо вводить с учетом регистра) в названии префикса и/или суффикса пары. Поэтому необходимы точность и аккуратность при вводе префикса и/или суффикса названия пары.
Как и в предыдущей статье, в этот советник мы также добавили 10 опционов на торгуемые пары. Одной из 10 пар опционов, которые будут торговаться, является Trader's Desired Pairs (трейдерские пары), где торгуемые пары должны быть введены трейдером вручную в свойствах советника. Не забывайте, что имя введенной пары уже должно быть в списке из 30 пар.
В этой версии советника мы также добавили опцию торговой сессии (часовой пояс), поэтому торгуемые пары соответствуют времени торговой сессии.
2. Сигнальные индикаторы.
В этой версии советника мы будем использовать сигналы двух индикаторов, в данном случае полос Боллинджера на канале Кельтнера. Канал Кельтнера мы будем использовать в качестве ценовых данных для полос Боллинджера.
2.1. Канал Кельтнера.
Канал Кельтнера был впервые представлен Честером Кельтнером в 1960-х годах. В исходной формуле для расчета полос использовались простые скользящие средние (SMA) и диапазон высоких/низких цен. В 1980-х годах была введена новая формула, в которой использовался средний истинный диапазон (ATR). Метод ATR широко используется сегодня.
При построении канала Кельтнера, применяемого для советников в этой статье, я использовал популярный сегодня метод, а именно экспоненциальную скользящую среднюю (EMA) с периодом 20 с верхней и нижней полосами с использованием индикатора ATR с периодом 20.
Входные параметры канала Кельтнера следующие:
2.2. Полосы Боллинджера.
Полосы Боллинджера были созданы Джоном Боллинджером в 1980-х годах и быстро стали одним из наиболее популярных индикаторов в области технического анализа. Полосы Боллинджера состоят из трех полос — верхней, средней и нижней — которые используются для обозначения краткосрочных ценовых экстремумов на рынке. Верхняя полоса является признаком перекупленности, а нижняя — перепроданности. Большинство финансовых аналитиков используют полосы Боллинджера и комбинируют их с другими индикаторами, чтобы получить лучшую аналитическую картину состояния рынка.
В советнике для этой статьи мы будем использовать полосы Боллинджера с периодом 38, который использует ценовые данные канала Кельтнера.
Входные параметры полос Боллинджера следующие:
На рисунках 1 и 2 представлен канал Кельтнера в качестве ценовых данных для полос Боллинджера для сигналов на покупку и продажу.
Рис. 1. Сигнал на покупку
Рис. 2. Сигнал на продажу
На иллюстрации выше сигнал подается только тогда, когда средняя линия канала Кельтнера пересекает верхнюю или нижнюю полосу Боллинджера. Но для советника в этой статье сигнал индикатора на самом деле представляет собой пересечение средней линии канала Кельтнера и верхней, средней и нижней линий полос Боллинджера.
- Для сигналов на покупку:
- Первый сигнал: Средняя линия канала Кельтнера пересекает нижнюю полосу Боллинджера снизу вверх; или
- Второй сигнал: Средняя линия канала Кельтнера пересекает среднюю полосу Боллинджера снизу вверх; или
- Третий сигнал: средняя линия канала Кельтнера пересекает верхнюю полосу Боллинджера снизу вверх.
- Для сигналов на продажу:
- Первый сигнал: Средняя линия канала Кельтнера пересекает верхнюю полосу Боллинджера сверху вниз; или
- Второй сигнал: Средняя линия канала Кельтнера пересекает среднюю полосу Боллинджера сверху вниз; или
- Третий сигнал: средняя линия канала Кельтнера пересекает нижнюю полосу Боллинджера сверху вниз.
3. Управление сделками и ордерами
Мультивалютный советник предоставляет вам несколько вариантов управления вашими сделками:
3.1. Ордера стоп-лосс
Варианты: Use Order Stop Loss (Yes) или (No) - использовать ордер стоп-лосс: да или нет
- При выборе Use Order Stop Loss (No) все ордера будут открываться без стоп-лосса.
- При выборе 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) советник будет проверять выполнение условий сигнала. Если они выполняются, ордер сохраняется. Если сигнал ослаб, ордер необходимо закрыть для сохранения прибыли или состояние сигнала изменило направление, и ордер должен быть закрыт, а убыток зафиксирован.
Примечание: Перед закрытием сделки из-за слабого сигнала запрашивается подтверждение пользователя.
При No, даже несмотря на то, что сигнал ослаб, ордер все равно будет сохранен или не будет закрыт для сохранения прибыли.
При Yes условия для канала Кельтнера и полос Боллинджера следующие:
- Для закрытия ордеров на покупку: когда нижняя линия индикатора канала Кельтнера пересекает верхнюю линию полос Боллинджера, ордер на покупку будет закрыт.
- Для закрытия ордеров на продажу: Когда верхняя линия индикатора канала Кельтнера пересекает верхнюю линию полос Боллинджера, ордер на продажу будет закрыт.
Код для установки стоп-лосса следующий:
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() //---------//
Код для закрытия сделки и сохранения прибыли из-за слабого сигнала:
int MCEA::GetCloseInWeakSignal(const string symbol,int exis) // Signal Indicator Position Close in profit { //--- int ret=0; int rise=1, down=-1; //-- int br=3; Pips(symbol); double difud=mc_symbol.NormalizePrice(1.5*pip); //-- double KCub[], KClb[]; double BBub[], BBlb[]; //-- ArrayResize(KCub,br,br); ArrayResize(KClb,br,br); ArrayResize(BBub,br,br); ArrayResize(BBlb,br,br); ArraySetAsSeries(KCub,true); ArraySetAsSeries(KClb,true); ArraySetAsSeries(BBub,true); ArraySetAsSeries(BBlb,true); //-- int xx=PairsIdxArray(symbol); //-- CopyBuffer(hKC[xx],1,0,br,KCub); CopyBuffer(hKC[xx],2,0,br,KClb); CopyBuffer(hBB[xx],1,0,br,BBub); CopyBuffer(hBB[xx],2,0,br,BBlb); //-- int dirmove=DirectionMove(symbol,TFt); bool closebuy=(KClb[1]>=BBub[1] && KClb[0]<BBub[0]-difud); bool closesel=(KCub[1]<=BBlb[1] && KCub[0]>BBlb[0]+difud); //-- if(exis==down && closesel && dirmove==rise) ret=rise; if(exis==rise && closebuy && dirmove==down) ret=down; //-- return(ret); //--- } //-end GetCloseInWeakSignal() //---------//
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. Трейлинг-стоп и трейлинг тейк-профита
Варианты: Use Trailing SL/TP (Yes) или (No) - использовать стоп-лосс/тейк-профит трейлинга: да или нет
- При Use Trailing SL/TP option (No) советник не будет использовать стоп-лосс и тейк-профит трейлинга.
- При Use Trailing SL/TP (Yes) снова появляется выбор: Use Automatic Trailing (Yes) или (No) - использовать автоматический трейлинг: да или нет
- При Use Automatic Trailing (Yes) трейлинг-стоп выполняется советником с применением значения Parabolic SAR (iSAR) одновременно с расчетом сигналов таймфрейма и в то же время путем получения скользящей прибыли на основе значения переменной TPmin (значение скользящей прибыли).
- При Use Automatic Trailing (No) трейлинг-стоп выполняется советником с использованием значения входного параметра.
Примечание: Советник осуществляет трейлинг тейк-профита одновременно с трейлинг-стопом.
Функция 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 0: { 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 1: { double PSAR[]; ArrayResize(PSAR,br,br); ArraySetAsSeries(PSAR,true); CopyBuffer(hParIU[x],0,0,br,PSAR); RefreshPrice(xsymb,TFt,br); //-- if(ptype==POSITION_TYPE_BUY && (PSAR[0]<iLow(xsymb,TFt,0))) pval=PSAR[0]; if(ptype==POSITION_TYPE_SELL && (PSAR[0]>iHigh(xsymb,TFt,0))) pval=PSAR[0]; break; } } //-- return(pval); //--- } //-end TSPrice() //---------//
Изменение функции SL/TP:
bool MCEA::ModifySLTP(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_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*pip); double modbuysl=vtrsb; double modbuytp=mc_symbol.NormalizePrice(price+TPmin*pip); bool modbuy = (price>modminsl && modbuysl>modstart && (pos_stop==0.0||modbuysl>pos_stop)); //-- if(modbuy && netp>0.05) { modist=mc_trade.PositionModify(symbol,modbuysl,modbuytp); } } 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_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*pip); double modselsl=vtrss; double modseltp=mc_symbol.NormalizePrice(price-TPmin*pip); bool modsel = (price<modminsl && modselsl<modstart && (pos_stop==0.0||modselsl<pos_stop)); //-- if(modsel && netp>0.05) { modist=mc_trade.PositionModify(symbol,modselsl,modseltp); } } } } //-- return(modist); //--- } //-end ModifySLTP() //---------//
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 - закрыть все ордера.
4.3. Close All Orders Profit - закрыть все прибыльные ордера.
5. Management Orders and Chart Symbols.
Очень полезной функцией для мультивалютных советников, торгующих 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 Australia 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 }; //--
Перечисление для выбора пар опционов для торговли
//-- 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 TFUSE { TFM5, // PERIOD_M5 TFM15, // PERIOD_M15 TFM30, // PERIOD_M30 TFH1, // PERIOD_H1 TFH2, // PERIOD_H2 TFH3, // PERIOD_H3 TFH4, // PERIOD_H4 TFH6, // PERIOD_H6 TFH8, // PERIOD_H8 TFH12, // PERIOD_H12 TFD1 // PERIOD_D1 }; //--
Примечание: При перечислении TFUSE мы ограничиваем использование расчетов временных рамок для советников только начиная с TF-M5 и заканчивая TF-D1
Входные параметры советника
//--- input group "=== Global Strategy EA Parameter ==="; // Global Strategy EA Parameter input TFUSE tfinuse = TFH1; // Select Expert TimeFrame, default PERIOD_H1 input int KCPeriod = 20; // Input Keltner Channel Period, default 20 input ENUM_MA_METHOD KCMethod = MODE_EMA; // Select Keltner Channel MA Method, default EMA input ENUM_APPLIED_PRICE KCMAAP = PRICE_TYPICAL; // Select Keltner Channel MA Applied Price, default Price Typical input int KCATRPer = 20; // Input Keltner Channel ATR Period, default 20 input double KCATRBandsMulti = 1.0; // Input Keltner Channel ATR bands multiplier input int BBPeriod = 38; // Input Bollinger Bands® Indicator period, default 38 input double BBDevi = 1.0; // Input Bollinger Bands® Indicator Deviations, default 1.00 //--- input group "=== Select Pairs to Trade ==="; // Selected Pairs to trading 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 string sym_prefix = ""; // Input the symbol prefix in case sensitive (if any) input string sym_suffix = ""; // Input the symbol suffix in case sensitive (if any) //-- 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; // 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 = 100; // If Not Use Automatic TP - Input TP value in Pips input YN TrailingSLTP = Yes; // Use Trailing SL/TP (Yes) or (No) input YN autotrl = Yes; // Use Automatic Trailing (Yes) or (No) input double TSval = 5; // If Not Use Automatic Trailing Input Trailing value in Pips input double TSmin = 5; // Minimum Pips to start Trailing Stop input double TPmin = 25; // 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) //--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 = 20231204; // Expert ID (Magic Number) //---
Примечание: Если входной параметр Expert ID (Magic Number) оставить пустым, советник сможет управлять ордерами, открытыми вручную.
В группе входных свойств Global Strategy EA Parameters советника трейдерам предлагается выбрать таймфрейм для расчета сигналов индикатора и ввести параметры для канала Кельтнера и полос Боллинджера.
При создании советника в этой статье канал Кельтнера в советнике будет иметь точно такие же входные параметры, что и в индикаторе.
//-- input int period_kc = 20; // Input Keltner Channel Period input ENUM_MA_METHOD ma_method = MODE_EMA; // Select MA Type of smoothing input ENUM_APPLIED_PRICE ma_price = PRICE_TYPICAL; // Select MA Applied Price input int atr_period = 20; // Input ATR Period (typically over 10 or 20) input double band_multi = 1.00; // Input the Band Multiplier ATR Desired //--
В группе свойств входных данных 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); aretc=ArraySize(EURs); 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<aretc; 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 30 Pairs { ArrayResize(DIRI,sall,sall); arrsymbx=sall; ArraySymbolResize(); ArrayCopy(DIRI,All30,0,0,WHOLE_ARRAY); pairs="Multi Currency 30 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,aretc,aretc); 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) { //--- symbfix=false; int ptriml; int ptrimr; string insymbol=Symbol(); int sym_Lenpre=StringLen(prefix); int sym_Lensuf=StringLen(suffix); if(sym_Lenpre>0) { ptriml=StringTrimLeft(suffix); ptriml=StringTrimRight(suffix); } if(sym_Lensuf>0) { ptrimr=StringTrimLeft(suffix); ptrimr=StringTrimRight(suffix); } string sym_pre=prefix; string sym_suf=suffix; //-- 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) - торговать в определенном часовом поясе: да или нет. При 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 Custom Session (пользовательская сессия): Трейдеры должны установить время или часы и минуты для начала торговли и часы и минуты для закрытия торговли. Таким образом, советник будет выполнять действия только в течение указанного времени.
В остальных случаях время начала и окончания торговли выбирается советником.
Класс для работы советника
Чтобы определить конструкцию и конфигурацию рабочего процесса советника, создадим класс, который объявляет все необходимые переменные, объекты и функции.
//+------------------------------------------------------------------+ //| 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 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 BBOnKeltnerChannel(const string symbol); int PARSAR05(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: //--- //-- BBOnKeltnerChannel_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 indiname; //-- int hKC[]; int hBB[]; int hParIU[], hPar05[]; int ALO, dgts, arrsar, arrsymbx; int sall, arusd, aretc, arspc, arper; ulong slip; //-- double profitb[], profits[]; //-- 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; //-- bool PanelExtra; //------------ MCEA(void); ~MCEA(void); //------------ //-- virtual void BBOnKeltnerChannel_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 CloseBuyPositions(const string symbol); void CloseSellPositions(const string symbol); void CloseAllOrders(void); void CheckClose(const string symbx); void TodayOrders(void); void UpdatePrice(const string symbol,ENUM_TIMEFRAMES xtf); void RefreshPrice(const string symbx,ENUM_TIMEFRAMES xtf,int bars); //-- 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 ModifySLTP(const string symbx,int TS_type); bool CloseAllProfit(void); bool ManualCloseAllProfit(void); //-- 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 //---------//
Самая первая и самая важная функция в работе мультивалютного советника, которая вызывается функцией OnInit() — это BBOnKeltnerChannel_MCEA_Config().
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(void) { //--- mc.BBOnKeltnerChannel_MCEA_Config(); //-- return(INIT_SUCCEEDED); //--- } //-end OnInit() //---------//
В функции BBOnKeltnerChannel_MCEA_Config() настраиваются все используемые символы, все используемые индикаторы хэндлов и некоторые важные функции заголовка включаемого файла.
В строках с 468 по 484 объясняется, как обрабатывать таймфрейм и создавать хэндлы индикаторов для всех используемых индикаторов.
//+------------------------------------------------------------------+ //| Expert Configuration | //+------------------------------------------------------------------+ void MCEA::BBOnKeltnerChannel_MCEA_Config(void) { //--- //-- HandlingSymbolArrays(); // With this function we will handle all pairs that will be traded //-- TFT05=PERIOD_M5; 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 //-- //-- Keltner Channel and Bollinger Bands® Indicators handle for all symbol for(int x=0; x<arrsymbx; x++) { hKC[x]=iCustom(DIRI[x],TFt,indiname,KCPeriod,KCMethod,KCMAAP,KCATRPer,KCATRBandsMulti); //-- Handle for the Keltner Channel indicator hParIU[x]=iSAR(DIRI[x],TFt,SARstep,SARmaxi); //-- Handle for the iSAR indicator according to the selected Timeframe hPar05[x]=iSAR(DIRI[x],TFT05,SARstep,SARmaxi); //-- Handle for the iSAR indicator for M5 Timeframe //-- } //-- for(int x=0; x<arrsymbx; x++) hBB[x]=iBands(DIRI[x],TFt,BBPeriod,0,BBDevi,hKC[x]); //-- Handle for the iBands On Keltner Channel Indicator handle //-- ALO=(int)mc_account.LimitOrders()>sall ? sall : (int)mc_account.LimitOrders(); //-- LotPS=(double)ALO; //-- mc_trade.SetExpertMagicNumber(magicEA); mc_trade.SetDeviationInPoints(slip); mc_trade.SetMarginMode(); Set_Time_Zone(); //-- return; //--- } //-end BBOnKeltnerChannel_MCEA_Config() //---------//
2. Функция 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++) { //-- if(mc.DIRI[x]==Symbol()) symbol=Symbol(); else symbol=mc.DIRI[x]; //-- 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 if(SaveOnRev==Yes) mc.CloseAllProfit(); } } 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 if(SaveOnRev==Yes) mc.CloseAllProfit(); } } } //-- 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]>0.02 && 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]>0.02 && 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(TrailingSLTP==Yes) //-- Use Trailing SL/TP (Yes) { if(autotrl==Yes) mc.ModifySLTP(symbol,1); //-- If Use Automatic Trailing (Yes) if(autotrl==No) mc.ModifySLTP(symbol,0); //-- Use Automatic Trailing (No) } } //-- 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 будут отображаться в торговой информации на графике.
В группе свойств советника 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)
Примечание: Как уже упоминалось выше, в остальных случаях время начала и окончания торговли выбирается советником. Таким образом, в свойствах 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() //---------//
3. Получение торговых сигналов для открытия позиций
Для получения сигнала функция ExpertActionTrade() вызывает функцию GetOpenPosition().
int MCEA::GetOpenPosition(const string symbol) // Signal Open Position { //--- int ret=0; int rise=1, down=-1; //-- int BBOnKC=BBOnKeltnerChannel(symbol); //-- if(BBOnKC==rise) ret=rise; if(BBOnKC==down) ret=down; //-- return(ret); //--- } //-end GetOpenPosition() //---------//
Функция GetOpenPosition() вызывает функции BBOnKeltnerChannel(), выполняющие расчет сигналов.
int MCEA::BBOnKeltnerChannel(const string symbol) // Fungction of Bollinger Bands® On Keltner Channel Indicator { //--- int ret=0; int rise=1, down=-1; int br=3; Pips(symbol); double difud=mc_symbol.NormalizePrice(1.5*pip); //-- double KCmb[], // Destination array for Keltner Channel Middle Line buffer KCub[], // Destination array for Keltner Channel Upper Band buffer KClb[]; // Destination array for Keltner Channel Lower Band buffer double BBmb[], // Destination array for Bollinger Bands® BASE_LINE buffer BBub[], // Destination array for Bollinger Bands® UPPER_BAND buffer BBlb[]; // Destination array for Bollinger Bands® LOWER_BAND buffer //-- ArrayResize(KCmb,br,br); ArrayResize(KCub,br,br); ArrayResize(KClb,br,br); ArrayResize(BBmb,br,br); ArrayResize(BBub,br,br); ArrayResize(BBlb,br,br); ArraySetAsSeries(KCmb,true); ArraySetAsSeries(KCub,true); ArraySetAsSeries(KClb,true); ArraySetAsSeries(BBmb,true); ArraySetAsSeries(BBub,true); ArraySetAsSeries(BBlb,true); //-- int xx=PairsIdxArray(symbol); //-- CopyBuffer(hKC[xx],0,0,br,KCmb); CopyBuffer(hKC[xx],1,0,br,KCub); CopyBuffer(hKC[xx],2,0,br,KClb); CopyBuffer(hBB[xx],0,0,br,BBmb); CopyBuffer(hBB[xx],1,0,br,BBub); CopyBuffer(hBB[xx],2,0,br,BBlb); //-- bool BBKCrise1=(KCmb[1]<=BBlb[1] && KCmb[0]>BBlb[0]+difud); bool BBKCrise2=(KCmb[1]<=BBmb[1] && KCmb[0]>BBmb[0]+difud); bool BBKCrise3=(KCmb[1]<=BBub[1] && KCmb[0]>BBub[0]+difud); //-- bool BBKCdown1=(KCmb[1]>=BBub[1] && KCmb[0]<BBub[0]-difud); bool BBKCdown2=(KCmb[1]>=BBmb[1] && KCmb[0]<BBmb[0]-difud); bool BBKCdown3=(KCmb[1]>=BBlb[1] && KCmb[0]<BBlb[0]-difud); //-- if(BBKCrise1 || BBKCrise2 || BBKCrise3) ret=rise; if(BBKCdown1 || BBKCdown2 || BBKCdown3) ret=down; //-- return(ret); //--- } //-end BBOnKeltnerChannel() //---------//
Как видим, внутри функции BBOnKeltnerChannel() мы используем и вызываем одну функцию — 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() используется для получения имени запрошенного символа и хэндлов индикаторов. Затем вызывается соответствующий хэндл индикатора, чтобы получить значение буфера сигнала канала Кельтнера и полос Боллинджера с этого таймфрейма.
Далее нам нужно узнать номер буфера канала Кельтнера. Номера буферов в функции OnInit() следующие: 0 - средняя полоса, 1 - верхняя полоса, 2 - нижняя полоса
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping //-- assignment of array to indicator buffer SetIndexBuffer(0,KC_Middle,INDICATOR_DATA); SetIndexBuffer(1,KC_UpperB,INDICATOR_DATA); SetIndexBuffer(2,KC_LowerB,INDICATOR_DATA); SetIndexBuffer(3,KC_ATRtemp,INDICATOR_CALCULATIONS); //-- handleMA=iMA(Symbol(),Period(),period_kc,0,MODE_EMA,ma_price); //-- if(handleMA==INVALID_HANDLE) { //--- tell about the failure and output the error code PrintFormat("Failed to create handle of the Moving Average indicator for the symbol %s/%s, error code %d", Symbol(), EnumToString(Period()), GetLastError()); //--- the indicator is stopped early return(INIT_FAILED); } //-- handleATR=iATR(Symbol(),Period(),atr_period); //-- if(handleATR==INVALID_HANDLE) { //--- tell about the failure and output the error code PrintFormat("Failed to create handle of the ATR indicator for the symbol %s/%s, error code %d", Symbol(), EnumToString(Period()), GetLastError()); //--- the indicator is stopped early return(INIT_FAILED); } //-- short_name=StringFormat("Keltner Channel(%d, %s, %.2f)",period_kc,EnumToString(ma_method),band_multi); IndicatorSetString(INDICATOR_SHORTNAME,short_name); IndicatorSetInteger(INDICATOR_DIGITS,Digits()); //-- return(INIT_SUCCEEDED); //--- } //---------//
В этом случае нам также потребуется знать количество буферов полос Боллинджера.
При этом, как поясняется в функции iBands®:
"Номера буферов следующие: 0 - BASE_LINE, 1 - UPPER_BAND, 2 - LOWER_BAND"
applied_price
[in] Используемая цена. Может быть любой ценовой константой ENUM_APPLIED_PRICE или хэндлом другого индикатора.
В этой статье советники будут использовать применяемую цену для полос Боллинджера из хэндла канала Кельтнера.
Итак, чтобы получить значение буфера для каждой линии канала Кельтнера и полосы Боллинджера, мы скопируем каждый буфер из хэндла индикаторов.
Чтобы скопировать буфер средней линии (буфер 0) из хэндла канала Кельтнера в целевой массив:
CopyBuffer(hKC[xx],0,0,br,KCmb);
Чтобы скопировать буфер верхней линии (буфер 1) из хэндла канала Кельтнера в целевой массив:
CopyBuffer(hKC[xx],1,0,br,KCub);
Чтобы скопировать буфер нижней линии (буфер 2) из хэндла канала Кельтнера в целевой массив:
CopyBuffer(hKC[xx],2,0,br,KClb);
Чтобы скопировать буфер BASE_LINE (буфер 0) из хэндла полос Боллинджера в целевой массив:
CopyBuffer(hBB[xx],0,0,br,BBmb);
Чтобы скопировать буфер UPPER_BAND (буфер 1) из хэндла полос Боллинджера в целевой массив:
CopyBuffer(hBB[xx],1,0,br,BBub);
Чтобы скопировать буфер LOWER_BAND buffer (буфер 2) из хэндла полос Боллинджера в целевой массив:
CopyBuffer(hBB[xx],2,0,br,BBlb);
Далее функция GetOpenPosition() возвращает следующие результаты:
- Значение 0 - сигнал неизвестен.
- Значение 1 - сигнал на покупку.
- Значение -1 - сигнал на продажу.
Советник вызывает функцию OpenBuy(), когда функция GetOpenPosition() возвращает значение 1.
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 //---------//
При этом советник вызывает функцию OpenSell(), если функция GetOpenPosition() возвращает значение -1.
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 //---------//
4. Функция 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() //---------//
Интерфейс мультивалютного советника BBOnKeltnerChannel_MCEA выглядит следующим образом.
Как видите, под именем советника BBOnKeltnerChannel_MCEA есть кнопки M, C и R
При нажатии кнопки М отобразится панель кнопок для ручного нажатия, как показано на рисунке ниже.
Трейдер может управлять ордерами вручную, когда отображается панель кнопок ручного нажатия:
1. Set SL/TP All Orders (установить SL/TP для всех ордеров). Как объяснялось выше, если 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 //---------//
2. 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() //---------//
3. 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 именами символов или пар, и трейдеры смогут щелкнуть по одному из названий пар или символов. Нажатие на одно из названий или символов пары немедленно заменяет символ графика символом, по имени которого был выполнен щелчок.
В этом случае функция OnChartEvent() вызывается функцией ChangeChartSymbol() при нажатии на одно из названий символов.
//--- 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 удалит с графика мультивалютный советник BBOnKeltnerChannel_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 мы будем тестировать BBOnKeltnerChannel_MCEA в качестве мультивалютного советника.
В этом тесте мы поместили BBOnKeltnerChannel_MCEA на пару XAGUSD H1 с пользовательским периодом времени 2023.09.04 - 2023.12.02.
Тест проводился с двумя разными входными параметрами из группы параметров Trade & Order Management (управление сделками и ордерами).
1. Входные параметры по умолчанию.
2. Пользовательские входные параметры.
Заключение
Создание мультивалютного советника с сигналами полос Боллинджера на канале Кельтнера с применением MQL5 наталкивает на следующие выводы:
- Создание мультивалютного советника на MQL5 мало чем отличается от разработки одновалютного.
- По сравнению с MetaTrader 4, использовать хэндл индикатора в MQL5 для получения значений индикаторов и сигналов проще и удобнее.
- Создание мультивалютного советника повысит эффективность и результативность трейдеров, поскольку им не нужно будет открывать много графиков.
- Правильная торговая стратегия увеличивает вероятность получения прибыли по сравнению с использованием одновалютного советника. Убытки по одной паре будут перекрываться прибылью в других парах.
- Мультивалютный советник BBOnKeltnerChannel_MCEA является всего лишь примером для изучения и развития собственных идей. Результаты тестирования в тестере стратегий по-прежнему неудовлетворительны. Экспериментируя и тестируя на разных таймфреймах или рассчитывая разные периоды индикаторов, можно получить более прибыльные результаты.
- Результаты тестирования BBOnKeltnerChannel_MCEA в тестере стратегий показывают, что результаты свойств пользовательских входных параметров лучше, чем свойства входных параметров по умолчанию.
Надеюсь, что статья и мультивалютный советник будут полезны трейдерам в изучении и развитии идей.
Спасибо за внимание!
Примечание: Если у вас есть идея создания простого мультивалютного советника на основе встроенных сигналов стандартного индикатора MQL5, предложите ее в комментариях.
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/13861
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Здравствуйте, Роберто, я загрузил на график два индикатора, необходимых советнику, но советник по-прежнему выдает сообщение о том, что эти два индикатора не могут быть открыты.
см. вложение
Спасибо
автоматический перевод применен модератором
Как видно на предоставленном вами изображении, имя файла индикатора Keltner Channel, который вы использовали, неверно.
Пожалуйста, посмотрите разницу в именах файлов индикаторов.
Вы используете индикаторы, отличные от тех, которые требуются экспертам.
Как видно на предоставленном вами изображении, имя файла индикатора Keltner Channel, который вы использовали, неверно.
Пожалуйста, ознакомьтесь с различиями в именах файлов индикаторов.
Вы используете индикаторы, отличные от тех, которые требуются экспертам.
Здравствуйте Роберто
изменил имя индикатора и советник работает нормально.
но открывает много ордеров на одном символе
Здравствуйте, Роберто
измените имя индикатора и советник работает нормально.
но открывается много ордеров на одном символе
Возможно, вы не понимаете или не знаете, что в интернете существует множество версий индикатора Keltner Channel.
Читали ли вы мои объяснения в статье или просто скачали программу советника.
В части 2. Сигнальные индикаторы" я объяснил:
"Индикатор Keltner Channel, используемый для советников в этой статье, я специально создал с использованием популярного сегодня метода, а именно экспоненциальной скользящей средней (EMA) периода 20 с верхней и нижней полосами с использованием индикатора ATR периода 20".
Поэтому не стоит надеяться, что, переименовав индикатор, советник будет работать корректно, так как у разных индикаторов разные входные параметры и разные расчеты.
Если вы не используете индикатор Keltner Channel, который я создал специально для советника Bollinger Bands® On Keltner Channel, то не стоит надеяться, что вы сможете использовать советник Bollinger Bands® On Keltner Channel.
Здравствуйте, Роберто,
Вы проделали отличную работу! Я надеюсь заставить его работать на моем ПК.
У меня есть следующее сообщение: Keltner Channel.ex5 open error и другие.
Конечно, тестер не дает никаких результатов.
Спасибо за помощь.
Алена
Привет, Роберто,
Вы проделали отличную работу! Я надеюсь заставить его работать на моем ПК.
У меня есть следующее сообщение: Ошибка открытия Keltner Channel.ex5 и другие.
Конечно, тестер не дает никаких результатов.
Спасибо за помощь.
Ален
Здравствуйте,
Мой советник называется BBOnKeltnerChannel_MCEA, а не Multi-currency BB-Keltner (на картинке, которую вы прислали мне на почту, название советника - Multi-currency BB-Keltner)
Название индикатора, который я создал специально для BBOnKeltnerChannel_MCEA - Keltner Channel.mq5 (посмотреть и скачать вложение к этой статье).
В сети существует множество версий индикатора Keltner Channel.
Пожалуйста, обращайте пристальное внимание на имя файла и вводимые свойства в индикаторах и экспертах, поскольку они чувствительны к регистру.
Если входные свойства не совпадают, то программа не будет работать.
Если вы измените программу советника и возникнет ошибка, не стоит спрашивать меня.
Все программы на MQL5 проходят проверку модератором, отвечающим за них, поэтому если они не проходят проверку, то публикация программы на MQL5 невозможна.