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
@@ -0,0 +1,535 @@
/**
* 정배열/역배열 + Stochastic Slow 기반 매매 전략
*
* [정배열/역배열 조건]
* - 정배열: 추세 강도 > 0
* - 역배열: 추세 강도 < 0
*
* [매매 조건]
* 1) 정배열인 경우:
* - Stochastic Slow 20 상향 돌파 → 매수
* - 50 상향 돌파 → 매수
* - 80 상향 돌파 → 매수
* - 20 하향 돌파 → 매도
* - 50 하향 돌파 → 매도
* - 80 하향 돌파 → 매도
*
* 2) 역배열인 경우:
* - 80 상향 돌파 → 매수
* - 80 하향 돌파 → 매도
*/
// OHLCV + 지표 데이터 인터페이스
export interface CandleWithIndicators {
date: string;
timestamp?: string;
open: number;
high: number;
low: number;
close: number;
volume: number;
stochasticK: number | null; // Stochastic Slow %K
stochasticD: number | null; // Stochastic Slow %D
macdLine?: number | null;
signalLine?: number | null;
macdHistogram?: number | null;
trendStrength?: number; // 추세 강도 (-100 ~ +100)
}
// 매매 신호 타입
export type SignalType = 'BUY' | 'SELL' | 'HOLD';
// 매매 신호 결과
export interface TradingSignal {
date: string;
signal: SignalType;
reason: string;
stochasticK: number;
stochasticD: number;
trendStrength: number;
isBullish: boolean; // 정배열 여부
crossLevel?: number; // 돌파 레벨 (20, 50, 80)
crossDirection?: 'UP' | 'DOWN'; // 돌파 방향
}
// 돌파 레벨 정의
const OVERSOLD_LEVEL = 20;
const NEUTRAL_LEVEL = 50;
const OVERBOUGHT_LEVEL = 80;
/**
* 상향 돌파 여부 확인
* @param prevValue 이전 값
* @param currValue 현재 값
* @param level 돌파 레벨
* @returns 상향 돌파 여부
*/
export function isCrossUp(prevValue: number, currValue: number, level: number): boolean {
return prevValue < level && currValue >= level;
}
/**
* 하향 돌파 여부 확인
* @param prevValue 이전 값
* @param currValue 현재 값
* @param level 돌파 레벨
* @returns 하향 돌파 여부
*/
export function isCrossDown(prevValue: number, currValue: number, level: number): boolean {
return prevValue > level && currValue <= level;
}
/**
* 정배열 여부 확인
* @param trendStrength 추세 강도
* @returns 정배열 여부 (추세 강도 > 0)
*/
export function isBullishTrend(trendStrength: number): boolean {
return trendStrength > 0;
}
/**
* 역배열 여부 확인
* @param trendStrength 추세 강도
* @returns 역배열 여부 (추세 강도 < 0)
*/
export function isBearishTrend(trendStrength: number): boolean {
return trendStrength < 0;
}
/**
* 단일 캔들에 대한 매매 신호 생성
* @param current 현재 캔들 데이터
* @param previous 이전 캔들 데이터
* @param trendStrength 추세 강도
* @returns 매매 신호 또는 null
*/
export function generateSignalForCandle(
current: CandleWithIndicators,
previous: CandleWithIndicators,
trendStrength: number
): TradingSignal | null {
// Stochastic 값이 없으면 신호 없음
if (current.stochasticK === null || previous.stochasticK === null) {
return null;
}
const currK = current.stochasticK;
const prevK = previous.stochasticK;
const isBullish = isBullishTrend(trendStrength);
// 정배열인 경우
if (isBullish) {
// 20 상향 돌파 → 매수
if (isCrossUp(prevK, currK, OVERSOLD_LEVEL)) {
return {
date: current.date,
signal: 'BUY',
reason: '정배열 + Stochastic 20 상향 돌파 (과매도 탈출)',
stochasticK: currK,
stochasticD: current.stochasticD ?? 0,
trendStrength,
isBullish: true,
crossLevel: OVERSOLD_LEVEL,
crossDirection: 'UP',
};
}
// 50 상향 돌파 → 매수
if (isCrossUp(prevK, currK, NEUTRAL_LEVEL)) {
return {
date: current.date,
signal: 'BUY',
reason: '정배열 + Stochastic 50 상향 돌파 (중립선 상향)',
stochasticK: currK,
stochasticD: current.stochasticD ?? 0,
trendStrength,
isBullish: true,
crossLevel: NEUTRAL_LEVEL,
crossDirection: 'UP',
};
}
// 80 상향 돌파 → 매수
if (isCrossUp(prevK, currK, OVERBOUGHT_LEVEL)) {
return {
date: current.date,
signal: 'BUY',
reason: '정배열 + Stochastic 80 상향 돌파 (강한 상승 모멘텀)',
stochasticK: currK,
stochasticD: current.stochasticD ?? 0,
trendStrength,
isBullish: true,
crossLevel: OVERBOUGHT_LEVEL,
crossDirection: 'UP',
};
}
// 20 하향 돌파 → 매도
if (isCrossDown(prevK, currK, OVERSOLD_LEVEL)) {
return {
date: current.date,
signal: 'SELL',
reason: '정배열 + Stochastic 20 하향 돌파 (과매도 진입)',
stochasticK: currK,
stochasticD: current.stochasticD ?? 0,
trendStrength,
isBullish: true,
crossLevel: OVERSOLD_LEVEL,
crossDirection: 'DOWN',
};
}
// 50 하향 돌파 → 매도
if (isCrossDown(prevK, currK, NEUTRAL_LEVEL)) {
return {
date: current.date,
signal: 'SELL',
reason: '정배열 + Stochastic 50 하향 돌파 (중립선 하향)',
stochasticK: currK,
stochasticD: current.stochasticD ?? 0,
trendStrength,
isBullish: true,
crossLevel: NEUTRAL_LEVEL,
crossDirection: 'DOWN',
};
}
// 80 하향 돌파 → 매도
if (isCrossDown(prevK, currK, OVERBOUGHT_LEVEL)) {
return {
date: current.date,
signal: 'SELL',
reason: '정배열 + Stochastic 80 하향 돌파 (과매수 이탈)',
stochasticK: currK,
stochasticD: current.stochasticD ?? 0,
trendStrength,
isBullish: true,
crossLevel: OVERBOUGHT_LEVEL,
crossDirection: 'DOWN',
};
}
}
// 역배열인 경우
else if (isBearishTrend(trendStrength)) {
// 80 상향 돌파 → 매수
if (isCrossUp(prevK, currK, OVERBOUGHT_LEVEL)) {
return {
date: current.date,
signal: 'BUY',
reason: '역배열 + Stochastic 80 상향 돌파 (반등 시그널)',
stochasticK: currK,
stochasticD: current.stochasticD ?? 0,
trendStrength,
isBullish: false,
crossLevel: OVERBOUGHT_LEVEL,
crossDirection: 'UP',
};
}
// 80 하향 돌파 → 매도
if (isCrossDown(prevK, currK, OVERBOUGHT_LEVEL)) {
return {
date: current.date,
signal: 'SELL',
reason: '역배열 + Stochastic 80 하향 돌파 (하락 지속)',
stochasticK: currK,
stochasticD: current.stochasticD ?? 0,
trendStrength,
isBullish: false,
crossLevel: OVERBOUGHT_LEVEL,
crossDirection: 'DOWN',
};
}
}
// 신호 없음
return null;
}
/**
* 전체 캔들 데이터에 대한 매매 신호 생성
* @param candles 캔들 데이터 배열 (시간순 정렬)
* @param trendStrength 추세 강도 (현재 종목의 추세)
* @returns 매매 신호 배열
*/
export function generateSignals(
candles: CandleWithIndicators[],
trendStrength: number
): TradingSignal[] {
const signals: TradingSignal[] = [];
if (candles.length < 2) {
return signals;
}
for (let i = 1; i < candles.length; i++) {
const signal = generateSignalForCandle(candles[i], candles[i - 1], trendStrength);
if (signal) {
signals.push(signal);
}
}
return signals;
}
/**
* 가장 최근 매매 신호 조회
* @param candles 캔들 데이터 배열 (시간순 정렬)
* @param trendStrength 추세 강도
* @param lookbackPeriod 조회할 기간 (캔들 수)
* @returns 가장 최근 매매 신호 또는 null
*/
export function getLatestSignal(
candles: CandleWithIndicators[],
trendStrength: number,
lookbackPeriod: number = 10
): TradingSignal | null {
if (candles.length < 2) {
return null;
}
const startIdx = Math.max(1, candles.length - lookbackPeriod);
for (let i = candles.length - 1; i >= startIdx; i--) {
const signal = generateSignalForCandle(candles[i], candles[i - 1], trendStrength);
if (signal) {
return signal;
}
}
return null;
}
/**
* 신호 요약 정보 생성
* @param signals 매매 신호 배열
* @returns 요약 정보
*/
export function getSignalSummary(signals: TradingSignal[]): {
totalSignals: number;
buySignals: number;
sellSignals: number;
lastSignal: TradingSignal | null;
bullishSignals: number;
bearishSignals: number;
} {
const buySignals = signals.filter(s => s.signal === 'BUY');
const sellSignals = signals.filter(s => s.signal === 'SELL');
const bullishSignals = signals.filter(s => s.isBullish);
const bearishSignals = signals.filter(s => !s.isBullish);
return {
totalSignals: signals.length,
buySignals: buySignals.length,
sellSignals: sellSignals.length,
lastSignal: signals.length > 0 ? signals[signals.length - 1] : null,
bullishSignals: bullishSignals.length,
bearishSignals: bearishSignals.length,
};
}
/**
* 전략 설정 인터페이스
*/
export interface TrendStochasticStrategyConfig {
oversoldLevel: number; // 과매도 레벨 (기본: 20)
neutralLevel: number; // 중립 레벨 (기본: 50)
overboughtLevel: number; // 과매수 레벨 (기본: 80)
enableBullishAllLevels: boolean; // 정배열 시 모든 레벨 활성화 (기본: true)
enableBearishOnlyOverbought: boolean; // 역배열 시 80 레벨만 활성화 (기본: true)
}
/**
* 기본 전략 설정
*/
export const DEFAULT_STRATEGY_CONFIG: TrendStochasticStrategyConfig = {
oversoldLevel: 20,
neutralLevel: 50,
overboughtLevel: 80,
enableBullishAllLevels: true,
enableBearishOnlyOverbought: true,
};
/**
* 설정 가능한 전략 신호 생성
* @param current 현재 캔들 데이터
* @param previous 이전 캔들 데이터
* @param trendStrength 추세 강도
* @param config 전략 설정
* @returns 매매 신호 또는 null
*/
export function generateSignalWithConfig(
current: CandleWithIndicators,
previous: CandleWithIndicators,
trendStrength: number,
config: TrendStochasticStrategyConfig = DEFAULT_STRATEGY_CONFIG
): TradingSignal | null {
if (current.stochasticK === null || previous.stochasticK === null) {
return null;
}
const currK = current.stochasticK;
const prevK = previous.stochasticK;
const isBullish = isBullishTrend(trendStrength);
// 정배열인 경우
if (isBullish && config.enableBullishAllLevels) {
// 과매도 레벨 상향 돌파 → 매수
if (isCrossUp(prevK, currK, config.oversoldLevel)) {
return {
date: current.date,
signal: 'BUY',
reason: `정배열 + Stochastic ${config.oversoldLevel} 상향 돌파`,
stochasticK: currK,
stochasticD: current.stochasticD ?? 0,
trendStrength,
isBullish: true,
crossLevel: config.oversoldLevel,
crossDirection: 'UP',
};
}
// 중립 레벨 상향 돌파 → 매수
if (isCrossUp(prevK, currK, config.neutralLevel)) {
return {
date: current.date,
signal: 'BUY',
reason: `정배열 + Stochastic ${config.neutralLevel} 상향 돌파`,
stochasticK: currK,
stochasticD: current.stochasticD ?? 0,
trendStrength,
isBullish: true,
crossLevel: config.neutralLevel,
crossDirection: 'UP',
};
}
// 과매수 레벨 상향 돌파 → 매수
if (isCrossUp(prevK, currK, config.overboughtLevel)) {
return {
date: current.date,
signal: 'BUY',
reason: `정배열 + Stochastic ${config.overboughtLevel} 상향 돌파`,
stochasticK: currK,
stochasticD: current.stochasticD ?? 0,
trendStrength,
isBullish: true,
crossLevel: config.overboughtLevel,
crossDirection: 'UP',
};
}
// 과매도 레벨 하향 돌파 → 매도
if (isCrossDown(prevK, currK, config.oversoldLevel)) {
return {
date: current.date,
signal: 'SELL',
reason: `정배열 + Stochastic ${config.oversoldLevel} 하향 돌파`,
stochasticK: currK,
stochasticD: current.stochasticD ?? 0,
trendStrength,
isBullish: true,
crossLevel: config.oversoldLevel,
crossDirection: 'DOWN',
};
}
// 중립 레벨 하향 돌파 → 매도
if (isCrossDown(prevK, currK, config.neutralLevel)) {
return {
date: current.date,
signal: 'SELL',
reason: `정배열 + Stochastic ${config.neutralLevel} 하향 돌파`,
stochasticK: currK,
stochasticD: current.stochasticD ?? 0,
trendStrength,
isBullish: true,
crossLevel: config.neutralLevel,
crossDirection: 'DOWN',
};
}
// 과매수 레벨 하향 돌파 → 매도
if (isCrossDown(prevK, currK, config.overboughtLevel)) {
return {
date: current.date,
signal: 'SELL',
reason: `정배열 + Stochastic ${config.overboughtLevel} 하향 돌파`,
stochasticK: currK,
stochasticD: current.stochasticD ?? 0,
trendStrength,
isBullish: true,
crossLevel: config.overboughtLevel,
crossDirection: 'DOWN',
};
}
}
// 역배열인 경우
else if (isBearishTrend(trendStrength) && config.enableBearishOnlyOverbought) {
// 과매수 레벨 상향 돌파 → 매수
if (isCrossUp(prevK, currK, config.overboughtLevel)) {
return {
date: current.date,
signal: 'BUY',
reason: `역배열 + Stochastic ${config.overboughtLevel} 상향 돌파`,
stochasticK: currK,
stochasticD: current.stochasticD ?? 0,
trendStrength,
isBullish: false,
crossLevel: config.overboughtLevel,
crossDirection: 'UP',
};
}
// 과매수 레벨 하향 돌파 → 매도
if (isCrossDown(prevK, currK, config.overboughtLevel)) {
return {
date: current.date,
signal: 'SELL',
reason: `역배열 + Stochastic ${config.overboughtLevel} 하향 돌파`,
stochasticK: currK,
stochasticD: current.stochasticD ?? 0,
trendStrength,
isBullish: false,
crossLevel: config.overboughtLevel,
crossDirection: 'DOWN',
};
}
}
return null;
}
// 사용 예시
/*
import {
generateSignals,
getLatestSignal,
getSignalSummary,
CandleWithIndicators
} from './trendStochasticStrategy';
// 캔들 데이터 예시
const candles: CandleWithIndicators[] = [
{ date: '2024-01-01', open: 100, high: 105, low: 98, close: 103, volume: 1000, stochasticK: 18, stochasticD: 15 },
{ date: '2024-01-02', open: 103, high: 110, low: 101, close: 108, volume: 1200, stochasticK: 22, stochasticD: 19 },
// ...
];
// 추세 강도 (양수: 정배열, 음수: 역배열)
const trendStrength = 45.5; // 정배열
// 전체 신호 생성
const signals = generateSignals(candles, trendStrength);
console.log('전체 신호:', signals);
// 최근 신호 조회
const latestSignal = getLatestSignal(candles, trendStrength);
console.log('최근 신호:', latestSignal);
// 신호 요약
const summary = getSignalSummary(signals);
console.log('요약:', summary);
*/