@gabriel3615/ta_analysis
Version:
stock ta analysis
348 lines (347 loc) • 13.8 kB
JavaScript
import { calculateADLine, calculateChaikinOscillator, calculateMoneyFlowIndex, calculateOBV, calculateSlope, calculateVolumeForce, } from './taUtil.js';
import { PatternDirection } from '../analysis/basic/patterns/analyzeMultiTimeframePatterns.js';
/**
* 计算积累分布线及相关的量价指标
* @param data K线数据
* @param lookbackPeriod 回溯期(默认为20个交易日)
*/
export function calculateAccumulationDistribution(data, lookbackPeriod = 20) {
if (data.length < lookbackPeriod) {
throw new Error('数据不足以进行积累分布线分析');
}
// 计算积累分布线 (A/D Line)
const adLine = calculateADLine(data);
// 计算积累分布线的近期斜率
const recentAD = adLine.slice(-lookbackPeriod);
const adSlope = calculateSlope(recentAD);
// 确定积累分布线趋势
const adTrend = determineADTrend(adLine, lookbackPeriod);
// 检测背离
const divergence = detectDivergence(data, adLine, lookbackPeriod);
// 计算成交量力量指标
const volumeForce = calculateVolumeForce(data, lookbackPeriod);
// 计算资金流指标 (MFI)
const mfi = calculateMoneyFlowIndex(data, 14);
// 计算蔡金摆动指标
const chaikinOsc = calculateChaikinOscillator(adLine);
// 计算能量潮 (OBV)
const obv = calculateOBV(data);
// 对OBV进行归一化处理再计算斜率,避免出现异常大的斜率值
const recentOBV = obv.slice(-lookbackPeriod);
const maxAbsOBV = Math.max(...recentOBV.map(v => Math.abs(v)));
const normalizedOBV = maxAbsOBV === 0 ? recentOBV : recentOBV.map(v => v / maxAbsOBV);
const obvSlope = calculateSlope(normalizedOBV);
// 判断价格与成交量是否确认趋势
const volumePriceConfirmation = checkVolumePriceConfirmation(data, lookbackPeriod);
// 生成综合分析摘要
const summary = generateADSummary(adTrend, divergence, volumeForce, mfi, chaikinOsc, obvSlope, volumePriceConfirmation);
return {
adLine,
adSlope,
adTrend,
divergence,
volumeForce,
moneyFlowIndex: mfi,
chaikinOscillator: chaikinOsc,
obv,
obvSlope,
volumePriceConfirmation,
summary,
};
}
/**
* 确定积累分布线趋势
*/
function determineADTrend(adLine, lookbackPeriod) {
if (adLine.length < lookbackPeriod) {
return PatternDirection.Neutral;
}
const recentAD = adLine.slice(-lookbackPeriod);
// 计算AD变化率
const startAD = recentAD[0];
const endAD = recentAD[recentAD.length - 1];
// 防止除以零
if (startAD === 0)
return PatternDirection.Neutral;
const changeRate = ((endAD - startAD) / Math.abs(startAD)) * 100;
if (changeRate > 2) {
return PatternDirection.Bullish;
}
else if (changeRate < -2) {
return PatternDirection.Bearish;
}
else {
return PatternDirection.Neutral;
}
}
/**
* 检测价格与积累分布线之间的背离
*/
function detectDivergence(data, adLine, lookbackPeriod) {
if (data.length < lookbackPeriod || adLine.length < lookbackPeriod) {
return { type: 'none', strength: 0, description: '数据不足以检测背离' };
}
const prices = data.map(d => d.close);
const recentPrices = prices.slice(-lookbackPeriod);
const recentAD = adLine.slice(-lookbackPeriod);
// 找出近期价格的高点和低点
const priceHighs = [];
const priceLows = [];
for (let i = 1; i < recentPrices.length - 1; i++) {
if (recentPrices[i] > recentPrices[i - 1] &&
recentPrices[i] > recentPrices[i + 1]) {
priceHighs.push(i);
}
if (recentPrices[i] < recentPrices[i - 1] &&
recentPrices[i] < recentPrices[i + 1]) {
priceLows.push(i);
}
}
// 找出同期内AD线的高点和低点
const adHighs = [];
const adLows = [];
for (let i = 1; i < recentAD.length - 1; i++) {
if (recentAD[i] > recentAD[i - 1] && recentAD[i] > recentAD[i + 1]) {
adHighs.push(i);
}
if (recentAD[i] < recentAD[i - 1] && recentAD[i] < recentAD[i + 1]) {
adLows.push(i);
}
}
// 检测常规看涨背离:价格创新低但AD线不创新低
if (priceLows.length >= 2 && adLows.length >= 2) {
const lastPriceLow = priceLows[priceLows.length - 1];
const prevPriceLow = priceLows[priceLows.length - 2];
const lastAdLow = adLows[adLows.length - 1];
const prevAdLow = adLows[adLows.length - 2];
if (recentPrices[lastPriceLow] < recentPrices[prevPriceLow] &&
recentAD[lastAdLow] > recentAD[prevAdLow]) {
// 计算背离强度
const priceChange = ((recentPrices[lastPriceLow] - recentPrices[prevPriceLow]) /
recentPrices[prevPriceLow]) *
100;
const adChange = ((recentAD[lastAdLow] - recentAD[prevAdLow]) /
Math.abs(recentAD[prevAdLow])) *
100;
const strength = Math.min(100, Math.abs(adChange - priceChange));
return {
type: 'bullish',
strength,
description: '检测到看涨背离:价格创新低但积累分布线未创新低,可能指示底部形成',
};
}
}
// 检测常规看跌背离:价格创新高但AD线不创新高
if (priceHighs.length >= 2 && adHighs.length >= 2) {
const lastPriceHigh = priceHighs[priceHighs.length - 1];
const prevPriceHigh = priceHighs[priceHighs.length - 2];
const lastAdHigh = adHighs[adHighs.length - 1];
const prevAdHigh = adHighs[adHighs.length - 2];
if (recentPrices[lastPriceHigh] > recentPrices[prevPriceHigh] &&
recentAD[lastAdHigh] < recentAD[prevAdHigh]) {
// 计算背离强度
const priceChange = ((recentPrices[lastPriceHigh] - recentPrices[prevPriceHigh]) /
recentPrices[prevPriceHigh]) *
100;
const adChange = ((recentAD[lastAdHigh] - recentAD[prevAdHigh]) /
Math.abs(recentAD[prevAdHigh])) *
100;
const strength = Math.min(100, Math.abs(adChange - priceChange));
return {
type: 'bearish',
strength,
description: '检测到看跌背离:价格创新高但积累分布线未创新高,可能指示顶部形成',
};
}
}
// 检测隐藏看涨背离:价格不创新低但AD线创新低
if (priceLows.length >= 2 && adLows.length >= 2) {
const lastPriceLow = priceLows[priceLows.length - 1];
const prevPriceLow = priceLows[priceLows.length - 2];
const lastAdLow = adLows[adLows.length - 1];
const prevAdLow = adLows[adLows.length - 2];
if (recentPrices[lastPriceLow] > recentPrices[prevPriceLow] &&
recentAD[lastAdLow] < recentAD[prevAdLow]) {
const strength = 70; // 隐藏背离通常强度较低
return {
type: 'hidden_bullish',
strength,
description: '检测到隐藏看涨背离:价格未创新低但积累分布线创新低,可能指示上升趋势将继续',
};
}
}
// 检测隐藏看跌背离:价格不创新高但AD线创新高
if (priceHighs.length >= 2 && adHighs.length >= 2) {
const lastPriceHigh = priceHighs[priceHighs.length - 1];
const prevPriceHigh = priceHighs[priceHighs.length - 2];
const lastAdHigh = adHighs[adHighs.length - 1];
const prevAdHigh = adHighs[adHighs.length - 2];
if (recentPrices[lastPriceHigh] < recentPrices[prevPriceHigh] &&
recentAD[lastAdHigh] > recentAD[prevAdHigh]) {
const strength = 70; // 隐藏背离通常强度较低
return {
type: 'hidden_bearish',
strength,
description: '检测到隐藏看跌背离:价格未创新高但积累分布线创新高,可能指示下降趋势将继续',
};
}
}
return {
type: 'none',
strength: 0,
description: '未检测到明显背离',
};
}
/**
* 检查价格与成交量是否协同确认趋势
*/
function checkVolumePriceConfirmation(data, lookbackPeriod) {
if (data.length < lookbackPeriod) {
return false;
}
const recentData = data.slice(-lookbackPeriod);
// 计算价格趋势
const startPrice = recentData[0].close;
const endPrice = recentData[recentData.length - 1].close;
const priceChange = ((endPrice - startPrice) / startPrice) * 100;
// 计算成交量趋势
const volumeData = recentData.map(d => d.volume);
const volumeSlope = calculateSlope(volumeData);
// 确定是否协同确认
return ((priceChange > 1 && volumeSlope > 0) ||
(priceChange < -1 && volumeSlope < 0));
}
/**
* 生成积累分布线分析摘要
*/
function generateADSummary(adTrend, divergence, volumeForce, mfi, chaikinOsc, obvSlope, volumePriceConfirmation) {
let summary = '';
// 根据AD趋势添加评估
if (adTrend === 'bullish') {
summary += '积累分布线呈上升趋势,表明买方力量占优,支持价格上涨。';
}
else if (adTrend === 'bearish') {
summary += '积累分布线呈下降趋势,表明卖方力量占优,支持价格下跌。';
}
else {
summary += '积累分布线趋势中性,买卖力量相对平衡。';
}
// 添加背离信息
if (divergence.type !== 'none') {
summary += ` ${divergence.description} `;
if (divergence.strength > 80) {
summary += '背离强度很高,可能导致显著的价格反转。';
}
else if (divergence.strength > 50) {
summary += '背离强度中等,需要注意潜在的价格转向。';
}
else {
summary += '背离强度较弱,可能需要更多确认信号。';
}
}
// 添加成交量力量评估
if (volumeForce > 30) {
summary += ' 成交量力量强劲偏向买方,支持价格上涨。';
}
else if (volumeForce < -30) {
summary += ' 成交量力量强劲偏向卖方,支持价格下跌。';
}
else if (volumeForce > 10) {
summary += ' 成交量力量小幅偏向买方。';
}
else if (volumeForce < -10) {
summary += ' 成交量力量小幅偏向卖方。';
}
else {
summary += ' 成交量力量基本平衡。';
}
// 添加资金流指标评估
if (mfi > 80) {
summary += ' MFI指标显示超买状态,可能即将回调。';
}
else if (mfi < 20) {
summary += ' MFI指标显示超卖状态,可能即将反弹。';
}
else if (mfi > 60) {
summary += ' MFI指标处于上方,资金流入占优。';
}
else if (mfi < 40) {
summary += ' MFI指标处于下方,资金流出占优。';
}
// 添加蔡金摆动指标评估
if (chaikinOsc > 0 && obvSlope > 0) {
summary += ' 蔡金摆动指标和OBV斜率均为正,确认买盘力量。';
}
else if (chaikinOsc < 0 && obvSlope < 0) {
summary += ' 蔡金摆动指标和OBV斜率均为负,确认卖盘力量。';
}
else if (chaikinOsc > 0) {
summary += ' 蔡金摆动指标为正,但缺乏OBV确认。';
}
else if (obvSlope > 0) {
summary += ' OBV斜率为正,但缺乏蔡金摆动确认。';
}
// 添加价格与成交量确认信息
if (volumePriceConfirmation) {
summary += ' 价格与成交量变化方向一致,确认当前趋势。';
}
else {
summary += ' 价格与成交量变化方向不一致,可能预示趋势减弱。';
}
return summary;
}
/**
* 格式化积累分布线分析结果为易读的字符串
*/
export function formatAccumulationDistributionAnalysis(analysis) {
let result = '=== 积累分布线分析 ===\n\n';
result += `积累分布线趋势: ${translateTrend(analysis.adTrend)}\n`;
result += `积累分布线斜率: ${analysis.adSlope.toFixed(4)}\n`;
// 添加背离信息
result += `背离类型: ${translateDivergenceType(analysis.divergence.type)}\n`;
result += `背离强度: ${analysis.divergence.strength.toFixed(2)}/100\n`;
result += `背离描述: ${analysis.divergence.description}\n\n`;
// 添加其他量价指标
result += `成交量力量: ${analysis.volumeForce.toFixed(2)} (范围: -100 至 100)\n`;
result += `资金流指标(MFI): ${analysis.moneyFlowIndex.toFixed(2)}\n`;
result += `蔡金摆动指标: ${analysis.chaikinOscillator.toFixed(4)}\n`;
result += `OBV斜率: ${analysis.obvSlope.toFixed(4)}\n`;
result += `价格成交量确认: ${analysis.volumePriceConfirmation ? '是' : '否'}\n\n`;
// 添加总结
result += `分析摘要: ${analysis.summary}\n`;
return result;
}
/**
* 将趋势类型翻译为中文
*/
function translateTrend(trend) {
switch (trend) {
case 'bullish':
return '看涨';
case 'bearish':
return '看跌';
case 'neutral':
return '中性';
default:
return trend;
}
}
/**
* 将背离类型翻译为中文
*/
function translateDivergenceType(type) {
switch (type) {
case 'bullish':
return '看涨背离';
case 'bearish':
return '看跌背离';
case 'hidden_bullish':
return '隐藏看涨背离';
case 'hidden_bearish':
return '隐藏看跌背离';
case 'none':
return '无背离';
default:
return type;
}
}