Предыстория стратегии
В начале этого года в сообществе широко распространилась новость о том, что кто-то на Polymarket за одну ночь заработал на квартиру. Из любопытства я решил поискать конкретные торговые рамки, но обнаружил, что все статьи были просто кликбейтом, ни одной строчки кода. Пришлось делать всё самому.
Поначалу было довольно туманно. Polymarket — крупнейшая на сегодняшний день децентрализованная платформа для рынков предсказаний, на ней размещено более 60 000 активов, охватывающих практически все предсказуемые события, такие как политика, спорт, макроэкономика и другие. Каждый актив по сути является бинарным опционным контрактом — если исход наступает, цена сходится к 1; если нет, падает до нуля. Если говорить о самом прибыльном, то это, конечно же, инсайдерская информация. Но обычный человек не знает, сколько твитов на следующей неделе опубликует Маск, не знает, снизит ли ФРС ставку в следующий раз, и у него нет времени и сил следить за графиками, анализировать рынок и принимать решения.
Тогда я подумал: а что если сменить угол зрения — не гоняться за информацией, а следить за деньгами. Люди, обладающие информационным преимуществом, будут размещать позиции заранее, и их покупки обязательно оставят следы в цене и объёме. Даже не зная «инсайда», можно по поведению средств сделать вывод, не закладывается ли кто-то тихо. Следуя этой идее, я собрал многоролевую интеллектуальную аналитическую торговую систему, объединившую поиск сигналов на свечных графиках, проверку настроений, принятие решений ИИ и автоматическое исполнение ордеров с контролем рисков в единый конвейер.
Архитектура системы
Вся система работает на FMZ Workflow и разделена на две параллельные ветви выполнения — основную и вспомогательную, которые полностью развязаны и не блокируют друг друга.
| Ветвь | Частота запуска | Ответственный модуль | Основная задача |
|---|---|---|---|
| Основная торговая | Каждые 10 минут | Фильтрация активов → Циклическая обработка → AI-агент → Исполнение сделок | Поиск возможностей, размещение ордеров |
| Вспомогательная торговая | Каждые 30 секунд | Стоп-лосс / тейк-профит | Мониторинг позиций, трейлинг-стоп, погашение при экспирации |
Основная линия отвечает за «поиск возможностей и открытие позиций». Каждый цикл включает четыре последовательных этапа: предварительный отбор → обнаружение аномалий на свечах → поиск новостей → многоролевой анализ ИИ, в итоге генерируется сигнал на покупку и передаётся на исполнение. Вспомогательная линия занимается только «существующими позициями» и работает непрерывно с периодом 30 секунд, независимо от того, есть ли текущие задачи в основной линии.
Такое разделение решает реальную проблему: цены на рынках предсказаний меняются непрерывно, но анализ активов — это вычислительно ёмкая и медленная задача. Если бы мониторинг стоп-лоссов был встроен в основную линию, то при медленной работе ИИ реакция на стоп-лосс сильно запаздывала бы. Независимое планирование — необходимое инженерное решение.
Подробное описание модулей
Предварительный отбор: сначала отсеиваем мусорные активы
Невозможно проанализировать 60 000 активов по одному, поэтому первый этап — это чисто правила для фильтрации. Цель одна: быстро отбросить заведомо неинтересные инструменты и оставить вычислительные ресурсы для ценных.
Условия отбора включают: минимальную ликвидность (слишком низкая означает трудности с покупкой/продажей), минимальный 24-часовой объём торгов (активы с почти нулевым объёмом не дают референсных значений), максимальный спред (большой спред означает высокие проскальзывания), максимальную конкуренцию, а также ценовой диапазон — рассматриваем только активы с подразумеваемой вероятностью от 5% до 50%. Слишком высокие уже полностью оценены, потенциал роста ограничен; слишком низкие вызывают сомнения в достоверности, ближе к лотерее, чем к арбитражу.
После прохождения фильтра одно и то же событие обычно имеет два направления: Yes и No. Стратегия оставляет только то, у которого цена ниже, так как эластичность коэффициентов на дешёвой стороне выше, а потенциальная доходность больше.
javascript
let truePrice = parseFloat(outcomePrices[outcomeIndex] || 0)
if (truePrice < CONFIG.MIN_TRUE_PRICE || truePrice > CONFIG.MAX_TRUE_PRICE) continue
let isActive = (
info.active === true && info.approved === true &&
info.closed === false && info.archived === false &&
info.acceptingOrders === true && info.pendingDeployment === false
)
if (!isActive) continue
let spreadPct = (ask - bid) / ask
if (spreadPct > CONFIG.MAX_SPREAD_PCT) continue
// Для одного события оставляем только направление с более низкой ценой
if (!marketMap[baseSymbol]) {
marketMap[baseSymbol] = entry
} else if (truePrice < marketMap[baseSymbol].price) {
marketMap[baseSymbol] = entry
}
Все пороговые значения фильтрации выведены как внешние параметры через переменные $vars, что позволяет настраивать их под свой риск-профиль и объём средств без изменения кода.
Обнаружение аномалий на свечных графиках: поиск следов денег
После грубой фильтрации для каждого прошедшего актива загружаются последние 240 свечей на 60-минутном таймфрейме, и запускается пять типов детекции аномалий. Это основной сигнальный слой стратегии и самое принципиальное отличие от подхода «просто отправить событие на анализ ИИ» — мы не угадываем исход события, мы ищем поведение средств.
Медленный подъём: суммарный рост за последние 120 свечей превышает 5%, но максимальный рост за одну свечу ниже 1,5%. Эта характеристика описывает ситуацию, когда деньги намеренно сдерживают темп, чтобы не привлекать внимание.
Линейный рост объёма: линейная регрессия по последовательности объёмов за последние 60 свечей; наклон положителен и R² больше 0,5. Это означает, что объём устойчиво и равномерно растёт, а не является случайным импульсом. R² — ключевой показатель, он отсеивает шумовые случаи «одна свеча с огромным объёмом, остальные тихие».
Сужение откатов: вычисляется максимальная глубина каждого отката после исторического максимума; если средняя глубина недавних откатов составляет менее 60% от ранних, это говорит о снижении продавленного давления и стабилизации позиций — кто-то подхватывает. Для каждого отката непрерывно отслеживается минимальная цена, расчёт ведётся по полной амплитуде.
Прорыв боковика: отклонение MA60 и MA120 менее 2% (боковик), а текущая цена превышает MA60 более чем на 3% — интерпретируется как прорыв предыдущего уровня сопротивления.
Всплеск объёма: средний объём за последние 5 свечей превышает базовый средний объём за последние 60 свечей в 2,5 раза — интерпретируется как ускорение входа средств.
javascript
function linearRegression(values) {
let n = values.length
let sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0
for (let i = 0; i < n; i++) {
sumX += i; sumY += values[i]
sumXY += i * values[i]; sumX2 += i * i
}
let slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX)
let intercept = (sumY - slope * sumX) / n
let meanY = sumY / n
let ssTot = 0, ssRes = 0
for (let i = 0; i < n; i++) {
ssTot += Math.pow(values[i] - meanY, 2)
ssRes += Math.pow(values[i] - (slope * i + intercept), 2)
}
let r2 = ssTot === 0 ? 0 : 1 - ssRes / ssTot
return { slope, intercept, r2 }
}
function detectAnomaly(closes, volumes) {
if (closes.length < 60) return null
let anomalies = [], score = 0
let slice120 = closes.slice(-Math.min(120, closes.length))
// ── Аномалия 1: Медленный подъем ──────────────────────
let totalChange = (slice120[slice120.length - 1] - slice120[0]) / slice120[0]
let maxSingle = 0
for (let i = 1; i < slice120.length; i++) {
let change = Math.abs(slice120[i] - slice120[i - 1]) / slice120[i - 1]
if (change > maxSingle) maxSingle = change
}
if (totalChange > 0.05 && maxSingle < 0.015) {
score++
anomalies.push(`Медленный подъем: общий рост ${(totalChange * 100).toFixed(1)}% макс. одна свеча ${(maxSingle * 100).toFixed(2)}%`)
}
// ── Аномалия 2: Линейный рост объема ──────────────────
let volSlice = volumes.slice(-60)
let volReg = linearRegression(volSlice)
if (volReg.slope > 0 && volReg.r2 > 0.5) {
score++
anomalies.push(`Линейный рост объема: наклон ${volReg.slope.toFixed(4)} R² ${volReg.r2.toFixed(2)}`)
}
// ── Аномалия 3: Сужение откатов (отслеживаем каждый минимум отката, рассчитываем полную глубину) ────
let pullbacks = [], localHigh = closes[0]
let inPullback = false, pullbackLow = closes[0]
for (let i = 1; i < slice120.length; i++) {
if (closes[i] > localHigh) {
if (inPullback) {
pullbacks.push((localHigh - pullbackLow) / localHigh)
inPullback = false
}
localHigh = closes[i]
} else if (closes[i] < localHigh * 0.99) {
if (!inPullback) { inPullback = true; pullbackLow = closes[i] }
else if (closes[i] < pullbackLow) { pullbackLow = closes[i] }
}
}
if (pullbacks.length >= 3) {
let first = pullbacks.slice(0, Math.floor(pullbacks.length / 2))
let last = pullbacks.slice(Math.floor(pullbacks.length / 2))
let firstAvg = first.reduce((a, b) => a + b, 0) / first.length
let lastAvg = last.reduce((a, b) => a + b, 0) / last.length
if (lastAvg < firstAvg * 0.6) {
score++
anomalies.push(`Сужение откатов: ранние ${(firstAvg * 100).toFixed(2)}% недавние ${(lastAvg * 100).toFixed(2)}%`)
}
}
// ── Аномалия 4: Пробой боковика ──────────────────────
let ma60 = closes.slice(-60).reduce((a, b) => a + b, 0) / 60
let ma120 = closes.length >= 120
? closes.slice(-120).reduce((a, b) => a + b, 0) / 120
: ma60
let currentPrice = closes[closes.length - 1]
if (Math.abs(ma60 - ma120) / ma120 < 0.02 && currentPrice > ma60 * 1.03) {
score++
anomalies.push(`Пробой боковика: MA60=${ma60.toFixed(3)} MA120=${ma120.toFixed(3)} текущая=${currentPrice.toFixed(3)}`)
}
// ── Аномалия 5: Всплеск объема ───────────────────────
let recentVol = volumes.slice(-5).reduce((a, b) => a + b, 0) / 5
let baseVol = volumes.slice(-65, -5).reduce((a, b) => a + b, 0) / 60
if (baseVol > 0 && recentVol > baseVol * 2.5) {
score++
anomalies.push(`Всплеск объема: средний объем за последние 5 свечей в ${(recentVol / baseVol).toFixed(1)} раз больше базового`)
}
return { score, anomalies }
}
Каждая обнаруженная аномалия добавляет 1 балл. Максимум — 5 баллов. К следующему этапу переходят только инструменты с суммой не менее 2 баллов. Одиночный сигнал может быть случайным шумом — только наложение нескольких сигналов указывает на определённую достоверность действий капитала. Это ключевая гипотеза логики анализа свечных графиков.
Поиск новостей: найти фундаментальное объяснение аномалии
Свечной сигнал — это доказательство того, что «капитал движется». Но почему он движется и насколько обосновано это движение — необходимо проверить с помощью информационного поля. На этом этапе для каждого инструмента с аномалиями на свечах ищутся новости, чтобы понять, есть ли соответствующее событие, поддерживающее сигнал.
Используется API Brave Search, предоставляющий 2000 бесплатных запросов в месяц — этого достаточно для ритма основного цикла с интервалом 10 минут. Ключевые слова поиска извлекаются из symbol: удаляются суффиксы направления и разделители, после чего восстанавливается читаемое описание события.
javascript
const q = item.symbol
.replace("_USDC.No", "")
.replace("_USDC.Yes", "")
.replace(/-/g, "+")
const rawResponse = HttpQuery(
"https://api.search.brave.com/res/v1/web/search?q=" + q,
{ method: "GET", headers: { "X-Subscription-Token": CONFIG.BRAVE_API_KEY } }
)
// Traverses all returned sections (web, news, etc.) and extracts structured fields uniformly
Object.keys(data).forEach(key => {
const section = data[key]
if (section && section.results && Array.isArray(section.results)) {
section.results.forEach(r => {
news.push({ type: key, title: r.title, description: r.description, age: r.age })
})
}
})
Найденные новости упаковываются вместе с данными свечного графика и передаются на следующий этап AI-анализа — две линии одновременно верифицируют друг друга, что гораздо надёжнее, чем полагаться только на одну из них.
Многоаспектный AI-анализ: четыре точки зрения для предотвращения самоубеждения
После подготовки данных начинается этап AI-анализа. Модель выбирает, какие роли задействовать, в зависимости от потребностей. В промпте определены четыре независимые роли — это ключевое архитектурное решение всей системы, которое стоит осветить отдельно.
Если дать одному AI одновременно анализировать и свечной график, и новости, он легко начнёт подтверждать одно другим, приходя к выводам, которые кажутся обоснованными, но на деле лишены независимой верификации — подкрепляя сигналы капитала новостями, а новости — сигналами капитала, и в итоге выдавая самоусиливающийся результат «я считаю, можно покупать». Разделение ролей принудительно изолирует источники информации: каждая роль видит только свои данные и выдаёт выводы только по своему измерению, а итоговая принимающая роль видит только готовые заключения, а не сам процесс.
Роль первая: Аналитик по свечному графику и капиталу. Не смотрит новости, анализирует только аномалии цены и объёма, определяет, поступают ли реальные деньги на покупки. Выдаёт силу сигнала капитала (сильный/средний/базовый).
Роль вторая: Аналитик по новостям и общественному мнению. Не смотрит свечной график, оценивает только информационное поле, определяет, есть ли позитивные события, ещё не заложенные в рыночную цену. Выдаёт уровень поддержки новостями (сильная/слабая/отсутствует).
Роль третья: Аналитик по оценке цены. Сводит выводы первых двух ролей, оценивает вероятный диапазон истинной вероятности события, сравнивает с текущей рыночной ценой и даёт оценку: недооценено/справедливо/переоценено. Эта роль имеет встроенное правило весов по дате экспирации: если до экспирации больше 7 дней, оценка обычная; если до экспирации 3 дня и менее, а рыночная цена выше 0.15, диапазон вероятности принудительно сужается и запрещается вывод «сильно недооценено». Обоснование простое: вблизи экспирации участники рынка владеют более актуальной информацией, чем обучающие данные AI. В этом случае AI должен уступать рыночной цене.
Роль четвёртая: Аналитик по принятию решений. Сводит выводы предыдущих трёх ролей по матрице решений и выдаёт итоговое действие. Логика матрицы: сигнал капитала — предпосылка, новости — верификация; их комбинация определяет уровень уверенности. Однако, каким бы высоким ни был уровень уверенности, если оценка цены не достигает «недооценено», действие не предпринимается.
Верификация новостями здесь не значит «догонять информацию»; она используется для оценки того, подкреплён ли поток капитала фундаментальными факторами. Когда инсайдерский капитал накапливает позиции, информация не появляется в открытых новостях. Но если сигнал капитала сочетается с новостной поддержкой, это означает, что информация находится на стадии распространения, а рынок ещё не полностью её заложил в цену — именно это окно стратегия и стремится захватить.
| Сигнал капитала \ Новостная поддержка | Сильная верификация | Слабая верификация | Нет верификации |
|---|---|---|---|
| Сильный (оценка 4~5) | Покупать, уверенность=высокая | Покупать, уверенность=средняя | Не действовать |
| Средний (оценка 3) | Покупать, уверенность=средняя | Наблюдать | Не действовать |
| Базовый (оценка 2) | Наблюдать | Не действовать | Не действовать |
Формат вывода AI — строгий JSON, включающий заключения каждой роли, диапазон истинной вероятности, оценку цены, итоговое решение, уровень уверенности и список рисков, готовый для потребления downstream-узлами исполнения.
Исполнение сделок: покупать только лучший вариант
После того как AI оценивает все инструменты в батче, исполнительный слой не выставляет ордера на все сигналы покупки — система выбирает только один с наивысшим рейтингом. Порядок сортировки: уровень уверенности (высокий > средний > низкий) → оценка цены (сильно недооценено > недооценено) → ширина диапазона истинной вероятности (чем уже, тем более определённым считается вывод).
javascript
const buySignals = signals.filter(s => s.signal === 'buy')
bestSignal = buySignals.sort((a, b) => {
const confDiff = (confidenceRank[b.confidence] || 0) - (confidenceRank[a.confidence] || 0)
if (confDiff !== 0) return confDiff
const assessDiff = (assessmentRank[b.priceAssessment] || 0) - (assessmentRank[a.priceAssessment] || 0)
if (assessDiff !== 0) return assessDiff
// Чем уже диапазон, тем определённее; выбираем более узкий
const aWidth = (a.trueProb.max || 0) - (a.trueProb.min || 0)
const bWidth = (b.trueProb.max || 0) - (b.trueProb.min || 0)
return aWidth - bWidth
})[0]
После выбора инструмента система в реальном времени получает цену Ask из стакана, проверяет достаточность средств и выставляет лимитный ордер. После отправки ордера система опрашивает его статус, ожидая до 30 секунд; при превышении лимита ордер отменяется во избежание накопления незакрытых заявок. Если цена Ask, умноженная на размер позиции, оказывается ниже минимального порога в 1 USDC, система автоматически округляет вверх до минимального исполнимого размера по формуле ceil(1/askPrice).
Модуль стоп-лосса: скользящий стоп + абсолютная подстраховка
Второстепенный цикл выполняется каждые 30 секунд. Логика в два шага: сначала обработка погашения по истечении, затем проверка скользящего стопа по активным позициям.
Polymarket имеет особый механизм — после экспирации контракта средства не возвращаются на счёт автоматически; требуется активный вызов redeem. В начале каждого вторичного цикла стратегия сканирует все погашаемые позиции и массово освобождает средства, чтобы избежать длительной блокировки капитала.
Основная идея скользящего стопа — конструкция из двух линий: скользящая линия стопа (историческая максимальная цена bid минус фиксированное падение LOSS_AMOUNT) и абсолютная подстраховка (цена входа × 50%). Фактическая цена стопа берётся как большая из двух величин.
javascript
function calcStopPrice(entryPrice, maxPrice) {
const trailingStopPrice = maxPrice - CONFIG.LOSS_AMOUNT
const absoluteStopPrice = entryPrice * CONFIG.FALLBACK_STOP_RATIO // фиксировано 0.50
const usingFallback = trailingStopPrice < absoluteStopPrice
const effectiveStopPrice = usingFallback ? absoluteStopPrice : trailingStopPrice
return { effectiveStopPrice, trailingStopPrice, absoluteStopPrice, usingFallback }
}
Эта двухлинейная конструкция соответствует двум типичным сценариям удержания позиции: в фазе прибыли скользящий стоп движется вверх вместе с ценой, фиксируя плавающую прибыль; если же позиция сразу уходит в минус, скользящий стоп может оказаться отрицательным или близким к нулю, и тогда вступает абсолютная подстраховка (цена входа × 50%), не давая позиции упасть до нуля без выхода. Каждая линия выполняет свою функцию, и обе необходимы.
При срабатывании стоп-лосса выставляется рыночный ордер на продажу, кеш исторического максимума цены для данного инструмента очищается, и система ожидает следующего сигнала на открытие. Приборная панель в реальном времени показывает расстояние от каждой позиции до уровня стоп-лосса, цветом обозначая степень угрозы для облегчения ручной проверки.
Ключевые проектные решения
Почему несколько ролей, а не единый промпт? Этот вопрос уже частично освещался в модуле анализа, но стоит сказать ещё чётче. Проблема единого промпта не только в «самоубеждении»; есть и более скрытая опасность: когда AI обрабатывает одновременно данные свечей и новостные тексты, он склонен объяснять первое вторым, «рационализируя» аномалии графика как неизбежное следствие новостей, а не оценивая их как независимые сигналы. Разделение ролей принудительно вводит две независимые информационные цепочки, и только когда обе цепочки указывают в одном направлении, доверие к результату становится подлинным.
Почему каждый раунд выбирается только один лучший инструмент, а не распределённое владение? Распределённое владение имеет веское теоретическое обоснование в традиционных финансах, но в предиктивных рынках логика иная. Результаты инструментов на предиктивных рынках бинарны, и риск падения до нуля реален. Если одновременно держать три инструмента со средней уверенностью, и два из них упадут до нуля, а третий вырастет, портфель в целом может не окупиться. Выбранная стратегия такова: лучше сконцентрироваться на точности, чем разбавлять разнородные сигналы количеством. Это компромисс, который не обязан быть оптимальным, но логически непротиворечив.
Почему правило весов по дате экспирации сделано жёстким ограничением? Вблизи экспирации рыночная цена отражает ожидания участников в реальном времени, и значительная доля этих участников владеет более свежей информацией, чем обучающие данные AI. Если AI в этой ситуации всё равно выдаёт «цена сильно недооценена, следует покупать», это, скорее всего, означает, что его информация уже устарела. Сделать это правило ограничением, а не рекомендацией, пришлось потому, что полагаться на способность AI самостоятельно распознать «мои данные уже неактуальны» нереалистично — необходимо внешнее принуждение.
Практические наблюдения
Судя по фактическим записям работы, коэффициент воронки стратегии достаточно крутой. В одном из запусков из 141 предварительно отобранного актива после обнаружения аномалий свечей осталось 3, и в итоге только один удовлетворил всем трём критериям — сигнал по капиталу, новостная поддержка и оценка цены — и вызвал открытие позиции; остальные два были пропущены из-за «разумной» оценки цены и недостаточной общей оценки соответственно. Это указывает на то, что стратегия в целом консервативна — лучше оставаться без позиции, чем снижать стандарты. В окна со спокойным рынком и отсутствием явных событий в настроениях система может за весь день не генерировать ни одного сигнала на покупку. Это конструктивное решение, а не ошибка. Также встречались случаи, когда аномалии свечей впоследствии оказывались ложными пробоями — капитал кратко входил и выходил, а трейлинг-стоп вмешивался и закрывал позицию. Анализ новостей также содержит шум: отчёты о соответствующих событиях могут быть запаздывающими или искажёнными, и один лишь AI не может полностью их распознать.
Панель мониторинга в реальном времени отображает баланс счёта, состояние позиций (текущие Bid/Ask, максимальная цена, расстояние от просадки до стоп-линии, режим стоп-лосса) и детали решений AI для удобства ручной проверки.
Заключение
Честно говоря, эта система — скорее рабочая концепция, нежели готовая к использованию машина для печати денег. Ключевая логика — трассировка поведения капитала, двухполосная проверка настроений и многоролевая оценка AI — имеет определённую обоснованность в теории, но на реальном рынке качество сигналов неоднородно, суждения AI также могут ошибаться, и всегда существует риск схождения цены к истечению срока.
Есть много мест для улучшения: торговые активы можно дополнительно сфокусировать на знакомых областях, подсказки для AI можно пересмотреть под свой стиль торговли, веса и пороги для пяти типов аномалий свечей имеют пространство для настройки, а коэффициент стоп-лосса также можно итерировать на основе реальных показателей позиций. Весь код открыт, можете изменять.
Предупреждение о рисках
Прогнозирование рыночных активов имеет бинарный результат, позиции сопряжены с риском полной потери стоимости. Сигналы аномалий свечей и выводы AI-анализа не являются инвестиционными рекомендациями, исторические показатели не гарантируют будущую доходность. Стратегия не прошла долгосрочную верификацию на реальном рынке, параметры настроек существенно влияют на результаты. Перед использованием данной стратегии для реальной торговли, пожалуйста, полностью понимайте правила платформы Polymarket, механизм истечения контрактов и риски ликвидности, а также самостоятельно оценивайте способность нести потери капитала.
Исходный код стратегии: Polymarket 多角色智能分析交易策略
- 1




