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
+86
View File
@@ -0,0 +1,86 @@
/**
* 매매 시그널 알림 표시용 포맷·라벨
*/
import { getKoreanName } from './marketNameCache';
import { formatUnixDateTime, formatUnixClock, getDisplayTimezone } from './timezone';
export interface TradeSignalDisplaySource {
market: string;
signalType: 'BUY' | 'SELL';
price: number;
candleTime: number;
strategyName?: string | null;
strategyId?: number | null;
executionType?: string;
candleType?: string;
receivedAt?: number;
}
export function parseMarket(market: string) {
const parts = market.split('-');
const fiat = parts[0] ?? 'KRW';
const coin = parts.length > 1 ? parts.slice(1).join('-') : market;
return { fiat, coin, code: market };
}
export function formatSignalPrice(price: number): string {
if (!Number.isFinite(price) || price <= 0) return '—';
return `${price.toLocaleString('ko-KR', { maximumFractionDigits: 0 })}`;
}
export function formatSignalTime(unixSec: number, withDate = true): string {
if (!unixSec) return '—';
const tz = getDisplayTimezone();
if (!withDate) return formatUnixClock(unixSec, tz, true);
return formatUnixDateTime(unixSec, tz);
}
export function getSignalTypeKo(type: 'BUY' | 'SELL'): string {
return type === 'BUY' ? '매수' : '매도';
}
export function getSignalHeadline(item: TradeSignalDisplaySource): string {
const korean = getKoreanName(item.market);
const { coin } = parseMarket(item.market);
const name = korean || coin;
return `${name} ${getSignalTypeKo(item.signalType)} 알림`;
}
export function getExecutionLabel(executionType?: string, candleType?: string): string {
const candle = candleType ?? '1m';
const candleKo = candle === '1m' ? '1분봉' : candle;
if (executionType === 'REALTIME_TICK') return `실시간 틱 · ${candleKo}`;
if (executionType === 'CANDLE_CLOSE') return `봉 마감 · ${candleKo}`;
return candleKo;
}
export function getMarketDisplayLine(market: string): { primary: string; secondary: string } {
const korean = getKoreanName(market);
const { code } = parseMarket(market);
return {
primary: korean || code,
secondary: code,
};
}
/** 상세 행 목록 (스낵바·목록 공통) */
export function buildSignalDetailRows(item: TradeSignalDisplaySource): Array<{ label: string; value: string; highlight?: boolean }> {
const { primary, secondary } = getMarketDisplayLine(item.market);
const rows: Array<{ label: string; value: string; highlight?: boolean }> = [
{ label: '종목', value: primary !== secondary ? `${secondary} · ${primary}` : secondary },
{ label: '매매 구분', value: `${getSignalTypeKo(item.signalType)} (${item.signalType})`, highlight: true },
{ label: '기준 가격', value: formatSignalPrice(item.price), highlight: true },
{ label: '캔들 시각', value: `${formatSignalTime(item.candleTime)} · ${item.candleType ?? '1m'}` },
];
if (item.receivedAt && Math.abs(item.receivedAt - item.candleTime * 1000) > 2000) {
rows.push({ label: '수신 시각', value: formatSignalTime(Math.floor(item.receivedAt / 1000)) });
}
rows.push(
{ label: '전략', value: item.strategyName?.trim() || (item.strategyId != null ? `전략 #${item.strategyId}` : '실시간 전략') },
{ label: '실행 방식', value: getExecutionLabel(item.executionType, item.candleType) },
);
return rows;
}