goldenChat base source add

This commit is contained in:
aidev
2026-05-23 15:11:48 +09:00
commit a4ea7762b5
2081 changed files with 1155760 additions and 0 deletions
+242
View File
@@ -0,0 +1,242 @@
/**
* 보조지표 계산 Web Worker
* - 메인 스레드를 차단하지 않고 지표 계산
* - 복잡한 계산(MA, RSI, MACD 등)을 백그라운드에서 처리
*/
self.addEventListener('message', (event) => {
const { type, data } = event.data;
try {
switch (type) {
case 'CALCULATE_MA':
const maResult = calculateMA(data.candles, data.periods);
self.postMessage({ type: 'MA_RESULT', data: maResult });
break;
case 'CALCULATE_RSI':
const rsiResult = calculateRSI(data.candles, data.period);
self.postMessage({ type: 'RSI_RESULT', data: rsiResult });
break;
case 'CALCULATE_MACD':
const macdResult = calculateMACD(data.candles, data.fastPeriod, data.slowPeriod, data.signalPeriod);
self.postMessage({ type: 'MACD_RESULT', data: macdResult });
break;
case 'CALCULATE_STOCHASTIC':
const stochResult = calculateStochastic(data.candles, data.kPeriod, data.dPeriod);
self.postMessage({ type: 'STOCHASTIC_RESULT', data: stochResult });
break;
case 'CALCULATE_ALL':
const allResults = calculateAllIndicators(data.candles, data.config);
self.postMessage({ type: 'ALL_RESULT', data: allResults });
break;
default:
self.postMessage({ type: 'ERROR', error: 'Unknown command' });
}
} catch (error) {
self.postMessage({ type: 'ERROR', error: error.message });
}
});
/**
* 이동평균선 계산
*/
function calculateMA(candles, periods) {
const results = {};
periods.forEach(period => {
const ma = [];
for (let i = 0; i < candles.length; i++) {
if (i < period - 1) {
ma.push(null);
} else {
let sum = 0;
for (let j = 0; j < period; j++) {
sum += candles[i - j].close;
}
ma.push(sum / period);
}
}
results[`ma${period}`] = ma;
});
return results;
}
/**
* RSI 계산
*/
function calculateRSI(candles, period = 14) {
const rsi = [];
const gains = [];
const losses = [];
for (let i = 0; i < candles.length; i++) {
if (i === 0) {
rsi.push(null);
continue;
}
const change = candles[i].close - candles[i - 1].close;
gains.push(change > 0 ? change : 0);
losses.push(change < 0 ? Math.abs(change) : 0);
if (i < period) {
rsi.push(null);
continue;
}
const avgGain = gains.slice(-period).reduce((a, b) => a + b, 0) / period;
const avgLoss = losses.slice(-period).reduce((a, b) => a + b, 0) / period;
if (avgLoss === 0) {
rsi.push(100);
} else {
const rs = avgGain / avgLoss;
rsi.push(100 - (100 / (1 + rs)));
}
}
return rsi;
}
/**
* MACD 계산
*/
function calculateMACD(candles, fastPeriod = 12, slowPeriod = 26, signalPeriod = 9) {
// EMA 계산 헬퍼
const calculateEMA = (values, period) => {
const ema = [];
const multiplier = 2 / (period + 1);
for (let i = 0; i < values.length; i++) {
if (i < period - 1) {
ema.push(null);
} else if (i === period - 1) {
const sum = values.slice(0, period).reduce((a, b) => a + b, 0);
ema.push(sum / period);
} else {
const value = (values[i] - ema[i - 1]) * multiplier + ema[i - 1];
ema.push(value);
}
}
return ema;
};
const closes = candles.map(c => c.close);
const fastEMA = calculateEMA(closes, fastPeriod);
const slowEMA = calculateEMA(closes, slowPeriod);
const macdLine = [];
for (let i = 0; i < candles.length; i++) {
if (fastEMA[i] === null || slowEMA[i] === null) {
macdLine.push(null);
} else {
macdLine.push(fastEMA[i] - slowEMA[i]);
}
}
const signalLine = calculateEMA(
macdLine.map(v => v === null ? 0 : v),
signalPeriod
);
const histogram = [];
for (let i = 0; i < candles.length; i++) {
if (macdLine[i] === null || signalLine[i] === null) {
histogram.push(null);
} else {
histogram.push(macdLine[i] - signalLine[i]);
}
}
return { macdLine, signalLine, histogram };
}
/**
* Stochastic 계산
*/
function calculateStochastic(candles, kPeriod = 14, dPeriod = 3) {
const stochK = [];
for (let i = 0; i < candles.length; i++) {
if (i < kPeriod - 1) {
stochK.push(null);
continue;
}
const slice = candles.slice(i - kPeriod + 1, i + 1);
const highest = Math.max(...slice.map(c => c.high));
const lowest = Math.min(...slice.map(c => c.low));
const current = candles[i].close;
if (highest === lowest) {
stochK.push(50);
} else {
stochK.push(((current - lowest) / (highest - lowest)) * 100);
}
}
// %D 계산 (K의 이동평균)
const stochD = [];
for (let i = 0; i < stochK.length; i++) {
if (i < dPeriod - 1 || stochK[i] === null) {
stochD.push(null);
} else {
const sum = stochK.slice(i - dPeriod + 1, i + 1).reduce((a, b) => a + (b || 0), 0);
stochD.push(sum / dPeriod);
}
}
return { stochK, stochD };
}
/**
* 모든 지표 한 번에 계산
*/
function calculateAllIndicators(candles, config) {
const results = {};
// MA
if (config.maPeriods && config.maPeriods.length > 0) {
Object.assign(results, calculateMA(candles, config.maPeriods));
}
// RSI
if (config.rsi) {
results.rsi = calculateRSI(candles, config.rsiPeriod || 14);
}
// MACD
if (config.macd) {
const macd = calculateMACD(
candles,
config.macdFastPeriod || 12,
config.macdSlowPeriod || 26,
config.macdSignalPeriod || 9
);
results.macdLine = macd.macdLine;
results.macdSignal = macd.signalLine;
results.macdHistogram = macd.histogram;
}
// Stochastic
if (config.stochastic) {
const stoch = calculateStochastic(
candles,
config.stochasticKPeriod || 14,
config.stochasticDPeriod || 3
);
results.stochasticK = stoch.stochK;
results.stochasticD = stoch.stochD;
}
return results;
}
console.log('📊 Indicator Worker initialized');