- 기존 카드의 "매입가" → "평균단가" (avg_price) 로 라벨 변경 - 신규 "매입가" (purchase_price) 컬럼 추가. 추가/수정 폼에 입력 필드 노출 (미입력 시 평균단가 값으로 자동 설정) - 브로커별 총 매입 금액은 purchase_price × quantity 합계 기준 - 손익/수익률은 평균단가(avg_price) 기준 유지 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
127 lines
4.2 KiB
JavaScript
127 lines
4.2 KiB
JavaScript
/* ── helpers ─────────────────────────────────────────────────────── */
|
|
|
|
export const formatNumber = (value) => {
|
|
if (value === null || value === undefined || value === '') return '-';
|
|
const numeric = Number(value);
|
|
if (Number.isNaN(numeric)) return value;
|
|
return new Intl.NumberFormat('ko-KR').format(numeric);
|
|
};
|
|
|
|
export const formatPercent = (value) => {
|
|
if (value === null || value === undefined || value === '') return '-';
|
|
if (typeof value === 'string' && value.includes('%')) return value;
|
|
const numeric = Number(value);
|
|
if (Number.isNaN(numeric)) return value;
|
|
return `${numeric >= 0 ? '+' : ''}${numeric.toFixed(2)}%`;
|
|
};
|
|
|
|
export const pickFirst = (...values) =>
|
|
values.find((value) => value !== undefined && value !== null && value !== '');
|
|
|
|
export const getQty = (item) =>
|
|
pickFirst(item?.qty, item?.quantity, item?.holding, item?.hold_qty);
|
|
|
|
export const getBuyPrice = (item) =>
|
|
pickFirst(
|
|
item?.buy_price,
|
|
item?.avg_price,
|
|
item?.avg,
|
|
item?.purchase_price,
|
|
item?.buyPrice,
|
|
item?.price
|
|
);
|
|
|
|
export const getCurrentPrice = (item) =>
|
|
pickFirst(
|
|
item?.current_price,
|
|
item?.current,
|
|
item?.cur_price,
|
|
item?.now_price,
|
|
item?.market_price
|
|
);
|
|
|
|
export const getProfitRate = (item) =>
|
|
pickFirst(
|
|
item?.profit_rate,
|
|
item?.profitRate,
|
|
item?.profit_pct,
|
|
item?.profitPercent,
|
|
item?.pnl_rate,
|
|
item?.return_rate,
|
|
item?.yield
|
|
);
|
|
|
|
export const getProfitLoss = (item) =>
|
|
pickFirst(item?.profit_loss, item?.pnl, item?.profitLoss);
|
|
|
|
export const toNumeric = (value) => {
|
|
if (value === null || value === undefined || value === '') return null;
|
|
const numeric = Number(String(value).replace(/[^0-9.-]/g, ''));
|
|
return Number.isNaN(numeric) ? null : numeric;
|
|
};
|
|
|
|
/* ── Chart colors ──────────────────────────────────────────────── */
|
|
|
|
export const CHART_COLORS = ['#818cf8', '#fbbf24', '#34d399', '#f472b6', '#fb923c', '#a78bfa', '#38bdf8', '#4ade80'];
|
|
|
|
export const profitColorClass = (numericValue) => {
|
|
if (numericValue > 0) return 'is-up';
|
|
if (numericValue < 0) return 'is-down';
|
|
if (numericValue === 0) return 'is-flat';
|
|
return '';
|
|
};
|
|
|
|
export const getVixLabel = (vix) => {
|
|
if (vix < 12) return '극히 낮음 (안일 주의)';
|
|
if (vix < 20) return '정상 (안정적)';
|
|
if (vix < 30) return '주의 (불확실성 증가)';
|
|
if (vix < 40) return '높음 (극도의 공포)';
|
|
return '극단 (패닉)';
|
|
};
|
|
|
|
export const getFgLabel = (score) => {
|
|
if (score <= 25) return '극단적 공포';
|
|
if (score <= 45) return '공포';
|
|
if (score <= 55) return '중립';
|
|
if (score <= 75) return '탐욕';
|
|
return '극단적 탐욕';
|
|
};
|
|
|
|
/* ── empty portfolio form ────────────────────────────────────────── */
|
|
|
|
export const emptyPortfolioForm = {
|
|
broker: '',
|
|
ticker: '',
|
|
name: '',
|
|
quantity: '',
|
|
avg_price: '',
|
|
purchase_price: '',
|
|
};
|
|
|
|
/* ── empty sell-history form ─────────────────────────────────────── */
|
|
|
|
export const toLocalDatetimeValue = (isoStr) => {
|
|
if (!isoStr) return '';
|
|
const d = new Date(isoStr);
|
|
const pad = (n) => String(n).padStart(2, '0');
|
|
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
|
};
|
|
|
|
export const emptySellForm = () => ({
|
|
broker: '',
|
|
ticker: '',
|
|
name: '',
|
|
quantity: '',
|
|
avg_price: '',
|
|
sell_price: '',
|
|
commission: '',
|
|
sold_at: toLocalDatetimeValue(new Date().toISOString()),
|
|
});
|
|
|
|
/* ── TAB IDs ─────────────────────────────────────────────────────── */
|
|
|
|
export const TAB_PORTFOLIO = 'portfolio';
|
|
export const TAB_AI = 'ai';
|
|
export const TAB_REPORT = 'report';
|
|
export const TAB_ADVISOR = 'advisor';
|