@gabriel3615/ta_analysis
Version:
stock ta analysis
343 lines (342 loc) • 15 kB
JavaScript
import { toEDTString } from '../util/util.js';
import { calculateWeightedReward } from '../util/analysisUtils.js';
/**
* 构建机器可解析摘要(单行 JSON 字符串)
*/
function buildMachineReadableSummary(tradePlan) {
const entry = tradePlan.entryStrategy.idealEntryPrice;
const stopPrices = tradePlan.exitStrategy.stopLossLevels.map(l => l.price);
const stopUsed = (() => {
if (tradePlan.direction === 'long') {
const below = stopPrices.filter(p => p < entry);
return below.length > 0 ? Math.max(...below) : (stopPrices[0] ?? entry);
}
else if (tradePlan.direction === 'short') {
const above = stopPrices.filter(p => p > entry);
return above.length > 0 ? Math.min(...above) : (stopPrices[0] ?? entry);
}
return stopPrices[0] ?? entry;
})();
const weightedReward = calculateWeightedReward(tradePlan.direction, entry, tradePlan.exitStrategy.takeProfitLevels);
const riskUsed = Math.max(1e-8, Math.abs(entry - stopUsed));
const rr = Math.max(0, weightedReward) / riskUsed;
const jsonSummary = {
symbol: tradePlan.symbol,
direction: tradePlan.direction,
confidence: Number(tradePlan.confidenceScore.toFixed(1)),
entry: Number(entry.toFixed(4)),
stopLossUsed: Number(stopUsed.toFixed(4)),
weightedTakeProfit: tradePlan.exitStrategy.takeProfitLevels.length
? Number((entry +
(tradePlan.direction === 'long' ? 1 : -1) *
Math.max(0, weightedReward)).toFixed(4))
: null,
reward: Number(Math.max(0, weightedReward).toFixed(4)),
risk: Number(riskUsed.toFixed(4)),
riskReward: Number(rr.toFixed(2)),
votes: {
chip: Number(tradePlan.chipAnalysisContribution.toFixed(1)),
pattern: Number(tradePlan.patternAnalysisContribution.toFixed(1)),
volume: Number(tradePlan.volumeAnalysisContribution.toFixed(1)),
bbsr: Number(tradePlan.bbsrAnalysisContribution.toFixed(1)),
},
summaries: tradePlan.summaries,
};
return JSON.stringify(jsonSummary);
}
// Common separator constants
const SEPARATOR = '='.repeat(80);
const SECTION_SEPARATOR = '-'.repeat(80);
/**
* Helper function to build a section of the output
*/
function buildSection(title, content, useSeparator = true) {
let output = '';
if (useSeparator) {
output += `\n${SECTION_SEPARATOR}\n`;
}
output += `\n【${title}】\n`;
output += content;
return output;
}
/**
* 格式化交易计划输出结果
* 将IntegratedTradePlan对象转换为更简洁高效的格式化字符串输出
* @param tradePlan 综合交易计划对象
* 仅需 IntegratedTradePlan 一个参数
* @returns 格式化的输出字符串
*/
function formatTradePlanOutput(tradePlan) {
// 初始化输出字符串
let output = '';
// 1. 标题和基本信息 - 简洁显示
output += `${SEPARATOR}\n`;
output += `交易计划 | ${tradePlan.symbol} | ${new Date(tradePlan.date).toLocaleString()} | ${tradePlan.currentPrice}\n`;
// 2. 核心信息 - 信号、方向和总结
output += buildSection('综合信号', `方向: ${formatDirection(tradePlan.direction)} | 强度: ${formatSignalStrength(tradePlan.signalStrength)} | 确信度: ${tradePlan.confidenceScore.toFixed(1)}/100\n` +
`${tradePlan.summary}\n`, false);
// 2.1 各子分析关键建议摘要
output += buildSection('各子分析关键建议', `筹码: ${tradePlan.summaries.chipSummary}\n` +
`形态/逆转: ${tradePlan.summaries.patternSummary}\n` +
`支阻位: ${tradePlan.summaries.bbsrSummary}\n` +
`波动率/量能: ${tradePlan.summaries.vvSummary}\n` +
`结构: ${tradePlan.summaries.structureSummary || '—'}\n` +
`供需区: ${tradePlan.summaries.supplyDemandSummary || '—'}\n` +
`区间/突破: ${tradePlan.summaries.rangeSummary || '—'}\n` +
`趋势线/通道: ${tradePlan.summaries.trendlineSummary || '—'}\n`);
// 3. 入场策略 - 合并为简洁格式
const criticalConditions = tradePlan.entryStrategy.entryConditions
.filter(c => c.priority === 'critical' || c.priority === 'important')
.map(c => `${formatPriority(c.priority)} ${c.description}`)
.join('\n ');
output += buildSection('入场策略', `价格: ${tradePlan.currentPrice.toFixed(2)} ➔ ${tradePlan.entryStrategy.idealEntryPrice.toFixed(2)} (${formatEntryType(tradePlan.entryStrategy.entryType)})\n` +
`区间: ${tradePlan.entryStrategy.priceZones.ideal[0].toFixed(2)}-${tradePlan.entryStrategy.priceZones.ideal[1].toFixed(2)} | 风险: ${formatRiskLevel(tradePlan.entryStrategy.riskLevel)}\n` +
`条件:\n ${criticalConditions}\n`);
// 4. 出场策略 - 合并为简洁格式
let exitContent = `止盈目标:\n`;
tradePlan.exitStrategy.takeProfitLevels.forEach((level, i) => {
const percent = (((level.price - tradePlan.entryStrategy.idealEntryPrice) /
tradePlan.entryStrategy.idealEntryPrice) *
100).toFixed(1);
const sign = level.price > tradePlan.entryStrategy.idealEntryPrice ? '+' : '';
exitContent += ` ${i + 1}. ${level.price.toFixed(2)} (${sign}${percent}%) | ${(level.proportion * 100).toFixed(0)}%仓位\n`;
});
exitContent += `止损位置:\n`;
tradePlan.exitStrategy.stopLossLevels.forEach((level, i) => {
const percent = (((level.price - tradePlan.entryStrategy.idealEntryPrice) /
tradePlan.entryStrategy.idealEntryPrice) *
100).toFixed(1);
const sign = level.price > tradePlan.entryStrategy.idealEntryPrice ? '+' : '';
exitContent += ` ${i + 1}. ${level.price.toFixed(2)} (${sign}${percent}%) | ${level.type === 'fixed' ? '固定' : '追踪'}\n`;
});
exitContent += `退出时间: ${tradePlan.exitStrategy.maximumHoldingPeriod}\n`;
output += buildSection('出场策略', exitContent);
// 5. 风险管理 - 更简洁的布局
output += buildSection('风险管理', `建议仓位: ${(tradePlan.riskManagement.suggestionPosition * 100).toFixed(1)}% | 风险回报比: ${tradePlan.riskManagement.riskRewardRatio.toFixed(2)}\n` +
`最大损失: ${tradePlan.riskManagement.maxLoss} | 波动性: ${tradePlan.riskManagement.volatilityConsideration}\n`);
// 6. 关键价位 - 分为支撑和阻力两组
const strongSupportLevels = tradePlan.keyLevels
.filter(level => level.type === 'support' && level.strength === 'strong')
.sort((a, b) => b.price - a.price);
const strongResistanceLevels = tradePlan.keyLevels
.filter(level => level.type === 'resistance' && level.strength === 'strong')
.sort((a, b) => a.price - b.price);
let keyLevelContent = `支撑位:\n`;
if (strongSupportLevels.length > 0) {
strongSupportLevels.slice(0, 3).forEach(level => {
keyLevelContent += ` ${level.price.toFixed(2)} | ${formatLevelSource(level.source)}\n`;
});
}
else {
keyLevelContent += ` 未检测到强支撑位\n`;
}
keyLevelContent += `阻力位:\n`;
if (strongResistanceLevels.length > 0) {
strongResistanceLevels.slice(0, 3).forEach(level => {
keyLevelContent += ` ${level.price.toFixed(2)} | ${formatLevelSource(level.source)}\n`;
});
}
else {
keyLevelContent += ` 未检测到强阻力位\n`;
}
output += buildSection('关键价位', keyLevelContent);
// 7. 支撑阻力位的牛熊信号分析 (如果有数据)
if (tradePlan.bbsrAnalysis.dailyBBSRResult ||
tradePlan.bbsrAnalysis.weeklyBBSRResult) {
let bbsrContent = '';
if (tradePlan.bbsrAnalysis.dailyBBSRResult) {
bbsrContent += `日线关键位: ${tradePlan.bbsrAnalysis.dailyBBSRResult.SRLevel.toFixed(2)}\n`;
bbsrContent += `日期: ${toEDTString(tradePlan.bbsrAnalysis.dailyBBSRResult.signalDate)}\n`;
bbsrContent += `名称: ${tradePlan.bbsrAnalysis.dailyBBSRResult.signal.patternNames.join(',')}\n`;
}
if (tradePlan.bbsrAnalysis.weeklyBBSRResult) {
bbsrContent += `周线关键位: ${tradePlan.bbsrAnalysis.weeklyBBSRResult.SRLevel.toFixed(2)}\n`;
bbsrContent += `日期: ${toEDTString(tradePlan.bbsrAnalysis.weeklyBBSRResult.signalDate)}\n`;
bbsrContent += `名称: ${tradePlan.bbsrAnalysis.weeklyBBSRResult.signal.patternNames.join(',')}\n`;
}
output += buildSection('支撑阻力位的牛熊信号分析', bbsrContent);
}
// 8. 时间周期分析
output += buildSection('时间周期分析', `主要周期: ${formatTimeframe(tradePlan.primaryTimeframe)} | 一致性: ${tradePlan.timeframeConsistency}\n` +
`短期: ${tradePlan.shortTermOutlook} | 中期: ${tradePlan.mediumTermOutlook} | 长期: ${tradePlan.longTermOutlook}\n`);
// 9. 波动率量能分析
if (tradePlan.vvInsights) {
output += buildSection('波动率量能分析', `${tradePlan.vvInsights.volumeAnalysisReason}\n` +
`${tradePlan.vvInsights.volatilityAnalysisReason}\n`);
}
// 10. 趋势逆转信号 (如果存在)
if (tradePlan.trendReversalInfo &&
tradePlan.trendReversalInfo.hasReversalSignal) {
let reversalContent = '';
if (tradePlan.trendReversalInfo.primaryReversalSignal) {
const signal = tradePlan.trendReversalInfo.primaryReversalSignal;
const direction = signal.direction > 0 ? '上涨' : '下跌';
reversalContent += `检测到${formatTimeframe(signal.smallTimeframe)}对${formatTimeframe(signal.largeTimeframe)}的顺势逆转\n`;
reversalContent += `方向: ${direction} | 强度: ${signal.reversalStrength.toFixed(1)}/100\n`;
if (signal.entryPrice && signal.stopLoss) {
reversalContent += `入场价: ${signal.entryPrice.toFixed(2)} | 止损价: ${signal.stopLoss.toFixed(2)}\n`;
}
if (signal.reversalStrength > 70) {
reversalContent += `评价: ✓ 强烈逆转信号,适合介入\n`;
}
else if (signal.reversalStrength > 50) {
reversalContent += `评价: ✓ 中等强度逆转信号,可以考虑\n`;
}
else {
reversalContent += `评价: ⚠ 弱逆转信号,建议等待确认\n`;
}
}
else {
reversalContent += `${tradePlan.trendReversalInfo.description}\n`;
}
output += buildSection('趋势逆转信号', reversalContent);
}
// 11. 交易理由
output += buildSection('交易理由', `${tradePlan.primaryRationale}\n` +
`${tradePlan.secondaryRationale}\n` +
(tradePlan.vvInsights?.combinedAnalysisSummary ?? ''));
// 12. 无效信号条件
let invalidationContent = '';
tradePlan.invalidationConditions
.filter(c => c.priority === 'critical' || c.priority === 'important')
.forEach(condition => {
invalidationContent += ` ${formatPriority(condition.priority)} ${condition.description}\n`;
});
output += buildSection('无效信号条件', invalidationContent);
// 13. 警告信息
if (tradePlan.warnings.length > 0) {
let warningContent = '';
tradePlan.warnings.forEach(warning => {
warningContent += ` ⚠️ ${warning}\n`;
});
output += buildSection('警告信息', warningContent);
}
// 14. 分析构成
output += buildSection('分析构成', `筹码分析: ${(tradePlan.chipAnalysisWeight * 100).toFixed(0)}% (得分:${tradePlan.chipAnalysisContribution.toFixed(1)}/100)\n` +
`形态分析: ${(tradePlan.patternAnalysisWeight * 100).toFixed(0)}% (得分:${tradePlan.patternAnalysisContribution.toFixed(1)}/100)\n` +
`量价分析: ${(tradePlan.volumeAnalysisWeight * 100).toFixed(0)}% (得分:${tradePlan.volumeAnalysisContribution.toFixed(1)}/100)\n` +
`支阻位分析: ${(tradePlan.bbsrAnalysisWeight * 100).toFixed(0)}% (得分:${tradePlan.bbsrAnalysisContribution.toFixed(1)}/100)\n`);
// 结尾分隔线
output += `\n${SEPARATOR}\n`;
return output;
}
/**
* 格式化交易方向
*/
function formatDirection(direction) {
switch (direction) {
case 'long':
return '📈 做多';
case 'short':
return '📉 做空';
case 'neutral':
return '⚖️ 中性';
default:
return direction;
}
}
/**
* 格式化信号强度
*/
function formatSignalStrength(strength) {
switch (strength) {
case 'strong':
return '🔥 强';
case 'moderate':
return '✅ 中等';
case 'weak':
return '⚡ 弱';
case 'neutral':
return '⚖️ 中性';
case 'conflicting':
return '⚠️ 冲突';
default:
return strength;
}
}
/**
* 格式化时间周期
*/
function formatTimeframe(timeframe) {
switch (timeframe) {
case 'weekly':
return '周线';
case 'daily':
return '日线';
case '1hour':
return '小时线';
default:
return timeframe;
}
}
/**
* 格式化入场类型
*/
function formatEntryType(entryType) {
switch (entryType) {
case 'immediate':
return '立即入场';
case 'pullback':
return '回调入场';
case 'breakout':
return '突破入场';
default:
return entryType;
}
}
/**
* 格式化风险等级
*/
function formatRiskLevel(riskLevel) {
switch (riskLevel) {
case 'high':
return '🔴 高';
case 'medium':
return '🟠 中';
case 'low':
return '🟢 低';
default:
return riskLevel;
}
}
/**
* 格式化优先级
*/
function formatPriority(priority) {
switch (priority) {
case 'critical':
return '[必要]';
case 'important':
return '[重要]';
case 'optional':
return '[可选]';
default:
return priority;
}
}
/**
* 格式化价位来源
*/
function formatLevelSource(source) {
switch (source) {
case 'chip':
return '筹码分析';
case 'pattern':
return '形态分析';
case 'bbsr':
return '支阻位分析';
case 'structure':
return '结构分析';
case 'supplyDemand':
return '供需区分析';
case 'range':
return '区间/突破';
case 'trendline':
return '趋势线/通道';
case 'combined':
return '综合分析';
default:
return source;
}
}
export { formatTradePlanOutput, buildMachineReadableSummary };