preview
Арбитражный трейдинг Forex: Простой бот-маркетмейкер синтетиков для старта

Арбитражный трейдинг Forex: Простой бот-маркетмейкер синтетиков для старта

MetaTrader 5Торговые системы | 20 марта 2025, 09:53
725 1
Yevgeniy Koshtenko
Yevgeniy Koshtenko

Введение: от розничного трейдера к институциональному мышлению

Меня часто спрашивают, как я пришел к разработке TrisWeb_Optimized. История начинается в 2016 году, когда я впервые окунулся в мир Forex, с наивностью новичка и верой в индикаторный трейдинг. В то время я еще верил в "святой Грааль" торговли — волшебную комбинацию индикаторов, которая якобы превратит $1000 в миллион за год.

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

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

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

В январе 2020 года, накануне одного из самых турбулентных периодов в истории финансовых рынков, родился Tris_Optimized — мой ответ на вопрос: "Как розничному трейдеру применять институциональные стратегии?"

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

За пять лет непрерывной работы в реальных рыночных условиях Tris_Optimized доказал свою жизнеспособность. Он пережил пандемию, инфляционные скачки, изменения процентных ставок и геополитические кризисы — и продолжает приносить стабильную прибыль (в те редкие периоды, когда я все же торгую, а не сижу целые ночи за идеями кодов и непосредственно кодами). Это не чудо-система, обещающая заоблачные доходы, а надежный рабочий инструмент, основанный на фундаментальных принципах институционального трейдинга.

Сегодня эта база институционального мышления позволяет мне успешно торговать на различных рынках. Хотя в последние годы я сосредоточился на стратегиях с использованием машинного обучения, арбитражные подходы всегда остаются важной частью моего торгового арсенала. Сочетание статистических методов ML с фундаментальными рыночными закономерностями, которые используют институциональные трейдеры, даёт мне серьезное преимущество. Меня даже пригласили в маленький и никому не известный хэдж-фонд, хотя это громко сказано: в фонде кроме меня еще только риск-менеджер и управляющая, и мы только готовимся развернуть инфраструктуру на MetaTrader 5 для хэдж-фондов.

Пару недель назад я занимался ревизией своего облачного хранилища и наткнулся на архив с моими старыми торговыми роботами. Среди них был и Triss_Optimized — один из моих первых проектов, который, несмотря на свою "простоту" по сравнению с современными ML-системами, продолжает работать и приносить результаты. Я запустил его на тестовом счете, и был удивлен — даже без оптимизации под текущие рыночные условия он показал положительный результат.

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

В этой статье я раскрою механику работы TrisWeb_Optimized, объясню его архитектуру и поделюсь своим опытом настройки и оптимизации. Готовы погрузиться в мир профессионального алготрейдинга? Тогда начнем.


Трехвалютный арбитраж на Forex: принцип треугольника EURUSD-GBPUSD-EURJPY

Давайте начнем с того, что треугольный арбитраж в мире Forex — это настоящая охота за призраками. Представьте себе ситуацию: вы одновременно следите за EURUSD, GBPUSD и EURJPY, пытаясь поймать мимолетный дисбаланс цен — когда замкнутый контур EUR→USD→GBP→EUR даст вам хотя бы пару пипсов чистой прибыли. Но правда в том, что чистые арбитражные возможности так же редки, как единороги в центре Москвы. Институционалы с их высокочастотными алгоритмами и прямым доступом к ликвидности съедают эти возможности быстрее, чем вы успеваете моргнуть.

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


Синтетические валютные пары в MQL5: тайная дверь к арбитражу

Что такое синтетическая валютная пара? Это призрак, математическая абстракция, которую мы конструируем из реально торгуемых инструментов. Например, имея котировки EURUSD и GBPUSD, мы можем рассчитать синтетический EURGBP — и в теории он должен в точности соответствовать реальному EURGBP. Но в реальном мире всегда есть расхождения, и они — наш хлеб насущный.

TrisWeb_Optimized не создает синтетики явно, но играет на их невидимых струнах. Вот фрагмент кода, в котором мы получаем текущие цены для каждого символа — первый шаг к охоте за дисбалансами:

// Получаем текущие цены для каждого символа
double priceSymbol1 = GetCurrentPrice(Symbol1);
double priceSymbol2 = GetCurrentPrice(Symbol2);
double priceSymbol3 = GetCurrentPrice(Symbol3);

Эти три строчки — точка входа в многомерное пространство возможностей, где малейшие расхождения превращаются в реальные деньги.


Архитектура TrisWeb_Optimized: машина, которая пережила пандемию

Когда я писал первые строки TrisWeb_Optimized в январе 2020 года, мир стоял на пороге пандемии, рыночных потрясений и безумной волатильности. Никто не мог предсказать, что этому простому советнику предстоит пройти боевое крещение в одних из самых экстремальных рыночных условий за последние десятилетия. И знаете что? Он выжил. Не просто выжил — он процветал.

Сердце системы бьется в функции OnTick() — она срабатывает при каждом изменении цены, анализируя всю картину по трем фронтам одновременно:

void OnTick()
{
   // Подсчет ордеров и прибыли
   int ordersCount1 = 0, ordersCount2 = 0, ordersCount3 = 0;
   double profit1 = 0, profit2 = 0, profit3 = 0;
   
   // Подсчет отложенных ордеров
   CountPendingOrders(ordersCount1, ordersCount2, ordersCount3);
   
   // Подсчет открытых позиций и их профита
   CountOpenPositions(ordersCount1, ordersCount2, ordersCount3, profit1, profit2, profit3);
   
   // ... остальной код
}

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


Настройка параметров сетки: танец с волатильностью

Рынок Forex напоминает океан — EURUSD течет медленно и величественно, как глубинные течения, в то время как EURJPY может взорваться внезапным цунами. Попытка торговать эти пары с одинаковыми параметрами — верный путь к разочарованию. Именно поэтому TrisWeb_Optimized позволяет настроить индивидуальные параметры сетки для каждого инструмента:

input group "Step Settings"
input int Step01_Symbol1 = 40;      // Начальный шаг для Symbol1 (в пунктах)
input int Step02_Symbol1 = 60;      // Шаг между ордерами для Symbol1 (в пунктах)
input int Step01_Symbol2 = 40;      // Начальный шаг для Symbol2 (в пунктах)
input int Step02_Symbol2 = 60;      // Шаг между ордерами для Symbol2 (в пунктах)
input int Step01_Symbol3 = 40;      // Начальный шаг для Symbol3 (в пунктах)
input int Step02_Symbol3 = 60;      // Шаг между ордерами для Symbol3 (в пунктах)

За пять лет работы с этой системой я выработал своеобразный ритуал: для спокойных пар типа EURUSD использую стандартные шаги в 40 и 60 пунктов, а для огненных JPY-кроссов увеличиваю их до 50 и 80. Это как настраивать чувствительность удочки под разные виды рыбы — с одинаковым подходом поймаешь либо все, либо ничего.


Оптимизация размера лотов: адаптивная ДНК системы

Фиксированный размер лота — это путь новичка. Профессионал знает: объем должен дышать вместе с балансом счета. TrisWeb_Optimized умеет это делать благодаря функции CalculateOptimalLotSize() , которую я считаю своим маленьким шедевром:

double CalculateOptimalLotSize(string symbol, double baseSize)
{
   if(!AutoLotOptimization)
      return baseSize;
      
   double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE);
   double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
   double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
   // ... остальной код расчета
}
Эта функция — настоящий швейцарский нож. Она учитывает текущий баланс, заданный процент риска, стоимость пункта для конкретного символа, ограничения брокера по минимальному размеру и шагу лота, а также — и это моя особая гордость — автоматически корректирует расчеты для экзотических валют:
// Корректировка для пар с JPY и других экзотических валют
string quoteCurrency = StringSubstr(symbol, 3, 3);
if(quoteCurrency == "JPY" || quoteCurrency == "XAU" || quoteCurrency == "XAG")
   pointCost *= 100.0;

Для непосвященных: стоимость пункта в парах с японской иеной в 100 раз меньше, чем в основных парах. Забудьте об этой корректировке — и ваш робот либо будет торговать микроскопическими объемами, либо разорит вас за одну сессию.


Управление временем работы советника: алгоритм с циркадным ритмом

Рынок Forex работает круглосуточно, но это не значит, что вашему советнику стоит делать то же самое. Есть периоды, когда лучше отойти в сторону — ночная сессия с ее широкими спредами и неестественными движениями часто приносит больше проблем, чем возможностей. TrisWeb_Optimized имеет встроенный "циркадный ритм", благодаря функции IsWorkTime() :

bool IsWorkTime()
{
   MqlDateTime currentTime;
   TimeToStruct(TimeLocal(), currentTime);
   
   datetime currentTimeSeconds = HoursMinutesToSeconds(currentTime.hour, currentTime.min);
   datetime startTimeSeconds = HoursMinutesToSeconds(StartTimeHour, StartTimeMin);
   datetime endTimeSeconds = HoursMinutesToSeconds(EndTimeHour, EndTimeMin);
   
   return (startTimeSeconds <= currentTimeSeconds && currentTimeSeconds <= endTimeSeconds);
}
А функция CheckNewDayAndWorkTime() добавляет советнику еще одну человеческую черту — способность "начать с чистого листа" каждый новый день:
void CheckNewDayAndWorkTime()
{
   if(IsNewDay && IsNewDayReset && IsWorkTime())
   {
      DeleteAllOrders();
      IsNewDay = false;
   }
   
   if(!IsWorkTime())
   {
      IsNewDay = true;
   }
}

Это особенно ценно в долгосрочной перспективе — система не накапливает "багаж" устаревших ордеров, а постоянно обновляет сетку в соответствии с актуальными рыночными условиями.


Алгоритм выхода из позиций: искусство вовремя уйти

В трейдинге, как и в жизни, важно не только вовремя войти, но и вовремя выйти. TrisWeb_Optimized использует простой, но эффективный критерий — достижение заданного уровня совокупной прибыли:

// Общее количество ордеров
int totalOrders = ordersCount1 + ordersCount2 + ordersCount3;
double totalProfit = profit1 + profit2 + profit3;

// Если нет ордеров - создаем, если есть - проверяем на прибыль
if(totalOrders == 0)
{
   if(IsWorkTime())
   {
      PlaceInitialOrders();
   }
}
else if(totalProfit > Profit)
{
   DeleteAllOrders();
}
Но дьявол, как всегда, в деталях. Система учитывает не просто "голый" профит, а совокупный результат с учетом свопов и комиссий:
double swap = PositionGetDouble(POSITION_SWAP);
double profit = PositionGetDouble(POSITION_PROFIT);
double commission = PositionGetDouble(POSITION_COMMISSION);
double totalProfitForPosition = profit + swap + commission;

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


Тонкая настройка исполнения ордеров: договор с рыночным дьяволом

В мире высокочастотной торговли качество исполнения может оказаться решающим фактором между прибылью и убытком. TrisWeb_Optimized использует низкоуровневые структуры MQL5 для максимального контроля над процессом размещения и исполнения ордеров:

bool OpenBuyStop(string symbol, double volume, double openPrice)
{
   MqlTradeRequest request = {};
   MqlTradeResult result = {};
   
   request.action = TRADE_ACTION_PENDING;
   request.symbol = symbol;
   request.volume = volume;
   request.type = ORDER_TYPE_BUY_STOP;
   request.price = openPrice;
   request.deviation = Deviation;
   request.magic = EXPERT_MAGIC;
   
   if(UseCommentsInOrders)
      request.comment = OrderComment;
   
   if(!OrderSend(request, result))
   {
      Print("OrderSend error ", GetLastError(), " retcode: ", result.retcode);
      return false;
   }
   
   return true;
}
Особое внимание стоит обратить на параметр Deviation — он определяет, насколько далеко от запрошенной цены может быть исполнен ордер:
input int Deviation = 3;          // Допустимое отклонение цены

Это ваш договор с рыночным дьяволом: "Я готов принять исполнение на 3 пункта хуже, чем запросил, но не более". В период высокой волатильности это может быть разницей между исполненным ордером и упущенной возможностью. Слишком малое значение — и ваши ордера будут отклоняться из-за реквотов, слишком большое — и вы рискуете получить исполнение по неожиданно плохой цене.


Вывод информации и анализ в реальном времени: глаза и уши трейдера

Когда я начинал разработку TrisWeb_Optimized, я понимал, что одна из ключевых проблем многих советников — они работают как "черный ящик", не давая трейдеру понять, что происходит. Поэтому я добавил подробный вывод информации через функцию DisplayInfo() :

void DisplayInfo(int totalOrders, double totalProfit, 
                double profit1, double profit2, double profit3,
                int count1, int count2, int count3)
{
   string info = "";
   
   if(AutoLotOptimization)
   {
      double lot1 = CalculateOptimalLotSize(Symbol1, Lot_Symbol1);
      double lot2 = CalculateOptimalLotSize(Symbol2, Lot_Symbol2);
      double lot3 = CalculateOptimalLotSize(Symbol3, Lot_Symbol3);
      
      info += "Auto Lot: " + Symbol1 + "=" + DoubleToString(lot1, 2) + 
              ", " + Symbol2 + "=" + DoubleToString(lot2, 2) + 
              ", " + Symbol3 + "=" + DoubleToString(lot3, 2) + "\n";
   }
   
   // ... остальной код формирования информации
   
   Comment(info);
}
Вы можете выбрать между базовым режимом отображения (для тех, кто предпочитает минимализм) и детальным анализом, включающим разбивку по каждой валютной паре:
input bool DisplayDetailedInfo = false;  // Показывать детальную информацию

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

Вот как повел себя один из старых сетов:

 


Масштабирование системы: от простого бота к торговой империи

TrisWeb_Optimized — это не просто готовый советник, а фундамент для строительства вашей собственной арбитражной империи. За пять лет эволюции я неоднократно модифицировал его, добавляя новые функции и оптимизируя существующие. Вот несколько направлений, по которым вы можете развивать систему:

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

Уведомления через Telegram API добавляют мобильность вашей стратегии — вы всегда в курсе важных событий, даже находясь вдали от торгового терминала. Это особенно ценно, когда советник работает на удаленном VPS.

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

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


Вместо заключения

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

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

Прикрепленные файлы |
Triss_Optimized.mq5 (40.32 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (1)
Evgen Khenkin
Evgen Khenkin | 31 мар. 2025 в 14:19

Интересный подход. Запустил как есть пока без оптимизации. Держит баланс, по малу есть прирост.

При желании можно закрытие сетки перенести на конец дня, до формирования свопа. Сечас закрываети сетку на старте торгов. Или не использовать ежедневное закрытие вообще.

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

Продолжайте!

Разработка системы репликации (Часть 70): Настройка времени (III) Разработка системы репликации (Часть 70): Настройка времени (III)
В данной статье мы рассмотрим, как правильно и эффективно использовать функцию CustomBookAdd. Несмотря на кажущуюся простоту, она имеет множество нюансов. Например, позволяет сообщить указателю мыши, находится ли пользовательский символ на аукционе, торгуется ли он или рынок закрыт. Представленные здесь материалы предназначены только для обучения. Ни в коем случае нельзя рассматривать это приложение как окончательное, цели которого будут иные, кроме изучения представленных концепций.
Нейросети в трейдинге: Адаптивное обнаружение рыночных аномалий (DADA) Нейросети в трейдинге: Адаптивное обнаружение рыночных аномалий (DADA)
Предлагаем познакомиться с фреймворком DADA — инновационным методом выявления аномалий во временных рядах. Он помогает отличить случайные колебания от подозрительных отклонений. В отличие от традиционных методов, DADA гибко подстраивается под разные данные. Вместо фиксированного уровня сжатия он использует несколько вариантов и выбирает наиболее подходящий для каждого случая.
Оптимизация нейробоидами — Neuroboids Optimization Algorithm (NOA) Оптимизация нейробоидами — Neuroboids Optimization Algorithm (NOA)
Новая авторская биоинспирированная метаэвристика оптимизации — NOA (Neuroboids Optimization Algorithm), объединяющая принципы коллективного интеллекта и нейронных сетей. В отличие от классических методов, алгоритм использует популяцию самообучающихся "нейробоидов", каждый с собственной нейросетью, адаптирующей стратегию поиска в реальном времени. Статья раскрывает архитектуру алгоритма, механизмы самообучения агентов и перспективы применения этого гибридного подхода в сложных задачах оптимизации.
MQL5-советник, интегрированный в Telegram (Часть 4): Модуляризация функций кода для улучшенного повторного использования MQL5-советник, интегрированный в Telegram (Часть 4): Модуляризация функций кода для улучшенного повторного использования
В этой статье мы реорганизуем существующий код отправки сообщений и скриншотов из MQL5 в Telegram, преобразовав его в многоразовые модульные функции. Это оптимизирует процесс, обеспечивая более эффективное выполнение и более простое управление кодом в нескольких экземплярах.