feat: ADX-Trendstärke-Filter (fix 20, nicht im Grid) gegen Chop-Whipsaw

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-09 21:46:57 +00:00
parent 736db184ab
commit c07a34e671
7 changed files with 225 additions and 17 deletions

View File

@@ -0,0 +1,44 @@
import type { Candle } from '../types';
/** Wilder ADX: +DM/DM → Wilder-geglättet → DI± → DX → ADX. NaN vor Index 2×period1. */
export function adx(candles: Candle[], period: number): number[] {
const n = candles.length;
const out = new Array<number>(n).fill(NaN);
if (n < 2 * period) return out;
const plusDM = [0];
const minusDM = [0];
const tr = [candles[0].high - candles[0].low];
for (let i = 1; i < n; i++) {
const up = candles[i].high - candles[i - 1].high;
const down = candles[i - 1].low - candles[i].low;
plusDM.push(up > down && up > 0 ? up : 0);
minusDM.push(down > up && down > 0 ? down : 0);
tr.push(Math.max(
candles[i].high - candles[i].low,
Math.abs(candles[i].high - candles[i - 1].close),
Math.abs(candles[i].low - candles[i - 1].close),
));
}
let sTR = 0, sPlus = 0, sMinus = 0;
for (let i = 1; i <= period; i++) { sTR += tr[i]; sPlus += plusDM[i]; sMinus += minusDM[i]; }
const dx = new Array<number>(n).fill(NaN);
const computeDx = () => {
if (sTR === 0) return 0; // völlig flacher Markt
const plusDI = (100 * sPlus) / sTR;
const minusDI = (100 * sMinus) / sTR;
const sum = plusDI + minusDI;
return sum === 0 ? 0 : (100 * Math.abs(plusDI - minusDI)) / sum;
};
dx[period] = computeDx();
for (let i = period + 1; i < n; i++) {
sTR = sTR - sTR / period + tr[i];
sPlus = sPlus - sPlus / period + plusDM[i];
sMinus = sMinus - sMinus / period + minusDM[i];
dx[i] = computeDx();
}
let sum = 0;
for (let i = period; i < 2 * period; i++) sum += dx[i];
out[2 * period - 1] = sum / period;
for (let i = 2 * period; i < n; i++) out[i] = (out[i - 1] * (period - 1) + dx[i]) / period;
return out;
}