UNPKG

@gabriel3615/ta_analysis

Version:

stock ta analysis

75 lines (74 loc) 3.74 kB
import { backtestStrategiesConfig } from '../strategyConfig.js'; import { analyzeMultiTimeframePatterns, PatternDirection, } from '../../basic/patterns/analyzeMultiTimeframePatterns.js'; export function PatternConsensusStrategy(symbol, timeframe, params = {}) { const { minSignalStrength = backtestStrategiesConfig.patternConsensus .minSignalStrength, requireAllAligned = backtestStrategiesConfig.patternConsensus .requireAllAligned, coolDownBars = backtestStrategiesConfig.patternConsensus.coolDownBars, lookback = backtestStrategiesConfig.patternConsensus.lookback, } = params; let lastSignalIndex = -1; let lastDirection = undefined; return { name: 'PatternConsensus', generateSignal(history, i) { if (i < Math.min(120, lookback / 2)) return null; if (i - lastSignalIndex < coolDownBars) return null; const dailyData = history.slice(Math.max(0, i - lookback + 1), i + 1); // 近似聚合周线:每5天取一根(已存在 util.aggregateDailyToWeekly 但此处只需近似) const weeklyData = []; for (let j = 0; j < dailyData.length; j += 5) { const slice = dailyData.slice(j, Math.min(j + 5, dailyData.length)); if (!slice.length) continue; weeklyData.push({ symbol: slice[0].symbol, open: slice[0].open, high: Math.max(...slice.map(c => c.high)), low: Math.min(...slice.map(c => c.low)), close: slice[slice.length - 1].close, volume: slice.reduce((s, c) => s + c.volume, 0), timestamp: slice[slice.length - 1].timestamp, }); } // 小时数据缺失时传空数组,内部不会报错 const hourlyData = []; const analysis = analyzeMultiTimeframePatterns(weeklyData, dailyData, hourlyData); if (analysis.signalStrength < minSignalStrength) return null; const tfa = analysis.timeframeAnalyses; const weeklySig = tfa.find(a => a.timeframe === 'weekly')?.patternSignal; const dailySig = tfa.find(a => a.timeframe === 'daily')?.patternSignal; const hourlySig = tfa.find(a => a.timeframe === '1hour')?.patternSignal; const allAligned = weeklySig && dailySig && hourlySig && weeklySig === dailySig && dailySig === hourlySig; if (requireAllAligned && !allAligned) return null; let direction = 'flat'; if (analysis.combinedSignal === PatternDirection.Bullish) direction = 'long'; else if (analysis.combinedSignal === PatternDirection.Bearish) direction = 'short'; if (direction === 'flat') return null; const signal = { timestamp: history[i].timestamp, direction, strength: Math.min(95, Math.max(60, analysis.signalStrength)), reason: `形态共识(${requireAllAligned ? '三周期一致' : '综合偏向'}) ${analysis.description}`, }; // 避免短时间内方向反转噪声 if (lastDirection && signal.direction !== lastDirection && i - lastSignalIndex < coolDownBars * 2 && (signal.strength ?? 0) < 80) { return null; } lastSignalIndex = i; lastDirection = signal.direction; return signal; }, }; }