보조지표 소수점 추가, cci 수정
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user