Ошибки при построении торговых систем

Привлекла внимание статья написанная Максимом Миловановым на робострое. Согласен со всеми пунктами, которые описал автор:

При проектировании торговых систем очень важно не только создать рабочую стратегию, приносящую прибыль, но и избежать ошибок в коде, потому что именно эти ошибки могут привести к так называемой «граальной» ловушке.

1 ловушка – подглядывание в будущее при входе в позицию

Впервые с такой ловушкой я столкнулся при разработке трендовой системы на основе индикаторов ADX+CCI. Найти эту ошибку мне помог Игорь Чечет, за что ему большое спасибо.

Кратко рассмотрим данную торговую систему.

Вход в лонг осуществляется при росте индикатора ADX, который показывает наличие тренда, и пересечении индикатором CCI значения +100, который показывает момент входа в позицию. В шорт соответственно наоборот.

Приведу часть кода, которая была изначально:

//Сравниваем значение ADX на текущем баре и предыдущем
if (ADX.Series(Bars,ADXperiod)[bar] > ADX.Series(Bars,ADXperiod)[bar — 1]) {
	if (CCI.Series(Bars,CCIperiod)[bar] > 100) { 	//Открытие позиции в Long
		BuyAtMarket(bar);
	}
	else if (CCI.Series(Bars,CCIperiod)[bar] < -100)		//Открытие позиции в Short
	{
		ShortAtMarket(bar);
	}
}

В результате выполнения данного кода получилась очень красивая кривая доходности:

Рис 1. Кривая доходности для системы с ошибкой в коде при входе в позицию

Ошибка здесь заключается в следующем. Вход в позицию осуществляется по рыночной цене на свече bar — BuyAtMarket(bar). Но если посмотреть правило, по которому осуществляется этот вход то становиться очевидно, что допущена ошибка – так называемое подглядывание в будущее. А именно, сравнивается значение индикатора ADX на текущей свече и предыдущей:

if (ADX.Series(Bars,ADXperiod)[bar] > ADX.Series(Bars,ADXperiod)[bar — 1])

Но дело в том что пока свеча не закрылась, мы не права имеем обращаться к значению индикатора на текущей свече. Соответственно, не имеем права входить в позицию на текущей свече. Таким образом, необходимо входить строго на следующей свече, по правилу BuyAtMarket(bar+1). В результате получим следующую кривую доходности:

Рис 2. Кривая доходности после исправления ошибки в коде

 

2 ловушка – подглядывание в будущее при выходе из позиции

Подглядывание в будущее, на мой взгляд, одна из самых часто встречающихся ошибок, и при недостаточном анализе кода после построения торговой системы, увидеть ошибку бывает довольно тяжело. Так случилось с моей недавней системой на основе пробоя максимума/минимума за N свечей. Ситуация входа в позицию ошибки не содержала, но код выходы из позиции как раз подглядывал в будущее:

Рис 3. Кривая доходности для системы с ошибкой в коде при выходе из позиции

Изначально я думал, что ошибка в коде у меня всё же есть, но найти у меня её не получалось.

Скептически относясь к таким сумасшедшим результатам, я обратился за помощью к Дмитрию Власову, который указал на мою ошибку в коде. Дословная цитата:

«Все дело в том, что в условии на выход ты смотришь — закрылась ли свеча ниже стоп-лосса на текущей свече ( if (Close[bar] < stopLoss) )и если это так, то говоришь программе, чтобы она продавала на открытии этой же свечи… (SellAtMarket(bar, LastActivePosition);)

В реальной жизни так быть не может… Если ты смотришь на закрытие текущей свечи ( if (Close[bar] < stopLoss) ), то закрыть позицию сможешь только на открытии следующей (SellAtMarket(bar + 1, LastActivePosition);)… 

Вот отстутствие этого +1 (которое указывает на то, что закрывать позицию нужно на следующей свече (а не на открытии текущей, зная ее закрытие) и дает такие хорошие результаты… »

Действительно, система подглядывает в будущее, но не на моменте входа в позицию, а на моменте выхода.

Соответственно эквити меняется совершенно в противоположную сторону:

Рис 4. Кривая доходности после исправления ошибки в коде

3 ловушка – синтаксическая ошибка в коде

Программирую не только торговые системы очень часто встречаются ошибки синтаксиса в коде. Так, например в Си-подобных языках программирования (C++, C#, Java), есть два оператора «=» (равно) и «==» (сравнение), которые выполняют совершенно разные функции. Оператор «=» (равно) используется для присвоения переменной некоторого значения, а  оператор «==» (сравнение) сравнивает две переменные.

Приведу пример кода с ошибкой:

if ( Close[bar] = Close[bar-1]  )

{

}

Такой код, безусловно, выполнится, и в результате в массиве Close, элементу с номером bar (т.е. Close[bar]) будет присвоено значение элемента c номеров bar-1. Поэтому для исправления этой ошибки нужно использовать оператор сравнение «==».

Эта ошибка довольно часто встречается, когда разработчик может просто опечататься.

4 ловушка – ошибка типов данных в коде

Довольно очевидная, на первый взгляд ошибка, но не всегда заметная при тестировании торговой стратегии, ошибка вычисления при использовании определенного типа данных.

Например, пусть в системе есть такой параметр как стоп-лосс, который составляет 0,3% риска на сделку и рассчитывается по формуле:

double stopLoss = price / 100 * 97;

Однако в данной строчке допущена ошибка, а именно результат деления — price / 100 – даст целое положительное число, а нам требуется вещественное (т.е. дробное), поэтому правильным вариантом будет использование оператора приведения типов:

double stopLoss = (double) price / 100 * 97;

5 ловушка – открытие/закрытие позиции на первой внутридневной свече

Часто при проектировании торговых систем вход осуществляется внутри дня, а позиция удерживается в течение нескольких дней, пока не сработает правило для выхода из позиции или стоп-лосс. Как мы все знаем, рынок, в большинстве случаев открывается гепом в одну или другую сторону, соответственно на первой свече (допустим минутной) происходим очень большое количество заявок, в том числе может сработать и стоп-заявка торговой системы. Однако, при тестировании торговой системы программы подобные Wealth Lab не учитывают скорость движения цены на такой свече, и вполне вероятно, что тестер стратегии закроют позицию по стоп-лосу именно по цене стоп-приказа. Понятно, что это будет не совсем верно, т.к. большое проскальзывание в реале не даст закрыть позицию.

 

6 ловушка – порядок выполнения приказов в торговой системе

Для пессимистичного сценария результатов торговой системы, рекомендуется выполнять код обработки сигнала выхода из позиции перед приказом входа

//Если позиция открыта

if (IsLastPositionActive)

{

//Правила для выхода

}

else        //Если позиция закрыта

{

//Правила для входа

}

Причем если используются одновременно в системе сигналы выхода из позиции по стоп-лосу и тейк-профиту, необходимо исполнять стоп-лосс именно перед тейк-профитом, т.к. данные сигналы могут возникнуть на одной свече.

7 ловушка – проскальзывание и комиссия

Перейдем к типичным ошибкам, которые из прибыльной торговой системы могут сделать убыточную. Это ситуации, в которых разработчик не учитывает проскальзывание и комиссию.

Рассмотрим следующую торговую систему.

Если цена пробивает максимум внутри дня, то входим в лонг, если минимум то сходим в шорт. Т.е. простая стратегия пробоя внутридневного экстремума. Приведу результат тестирования системы без учета комиссии и проскальзывания на фьючерсе на индекс РТС:

Рис 5. Кривая доходности системы без учета комиссии и проскальзывания

Теперь учтем в системе проскальзывание в 50 пунктов и комиссию в размере двух рублей.

Рис 6. Кривая доходности системы с учетом комиссии и проскальзывания

Как видно из графиков доходностей, проскальзывание и комиссии делают систему низкоприбыльной.

8 ловушка – некорректные данные для анализа

Одной из причин неудовлетворительного тестирования торговой системы, могут служить некорректные данные по какому-либо инструменту для тестирования. Это могут быть либо неверные свечи, либо аномально длинные тени свечей, которые в рабочей стратегии могут давать убыточные результаты при срабатывании стоп-лоссов. Например, на изображении видны минутные свечи 28.05.2008, 29.05.2008, 30.05.2008, 02.06.2008, 03.06.2008 после 18.00 инструмента RTS-6.08 импортированные с сайта известного российского брокера. И это на самом деле неверные данные, а не сумасшедший торговый робот.

Рис 7. Некорректные данные для анализа

9 ловушка – переоптимизация торговой системы

Приведу в пример систему, которую я разрабатывал одной из первых. В системе использовались такие индикаторы как CCI, ADX и набор SMA. В результате получилась система с очень большим количеством параметров. Путем оптимизации я добился прибыльной торговой системы на истории, однако прибыль в будущем данной системы, почти гарантированно будет отрицательной.

Рис 8. Стратегия на основе индикаторов CCI, ADX и SMA

Очевидно, для того чтобы система работала стабильно, количество оптимизируемых параметров в ней должно быть сведено к минимуму (по разным данным советуется не более 2 параметров).

В качестве вывода данной статьи, я хотел бы сказать, что ошибки в коде при построении торговых систем, могут появляться даже у опытных программистов и разработчиков, поэтому очень хорошо, если в вашем круге окружения есть люди, которые могут посмотреть на вашу систему со стороны. Не бойтесь рассказывать о своих ошибках и неудачах, ведь, как говорится, на ошибках учатся.

Похожие статьи:

Буду признателен, если поделитесь этой записью с друзьми:


Понравилась статья ? Подпишитесь на обновления:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *



Denoy.ru