보조지표 소수점 추가, cci 수정

This commit is contained in:
Macbook
2026-05-30 00:13:22 +09:00
parent 6332d198b1
commit ad92dace28
11 changed files with 286 additions and 20 deletions
+113 -11
View File
@@ -15,6 +15,20 @@ function sma(values: number[], period: number): number[] {
});
}
function wma(values: number[], period: number): number[] {
return values.map((_, i) => {
if (i < period - 1) return NaN;
let wSum = 0;
let w = 0;
for (let j = 0; j < period; j++) {
const weight = j + 1;
wSum += values[i - period + 1 + j] * weight;
w += weight;
}
return wSum / w;
});
}
function ema(values: number[], period: number): number[] {
const out = new Array<number>(values.length).fill(NaN);
if (values.length < period) return out;
@@ -182,6 +196,104 @@ export function calcInvestPsychological(
return { plot0: toPlotData(bars, vals) };
}
/**
* RSI + 신호선 (업비트·TradingView Wilder RSI)
*/
export function calcRSI(
bars: OHLCVBar[],
params: Record<string, number | string | boolean>,
): Record<string, PlotData> {
const len = Math.max(1, Number(params.length ?? 14) || 14);
const maLen = Math.max(1, Number(params.maLength ?? 14) || 14);
const rawMa = String(params.maType ?? 'SMA');
const maType = rawMa === 'EMA' || rawMa === 'WMA' ? rawMa : (rawMa === 'None' ? 'None' : 'SMA');
const srcKey = String(params.src ?? 'close');
const prices = bars.map(b => sourceAt(b, srcKey));
const rsi = new Array<number>(prices.length).fill(NaN);
if (prices.length > len) {
let gain = 0;
let loss = 0;
for (let i = 1; i <= len; i++) {
const d = prices[i] - prices[i - 1];
if (d > 0) gain += d;
else loss -= d;
}
let avgG = gain / len;
let avgL = loss / len;
rsi[len] = avgL === 0 ? 100 : 100 - 100 / (1 + avgG / avgL);
for (let i = len + 1; i < prices.length; i++) {
const d = prices[i] - prices[i - 1];
const g = d > 0 ? d : 0;
const l = d < 0 ? -d : 0;
avgG = (avgG * (len - 1) + g) / len;
avgL = (avgL * (len - 1) + l) / len;
rsi[i] = avgL === 0 ? 100 : 100 - 100 / (1 + avgG / avgL);
}
}
let signal: number[];
if (maType === 'None') {
signal = rsi.map(() => NaN);
} else if (maType === 'EMA') {
signal = ema(rsi.map(v => (Number.isFinite(v) ? v : NaN)), maLen);
} else if (maType === 'WMA') {
signal = wma(rsi.map(v => (Number.isFinite(v) ? v : NaN)), maLen);
} else {
signal = sma(rsi, maLen);
}
return {
plot0: toPlotData(bars, rsi),
plot1: toPlotData(bars, signal),
};
}
/**
* CCI + 신호선 (업비트·TradingView)
* CCI = (src - SMA(src,N)) / (0.015 × MeanDeviation)
* 신호선 = CCI의 SMA/EMA/WMA (기본 SMA 10)
*/
export function calcCCI(
bars: OHLCVBar[],
params: Record<string, number | string | boolean>,
): Record<string, PlotData> {
const len = Math.max(1, Number(params.length ?? 13) || 13);
const maLen = Math.max(1, Number(params.maLength ?? 10) || 10);
const rawMa = String(params.maType ?? 'SMA');
const maType = rawMa === 'EMA' || rawMa === 'WMA' ? rawMa : (rawMa === 'None' ? 'None' : 'SMA');
const srcKey = String(params.src ?? 'close');
const prices = bars.map(b => sourceAt(b, srcKey));
const cci = prices.map((_, i) => {
if (i < len - 1) return NaN;
let sum = 0;
for (let j = i - len + 1; j <= i; j++) sum += prices[j];
const ma = sum / len;
let md = 0;
for (let j = i - len + 1; j <= i; j++) md += Math.abs(prices[j] - ma);
md /= len;
if (md === 0) return 0;
return (prices[i] - ma) / (0.015 * md);
});
let signal: number[];
if (maType === 'None') {
signal = cci.map(() => NaN);
} else if (maType === 'EMA') {
signal = ema(cci.map(v => (Number.isFinite(v) ? v : NaN)), maLen);
} else if (maType === 'WMA') {
signal = wma(cci.map(v => (Number.isFinite(v) ? v : NaN)), maLen);
} else {
signal = sma(cci, maLen);
}
return {
plot0: toPlotData(bars, cci),
plot1: toPlotData(bars, signal),
};
}
/**
* TRIX + TRMA(시그널) — 업비트·키움 HTS
* EMA3 = EMA(EMA(EMA(종가, N), N), N)
@@ -241,17 +353,7 @@ export function calcOBVWithMA(
ma = ema(obv, maLen);
break;
case 'WMA':
ma = obv.map((_, i) => {
if (i < maLen - 1) return NaN;
let wSum = 0;
let w = 0;
for (let j = 0; j < maLen; j++) {
const weight = j + 1;
wSum += obv[i - maLen + 1 + j] * weight;
w += weight;
}
return wSum / w;
});
ma = wma(obv, maLen);
break;
default:
ma = sma(obv, maLen);