UNPKG

@gabriel3615/ta_analysis

Version:

stock ta analysis

66 lines (65 loc) 3.03 kB
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; }, }; }