//+------------------------------------------------------------------+ //| Самообучающийся ЭКСПЕРТ(lsv).mq4 | //| Copyright © 2005, MetaQuotes Software Corp. | //| http://www.metaquotes.net | //| Изменил: Лыжин Сергей 03.09.2006 | //+------------------------------------------------------------------+ #property copyright "Copyright © 2005, MetaQuotes Software Corp." #property link "http://www.metaquotes.net" // <Работа с программой.Обучение.> // При заданном наборе внешних (входных) параметров обучение программы // можно проводить двумя способами: // А) В результате предварительного тестирования на Тестере Стратегий. // Для этого нужно предварительно загрузить достаточный объем истории, // хотя бы за год, что можно сделать, открыв график нужной валютной // пары и удерживая клавишу Home. Затем открываем меню Вид/Тестер // Стратегий, и запускаем тест с выбором точной модели тестирования // “по всем тикам” (подробнее о работе с Тестером см. встроенную // инструкцию платформы МТ4). // В процессе теста файлы статистики сделок создаются Тестером и // записываются в папку Tester/files. Для работы с реальным счетом // их достаточно перенести в папку Experts/files. // Б) Обучение может быть также проведено при запуске программы в // режиме реального времени. В этом случае файлы статистики сделок // создаются сразу в папке Experts/files. Накопление статистики // фиктивных сделок в реальном времени происходит не быстро. Может // потребоваться неделя или больше, прежде чем советник соберет // достаточный объем информации и откроет первую позицию. В то же // время, файлы статистики, быстро создаваемые Тестером Стратегий, // не вполне удовлетворительны, так как они несколько искажены // погрешностями, возникающими при моделировании Тестером движения // цены внутри бара. Этого достаточно для демонстрации // работоспособности алгоритма и презентации программы, однако запуск // советника на реальном счете с этими файлами статистики сопряжен с // определенным риском. Поэтому оптимальным способом обучения // представляется следующий. Файлы обучения созданные Тестером после // тестирования на исторических данных, следует рассматривать как // “первое приближение”. Их следует перенести в папку Experts/files // и запустить советника в режиме реального времени на демо-счете. // Советник сразу может совершать сделки. Однако обновленные файлы // статистики сделок появятся спустя некоторое время (около недели- // для параметров принятых по умолчанию). Проще говоря, после перехода // с Тестера на реальный счет советнику необходимо “расторговаться”. // За это время он корректирует файлы статистики, созданные ранее // Тестером. // <Переобучение. Параметр ReadHistory.> // Если программа была ранее хотя бы раз протестирована на данном // символе, она считается обученной работать на этом символе, а // папка Tester/files содержит файлы статистики. При повторном // запуске на Тестере программа по умолчанию считывает информацию // из файлов статистики. Поэтому при изменении значений каких либо // входных параметров необходимо запретить чтение данных из файлов // статистики. В противном случае возникнет несоответствие старых // результатов обучения и новых входных параметров, в результате // чего программа может даже продемонстрировать отрицательный // результат. За переобучение программы отвечает параметр // ReadHistory. Если при запуске программы ReadHistory=0, программа // стирает прежний файл статистики и пишет на его место новый, // отвечающий новым входным параметрам. Если ReadHistory=1, // программа считывает результаты прежнего обучения и использует // их в работе. Eсли ReadHistory=1, а файла статистики не // обнаружено, программа прописывает в журнал ошибку чтения файла // (это нормально), создает нужные файлы и записывает в них // информацию аналогично запуску c выбором ReadHistory=0. // <Прочие параметры.> // - Probab – требуемая вероятность выигрыша, на основании // статистических данных. Чем ближе Probab к единице, тем более // вероятно достижение выигрыша в случае открытия позиции. Однако // рыночные ситуации с гарантированным выигрышем реализуются редко. // Поэтому для фактического достижения выигрыша пользователь обязан // разрешить определенный риск. Таким образом, советник не является // машиной по стабильному производству денег. Пользователь // осознает наличие инвестиционного риска и самостоятельно назначает // вероятность проигрыша. Указанная вероятность проигрыша равна нулю // при выборе Probab=1, в этом случае советник никогда не откроет // позицию; // - forg – скорость забывания результатов обучения. От 1 (забывания // нет) до 1.1 (сильное забывание). Параметр полезен, если необходимо // придать последним сделкам больший статистический вес. Известно, // что рынок со временем изменяется. Это выражается, к примеру в том, // что рыночные ситуации, которые в прошлом году традиционно были // благоприятными для позиции Buy, в новом году могут оказаться // благоприятными для позиции Sell. Если поставлено forg=1, // программа считает вполне равноправными рыночные ситуации, // реализованные в прошлом и в новом году. С учетом требуемой // вероятности положительного закрытия (Probab) это приводит к тому, // что программа ищет долгосрочные стабильные тренды. В то же время, // она игнорирует явные признаки появления новых трендов. Напротив, // при выборе forg>1 программа, наряду с долгосрочными трендами, // которые действовали на всей доступной истории, отслеживает также // появление новых трендов. При появлении очередной рыночной ситуации // статистический вес предыдущих аналогичных рыночных ситуации // понижается в forg раз. Иначе говоря, при forg=1.05 забывание // истории “наполовину” происходит при появлении 10-15 аналогичных // ценовых комбинаций; // - dstop =TakeProfit=StopLoss. В текущей версии программы всегда // выставляется ограничение прибыли, равное ограничению потерь. // Однако, программа может закрывать позиции, не дожидаясь // достижения стопов. // - delta – минимальный шаг, при котором цена считается // нетождественна предыдущей, и происходит обновление ценовых // паттернов. При обучении на коротких периодах этот параметр можно // взять 1-2 (обновление паттернов на каждый тик), большие периоды // могут быть адекватно восприняты Тестером только при более грубом // разрешении, delta=3-5. Фактически, программа анализирует тики, // а параметр delta заменяет собой выбор таймфрейма; // - Nidelt – число типов ценовых паттернов (число шагов по изменению // шага). Это непростой параметр, значение которого можно оставить // равным по умолчанию. При каждом изменении цены программа // анализирует цены, записанные в наборе из Nidelt типов паттернов // из NN элементов каждый. Цены соседних элементов idelt паттерна // отличаются на величину delta*idelt, где 1 (delt[idelt] - 0.5)*Point) { for(i = 1; i <= NN - 1; i++) { P[NN+1-i, idelt] = P[NN-i, idelt]; } P[1,idelt] = Ask; } //при изменении вектора проводим обработку новой информации if(MathAbs(P[1, idelt] - pt[idelt]) > 0.5*Point) { //строим вектор парных сравнений for(i = 1; i <= NN - 1; i++) { if(P[i,idelt] > P[i+1, idelt]) { sr[i, idelt] = 1; } else { sr[i,idelt] = 0; } } //вычисляем номер текущей комбинации Ncomb = 0; mn = 1; //---- for(i = 1; i <= NN - 1; i++) { Ncomb = Ncomb + mn*sr[i, idelt]; mn = 2*mn; } //открываем фиктивную позицию с меткой комбинации, если такая позиция еще не открыта //открытие проводим по всей пачке стопов c порогом по частоте сделок в каждом канале for(istop = 1; istop <= Nstop; istop++) { if(sd[Ncomb, idelt, istop] == 0 && CurTime() - LastSd[Ncomb, idelt, istop] > 2*Period()*60) { sd[Ncomb, idelt, istop] = 1; cen[Ncomb, idelt, istop] = Ask; LastSd[Ncomb, idelt, istop] = CurTime(); } } //проверяем все прежние фиктивные позиции на предмет закрытия for(istop = 1; istop <= Nstop; istop++) { stop[istop] = dstop*istop; //---- for(i = 0; i <= mn - 1; i++) { if(sd[i, idelt, istop] == 1) { if(Ask - cen[i, idelt, istop] > stop[istop]*Point) { rost[i, idelt, istop] = rost[i, idelt, istop] / forg + 1; spad[i, idelt, istop] = spad[i, idelt, istop] / forg; sd[i, idelt, istop] = 0; nsd[i, idelt, istop] = nsd[i, idelt, istop] + 1; } //---- if(cen[i, idelt, istop] - Ask > stop[istop]*Point) { spad[i, idelt, istop] = spad[i, idelt, istop] / forg + 1; rost[i, idelt, istop] = rost[i, idelt, istop] / forg; sd[i, idelt, istop] = 0; nsd[i, idelt, istop] = nsd[i, idelt, istop] + 1; } } } } // даем приказ на открытие реальной позиции, если статистика текущей // комбинации благоприятна for(istop = 1; istop <= Nstop; istop++) { stop[istop] = dstop*istop; prob = rost[Ncomb, idelt, istop] / (rost[Ncomb, idelt, istop] + spad[Ncomb, idelt, istop] + 0.0001); //---- if(prob > Probab && nsd[Ncomb, idelt, istop] > 10 && CurTime() - LastBuyOpen > 2*Period()*60) { Take0 = stop[istop]; Stop0 = stop[istop]; buy_open = 1; //---- a = OrdersTotal(); //---- for(i=0; i (prob0 + 0.05)) sell_close = 1; FileClose(file1); } } FileName = "FDlast_buy" + Symbol() + Period(); file1 = FileOpen(FileName, FILE_CSV | FILE_WRITE); FileWrite(file1, prob); FileClose(file1); } //фиксация прибыли слабым сигналом a = OrdersTotal(); //---- for(i=0; i 0.6 && nsd[Ncomb, idelt, istop] > 10 && (OrderOpenPrice() - Ask) > (dstop / 2)*Point) sell_close = 1; } } prob = spad[Ncomb, idelt, istop] / (rost[Ncomb, idelt, istop] + spad[Ncomb, idelt, istop] + 0.0001); //---- if(prob > Probab && nsd[Ncomb, idelt, istop] > 10 && CurTime() - LastSellOpen > 2*Period()*60) { Take0 = stop[istop]; Stop0 = stop[istop]; sell_open = 1; //---- a = OrdersTotal(); //---- for(i=0; i (prob0 + 0.05)) buy_close = 1; FileClose(file1); } } FileName = "FDlast_sell" + Symbol() + Period(); file1 = FileOpen(FileName, FILE_CSV | FILE_WRITE); FileWrite(file1, prob); FileClose(file1); } //фиксация прибыли слабым сигналом a = OrdersTotal(); //---- for(i=0; i 0.6 && nsd[Ncomb, idelt, istop] > 10 && (Bid - OrderOpenPrice()) > (dstop / 2)*Point) buy_close = 1; } } } izm = izm + 1; } pt[idelt]=P[1,idelt]; } //Считывание информации из файла if(ii == 0) { FileName = "FD_" + Symbol(); ii = 1; file1 = FileOpen(FileName, FILE_CSV | FILE_READ); metka = FileReadNumber(file1); time0 = FileReadNumber(file1); //---- if(metka == 1 && ReadHistory == 1 && ReWriteHistory != 1) { /* for(idelt = 1; idelt <= Nidelt; idelt++) { for(i = 1; i <= NN; i++) { P[i,idelt] = FileReadNumber(file1); } } */ for(istop = 1; istop <= Nstop; istop++) { for(idelt = 1; idelt <= Nidelt; idelt++) { for(i= 0; i <= mn - 1; i++) { rost[i,idelt,istop] = FileReadNumber(file1); spad[i,idelt,istop] = FileReadNumber(file1); nsd[i,idelt,istop] = FileReadNumber(file1); } } } } FileClose(file1); } //Запись информации в файл через 100 изменений вектора //Возможность накрутки статистики при частом тестировании исключена if(izm > 10 && (CurTime() >= time0 || ReWriteHistory == 1)) { izm = 0; FileName = "FD_" + Symbol(); file1=FileOpen(FileName, FILE_CSV | FILE_WRITE); FileWrite(file1, "1"); FileWrite(file1, CurTime()); /* for(idelt = 1; idelt <= Nidelt; idelt++) { for(i=1;i<=NN;i++) { FileWrite(file1, P[i,idelt]); } } */ for(istop = 1; istop <= Nstop; istop++) { for(idelt = 1; idelt <= Nidelt; idelt++) { for(i = 0; i <= mn - 1; i++) { FileWrite(file1, rost[i,idelt,istop]); FileWrite(file1, spad[i,idelt,istop]); FileWrite(file1, nsd[i,idelt,istop]); } } } FileClose(file1); } //БЛОК ИСПОЛНЕНИЯ ПРИКАЗОВ a = OrdersTotal(); total = 0; // Количество ордеров открытых этим экспертом (по 1-му в обе стороны) //---- for(i = 0; i < a; i++) if(MyOrder(i)) total ++; //исполняем приказы на открытие if(total == 0) { if(buy_open == 1 && MathAbs(OpenPriceBuy - Ask) > 10*Point) { OrderNew(OP_BUY); return(0); } //---- if(sell_open == 1 && MathAbs(OpenPriceSell - Bid) > 10*Point) { OrderNew(OP_SELL); return(0); } } //---- if(total == 1) { a = OrdersTotal(); //---- for(i = 0; i < a; i++) if(MyOrder(i)) { if(OrderType() == OP_SELL) { if(buy_open == 1 && MathAbs(OpenPriceSell - Bid) > 10*Point) { OrderNew(OP_BUY); return(0); } } //---- if(OrderType() == OP_BUY) { if(sell_open == 1 && MathAbs(OpenPriceBuy - Ask) > 10*Point) { OrderNew(OP_SELL); return(0); } } } } //смещение стопов при повторных приказах на открытие позиции того же типа a = OrdersTotal(); //---- for(i = 0; i < a; i++) if(MyOrder(i)) { if(OrderType() == OP_BUY && buy_open == 1 && Bid - OrderStopLoss() > Stop0*Point) { OrderModify(OrderTicket(), OrderOpenPrice(), Bid - Stop0*Point, Bid + Take0*Point, 0, Orange); return(0); } //---- if(OrderType() == OP_SELL && sell_open == 1 && OrderStopLoss() - Ask > Stop0*Point) { OrderModify(OrderTicket(), OrderOpenPrice(), Ask + Stop0*Point, Ask - Take0*Point, 0, Red); return(0); } } //снимаем приказы на открытие, если нужные позиции уже открыты a = OrdersTotal(); //---- for(i = 0; i < a; i++) if(MyOrder(i)) { if(OrderType() == OP_BUY) { buy_open = 0; LastBuyOpen = CurTime(); OpenPriceBuy = OrderOpenPrice(); } //---- if(OrderType() == OP_SELL) { sell_open = 0; LastSellOpen = CurTime(); OpenPriceSell = OrderOpenPrice(); } } //исполняем приказы на закрытие a = OrdersTotal(); //---- for(i = 0; i < a; i++) if(MyOrder(i)) { if(buy_close == 1 && OrderType() == OP_BUY) { OrderClose(OrderTicket(), OrderLots(), Bid, 3, Orange); return(0); } //---- if(sell_close == 1 && OrderType() == OP_SELL) { OrderClose(OrderTicket(), OrderLots(), Ask, 3, Violet); return(0); } } //снимаем приказы на закрытие, если нужные позиции уже закрыты nb = 0; ns = 0; a = OrdersTotal(); for(i = 0; i < a; i++) if(MyOrder(i)) { if(OrderType() == OP_BUY) nb = 1; if(OrderType() == OP_SELL) ns = 1; } //---- if (nb == 0) buy_close = 0; //---- if (ns == 0) sell_close = 0; //ДВИЖЕНИЕ СТОПОВ a = OrdersTotal(); for(i = 0; i < a; i++) if(MyOrder(i)) { if(OrderType() == OP_BUY && TrailingStop > 10) { if(OrderStopLoss() < Bid - Point*TrailingStop) OrderModify(OrderTicket(), OrderOpenPrice(), Bid - Point*TrailingStop, Bid + Point*TrailingStop, 0, Orange); } if(OrderType() == OP_SELL && TrailingStop > 10) { if(OrderStopLoss() > Ask + Point*TrailingStop) OrderModify(OrderTicket(), OrderOpenPrice(), Ask + Point*TrailingStop, Ask - Point*TrailingStop, 0, Red); } } return(0); } //+------------------------------------------------------------------+ // Создание нового ордера //+------------------------------------------------------------------+ void OrderNew(int cmd) { if(cmd == OP_BUY) { ticket = OrderSend(Symbol(), OP_BUY, Lots, Ask, 3, Ask - Stop0*Point, Ask + Take0*Point, "AT", MagicID, 0, Blue); //---- if(ticket < 0) Print("СЭ OrderSend() Error: ", GetLastError()); else Print("Trying to open BUY price: ", Ask, " Stop ", Ask - Stop0*Point, " TP ", Point, Ask + (Take0*Point)); } else { ticket = OrderSend(Symbol(), OP_SELL, Lots, Bid, 3, Bid + Stop0*Point, Bid - Take0*Point, "AT", MagicID, 0, Red); //---- if(ticket < 0) Print("СЭ OrderSend() Error: ", GetLastError()); else Print("Trying to open SELL price: ", Bid, " Stop ", Bid + Stop0*Point, " TP ", Bid - Take0*Point); } } //+------------------------------------------------------------------+ // Возвращает TRUE если выбранный ордер принадлежит этому эксперту //+------------------------------------------------------------------+ bool MyOrder(int a) { return(OrderSelect(a, SELECT_BY_POS, MODE_TRADES) && OrderSymbol() == Symbol() && OrderMagicNumber() == MagicID); } //+------------------------------------------------------------------+