Эволюционный отбор LLM-агентов в MetaTrader 5
Представьте, что вы наняли 20 трейдеров с принципиально разными стилями работы. Один торгует только моментум, второй ищет откаты к среднему, третий охотится за пробоями, четвёртый работает исключительно по статистике z-score. Вы платите им всем одинаково — независимо от результата. Через месяц вы обнаруживаете, что пятеро из них заработали всё, остальные пятнадцать либо в нуле, либо в минусе. Что вы сделаете?
Любой разумный менеджер уволит слабых и наймёт больше людей, похожих на сильных — но с небольшими различиями в подходе, чтобы найти ещё лучшие комбинации. Именно эта логика лежит в основе данной статьи. Только вместо трейдеров — LLM-агенты с разными системными промптами, вместо зарплаты — вычислительные ресурсы, а вместо менеджера — генетический алгоритм, который автоматически убивает слабых, клонирует сильных с мутациями и непрерывно улучшает популяцию прямо в ходе торговли.
Большинство статей о LLM-советниках описываeт одного агента с одним фиксированным промптом. Этот промпт пишется один раз и никогда не меняется. Но рынок не постоянен: в режиме тренда лучший агент — тот, кто следует импульсу; в боковике — тот, кто торгует от уровней; в период высокой волатильности — тот, кто умеет ждать. Фиксированный промпт — это стратегия, замёрзшая во времени: она либо хороша сейчас, либо нет. Она не адаптируется, не учится и не эволюционирует.
По итогам статьи у вас будет архитектура из 20 LLM-агентов на базе Grok от xAI. Она выполняет эволюционный отбор каждые 20 сделок: удаляет нижние 25% по фитнесу, клонирует верхние 25% с мутацией промпта и публикует лидерборд на графике MetaTrader 5.
Почему Grok и почему без SDK
Для предыдущей версии этой системы использовался DeepSeek-R1 через GitHub Models. Это потребовало установки openai SDK, получения GitHub-токена и работы через Azure-прокси. Grok от xAI упрощает интеграцию: доступен прямой REST API (api.x.ai) и стандартный Bearer-токен. Единственная зависимость — библиотека requests, которая обычно уже есть в Python-окружении. Никаких SDK, никаких промежуточных прокси.
Второе принципиальное отличие — Grok не возвращает блоки <think>...</think> в стандартном режиме, в отличие от reasoning-моделей DeepSeek. Это упрощает парсинг ответа: нет необходимости удалять рассуждения перед извлечением JSON.
Почему именно генетический алгоритм
Генетические алгоритмы — метод оптимизации, вдохновлённый биологической эволюцией. Классическая работа Холланда (John Holland, «Adaptation in Natural and Artificial Systems», 1975) описывает три оператора: отбор, скрещивание и мутацию. В контексте торговых систем GA применялись для оптимизации параметров стратегий с конца 1990-х годов — платформа MetaTrader 5 сама использует генетический оптимизатор в тестере стратегий.
Наш подход отличается тем, что единицей эволюции является системный промпт LLM-агента, а не числовые параметры индикатора. Это создаёт принципиально новое пространство поиска — пространство торговых философий, выраженных на естественном языке.
Ключевой вопрос генетического алгоритма — как измерить качество особи. Наивный ответ «по PnL» неверен: агент с тремя прибыльными сделками из трёх выглядит лучше агента с 50 из 70, хотя статистически второй надёжнее. Мы используем взвешенный коэффициент Шарпа:
Fitness = Sharpe × √(число_сделок)
где Sharpe = mean(PnL) / std(PnL).
Множитель √N вознаграждает агентов, которые не только прибыльны, но и активны. Агенты с менее чем тремя сделками получают фитнес = 0 и не участвуют в рейтинге. Важно: лог сервера показывает personal_trades — число личных сделок конкретного агента, а не суммарное число сделок по всей популяции. Путаница между этими двумя числами — частая причина ложного ощущения, что «фитнес не растёт».
Выбор агента для очередного анализа балансирует между эксплуатацией и исследованием (exploitation/exploration). Для проверенных агентов используется softmax-рулетка по фитнесу, а агенты без достаточной статистики получают совокупный 30% шанс быть выбранными — новорождённые мутанты гарантированно получат попытки для объективной оценки.
Мутация промпта: как изменить философию, не сломав её
Мутация языкового промпта — нетривиальная задача. В отличие от числовых параметров, «изменить промпт на 5%» не имеет однозначного смысла. Мы реализуем четыре вида управляемой мутации, каждая применяется с вероятностью 0.3.
Первая — замена прилагательного стиля: «pure momentum trader» → «adaptive momentum trader», «systematic trend follower» → «ruthless trend follower». Это меняет агрессивность и гибкость агента, не затрагивая его базовую логику.
Вторая — замена ключевого концепта стратегии: momentum ↔ velocity, trend ↔ impulse, RSI ↔ stochastic oscillator. Агент сохраняет общую идею, но начинает смотреть на рынок через другой инструмент.
Третья — смещение числовых порогов на ±5–15%: RSI > 70 → RSI > 68 или RSI > 75, ATR period 14 → period 10 или 20. Самая тонкая мутация, меняющая чувствительность без изменения логики.
Четвёртая — добавление поведенческого суффикса: «Always confirm with at least two independent signals.» или «Adapt aggressiveness based on recent win streak.» Это добавляет мета-правило поверх существующей философии.
Пример. Исходный промпт агента SIGMA:
"You are SIGMA — a quantitative statistician. You see only probabilities and z-scores. " "Buy when price is more than 1.5 standard deviations below its 20-period mean. " "Sell when price is more than 1.5 standard deviations above its 20-period mean. " "Emotions and patterns are noise; only statistical edge matters."
Его потомок после двух поколений мутаций (SIGMA_G2):
"You are SIGMA — a disciplined statistician. You see only probabilities and z-scores. " "Buy when price is more than 1.5 standard deviations below its 20-period mean. " "Sell when price is more than 1.5 standard deviations above its 18-period mean. " "Emotions and patterns are noise; only statistical edge matters. " "Always confirm with at least two independent signals."
Изменения тонкие, но принципиальные: quantitative → disciplined (адаптация вместо механики), 20-period → 18-period (более быстрый отклик), добавлено требование двойного подтверждения. Философия сохранена — характер изменился.
Архитектура: как это работает вместе
Система состоит из двух компонентов: Python-сервера с эволюционным движком и MQL5-советника. Принципиальное отличие от предыдущих версий — синхронная обработка команды EVOLVE и защита от конкурентной записи в сокет:
MetaTrader 5 (EA_Evolution_Grok.mq5) │ ├─ EVOLVE:SYMBOL:csv ──────────────────────────────────┐ │ │ ├─ RESULT:SYMBOL:agent_id:pnl ─────────────────────┐ │ │ WebSocket │ │ │ Python-сервер (llm_server_evolution_grok.py) │ │ │ │ │ ├─ EvolutionEngine │ │ │ ├─ Пул 20 агентов │ │ │ ├─ Softmax-выбор агента ◄──────────────────────┘ │ │ ├─ calc_indicators() + build_market_context() │ │ └─ evolve() каждые 20 сделок │ │ │ └─ Grok (xAI API) ◄────────────────────────────────────┘ {"signal","comment","confidence"}
На каждом новом баре советник собирает последние 60 цен закрытия и отправляет их серверу командой EVOLVE:SYMBOL:csv . Сервер выбирает агента (softmax-рулетка), вычисляет 11 индикаторов, формирует контекст и синхронно запрашивает Grok. Модель возвращает JSON с сигналом, комментарием и уверенностью. Если уверенность выше порога InpMinConf , советник открывает позицию и запоминает agent_id .
При закрытии позиции советник находит её в истории сделок по DEAL_POSITION_ID — это надёжный способ, проверенный на реальной торговле. Затем он считает PnL в нормализованных пунктах и отправляет на сервер RESULT:SYMBOL:agent_id:pnl . Сервер обновляет личную статистику агента. После каждых 20 сделок по всей популяции автоматически запускается эволюционный цикл.
Три критических проблемы, которые нужно знать
Разработка этой системы выявила несколько нетривиальных проблем, решение которых не очевидно из документации.
Гонка потоков в сервере. В наивной реализации команда EVOLVE запускает отдельный поток для вызова Grok ( threading.Thread(_run).start() ). Пока поток ждёт ответа 10–15 секунд, главный цикл продолжает обрабатывать входящие команды — в том числе EVOLVE_STATUS — и вызывает conn.sendall() параллельно с потоком Grok. Два потока пишут в один сокет без блокировки; байты перемешиваются; советник получает мусор вместо JSON и интерпретирует его как hold . Решение: EVOLVE обрабатывается синхронно, а все отправки идут через send_lock .
Flood EVOLVE_STATUS. В Strategy Tester таймер с интервалом 30 секунд ( EventSetTimer(30) ) срабатывает на каждом баре, а не в реальном времени. На M15 это означает 20+ запросов EVOLVE_STATUS в секунду. Решение: убрать EventSetTimer полностью; статус запрашивается раз в 10 баров прямо в OnTick . На сервере добавлена дедупликация: повторный ответ на EVOLVE_STATUS не чаще раза в 2 секунды.
Формат JSON-инструкции. Шаблон вида "signal":"buy"|"sell"|"hold" и "confidence":0.0-1.0 — это не валидный JSON. Grok воспринимает его буквально и либо возвращает строку с символом | , либо выбирает консервативный дефолт — hold . Решение: показывать конкретный рабочий пример: {"signal": "buy", "comment": "Strong uptrend", "confidence": 0.82} , добавить "temperature": 0.2 и явное указание, что hold — редкое исключение.
Популяция: 20 агентов и их философии
| Агент | Философия |
|---|---|
| ATLAS | Чистый моментум по скорости цены |
| ORACLE | Mean reversion через RSI + Bollinger Bands |
| FALCON | Breakout-охотник (пробой + ATR-экспансия) |
| COMPASS | Систематический трендследователь по MA |
| DEVIL | Контртрейдер: покупает панику, продаёт эйфорию |
| STORM | Торгует только в низковолатильных режимах |
| RAZOR | Агрессивный скальпер по MA5 |
| STONE | Прайс-экшн без индикаторов, только структура |
| SHIELD | Консерватор: только A+ сетапы, лучше пропустить |
| PENDULUM | RSI-экстремы с тройным подтверждением |
| CROSS | MA-кроссоверы с фильтром импульса |
| PILLAR | Поддержка/сопротивление + свечное подтверждение |
| SPIRAL | Фибоначчи-откаты после импульсных движений |
| PRISM | Мультитаймфреймовый синтез (тренд H4, вход M15) |
| PULSE | Анализ соотношения объём/цена |
| DIVERGE | Дивергенция RSI относительно цены |
| WAVE | Свинг-трейдинг по Stochastic |
| HYDRA | Адаптивный гибрид: определяет режим и меняет логику |
| THUNDER | Агрессивный пробой, принимает ложные сигналы как плату за настоящие |
| SIGMA | Квантовый статистик: только z-score и отклонения |
Все агенты получают одинаковый рыночный контекст с 11 показателями: текущая цена, MA5/20/50, RSI(14), Stochastic(14), ATR(14), BB(mid/lo/hi), z-score и выравнивание тренда. Единство входных данных при разнообразии интерпретаций делает сравнение честным.
Ключевые фрагменты кода
Класс Agent хранит всю статистику и считает фитнес:
@dataclass class Agent: agent_id: int name: str prompt: str generation: int = 0 trades: int = 0 pnl_history: List[float] = field(default_factory=list) alive: bool = True @property def fitness(self) -> float: if self.trades < 3: return 0.0 avg = sum(self.pnl_history) / self.trades std = float(np.std(self.pnl_history)) or 1.0 return (avg / std) * math.sqrt(self.trades)
Вызов Grok — без SDK, только requests , с temperature=0.2 для решительных сигналов:
def ask_grok(system_prompt: str, user_message: str) -> str: enforced_system = ( system_prompt + "\n\n" "OUTPUT RULE: Reply with ONLY one valid JSON object.\n" 'Example: {"signal": "buy", "comment": "reason", "confidence": 0.82}\n' "signal must be exactly: buy sell or hold\n" "BIAS: 'hold' only for perfectly ambiguous conditions. Commit to a direction." ) payload = { "model": "grok-3-mini", "max_tokens": 1200, "temperature": 0.2, "messages": [ {"role": "system", "content": enforced_system}, {"role": "user", "content": user_message}, ], } resp = requests.post("https://api.x.ai/v1/chat/completions", headers=HEADERS, json=payload, timeout=60) resp.raise_for_status() return resp.json()["choices"][0]["message"]["content"].strip()Синхронный обработчик EVOLVE с send_lock — ключевое исправление против гонки потоков:
send_lock = threading.Lock() def safe_send(data: bytes): with send_lock: conn.sendall(data) # В handle_client: if cmd.startswith("EVOLVE:"): prices = [float(x) for x in csv.split(",") if x.strip()] result = evolve_analyze(sym, prices) # синхронно, без потока safe_send(ws_encode(json.dumps(result, ensure_ascii=False))) continueНа стороне MQL5 советник трекает открытую позицию и ищет закрывающую сделку через DEAL_POSITION_ID — это надёжнее чем поиск по order ticket:
double GetDealProfit(ulong posTicket) { HistorySelect(0, TimeCurrent() + 86400); int total = HistoryDealsTotal(); double pnl = 0.0; bool found = false; for(int i = total - 1; i >= MathMax(0, total - 100); i--) { ulong dTicket = HistoryDealGetTicket(i); ulong dPos = (ulong)HistoryDealGetInteger(dTicket, DEAL_POSITION_ID); if(dPos != posTicket) continue; ENUM_DEAL_ENTRY entry = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(dTicket, DEAL_ENTRY); if(entry == DEAL_ENTRY_OUT || entry == DEAL_ENTRY_INOUT) { pnl += HistoryDealGetDouble(dTicket, DEAL_PROFIT) + HistoryDealGetDouble(dTicket, DEAL_SWAP); found = true; } } return found ? pnl : EMPTY_VALUE; } ``` --- ## Запуск за три шага Получите API-ключ xAI на [console.x.ai](https://console.x.ai) — новые аккаунты получают стартовый кредит. Установите единственную зависимость: `pip install requests numpy`. Вставьте ключ в строку `XAI_API_KEY` и запустите сервер командой `python llm_server_evolution_grok.py`. Скомпилируйте и запустите `EA_Evolution_Grok.mq5` в MetaTrader 5. После первых 20 сделок популяции вы увидите в консоли: ``` [11:42:17] [EVO #1] Elite: ['SIGMA', 'HYDRA', 'ORACLE', 'COMPASS'] [11:42:17] [EVO #1] Culled: ['THUNDER', 'DEVIL', 'RAZOR', 'STONE'] [11:42:17] [EVO #1] Born: SIGMA_G1 (parent=SIGMA, id=20) personal_trades=0 [11:42:17] [EVO #1] Born: HYDRA_G1 (parent=HYDRA, id=21) personal_trades=0 [11:42:17] [EVO #1] Born: ORACLE_G1 (parent=ORACLE, id=22) personal_trades=0 [11:42:17] [EVO #1] Born: COMPASS_G1 (parent=COMPASS, id=23) personal_trades=0 [11:42:17] [EVO #1] Population: 20 agents alive | Evolutions done: 1
Нормализация PnL в пунктах (а не в деньгах) позволяет корректно сравнивать агентов на разных инструментах и размерах лота.
Обратите внимание на personal_trades в логах. Если агент показывает fitness=0.000 — это нормально: у него ещё нет трёх личных сделок. Фитнес станет ненулевым только после третьей сделки конкретного агента, независимо от общего числа сделок популяции.
Что важно учитывать
Latency Grok через API xAI составляет 1–5 секунд для стандартных запросов, что значительно быстрее DeepSeek-R1 через GitHub Models. Для таймфреймов M5 и выше это абсолютно приемлемо.
Система начинает работать корректно после 50+ сделок на популяцию: до этого фитнес-оценки слишком шумные. Параметр EVOLUTION_INTERVAL = 20 — минимально достаточное значение. Сумма ELITE_RATIO + CULL_RATIO не должна превышать 0.6, иначе популяция схлопнется к одному клону. Интенсивность мутации mutation_strength = 0.3 — золотая середина.
Никогда не используйте EventSetTimer вместе с тестером стратегий — таймер не работает в реальном времени и превращается в генератор flood-запросов. Для периодических задач используйте счётчик баров.
Важное ограничение текущей версии — отсутствие crossover: настоящий GA комбинирует гены двух родителей, а не только мутирует одного. Объединить первый абзац промпта SIGMA со вторым абзацем HYDRA — интересная задача для следующей итерации. Ещё одно направление — использовать встроенный поиск Grok в реальном времени для обогащения рыночного контекста свежими новостями прямо в промпте агента.
Результаты тестирования
Система была протестирована на паре EURUSD, таймфрейм M15, в период с 1 февраля по 16 марта 2026 года на платформе RoboForex-ECN (Build 5672). Качество моделирования — 100%, начальный депозит — $10 000, кредитное плечо 1:500, лот 0.01.

За тестовый период система совершила 22 сделки (44 ордера с учётом парных открытий/закрытий). Чистая прибыль составила $1.51 при Profit Factor 1.04 и Sharpe Ratio 0.14 — результаты скромные, однако система завершила период в плюсе при максимальной просадке баланса всего 0.23% ($23.28).

Процент прибыльных сделок — 45.45%, при этом средняя прибыльная сделка ($4.35) заметно превышает среднюю убыточную ($3.48). Лучшая сделка принесла $18.73 (агент ATLAS, шорт с 26.02 по 03.03), крупнейший убыток — $9.56 (последняя незакрытая позиция EVO_DEVIL, принудительно закрытая по окончании теста).
Наиболее активным агентом оказался DEVIL — контртрейдер, совершивший 5 сделок из 22 и показавший смешанные результаты: как лучшую сделку теста ($18.65 на шорте в начале марта), так и итоговый убыток в конце. Активно торговали также THUNDER, CROSS, PULSE и WAVE.
Важно учитывать контекст: 22 сделки — это минимальная статистика, при которой эволюционный цикл успевает запуститься лишь один раз (каждые 20 сделок популяции). Реальный потенциал системы раскрывается после 50+ сделок, когда фитнес-оценки агентов становятся достаточно надёжными для осмысленного отбора.
Заключение
В начале статьи у нас была проблема фиксированного промпта — стратегии, замёрзшей во времени. Теперь у нас есть живая экосистема из 20 агентов, каждый из которых несёт собственную торговую философию, и механизм, который непрерывно проверяет эти философии на прочность.
Бэктест на EURUSD M15 показал скромный, но положительный результат: $1.51 чистой прибыли при просадке 0.23% за шесть недель торговли. Это не тот результат, которым хвастаются в рекламных буклетах. Но это и не цель данной работы. Цель — доказать, что система способна самостоятельно находить работающих агентов, отсеивать нежизнеспособных и воспроизводить лучших с улучшениями — и она с этим справляется. Агент ATLAS поймал лучшую сделку теста не потому, что его запрограммировали на конкретный паттерн февраля 2026 года, а потому что эволюционный отбор дал ему шанс проявить себя в подходящих условиях.
Два направления очевидны для следующей итерации. Первое — кроссовер промптов: настоящий генетический алгоритм комбинирует гены двух родителей, а не только мутирует одного. Объединить аналитическую строгость SIGMA с адаптивностью HYDRA — задача принципиально иного уровня, чем замена прилагательного в системном промпте. Второе — обогащение контекста: встроенный поиск Grok позволяет подтягивать актуальные новости прямо в промпт агента в момент принятия решения, превращая статичный технический анализ в ситуационную осведомлённость.
Генетические алгоритмы десятилетиями доказывали свою состоятельность в оптимизации числовых параметров. Применение той же логики к пространству торговых философий, выраженных на естественном языке — это качественно иной шаг. Рынок не стоит на месте: у него нет фиксированного промпта. Теперь и у вашей стратегии его нет тоже.
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Торговые инструменты на MQL5 (Часть 15): Эффекты размытия холста, рендеринг теней и плавная прокрутка колесом мыши
Торговые инструменты на MQL5 (Часть 14): Прокручиваемый текстовый холст с пиксельной точностью, сглаживанием и закругленной полосой прокрутки
Внедрение в MQL5 практических модулей из других языков (Часть 04): Модули time, date и datetime из Python
Нейросети в трейдинге: Многодоменная архитектура анализа финансовых данных (Основные компоненты)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования