Исходный код опубликован в этом репозитории на 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/