I. Pourquoi l’extraction automatisée des facteurs est-elle nécessaire ?
Si vous avez déjà abordé le trading quantitatif, vous avez forcément entendu le terme « facteur ». Qu’est-ce qu’un facteur ? En clair, c’est un signal de marché exprimé par des données. Par exemple : momentum de prix, anomalie de volume, position des bandes de Bollinger – autant d’éléments utilisés pour prédire si un actif va monter ou baisser dans un laps de temps donné.
Cela semble simple, mais ceux qui travaillent vraiment sur les facteurs savent à quel point c’est difficile :
Une solide connaissance financière et une base approfondie en statistiques mathématiques
Un grand volume de données historiques propres
Un cadre de backtest strict
Sans oublier un problème inévitable : les facteurs se dégradent avec le temps.
Un signal efficace aujourd’hui peut devenir totalement inefficace quelques jours plus tard – car les acteurs du marché apprennent, s’adaptent et exploitent cette régularité. Ainsi, l’extraction de facteurs n’est jamais un travail ponctuel ; elle nécessite une itération continue.
Ce que cet article présente, c’est un système qui automatise tout ce processus : une boucle complète d’extraction de facteurs → validation → élimination → synthèse de signaux → exécution des ordres, exécutée à intervalles fixes. On remplace la répétition manuelle par l’itération machine, permettant à la stratégie de suivre en permanence le rythme des changements du marché.
II. Architecture globale du système
Le flux traditionnel d’extraction de facteurs est le suivant : un chercheur émet une hypothèse → écrit du code → exécute un backtest → filtre → met en production → constate une dégradation quelques mois plus tard → recommence. Ce cycle peut prendre des semaines, voire des mois.
Le système présenté comprime l’ensemble du cycle en une exécution automatique à intervalle fixe :
| Étape | Module | Description |
|---|---|---|
| Étape 1 | Acquisition du pool d’actifs | Filtrer les contrats perpétuels à forte liquidité (par volume d’échange) et détecter l’état du marché |
| Étape 2 | Inspection du pool de facteurs | Analyser la santé actuelle des facteurs et déterminer la direction d’exploration pour ce cycle |
| Étape 3 | Génération de facteurs par IA | Dans un cadre contraint, demander à l’IA de générer des facteurs candidats dans de nouvelles dimensions |
| Étape 4 | Validation IC | Calculer le coefficient d’information (Information Coefficient) sur les données historiques et éliminer les facteurs inefficaces |
| Étape 5 | Filtrage par corrélation & élimination du dernier | Supprimer les facteurs redondants (chevauchement d’information) pour garder un pool de facteurs précis et non pléthorique |
| Étape 6 | Synthèse des signaux & exécution des ordres | Synthèse pondérée des scores, déclenchement du rééquilibrage du portefeuille lorsque le signal dépasse un seuil |
Le système est piloté par deux planificateurs : un déclencheur lent exécute le processus complet d’itération des facteurs à une échelle horaire ; un déclencheur rapide interroge l’état des positions à une échelle de secondes pour gérer le stop-profit/stop-loss et rafraîchir le tableau de bord.
III. Détail de chaque module et code principal
3.1 Acquisition du pool d’actifs
Au début de chaque cycle, le système récupère les données en temps réel de tous les contrats perpétuels depuis l’exchange, les trie par volume d’échange et sélectionne les N premiers. La liquidité est la condition préalable à l’efficacité d’un facteur – les petites cryptos ont des volumes trop faibles pour que tout signal soit fiable.
Parallèlement, il détecte le percentile de volatilité des bougies 4H du BTC pour déterminer l’état global du marché (normal / high_vol / low_vol / volatile), ce qui influence directement l’orientation préférentielle de la génération des facteurs par l’IA.
javascript
// Filtrer les actifs à forte liquidité en fonction du volume d’échange
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));
// Détecter le percentile de volatilité du BTC pour déterminer l’état du marché
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;
// Comparer avec la volatilité historique complète pour déterminer le quantile
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 Inspection de l’état du pool de facteurs
Avant de laisser l’IA générer de nouveaux facteurs, le système fait d’abord le point sur la santé du pool actuel : quels facteurs ont vu leur IC (coefficient d’information) récent décliner continuellement (dégradation) et quelles dimensions n’ont pas encore été couvertes. Ces informations sont transmises directement comme contraintes à l’IA, afin d’éviter d’explorer à nouveau des directions déjà inefficaces.
javascript
const factorPool = JSON.parse(_G('afi_factorPool') || '[]');
const icHistory = JSON.parse(_G('afi_icHistory') || '{}');
const icDecayWindow = $vars.icDecayWindow || 48; // Longueur de la fenêtre récente
const icDecayThreshold = $vars.icDecayThreshold || -0.01; // Seuil de détection de dégradation
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
});
}
}
// Déterminer dynamiquement le nombre de nouveaux facteurs à explorer pour ce cycle
const explorationBuffer = $vars.explorationBuffer || 3;
const explorationCount = Math.max(
explorationBuffer,
targetFactorCount - validCount + explorationBuffer
);
const action = factorPool.length === 0 ? 'generate_initial' : 'iterate_factors';
3.3 Construction du prompt pour que l’IA invente des facteurs
L’IA ne reçoit pas une tâche ouverte, mais un cadre contraint. Le prompt inclut : l’état actuel du marché, la liste des facteurs existants (interdiction de reproduire), les facteurs récemment dégradés (interdiction de les ajuster légèrement), les dimensions déjà couvertes, et celles encore inexplorées.
Les facteurs candidats ainsi générés sont de véritables tentatives dans de nouvelles directions, et non une simple reconfiguration des facteurs existants.
javascript
// Extrait clé du prompt en mode itératif
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';
Le System Prompt de l'IA intègre nativement les spécifications complètes des fonctions TA de la plateforme Inventor, les contraintes de format de code, les connaissances préalables du marché crypto, ainsi que la liste complète des dimensions factorielles explorables (voir le code source de la stratégie pour le contenu complet). Le format de sortie est strictement du JSON pur (sans balisage 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 Validation IC : les données parlent, pas l'intuition
L'IC (coefficient d'information, Information Coefficient) mesure la corrélation entre le classement transversal calculé par le facteur et le classement réel du rendement de la bougie suivante. Plus l'IC est élevé, plus le facteur est prédictif.
La méthode de validation est une marche avant (Walk-Forward) : avec les quelques centaines de bougies passées, à chaque point temporel t, on calcule la valeur du facteur à l'aide des données de l'instant t-1 pour prédire la hausse ou la baisse de la bougie t. Alignement temporel strict, pas de fuite de données futures.
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];
// Utiliser les données de la période t-1 pour calculer le facteur (slice(0, t) n'inclut pas la bougie 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 });
// Prédire le rendement réel de la bougie t
nRets.push({
sym,
ret: (fullRecords[t].Close - fullRecords[t-1].Close) / fullRecords[t-1].Close
});
}
if (fVals.length < 8) continue;
// Calcul du Rank IC (coefficient de corrélation de 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 };
}
Le seuil IC est contrôlé par la variable
$vars.icThreshold, par défaut 0.02. Il s'agit d'un seuil d'entrée relativement peu strict, adapté pour filtrer rapidement les facteurs manifestement inefficaces ; pour un meilleur contrôle de significativité statistique, vous pouvez augmenter cette valeur selon les besoins. Les facteurs qui ne franchissent pas ce seuil, quelle que soit la perfection de leur logique, sont directement éliminés.
3.5 Filtrage de corrélation et élimination du dernier
Les facteurs ayant passé la validation IC doivent encore franchir deux étapes :
Première étape : filtrage de corrélation. Si les scores transversaux de deux facteurs sont très similaires (|corr| > seuil), on conserve celui avec l'IC le plus élevé et on élimine l'autre. Comme deux bulletins de vote derrière lesquels se trouve la même idée : on les fusionne en un seul bulletin, un bulletin de plus n'apporte pas une opinion supplémentaire.
Deuxième étape : élimination du dernier. Le pool de facteurs a une capacité maximale ; au‑delà, on classe en fonction des performances et le moins performant sort. Les facteurs dont l'IC récent baisse constamment utilisent l'IC récent (et non l'IC historique moyen) pour le classement, subissant ainsi une pression d'élimination plus forte.
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));
Note : le calcul de la corrélation repose sur les scores factoriels de la coupe transversale actuelle, ce qui peut occasionnellement entraîner des erreurs de jugement à certains moments. Une approche plus robuste consisterait à utiliser la corrélation moyenne sur plusieurs coupes historiques, ce qui constitue une piste d'amélioration future.
3.6 Synthèse des signaux et exécution du rééquilibrage
Une fois le pool de facteurs stabilisé, le système calcule un score composite pour chaque instrument : les valeurs transversales de chaque facteur sont normalisées en Z-score, puis pondérées par leur IC récent respectif – plus un facteur est performant, plus son poids est important ; les facteurs dont l'IC récent est négatif se voient attribuer un poids nul.
javascript
// 因子权重:近期 IC 加权(负 IC 因子权重置零)
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-score 标准化
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);
Lors de l'exécution du rééquilibrage, les anciennes positions qui ne figurent pas dans la liste du cycle en cours sont d'abord liquidées, puis les nouvelles positions sont ouvertes proportionnellement aux capitaux propres du compte, en fonction des signaux retenus :
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 Surveillance des positions : stop-loss / take-profit / trailing stop dynamique
Le déclencheur rapide s'exécute toutes les secondes, surveille en temps réel le profit flottant de toutes les positions et applique trois logiques de sortie :
- Stop-loss fixe : fermeture automatique lorsque la perte flottante dépasse
STOP_LOSS_PCT(5 % par défaut). - Take-profit fixe : fermeture automatique lorsque le profit flottant dépasse
TAKE_PROFIT_PCT(10 % par défaut). - Trailing stop dynamique : activé lorsque le profit flottant atteint
TRAIL_TRIGGER(3 %) ; le seuil de retracement s'ajuste dynamiquement en fonction du profit flottant maximal.
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);
}
}
}
IV. Décisions de conception clés
4.1 Pourquoi utiliser Rank IC (plutôt que Pearson IC)
Le Rank IC utilise les rangs plutôt que les valeurs brutes pour calculer la corrélation, ce qui le rend naturellement robuste face aux valeurs extrêmes (outliers) dans les facteurs. Les distributions de prix sur le marché des crypto‑monnaies ont des queues épaisses sévères ; le Pearson IC peut facilement être faussé par quelques bougies extrêmes, tandis que le Rank IC offre une stabilité bien supérieure.
4.2 Alignement temporel strict, pas de fuite de données futures
Le calcul du IC de validation et celui des signaux en ligne utilisent systématiquement les valeurs du facteur à l'instant t‑1 pour prédire le rendement à l'instant t : lors de la validation, records reçoit fullRecords.slice(0, t), ce qui tronque physiquement les données futures ; quel que soit la manière dont le code du facteur généré par l'IA référence records[n], il n'accède qu'à l'historique jusqu'à t‑1. En ligne, on retire la dernière bougie (slice(0, n-1)) pour calculer la valeur du facteur et prédire la variation de la bougie suivante. Les deux logiques sont parfaitement cohérentes, évitant un IC artificiellement élevé dû à la visualisation de données futures.
4.3 Pondération du IC récent, poids adaptatif au marché
(Les lignes suivantes manquent dans le texte original – le contenu s'arrête ici.)
Les poids des facteurs ne sont pas fixes, mais sont ajustés dynamiquement en fonction de l'IC récent. Lorsqu'un facteur commence à perdre en efficacité (baisse de l'IC récent), son poids dans la synthèse des signaux diminue automatiquement jusqu'à zéro, et le système effectue le rééquilibrage des poids sans intervention humaine.
4.4 Architecture à double déclencheur
L'itération des facteurs est une tâche de calcul lourde (extraction des bougies + backtest IC + appel IA), et une exécution toutes les heures est suffisante ; la protection des positions est une tâche sensible au temps, nécessitant une réponse en secondes. En séparant ces deux tâches en déclencheurs de fréquences différentes, on évite les blocages mutuels.
五、Observation en conditions réelles : processus d'itération des facteurs
Après deux jours de fonctionnement en conditions réelles, les phénomènes suivants ont été observés :
- Les facteurs entrés tôt dans le pool présentaient un IC historique généralement compris entre 0,04 et 0,07, franchissant le seuil de base.
- Au fur et à mesure de l'itération, l'IC récent de presque tous les facteurs a diminué, certains passant de 0,06 à 0,008, d'autres tombant dans des valeurs négatives. Cela indique que les signaux captés par ces facteurs perdent en efficacité dans l'environnement de marché actuel.
- Après avoir détecté la dégradation, le système explore en priorité les dimensions non encore couvertes lors du cycle suivant, afin de trouver de nouveaux facteurs candidats pour les remplacer. L'ensemble du processus ne nécessite aucune intervention humaine.
Deux jours sont trop courts pour valider pleinement l'efficacité réelle de la capacité d'adaptation du système. Nous avons simplement enregistré que le système a exécuté les actions d'itération comme prévu. Des conclusions plus significatives nécessitent une observation continue sur une période plus longue. Cependant, ce processus montre déjà que la logique fondamentale de la conception du système est en action : il ne s'accroche pas aux signaux inefficaces, mais explore continuellement de nouvelles dimensions.
六、En guise de conclusion
Construire ce système n'a pas pour but de prouver que l'IA peut surpasser le marché. Il s'agit plutôt de dire qu'à l'ère de l'IA, de nombreuses choses qui n'étaient auparavant possibles que pour les institutions de premier plan sont désormais accessibles aux particuliers.
Extraction de facteurs, itération de stratégies, exécution automatique – tout ce qui nécessitait auparavant une équipe, une infrastructure de données importante et des années d'accumulation peut aujourd'hui être mis en œuvre avec un seul flux de travail.
Cela ne signifie pas qu'il générera des bénéfices stables. Le marché est toujours plus complexe que n'importe quel système. Mais cela signifie que les barrières à l'entrée s'abaissent, que les outils deviennent plus puissants et que la probabilité pour les particuliers de participer à cette activité augmente.
⚠️ Avertissement sur les risques : Toute stratégie comporte un risque de perte. Le contenu de cet article est uniquement destiné à l'apprentissage technique et ne constitue pas un conseil en investissement. Avant de passer en conditions réelles, veuillez effectuer des tests approfondis.
Code source de la stratégie : Version de test de la stratégie quantitative d'extraction adaptative de facteurs
- 1


