Code 노드에서 순수 JavaScript로 기술적 지표를 직접 구현. 외부 라이브러리 없이 RSI·이동평균·볼린저 밴드의 수학적 원리 체득.
Code 노드에서 RSI(Relative Strength Index) 14일 기간을 직접 계산하는 함수를 작성하고, 관심 종목 5개에 적용합니다.
// RSI 계산 함수 — 이 코드의 빈칸을 완성하세요 function calculateRSI(closes, period = 14) { if (closes.length < period + 1) return null; let gains = 0, losses = 0; // TODO: 첫 period 기간의 평균 상승/하락 계산 for (let i = 1; i <= period; i++) { const diff = closes[i] - closes[i - 1]; if (diff > 0) gains += diff; else losses += Math.abs(diff); } const avgGain = gains / period; const avgLoss = losses / period; if (avgLoss === 0) return 100; const rs = avgGain / avgLoss; return 100 - (100 / (1 + rs)); } // 사용 예 const closes = $input.first().json.closes; // 30일 종가 배열 const rsi = calculateRSI(closes, 14); return [{ json: { rsi: rsi.toFixed(2), zone: rsi <= 30 ? 'oversold' : rsi >= 70 ? 'overbought' : 'neutral' } }];
const result = $input.first().json.chart.result[0]; const closes = result.indicators.quote[0].close.filter(c => c !== null); // 또는 안전하게: const closes = ($json.chart?.result?.[0]?.indicators?.quote?.[0]?.close || []) .filter(c => c !== null);null 값 필터링이 필수 (휴장일 데이터). 30일 중 실제 유효한 종가가 22~25개 정도.
| A. RSI 계산 함수 정확성 | 7점 |
| B. 5종목 모두 적용 | 4점 |
| C. 과매수/과매도 자동 라벨링 | 4점 |
| D. Sheets 저장 형식 (RSI · Zone · Date) | 3점 |
| E. 16:00 자동 실행 설정 | 2점 |
MA5, MA20, MA60을 동시 계산하고, 단기선이 장기선을 상향 돌파하는 골든 크로스 시그널을 자동 탐지합니다.
function calculateMA(closes, period) { if (closes.length < period) return null; const recent = closes.slice(-period); return recent.reduce((a, b) => a + b, 0) / period; } // 현재와 어제 두 시점의 MA를 계산해 크로스 탐지 const ma5_today = calculateMA(closes, 5); const ma5_yesterday = calculateMA(closes.slice(0, -1), 5); const ma20_today = calculateMA(closes, 20); const ma20_yesterday = calculateMA(closes.slice(0, -1), 20); const isGoldenCross = ma5_today > ma20_today && ma5_yesterday <= ma20_yesterday; const isDeadCross = ma5_today < ma20_today && ma5_yesterday >= ma20_yesterday;
| A. MA 3종 정확 계산 | 6점 |
| B. 골든/데드크로스 탐지 로직 | 6점 |
| C. Discord 시그널 알림 | 4점 |
| D. IF 노드로 분기 처리 | 2점 |
| E. 시그널 로그 Sheets 저장 | 2점 |
볼린저 밴드(20, 2σ)를 계산하고, 현재가가 상단/하단 밴드를 돌파하면 Discord에 알림 발송.
(Upper - Lower) / Middle × 100function calculateStdDev(values) {
const mean = values.reduce((a,b) => a+b, 0) / values.length;
const sqDiff = values.map(v => Math.pow(v - mean, 2));
const variance = sqDiff.reduce((a,b) => a+b, 0) / values.length;
return Math.sqrt(variance);
}
const recent20 = closes.slice(-20);
const middle = calculateMA(recent20, 20);
const std = calculateStdDev(recent20);
const upper = middle + 2 * std;
const lower = middle - 2 * std;
| A. 표준편차 + 밴드 3종 정확성 | 7점 |
| B. 돌파/이탈 탐지 | 5점 |
| C. Band Width 계산 + 저장 | 3점 |
| D. Discord Embed 디자인 | 3점 |
| E. Switch 노드로 상/하단 분기 | 2점 |
과제 1~3의 RSI + MA + 볼린저 3개 지표를 모두 합쳐, 각 지표의 시그널에 가중치를 부여한 종합 점수를 만듭니다.
RSI × 0.4 + MA × 0.4 + Bollinger × 0.2// RSI 점수 (-1 ~ +1) function rsiScore(rsi) { if (rsi <= 30) return +1; // 과매도 → 매수 if (rsi <= 40) return +0.5; if (rsi < 60) return 0; // 중립 if (rsi < 70) return -0.5; return -1; // 과매수 → 매도 } // MA 점수 — 정배열/역배열 판단 function maScore(ma5, ma20, ma60) { if (ma5 > ma20 && ma20 > ma60) return +1; // 완전 정배열 if (ma5 > ma20) return +0.5; if (ma5 < ma20 && ma20 < ma60) return -1; // 완전 역배열 if (ma5 < ma20) return -0.5; return 0; } const total = rsiScore * 0.4 + maScore * 0.4 + bollScore * 0.2; // -1.0 ~ +1.0 사이 값
| A. 3지표 통합 로직 품질 | 6점 |
| B. 가중치 체계 타당성 | 4점 |
| C. 5단계 분류 정확성 | 4점 |
| D. Discord 색상 Embed 구현 | 4점 |
| E. 실제 발생 시그널 기록 | 2점 |
과거 1년 데이터로 과제 4의 시그널을 백테스트해 승률을 측정합니다. "이 전략이 실제로 수익이 날까?"를 실증.
Backtest Report — {ticker}
Period: 2025-04 ~ 2026-04
Signals fired: 23 (BUY) / 19 (SELL)
Win Rate (BUY): 61% (14/23)
Avg Return: +2.3%
Max Drawdown: -7.1%
Sharpe Ratio: 0.89
Conclusion: 전략이 유효하지만 승률이 낮아 개선 필요
| A. 1년치 데이터 순회 로직 | 5점 |
| B. Look-ahead 없이 시그널 생성 | 5점 |
| C. 4개 메트릭 계산 정확성 | 5점 |
| D. 리포트 가독성·시각화 | 3점 |
| E. 시그널 개선 제안 | 2점 |