Вещий сон алготрейдера - 2: Явь.

10 июня 2026, 13:00
fxsaber
0
36

Продолжение ранее написанного.


После просыпания появилась мотивация создать что-то из увиденного во сне (что из этого вышло - смотрим ниже).


Перебор.

Там шло создание Облаков наборов хороших параметров через алгоритмы эвристического перебора. Но в реальности на такие манипуляции уходят огромные вычислительные ресурсы, а главное - время. Много времени.


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


BestInterval.

Когда-то была написана библиотека, которая могла за линейное время (очень быстро) найти на любой истории торговли хороший суточный интервал торговли. Делалось это через автоматическое удаление плохих суточных периодов торговли.


Длительное использование такого инструмента выявило серьезные изъяны такого подхода. Поэтому потребовалась модификация.



Нужно было научиться находить много хороших интервалов торговли. Были введены следующие опции.

  • MinPF - создаем набор торговых интервалов с заданным (не ниже) ПрофитФактором. 
  • OnlyDelete - либо только выбрасываем плохие интервалы (как раньше), либо же допускаем нахождение новых хороших. В первом случае суммарная длительность только уменьшается, во втором - может и увеличиваться.
  • Method - работаем внутри суток (как раньше) или недели.
  • Interval MinPF - отказываемся от всех найденных подынтервалов, которые не удовлетворяют показателю MinPF.
  • Interval MinTotalотказываемся от всех найденных подынтервалов, у которых мало сделок.

Сделки.

  • MinDuration - отказываемся от анализа короткоживущих позиций. Например, белые лебеди сильно все искажают.
  • Penalty - вводим систему штрафов/премирования на результат сделки, в зависимости от ее длительности.
  DEAL_BASE Deal = {100, 0, 0, 0, 10 * 3600}; // Profit = 100, Lenght = 10.
  
// Превышение порога.    
    // Штраф.
//  Deal.Penalty(+12, -13); // 100 - порог штрафа на два часа выше, поэтому ничего не меняется.
//  Deal.Penalty(+9, -13);  //  87 - порог штрафа на час ниже, поэтому один час ШТРАФА.

    // Премирование.
//  Deal.Penalty(-12, +13); // 100 - порог премирования на два часа выше, поэтому ничего не меняется.
//  Deal.Penalty(-9, +13);  // 113 - порог премирования на час ниже, поэтому один час ПРЕМИРОВАНИЯ.

// Недотягивание до порога.
    // Штраф.
//  Deal.Penalty(+12, +13); //  74 - длительность не дотягивает два часа до порога - ШТРАФ за каждый недотянутый час.
//  Deal.Penalty(+9, +13);  // 100 - длительность превышает порог - штрафа нет.

    // Премирование.
//  Deal.Penalty(-12, -13); // 126 - длительность не дотягивает два часа до порога - ПРЕМИРОВАНИЕ за каждый недотянутый час.
//  Deal.Penalty(-9, -13);  // 100 - длительность превышает порог - премирования нет.

  
  Print(Deal.ToString());

Например, хотим отдавать предпочтение более коротким по времени сделкам. Тогда можно на результат (прибыль) длительных сделок накладывать штраф, в зависимости от длительности. Это позволяет повысить приоритет короткоживущих сделок при нахождении хороших интервалов торговли.


Пересечение.

И вот дошли до пункта, на котором проснулись. Желательно осознанно его еще раз прочесть.

Но если есть постоянные закономерности, то они есть в каждом из 20-ти годов. Тогда вы решаете оптимизировать ТС каждый год отдельно. А затем полученные 20 облаков хороших наборов входных параметров пересечь, найдя что-то общее.

Грубо говоря, на каждом "году" делаем BestInterval, а затем их все пересекаем. За это отвечают следующие параметры.

  • Amount - количество BestInterval для последующего пересечения.
  • Length - какую часть (0.0 - 1.0) от общей истории торговли использовать под каждый из этих BestInterval.

Amount = 1, Length = 1.0.

Пробуем без этого механизма валидации.

Amount of Delete Intervals = 5 (2025.01.02 - 2026.05.31) BESTINTERVAL_METHOD_WEEK 
0 00:00:00 - 1 01:10:40 : Profit = 31826.00 (25.12%), Total = 176 (72.16%), PF = 1.79, Mean = 180.83, DD = 9582.00, RF = 3.32, MDL = 3.05, Length = 25.18 hours, Efficiency = 1264.04
3 17:51:10 - 3 18:04:22 : Profit = 23115.00 (18.24%), Total = 163 (74.85%), PF = 2.70, Mean = 141.81, DD = 2628.00, RF = 8.80, MDL = 0.96, Length = 0.22 hours, Efficiency = 104935.69
3 19:18:24 - 3 19:54:48 : Profit = 29413.00 (23.22%), Total = 272 (71.32%), PF = 2.18, Mean = 108.14, DD = 2880.00, RF = 10.21, MDL = 1.81, Length = 0.61 hours, Efficiency = 48460.78
5 16:41:09 - 5 16:46:07 : Profit = 16502.00 (13.02%), Total = 79 (75.95%), PF = 3.18, Mean = 208.89, DD = 1997.00, RF = 8.26, MDL = 0.35, Length = 0.08 hours, Efficiency = 198686.29
5 17:29:14 - 5 17:46:01 : Profit = 25842.00 (20.40%), Total = 250 (71.20%), PF = 1.83, Mean = 103.37, DD = 3590.00, RF = 7.20, MDL = 1.20, Length = 0.28 hours, Efficiency = 92292.86
SUMMARY: 0 00:00:00 - 6 23:59:59 : Profit = 126698.00 (100.00%), Total = 940 (69.57%), PF = 2.08, Mean = 134.79, DD = 8612.00, RF = 14.71, MDL = 3.05, MDLF = 8.30 hours

Четко показано, когда надо было торговать. Красота!


Amount = 5, Length = 0.9.

Пересекаем пять облаков,  каждое из которых занимает 90% торгового времени

01/5. 0.00-0.90: 2025.01.01 - 2026.04.09, 1 Year 3 Months 7 Days, 6 intervals.
02/5. 0.02-0.93: 2025.01.13 - 2026.04.22, 1 Year 3 Months 8 Days, 5 intervals.
03/5. 0.05-0.95: 2025.01.26 - 2026.05.05, 1 Year 3 Months 9 Days, 5 intervals.
04/5. 0.07-0.98: 2025.02.08 - 2026.05.18, 1 Year 3 Months 9 Days, 4 intervals.
05/5. 0.10-1.00: 2025.02.21 - 2026.05.30, 1 Year 3 Months 8 Days, 4 intervals.
Amount of Delete Intervals = 3 (2025.01.01 - 2026.05.30) BESTINTERVAL_METHOD_WEEK 
0 00:00:00 - 1 01:00:19 : Profit = 21957.00 (33.95%), Total = 43 (81.40%), PF = 4.88, Mean = 510.63, DD = 1883.00, RF = 11.66, MDL = 3.05, Length = 25.01 hours, Efficiency = 878.08
3 17:54:12 - 3 18:04:22 : Profit = 20894.00 (32.30%), Total = 129 (72.87%), PF = 3.11, Mean = 161.97, DD = 1788.00, RF = 11.69, MDL = 0.96, Length = 0.17 hours, Efficiency = 123107.04
3 19:32:40 - 3 19:54:48 : Profit = 21828.00 (33.75%), Total = 152 (72.37%), PF = 3.07, Mean = 143.61, DD = 2067.00, RF = 10.56, MDL = 1.81, Length = 0.37 hours, Efficiency = 59127.77
5 23:56:50 - 6 23:59:59 : Profit = 0.00 (0.00%), Total = 0, PF = Max, Mean = 0.00, MDL = 0.00, Length = 24.05 hours, Efficiency = 0.00
SUMMARY: 0 00:00:00 - 6 23:59:59 : Profit = 64679.00 (100.00%), Total = 324 (73.77%), PF = 3.48, Mean = 199.63, DD = 2113.00, RF = 30.61, MDL = 3.05, MDLF = 8.30 hours

И тут видим значительное усечение времени торговли.


Amount = 5, Length = 0.5.

01/5. 0.00-0.50: 2025.01.01 - 2025.09.15, 8 Months 13 Days, 6 intervals.
02/5. 0.13-0.63: 2025.03.06 - 2025.11.19, 8 Months 12 Days, 5 intervals.
03/5. 0.25-0.75: 2025.05.09 - 2026.01.22, 8 Months 12 Days, 3 intervals.
04/5. 0.38-0.88: 2025.07.12 - 2026.03.27, 8 Months 14 Days, 0 intervals.

При 50% получили пустое пересечение облаков. Т.е. входные параметры ТС можно все же выкидывать.


Итак, получен валидационный механизм пересечения для каждого прохода. Что дальше?


Конечно, оптимизировать с включенным необычным механизмом. Но хочется еще более необычного.


Критерий оптимизации.

Этот критерий нужен, чтобы можно было как-то сравнивать между собой разные проходы ТС. 


99% критериев оптимизации в мире - это функция от одного источника данных: история торговли. Туда могут входит и Equity и MFE/MAE и многое другое. Но это все производные истории торговли.


Казалось бы, что больше ничего и не добавить. Однако, вспоминаем эту мысль.

И возникает мысль увеличить вероятность пересечений. Для этого вы собираетесь использовать ТС, которая постоянно имеет одну открытую позицию - переворотная.

И понимаем, что есть сырая история торговли - нет фильтра. Да, торгуете только через фильтр. Зачем может быть нужна сырая история торгвли?!


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


Снова цитата.

А что, если будущие 20 лет - случайность? Или там есть какие-то закономерности? Если есть, то хотелось бы почаще на них нарываться, чтобы спокойнее было.

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


Но что это может быть? 


Рассуждения.

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


Представим, что алгоритм ТС в случайность все же попал - в этой случайности открыта позиция. Как из нее выбраться? Каким свойством должен обладать алгоритм, чтобы выбираться из плохих ситуаций?


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


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


Вот это и будем использовать: в сырой нефильтрованной истории торговли хорошо бы посмотреть на самую длительную по времени жизни позицию. Ее результат не важен, важна сама длительность - MaxRawDealLength.


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


А вот с настройками ему можно помочь через критерий оптимизации.


Редчайший критерий.

Какой бы ни был критерий, его надо модифицировать с учетом мысли выше. А это значит, что критерий нужно делить на монотонно возрастающую функцию от MaxRawDealLength.


В BestInterval встроен такой фукнционал. Инструмент сам вычисляет MaxRawDealLength, за вычетом выходных по правилам форекс - суббота/воскресенье, католические Рождество/Страстная пятница и Новый год. А в качестве функции берется степенная: MaxRawDealLength^PowMaxLength

  • PowMaxLength - на какую степень MaxRawDealLength делить критерий оптимизации. Нулевая степень - единица (нет деления - классика).


Задумайтесь над необычностью предложенного. Мы для критерия используем нечто, чего нет в торговле!


Мера случайности исторических котировок.

Да, во сне было про это. Хочется спать.