@gabriel3615/ta_analysis
Version:
stock ta analysis
123 lines (122 loc) • 6.06 kB
JavaScript
export class NarrativeBuilder {
buildSummary(signalResult) {
const dirText = signalResult.direction === 'long'
? '做多'
: signalResult.direction === 'short'
? '做空'
: '中性';
return `综合信号 ${dirText}|强度:${signalResult.signalStrength}|置信度:${signalResult.confidenceScore?.toFixed?.(1) ?? signalResult.confidenceScore}%`;
}
buildPrimaryRationale(signalResult, analyses) {
const ws = signalResult.weightedScores;
const entries = Object.entries(ws).sort((a, b) => Math.abs(b[1]) - Math.abs(a[1]));
const top = entries
.slice(0, 3)
.map(([k]) => k)
.join('/');
const patternDir = analyses.pattern?.combinedSignal ?? '-';
const chip = analyses.chip;
return `主要由${top} 驱动;形态:${patternDir};筹码买:${chip.combinedBuySignalStrength}/卖:${chip.combinedShortSignalStrength}`;
}
buildSecondaryRationale(analyses) {
const structure = analyses.structure;
const sd = analyses.supplyDemand;
const range = analyses.range;
const tl = analyses.trendline;
const parts = [];
if (structure?.trend)
parts.push(`结构:${structure.trend}`);
if (sd?.premiumDiscount?.position != null)
parts.push(`估值位:${Math.round(sd.premiumDiscount.position)}%`);
if (range?.breakout)
parts.push(`区间突破:${range.breakout.direction}/质量${range.breakout.qualityScore}`);
if (tl?.breakoutRetest)
parts.push(`趋势线突破回踩:${tl.breakoutRetest.direction}/质量${tl.breakoutRetest.qualityScore}`);
return parts.length ? parts.join(';') : '—';
}
buildSummariesFromPlugins(plugins, analysisData, context) {
const get = (id, fallback) => {
const plugin = plugins.find(p => p.id === id);
if (plugin?.summarize) {
try {
return plugin.summarize(analysisData, context);
}
catch {
return 'Error in summarize()';
}
}
return fallback();
};
return {
chipSummary: get('chip', () => {
const chip = analysisData.analyses.chip;
return `买入强度:${chip.combinedBuySignalStrength} 做空强度:${chip.combinedShortSignalStrength} 主周期:${chip.primaryTimeframe}`;
}),
patternSummary: get('pattern', () => {
const p = analysisData.analyses.pattern;
return `形态综合方向:${p.combinedSignal} 强度:${p.signalStrength?.toFixed?.(1) ?? p.signalStrength}`;
}),
bbsrSummary: get('bbsr', () => {
const b = analysisData.analyses.bbsr;
const d = b.dailyBBSRResult?.strength;
const w = b.weeklyBBSRResult?.strength;
return `BBSR(日/周) 强度:${d ?? '-'} / ${w ?? '-'}`;
}),
vvSummary: get('volume', () => {
const vv = analysisData.analyses.volatility;
const regime = vv.volatilityAnalysis?.volatilityAnalysis?.volatilityRegime ?? 'low';
const atrp = vv.volatilityAnalysis?.volatilityAnalysis?.atrPercent ?? 0;
const volConfirm = vv.volumeAnalysis?.volumeAnalysis
?.volumePriceConfirmation
? '确认'
: '未确认';
return `波动率:${regime} ATR%:${atrp.toFixed?.(2) ?? atrp} 成交量确认:${volConfirm}`;
}),
structureSummary: get('structure', () => {
const s = analysisData.analyses.structure;
return `结构趋势:${s.trend} 关键位数:${s.keyLevels?.length ?? 0}`;
}),
supplyDemandSummary: get('supplyDemand', () => {
const sd = analysisData.analyses.supplyDemand;
const pos = sd.premiumDiscount?.position ?? 50;
const zones = sd.recentEffectiveZones?.length ?? 0;
return `供需位置:${pos.toFixed?.(1) ?? pos} 有效区域:${zones}`;
}),
rangeSummary: get('range', () => {
const r = analysisData.analyses.range;
const comp = r.compressionScore;
const br = r.breakout
? `${r.breakout.direction}/${r.breakout.qualityScore}`
: '无突破';
return `压缩:${comp} 突破:${br}`;
}),
trendlineSummary: get('trendline', () => {
const tl = analysisData.analyses.trendline;
const slope = tl.channel?.slope ?? 0;
return tl.summary || `通道斜率:${slope.toFixed?.(4) ?? slope}`;
}),
};
}
buildShortTermOutlook(analyses) {
const pattern = analyses.pattern;
const vv = analyses.volatility;
const tl = analyses.trendline;
const dir = pattern?.combinedSignal ?? '-';
const regime = vv?.volatilityAnalysis?.volatilityAnalysis?.volatilityRegime ?? 'low';
const slope = tl?.channel?.slope ?? 0;
return `短期(${dir}),波动率${regime},通道斜率${typeof slope?.toFixed === 'function' ? slope.toFixed(3) : slope}`;
}
buildMediumTermOutlook(analyses) {
const chip = analyses.chip;
const sd = analyses.supplyDemand;
const pos = sd?.premiumDiscount?.position ?? 50;
return `中期(筹码买:${chip.combinedBuySignalStrength}/卖:${chip.combinedShortSignalStrength}),估值位置${typeof pos?.toFixed === 'function' ? pos.toFixed(1) : pos}`;
}
buildLongTermOutlook(analyses) {
const structure = analyses.structure;
const range = analyses.range;
const trend = structure?.trend ?? '-';
const comp = range?.compressionScore ?? '-';
return `长期(${trend}),压缩度${comp}`;
}
}