Проблема не в торговых стратегиях, а в том, что тысячи копий одного и того же алгоритма от Claude/Grok/ChatGPT торгуют друг против другЧто посрёшь, то пожрёшь Проблема не в торговых стратегиях, а в том, что тысячи копий одного и того же алгоритма от Claude/Grok/ChatGPT торгуют друг против другЧто посрёшь, то пожрёшь

Хаос второго порядка: Как алгоритмические торговые боты играют сами против себя в убыток

Проблема не в торговых стратегиях, а в том, что тысячи копий одного и того же алгоритма от Claude/Grok/ChatGPT торгуют друг против друг

Что посрёшь, то пожрёшь Биогеоценоз
Что посрёшь, то пожрёшь Биогеоценоз

Хаос первого vs второго порядка

Определим термины.

Хаос первого порядка: рынок непредсказуем, но участники не знают паттернов. Можно найти преимущество через анализ. Классические индикаторы работают.

Хаос второго порядка: рынок непредсказуем ПОТОМУ, что все знают паттерны. Все торгуют одинаково. Боты реагируют на ботов. Стратегии СОЗДАЮТ шум, который пытаются торговать.

Мы сейчас живём во втором.

Один алгоритм против самого себя

Вот что происходит каждую секунду на криптобиржах:

09:00:00.000 - Цена BTC падает до $87,300 09:00:00.050 - 1000 ботов freqtrade видят: RSI < 30 Каждый бот думает: "RSI в зоне перепроданности → сигнал на покупку" 09:00:00.100 - Все одновременно отправляют ордера BUY 09:00:00.150 - Цена подскакивает до $87,450 (они купили друг у друга) 09:00:00.200 - RSI мгновенно прыгает до 75 Те же боты: "RSI в зоне перекупленности → сигнал на продажу" 09:00:00.250 - Все одновременно отправляют ордера SELL 09:00:00.300 - Цена обратно до $87,300

Итоговый счёт:

  • Каждый бот: -0.1% (спред) -0.04% (комиссии) = -0.14%

  • Биржа: +0.04% × 1000 = +40 USD

Это не разные боты конкурируют. Это буквально один алгоритм играет сам против себя в убыток.

Почему это происходит

Посмотрите на типичную стратегию из туториала freqtrade:

class RSIStrategy(IStrategy): def populate_indicators(self, dataframe, metadata): dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) return dataframe def populate_entry_trend(self, dataframe, metadata): dataframe.loc[ (dataframe['rsi'] < 30), # Перепроданность 'enter_long'] = 1 return dataframe def populate_exit_trend(self, dataframe, metadata): dataframe.loc[ (dataframe['rsi'] > 70), # Перекупленность 'exit_long'] = 1 return dataframe

Эта стратегия:

  1. Пропагандируется Claude, Grok, ChatGPT как идеальный действительно работающий вариант заработка

  2. Скопирована в тысячи ботов

  3. Запускается на одинаковых таймфреймах (5m, 15m)

  4. Использует идентичные параметры (RSI 14, пороги 30/70)

Результат: тысячи идентичных экземпляров одного кода торгуют на одном рынке.

Математика само-конфликта

Разберём на реальных числах:

"Условия:" - 1000 ботов с идентичной RSI-стратегией - Каждый торгует 0.01 BTC - Спред: 0.05% - Комиссия: 0.04% (тейкер) "Сценарий RSI-разворот": 1. RSI падает до 29 2. Все 1000 ботов отправляют BUY 0.01 BTC 3. Общий объём: 10 BTC куплено за 100мс 4. Цена растёт на 0.15% от импакта 5. RSI прыгает до 72 (боты сами его подняли) 6. Все 1000 ботов отправляют SELL 0.01 BTC 7. Цена падает на 0.15% обратно "PnL на бота:" - Вход: куплено на 0.05% выше мида (спред) - Выход: продано на 0.05% ниже мида (спред) - Комиссии: 0.04% × 2 = 0.08% - Импакт цены: ~0% (взаимно погасился) - Итого: -0.18% за раунд-трип "За 100 таких циклов в день:" - Каждый бот: -18% в день - Биржа: +0.08% × 1000 × 100 = +8000 USD в день

Но мой бот использует другие индикаторы!

Нет, не использует. Вот что применяет 90% розничных ботов:

Классика жанра
Классика жанра

Боты комбинируют эти индикаторы, но комбинации тоже стандартные:

  • RSI + MACD = самая популярная

  • BB + RSI = вторая по популярности

  • EMA cross + Volume = третья

Вы не уникальны. Ваша "секретная" стратегия это копипаста из топ-5 туториалов.

Предсказуемые стоп-лоссы

Это боль. Размещение стопов. 90% ботов используют:

stoploss = -0.10 # Фиксированные 10% # или stoploss = atr * 1.5 # На основе ATR # или stoploss = last_swing_low - 0.5% # На основе структуры

Маркет-мейкеры видят эти кластеры стопов и охотятся за ними:

"Типичная ситуация:" 1. Цена: $87,500 2. Боты вошли в LONG на прорывe у $87,450 3. Стопы: $87,450 - ATR(1.5) ≈ $87,280 4. Маркет-мейкер видит: кластер стопов @ $87,280-$87,300 5. Толкает цену до $87,275 6. Стопы срабатывают = каскад продаж 7. Покупает дешевле $87,250 8. Цена возвращается к $87,400 "Боты:" -1.5% каждый "Маркет-мейкер:" +0.3%

Спираль смерти

Вот бесконечный цикл, что происходит:

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

Что делать: LLM вместо императивной логики

Как вариант, использовать backtest-kit с Ollama для генерации сигналов. LLM анализирует контекст целостно, вместо того чтобы дёргаться на каждое пересечение линий.

Схема сигнала

import { str } from "functools-kit"; import { z } from "zod"; export const SignalSchema = z.object({ position: z .enum(["long", "short", "wait"]) .describe( str.newline( "Направление позиции (ВСЕГДА обязательно):", "long: рынок показывает выровненные бычьи сигналы", "short: рынок показывает выровненные медвежьи сигналы", "wait: конфликтующие сигналы между таймфреймами" ) ), description: z .string() .describe( str.newline( "Профессиональная рекомендация в ОДНОЙ СТРОКЕ:", "Для LONG: стратегия, причина, план (купить → продать выше)", "Для SHORT: стратегия, причина, план (продать → купить ниже)", "Для WAIT: почему и когда входить" ) ), reasoning: z .string() .describe( str.newline( "Техническое обоснование (4-6 предложений):", "Выравнивание сигналов по таймфреймам", "Качество и динамика объёма", "Моментум, наклон, скрытые дивергенции", "Ключевые уровни поддержки и сопротивления", "ОБЯЗАТЕЛЬНО: выбор уровней TP/SL с техническим обоснованием" ) ), price_open: z.number().describe("Цена входа в USD"), price_stop_loss: z.number().describe( str.newline( "Цена стоп-лосса в USD", "Для LONG: ниже price_open", "Для SHORT: выше price_open", "НИКОГДА не ставить SL без технического обоснования" ) ), price_take_profit: z.number().describe( str.newline( "Цена тейк-профита в USD", "Для LONG: выше price_open", "Для SHORT: ниже price_open" ) ), minute_estimated_time: z.number().describe( str.newline( "Время до достижения TP в минутах", "На основе ATR, ADX, MACD, Momentum" ) ), risk_note: z .string() .describe( str.newline( "Описание рисков (ВСЕГДА обязательно):", "1. Манипуляции китов (фейковые прорывы, пин-бары)", "2. Ордербук (стены, спуфинг, дисбаланс)", "3. Временные факторы (сессия, ликвидность)", "4. Технические риски (дивергенции, слабый объём)", "Указывать КОНКРЕТНЫЕ числа и вероятности" ) ), });

Промпт для генерации сигнала

import { str } from "functools-kit" const SIGNAL_PROMPT = str.newline( "Анализируй рынок по прикреплённым данным.", "Минимальный процент прибыли на TP: 1%", "Соотношение риск/прибыль: не хуже 1:2", "", "Требования:", "- Таймфрейм 1m, следующие три часа", "- Рекомендация для обоих сценариев: вверх и вниз", "- Условия входа как IF-THEN с уровнями цен", "- Вход/Стоп/TP как числа в USD", "- Обязательные фильтры", "- Риск-менеджмент в конце", "- Без пересказа данных", "- Максимальная сжатость", "", "Формат:", "LONG условия: [логика с Support/Resistance]", "Entry: [USD], TP: [USD], SL: [USD]", "", "SHORT условия: [логика с Support/Resistance]", "Entry: [USD], TP: [USD], SL: [USD]", "", "Фильтры: [список]", "Риск: [правила]" );

Стратегия

import { json } from "agent-swarm-kit"; import { addStrategy } from "backtest-kit"; addStrategy({ strategyName: "llm-anti-bot", interval: "5m", riskList: [ "TakeProfitDistanceRisk", "RiskRewardRatioRisk", "LanguageModelOpinionRisk", ], getSignal: async (symbol) => { const { data, resultId, error } = await json( "SignalOutline", symbol ); if (error) throw new Error(error); if (data.position === "wait") return null; return { id: resultId, position: data.position, minuteEstimatedTime: +data.minute_estimated_time, priceStopLoss: +data.price_stop_loss, priceTakeProfit: +data.price_take_profit, priceOpen: +data.price_open, note: data.risk_note, }; }, });

Подготовка истории для LLM

import { Cache } from "backtest-kit"; import { str, trycatch } from "functools-kit"; const fetchHourHistory = Cache.fn( lib.hourCandleHistoryService.getReport, { interval: "30m" } ); const fetchFifteenMinuteHistory = Cache.fn( lib.fifteenMinuteCandleHistoryService.getReport, { interval: "5m" } ); const commitHourHistory = trycatch( async (symbol: string, history: History) => { const hourHistory = await fetchHourHistory(symbol); await history.push( { role: "user", content: str.newline( "=== 1-ЧАСОВЫЕ СВЕЧИ (последние 6 свечей) ===", "", hourHistory ), }, { role: "assistant", content: "1-часовые свечи получены.", } ); }, { fallback: () => Cache.clear(fetchHourHistory) } ); const commitFifteenMinuteHistory = trycatch( async (symbol: string, history: History) => { const data = await fetchFifteenMinuteHistory(symbol); await history.push( { role: "user", content: str.newline( "=== 15-МИНУТНЫЕ СВЕЧИ (последние 8 свечей) ===", "", data ), }, { role: "assistant", content: "15-минутные свечи получены.", } ); }, { fallback: () => Cache.clear(fetchFifteenMinuteHistory) } );

Сервис расчёта индикаторов

import { FasterRSI as RSI, FasterMACD as MACD, FasterBollingerBands as BollingerBands, FasterATR as ATR, FasterADX as ADX, // ... другие индикаторы } from "trading-signals"; function generateAnalysis(symbol: string, candles: ICandleData[]) { const rsi = new RSI(14); const macd = new MACD(new EMA(12), new EMA(26), new EMA(9)); const bollinger = new BollingerBands(20, 2.0); const atr14 = new ATR(14); const adx = new ADX(14); // ... инициализация других индикаторов const results: ILongTermRow[] = []; candles.forEach((candle, i) => { const { high, low, close } = candle; // Обновление всех индикаторов rsi.update(close, false); macd.update(close, false); bollinger.update(close, false); atr14.update({ high, low, close }, false); adx.update({ high, low, close }, false); // Пропуск до прогрева индикаторов if (i < WARMUP_PERIOD) return; // Тренд объёма: сравнение SMA(6) текущего и предыдущего периода const volumeTrend = calculateVolumeTrend(volumes, i); // Поддержка/Сопротивление: мин/макс за последние N свечей const { support, resistance } = calculatePivots(highs, lows, i); // Фибоначчи: ближайший уровень к текущей цене const fibonacciNearest = calculateFibonacciLevels(candles, i); results.push({ symbol, rsi14: rsi.getResult(), macd12_26_9: macd.getResult()?.macd, adx14: adx.getResult(), atr14: atr14.getResult(), support, resistance, fibonacciNearestLevel: fibonacciNearest.level, // ... остальные поля }); }); return results; }

Валидации

import { addOutline, dumpOutlineResult } from "agent-swarm-kit"; import { zodResponseFormat } from "openai/helpers/zod"; addOutline({ outlineName: "SignalOutline", completion: "OllamaCompletion", format: zodResponseFormat(SignalSchema, "position_open_decision"), getOutlineHistory: async ({ history, param: symbol }) => { await commitHourHistory(symbol, history); await commitFifteenMinuteHistory(symbol, history); // ... другие таймфреймы }, validations: [ { validate: ({ data }) => { if (data.position === "long") { if (data.price_stop_loss >= data.price_open) { throw new Error("LONG: SL должен быть ниже входа"); } if (data.price_take_profit <= data.price_open) { throw new Error("LONG: TP должен быть выше входа"); } } }, docDescription: "Валидация правильности цен для LONG", }, { validate: ({ data }) => { if (data.position === "short") { if (data.price_stop_loss <= data.price_open) { throw new Error("SHORT: SL должен быть выше входа"); } if (data.price_take_profit >= data.price_open) { throw new Error("SHORT: TP должен быть ниже входа"); } } }, docDescription: "Валидация правильности цен для SHORT", }, { validate: ({ data }) => { if (data.position !== "wait" && data.minute_estimated_time > 360) { throw new Error("Время до TP > 6 часов. Использовать wait"); } }, docDescription: "Валидация реалистичности времени", }, ], callbacks: { async onValidDocument(result) { await dumpOutlineResult(result); }, }, });

Почему LLM работает лучше

LLM не дёргается на каждый RSI < 30. Он видит контекст:

RSI 1m: 28 (перепроданность) RSI 1h: 55 (нейтрально) Volume: 0.3x от среднего (слабый) ADX: 15 (нет тренда)

Стандартный бот увидит RSI < 30 и купит. LLM поймёт, что это ловушка.

Вывод LLM: "WAIT - RSI перепродан на 1m, но 1h нейтрально, объём слабый. Это боты пытаются купить, а не реальная капитуляция. Ждать прорыва с объёмом или к

Спасибо за внимание!

Источник

Возможности рынка
Логотип GROK
GROK Курс (GROK)
$0.0004954
$0.0004954$0.0004954
-1.58%
USD
График цены GROK (GROK) в реальном времени
Отказ от ответственности: Статьи, размещенные на этом веб-сайте, взяты из общедоступных источников и предоставляются исключительно в информационных целях. Они не обязательно отражают точку зрения MEXC. Все права принадлежат первоисточникам. Если вы считаете, что какой-либо контент нарушает права третьих лиц, пожалуйста, обратитесь по адресу [email protected] для его удаления. MEXC не дает никаких гарантий в отношении точности, полноты или своевременности контента и не несет ответственности за любые действия, предпринятые на основе предоставленной информации. Контент не является финансовой, юридической или иной профессиональной консультацией и не должен рассматриваться как рекомендация или одобрение со стороны MEXC.