UNPKG

@gabriel3615/ta_analysis

Version:

stock ta analysis

378 lines (377 loc) 13.6 kB
/** * 关键位合并与管理模块 * 负责从各个分析模块收集关键位,进行合并和去重处理 */ export class KeyLevelManager { constructor(config) { this.config = config; } /** * 从所有分析结果中提取并合并关键位 */ extractAndMergeKeyLevels(input, context) { const allLevels = []; // 从各个模块提取关键位 allLevels.push(...this.extractChipKeyLevels(input.analyses.chip)); allLevels.push(...this.extractPatternKeyLevels(input.analyses.pattern)); allLevels.push(...this.extractBBSRKeyLevels(input.analyses.bbsr)); allLevels.push(...this.extractStructureKeyLevels(input.analyses.structure)); allLevels.push(...this.extractSupplyDemandKeyLevels(input.analyses.supplyDemand)); allLevels.push(...this.extractRangeKeyLevels(input.analyses.range)); allLevels.push(...this.extractTrendlineKeyLevels(input.analyses.trendline)); const originalCount = allLevels.length; // 排序关键位 const sortedLevels = this.sortKeyLevels(allLevels); // 合并相近的关键位 const mergedLevels = this.mergeNearbyLevels(sortedLevels); const mergedCount = mergedLevels.length; const compressionRatio = originalCount > 0 ? mergedCount / originalCount : 1; return { mergedLevels, originalCount, mergedCount, compressionRatio, }; } /** * 从筹码分析提取关键位 */ extractChipKeyLevels(chipAnalysis) { const levels = []; const timeframe = chipAnalysis.primaryTimeframe; // 使用聚合的支撑/阻力位 chipAnalysis.aggregatedSupportLevels?.forEach((price, index) => { levels.push({ price, type: 'support', strength: index < 2 ? 'strong' : 'moderate', source: 'chip', timeframe, description: `筹码聚合支撑位 #${index + 1}`, }); }); chipAnalysis.aggregatedResistanceLevels?.forEach((price, index) => { levels.push({ price, type: 'resistance', strength: index < 2 ? 'strong' : 'moderate', source: 'chip', timeframe, description: `筹合聚合阻力位 #${index + 1}`, }); }); return levels; } /** * 从形态分析提取关键位 */ extractPatternKeyLevels(patternAnalysis) { const levels = []; // 从各时间框架主导形态中提取关键位(突破位、止损位) patternAnalysis.timeframeAnalyses?.forEach(tfa => { const dp = tfa.dominantPattern; if (!dp) return; // 突破位作为关键位 const breakout = dp.component?.breakoutLevel; if (typeof breakout === 'number') { levels.push({ price: breakout, type: dp.direction === 'bullish' ? 'resistance' : 'support', strength: dp.reliability > 70 ? 'strong' : 'moderate', source: 'pattern', timeframe: tfa.timeframe, description: `形态突破位 (${tfa.timeframe})`, }); } // 止损位 if (typeof dp.stopLoss === 'number') { levels.push({ price: dp.stopLoss, type: dp.direction === 'bullish' ? 'support' : 'resistance', strength: 'moderate', source: 'pattern', timeframe: tfa.timeframe, description: `形态止损位 (${tfa.timeframe})`, }); } }); return levels; } /** * 从BBSR分析提取关键位 */ extractBBSRKeyLevels(bbsrAnalysis) { const levels = []; // 从BBSR日线和周线结果中提取关键位 const bbsrSignals = [ bbsrAnalysis.dailyBBSRResult, bbsrAnalysis.weeklyBBSRResult, ].filter(Boolean); bbsrSignals.forEach(signal => { levels.push({ price: signal.SRLevel, type: signal.signal.patternType === 'bullish' ? 'support' : 'resistance', strength: signal.strength > 70 ? 'strong' : 'moderate', source: 'bbsr', timeframe: 'daily', description: `BBSR关键信号位`, }); }); return levels; } /** * 从结构分析提取关键位 */ extractStructureKeyLevels(structureAnalysis) { const levels = []; // 结构突破位 // 部分结构实现可能没有 lastBreakLevel,这里仅基于趋势与基本关键点提取 // 关键摆动点 structureAnalysis.swingPoints?.forEach((point, index) => { if (index < 3) { // 只取最近3个摆动点 levels.push({ price: point.price, type: point.type === 'high' ? 'resistance' : 'support', strength: 'moderate', source: 'structure', timeframe: structureAnalysis.timeframe, description: `结构摆动${point.type === 'high' ? '高' : '低'}点`, }); } }); return levels; } /** * 从供需分析提取关键位 */ extractSupplyDemandKeyLevels(sdAnalysis) { const levels = []; // 供需区域 sdAnalysis.zones?.forEach((zone, index) => { if (zone.status === 'fresh' || zone.status === 'tested') { // 区域上边界 levels.push({ price: zone.upper ?? zone.high, type: zone.type === 'demand' ? 'support' : 'resistance', strength: zone.status === 'fresh' ? 'strong' : 'moderate', source: 'supplyDemand', timeframe: sdAnalysis.timeframe, description: `${zone.type === 'demand' ? '需求' : '供应'}区域上边界`, }); // 区域下边界 levels.push({ price: zone.lower ?? zone.low, type: zone.type === 'demand' ? 'support' : 'resistance', strength: zone.status === 'fresh' ? 'strong' : 'moderate', source: 'supplyDemand', timeframe: sdAnalysis.timeframe, description: `${zone.type === 'demand' ? '需求' : '供应'}区域下边界`, }); } }); return levels; } /** * 从区间分析提取关键位 */ extractRangeKeyLevels(rangeAnalysis) { const levels = []; // 区间上下边界 if (rangeAnalysis.range) { levels.push({ price: rangeAnalysis.range.upper ?? rangeAnalysis.range.high, type: 'resistance', strength: 'moderate', source: 'range', timeframe: rangeAnalysis.timeframe, description: '区间上边界', }); levels.push({ price: rangeAnalysis.range.lower ?? rangeAnalysis.range.low, type: 'support', strength: 'moderate', source: 'range', timeframe: rangeAnalysis.timeframe, description: '区间下边界', }); } return levels; } /** * 从趋势线分析提取关键位 */ extractTrendlineKeyLevels(trendlineAnalysis) { const levels = []; // 趋势通道边界 if (trendlineAnalysis.channel) { // trendline types 使用 upper/lower 或者 upperBand/lowerBand,这里兼容 const upper = trendlineAnalysis.channel.upperBand ?? trendlineAnalysis.channel.upper?.high; const lower = trendlineAnalysis.channel.lowerBand ?? trendlineAnalysis.channel.lower?.low; if (typeof upper === 'number') { levels.push({ price: upper, type: 'resistance', strength: 'moderate', source: 'trendline', timeframe: trendlineAnalysis.timeframe, description: '趋势通道上边界', }); } if (typeof lower === 'number') { levels.push({ price: lower, type: 'support', strength: 'moderate', source: 'trendline', timeframe: trendlineAnalysis.timeframe, description: '趋势通道下边界', }); } } return levels; } /** * 排序关键位(按价格) */ sortKeyLevels(levels) { return [...levels].sort((a, b) => a.price - b.price); } /** * 合并相近的关键位 */ mergeNearbyLevels(sortedLevels) { if (sortedLevels.length === 0) return []; const mergedLevels = []; const mergeTolerancePercent = 0.01; // 1%的容忍度 for (let i = 0; i < sortedLevels.length; i++) { const currentLevel = sortedLevels[i]; if (mergedLevels.length === 0) { mergedLevels.push({ ...currentLevel }); continue; } const lastMerged = mergedLevels[mergedLevels.length - 1]; const priceDiff = Math.abs(currentLevel.price - lastMerged.price); const averagePrice = (currentLevel.price + lastMerged.price) / 2; const tolerance = averagePrice * mergeTolerancePercent; if (priceDiff <= tolerance) { // 合并相近的价位 this.mergeTwoLevels(lastMerged, currentLevel); } else { mergedLevels.push({ ...currentLevel }); } } return mergedLevels; } /** * 合并两个关键位 */ mergeTwoLevels(target, source) { // 使用加权平均价格 const totalWeight = this.getStrengthWeight(target.strength) + this.getStrengthWeight(source.strength); const targetWeight = this.getStrengthWeight(target.strength) / totalWeight; const sourceWeight = this.getStrengthWeight(source.strength) / totalWeight; target.price = target.price * targetWeight + source.price * sourceWeight; // 如果类型相同,提升强度 if (target.type === source.type) { if (source.strength === 'strong' || target.strength === 'strong') { target.strength = 'strong'; } } // 合并来源信息 if (target.source !== source.source) { target.source = 'combined'; target.description = `${target.description} + ${source.description}`; } // 选择更高级别的时间周期 if (this.getTimeframeRank(source.timeframe) > this.getTimeframeRank(target.timeframe)) { target.timeframe = source.timeframe; } } /** * 获取强度权重 */ getStrengthWeight(strength) { switch (strength) { case 'strong': return 3; case 'moderate': return 2; case 'weak': return 1; default: return 1; } } /** * 获取时间周期等级 */ getTimeframeRank(timeframe) { switch (timeframe) { case 'weekly': return 3; case 'daily': return 2; case '1hour': return 1; default: return 1; } } /** * 过滤关键位(按距离当前价格的距离) */ filterKeyLevelsByDistance(levels, currentPrice, maxDistancePercent = 0.1) { return levels.filter(level => { const distance = Math.abs(level.price - currentPrice) / currentPrice; return distance <= maxDistancePercent; }); } /** * 获取最强的关键位 */ getStrongestLevels(levels, count = 5) { return levels .sort((a, b) => { // 先按强度排序,再按来源权重排序 const strengthDiff = this.getStrengthWeight(b.strength) - this.getStrengthWeight(a.strength); if (strengthDiff !== 0) return strengthDiff; return this.getSourceWeight(b.source) - this.getSourceWeight(a.source); }) .slice(0, count); } /** * 获取来源权重 */ getSourceWeight(source) { switch (source) { case 'combined': return 5; case 'chip': return 4; case 'structure': return 4; case 'bbsr': return 3; case 'pattern': return 3; case 'supplyDemand': return 3; case 'trendline': return 2; case 'range': return 2; default: return 1; } } }