goldenChat base source add
This commit is contained in:
@@ -0,0 +1,515 @@
|
||||
import axios from 'axios';
|
||||
import { getApiBaseUrl } from './apiConfig';
|
||||
|
||||
// API 베이스 URL (통합 설정 사용)
|
||||
const API_BASE_URL = getApiBaseUrl();
|
||||
|
||||
const alertApi = axios.create({
|
||||
baseURL: API_BASE_URL,
|
||||
timeout: 60000, // 60초로 증가 (외부 DB 연결 시간 고려)
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
withCredentials: false,
|
||||
});
|
||||
|
||||
// ==================== Types ====================
|
||||
|
||||
export type IndicatorType =
|
||||
| 'VOLUME'
|
||||
| 'MACD'
|
||||
| 'STOCHASTIC'
|
||||
| 'STOCHASTIC_K'
|
||||
| 'STOCHASTIC_D'
|
||||
| 'RSI'
|
||||
| 'ADX'
|
||||
| 'CCI'
|
||||
| 'TRIX'
|
||||
| 'DISPARITY'
|
||||
| 'NEW_PSYCHOLOGICAL'
|
||||
| 'INVEST_PSYCHOLOGICAL'
|
||||
| 'WILLIAMS_R'
|
||||
| 'BWI'
|
||||
| 'OBV'
|
||||
| 'VOLUME_OSC'
|
||||
| 'VR'
|
||||
| 'DMI';
|
||||
|
||||
export type ConditionType =
|
||||
| 'CROSS_UP'
|
||||
| 'CROSS_DOWN'
|
||||
| 'ABOVE'
|
||||
| 'BELOW'
|
||||
| 'EQUAL'
|
||||
| 'BETWEEN'
|
||||
| 'ZERO_CROSS_UP'
|
||||
| 'ZERO_CROSS_DOWN'
|
||||
| 'SIGNAL_CROSS_UP'
|
||||
| 'SIGNAL_CROSS_DOWN'
|
||||
| 'HISTOGRAM_POSITIVE'
|
||||
| 'HISTOGRAM_NEGATIVE'
|
||||
| 'HISTOGRAM_INCREASE'
|
||||
| 'HISTOGRAM_DECREASE'
|
||||
| 'K_CROSS_D_UP'
|
||||
| 'K_CROSS_D_DOWN'
|
||||
| 'VOLUME_SPIKE'
|
||||
| 'VOLUME_ABOVE_AVG'
|
||||
| 'VOLUME_BELOW_AVG'
|
||||
| 'INCREASE'
|
||||
| 'DECREASE'
|
||||
| 'GOLDEN_CROSS'
|
||||
| 'DEAD_CROSS';
|
||||
|
||||
export interface AlertConditionDto {
|
||||
id?: number;
|
||||
cryptoAlertId?: number;
|
||||
indicatorType: IndicatorType;
|
||||
conditionType: ConditionType;
|
||||
targetValue?: number;
|
||||
secondaryValue?: number;
|
||||
period?: number;
|
||||
comparePeriod?: number;
|
||||
enabled?: boolean;
|
||||
orderIndex?: number;
|
||||
description?: string;
|
||||
logicalOperator?: 'AND' | 'OR';
|
||||
}
|
||||
|
||||
export interface CryptoAlertDto {
|
||||
id?: number;
|
||||
symbol: string;
|
||||
koreanName: string;
|
||||
englishName?: string;
|
||||
enabled?: boolean;
|
||||
userId?: number;
|
||||
conditions?: AlertConditionDto[]; // 하위 호환성
|
||||
createdAt?: string;
|
||||
updatedAt?: string;
|
||||
}
|
||||
|
||||
export interface AlertNotificationDto {
|
||||
id?: number;
|
||||
cryptoAlertId?: number;
|
||||
symbol?: string;
|
||||
koreanName?: string;
|
||||
message: string;
|
||||
conditionDescription?: string;
|
||||
isRead?: boolean;
|
||||
triggeredAt?: string;
|
||||
notifiedAt?: string;
|
||||
userId?: number;
|
||||
/** 조건 충족한 봉 시각(백엔드 LocalDateTime → ISO 또는 배열) */
|
||||
candleTime?: string;
|
||||
/** 알림 스케줄/조건의 캔들 간격 (예: 3m, 1h, 1d) */
|
||||
timeInterval?: string;
|
||||
}
|
||||
|
||||
export interface IndicatorDataDto {
|
||||
time?: string;
|
||||
open?: number;
|
||||
high?: number;
|
||||
low?: number;
|
||||
price?: number;
|
||||
ma10?: number;
|
||||
ma20?: number;
|
||||
ma60?: number;
|
||||
ma120?: number;
|
||||
rsi?: number;
|
||||
macdLine?: number;
|
||||
signalLine?: number;
|
||||
histogram?: number;
|
||||
stochK?: number;
|
||||
stochD?: number;
|
||||
cci?: number;
|
||||
adx?: number;
|
||||
plusDI?: number;
|
||||
minusDI?: number;
|
||||
williamsR?: number;
|
||||
newPsy?: number;
|
||||
investPsy?: number;
|
||||
bwi?: number;
|
||||
obv?: number;
|
||||
obvSignal?: number; // OBV Signal Line (이동평균)
|
||||
volumeOsc?: number;
|
||||
vr?: number;
|
||||
trix?: number;
|
||||
trixSignal?: number; // TRIX Signal Line
|
||||
ichimokuTenkan?: number;
|
||||
ichimokuKijun?: number;
|
||||
ichimokuSenkouA?: number;
|
||||
ichimokuSenkouB?: number;
|
||||
ichimokuChikou?: number;
|
||||
disparityMid?: number;
|
||||
volume?: number;
|
||||
}
|
||||
|
||||
export interface AlertNotificationDetailDto extends AlertNotificationDto {
|
||||
englishName?: string;
|
||||
indicatorData?: IndicatorDataDto;
|
||||
/** 알림 발생 시점 전후 지표 시계열 (조건 관련 차트 렌더링용) */
|
||||
indicatorDataSeries?: IndicatorDataDto[];
|
||||
satisfiedConditions?: AlertConditionDto[];
|
||||
currentPrice?: number;
|
||||
changeRate?: number;
|
||||
volume?: number;
|
||||
/** 백엔드: 알림 발생 봉 기준 OHLCV 스냅샷 */
|
||||
snapshotOpen?: number;
|
||||
snapshotHigh?: number;
|
||||
snapshotLow?: number;
|
||||
snapshotClose?: number;
|
||||
snapshotVolume?: number;
|
||||
}
|
||||
|
||||
export interface AlertScheduleConfigDto {
|
||||
id?: number;
|
||||
userId?: number;
|
||||
cronExpression?: string;
|
||||
timeInterval?: string; // 시간봉 간격 (1m, 3m, 5m, 10m, 15m, 30m, 1h, 4h, 1d, 1w)
|
||||
/** 전략 조건 평가 주기(초) 3~30, 스케줄러 실행 간격 */
|
||||
strategyCheckIntervalSeconds?: number;
|
||||
enabled?: boolean;
|
||||
lastRunTime?: string;
|
||||
nextRunTime?: string;
|
||||
createdAt?: string;
|
||||
updatedAt?: string;
|
||||
}
|
||||
|
||||
// ==================== CryptoAlert APIs ====================
|
||||
|
||||
/**
|
||||
* 전체 알림 목록 조회
|
||||
*/
|
||||
export const getAllAlerts = async (): Promise<CryptoAlertDto[]> => {
|
||||
const response = await alertApi.get<CryptoAlertDto[]>('/alerts');
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* 활성화된 알림 목록 조회
|
||||
*/
|
||||
export const getEnabledAlerts = async (): Promise<CryptoAlertDto[]> => {
|
||||
const response = await alertApi.get<CryptoAlertDto[]>('/alerts/enabled');
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* 알림 상세 조회
|
||||
*/
|
||||
export const getAlert = async (alertId: number): Promise<CryptoAlertDto> => {
|
||||
const response = await alertApi.get<CryptoAlertDto>(`/alerts/${alertId}`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* 심볼로 알림 조회
|
||||
*/
|
||||
export const getAlertBySymbol = async (symbol: string): Promise<CryptoAlertDto | null> => {
|
||||
try {
|
||||
const response = await alertApi.get<CryptoAlertDto>(`/alerts/symbol/${symbol}`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
if (error.response?.status === 404) {
|
||||
return null;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 알림 생성
|
||||
*/
|
||||
export const createAlert = async (dto: CryptoAlertDto): Promise<CryptoAlertDto> => {
|
||||
const response = await alertApi.post<CryptoAlertDto>('/alerts', dto);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* 알림 ON/OFF 토글
|
||||
*/
|
||||
export const toggleAlert = async (alertId: number, enabled: boolean): Promise<CryptoAlertDto> => {
|
||||
const response = await alertApi.put<CryptoAlertDto>(
|
||||
`/alerts/${alertId}/toggle`,
|
||||
null,
|
||||
{ params: { enabled } }
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* 알림 삭제
|
||||
*/
|
||||
export const deleteAlert = async (alertId: number): Promise<void> => {
|
||||
await alertApi.delete(`/alerts/${alertId}`);
|
||||
};
|
||||
|
||||
// ==================== AlertCondition APIs ====================
|
||||
|
||||
/**
|
||||
* 전역 알림 조건 목록 조회
|
||||
*/
|
||||
export const getGlobalConditions = async (): Promise<AlertConditionDto[]> => {
|
||||
const response = await alertApi.get<AlertConditionDto[]>('/alerts/conditions/global');
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* 알림 조건 목록 조회 (개별 종목용 - 하위 호환)
|
||||
*/
|
||||
export const getConditions = async (alertId: number): Promise<AlertConditionDto[]> => {
|
||||
const response = await alertApi.get<AlertConditionDto[]>(`/alerts/${alertId}/conditions`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* 전역 조건 추가
|
||||
*/
|
||||
export const addGlobalCondition = async (dto: AlertConditionDto): Promise<AlertConditionDto> => {
|
||||
const response = await alertApi.post<AlertConditionDto>('/alerts/conditions/global', dto);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* 조건 추가 (개별 종목용 - 하위 호환)
|
||||
*/
|
||||
export const addCondition = async (
|
||||
alertId: number,
|
||||
dto: AlertConditionDto
|
||||
): Promise<AlertConditionDto> => {
|
||||
const response = await alertApi.post<AlertConditionDto>(
|
||||
`/alerts/${alertId}/conditions`,
|
||||
dto
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* 조건 수정
|
||||
*/
|
||||
export const updateCondition = async (
|
||||
conditionId: number,
|
||||
dto: AlertConditionDto
|
||||
): Promise<AlertConditionDto> => {
|
||||
const response = await alertApi.put<AlertConditionDto>(
|
||||
`/alerts/conditions/${conditionId}`,
|
||||
dto
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* 조건 삭제
|
||||
*/
|
||||
export const deleteCondition = async (conditionId: number): Promise<void> => {
|
||||
await alertApi.delete(`/alerts/conditions/${conditionId}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* 조건 활성화/비활성화
|
||||
*/
|
||||
export const toggleCondition = async (
|
||||
conditionId: number,
|
||||
enabled: boolean
|
||||
): Promise<AlertConditionDto> => {
|
||||
const response = await alertApi.put<AlertConditionDto>(
|
||||
`/alerts/conditions/${conditionId}/toggle`,
|
||||
null,
|
||||
{ params: { enabled } }
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
// ==================== AlertNotification APIs ====================
|
||||
|
||||
/**
|
||||
* 미읽음 알림 조회
|
||||
*/
|
||||
export const getUnreadNotifications = async (userId?: number): Promise<AlertNotificationDto[]> => {
|
||||
const response = await alertApi.get<AlertNotificationDto[]>(
|
||||
'/alerts/notifications/unread',
|
||||
{ params: { userId } }
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* 알림 목록 조회 (페이징)
|
||||
*/
|
||||
export const getNotifications = async (
|
||||
userId?: number,
|
||||
page: number = 0,
|
||||
size: number = 20
|
||||
): Promise<{ content: AlertNotificationDto[]; totalPages: number; totalElements: number }> => {
|
||||
const response = await alertApi.get('/alerts/notifications', {
|
||||
params: { userId, page, size },
|
||||
});
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* 알림 읽음 처리
|
||||
*/
|
||||
export const markAsRead = async (notificationId: number): Promise<void> => {
|
||||
await alertApi.put(`/alerts/notifications/${notificationId}/read`);
|
||||
};
|
||||
|
||||
/**
|
||||
* 모든 미읽은 알림 읽음 처리
|
||||
*/
|
||||
export const markAllAsRead = async (): Promise<void> => {
|
||||
await alertApi.put('/alerts/notifications/read-all');
|
||||
};
|
||||
|
||||
/**
|
||||
* 알림 상세 정보 조회 (분석 데이터 포함)
|
||||
* @param symbol 통합 알림 등 행별 마켓(예: KRW-BTC). 지정 시 해당 종목 캔들·지표로 스냅샷 (알림 봉 시각은 동일).
|
||||
*/
|
||||
export const getNotificationDetail = async (
|
||||
notificationId: number,
|
||||
options?: { symbol?: string | null }
|
||||
): Promise<AlertNotificationDetailDto> => {
|
||||
const sym = options?.symbol?.trim();
|
||||
const response = await alertApi.get<AlertNotificationDetailDto>(
|
||||
`/alerts/notifications/${notificationId}/detail`,
|
||||
sym ? { params: { symbol: sym } } : undefined
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
// ==================== AlertScheduleConfig APIs ====================
|
||||
|
||||
/**
|
||||
* 스케줄 설정 조회
|
||||
*/
|
||||
export const getScheduleConfig = async (userId?: number): Promise<AlertScheduleConfigDto> => {
|
||||
const response = await alertApi.get<AlertScheduleConfigDto>(
|
||||
'/alerts/schedule',
|
||||
{ params: { userId } }
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* 스케줄 설정 저장/업데이트
|
||||
*/
|
||||
export const saveScheduleConfig = async (
|
||||
dto: AlertScheduleConfigDto
|
||||
): Promise<AlertScheduleConfigDto> => {
|
||||
const response = await alertApi.put<AlertScheduleConfigDto>('/alerts/schedule', dto);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* 알림 삭제
|
||||
*/
|
||||
export const deleteNotification = async (notificationId: number): Promise<void> => {
|
||||
await alertApi.delete(`/alerts/notifications/${notificationId}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* 읽은 알림 모두 삭제
|
||||
*/
|
||||
export const deleteReadNotifications = async (userId?: number): Promise<void> => {
|
||||
await alertApi.delete('/alerts/notifications/read', {
|
||||
params: { userId },
|
||||
});
|
||||
};
|
||||
|
||||
// ==================== Scheduler Service APIs ====================
|
||||
|
||||
export interface SchedulerStatusDto {
|
||||
running: boolean;
|
||||
status?: string;
|
||||
error?: string;
|
||||
schedulerRunning?: boolean;
|
||||
kafkaConnected?: boolean;
|
||||
}
|
||||
|
||||
export interface SchedulerStartResultDto {
|
||||
success: boolean;
|
||||
message: string;
|
||||
manualCommand?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 스케줄러 서비스 상태 조회
|
||||
*/
|
||||
export const getSchedulerStatus = async (): Promise<SchedulerStatusDto> => {
|
||||
const response = await alertApi.get<SchedulerStatusDto>('/scheduler/status');
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* 스케줄러 서비스 시작 시도
|
||||
*/
|
||||
export const startScheduler = async (): Promise<SchedulerStartResultDto> => {
|
||||
const response = await alertApi.post<SchedulerStartResultDto>('/scheduler/start');
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/** Internal `/internal/alerts/evaluate` 요청 본문 */
|
||||
export interface AlertEvaluateRequest {
|
||||
symbol: string;
|
||||
strategyRuleId: number;
|
||||
timeInterval: string;
|
||||
liveCandles?: Record<string, unknown>[];
|
||||
useLiveCandleTail?: boolean;
|
||||
}
|
||||
|
||||
export interface AlertEvaluateResponse {
|
||||
symbol?: string;
|
||||
strategyRuleId?: number;
|
||||
conditionMet: boolean;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 알림 전략 조건 평가 (스케줄러와 동일 백엔드 경로)
|
||||
* `liveCandles`가 있으면 WS 버퍼 기준, 없으면 서버 Redis/REST 200봉.
|
||||
*/
|
||||
export const evaluateAlertStrategy = async (body: AlertEvaluateRequest): Promise<AlertEvaluateResponse> => {
|
||||
const response = await alertApi.post<AlertEvaluateResponse>('/internal/alerts/evaluate', body);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* 테스트 알림 생성 (파이프라인 검증용)
|
||||
* Internal API - 첫 번째 종목으로 테스트 알림 생성
|
||||
*/
|
||||
export const createTestNotification = async (): Promise<AlertNotificationDto> => {
|
||||
const response = await alertApi.post<AlertNotificationDto>('/internal/alerts/test');
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* 알림 읽음 처리
|
||||
*/
|
||||
export const markNotificationAsRead = async (notificationId: number): Promise<void> => {
|
||||
await alertApi.put(`/alerts/notifications/${notificationId}/read`);
|
||||
};
|
||||
|
||||
export default {
|
||||
getAllAlerts,
|
||||
getEnabledAlerts,
|
||||
getAlert,
|
||||
getAlertBySymbol,
|
||||
createAlert,
|
||||
toggleAlert,
|
||||
deleteAlert,
|
||||
getGlobalConditions,
|
||||
getConditions,
|
||||
addGlobalCondition,
|
||||
addCondition,
|
||||
updateCondition,
|
||||
deleteCondition,
|
||||
toggleCondition,
|
||||
getUnreadNotifications,
|
||||
getNotifications,
|
||||
markAsRead,
|
||||
getScheduleConfig,
|
||||
saveScheduleConfig,
|
||||
deleteNotification,
|
||||
deleteReadNotifications,
|
||||
markNotificationAsRead,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user