@gabriel3615/ta_analysis
Version:
stock ta analysis
801 lines (800 loc) • 35.7 kB
JavaScript
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, };