UNPKG

@gabriel3615/ta_analysis

Version:

stock ta analysis

801 lines (800 loc) 35.7 kB
import { toEDTString } from '../../../util/util.js'; import { patternConfig } from './patternConfig.js'; import _ from 'lodash'; import { findHeadAndShoulders } from './findHeadAndShoulders.js'; import { findDoubleTopsAndBottoms } from './findDoubleTopsAndBottoms.js'; import { findTriangles } from './findTriangles.js'; import { findWedges } from './findWedges.js'; import { findFlagsAndPennants } from './findFlagsAndPennants.js'; import { findCupAndHandle } from './findCupAndHandle.js'; import { findRoundingPatterns } from './findRoundingPatterns.js'; import { findBuyingClimax } from './findBuyingClimax.js'; import { findSellingClimax } from './findSellingClimax.js'; /** * 价格形态类型枚举 */ var PatternType; (function (PatternType) { PatternType["HeadAndShoulders"] = "head_and_shoulders"; PatternType["InverseHeadAndShoulders"] = "inverse_head_and_shoulders"; PatternType["DoubleTop"] = "double_top"; PatternType["DoubleBottom"] = "double_bottom"; PatternType["TripleTop"] = "triple_top"; PatternType["TripleBottom"] = "triple_bottom"; PatternType["AscendingTriangle"] = "ascending_triangle"; PatternType["DescendingTriangle"] = "descending_triangle"; PatternType["SymmetricalTriangle"] = "symmetrical_triangle"; PatternType["RisingWedge"] = "rising_wedge"; PatternType["FallingWedge"] = "falling_wedge"; PatternType["Rectangle"] = "rectangle"; PatternType["Flag"] = "flag"; PatternType["Pennant"] = "pennant"; PatternType["CupAndHandle"] = "cup_and_handle"; PatternType["RoundingBottom"] = "rounding_bottom"; PatternType["RoundingTop"] = "rounding_top"; PatternType["BuyingClimax"] = "buying_climax"; PatternType["SellingClimax"] = "selling_climax"; })(PatternType || (PatternType = {})); /** * 形态识别状态枚举 */ var PatternStatus; (function (PatternStatus) { PatternStatus["Forming"] = "forming"; PatternStatus["Completed"] = "completed"; PatternStatus["Confirmed"] = "confirmed"; PatternStatus["Failed"] = "failed"; })(PatternStatus || (PatternStatus = {})); /** * 形态趋势方向枚举 */ var PatternDirection; (function (PatternDirection) { PatternDirection["Bullish"] = "bullish"; PatternDirection["Bearish"] = "bearish"; PatternDirection["Neutral"] = "neutral"; })(PatternDirection || (PatternDirection = {})); /** * 增强型峰谷检测函数,更注重最近的价格波动 * @param data K线数据 * @param windowSize 用于峰谷检测的窗口大小 * @param recentEmphasis 最近数据的权重因子 */ function detectPeaksAndValleys(data, windowSize = patternConfig.windows.peakWindow, recentEmphasis = patternConfig.windows.recentEmphasis) { const result = []; // 确保有足够的数据 if (data.length < windowSize * 2 + 1) { return result; } // 计算价格波动率,用于动态调整窗口大小 const priceChanges = []; for (let i = 1; i < data.length; i++) { priceChanges.push(Math.abs(data[i].close - data[i - 1].close) / data[i - 1].close); } const avgVolatility = priceChanges.reduce((sum, change) => sum + change, 0) / priceChanges.length; // 根据波动率动态调整窗口大小 const dynamicWindowSize = avgVolatility > 0.03 ? Math.max(3, windowSize - 1) : avgVolatility < 0.01 ? Math.min(data.length / 4, windowSize + 2) : windowSize; // 计算最小显著变化百分比,用于过滤噪声 const avgPrice = data.reduce((sum, d) => sum + d.close, 0) / data.length; const minSignificantChange = avgPrice * 0.005; // 0.5%的最小变化 // 第一阶段:检测所有可能的峰谷点 const potentialPeaksValleys = []; // 使用滑动窗口检测峰谷,但允许边界检测 for (let i = dynamicWindowSize; i < data.length - dynamicWindowSize; i++) { const current = data[i]; let isPeak = true; let isValley = true; // 检查是否是峰 for (let j = i - dynamicWindowSize; j < i; j++) { if (data[j].high >= current.high) { isPeak = false; break; } } for (let j = i + 1; j <= i + dynamicWindowSize; j++) { if (data[j].high >= current.high) { isPeak = false; break; } } // 检查是否是谷 for (let j = i - dynamicWindowSize; j < i; j++) { if (data[j].low <= current.low) { isValley = false; break; } } for (let j = i + 1; j <= i + dynamicWindowSize; j++) { if (data[j].low <= current.low) { isValley = false; break; } } if (isPeak) { potentialPeaksValleys.push({ index: i, price: current.high, date: current.timestamp, type: 'peak', }); } else if (isValley) { potentialPeaksValleys.push({ index: i, price: current.low, date: current.timestamp, type: 'valley', }); } } // 第二阶段:过滤噪声和过近的峰谷点 const filteredPeaksValleys = []; // 按时间顺序排序 potentialPeaksValleys.sort((a, b) => a.index - b.index); for (let i = 0; i < potentialPeaksValleys.length; i++) { const current = potentialPeaksValleys[i]; // 检查与最近已添加峰谷的距离 let tooClose = false; for (const existing of filteredPeaksValleys) { const distance = Math.abs(current.index - existing.index); const priceDiff = Math.abs(current.price - existing.price); // 如果时间距离太近或价格变化不显著,则跳过 if (distance < dynamicWindowSize && priceDiff < minSignificantChange) { tooClose = true; break; } } if (!tooClose) { filteredPeaksValleys.push(current); } } // 第三阶段:确保峰谷交替出现 const alternatingPeaksValleys = []; if (filteredPeaksValleys.length > 0) { alternatingPeaksValleys.push(filteredPeaksValleys[0]); for (let i = 1; i < filteredPeaksValleys.length; i++) { const current = filteredPeaksValleys[i]; const last = alternatingPeaksValleys[alternatingPeaksValleys.length - 1]; // 确保峰谷交替 if (current.type !== last.type) { alternatingPeaksValleys.push(current); } else { // 如果类型相同,保留更显著的点 const lastSignificance = calculatePointSignificance(data, last); const currentSignificance = calculatePointSignificance(data, current); if (currentSignificance > lastSignificance) { alternatingPeaksValleys[alternatingPeaksValleys.length - 1] = current; } } } } // 当启用最近数据强调时,对峰谷点按照与当前时间的接近程度进行排序 if (recentEmphasis && alternatingPeaksValleys.length > 0) { const lastIndex = data.length - 1; // 对每个峰谷点的重要性进行加权 for (const point of alternatingPeaksValleys) { // 计算与最后一个K线的距离 const distance = lastIndex - point.index; // 添加一个重要性属性,随着距离增加而降低 point.importance = Math.exp(-patternConfig.weights.peakImportanceDecay * distance); } // 按照新的重要性属性排序 alternatingPeaksValleys.sort((a, b) => b.importance - a.importance); } return alternatingPeaksValleys; } /** * 计算峰谷点的显著性,用于过滤和比较 */ function calculatePointSignificance(data, point) { const windowSize = 5; const startIndex = Math.max(0, point.index - windowSize); const endIndex = Math.min(data.length - 1, point.index + windowSize); let significance = 0; if (point.type === 'peak') { // 对于峰点,计算比周围高点的突出程度 for (let i = startIndex; i <= endIndex; i++) { if (i !== point.index) { significance += Math.max(0, point.price - data[i].high); } } } else { // 对于谷点,计算比周围低点的突出程度 for (let i = startIndex; i <= endIndex; i++) { if (i !== point.index) { significance += Math.max(0, data[i].low - point.price); } } } // 标准化显著性 const avgPrice = data.reduce((sum, d) => sum + d.close, 0) / data.length; return significance / (windowSize * avgPrice); } /** * 形态与时间周期的匹配配置 */ const PATTERN_TIMEFRAME_CONFIG = { // 基础形态(所有时间周期都检测) base: { patterns: ['findTriangles'], timeframes: ['weekly', 'daily', '1hour'], }, // 长期形态 long: { patterns: [ 'findHeadAndShoulders', 'findDoubleTopsAndBottoms', 'findCupAndHandle', 'findRoundingPatterns', ], timeframes: ['weekly'], }, // 中长期形态 mediumLong: { patterns: [ 'findWedges', 'findHeadAndShoulders', 'findDoubleTopsAndBottoms', ], timeframes: ['weekly', 'daily'], }, // 中短期形态 mediumShort: { patterns: ['findFlagsAndPennants'], timeframes: ['daily', '1hour'], }, // 短期形态 short: { patterns: ['findBuyingClimax', 'findSellingClimax'], timeframes: ['1hour'], }, }; /** * 形态检测函数映射 */ const PATTERN_DETECTORS = { findTriangles, findHeadAndShoulders, findDoubleTopsAndBottoms, findCupAndHandle, findRoundingPatterns, findWedges, findFlagsAndPennants, findBuyingClimax, findSellingClimax, }; /** * 主函数:根据时间周期分析适合的形态,增强最近形态的重要性 */ function analyzeAllPatterns(rawData, timeframe) { // 仅保留最近N根K线 const data = rawData.slice(-patternConfig.windows.sliceRecentCount); // 检测所有峰谷点 const peaksValleys = detectPeaksAndValleys(data); // 初始化形态数组 let allPatterns = []; // 遍历配置,执行符合当前时间周期的形态检测 for (const [category, config] of Object.entries(PATTERN_TIMEFRAME_CONFIG)) { if (config.timeframes.includes(timeframe)) { for (const patternName of config.patterns) { const detector = PATTERN_DETECTORS[patternName]; const patterns = detector(data, peaksValleys); allPatterns = [...allPatterns, ...patterns]; } } } // 计算当前最后一根K线的索引 const lastIndex = data.length - 1; // 调整形态重要性和可靠性 allPatterns.forEach(pattern => { const distanceFromCurrent = lastIndex - pattern.component.endIndex; const distanceFactor = Math.exp(-patternConfig.weights.patternDistanceDecay * distanceFromCurrent); // 根据形态类型和时间周期的匹配度调整可靠性 const timeframeMatchFactor = calculateTimeframeMatchFactor(pattern.patternType, timeframe); // 调整形态的可靠性和重要性 pattern.reliability = Math.min(100, pattern.reliability * timeframeMatchFactor); pattern.significance = pattern.significance * distanceFactor * timeframeMatchFactor; // 为已确认突破的形态和正在形成的形态调整权重 if (pattern.status === PatternStatus.Confirmed) { pattern.significance *= patternConfig.weights.confirmedBoost; } if (pattern.status === PatternStatus.Forming && pattern.breakoutExpected && distanceFromCurrent < patternConfig.windows.formingNearDistance) { pattern.significance *= patternConfig.weights.formingBoost; } }); // 按照调整后的可靠性和重要性排序 allPatterns.sort((a, b) => b.reliability * b.significance - a.reliability * a.significance); // 确定主导形态和形态综合信号 const dominantPattern = allPatterns.length > 0 ? allPatterns[0] : undefined; const patternSignal = calculatePatternSignal(allPatterns); return { timeframe, patterns: allPatterns, dominantPattern, patternSignal, }; } /** * 计算时间周期匹配因子 */ function calculateTimeframeMatchFactor(patternType, timeframe) { const longTermPatterns = [ PatternType.HeadAndShoulders, PatternType.InverseHeadAndShoulders, PatternType.DoubleTop, PatternType.DoubleBottom, PatternType.TripleTop, PatternType.TripleBottom, PatternType.CupAndHandle, PatternType.RoundingBottom, PatternType.RoundingTop, ]; const mediumTermPatterns = [ PatternType.AscendingTriangle, PatternType.DescendingTriangle, PatternType.SymmetricalTriangle, PatternType.RisingWedge, PatternType.FallingWedge, PatternType.Rectangle, ]; const shortTermPatterns = [ PatternType.Flag, PatternType.Pennant, PatternType.BuyingClimax, PatternType.SellingClimax, ]; if (longTermPatterns.includes(patternType)) { return timeframe === 'weekly' ? 1.3 : timeframe === 'daily' ? 1.1 : 0.8; } if (mediumTermPatterns.includes(patternType)) { return timeframe === 'daily' ? 1.3 : timeframe === 'weekly' ? 1.1 : 0.9; } if (shortTermPatterns.includes(patternType)) { return timeframe === '1hour' ? 1.3 : timeframe === 'daily' ? 1.0 : 0.7; } return 1.0; } /** * 计算形态综合信号 */ function calculatePatternSignal(patterns) { const recentPatternCount = Math.min(patternConfig.signals.recentCount, patterns.length); const recentPatterns = patterns.slice(0, recentPatternCount); let bullishScore = 0; let bearishScore = 0; for (const pattern of recentPatterns) { const patternWeight = pattern.reliability * pattern.significance; if (pattern.direction === PatternDirection.Bullish) { bullishScore += patternWeight; } else if (pattern.direction === PatternDirection.Bearish) { bearishScore += patternWeight; } } if (bullishScore > bearishScore * patternConfig.signals.strongRatio) { return PatternDirection.Bullish; } else if (bearishScore > bullishScore * patternConfig.signals.strongRatio) { return PatternDirection.Bearish; } else if (bullishScore > bearishScore) { return PatternDirection.Bullish; } else if (bearishScore > bullishScore) { return PatternDirection.Bearish; } return PatternDirection.Neutral; } /** * 综合多时间周期的形态分析,更注重最近形态 * @param timeframeAnalyses 各时间周期的形态分析结果 */ function combinePatternAnalyses(timeframeAnalyses) { // 计算综合信号 let bullishCount = 0; let bearishCount = 0; let neutralCount = 0; // 对不同时间周期的信号进行加权 const timeframeWeights = patternConfig.weights.timeframe; for (const analysis of timeframeAnalyses) { const weight = timeframeWeights[analysis.timeframe] || 1.0; if (analysis.patternSignal === PatternDirection.Bullish) { bullishCount += weight; } else if (analysis.patternSignal === PatternDirection.Bearish) { bearishCount += weight; } else { neutralCount += weight; } } let combinedSignal; if (bullishCount > bearishCount * patternConfig.signals.combineBiasRatio) { combinedSignal = PatternDirection.Bullish; } else if (bearishCount > bullishCount * patternConfig.signals.combineBiasRatio) { combinedSignal = PatternDirection.Bearish; } else { combinedSignal = PatternDirection.Neutral; } // 计算信号强度 let signalStrength = 50; // 中性起点 const totalWeight = Object.values(timeframeWeights).reduce((sum, weight) => sum + weight, 0); if (combinedSignal === PatternDirection.Bullish) { signalStrength += 20 * (bullishCount / totalWeight); signalStrength += 15 * (bullishCount > totalWeight / 2 ? 1 : bullishCount / (totalWeight / 2)); const hourlyAnalysis = timeframeAnalyses.find(a => a.timeframe === '1hour'); if (hourlyAnalysis && hourlyAnalysis.patternSignal === PatternDirection.Bullish) { signalStrength += 10; } const dailyAnalysis = timeframeAnalyses.find(a => a.timeframe === 'daily'); if (dailyAnalysis && dailyAnalysis.patternSignal === PatternDirection.Bullish) { signalStrength += 15; } } else if (combinedSignal === PatternDirection.Bearish) { signalStrength += 20 * (bearishCount / totalWeight); signalStrength += 15 * (bearishCount > totalWeight / 2 ? 1 : bearishCount / (totalWeight / 2)); const hourlyAnalysis = timeframeAnalyses.find(a => a.timeframe === '1hour'); if (hourlyAnalysis && hourlyAnalysis.patternSignal === PatternDirection.Bearish) { signalStrength += 10; } const dailyAnalysis = timeframeAnalyses.find(a => a.timeframe === 'daily'); if (dailyAnalysis && dailyAnalysis.patternSignal === PatternDirection.Bearish) { signalStrength += 15; } } for (const analysis of timeframeAnalyses) { if (analysis.dominantPattern) { const pattern = analysis.dominantPattern; if (pattern.reliability > patternConfig.signals.reliabilityBoostThreshold) { signalStrength += patternConfig.signals.recencyMediumBonus; } if (pattern.component) { const patternEndIndex = pattern.component.endIndex; const estimatedDataLength = patternEndIndex + (patternEndIndex - pattern.component.startIndex); const recencyRatio = patternEndIndex / estimatedDataLength; if (recencyRatio > patternConfig.signals.recencyHighThreshold) { signalStrength += patternConfig.signals.recencyHighBonus; } else if (recencyRatio > patternConfig.signals.recencyMediumThreshold) { signalStrength += patternConfig.signals.recencyMediumBonus; } } } } signalStrength = Math.max(0, Math.min(100, signalStrength)); let description = ''; if (combinedSignal === PatternDirection.Bullish) { description = `综合形态分析显示看涨信号`; } else if (combinedSignal === PatternDirection.Bearish) { description = `综合形态分析显示看跌信号`; } else { description = `综合形态分析显示中性信号`; } description += `,信号强度: ${signalStrength.toFixed(2)}/100。`; const hourlySignal = timeframeAnalyses.find(a => a.timeframe === '1hour')?.patternSignal; const dailySignal = timeframeAnalyses.find(a => a.timeframe === 'daily')?.patternSignal; const weeklySignal = timeframeAnalyses.find(a => a.timeframe === 'weekly')?.patternSignal; if (hourlySignal === dailySignal && hourlySignal === weeklySignal && hourlySignal !== PatternDirection.Neutral) { description += ` 短期和长期形态分析一致${hourlySignal === PatternDirection.Bullish ? '看涨' : '看跌'},信号非常可靠。`; } else if (hourlySignal === dailySignal && hourlySignal !== PatternDirection.Neutral) { description += ` 短期和中期形态分析一致${hourlySignal === PatternDirection.Bullish ? '看涨' : '看跌'},信号较为可靠。`; } else if (dailySignal === weeklySignal && dailySignal !== PatternDirection.Neutral) { description += ` 中期和长期形态分析一致${dailySignal === PatternDirection.Bullish ? '看涨' : '看跌'},但短期可能有波动。`; } else if (hourlySignal !== PatternDirection.Neutral) { description += ` 短期形态分析显示${hourlySignal === PatternDirection.Bullish ? '看涨' : '看跌'},建议关注短线机会。`; } const hourlyAnalyses = timeframeAnalyses.find(a => a.timeframe === '1hour'); const dailyAnalyses = timeframeAnalyses.find(a => a.timeframe === 'daily'); const hourlyOtherPatternsDesc = hourlyAnalyses?.patterns .filter(p => p !== hourlyAnalyses.dominantPattern) .map(p => { const datePriceMapping = _.zip(p.keyDates, p.keyPrices); return `${p.patternType} ${datePriceMapping.map(([date, price]) => `${toEDTString(date)} @ (${price.toFixed(2)})`).join(' | ')}`; }) .join('\n'); const dailyOtherPatternsDesc = dailyAnalyses?.patterns .filter(p => p !== dailyAnalyses.dominantPattern) .map(p => { const datePriceMapping = _.zip(p.keyDates, p.keyPrices); return `${p.patternType} ${datePriceMapping.map(([date, price]) => `${toEDTString(date)} @ (${price.toFixed(2)})`).join(' | ')}`; }) .join('\n'); if (hourlyAnalyses?.dominantPattern) { const datePriceMapping = _.zip(hourlyAnalyses.dominantPattern.keyDates, hourlyAnalyses.dominantPattern.keyPrices); description += `\n\n小时线主导形态: ${hourlyAnalyses.dominantPattern.patternType} \n 关键时间: ${datePriceMapping.map(([date, price]) => `${toEDTString(date)} @ (${price.toFixed(2)})`).join(' | ')}, (${hourlyAnalyses.dominantPattern.direction === PatternDirection.Bullish ? '看涨' : '看跌'}),可靠性: ${hourlyAnalyses.dominantPattern.reliability.toFixed(2)}/100。`; } if (dailyAnalyses?.dominantPattern) { const datePriceMapping = _.zip(dailyAnalyses.dominantPattern.keyDates, dailyAnalyses.dominantPattern.keyPrices); description += `\n\n日线主导形态: ${dailyAnalyses.dominantPattern.patternType} \n 关键时间: ${datePriceMapping.map(([date, price]) => `${toEDTString(date)} @ (${price.toFixed(2)})`).join(' | ')}, (${dailyAnalyses.dominantPattern.direction === PatternDirection.Bullish ? '看涨' : '看跌'}),可靠性: ${dailyAnalyses.dominantPattern.reliability.toFixed(2)}/100。`; } if (hourlyOtherPatternsDesc) { description += `\n\n小时线其他形态:\n ${hourlyOtherPatternsDesc}`; } if (dailyOtherPatternsDesc) { description += `\n\n日线其他形态:\n ${dailyOtherPatternsDesc}`; } return { timeframeAnalyses, combinedSignal, signalStrength, description, }; } /** * 导出的API函数:多时间周期的价格形态分析 */ function analyzeMultiTimeframePatterns(weeklyData, dailyData, hourlyData) { const weeklyAnalysis = analyzeAllPatterns(weeklyData, 'weekly'); const dailyAnalysis = analyzeAllPatterns(dailyData, 'daily'); const hourlyAnalysis = analyzeAllPatterns(hourlyData, '1hour'); return combinePatternAnalyses([ weeklyAnalysis, dailyAnalysis, hourlyAnalysis, ]); } /** * 格式化并打印形态分析结果 * @param analysisResult 综合形态分析结果 * @param symbol 股票代码 */ function formatAndPrintPatternAnalysis(analysisResult, symbol = '') { console.log(`\n===== ${symbol ? symbol + ' ' : ''}形态分析综合结果 =====`); console.log(`${analysisResult.description}`); console.log(`信号强度: ${analysisResult.signalStrength.toFixed(2)}/100`); const patternCountsByTimeframe = analysisResult.timeframeAnalyses.map(tfa => { const bullishPatterns = tfa.patterns.filter(p => p.direction === PatternDirection.Bullish); const bearishPatterns = tfa.patterns.filter(p => p.direction === PatternDirection.Bearish); const neutralPatterns = tfa.patterns.filter(p => p.direction === PatternDirection.Neutral); return { timeframe: tfa.timeframe, bullish: bullishPatterns.length, bearish: bearishPatterns.length, neutral: neutralPatterns.length, total: tfa.patterns.length, signal: tfa.patternSignal, }; }); console.log('\n----- 各时间周期信号分布 -----'); console.log('时间周期 | 看涨 | 看空 | 中性 | 总计 | 综合信号'); console.log('----------|-------|-------|-------|-------|--------'); patternCountsByTimeframe.forEach(count => { const timeframeLabel = count.timeframe === 'weekly' ? '周线 ' : count.timeframe === 'daily' ? '日线 ' : '小时线 '; const signalText = count.signal === PatternDirection.Bullish ? '看涨' : count.signal === PatternDirection.Bearish ? '看空' : '中性'; console.log(`${timeframeLabel}| ${count.bullish.toString().padEnd(5)} | ${count.bearish.toString().padEnd(5)} | ${count.neutral.toString().padEnd(5)} | ${count.total.toString().padEnd(5)} | ${signalText}`); }); console.log('\n===== 主导形态分析 ====='); analysisResult.timeframeAnalyses.forEach(tfa => { const timeframeLabel = tfa.timeframe === 'weekly' ? '周线' : tfa.timeframe === 'daily' ? '日线' : '小时线'; console.log(`\n----- ${timeframeLabel}主导形态 -----`); if (tfa.dominantPattern) { const pattern = tfa.dominantPattern; const patternTypeMap = { head_and_shoulders: '头肩顶', inverse_head_and_shoulders: '头肩底', double_top: '双顶', double_bottom: '双底', triple_top: '三重顶', triple_bottom: '三重底', ascending_triangle: '上升三角形', descending_triangle: '下降三角形', symmetrical_triangle: '对称三角形', rising_wedge: '上升楔形', falling_wedge: '下降楔形', rectangle: '矩形', flag: '旗形', pennant: '三角旗', cup_and_handle: '杯柄', rounding_bottom: '圆底', rounding_top: '圆顶', }; const statusMap = { forming: '正在形成中', completed: '已完成但未突破', confirmed: '已确认突破', failed: '形成后失败', }; const patternName = patternTypeMap[pattern.patternType] || pattern.patternType; const statusText = statusMap[pattern.status] || pattern.status; const directionText = pattern.direction === PatternDirection.Bullish ? '看涨' : pattern.direction === PatternDirection.Bearish ? '看空' : '中性'; console.log(`形态类型: ${patternName}`); console.log(`方向: ${directionText} | 状态: ${statusText}`); console.log(`可靠性评分: ${pattern.reliability.toFixed(2)}/100 | 重要性: ${pattern.significance.toFixed(2)}/100`); console.log(`目标价位: ${pattern.priceTarget ? pattern.priceTarget.toFixed(2) : '未计算'} | 止损位: ${pattern.stopLoss ? pattern.stopLoss.toFixed(2) : '未计算'}`); if (pattern.probableBreakoutZone) { console.log(`可能突破区间: ${pattern.probableBreakoutZone[0].toFixed(2)} - ${pattern.probableBreakoutZone[1].toFixed(2)}`); } console.log(`形态描述: ${pattern.description}`); console.log(`交易含义: ${pattern.tradingImplication}`); } else { console.log('未检测到显著形态'); } const bullishPatterns = tfa.patterns.filter(p => p.direction === PatternDirection.Bullish); const bearishPatterns = tfa.patterns.filter(p => p.direction === PatternDirection.Bearish); if (bullishPatterns.length > 0 || bearishPatterns.length > 0) { console.log(`\n${timeframeLabel}检测到的其他形态:`); if (bullishPatterns.length > 0) { console.log(' 看涨形态:'); bullishPatterns.slice(0, 3).forEach((p, idx) => { if (idx === 0 && p === tfa.dominantPattern) return; console.log(` - ${p.patternType} (可靠性: ${p.reliability.toFixed(2)})`); }); if (bullishPatterns.length > 3) console.log(` ... 等共${bullishPatterns.length}个看涨形态`); } if (bearishPatterns.length > 0) { console.log(' 看空形态:'); bearishPatterns.slice(0, 3).forEach((p, idx) => { if (idx === 0 && p === tfa.dominantPattern) return; console.log(` - ${p.patternType} (可靠性: ${p.reliability.toFixed(2)})`); }); if (bearishPatterns.length > 3) console.log(` ... 等共${bearishPatterns.length}个看空形态`); } } }); console.log('\n===== 关键价位分析 ====='); analysisResult.timeframeAnalyses.forEach(tfa => { const timeframeLabel = tfa.timeframe === 'weekly' ? '周线' : tfa.timeframe === 'daily' ? '日线' : '小时线'; const supportLevels = []; const resistanceLevels = []; tfa.patterns.forEach(p => { if (p.direction === PatternDirection.Bullish) { resistanceLevels.push(p.component.breakoutLevel); if (p.stopLoss) supportLevels.push(p.stopLoss); } else if (p.direction === PatternDirection.Bearish) { supportLevels.push(p.component.breakoutLevel); if (p.stopLoss) resistanceLevels.push(p.stopLoss); } if (p.priceTarget) { if (p.direction === PatternDirection.Bullish) { resistanceLevels.push(p.priceTarget); } else if (p.direction === PatternDirection.Bearish) { supportLevels.push(p.priceTarget); } } }); const uniqueSupportLevels = [...new Set(supportLevels)].sort((a, b) => a - b); const uniqueResistanceLevels = [...new Set(resistanceLevels)].sort((a, b) => a - b); console.log(`\n----- ${timeframeLabel}关键价位 -----`); if (uniqueResistanceLevels.length > 0) { console.log(`阻力位: ${uniqueResistanceLevels.map(l => l.toFixed(2)).join(', ')}`); } else { console.log('未检测到明确的阻力位'); } if (uniqueSupportLevels.length > 0) { console.log(`支撑位: ${uniqueSupportLevels.map(l => l.toFixed(2)).join(', ')}`); } else { console.log('未检测到明确的支撑位'); } }); console.log('\n===== 形态分析总结 ====='); const overallDirection = analysisResult.combinedSignal === PatternDirection.Bullish ? '看涨' : analysisResult.combinedSignal === PatternDirection.Bearish ? '看空' : '中性'; const timeframeDirections = analysisResult.timeframeAnalyses.map(tfa => tfa.patternSignal); const allBullish = timeframeDirections.every(dir => dir === PatternDirection.Bullish); const allBearish = timeframeDirections.every(dir => dir === PatternDirection.Bearish); let consistencyMessage = ''; if (allBullish) { consistencyMessage = '所有时间周期一致看涨,信号强度高'; } else if (allBearish) { consistencyMessage = '所有时间周期一致看空,信号强度高'; } else { const bullishCount = timeframeDirections.filter(dir => dir === PatternDirection.Bullish).length; const bearishCount = timeframeDirections.filter(dir => dir === PatternDirection.Bearish).length; const neutralCount = timeframeDirections.filter(dir => dir === PatternDirection.Neutral).length; if (bullishCount > bearishCount && bullishCount > neutralCount) { consistencyMessage = `偏向看涨,但时间周期一致性不强 (${bullishCount}看涨/${bearishCount}看空/${neutralCount}中性)`; } else if (bearishCount > bullishCount && bearishCount > neutralCount) { consistencyMessage = `偏向看空,但时间周期一致性不强 (${bullishCount}看涨/${bearishCount}看空/${neutralCount}中性)`; } else { consistencyMessage = `时间周期信号混合,无明确方向 (${bullishCount}看涨/${bearishCount}看空/${neutralCount}中性)`; } } console.log(`综合方向: ${overallDirection}`); console.log(`时间周期一致性: ${consistencyMessage}`); console.log(`形态分析信号强度: ${analysisResult.signalStrength.toFixed(2)}/100`); console.log('\n----- 形态分析交易建议 -----'); if (analysisResult.signalStrength > 70) { if (analysisResult.combinedSignal === PatternDirection.Bullish) { console.log('强烈看涨信号,建议考虑做多策略'); } else if (analysisResult.combinedSignal === PatternDirection.Bearish) { console.log('强烈看空信号,建议考虑做空策略'); } } else if (analysisResult.signalStrength > 50) { if (analysisResult.combinedSignal === PatternDirection.Bullish) { console.log('中等强度看涨信号,可考虑小仓位做多或等待更多确认'); } else if (analysisResult.combinedSignal === PatternDirection.Bearish) { console.log('中等强度看空信号,可考虑小仓位做空或等待更多确认'); } } else { console.log('信号强度不足,建议观望或寻求其他分析方法确认'); } const mostReliablePattern = analysisResult.timeframeAnalyses .flatMap(tfa => tfa.patterns) .sort((a, b) => b.reliability - a.reliability)[0]; if (mostReliablePattern && mostReliablePattern.reliability > 60) { const patternTimeframe = analysisResult.timeframeAnalyses.find(tfa => tfa.patterns.includes(mostReliablePattern))?.timeframe; const timeframeText = patternTimeframe === 'weekly' ? '周线' : patternTimeframe === 'daily' ? '日线' : '小时线'; console.log(`\n参考最可靠形态 (${timeframeText}): ${mostReliablePattern.patternType} (可靠性: ${mostReliablePattern.reliability.toFixed(2)})`); console.log(`形态描述: ${mostReliablePattern.description}`); console.log(`交易含义: ${mostReliablePattern.tradingImplication}`); } } export { PatternType, PatternStatus, PatternDirection, analyzeAllPatterns, combinePatternAnalyses, analyzeMultiTimeframePatterns, formatAndPrintPatternAnalysis, };