goldenChat base source add
This commit is contained in:
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user