@gabriel3615/ta_analysis
Version:
stock ta analysis
66 lines (65 loc) • 3.03 kB
JavaScript
import { backtestStrategiesConfig } from '../strategyConfig.js';
import { analyzeSupplyDemandZone } from '../../analyzer/supplyDemand/sdDetector.js';
export function SupplyDemandRetestStrategy(symbol, timeframe, params = {}) {
const { minZoneWidthPercent = backtestStrategiesConfig.supplyDemandRetest
.minZoneWidthPercent, allowFreshEntry = backtestStrategiesConfig.supplyDemandRetest
.allowFreshEntry, requireTested = backtestStrategiesConfig.supplyDemandRetest.requireTested, coolDownBars = backtestStrategiesConfig.supplyDemandRetest.coolDownBars, } = params;
let lastSignalIndex = -1;
let lastDirection = undefined;
return {
name: 'SupplyDemandRetest',
generateSignal(history, i) {
if (i < 60)
return null;
if (i - lastSignalIndex < coolDownBars)
return null;
const window = history.slice(Math.max(0, i - 300), i + 1);
const res = analyzeSupplyDemandZone(symbol, window, timeframe);
const current = window[window.length - 1].close;
const candidate = res.recentEffectiveZones
.filter(z => (z.high - z.low) / Math.max(1e-8, z.low) >= minZoneWidthPercent)
.slice(-1)[0];
if (!candidate)
return null;
// 状态过滤
if (requireTested && candidate.status !== 'tested')
return null;
if (!allowFreshEntry && candidate.status === 'fresh')
return null;
// 在供需区内触发回踩
const isInside = current >= candidate.low && current <= candidate.high;
if (!isInside)
return null;
let signal = null;
if (candidate.type === 'demand') {
signal = {
timestamp: history[i].timestamp,
direction: 'long',
strength: candidate.status === 'tested' ? 80 : 65,
reason: `${timeframe} 需求区回踩 (${candidate.low.toFixed(2)} - ${candidate.high.toFixed(2)}) 触发`,
};
}
else if (candidate.type === 'supply') {
signal = {
timestamp: history[i].timestamp,
direction: 'short',
strength: candidate.status === 'tested' ? 80 : 65,
reason: `${timeframe} 供给区回踩 (${candidate.low.toFixed(2)} - ${candidate.high.toFixed(2)}) 触发`,
};
}
// 避免短时间内方向反转噪声
if (signal &&
lastDirection &&
signal.direction !== lastDirection &&
i - lastSignalIndex < coolDownBars * 2 &&
(signal.strength ?? 0) < 80) {
return null;
}
if (signal) {
lastSignalIndex = i;
lastDirection = signal.direction;
}
return signal;
},
};
}