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

1. 자동화된 팩터 마이닝이 필요한 이유

퀀트 트레이딩을 접해보셨다면 '팩터(factor)'라는 단어를 들어보셨을 것입니다. 팩터란 무엇일까요? 쉽게 말해, 데이터로 표현된 시장 신호입니다. 예를 들어, 가격 모멘텀, 거래량 이상 징후, 볼린저 밴드 위치 등은 특정 코인이 앞으로 일정 기간 상승할지 하락할지를 예측하는 데 사용됩니다.

간단해 보이지만, 실제로 팩터 연구를 해본 사람들은 이 작업이 얼마나 어려운지 잘 알고 있습니다.

탄탄한 금융 지식깊이 있는 수리통계학 배경
대량의 깨끗한 과거 데이터
엄격한 백테스팅 프레임워크

또한 절대 피할 수 없는 문제에 직면하게 됩니다: 팩터의 붕괴(Decay)

오늘 유효한 신호가 며칠 후에는 완전히 무용지물이 될 수 있습니다. 시장 참여자들이 학습하고 적응하며 그 패턴을 차익거래해 버리기 때문입니다. 따라서 팩터 마이닝은 일회성 작업이 아니라 지속적인 반복이 필요합니다.

이 글에서 소개하는 것은 바로 이 작업을 자동화하는 시스템입니다: 고정된 간격으로 팩터 마이닝 → 검증 → 제거 → 신호 합성 → 주문 실행의 전체 사이클을 반복합니다. 기계가 반복 작업을 대체하여 전략이 시장 변화에 지속적으로 적응할 수 있도록 합니다.

img


2. 시스템 전체 아키텍처

전통적인 팩터 마이닝 프로세스는 다음과 같습니다: 연구자가 가설 제안 → 코드 작성 → 백테스팅 실행 → 선별 → 실전 적용 → 몇 달 후 무효화 발견 → 다시 시작. 전체 사이클은 몇 주에서 몇 개월이 걸릴 수 있습니다.

이 시스템은 전체 사이클을 고정된 간격으로 자동 실행되도록 압축합니다.

단계모듈설명
Step 1거래 대상 풀 획득거래대금 기준으로 유동성이 높은 무기한 계약 선별, 시장 상태 감지
Step 2팩터 풀 점검현재 팩터 건강도 분석, 이번 탐색 방향 결정
Step 3AI 팩터 생성제약 프레임워크 내에서 AI가 새로운 차원의 후보 팩터를 생성
Step 4IC 검증과거 데이터로 정보 계수(IC) 계산, 무효 팩터 제거
Step 5상관관계 필터링 및 하위 제거정보 중복 팩터 제거, 팩터 풀을 정제하고 적게 유지
Step 6신호 합성 및 주문 실행가중 합성 점수, 임계값 초과 시 포지션 조정 실행

시스템은 두 개의 스케줄러로 구동됩니다: 느린 트리거는 시간 단위로 전체 팩터 반복 프로세스를 한 번 실행하고, 빠른 트리거는 초 단위로 포지션 상태를 폴링하여 이익 실현 및 손절, 대시보드 업데이트를 처리합니다.


3. 각 모듈 상세 설명 및 핵심 코드

3.1 거래 대상 풀 획득

각 라운드가 시작되면 시스템은 거래소에서 모든 무기한 계약의 실시간 시세를 가져와 거래대금 순으로 상위 N개를 선별합니다. 유동성은 팩터 유효성의 전제 조건입니다. 작은 코인은 거래량이 부족하여 어떤 신호도 왜곡되기 쉽습니다.

동시에 BTC 4시간봉 변동성의 역사적 백분위수를 감지하여 시장 전체 상태(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의 System Prompt에는 완전한 발명자 플랫폼 TA 함수 명세, 코드 포맷 제약, 암호화폐 시장 사전 지식, 그리고 탐색 가능한 모든 팩터 차원 목록이 내장되어 있습니다(전체 내용은 전략 소스코드 참조). 출력 형식은 순수 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번째 캔들의 등락을 예측합니다. 시계열을 엄격하게 정렬하여 미래 함수를 철저히 배제합니다.

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 (Spearman 상관 계수) 계산 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가 더 높은 팩터를 유지하고 다른 팩터는 버립니다. 이는 두 표가 동일한 사람의 의견을 나타내는 것과 같아 하나로 통합하면 충분하며, 하나 더 있어도 새로운 관점을 추가하지 않습니다.

두 번째: 하위 순위 제거. 팩터 풀에는 용량 상한이 있으며, 초과분은 성과 순으로 정렬하여 가장 나쁜 것이 탈락합니다. 최근 IC가 지속적으로 하락하는 팩터는 역사적 평균 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가 음수인 팩터의 가중치는 0으로 설정됩니다.

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 기 수익률 예측을 일관되게 사용합니다. 검증 시 recordsfullRecords.slice(0, t)를 전달하여 미래 데이터를 물리적으로 차단함으로써 AI가 생성한 팩터 코드가 records[n]을 어떻게 참조하더라도 t-1까지의 과거만 접근하도록 합니다. 온라인 시에는 마지막 캔들을 제거(slice(0, n-1))하여 팩터 값을 계산하고 다음 캔들의 등락을 예측합니다. 두 로직이 완전히 일치하여 미래 데이터를 참조함으로 인한 IC 부풀리기를 방지합니다.

4.3 최근 IC 가중치, 가중치가 시장에 따라 적응

최근 IC에 더 높은 가중치를 부여하며 가중치는 시장 상황에 따라 적응적으로 변화합니다.

팩터 가중치는 고정되어 있지 않고, 최근 IC에 따라 동적으로 조정됩니다. 특정 팩터가 무효화되기 시작하면(최근 IC 하락), 신호 합성에서 해당 팩터의 가중치는 자동으로 감소하여 0이 될 때까지 줄어듭니다. 시스템은 인위적인 개입 없이도 가중치 재조정을 완료할 수 있습니다.

4.4 이중 트리거 구조

팩터 반복은 재계산 작업(캔들 데이터 로드 + IC 백테스트 + AI 호출)으로, 시간 단위로 한 번씩 실행하면 충분합니다. 포지션 보호는 시간에 민감한 작업으로, 초 단위 응답이 필요합니다. 이 두 가지를 서로 다른 빈도의 트리거로 분리하여 상호 간섭을 방지합니다.


5장, 실전 관찰: 팩터 반복 과정

실전 운용 이틀 후, 다음과 같은 현상이 관찰되었습니다.

  • 초기에 풀에 진입한 팩터들은 역사적 IC가 일반적으로 0.04에서 0.07 사이로 기본 임계값을 통과했습니다.
  • 반복이 진행됨에 따라 거의 모든 팩터의 최근 IC가 하락했으며, 일부는 0.06에서 0.008로 떨어지고, 어떤 것은 음수로 떨어졌습니다. 이는 이러한 팩터들이 포착한 신호가 현재 시장 환경에서 무효화되고 있음을 의미합니다.
  • 시스템은 감쇠를 감지한 후, 다음 라운드에서 우선적으로 아직 커버되지 않은 차원을 탐색하여 새로운 후보 팩터를 찾아 교체합니다. 전체 과정은 인위적인 개입이 필요 없습니다.

img

이틀이라는 시간은 비교적 짧아 시스템의 적응 능력이 실제로 효과적인지 완전히 검증하기에는 부족합니다. 여기서는 시스템이 예상대로 반복 동작을 실행한 것을 기록했을 뿐이며, 더 의미 있는 결론을 얻기 위해서는 더 긴 기간의 지속적인 관찰이 필요합니다. 그러나 이 과정 자체만으로도 시스템 설계의 기본 로직이 작동하고 있음을 알 수 있습니다. 즉, 무효화된 신호에 집착하지 않고 새로운 차원을 지속적으로 시도한다는 점입니다.


6장, 마지막으로

이 시스템을 구축한 이유는 AI가 시장을 이길 수 있다는 것을 증명하기 위해서가 아닙니다. 오히려 AI 시대에 과거에는 최고 기관만이 할 수 있었던 많은 일들을 일반인도 시도해 볼 수 있는 기회가 생겼다는 점을 말하고 싶었습니다.

팩터 마이닝, 전략 반복, 자동 실행 – 이전에는 팀, 대규모 데이터 인프라, 그리고 수년간의 축적이 필요했던 것들이 오늘날 하나의 워크플로우로 실행될 수 있습니다.

이것이 안정적인 수익을 보장한다는 의미는 아닙니다. 시장은 항상 어떤 시스템보다도 복잡합니다. 하지만 그것은 진입 장벽이 낮아지고, 도구가 강력해지며, 일반인이 이 분야에 참여할 가능성이 커지고 있음을 의미합니다.

⚠️ 위험 고지: 모든 전략은 손실 위험이 있으며, 본 문서의 내용은 기술 학습 참고용으로만 제공되며 투자 조언을 구성하지 않습니다. 실전 운용 전에 반드시 충분히 테스트하시기 바랍니다.

전략 소스 코드: 적응형 팩터 마이닝 양적 전략 테스트 버전

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