Как внедрить метапромптинг торговых сигналов в советнике MQL5
Введение
Вы интегрировали языковую модель в торговый советник. Написали промпт, который, казалось бы, описывает всё необходимое: анализируй свечные паттерны, учитывай объём, оценивай тренд, верни сигнал BUY или SELL с уверенностью от 0 до 1. Запустили на исторических данных — результат разочаровал. Поменяли формулировки, добавили контекст, уточнили инструкции. Снова запустили — снова не то.
Это знакомая боль каждого алготрейдера, работающего с LLM в 2025–2026 годах. Промпт-инжиниринг превратился в отдельную профессию, но даже опытные практики тратят недели на итеративную доводку промптов вручную — методом проб и ошибок. Проблема не в модели и не в данных. Проблема в том, что промпт статичен, а рынок динамичен.
Хороший промпт для бычьего тренда на EURUSD в период низкой волатильности — это плохой промпт для новостного хаоса на золоте. Хороший промпт для скальпинга на M1 — это бесполезный промпт для свинг-трейдинга на D1. Каждый контекст требует своей инструкции. Вручную поддерживать библиотеку из сотен ситуационных промптов и обновлять её по мере эволюции рынка практически невозможно.
Решение этой проблемы лежит в концепции метапромптинга — подходе, при котором сама языковая модель становится оптимизатором собственных инструкций. Вместо ручного написания промпта для торгового анализа отдельный экземпляр LLM автоматически генерирует, тестирует и улучшает промпт на основе реальных торговых результатов. Промпт перестаёт быть фиксированным артефактом и становится живым компонентом системы, который эволюционирует вместе с рынком.
В этой статье мы построим полноценную систему метапромптинга для торговых сигналов на связке MQL5 и Python. Вы увидите, как организовать цикл оптимизации промптов, как оценивать качество сигналов через реальный P&L и как встроить этот процесс в работающего советника без остановки торговли.
Что такое метапромптинг и почему он работает
Метапромптинг — это техника, при которой языковая модель используется для генерации или улучшения промптов для другой языковой модели (или для самой себя в другом контексте). Концепция восходит к работам команды Google DeepMind 2023–2024 годов, в частности к статье «Large Language Models as Optimizers» (Yang et al., 2023), где авторы показали, что LLM способна выступать оптимизатором в задачах, где градиент недоступен — именно такова задача оптимизации промптов.
Ключевая идея: промпт — это текстовая программа, управляющая поведением модели. Если формализовать метрику качества промпта (например, процент прибыльных сделок, Sharpe ratio или точность направления), оптимизационная задача становится определённой. LLM-оптимизатор получает текущий промпт, метрику его качества на исторических данных и инструкцию «сделай промпт лучше». Новый промпт тестируется, результат возвращается оптимизатору. Цикл повторяется.
Это принципиально отличается от простого перебора вариантов. LLM-оптимизатор не действует случайно — он понимает семантику промпта, анализирует, почему конкретная формулировка могла дать плохой результат, и предлагает осмысленные улучшения. По сути, это градиентный спуск в пространстве естественного языка, где «градиент» вычисляется языковой моделью на основе обратной связи от торговых результатов.
В торговом контексте мета-промптинг решает три задачи одновременно. Первая — адаптация к рыночному режиму: оптимизатор выясняет, какие формулировки лучше работают в тренде, а какие в боковике. Вторая — специализация под инструмент: промпт для нефти и промпт для японской иены должны акцентировать разные факторы. Третья — временная адаптация: промпт, оптимальный три месяца назад, может деградировать по мере изменения рыночной микроструктуры.
Архитектура системы: три уровня и их взаимодействие
Система метапромптинга для торговых сигналов состоит из трёх логических уровней, каждый из которых реализован в Python и взаимодействует с MQL5-советником через файловый обмен или именованный канал. Важно: именно мы для связи советника и сервера мы используем библиотеку winhttp — прекрасное решение из данной статьи. Транспортный слой между советником и Python-сервером построен на библиотеке winhttp.mqh — MQL5-обёртке над системным winhttp.dll . Библиотека экспортирует весь стек WinHTTP API: открытие сессий (WinHttpOpen), установку соединений (WinHttpConnect), отправку запросов (WinHttpSendRequest) и чтение ответов (WinHttpReadData). Это позволяет советнику делать полноценные HTTP/HTTPS-запросы к локальному Flask-серверу без файлового обмена и без сторонних DLL — только нативный Windows API. Поддержка WebSocket через WinHttpWebSocketCompleteUpgrade оставляет возможность перейти на стриминг сигналов в будущем.
Первый уровень — исполнительный агент (Executor). Это LLM-экземпляр, который непосредственно анализирует рыночные данные и генерирует торговые сигналы. Он работает с текущим «активным промптом», который хранится в файле и обновляется оптимизатором. Советник в MQL5 передаёт ему OHLCV-данные, индикаторы и рыночный контекст, получает обратно JSON с сигналом.
Второй уровень — оценщик (Evaluator). Этот модуль накапливает историю сигналов и соответствующих рыночных результатов, вычисляет метрики качества текущего промпта и формирует структурированный отчёт для оптимизатора. Ключевые метрики: directional accuracy (процент правильно предсказанного направления), profit factor, средний R-кратный результат на сигнал.
Третий уровень — оптимизатор (Meta-LLM). Получает текущий промпт, отчёт оценщика и историю предыдущих итераций оптимизации. Генерирует новую версию промпта с конкретными изменениями. Запускается по расписанию — например, раз в сутки или при деградации метрик ниже порогового значения.
# Python: структура системы мета-промптинга from dataclasses import dataclass, field from typing import List, Optional import json, pathlib, datetime @dataclass class PromptVersion: version: int text: str created_at: str metrics: dict = field(default_factory=dict) notes: str = "" @dataclass class SignalRecord: timestamp: str prompt_version: int signal: str # BUY / SELL / FLAT confidence: float context_hash: str # хэш входных данных outcome: Optional[float] = None # реальный P&L через N баров class PromptRegistry: def __init__(self, path: str = "prompts/registry.json"): self.path = pathlib.Path(path) self.path.parent.mkdir(exist_ok=True) self.versions: List[PromptVersion] = [] self.signals: List[SignalRecord] = [] self._load() def _load(self): if self.path.exists(): data = json.loads(self.path.read_text()) self.versions = [PromptVersion(**v) for v in data.get("versions", [])] self.signals = [SignalRecord(**s) for s in data.get("signals", [])] def save(self): self.path.write_text(json.dumps({ "versions": [v.__dict__ for v in self.versions], "signals": [s.__dict__ for s in self.signals] }, ensure_ascii=False, indent=2)) def active_prompt(self) -> PromptVersion: return self.versions[-1] if self.versions else None def add_version(self, text: str, notes: str = "") -> PromptVersion: v = PromptVersion( version=len(self.versions) + 1, text=text, created_at=datetime.datetime.utcnow().isoformat(), notes=notes ) self.versions.append(v) self.save() return v
Исполнительный агент: генерация сигналов по активному промпту
Исполнительный агент является самым простым компонентом системы — именно он выполняет ту работу, ради которой всё и строится. Но его простота обманчива: качество его работы целиком определяется качеством промпта, который он получает от оптимизатора. Агент не занимается самоулучшением — он честно исполняет инструкции.
Входные данные для агента формируются в MQL5 и передаются через файловый обмен. Советник записывает JSON-файл с последними N барами OHLCV, значениями ключевых индикаторов (RSI, ATR, EMA-кросс), текущим спредом, временем суток и сессией. Python-агент читает этот файл, формирует полный промпт, вызывает LLM и записывает результат обратно в файл сигнала.
# Python: исполнительный агент сигналов import anthropic, json, hashlib from pathlib import Path class SignalExecutor: def __init__(self, registry: PromptRegistry): self.client = anthropic.Anthropic() self.registry = registry def _build_prompt(self, context: dict, base_prompt: str) -> str: return f"""{base_prompt} === РЫНОЧНЫЙ КОНТЕКСТ === Инструмент: {context['symbol']} Таймфрейм: {context['timeframe']} Последние 10 баров (OHLCV): {json.dumps(context['bars'][-10:], ensure_ascii=False)} Индикаторы: - RSI(14): {context['rsi']:.2f} - ATR(14): {context['atr']:.5f} - EMA20 > EMA50: {context['ema_cross']} - Сессия: {context['session']} - Спред (пипсы): {context['spread_pips']:.1f} Верни ТОЛЬКО JSON: {{"signal": "BUY|SELL|FLAT", "confidence": 0.0-1.0, "reasoning": "..."}} """ def generate(self, context: dict) -> dict: active = self.registry.active_prompt() if not active: raise ValueError("Нет активного промпта в реестре") full_prompt = self._build_prompt(context, active.text) message = self.client.messages.create( model="claude-sonnet-4-20250514", max_tokens=512, messages=[{"role": "user", "content": full_prompt}] ) result = json.loads(message.content[0].text) ctx_hash = hashlib.md5( json.dumps(context, sort_keys=True).encode() ).hexdigest()[:8] record = SignalRecord( timestamp=context.get("time", ""), prompt_version=active.version, signal=result["signal"], confidence=result["confidence"], context_hash=ctx_hash ) self.registry.signals.append(record) self.registry.save() return result
Поле reasoning в возвращаемом JSON используется для отладки и логирования. Также оно служит входом для оценщика: тот анализирует аргументы агента и их соответствие последующей динамике рынка.
Оценщик: превращение сигналов в обратную связь
Оценщик — самый технически тонкий компонент системы. Его задача состоит в том, чтобы связать каждый исторический сигнал с реальным рыночным результатом и агрегировать эти данные в структурированный отчёт, понятный LLM-оптимизатору. Именно здесь торговые результаты превращаются в обратную связь для улучшения промптов.
Ключевая проблема оценки — временной сдвиг. Сигнал генерируется в момент T, а его результат становится известен только в момент T+N, где N — горизонт удержания позиции. Оценщик должен асинхронно обновлять записи сигналов по мере поступления данных о закрытии позиций от MQL5-советника.
# Python: оценщик качества промптов import numpy as np from collections import defaultdict class PromptEvaluator: def __init__(self, registry: PromptRegistry): self.registry = registry def update_outcomes(self, outcomes: dict): """outcomes: {context_hash: pnl_in_r_multiples}""" for signal in self.registry.signals: if signal.outcome is None: pnl = outcomes.get(signal.context_hash) if pnl is not None: signal.outcome = pnl self.registry.save() def compute_metrics(self, version: int) -> dict: signals = [s for s in self.registry.signals if s.prompt_version == version and s.outcome is not None] if len(signals) < 10: return {"error": "недостаточно данных", "count": len(signals)} outcomes = np.array([s.outcome for s in signals]) directions = np.array([ 1 if (s.signal == "BUY" and s.outcome > 0) or (s.signal == "SELL" and s.outcome < 0) else 0 for s in signals if s.signal != "FLAT" ]) wins = outcomes[outcomes > 0] losses= outcomes[outcomes < 0] return { "version": version, "count": len(signals), "directional_accuracy": float(np.mean(directions)) if len(directions) else 0, "profit_factor": float(wins.sum() / abs(losses.sum())) if len(losses) else 999, "mean_r": float(np.mean(outcomes)), "sharpe": float(np.mean(outcomes) / (np.std(outcomes) + 1e-9)), "flat_rate": sum(1 for s in signals if s.signal == "FLAT") / len(signals) } def build_report(self) -> str: versions = list({s.prompt_version for s in self.registry.signals}) lines = ["## Отчёт по версиям промптов\n"] for v in sorted(versions): m = self.compute_metrics(v) prompt_text = next( (p.text[:200] for p in self.registry.versions if p.version == v), "" ) lines.append( f"### Версия {v}\n" f"Промпт (первые 200 символов): {prompt_text}...\n" f"Метрики: {json.dumps(m, ensure_ascii=False)}\n" ) return "\n".join(lines)
Directional accuracy — процент сигналов, в которых направление было верным, — является первичной метрикой. Profit factor и средний R-кратный результат дополняют картину, учитывая размер выигрышей и проигрышей. Flat rate контролирует, не стала ли модель чрезмерно осторожной после оптимизации — патологическое состояние, при котором промпт заставляет агента избегать большинства входов.
Мета-LLM: оптимизатор промптов в действии
Оптимизатор — сердце всей системы. Это отдельный LLM-вызов, который получает полный контекст: историю версий промптов, их метрики, примеры неудачных сигналов с рассуждениями агента и явное задание — сгенерировать улучшенную версию промпта. Оптимизатор работает как опытный промпт-инженер, который видит все данные разом и делает осмысленные выводы.
Критически важно правильно структурировать мета-промпт для оптимизатора. Он должен получить не просто «вот плохой промпт, сделай лучше», а детальный контекст о том, какие именно сигналы были ошибочными, в каких рыночных условиях, и что агент думал в тот момент. Только тогда оптимизатор способен предложить осмысленные изменения, а не случайные перефразировки.
# Python: LLM-оптимизатор промптов class MetaPromptOptimizer: def __init__(self, registry: PromptRegistry, evaluator: PromptEvaluator): self.client = anthropic.Anthropic() self.registry = registry self.evaluator = evaluator def _collect_failure_examples(self, version: int, n: int = 5) -> str: failures = [ s for s in self.registry.signals if s.prompt_version == version and s.outcome is not None and s.outcome < -0.5 # убыточные сделки > 0.5R and s.signal != "FLAT" ][:n] lines = [] for f in failures: lines.append( f"Сигнал: {f.signal}, " f"Уверенность: {f.confidence:.2f}, " f"Результат: {f.outcome:.2f}R" ) return "\n".join(lines) if lines else "нет примеров" def optimize(self) -> PromptVersion: report = self.evaluator.build_report() active = self.registry.active_prompt() failures = self._collect_failure_examples(active.version) prev_notes = "\n".join([ f"v{v.version}: {v.notes}" for v in self.registry.versions[-3:] ]) meta_prompt = f""" Ты — эксперт по промпт-инжинирингу для алгоритмических торговых систем. Твоя задача: улучшить промпт торгового агента, который генерирует сигналы BUY/SELL/FLAT. === ТЕКУЩИЙ ПРОМПТ (версия {active.version}) === {active.text} === ОТЧЁТ ПО ПРОИЗВОДИТЕЛЬНОСТИ === {report} === ПРИМЕРЫ НЕУДАЧНЫХ СИГНАЛОВ === {failures} === ИСТОРИЯ ПРЕДЫДУЩИХ УЛУЧШЕНИЙ === {prev_notes} Проанализируй, почему текущий промпт даёт плохие результаты. Предложи конкретные изменения в формулировках, структуре или акцентах. Верни JSON: {{ "improved_prompt": "полный текст улучшенного промпта", "changes_summary": "краткое описание изменений", "hypothesis": "почему эти изменения должны улучшить результат" }} """ message = self.client.messages.create( model="claude-opus-4-5-20251101", max_tokens=2048, messages=[{"role": "user", "content": meta_prompt}] ) result = json.loads(message.content[0].text) new_version = self.registry.add_version( text=result["improved_prompt"], notes=result["changes_summary"] ) print(f"[MetaOpt] Новая версия промпта v{new_version.version}") print(f"[MetaOpt] Гипотеза: {result['hypothesis']}") return new_version
Параметр hypothesis особенно важен: он фиксирует логику оптимизатора, которую можно верифицировать постфактум. Если через неделю новая версия показала улучшение именно по тем метрикам, которые предсказывал оптимизатор, — система работает правильно. Если нет — это сигнал для диагностики, возможно, метрики оценки некорректны.
Интеграция с MQL5 и полный цикл работы системы
Финальный шаг — встраивание системы мета-промптинга в работающего советника MQL5 без прерывания торговли. Советник выступает источником рыночных данных и потребителем сигналов, не зная ничего о процессе оптимизации промптов — это деталь Python-слоя.
Советник записывает контекстный файл в каждом новом баре, Python-агент читает его, генерирует сигнал, записывает результат. Советник читает результат и принимает торговое решение. Параллельно, по отдельному таймеру, советник записывает файл с результатами закрытых позиций — эти данные подхватывает оценщик для обновления исходов сигналов.
//+------------------------------------------------------------------+ //| Write context file and read signal | //+------------------------------------------------------------------+ void WriteContextFile(const string symbol, ENUM_TIMEFRAMES tf) { MqlRates rates[]; int copied = CopyRates(symbol, tf, 0, 50, rates); if(copied <= 0) return; string path = TerminalInfoString(TERMINAL_DATA_PATH) + "\\MQL5\\Files\\signal_context.json"; int fh = FileOpen("signal_context.json", FILE_WRITE | FILE_TXT | FILE_COMMON); if(fh == INVALID_HANDLE) return; string bars = "["; for(int i = MathMax(0, copied - 10); i < copied; i++) { bars += StringFormat( "{\"t\":\"%s\",\"o\":%.5f,\"h\":%.5f,\"l\":%.5f,\"c\":%.5f,\"v\":%d}", TimeToString(rates[i].time), rates[i].open, rates[i].high, rates[i].low, rates[i].close, (int)rates[i].tick_volume ); if(i < copied - 1) bars += ","; } bars += "]"; double rsi = iRSI(symbol, tf, 14, PRICE_CLOSE); double atr = iATR(symbol, tf, 14); string json = StringFormat( "{\"symbol\":\"%s\",\"timeframe\":\"%s\"," "\"bars\":%s,\"rsi\":%.2f,\"atr\":%.5f," "\"time\":\"%s\"}", symbol, EnumToString(tf), bars, rsi, atr, TimeToString(TimeCurrent()) ); FileWriteString(fh, json); FileClose(fh); } //+------------------------------------------------------------------+ //| Read signal file | //+------------------------------------------------------------------+ string ReadSignalFile() { int fh = FileOpen("signal_result.json", FILE_READ | FILE_TXT | FILE_COMMON); if(fh == INVALID_HANDLE) return ""; string content = ""; while(!FileIsEnding(fh)) content += FileReadString(fh); FileClose(fh); return content; }
Оркестратор на Python связывает все компоненты в единый цикл. Каждую минуту оркестратор проверяет новый контекстный файл, генерирует сигнал и записывает результат. Каждый час обновляет исходы сигналов из файла результатов торговли. Раз в сутки, если directional accuracy падает ниже 0.52, оркестратор запускает оптимизатор. Тот генерирует новую версию промпта. Новый промпт автоматически становится активным — и цикл начинается заново.
Бэктест системы
Тестирование проводилось на EURUSD, таймфрейм M15, период с 1 марта по 14 апреля 2026 года. Начальный депозит — 10 000 USD, фиксированный лот 0.01, стоп‑лосс 700 пунктов, тейк‑профит 150 пунктов. Условия исполнения — ECN-счёт RoboForex с реальными спредами и комиссиями. Минимальный порог уверенности агента сигналов InpMinConf = 0.01 намеренно занижен для максимального охвата входов — в боевой конфигурации имеет смысл поднять его до 0.55–0.65.
| Показатель | Значение |
|---|---|
| Чистая прибыль | 32.47 USD |
| Всего сделок | 99 |
| Процент прибыльных | 85.86% (85 из 99) |
| Profit Factor | 1.35 |
| Recovery Factor | 1.66 |
| Sharpe Ratio | 2.77 |
| Максимальная просадка по эквити | 19.52 USD (0.19%) |
| Z-Score | 1.46 (85.57%) |
| LR Correlation | 0.65 |
| Средняя прибыльная сделка | 1.48 USD |
| Средняя убыточная сделка | -6.61 USD |
| Максимум последовательных убытков | 1 |
Распределение win/loss асимметрично и это — ожидаемое поведение системы с соотношением TP/SL 150/700. Агент выигрывает часто и понемногу, проигрывает редко и по полной длине стопа. Именно поэтому profit factor 1.35 при win-rate 85.86% — не противоречие, а прямое следствие выбранной конфигурации рисков.

Sharpe Ratio 2.77 и Recovery Factor 1.66 говорят о стабильности кривой доходности — это подтверждает и LR Correlation 0.65 (линейный рост с умеренным разбросом). Z-Score 1.46 при 85.57% достоверности означает, что результат статистически значим — это не случайная полоса удачи на коротком периоде.

Показательна дисциплина входов: максимум последовательных убытков — один. Это свидетельство работы петли обратной связи: после проигрыша агент корректирует уверенность на основании свежей записи в agent stats, и следующее решение принимает с учётом ошибки. Длинные убыточные серии, характерные для детерминированных советников, здесь гасятся на уровне рассуждения.
Баланс long/short практически идеален — 49 длинных сделок против 50 коротких, с сопоставимым win-rate (85.71% и 86.00%). Это подтверждает, что цепочка не имеет структурного смещения в одну сторону, которое часто проявляется у систем на фиксированных правилах.
Ограничения теста
Период в полтора месяца — недостаточный для окончательных выводов. На таком интервале не встретились ни крупные новостные шоки (где агент новостей должен показать реальную ценность), ни затяжные флэты с высокой волатильностью. Для продакшен-внедрения прогоните систему минимум на истории за 6–12 месяцев и на форвард-тесте. Реальные вызовы API важны: задержки и недоступность модели меняют поведение системы в ситуациях, которые бэктестер не моделирует.
Отдельная оговорка: тестер MetaTrader 5 не эмулирует реальные LLM-ответы — при бэктесте запросы идут к живому серверу. Это означает, что результаты отражают поведение модели на текущий момент, и после её обновления характеристики могут измениться. Это нормальная особенность систем на базе LLM, с которой нужно жить: раз в квартал перепрогонять бенчмарк и сверять метрики.
Заключение
Читатель, дошедший до этой страницы, получил нечто большее, чем набор кода. Он получил новую архитектурную парадигму: промпт для LLM — это не конфигурационный файл, который пишут один раз и забывают. Это обучаемый компонент торговой системы, который должен эволюционировать вместе с рынком.
Мы построили замкнутый цикл из четырёх звеньев: исполнительный агент генерирует сигналы по текущему промпту, оценщик связывает каждый сигнал с реальным рыночным результатом, мета-LLM анализирует паттерны ошибок и предлагает улучшенный промпт, советник MQL5 получает более качественные сигналы и генерирует новые данные для следующего витка оптимизации. Это не просто автоматизация — это самосовершенствующаяся система с рефлексией на уровне инструкций.
Практическая ценность подхода подтверждается логикой, а не случайностью. Мета-промптинг адресует фундаментальную проблему: человек-промпт-инженер не может отслеживать тысячи рыночных ситуаций и адаптировать под них инструкции в реальном времени. LLM-оптимизатор делает именно это — методично, масштабируемо, без усталости.
В апреле 2026 года, когда языковые модели стали стандартным инструментом в арсенале алготрейдера, конкурентное преимущество смещается с вопроса «использовать ли LLM» на вопрос «насколько умно вы её используете». Статичный промпт — это статичная стратегия. Система, которая оптимизирует собственные инструкции на основе реальных результатов, — это стратегия, которая учится.
В следующих публикациях мы углубим архитектуру: реализуем A/B-тестирование параллельных версий промптов на живом рынке, добавим контекстно-зависимую маршрутизацию — разные промпты для разных рыночных режимов — и интегрируем систему с архитектурой персон-трейдеров, описанной в предыдущей статье. Две системы вместе образуют торгового агента, который не только адаптирует стиль торговли, но и адаптирует инструкции для своего аналитического инструмента.
Рынок оптимизирует всех участников. Пора, чтобы система оптимизировала саму себя.
| Название файла | Содержание файла |
|---|---|
| llm_server_metaprompt.py | Сервер системы |
| EA_MetaPrompt.mq5 | Советник и исполнитель системы |
| winhttp.mqh | Библиотека связи советника и сервера - мы взяли ее из этой статьи |
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
От CPU к GPU в MQL5: практическая схема OpenCL для ускорения исследований, оптимизаций и паттернов
Создание интеллектуального торгового менеджера в MQL5: Автоматизация перевода в безубыток, трейлинг-стопа и частичного закрытия позиции
Нейросети в трейдинге: Оценка риска по несогласованности представлений (ReGEN-TAD)
Освоение быстрых сделок: Преодоление паралича исполнения
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования