Гринд ликвидности с помощью ИИ

от автора

Исходный код опубликован в этом репозитории на GitHub

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

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

Автор канала — настоящий мастер своего дела: в январе 2026 года все 8 из 8 SHORT-сигналов по TRXUSDT резко двинулись в противоположную сторону в течение 45 минут после публикации. Но действительно ли это именно автор как человек?

Расчехляем тамагочи

Множители шагов между уровнями тейк-профита всегда одинаковы: ×1.52 → ×1.74 → ×1.47 → ×1.50. Соотношение T5/SL всегда равно 1.34. Эта рекомендация явно сгенерирована торговым алгоритмом. Но это ещё не самое интересное.

За пятнадцать минут до каждого поста на графике появляется аномалия объёма: большая синяя свеча в 10:00. Пост публикуется в 10:15. Затем ещё одна свеча ровно в 10:30.

SHORT-рекомендация с плечом x25 размещается точно на низу 4-часовой свечи (зелёная линия).

Инвертируем сигнал и забираем ликвидацию

Используя приведённые выше критерии, мы инвертируем рекомендацию и входим против предложенного направления. Для сигнала №7 движение произошло, однако его не хватило, чтобы перевести позицию в безубыток.

Вот ценовой график с тем, что произошло после каждой SHORT-рекомендации с плечом x25. Цена всегда двигалась в противоположную сторону, вопрос лишь как сильно. Тут движение принёсло 0.5% от входа, бот не вышел так как комиссия 0.4%

PNL на скриншотах чистый, комиссия биржы учтена

Результаты

  • Portfolio PNL: 8.54%

  • Portfolio Sharpe: 1.08

  • Avg Peak PNL: 1.44%

  • Avg Max Drawdown PNL: −0.48%

Исходный код

addStrategySchema({  strategyName: "jan_2026_strategy",  getSignal: async (symbol, when, currentPrice) => {    const signal = getActiveSignal(symbol, when);    if (!signal) {      return null;    }    const close_1m = await getClosePrice(symbol, "1m");    if (close_1m < signal.entry.from || close_1m > signal.entry.to) {      return null;    }    const [close_4h_prev, close_4h_cur] = await getCandles(symbol, "4h", 2);    const range_high = Math.max(close_4h_prev.high, close_4h_cur.high);    const range_low = Math.min(close_4h_prev.low, close_4h_cur.low); // fix: min, not max    const range_middle = (range_high + range_low) / 2; // fix: division applies to sum    const position = close_1m > range_middle ? "short" : "long";    return {      position,      ...Position.moonbag({        position,        currentPrice,        percentStopLoss: HARD_STOP,      }),      minuteEstimatedTime: 24 * 60,      note: signal.note,    };  },});listenActivePing(async ({ symbol, data }) => {  const peakProfitDistance = await getPositionHighestProfitDistancePnlPercentage(symbol);  const currentProfit = await getPositionPnlPercent(symbol);  if (currentProfit < 0) {    return;  }  if (peakProfitDistance < TRAILING_TAKE) {    return;  }  Log.info("position closed due to the trailing take", {    symbol,    data,  });  await commitClosePending(symbol, {    id: "unknown",    note: str.newline(      "# Позиция закрыта по trailing take",    ),  });});listenActivePing(async ({ symbol, data }) => {  const peakProfitCost = await getPositionHighestPnlPercentage(symbol);  const peakProfitMinutes = await getPositionHighestProfitMinutes(symbol);  if (peakProfitCost < PEAK_STALENESS_SINCE_PROFIT) {    return;  }  if (peakProfitMinutes < PEAK_STALENESS_SINCE_MINUTES) {    return;  }  Log.info("position closed due to the peak staleness", {    symbol,    data,  });  await commitClosePending(symbol, {    id: "unknown",    note: str.newline(      "# Позиция закрыта по peak staleness",    ),  });});

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

Не является торговой рекомендацией

ссылка на оригинал статьи https://habr.com/ru/articles/1028592/