Type/to search
2
Follow
484
Followers
Многофакторные стратегии не являются прерогативой крупных компаний: исследовательская структура независимого кванта
Discussions
Created 2026-03-27 15:05:12  Updated 2026-03-31 18:40:35
 0
 397

img

一、Зачем нужна автоматическая добыча факторов

Если вы сталкивались с количественной торговлей, вы наверняка слышали термин "фактор". Что такое фактор? Проще говоря, это рыночный сигнал, выраженный через данные. Например, импульс цены, аномалии объема, полосы Боллинджера — всё это используется для прогнозирования, вырастет или упадет конкретная монета в ближайшее время.

Звучит просто, но те, кто реально занимается исследованием факторов, знают, насколько это сложно:

Глубокие знания в финансах и фундаментальная статистическая подготовка
Большой объем чистых исторических данных
Строгая структура для бэктестинга

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

В этой статье представлена система, автоматизирующая этот процесс: фиксированные интервалы, циклическое выполнение полного цикла добычи факторов → проверка → отбраковка → синтез сигналов → исполнение сделок. Машина заменяет ручную работу, позволяя стратегии всегда соответствовать изменяющейся рыночной динамике.

img


二、Общая архитектура системы

Традиционный процесс добычи факторов выглядит так: исследователь формулирует гипотезу → пишет код → запускает бэктест → отбирает → внедряет → через несколько месяцев находит, что фактор перестал работать → всё заново. Весь цикл может занимать недели или даже месяцы.

Эта система сжимает весь цикл до автоматического выполнения через фиксированный интервал:

ШагМодульОписание
Шаг 1Получение пула инструментовОтбор высоколиквидных бессрочных контрактов по объёму торгов, определение состояния рынка
Шаг 2Проверка пула факторовАнализ текущего здоровья факторов, определение направления для данного цикла
Шаг 3Генерация факторов AIВ рамках ограничений AI генерирует новые факторы-кандидаты
Шаг 4Валидация ICРасчёт информационного коэффициента на исторических данных, отбраковка неэффективных факторов
Шаг 5Фильтрация корреляции и отсев слабыхУдаление дублирующих информацию факторов, сохранение пула факторов компактным и эффективным
Шаг 6Синтез сигналов и исполнениеВзвешенный синтез скора, сигналы, превышающие порог, запускают ребалансировку

Система управляется двумя планировщиками: медленный триггер выполняет полный цикл итерации факторов с часовым интервалом; быстрый триггер опрашивает состояние позиций с секундным интервалом, обрабатывая стоп-лосс/тейк-профит и обновляя дашборд.


三、Подробное описание модулей и основной код

3.1 Получение пула инструментов

В начале каждого цикла система загружает текущие рыночные данные всех бессрочных контрактов с биржи, сортирует по объёму торгов и берёт первые N. Ликвидность — это основа эффективности фактора: у мелких монет объёмы разрежены, и любой сигнал легко искажается.

Одновременно определяется квантиль волатильности 4-часовых свечей BTC за последнее время, чтобы оценить общее состояние рынка (normal / high_vol / low_vol / volatile). Эта оценка напрямую влияет на предпочтения AI при генерации факторов.

javascript
// Отбор высоколиквидных инструментов по объёму торгов const topN = $vars.topN || 150; const tickers = exchange.GetTickers(); const filtered = tickers .filter(t => t.Symbol.endsWith('USDT.swap')) .map(t => ({ symbol: t.Symbol, quoteVolume: t.Last * t.Volume })) .sort((a, b) => b.quoteVolume - a.quoteVolume) .slice(0, topN) .map(t => t.symbol); _G('afi_symbolPool', JSON.stringify(filtered)); // Определение квантиля волатильности BTC для оценки состояния рынка const btcR = exchange.GetRecords('BTC_USDT.swap', PERIOD_H4); const n = btcR.length; const returns20 = []; for (let i = n - 20; i < n; i++) returns20.push(Math.abs((btcR[i].Close - btcR[i-1].Close) / btcR[i-1].Close)); const avgVol = returns20.reduce((a, b) => a + b, 0) / returns20.length; // Сравнение с исторической волатильностью для определения квантиля const allVols = []; for (let i = 1; i < n; i++) allVols.push(Math.abs((btcR[i].Close - btcR[i-1].Close) / btcR[i-1].Close)); allVols.sort((a, b) => a - b); let btcVolPercentile = allVols.findIndex(v => v >= avgVol) / allVols.length; let marketState = 'normal'; if (btcVolPercentile > 0.8) marketState = 'high_vol'; else if (btcVolPercentile < 0.3) marketState = 'low_vol'; _G('afi_marketState', marketState); _G('afi_btcVolPct', btcVolPercentile.toFixed(2));

3.2 Проверка состояния пула факторов

Прежде чем AI сгенерирует новые факторы, система оценивает текущее состояние пула: какие факторы недавно демонстрируют устойчивое снижение IC (затухание), какие измерения ещё не покрыты. Эта информация напрямую передаётся AI в качестве ограничений, чтобы избежать повторного исследования уже неэффективных направлений.

javascript
const factorPool = JSON.parse(_G('afi_factorPool') || '[]'); const icHistory = JSON.parse(_G('afi_icHistory') || '{}'); const icDecayWindow = $vars.icDecayWindow || 48; // длина недавнего окна const icDecayThreshold = $vars.icDecayThreshold || -0.01; // порог затухания const targetFactorCount = $vars.targetFactorCount || 10; const degradedFactors = []; for (const factor of factorPool) { const icArr = icHistory[factor.name] || []; if (icArr.length >= 20) { const window = Math.min(icArr.length, icDecayWindow); const recentAvg = icArr.slice(-window).reduce((a, b) => a + b, 0) / window; if (recentAvg < icDecayThreshold) degradedFactors.push({ name: factor.name, recentIC: recentAvg.toFixed(4), rationale: factor.rationale }); } } // Динамически определяем, сколько новых факторов нужно исследовать в этом цикле const explorationBuffer = $vars.explorationBuffer || 3; const explorationCount = Math.max( explorationBuffer, targetFactorCount - validCount + explorationBuffer ); const action = factorPool.length === 0 ? 'generate_initial' : 'iterate_factors';

3.3 Построение промпта и генерация факторов AI

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

Таким образом, генерируемые факторы-кандидаты действительно направлены на поиск новых направлений, а не на повторное прогон существующих факторов с изменёнными параметрами.

javascript
// Итеративный режим Prompt — ключевой фрагмент const usedDimensions = factorPool .map(f => f.name + '(' + (f.rationale || '') + ')') .join(', ') || '暂无'; const validSummary = validFactors.map(f => { const arr = icHistory[f.name] || []; const avg = arr.length > 0 ? (arr.reduce((a,b) => a+b, 0) / arr.length).toFixed(4) : 'N/A'; const recent = arr.length >= 20 ? (arr.slice(-20).reduce((a,b) => a+b, 0) / 20).toFixed(4) : 'N/A'; return f.name + ': 历史IC=' + avg + ' 近期IC=' + recent + ' | 逻辑: ' + f.rationale; }).join('\n') || '暂无'; const degradedSummary = degradedFactors.length > 0 ? degradedFactors.map(f => f.name + ': 近期IC=' + f.recentIC + ' | 原逻辑: ' + f.rationale ).join('\n') : '本轮无衰减因子'; prompt += '【当前有效因子(不需要生成变体)】\n' + validSummary + '\n\n'; prompt += '【近期衰减因子(禁止在这些维度上微调)】\n' + degradedSummary + '\n\n'; prompt += '【已覆盖维度(禁止重复)】\n' + usedDimensions + '\n\n'; prompt += '【尚未探索的维度(优先从这里选)】\n' + unusedSample + '\n\n'; prompt += '生成 ' + explorationCount + ' 个全新方向因子:\n'; prompt += '1. 必须与已覆盖维度完全不同,禁止微调失效因子\n'; prompt += '2. 优先从尚未探索的维度中选取\n'; prompt += '3. 优先设计非线性组合因子\n'; prompt += '4. 针对当前 ' + marketState + ' 市场状态设计\n';

В системный промпт AI встроены полные спецификации TA-функций платформы Inventor, ограничения по формату кода, предварительные знания о крипторынке, а также полный перечень всех доступных для исследования факторных измерений (полное содержание см. в исходном коде стратегии). Формат вывода строго чистый JSON (без обёртки Markdown):

json
{ "factors": [ { "name": "MomentumAcceleration", "rationale": "短期动量加速度,捕捉散户追涨惯性拐点", "code": "(records[n-1].Close - records[n-6].Close)/records[n-6].Close - (records[n-2].Close - records[n-7].Close)/(records[n-7].Close + 0.0001)", "direction": 1, "type": "exploration" } ] }

3.4 IC-валидация: данные говорят, а не интуиция

IC (Information Coefficient) измеряет корреляцию между ранжированием по поперечному сечению, полученному с помощью фактора, и фактическим ранжированием доходности на следующей свече. Чем выше IC, тем точнее прогноз фактора.

Метод валидации — Walk-Forward (историческое воспроизведение): берутся сотни прошлых свечей, в каждый момент времени t фактор рассчитывается по данным на момент t-1, и прогнозируется рост/падение на свече t. Временна́я последовательность строго выровнена, исключены future-функции.

javascript
function calcRankICFull(code, symRecords, factorName) { const syms = Object.keys(symRecords); const icList = []; const minLen = 30; const allLengths = syms.map(s => symRecords[s].length); const minSymLen = Math.min(...allLengths); const testLen = Math.min(500, minSymLen - 1); for (let t = minLen; t < testLen; t++) { const fVals = [], nRets = []; for (const sym of syms) { const fullRecords = symRecords[sym]; // Используем данные за период t-1 для расчёта фактора (slice(0, t) не включает свечу t) const records = fullRecords.slice(0, t); const n = records.length; const v = (function() { return eval(code); })(); if (isNaN(v) || !isFinite(v)) continue; fVals.push({ sym, val: v }); // Прогнозируем фактическую доходность на свече t nRets.push({ sym, ret: (fullRecords[t].Close - fullRecords[t-1].Close) / fullRecords[t-1].Close }); } if (fVals.length < 8) continue; // Вычисляем Rank IC (коэффициент корреляции Спирмена) const fRank = {}, rRank = {}; [...fVals].sort((a,b) => a.val - b.val).forEach((x,i) => fRank[x.sym] = i); [...nRets].sort((a,b) => a.ret - b.ret).forEach((x,i) => rRank[x.sym] = i); const ss = fVals.map(x => x.sym); const fr = ss.map(s => fRank[s]); const rr = ss.map(s => rRank[s]); const n2 = ss.length; const fm = fr.reduce((a,b) => a+b, 0) / n2; const rm = rr.reduce((a,b) => a+b, 0) / n2; const num = fr.map((f,i) => (f-fm) * (rr[i]-rm)).reduce((a,b) => a+b, 0); const den = Math.sqrt( fr.map(f => (f-fm)**2).reduce((a,b) => a+b, 0) * rr.map(r => (r-rm)**2).reduce((a,b) => a+b, 0) ); if (den > 0) icList.push(num / den); } const avgIC = icList.length > 0 ? icList.reduce((a,b) => a+b, 0) / icList.length : 0; return { avgIC, icList }; }

Порог IC задаётся переменной $vars.icThreshold, по умолчанию 0,02. Это относительно либеральный входной порог, подходящий для быстрого отсева явно неработающих факторов; для более строгого контроля статистической значимости его можно повысить в зависимости от ситуации. Факторы, не прошедшие порог, отбрасываются независимо от того, насколько логически безупречна их теория.


3.5 Фильтрация корреляции и выбывание худших

Факторы, прошедшие IC-валидацию, проходят ещё две проверки:

Первая: фильтрация корреляции. Если поперечные оценки двух факторов высоко похожи (|corr| > порог), сохраняется тот, у которого IC выше, второй отбрасывается. Аналогично: если два голоса по сути отражают одно и то же мнение, достаточно одного, второй не добавляет новой информации.

Вторая: выбывание худших (bottom‑killing). Пул факторов имеет верхний предел вместимости; при превышении факторы ранжируются по производительности, и наихудшие выбывают. Факторы с недавним устойчивым падением IC участвуют в ранжировании по недавнему IC, а не по историческому среднему, что усиливает давление на их устранение.

javascript
// Фильтрация по корреляции (оставляем факторы с самым высоким IC, отбрасываем высоко коррелированные избыточные) const corrThreshold = $vars.corrThreshold || 0.7; survivedFactors.sort((a, b) => b.icAvg - a.icAvg); // сначала сортируем по убыванию IC const decorrelatedFactors = []; for (const factor of survivedFactors) { let isRedundant = false; for (const selected of decorrelatedFactors) { const corr = Math.abs(calcCorrelation( factorScoresMap[factor.name], factorScoresMap[selected.name] )); if (corr > corrThreshold) { // Записываем поглощённый коррелированный фактор (для отображения на панели) selected.corrGroup = (selected.corrGroup ? selected.corrGroup + ',' : '') + factor.name; isRedundant = true; break; } } if (!isRedundant) decorrelatedFactors.push({ ...factor, corrGroup: '' }); } // Отсев слабых: для затухающих факторов используем недавний IC, а не средний исторический const targetFactorCount = $vars.targetFactorCount || 10; decorrelatedFactors.sort((a, b) => { const scoreA = a.isDecaying ? a.recentIC : a.icAvg; const scoreB = b.isDecaying ? b.recentIC : b.icAvg; return scoreB - scoreA; }); const finalPool = decorrelatedFactors.slice(0, targetFactorCount); _G('afi_factorPool', JSON.stringify(finalPool));

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


3.6 Формирование сигнала и исполнение ребалансировки

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

javascript
// Веса факторов: взвешивание по недавнему IC (отрицательный IC → вес равен 0) const weights = {}; let totalW = 0; for (const f of factorPool) { const arr = icHistory[f.name] || []; const recentArr = arr.slice(-48); const recentIC = recentArr.length > 0 ? recentArr.reduce((a,b) => a+b, 0) / recentArr.length : 0; const w = Math.max(0, recentIC); // отрицательный IC → вес 0 weights[f.name] = w; totalW += w; } if (totalW > 0) Object.keys(weights).forEach(k => weights[k] /= totalW); else factorPool.forEach(f => weights[f.name] = 1 / factorPool.length); // Z-оценка (стандартизация) function zscore(fname) { const vals = validSyms .map(s => ({ sym: s, val: rawMatrix[s][fname] })) .filter(x => x.val !== null); if (vals.length < 5) return {}; const mean = vals.reduce((a,b) => a + b.val, 0) / vals.length; const std = Math.sqrt(vals.reduce((a,b) => a + (b.val - mean)**2, 0) / vals.length); const r = {}; vals.forEach(x => r[x.sym] = std > 0 ? (x.val - mean) / std : 0); return r; } // Итоговая оценка const scores = {}; for (const sym of validSyms) { let score = 0; for (const f of factorPool) { const z = zscore(f.name)[sym]; if (z !== undefined) score += weights[f.name] * f.direction * z; } scores[sym] = score; } // Пороговая фильтрация: неопределённые сигналы пропускаются – вход в позицию не производится const longShortN = $vars.longShortN || 5; const longThreshold = $vars.longThreshold || 0.3; const shortThreshold = $vars.shortThreshold || -0.3; const sorted = Object.keys(scores).sort((a,b) => scores[b] - scores[a]); const longList = sorted.filter(s => scores[s] >= longThreshold).slice(0, longShortN); const shortList = sorted.slice().reverse() .filter(s => scores[s] <= shortThreshold).slice(0, longShortN);

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

javascript
const positionRatio = $vars.positionRatio || 0.8; // Коэффициент использования общего капитала const maxLeverage = $vars.maxLeverage || 3; const account = exchange.GetAccount(); const equity = account.Equity || account.Balance; const perAmt = (equity * positionRatio) / (longList.length + shortList.length); // Закрываем старые позиции, не входящие в целевой набор const targetSet = new Set([...longList, ...shortList]); for (const sym of Object.keys(currentHoldings)) { if (!targetSet.has(sym)) { const pos = currentHoldings[sym]; const isLong = pos.Type === PD_LONG || pos.Type === 0; exchange.CreateOrder(sym, isLong ? 'closebuy' : 'closesell', -1, Math.abs(pos.Amount)); // Сбрасываем состояние трейлинга тейк-профита const cm = sym.match(/^(.+)_USDT/); if (cm) { _G(cm[1] + '_maxpnl', null); _G(cm[1] + '_trail', null); } } } // Открываем новую позицию по сигналу (рыночный ордер, -1 означает рыночную цену) function openPos(sym, isLong) { exchange.SetMarginLevel(sym, maxLeverage); const market = allMarkets[sym]; const price = exchange.GetTicker(sym).Last; const ctVal = (market.CtVal && market.CtVal > 0) ? market.CtVal : 1; const amtPrec = market.AmountPrecision !== undefined ? market.AmountPrecision : 0; const minQty = (market.MinQty && market.MinQty > 0) ? market.MinQty : 1; const maxQty = (market.MaxQty && market.MaxQty > 0) ? market.MaxQty : 999999; let qty = _N(perAmt / price / ctVal, amtPrec); qty = Math.min(Math.max(qty, minQty), maxQty); exchange.CreateOrder(sym, isLong ? 'buy' : 'sell', -1, qty); }

3.7 Мониторинг позиций: Стоп-лосс / Тейк-профит / Динамический трейлинг-стоп

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

  • Фиксированный стоп-лосс**: Плавающий убыток превышает STOP_LOSS_PCT (по умолчанию 5%) — автоматическое закрытие позиции.
  • Фиксированный тейк-профит**: Плавающая прибыль превышает TAKE_PROFIT_PCT (по умолчанию 10%) — автоматическое закрытие.
  • Динамический трейлинг-стоп**: Включается, когда плавающая прибыль достигает TRAIL_TRIGGER (3%), порог просадки динамически корректируется в зависимости от максимальной прибыли.
javascript
const STOP_LOSS_PCT = $vars.stopLossPct || 5; const TAKE_PROFIT_PCT = $vars.takeProfitPct || 10; const TRAIL_TRIGGER = 3; // Запуск трейлинга при достижении плавающей прибыли 3% // Динамический порог просадки: чем выше максимальная прибыль, тем больше допустимая просадка function getDynamicTrailDrawdown(maxPnl) { if (maxPnl >= 7) return 3; // Максимальная прибыль ≥7%, допустимая просадка 3% if (maxPnl >= 4) return 2; // Максимальная прибыль ≥4%, допустимая просадка 2% return 1.5; // В остальных случаях просадка 1.5% } function monitorTPSL(positions, tickers) { for (const pos of (positions || [])) { if (Math.abs(pos.Amount) === 0) continue; const cm = pos.Symbol.match(/^(.+)_USDT/); if (!cm) continue; const coin = cm[1]; const ticker = tickers[coin + '_USDT.swap']; if (!ticker) continue; const isLong = pos.Type === PD_LONG || pos.Type === 0; const cur = ticker.Last; const ent = pos.Price; const amt = Math.abs(pos.Amount); const pnlPct = (cur - ent) * (isLong ? 1 : -1) / ent * 100; // Отслеживание максимальной плавающей прибыли let maxPnl = _G(coin + '_maxpnl'); if (maxPnl === null) { maxPnl = pnlPct; _G(coin + '_maxpnl', maxPnl); } else if (pnlPct > maxPnl) { maxPnl = pnlPct; _G(coin + '_maxpnl', maxPnl); } // Запуск трейлинга if (!_G(coin + '_trail') && maxPnl >= TRAIL_TRIGGER) { _G(coin + '_trail', true); Log(coin + ' Запуск трейлинга, плавающая прибыль: +' + pnlPct.toFixed(2) + '%'); } const trailDrawdown = getDynamicTrailDrawdown(maxPnl); let reason = null; if (_G(coin + '_trail') && (maxPnl - pnlPct) >= trailDrawdown) reason = 'Трейлинг-стоп (просадка ' + (maxPnl - pnlPct).toFixed(2) + '%, порог ' + trailDrawdown + '%)'; if (!reason && pnlPct <= -STOP_LOSS_PCT) reason = 'Стоп-лосс (' + pnlPct.toFixed(2) + '%)'; if (!reason && pnlPct >= TAKE_PROFIT_PCT) reason = 'Тейк-профит (' + pnlPct.toFixed(2) + '%)'; if (reason) { exchange.CreateOrder(pos.Symbol, isLong ? 'closebuy' : 'closesell', -1, amt); Log(coin, 'Сработал', reason); _G(coin + '_maxpnl', null); _G(coin + '_trail', null); } } }

四、Ключевые проектные решения

4.1 Почему Rank IC (а не Pearson IC)

Rank IC использует ранги вместо исходных значений для расчета корреляции, что делает его устойчивым к экстремальным значениям (выбросам) в факторах. Распределение цен на криптовалютных рынках имеет тяжелые хвосты, Pearson IC легко искажается несколькими экстремальными свечами, тогда как Rank IC более стабилен.

4.2 Строгое выравнивание временных рядов для предотвращения будущих функций

И проверка IC, и расчет сигналов в реальном времени используют значение фактора на периоде t-1 для прогнозирования доходности на периоде t: при проверке records передается fullRecords.slice(0, t), что физически отсекает будущие данные, так что сгенерированный AI код фактора, как бы он ни ссылался на records[n], обращается только к истории до t-1; в реальном времени последняя свеча удаляется (slice(0, n-1)) для расчета значения фактора с прогнозом движения следующей свечи. Оба подхода полностью идентичны, что исключает завышение IC из-за взгляда в будущее.

4.3 Взвешивание недавнего IC, веса адаптируются к рынку

Веса факторов не фиксированы, а динамически корректируются в зависимости от недавней IC. Когда какой-то фактор начинает терять эффективность (его недавняя IC снижается), его вес в синтезе сигналов автоматически уменьшается и в итоге обнуляется. Система завершает ребалансировку весов без вмешательства человека.

4.4 Двухтриггерная архитектура

Итерация факторов — это задача, требующая интенсивных вычислений (загрузка свечных данных + бэктестинг IC + вызов ИИ), достаточно выполнять один раз в час. Защита позиции — это чувствительная ко времени задача, требующая отклика на уровне секунд. Разделяем их на триггеры разной частоты, чтобы избежать взаимных блокировок.


V. Наблюдения на реальном счёте: процесс итерации факторов

После двух дней работы на реальном счёте наблюдались следующие явления:

  • Факторы, попавшие в пул на раннем этапе, имели историческую IC в основном в диапазоне от 0,04 до 0,07, прошли базовый порог.
  • По мере продвижения итераций недавняя IC почти всех факторов начала снижаться. У некоторых она упала с 0,06 до 0,008, у некоторых вошла в отрицательную зону. Это говорит о том, что сигналы, захватываемые этими факторами, теряют эффективность в текущих рыночных условиях.
  • После обнаружения снижения система в следующем раунде приоритетно исследует ещё не охваченные измерения, чтобы найти новые факторы-кандидаты для замены. Весь процесс не требует вмешательства человека.

img

Двух дней слишком мало, чтобы полностью проверить, действительно ли адаптивная способность системы эффективна. Здесь просто зафиксировано, что система выполнила итерации, как и ожидалось. Более значимые выводы требуют более длительного непрерывного наблюдения. Но сам этот процесс уже показывает, что базовая логика конструкции системы работает: она не цепляется за потерявшие эффективность сигналы, а постоянно пробует новые измерения.


VI. В заключение

Эта система создавалась не для того, чтобы доказать, что ИИ может победить рынок. А для того, чтобы сказать: в эпоху ИИ у обычных людей тоже есть возможность попробовать то, что раньше могли делать только ведущие институты.

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

Это не означает, что она будет стабильно приносить прибыль. Рынок всегда сложнее любой системы. Но это означает, что порог входа снижается, инструменты становятся мощнее, и вероятность того, что обычные люди смогут участвовать в этом, растёт.

⚠️ Предупреждение о рисках: Любая стратегия сопряжена с риском убытков. Содержание данной статьи предназначено только для технического изучения и не является инвестиционной рекомендацией. Перед реальной торговлей обязательно проведите тщательное тестирование.

Исходный код стратегии: Адаптивная стратегия количественной торговли с добычей факторов (тестовая версия)

Comment
All comments (0)
No data
No data
  • 1
iPhone Download
Forums
PINE Language
© 2015 - ∞ INVENTOR PTE LTD (SG)