111 lines
4.5 KiB
TypeScript
111 lines
4.5 KiB
TypeScript
import type { HLineStyle, PlotDef } from './indicatorRegistry';
|
|
|
|
const DEFAULT_HIST_UP = '#26A69A';
|
|
const DEFAULT_HIST_DOWN = '#EF5350';
|
|
|
|
/** 업비트/트레이딩뷰 스타일 색상 팔레트 (10×7) */
|
|
export const COLOR_PALETTE: string[] = [
|
|
'#FFFFFF', '#F5F5F5', '#E0E3EB', '#B2B5BE', '#9598A1', '#787B86', '#5D606B', '#434651', '#2A2E39', '#131722',
|
|
'#FCE4EC', '#FFCDD2', '#EF9A9A', '#E57373', '#EF5350', '#F44336', '#E53935', '#D32F2F', '#C62828', '#B71C1C',
|
|
'#FFF3E0', '#FFE0B2', '#FFCC80', '#FFB74D', '#FFA726', '#FF9800', '#FB8C00', '#F57C00', '#EF6C00', '#E65100',
|
|
'#FFFDE7', '#FFF9C4', '#FFF59D', '#FFF176', '#FFEE58', '#FFEB3B', '#FDD835', '#FBC02D', '#F9A825', '#F57F17',
|
|
'#E8F5E9', '#C8E6C9', '#A5D6A7', '#81C784', '#66BB6A', '#4CAF50', '#43A047', '#388E3C', '#2E7D32', '#1B5E20',
|
|
'#E0F7FA', '#B2EBF2', '#80DEEA', '#4DD0E1', '#26C6DA', '#00BCD4', '#00ACC1', '#0097A7', '#00838F', '#006064',
|
|
'#E3F2FD', '#BBDEFB', '#90CAF9', '#64B5F6', '#42A5F5', '#2196F3', '#1E88E5', '#1976D2', '#1565C0', '#0D47A1',
|
|
];
|
|
|
|
export function parsePlotColor(value: string): { hex6: string; alpha: number } {
|
|
const v = value.trim();
|
|
if (v.length >= 9 && v.startsWith('#')) {
|
|
const hex6 = v.slice(0, 7);
|
|
const alpha = Math.round(parseInt(v.slice(7, 9), 16) / 255 * 100);
|
|
return { hex6, alpha: Math.max(0, Math.min(100, alpha)) };
|
|
}
|
|
if (v.length >= 7 && v.startsWith('#')) {
|
|
return { hex6: v.slice(0, 7), alpha: 100 };
|
|
}
|
|
return { hex6: '#2962FF', alpha: 100 };
|
|
}
|
|
|
|
export function formatPlotColor(hex6: string, alphaPercent: number): string {
|
|
const h = hex6.length >= 7 ? hex6.slice(0, 7) : '#2962FF';
|
|
const a = Math.max(0, Math.min(100, alphaPercent));
|
|
const alphaHex = Math.round(a / 100 * 255).toString(16).padStart(2, '0');
|
|
return h + alphaHex;
|
|
}
|
|
|
|
export function plotColorCss(value: string): string {
|
|
const v = value.trim();
|
|
if (/^rgba?\(/i.test(v) || /^hsla?\(/i.test(v)) return v;
|
|
const { hex6, alpha } = parsePlotColor(value);
|
|
if (alpha >= 100) return hex6;
|
|
const r = parseInt(hex6.slice(1, 3), 16);
|
|
const g = parseInt(hex6.slice(3, 5), 16);
|
|
const b = parseInt(hex6.slice(5, 7), 16);
|
|
return `rgba(${r},${g},${b},${(alpha / 100).toFixed(2)})`;
|
|
}
|
|
|
|
export const LINE_WIDTH_OPTIONS = [1, 2, 3, 4] as const;
|
|
export const LINE_STYLE_OPTIONS: HLineStyle[] = ['solid', 'dashed', 'dotted'];
|
|
|
|
export const LINE_STYLE_LABELS: Record<HLineStyle, string> = {
|
|
solid: '실선',
|
|
dashed: '점선',
|
|
dotted: '도트',
|
|
};
|
|
|
|
/** histogram 막대 색에 투명도(88) 보장 */
|
|
export function withHistogramAlpha(color: string): string {
|
|
if (color.startsWith('rgba(') || color.startsWith('rgb(')) return color;
|
|
if (color.length >= 9 && color.startsWith('#')) return color;
|
|
if (color.length >= 7 && color.startsWith('#')) return `${color}88`;
|
|
return `${color}88`;
|
|
}
|
|
|
|
export function resolveHistogramUpColor(plot: PlotDef): string {
|
|
return plot.histogramUpColor ?? plot.color ?? DEFAULT_HIST_UP;
|
|
}
|
|
|
|
export function resolveHistogramDownColor(plot: PlotDef): string {
|
|
return plot.histogramDownColor ?? DEFAULT_HIST_DOWN;
|
|
}
|
|
|
|
/** MACD 히스토그램 — histogramUp/DownColor 로 0선 위·아래 색 분리 */
|
|
export function isMacdDualHistogramPlot(plot: PlotDef): boolean {
|
|
return plot.type === 'histogram'
|
|
&& plot.histogramUpColor != null
|
|
&& plot.histogramDownColor != null;
|
|
}
|
|
|
|
/**
|
|
* histogram 막대 색.
|
|
* - MACD(상·하 색 지정): 0선 기준 — 양수=상승색, 음수=하락색 (전봴 대비 혼합 없음)
|
|
* - 기타: 전봴 대비 방향 + 음수 영역 하락색
|
|
*/
|
|
export function histogramBarColor(
|
|
value: number,
|
|
prev: number | null | undefined,
|
|
plot: PlotDef,
|
|
): string {
|
|
if (isMacdDualHistogramPlot(plot)) {
|
|
const raw = value < 0 ? resolveHistogramDownColor(plot) : resolveHistogramUpColor(plot);
|
|
return withHistogramAlpha(raw);
|
|
}
|
|
const isDown = prev != null && !isNaN(prev) && value < prev;
|
|
const isNeg = value < 0;
|
|
const raw = (isDown || isNeg) ? resolveHistogramDownColor(plot) : resolveHistogramUpColor(plot);
|
|
return withHistogramAlpha(raw);
|
|
}
|
|
|
|
export function mapHistogramSeriesData(
|
|
filtered: Array<{ time: number; value: number; color?: string }>,
|
|
plot: PlotDef,
|
|
): Array<{ time: import('lightweight-charts').Time; value: number; color: string }> {
|
|
return filtered.map((p, idx, arr) => ({
|
|
time: p.time as import('lightweight-charts').Time,
|
|
value: p.value,
|
|
// lightweight-charts-indicators MACD 등이 막대별 color를 넣어도 설정(상승/하락) 색을 우선
|
|
color: histogramBarColor(p.value, idx > 0 ? arr[idx - 1].value : null, plot),
|
|
}));
|
|
}
|