주가 표시형식 수정

This commit is contained in:
Macbook
2026-06-06 00:56:45 +09:00
parent 1d4e606f77
commit 94d3f92bb9
18 changed files with 156 additions and 57 deletions
+5 -4
View File
@@ -25,6 +25,7 @@ import { BollingerBandFillPrimitive } from './BollingerBandFillPrimitive';
import { resolveBbBandBackground } from './bollingerConfig';
import type { OHLCVBar, ChartType, Theme, IndicatorConfig, Timeframe } from '../types';
import { formatChartAxisPrice, formatIndicatorAxisPrice } from './dataGenerator';
import { formatUpbitKrwPrice } from './safeFormat';
import { TRADE_BUY_COLOR, TRADE_SELL_COLOR } from './tradeSignalColors';
import {
DEFAULT_CHART_TIME_FORMAT,
@@ -1271,7 +1272,7 @@ export class ChartManager {
: buy ? '매수'
: '매도';
const text = showPrice
? `${label} : ${s.price.toLocaleString()}`
? `${label} : ${formatUpbitKrwPrice(s.price)}`
: label;
return {
time: s.time,
@@ -1304,7 +1305,7 @@ export class ChartManager {
const buy = m.signal === 'BUY';
const label = buy ? '매수' : '매도';
const text = showPrice
? `${label} : ${m.price.toLocaleString()}`
? `${label} : ${formatUpbitKrwPrice(m.price)}`
: label;
return {
time: m.time,
@@ -1988,7 +1989,7 @@ export class ChartManager {
if (!this.mainSeries) return;
const line = this.mainSeries.createPriceLine({
price, color: '#FFB300', lineWidth: 1, lineStyle: LineStyle.Dashed,
axisLabelVisible: true, title: label || `Alert ${price.toFixed(2)}`,
axisLabelVisible: true, title: label || `Alert ${formatUpbitKrwPrice(price)}`,
});
this.alertEntries.push({ price, label, line });
}
@@ -2051,7 +2052,7 @@ export class ChartManager {
this.drawingPoints = [];
} else if (tool === 'trendline' && this.drawingPoints.length === 2) {
const [p1, p2] = this.drawingPoints;
const l = this.mainSeries.createPriceLine({ price: (p1.price + p2.price) / 2, color: '#2962FF', lineWidth: 1, lineStyle: LineStyle.Solid, axisLabelVisible: false, title: `${p1.price.toFixed(2)}${p2.price.toFixed(2)}` });
const l = this.mainSeries.createPriceLine({ price: (p1.price + p2.price) / 2, color: '#2962FF', lineWidth: 1, lineStyle: LineStyle.Solid, axisLabelVisible: false, title: `${formatUpbitKrwPrice(p1.price)}${formatUpbitKrwPrice(p2.price)}` });
this.drawingLines.push(l);
this.drawingPoints = [];
} else if (tool === 'measure' && this.drawingPoints.length === 2) {
+6 -2
View File
@@ -1,4 +1,5 @@
import type { ChartType, Timeframe } from '../types';
import { formatUpbitKrwPrice } from './safeFormat';
export function toUpbitMarket(symbol: string): string {
return symbol.startsWith('KRW-') ? symbol : `KRW-${symbol}`;
@@ -79,8 +80,11 @@ export const pct = (v: number, dec = 1) =>
export const pctAbs = (v: number, dec = 0) =>
isFinite(v) ? `${(v * 100).toFixed(dec)}%` : '0%';
export const krw = (v: number) =>
isFinite(v) ? `${v >= 0 ? '+' : ''}${Math.round(v).toLocaleString()}` : '';
export const krw = (v: number) => {
if (!isFinite(v)) return '';
const sign = v >= 0 ? '+' : '-';
return `${sign}${formatUpbitKrwPrice(Math.abs(v), '0')}`;
};
const TF_SEC: Record<string, number> = {
'1m': 60, '3m': 180, '5m': 300, '10m': 600, '15m': 900, '30m': 1800,
+2 -1
View File
@@ -3,6 +3,7 @@
*/
import type { TickerData } from '../hooks/useMarketTicker';
import type { OHLCVBar } from '../types';
import { formatUpbitKrwPrice } from './safeFormat';
export interface ChartLiveQuote {
price: number | null;
@@ -41,7 +42,7 @@ export function resolveChartLiveQuote(
export function formatChartLivePrice(price: number | null): string {
if (price == null || !Number.isFinite(price)) return '—';
return `${Math.round(price).toLocaleString('ko-KR')} KRW`;
return `${formatUpbitKrwPrice(price)} KRW`;
}
export function formatChartLiveChangePct(changePct: number | null): string {
+8 -10
View File
@@ -1,5 +1,6 @@
import type { OHLCVBar, Timeframe } from '../types';
import { formatUnixForChart } from './timezone';
import { formatUpbitKrwPrice } from './safeFormat';
export const SYMBOLS = [
'BTCUSD','ETHUSD','BNBUSD','SOLUSD','ADAUSD',
@@ -82,25 +83,22 @@ export function generateBars(symbol: string, timeframe: Timeframe): OHLCVBar[] {
}
export function formatPrice(price: number): string {
if (price >= 10000) return price.toLocaleString('en-US', { minimumFractionDigits: 0, maximumFractionDigits: 0 });
if (price >= 100) return price.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
if (price >= 1) return price.toLocaleString('en-US', { minimumFractionDigits: 3, maximumFractionDigits: 4 });
return price.toLocaleString('en-US', { minimumFractionDigits: 4, maximumFractionDigits: 6 });
return formatUpbitKrwPrice(price, String(price));
}
/** 실시간 차트 우측 가격축·크로스헤어·시리즈 라벨 (KRW 주가 — 소수점 없음) */
/**
* 실시간 차트 우측 가격축·크로스헤어·시리즈 라벨 — 업비트 KRW 가격 표시 규칙 적용.
* LightweightCharts formatter 콜백으로 사용되므로 항상 string 반환.
*/
export function formatChartAxisPrice(price: number): string {
if (!Number.isFinite(price)) return String(price);
return Math.round(price).toLocaleString('en-US', {
minimumFractionDigits: 0,
maximumFractionDigits: 0,
});
return formatUpbitKrwPrice(price, String(price));
}
/** 보조지표 pane 우측 가격축·시리즈 라벨 (소수점 2자리 고정) */
export function formatIndicatorAxisPrice(price: number): string {
if (!Number.isFinite(price)) return String(price);
return price.toLocaleString('en-US', {
return price.toLocaleString('ko-KR', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
+93
View File
@@ -1,3 +1,96 @@
/**
* 업비트 KRW 마켓 가격 표시 규칙 (2024년 개정 기준)
*
* 가격대 별 호가 단위:
* ≥ 2,000,000원 : 1,000원 단위 (소수점 없음) 예) 92,133,000
* ≥ 200,000원 : 100원 단위 (소수점 없음) 예) 234,100
* ≥ 10,000원 : 10원 단위 (소수점 없음) 예) 23,450
* ≥ 100원 : 1원 단위 (소수점 없음) 예) 1,234 / 452
* ≥ 10원 : 0.1원 단위 (소수점 1자리) 예) 45.3
* ≥ 1원 : 0.01원 단위 (소수점 2자리) 예) 4.53
* ≥ 0.1원 : 0.001원 단위 (소수점 3자리) 예) 0.453
* < 0.1원 : 0.0001원 단위 (소수점 4자리) 예) 0.0123
*
* 주의: 100~1,000원 구간은 2024년 이후 1원 단위(소수점 없음)로 환원됨.
*/
export function formatUpbitKrwPrice(
price: number | null | undefined,
fallback = '—',
): string {
if (price == null || !Number.isFinite(price)) return fallback;
const abs = Math.abs(price);
if (abs >= 2_000_000) {
// 1,000원 단위
return (Math.round(price / 1000) * 1000).toLocaleString('ko-KR', {
maximumFractionDigits: 0,
});
}
if (abs >= 200_000) {
// 100원 단위
return (Math.round(price / 100) * 100).toLocaleString('ko-KR', {
maximumFractionDigits: 0,
});
}
if (abs >= 10_000) {
// 10원 단위
return (Math.round(price / 10) * 10).toLocaleString('ko-KR', {
maximumFractionDigits: 0,
});
}
if (abs >= 100) {
// 1원 단위 (소수점 없음) — 100~10,000원 구간
return Math.round(price).toLocaleString('ko-KR', {
maximumFractionDigits: 0,
});
}
if (abs >= 10) {
// 0.1원 단위 (소수점 1자리)
return (Math.round(price * 10) / 10).toLocaleString('ko-KR', {
minimumFractionDigits: 1,
maximumFractionDigits: 1,
});
}
if (abs >= 1) {
// 0.01원 단위 (소수점 2자리)
return (Math.round(price * 100) / 100).toLocaleString('ko-KR', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
}
if (abs >= 0.1) {
// 0.001원 단위 (소수점 3자리)
return (Math.round(price * 1000) / 1000).toLocaleString('ko-KR', {
minimumFractionDigits: 3,
maximumFractionDigits: 3,
});
}
if (abs > 0) {
// 0.0001원 단위 (소수점 4자리)
return (Math.round(price * 10000) / 10000).toLocaleString('ko-KR', {
minimumFractionDigits: 4,
maximumFractionDigits: 4,
});
}
return '0';
}
/**
* 업비트 KRW 가격의 최소 호가 단위(tick size)를 반환한다.
* LightweightCharts priceFormat.minMove 또는 주문 입력 스텝 계산에 사용.
*/
export function upbitKrwTickSize(price: number): number {
const abs = Math.abs(price);
if (abs >= 2_000_000) return 1000;
if (abs >= 200_000) return 100;
if (abs >= 10_000) return 10;
if (abs >= 100) return 1;
if (abs >= 10) return 0.1;
if (abs >= 1) return 0.01;
if (abs >= 0.1) return 0.001;
return 0.0001;
}
/**
* API·JSON·WebSocket에서 문자열로 올 수 있는 숫자를 안전하게 처리
*/
+2 -1
View File
@@ -1,6 +1,7 @@
/**
* 매매 시그널 알림 표시용 포맷·라벨
*/
import { formatUpbitKrwPrice } from './safeFormat';
import { formatUnixWithChartPattern, splitChartTimePattern } from './chartTimeFormat';
import { getKoreanName } from './marketNameCache';
import { getTradeAlertTimeFormat } from './tradeAlertTimeFormat';
@@ -27,7 +28,7 @@ export function parseMarket(market: string) {
export function formatSignalPrice(price: number): string {
if (!Number.isFinite(price) || price <= 0) return '—';
return `${price.toLocaleString('ko-KR', { maximumFractionDigits: 0 })}`;
return `${formatUpbitKrwPrice(price, '—')}`;
}
export function formatSignalTime(unixSec: number, withDate = true): string {