Сказки торговых роботов: лучше меньше - да лучше?
Чтобы решить проблему, ее нужно сначала сформулировать. Если я считаю, что нашел решение, его нужно проверить в деле, чтобы убедиться в своей правоте.
Я знаю только один способ проверки — на собственных деньгах.
Д.Ливермор
Пролог
В статье Последний крестовый поход мы с вами, уважаемый читатель, рассмотрели довольно интересный и мало используемый в настоящее время способ отображения рыночной информации - графики крестиков-ноликов. Предложенный в статье скрипт позволял строить графики, но не предполагал автоматизации торговли. Предлагаю вам автоматизировать процесс торговли, используя для анализа и принятия решений о направлении и объемах торговли графики крестиков-ноликов.
Не буду напоминать основных принципов построения, предложу лучше посмотреть на типичный график:
Copyright (c) 2012-2014 Roman Rich Euro vs US Dollar, Box-20, Reverse-3 1.4588 | \.....\.................................................................... | 1.4588 1.4521 | X\....X\................................................................... | 1.4521 1.4454 | XO\.\.XO\.................................................................. | 1.4454 1.4388 | XOX\X\XO.\................................................................. | 1.4388 1.4322 | XOXOXOXO..\................................................................ | 1.4322 1.4256 | XOXOXOXO...\....\.......................................................... | 1.4256 1.4191 | XOXO/OXO....\...X\......................................................... | 1.4191 1.4125 | XOX/.O/O.....\..XO\........................................................ | 1.4125 1.4060 | XO/../.O......\.XO.\....................................................... | 1.4060 1.3996 | ./.....O.......\XO..\...................................................... | 1.3996 1.3932 | .......OX.......XO...\....................................................X | 1.3932 1.3868 | .......OXO..X.X.XOX...\.................................................X.X | 1.3868 1.3804 | .......OXO..XOXOXOXOX..\..............................................X.XOX | 1.3804 1.3740 | .......OXO..XOXOXOXOXO..\.................................\...........XOXOX | 1.3740 1.3677 | .......OXOX.XO.O.OXOXO...\................................X\..........XOXOX | 1.3677 1.3614 | .......OXOXOX....O.OXO....\...............................XO\.........XOXOX | 1.3614 1.3552 | .......O.OXOX...../OXO.....\..............................XO.\........XOXOX | 1.3552 1.3490 | .........OXOX..../.O.OX.....\.............................XO..\.......XOXO. | 1.3490 1.3428 | .........OXOX.../....OXO.....\X.\.........................XO...\\...X.XOX.. | 1.3428 1.3366 | .........O.OX../.....OXO......XOX\........................XO....X\..XOXOX.. | 1.3366 1.3305 | ...........OX./......OXO....X.XOXO\.....................X.XO....XO\.XOXO... | 1.3305 1.3243 | ...........OX/.......O.O....XOXOXOX\....................XOXO....XO.\XOX.../ | 1.3243 1.3183 | ...........O/..........OX...XOXOXOXO\...................XOXOX.X.XOX.XOX../. | 1.3183 1.3122 | .........../...........OXO..XOXOXOXO.\..........X...X.X.XOXOXOXOXOXOXO../.. | 1.3122 1.3062 | .......................OXOX.XOXO.OXO..\.........XOX.XOXOXOXOXOXOXOXOX../... | 1.3062 1.3002 | .......................O.OXOXO...O/O...\........XOXOXOXOXO.OXO.OXOXO../.... | 1.3002 1.2942 | .........................OXOX..../.O....\.......XOXOXOXOX..OX..OXOX../..... | 1.2942 1.2882 | .........................O.OX.../..O.....\......XOXO.OXO...OX..OXOX./...... | 1.2882 1.2823 | ...........................OX../...OX.....\.....XO...OX.../OX..O/OX/....... | 1.2823 1.2764 | ...........................OX./....OXO.....\....X....OX../.O.../.O/........ | 1.2764 1.2706 | ...........................OX/.....OXO..X...\...X....O../......../......... | 1.2706 1.2647 | ...........................O/......O.OX.XOX..\..X....../................... | 1.2647 1.2589 | .........................../.........OXOXOXO..\.X...../.................... | 1.2589 1.2531 | .....................................OXOXOXO...\X..../..................... | 1.2531 1.2474 | .....................................OXO.OXO....X.../...................... | 1.2474 1.2417 | .....................................OX..O.O..X.X../....................... | 1.2417 1.2359 | .....................................OX....OX.XOX./........................ | 1.2359 1.2303 | .....................................O.....OXOXOX/......................... | 1.2303 1.2246 | ...........................................OXOXO/.......................... | 1.2246 1.2190 | ...........................................OXO./........................... | 1.2190 1.2134 | ...........................................OX.............................. | 1.2134 1.2078 | ...........................................O............................... | 1.2078 1.2023 | ........................................................................... | 1.2023 222222222222222222222222222222222222222222222222222222222222222222222222222 000000000000000000000000000000000000000000000000000000000000000000000000000 111111111111111111111111111111111111111111111111111111111111111111111111111 111111111111111111111111112222222222222222222222222222222333333333333333344 ........................................................................... 000000000001111111111111110000000000000000000000011111111000000000000011100 788888899990000001111112221122233445566666677888900001222123444567778901213 ........................................................................... 200011211220111220011231220101212121201112222001100010001002123110112020231 658801925683489071404504193396436668111288937260415979579417630739120547713 000100001012111111110111111100112010210001111101101101011111111101011101110 910501876933613095500253237788652909250001557626626824655375907538165785367 ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 550433251023230204310404232105354323532031240033315125241340044324523153453 000000000000000000000000000000000000000000000000000000000000000000000000000
Не стану утверждать, что торговые возможности явно видны на этом графике, предложу лишь проверить две торговые гипотезы:
Торгуйте по тренду — покупайте на бычьем рынке и продавайте на медвежьем.
Торгуйте со стоп-ордерами, определенными до входа в рынок.
- покупаем над линией поддержки стоп-ордером выше максимума предыдущей колонки "Х", продаем под линией сопротивления стоп-ордером ниже минимума предыдущей колонки "О", скользящий стоп - на уровне разворота;
Дайте прибыли расти.
Закрывайте сделки, которые показывают потери (хорошие сделки, обычно, сразу же показывают прибыль).
- покупаем при пробое линии сопротивления, продаем при пробое линии поддержки, стоп-лосс - на уровне разворота, скользящий стоп - по трендовой линии.
А каким объемом входим?
Обратите внимание на приведенные выше цитаты классика биржевых спекуляций: торгуйте со стоп-ордерами. Я предпочитаю входить в рынок таким объемом, чтобы при срабатывании ордера Стоп Лосс потери баланса составляли не более приемлемого для меня процента от этого самого баланса (то, что Ральф Винс в своей Математике управления капиталом назвал оптимальное F). Риск потери с одной сделки - отлично оптимизируемая переменная (в приведенном ниже коде - opt_f).
Таким образом, имеем функцию установки ордера на покупку/продажу с механизмом расчета объема, зависящего от приемлемого для вас риска на одну сделку:
//+------------------------------------------------------------------+ //| Функция выставления ордера с предварительно подсчитанным объемом | //+------------------------------------------------------------------+ void PlaceOrder() { //--- Переменные для расчета лота uint digits_2_lot=(uint)SymbolInfoInteger(symbol,SYMBOL_DIGITS); double trade_risk=AccountInfoDouble(ACCOUNT_EQUITY)*opt_f; double one_tick_loss_min_lot=SymbolInfoDouble(symbol,SYMBOL_TRADE_TICK_VALUE_LOSS)*SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP); //--- Заполняем основные поля запроса trade_request.magic=magic; trade_request.symbol=symbol; trade_request.action=TRADE_ACTION_PENDING; trade_request.tp=NULL; trade_request.comment=NULL; trade_request.type_filling=NULL; trade_request.stoplimit=NULL; trade_request.type_time=NULL; trade_request.expiration=NULL; if(is_const_lot==true) { order_vol=SymbolInfoDouble(symbol,SYMBOL_VOLUME_MIN); } else { order_vol=trade_risk/(MathAbs(trade_request.price-trade_request.sl)*MathPow(10,digits_2_lot)*one_tick_loss_min_lot)*SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP); order_vol=MathMax(order_vol,SymbolInfoDouble(symbol,SYMBOL_VOLUME_MIN)); if(SymbolInfoDouble(symbol,SYMBOL_VOLUME_LIMIT)!=0) order_vol=MathMin(order_vol,SymbolInfoDouble(symbol,SYMBOL_VOLUME_LIMIT)); order_vol=NormalizeDouble(order_vol,(int)MathAbs(MathLog10(SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP)))); } //--- Ставим ордер while(order_vol>0) { trade_request.volume=MathMin(order_vol,SymbolInfoDouble(symbol,SYMBOL_VOLUME_MAX)); if(!OrderSend(trade_request,trade_result)) Print("Failed to send order #",trade_request.order); order_vol=order_vol-SymbolInfoDouble(symbol,SYMBOL_VOLUME_MAX); }; ticket=trade_result.order; };
А по какому критерию оптимизируем?
Реально критериев оптимизации всего два: либо минимизация просадки при заданном уровне доходности, либо максимизация баланса при заданном уровне просадки. Я предпочитаю оптимизировать по второму критерию:
//+------------------------------------------------------------------+ //| Результат работы стратегии в режиме тестирования | //+------------------------------------------------------------------+ double OnTester() { if(TesterStatistics(STAT_EQUITY_DDREL_PERCENT)>(risk*100)) return(0); else return(NormalizeDouble(TesterStatistics(STAT_PROFIT),(uint)SymbolInfoInteger(symbol,SYMBOL_DIGITS))); };
где risk - уровень просадки, выше которого стратегия для меня считается неприемлемой. Эта также одна из оптимизируемых переменных.
А когда необходимо оптимизировать повторно?
Кто-то проводит повторную оптимизацию через интервалы времени (например, раз в неделю, в месяц), кто-то - через интервалы сделок (через 50 сделок, через 100), кто-то - как рынок поменяется. Реально же необходимость в повторной оптимизации диктуется лишь принятым критерием оптимизации в предыдущем пункте. Просела система ниже максимально допустимого параметра risk - проводим повторную оптимизацию, не просела - не трогаем. Для себя считаю неприемлемой просадку больше 10%. Таким образом, если просадка в процессе реальной работы системы превышает это значение - оптимизирую повторно.
А оптимизировать сразу все?
К сожалению, в тестере стратегий пока не реализована возможность оптимизировать внешние переменные для режима "Все символы, выбранные в окне 'Обзор рынка'". Поэтому выбор рыночного инструмента наряду с прочими оптимизируемыми внешними переменными произведем следующим нехитрым способом:
//+------------------------------------------------------------------+ //| Перечисление символов | //+------------------------------------------------------------------+ enum SYMBOLS { AA=1, AIG, AXP, BA, C, CAT, DD, DIS, GE, HD, HON, HPQ, IBM, IP, INTC, JNJ, JPM, KO, MCD, MMM, MO, MRK, MSFT, PFE, PG, QQQ, T, SPY, UTX, VZ, WMT, XOM }; //+------------------------------------------------------------------+ //| Функция выбора символа | //+------------------------------------------------------------------+ void SelectSymbol() { switch(selected_symbol) { case 1: symbol="#AA"; break; case 2: symbol="#AIG"; break; case 3: symbol="#AXP"; break; case 4: symbol="#BA"; break; case 5: symbol="#C"; break; case 6: symbol="#CAT"; break; case 7: symbol="#DD"; break; case 8: symbol="#DIS"; break; case 9: symbol="#GE"; break; case 10: symbol="#HD"; break; case 11: symbol="#HON"; break; case 12: symbol="#HPQ"; break; case 13: symbol="#IBM"; break; case 14: symbol="#IP"; break; case 15: symbol="#INTC"; break; case 16: symbol="#JNJ"; break; case 17: symbol="#JPM"; break; case 18: symbol="#KO"; break; case 19: symbol="#MCD"; break; case 20: symbol="#MMM"; break; case 21: symbol="#MO"; break; case 22: symbol="#MRK"; break; case 23: symbol="#MSFT"; break; case 24: symbol="#PFE"; break; case 25: symbol="#PG"; break; case 26: symbol="#QQQ"; break; case 27: symbol="#T"; break; case 28: symbol="#SPY"; break; case 29: symbol="#UTX"; break; case 30: symbol="#VZ"; break; case 31: symbol="#WMT"; break; case 32: symbol="#XOM"; break; default: symbol="#SPY"; break; }; };
При необходимости вы можете добавлять в перечисление и функцию выбора символа (которую вызовем в OnInit()) нужные вам инструменты.
Что за робот у нас получился?
Обработчик тиков:
//+------------------------------------------------------------------+ //| Типичный обработчик тиков OnTick() | //| График строим только по сформированным барам и сначала | //| проверяем, не новый ли сейчас бар? | //| Если бар новый, то при наличии позиций проверяем, | //| не нужно ли передвинуть стоп-лосс, | //| а при отсутствии позиций проверяем, | //| может есть условия для открытия сделки? | //+------------------------------------------------------------------+ void OnTick() { //--- Если новый бар if(IsNewBar()==true) { RecalcIndicators(); //--- Режим тестера/оптимизатора? if((MQLInfoInteger(MQL_TESTER)==true) || (MQLInfoInteger(MQL_OPTIMIZATION)==true)) { //--- Это уже период тестирования? if(cur_bar_time_dig[0]>begin_of_test) { //--- Если есть открытая по символу позиция if(PositionSelect(symbol)==true) //--- проверяем, не нужно-ли передвинуть SL, и если нужно - передвигаем TrailCondition(); //--- а если позиций нет else //--- проверяем, нужно-ли открыть позицию, и если нужно - открываем TradeCondition(); } } else { //--- Если есть открытая по символу позиция if(PositionSelect(symbol)==true) //--- проверяем, не нужно-ли передвинуть SL, и если нужно - передвигаем TrailCondition(); //--- а если позиций нет else //--- проверяем, нужно-ли открыть позицию, и если нужно - открываем TradeCondition(); } }; };
Для стратегии №1 "покупаем над линией поддержки стоп-ордером выше максимума предыдущей колонки "Х", продаем под линией сопротивления стоп-ордером ниже минимума предыдущей колонки "О", скользящий стоп - на уровне разворота":
//+------------------------------------------------------------------+ //| Функция проверки торговых условий для открытия сделки | //+------------------------------------------------------------------+ void TradeCondition() { if(order_col_number!=column_count) //--- Завалялись какие-то ордера по символу? { if(OrdersTotal()>0) { //--- Удалить их! for(int loc_count_1=0;loc_count_1<OrdersTotal();loc_count_1++) { ticket=OrderGetTicket(loc_count_1); if(!OrderSelect(ticket)) Print("Failed to select order #",ticket); if(OrderGetString(ORDER_SYMBOL)==symbol) { trade_request.order=ticket; trade_request.action=TRADE_ACTION_REMOVE; if(!OrderSend(trade_request,trade_result)) Print("Failed to send order #",trade_request.order); }; }; order_col_number=column_count; return; } else { order_col_number=column_count; return; } } else if((MathPow(10,pnf[column_count-1].resist_price)<SymbolInfoDouble(symbol,SYMBOL_ASK)) && (pnf[column_count-1].column_type=='X') && (pnf[column_count-1].max_column_price<=pnf[column_count-3].max_column_price)) { //--- Условия для BUY выполнены, смотрим, а нет ли отложенных ордеров BUY по символу с нужной ценой? trade_request.price=NormalizeDouble(MathPow(10,pnf[column_count-3].max_column_price+double_box),digit_2_orders); trade_request.sl=NormalizeDouble(MathPow(10,pnf[column_count-3].max_column_price-(reverse-1)*double_box),digit_2_orders); trade_request.type=ORDER_TYPE_BUY_STOP; if(OrderSelect(ticket)==false) //--- Нет, отложенных ордеров нет - размещаем ордер { PlaceOrder(); order_col_number=column_count; } else //--- или отложенный ордер есть { //--- а что за тип и цена у отложенного ордера? if((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_STOP) || ((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_BUY_STOP) && (OrderGetDouble(ORDER_PRICE_OPEN)!=trade_request.price))) { //--- Тип не тот или цена отличается - закрываем ордер trade_request.order=ticket; trade_request.action=TRADE_ACTION_REMOVE; if(!OrderSend(trade_request,trade_result)) Print("Failed to send order #",trade_request.order); //--- и открываем с нужной ценой PlaceOrder(); order_col_number=column_count; }; }; return; } else if((MathPow(10,pnf[column_count-1].resist_price)>SymbolInfoDouble(symbol,SYMBOL_ASK)) && (pnf[column_count-1].column_type=='O') && (pnf[column_count-1].min_column_price>=pnf[column_count-3].min_column_price)) { //--- Условия для SELL выполнены, смотрим, а нет ли отложенных ордеров SELL по символу с нужной ценой? trade_request.price=NormalizeDouble(MathPow(10,pnf[column_count-3].min_column_price-double_box),digit_2_orders); trade_request.sl=NormalizeDouble(MathPow(10,pnf[column_count-3].min_column_price+(reverse-1)*double_box),digit_2_orders); trade_request.type=ORDER_TYPE_SELL_STOP; if(OrderSelect(ticket)==false) //--- Нет, отложенных ордеров нет - размещаем ордер { PlaceOrder(); order_col_number=column_count; } else //--- или отложенный ордер есть { //--- а что за тип и цена у отложенного ордера? if((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_BUY_STOP) || ((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_STOP) && (OrderGetDouble(ORDER_PRICE_OPEN)!=trade_request.price))) { //--- Тип не тот или цена отличается - закрываем ордер trade_request.order=ticket; trade_request.action=TRADE_ACTION_REMOVE; if(!OrderSend(trade_request,trade_result)) Print("Failed to send order #",trade_request.order); //--- и открываем с нужной ценой PlaceOrder(); order_col_number=column_count; }; }; return; } else return; }; //+------------------------------------------------------------------+ //| Функция проверки условия для перемещения стоп-лосса | //+------------------------------------------------------------------+ void TrailCondition() { if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY) trade_request.sl=NormalizeDouble(MathPow(10,pnf[column_count-1].max_column_price-reverse*double_box),digit_2_orders); else trade_request.sl=NormalizeDouble(MathPow(10,pnf[column_count-1].min_column_price+reverse*double_box),digit_2_orders); if(PositionGetDouble(POSITION_SL)!=trade_request.sl) PlaceTrailOrder(); };
Для стратегии №2 "покупаем при пробое линии сопротивления, продаем при пробое линии поддержки, стоп-лосс - на уровне разворота, скользящий стоп - по трендовой линии":
//+------------------------------------------------------------------+ //| Функция проверки торговых условий для открытия сделки | //+------------------------------------------------------------------+ void TradeCondition() { if(order_col_number!=column_count) //--- Завалялись какие-то ордера по символу? { if(OrdersTotal()>0) { //--- Удалить их! for(int loc_count_1=0;loc_count_1<OrdersTotal();loc_count_1++) { ticket=OrderGetTicket(loc_count_1); if(!OrderSelect(ticket)) Print("Failed to select order #",ticket); if(OrderGetString(ORDER_SYMBOL)==symbol) { trade_request.order=ticket; trade_request.action=TRADE_ACTION_REMOVE; if(!OrderSend(trade_request,trade_result)) Print("Failed to send order #",trade_request.order); }; }; order_col_number=column_count; return; } else { order_col_number=column_count; return; } } else if(MathPow(10,pnf[column_count-1].resist_price)>SymbolInfoDouble(symbol,SYMBOL_ASK)) { //--- Условия для BUY выполнены, смотрим, а нет ли отложенных ордеров BUY по символу с нужной ценой? trade_request.price=NormalizeDouble(MathPow(10,pnf[column_count-1].resist_price),digit_2_orders); trade_request.sl=NormalizeDouble(MathPow(10,pnf[column_count-1].resist_price-(reverse-1)*double_box),digit_2_orders); trade_request.type=ORDER_TYPE_BUY_STOP; if(OrderSelect(ticket)==false) //--- Нет, отложенных ордеров нет - размещаем ордер { PlaceOrder(); order_col_number=column_count; } else //--- или отложенный ордер есть { //--- а что за тип и цена у отложенного ордера? if((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_STOP) || ((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_BUY_STOP) && (OrderGetDouble(ORDER_PRICE_OPEN)!=trade_request.price))) { //--- Тип не тот или цена отличается - закрываем ордер trade_request.order=ticket; trade_request.action=TRADE_ACTION_REMOVE; if(!OrderSend(trade_request,trade_result)) Print("Failed to send order #",trade_request.order); //--- и открываем с нужной ценой PlaceOrder(); order_col_number=column_count; }; }; return; } else if(MathPow(10,pnf[column_count-1].resist_price)<SymbolInfoDouble(symbol,SYMBOL_ASK)) { //--- Условия для SELL выполнены, смотрим, а нет ли отложенных ордеров SELL по символу с нужной ценой? trade_request.price=NormalizeDouble(MathPow(10,pnf[column_count-1].supp_price),digit_2_orders); trade_request.sl=NormalizeDouble(MathPow(10,pnf[column_count-1].supp_price+(reverse-1)*double_box),digit_2_orders); trade_request.type=ORDER_TYPE_SELL_STOP; if(OrderSelect(ticket)==false) //--- Нет, отложенных ордеров нет - размещаем ордер { PlaceOrder(); order_col_number=column_count; } else //--- или отложенный ордер есть { //--- а что за тип и цена у отложенного ордера? if((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_BUY_STOP) || ((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_STOP) && (OrderGetDouble(ORDER_PRICE_OPEN)!=trade_request.price))) { //--- Тип не тот или цена отличается - закрываем ордер trade_request.order=ticket; trade_request.action=TRADE_ACTION_REMOVE; if(!OrderSend(trade_request,trade_result)) Print("Failed to send order #",trade_request.order); //--- и открываем с нужной ценой PlaceOrder(); order_col_number=column_count; }; }; return; } else return; }; //+------------------------------------------------------------------+ //| Функция проверки условия для перемещения стоп-лосса | //+------------------------------------------------------------------+ void TrailCondition() { if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY) trade_request.sl=NormalizeDouble(MathMax(SymbolInfoDouble(symbol,SYMBOL_ASK),MathPow(10,pnf[column_count-1].max_column_price-reverse*double_box)),digit_2_orders); else trade_request.sl=NormalizeDouble(MathMin(SymbolInfoDouble(symbol,SYMBOL_BID),MathPow(10,pnf[column_count-1].min_column_price+reverse*double_box)),digit_2_orders); if(PositionGetDouble(POSITION_SL)!=trade_request.sl) PlaceTrailOrder(); };
Обратите внимание, уважаемый читатель, на несколько тонкостей.
- Цены на рыночные инструменты колеблются в довольно широком диапазоне от центов до десятков тысяч (пример - акции и контракты на разницу на японских биржах). Поэтому при построении графика крестиков-ноликов я использую логарифм цены, чтобы не вводить в качестве размеров бокса значения от 1 пипса до тысяч и десятков тысяч пипсов.
- Массив графика крестиков-ноликов начинает индексироваться с нуля, последняя колонка имеет индекс, соответственно, число колонок минус один.
- При наличии линии поддержки ее значение будет больше -10.0, но меньше логарифма цены, при отсутствии линии поддержки (и сопротивления тоже) ее значение в массиве графика составляет -10.0. Поэтому условия пробоя линии поддержки/сопротивления записаны в таком виде, как выше в коде стратегии №2.
Код вспомогательных функций не привожу в статье, а прилагаю во вложениях. Кодом построения самого графика ввиду его объемности также засорять статью не буду, он с комментариями во вложении.
Результаты роботрейдинга
Для оптимизации я подготовил два набора рыночных инструментов в файлах symbol_list_1.mhq и symbol_list_2.mhq: валютные пары и контракты на разницу по акциям, входящим в индекс Доу.
Окно настроек:
Окно параметров в тестере в первом случае для нашей стратегии имеет следующий вид:
Обратите внимание на строку "Начало тестирования". Для анализа и принятия решений роботу нужно хотя-бы несколько колонок графика, а при выборе размера бокса от 50 пипсов и выше, годичной истории зачастую не хватает даже для одной колонки. Поэтому для графиков с размером бокса от 50 пипсов я рекомендую задавать значение интервала порядка трех лет и выше от начала работы робота, а само начало работы робота задавать в окне параметров строкой "Начало тестирования". В нашем примере для тестирования по инструменту с размером бокса в 100 пипсов начиная с 01.01.2012 г. на вкладке "Настройки" указываем интервал с 01.01.2009 г., а на вкладке "Параметры" - с 01.01.2012 г.
Значение false для параметра "Торгуем минимальным лотом?" говорит о том, что размер лота у нас переменный, зависит от баланса и переменной "Риск на сделку, %" (в нашем случае 1% на сделку, но его можно также оптимизировать). "Допустимая просадка, %" - тот самый критерий оптимизации из функции OnTester(). Для примера я выбрал для оптимизации лишь две переменные: торговый инструмент "Символ" и "Размер бокса в пипсах".
Оптимизацию проведем на данных 2012-2013 года. Сам робот лучше всего прикрепить к графику EURUSD, как символу с наибольшим покрытием тиками. В таблице ниже приведу полный отчет для тестирования по валютным парам для размера бокса 10 по первой стратегии:
Pass | Result | Profit | Expected Payoff | Profit Factor | Recovery Factor | Sharpe Ratio | Custom | Equity DD % | Trades | selected_symbol | box |
---|---|---|---|---|---|---|---|---|---|---|---|
0,00 | 0,00 | -1 002,12 | -18,91 | 0,54 | -0,79 | -0,24 | 0,00 | 12,67 | 53,00 | AUDCAD | 10,00 |
1,00 | 886,56 | 886,56 | 14,53 | 1,40 | 1,52 | 0,13 | 886,56 | 5,76 | 61,00 | AUDCHF | 10,00 |
2,00 | 0,00 | -1 451,63 | -10,60 | 0,77 | -0,70 | -0,09 | 0,00 | 19,92 | 137,00 | AUDJPY | 10,00 |
3,00 | -647,66 | -647,66 | -17,50 | 0,57 | -0,68 | -0,24 | -647,66 | 9,46 | 37,00 | AUDNZD | 10,00 |
4,00 | -269,22 | -269,22 | -3,17 | 0,92 | -0,26 | -0,03 | -269,22 | 9,78 | 85,00 | AUDUSD | 10,00 |
5,00 | 0,00 | -811,44 | -13,52 | 0,72 | -0,64 | -0,14 | 0,00 | 12,20 | 60,00 | CADCHF | 10,00 |
6,00 | 0,00 | 1 686,34 | 16,53 | 1,36 | 1,17 | 0,12 | 0,00 | 11,78 | 102,00 | CHFJPY | 10,00 |
7,00 | 356,68 | 356,68 | 5,66 | 1,13 | 0,40 | 0,06 | 356,68 | 8,04 | 63,00 | EURAUD | 10,00 |
8,00 | 0,00 | -1 437,91 | -25,68 | 0,53 | -0,92 | -0,25 | 0,00 | 15,47 | 56,00 | EURCAD | 10,00 |
9,00 | 0,00 | -886,66 | -46,67 | 0,34 | -0,74 | -0,46 | 0,00 | 11,56 | 19,00 | EURCHF | 10,00 |
10,00 | 0,00 | -789,59 | -21,93 | 0,54 | -0,75 | -0,26 | 0,00 | 10,34 | 36,00 | EURGBP | 10,00 |
11,00 | 0,00 | 3 074,86 | 28,47 | 1,62 | 1,72 | 0,20 | 0,00 | 12,67 | 108,00 | EURJPY | 10,00 |
12,00 | 0,00 | -1 621,85 | -19,78 | 0,55 | -0,97 | -0,25 | 0,00 | 16,75 | 82,00 | EURNZD | 10,00 |
13,00 | 152,73 | 152,73 | 2,88 | 1,07 | 0,21 | 0,03 | 152,73 | 6,90 | 53,00 | EURUSD | 10,00 |
14,00 | 0,00 | -1 058,85 | -14,50 | 0,65 | -0,66 | -0,16 | 0,00 | 15,87 | 73,00 | GBPAUD | 10,00 |
15,00 | 0,00 | -1 343,47 | -25,35 | 0,43 | -0,64 | -0,34 | 0,00 | 20,90 | 53,00 | GBPCAD | 10,00 |
16,00 | 0,00 | -2 607,22 | -44,19 | 0,27 | -0,95 | -0,59 | 0,00 | 27,15 | 59,00 | GBPCHF | 10,00 |
17,00 | 0,00 | 1 160,54 | 11,72 | 1,27 | 0,81 | 0,10 | 0,00 | 12,30 | 99,00 | GBPJPY | 10,00 |
18,00 | 0,00 | -1 249,91 | -14,70 | 0,69 | -0,85 | -0,15 | 0,00 | 14,41 | 85,00 | GBPNZD | 10,00 |
19,00 | 208,94 | 208,94 | 5,36 | 1,12 | 0,25 | 0,05 | 208,94 | 7,81 | 39,00 | GBPUSD | 10,00 |
20,00 | 0,00 | -2 137,68 | -21,17 | 0,53 | -0,79 | -0,24 | 0,00 | 25,62 | 101,00 | NZDUSD | 10,00 |
21,00 | 0,00 | -1 766,80 | -38,41 | 0,30 | -0,97 | -0,53 | 0,00 | 18,10 | 46,00 | USDCAD | 10,00 |
22,00 | -824,69 | -824,69 | -11,95 | 0,73 | -0,90 | -0,13 | -824,69 | 9,11 | 69,00 | USDCHF | 10,00 |
23,00 | 2 166,53 | 2 166,53 | 26,10 | 1,58 | 2,40 | 0,18 | 2 166,53 | 7,13 | 83,00 | USDJPY | 10,00 |
2 029,87 | -10 213,52 | 13,40 | 1 659,00 |
и сводную таблицу для разных инструментов и размеров бокса:
Стратегия | Инструменты | Размер бокса | Trades | Equity DD % | Profit | Result | Прогноз баланса |
---|---|---|---|---|---|---|---|
1 | Валюты | 10 | 1 659 | 13 | -10 214 | 2 030 | 2 030 |
1 | Валюты | 20 | 400 | 5 | 1 638 | 2 484 | 2 484 |
1 | Акции | 50 | 350 | 4 | 7 599 | 7 599 | 15 199 |
1 | Акции | 100 | 81 | 2 | 4 415 | 4 415 | 17 659 |
2 | Валюты | 10 | 338 | 20 | -4 055 | 138 | 138 |
2 | Валюты | 20 | 116 | 8 | 4 687 | 3 986 | 3 986 |
2 | Акции | 50 | 65 | 6 | 6 770 | 9 244 | 9 244 |
2 | Акции | 100 | 12 | 1 | -332 | -332 | -5 315 |
Что мы видим?
Существуют круглые дураки, которые все и всегда делают неверно.
И существуют дураки с Уолл-Стрит, которые считают, что торговать надо всегда.
На свете нет человека, который бы ежедневно имел нужную информацию, чтобы покупать или продавать акции и вести свою игру достаточно разумно.
Видим неожиданную для многих картину: депозит с большей вероятностью будет выше при меньшем числе сделок. Если бы мы без всякой оптимизации два года назад накинули на акции наш эксперт и задали размер бокса 100, а риск на сделку 1%, то за два года робот совершил бы всего 81 сделку (в среднем за год по одному инструменту 1,25 сделки), наш депо вырос бы на 44%, и при этом в среднем просадка по эквити была бы чуть выше 2%. Принимая для себя допустимую просадку в 10%, мы бы могли рисковать на сделку в 4% и за два года депозит бы прибавил 177%, доходность - под 90% годовых в долларах США!
Эпилог
Курс никогда не бывает слишком высоким, чтобы начать покупать, и никогда не бывает слишком низким, чтобы начать продавать.
Большие деньги делаются не в раздумьях, а в ожидании.
Предложенные к рассмотрению стратегии могут быть модифицированы и они покажут даже большую доходность при просадке не выше 10%. Не пытайтесь мельтешить, торговать часто, лучше найдите брокера, который предоставляет не просто "стандартный набор" инструментов из двух десятков валютных пар и трех десятков акций, а хотя бы сотни три-четыре инструментов (акций, фьючерсов). С большей вероятностью инструменты не будут коррелированы и ваш депозит будет в большей безопасности. И да, акции почему-то показывают лучшие результаты, чем валютные пары.
P.S. (на правах рекламы)
В Маркете я предлагаю скрипт PnF Chartist для построения графиков крестиков-ноликов в текстовых файлах из котировок терминалов МТ4, МТ5 или Yahoo finance. Используйте его для визуального поиска паттернов поведения цен, ибо нет лучше тестера/оптимизатора, чем своя голова, а найдя закономерности - воспользуйтесь шаблонами экспертов из статьи для проверки в боевых условиях ваших задумок.
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Переработанный для МТ4 эксперт.
Добрый день. В режиме тестирования все роботы вылетают с ошибками. Это касается как mt4 так и mt5
zero divide in 'strategy_two.mq5' (348,99)
или
array out of range in 'strategy_one.mq5' (438,32)
Никак не могу понять в чем причина, подскажите плз