Skip to content

Commit d43ae94

Browse files
authored
Merge pull request #142 from InserToken/feat/1-design-yeakyung
[feat] chart error
2 parents 78701e1 + 04b1963 commit d43ae94

File tree

3 files changed

+129
-62
lines changed

3 files changed

+129
-62
lines changed

src/components/blocks/Investment/InvestmentStock/InvestmentStock.client.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ export default function InvestmentStockClient() {
393393
</>
394394
)}
395395
<span
396-
className="px-1 cursor-pointer text-gray-400 hover:bg-gray-800 rounded-sm"
396+
className="cursor-pointer text-gray-400 hover:bg-gray-800 rounded-sm"
397397
onClick={() => setShowIndicators((prev) => !prev)}
398398
>
399399
{showIndicators ? "– 보조지표 접기" : "+ 보조지표 설정"}

src/components/blocks/PracticePage/PracticePage.client.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ export default function PracticeClient() {
307307
)}
308308

309309
<span
310-
className="px-1 cursor-pointer text-gray-400 hover:bg-gray-800 rounded-sm"
310+
className="cursor-pointer text-gray-400 hover:bg-gray-800 rounded-sm"
311311
onClick={() => setShowIndicators((prev) => !prev)}
312312
>
313313
{showIndicators ? "– 보조지표 접기" : "+ 보조지표 설정"}

src/components/charts/InvestCandleChart.tsx

Lines changed: 127 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -385,39 +385,105 @@ export default function InvestCandleChart({
385385
visibleCandles > 1 ? chartWidth / (visibleCandles - 1) : chartWidth;
386386
const candleWidth = Math.min(40, candleSpacing * 0.7, 24);
387387

388+
// function getLinePoints(
389+
// arr: (number | null)[],
390+
// candleSpacing: number,
391+
// getY: (v: number) => number
392+
// ) {
393+
// return arr
394+
// .map((val, i) =>
395+
// typeof val === "number" && !isNaN(val)
396+
// ? `${i * candleSpacing},${getY(val)}`
397+
// : null
398+
// )
399+
// .filter(Boolean)
400+
// .join(" ");
401+
// }
388402
function getLinePoints(
389403
arr: (number | null)[],
390404
candleSpacing: number,
391-
getY: (v: number) => number
405+
getY: (v: number) => number,
406+
slicedData: Candle[]
392407
) {
393408
return arr
394409
.map((val, i) =>
395-
typeof val === "number" && !isNaN(val)
410+
typeof val === "number" &&
411+
!isNaN(val) &&
412+
slicedData[i] &&
413+
!isDotOnlyCandle(slicedData[i])
396414
? `${i * candleSpacing},${getY(val)}`
397415
: null
398416
)
399417
.filter(Boolean)
400418
.join(" ");
401419
}
402420

403-
const ma5Points = getLinePoints(ma5_visible, candleSpacing, getY);
404-
const ma20Points = getLinePoints(ma20_visible, candleSpacing, getY);
405-
const ma60Points = getLinePoints(ma60_visible, candleSpacing, getY);
406-
const ma120Points = getLinePoints(ma120_visible, candleSpacing, getY);
421+
// 캔들이 정상적인 날인지 확인하는 유틸 함수
422+
function isDotOnlyCandle(candle: Candle) {
423+
return (
424+
candle.open === candle.close &&
425+
candle.high === candle.close &&
426+
candle.low === candle.close &&
427+
candle.volume === 0
428+
);
429+
}
430+
431+
// const ma5Points = getLinePoints(ma5_visible, candleSpacing, getY);
432+
// const ma20Points = getLinePoints(ma20_visible, candleSpacing, getY);
433+
// const ma60Points = getLinePoints(ma60_visible, candleSpacing, getY);
434+
// const ma120Points = getLinePoints(ma120_visible, candleSpacing, getY);
435+
// const bb_upper_points = getLinePoints(
436+
// bb_visible.map((b) => b?.upper),
437+
// candleSpacing,
438+
// getY
439+
// );
440+
// const bb_middle_points = getLinePoints(
441+
// bb_visible.map((b) => b?.middle),
442+
// candleSpacing,
443+
// getY
444+
// );
445+
// const bb_lower_points = getLinePoints(
446+
// bb_visible.map((b) => b?.lower),
447+
// candleSpacing,
448+
// getY
449+
// );
450+
const ma5Points = getLinePoints(ma5_visible, candleSpacing, getY, slicedData);
451+
const ma20Points = getLinePoints(
452+
ma20_visible,
453+
candleSpacing,
454+
getY,
455+
slicedData
456+
);
457+
const ma60Points = getLinePoints(
458+
ma60_visible,
459+
candleSpacing,
460+
getY,
461+
slicedData
462+
);
463+
const ma120Points = getLinePoints(
464+
ma120_visible,
465+
candleSpacing,
466+
getY,
467+
slicedData
468+
);
469+
407470
const bb_upper_points = getLinePoints(
408471
bb_visible.map((b) => b?.upper),
409472
candleSpacing,
410-
getY
473+
getY,
474+
slicedData
411475
);
412476
const bb_middle_points = getLinePoints(
413477
bb_visible.map((b) => b?.middle),
414478
candleSpacing,
415-
getY
479+
getY,
480+
slicedData
416481
);
417482
const bb_lower_points = getLinePoints(
418483
bb_visible.map((b) => b?.lower),
419484
candleSpacing,
420-
getY
485+
getY,
486+
slicedData
421487
);
422488

423489
const chartRef = useRef<HTMLDivElement>(null);
@@ -481,7 +547,13 @@ export default function InvestCandleChart({
481547
const lowerPoints: string[] = [];
482548

483549
bb_visible.forEach((bb, i) => {
484-
if (bb?.upper && bb?.lower) {
550+
const candle = slicedData[i];
551+
if (
552+
bb?.upper &&
553+
bb?.lower &&
554+
candle && // candle 존재 확인
555+
!isDotOnlyCandle(candle) // dot 전용 데이터 제외
556+
) {
485557
const x = i * candleSpacing;
486558
upperPoints.push(`${x},${getY(bb.upper)}`);
487559
lowerPoints.push(`${x},${getY(bb.lower)}`);
@@ -490,12 +562,11 @@ export default function InvestCandleChart({
490562

491563
if (upperPoints.length === 0) return "";
492564

493-
// 상단선을 그리고, 하단선을 역순으로 연결해서 닫힌 영역 만들기
494565
const pathData = [
495-
`M ${upperPoints[0]}`, // 시작점으로 이동
496-
`L ${upperPoints.slice(1).join(" L ")}`, // 상단선 그리기
497-
`L ${lowerPoints.slice().reverse().join(" L ")}`, // 하단선을 역순으로 그리기
498-
"Z", // path 닫기
566+
`M ${upperPoints[0]}`,
567+
`L ${upperPoints.slice(1).join(" L ")}`,
568+
`L ${lowerPoints.slice().reverse().join(" L ")}`,
569+
"Z",
499570
].join(" ");
500571

501572
return pathData;
@@ -504,7 +575,7 @@ export default function InvestCandleChart({
504575
// --- 렌더 ---
505576
return (
506577
<div
507-
className="flex flex-col"
578+
className="flex flex-col "
508579
style={{
509580
width: "100%",
510581
maxWidth: w,
@@ -921,12 +992,13 @@ export default function InvestCandleChart({
921992
strokeWidth="2"
922993
points={rsi_visible
923994
.map((val, i) =>
924-
typeof val === "number" && isFinite(val)
995+
typeof val === "number" &&
996+
isFinite(val) &&
997+
!isDotOnlyCandle(slicedData[i])
925998
? `${i * candleSpacing},${(1 - val / 100) * RSI_HEIGHT}`
926999
: null
9271000
)
9281001
.filter((v): v is string => v !== null)
929-
9301002
.join(" ")}
9311003
opacity={0.96}
9321004
/>
@@ -995,13 +1067,15 @@ export default function InvestCandleChart({
9951067
</div>
9961068
{/* 일반 candle 값 or dot 전용 candle 값 구분 */}
9971069
{(() => {
1070+
// dotData도 있는 날짜면
9981071
const dot = dotData?.find((d) =>
9991072
dayjs(d.date).isSame(tooltip.data!.date, "day")
10001073
);
1074+
// "오늘" 날짜인지 확인
10011075
const isToday = dayjs(tooltip.data!.date).isSame(dayjs(), "day");
10021076

1003-
// 실시간 시세 + 예측값만 있는 경우 (오늘)
1004-
if (isToday && todayPrice && dot?.close) {
1077+
// 오늘이고 todayPrice가 있을 때 (실시간)
1078+
if (isToday && todayPrice && dot && dot.close) {
10051079
return (
10061080
<>
10071081
<div>
@@ -1012,6 +1086,7 @@ export default function InvestCandleChart({
10121086
</div>
10131087
<div style={{ marginTop: 4 }}>
10141088
<span style={{ color: "#C9DF00", fontWeight: 600 }}>
1089+
10151090
예측값
10161091
</span>
10171092
: {dot.close.toLocaleString()}
@@ -1020,55 +1095,47 @@ export default function InvestCandleChart({
10201095
);
10211096
}
10221097

1023-
// 예측값만 있는 경우
1024-
if (
1025-
dot?.close &&
1026-
(!tooltip.data?.open || tooltip.data.volume === 0)
1027-
) {
1098+
// dotData(예측값)가 있는 경우
1099+
if (dot) {
10281100
return (
10291101
<div>
10301102
<span style={{ color: "#C9DF00", fontWeight: 600 }}>
10311103
예측값
1032-
</span>
1104+
</span>{" "}
10331105
: {dot.close.toLocaleString()}
10341106
</div>
10351107
);
10361108
}
1037-
1038-
// 일반 캔들값 (or 예측값도 있는 경우 같이 표시)
1039-
const rows = [];
1040-
1041-
rows.push(
1042-
<div key="open">시: {tooltip.data.open.toLocaleString()}</div>,
1043-
<div key="high">고: {tooltip.data.high.toLocaleString()}</div>,
1044-
<div key="low">저: {tooltip.data.low.toLocaleString()}</div>,
1045-
<div key="close">종: {tooltip.data.close.toLocaleString()}</div>,
1046-
<div key="vol">
1047-
거래량: {tooltip.data.volume.toLocaleString()}
1048-
</div>,
1049-
<div key="rsi">RSI: {rsi}</div>
1050-
);
1051-
1052-
if (dot?.close) {
1053-
rows.push(
1054-
<div key="pred" style={{ marginTop: 6 }}>
1055-
<span style={{ color: "#396FFB", fontWeight: 600 }}>
1056-
예측값
1057-
</span>
1058-
: {dot.close.toLocaleString()}
1059-
</div>,
1060-
<div key="diff" className="text-[#d23e3e] font-bold">
1061-
오차: {(dot.close - tooltip.data.close).toFixed(2)} (
1062-
{(
1063-
((dot.close - tooltip.data.close) / tooltip.data.close) *
1064-
100
1065-
).toFixed(2)}
1066-
%)
1109+
// 일반 candle 값 표기
1110+
return (
1111+
<>
1112+
<div>시: {tooltip.data.open.toLocaleString()}</div>
1113+
<div>고: {tooltip.data.high.toLocaleString()}</div>
1114+
<div>저: {tooltip.data.low.toLocaleString()}</div>
1115+
<div>종: {tooltip.data.close.toLocaleString()}</div>
1116+
<div>거래량: {tooltip.data.volume.toLocaleString()}</div>
1117+
<div>
1118+
{/* RSI:{" "}
1119+
{typeof rsi_visible[tooltip.idx] === "number"
1120+
? rsi_visible[tooltip.idx].toFixed(2)
1121+
: "-"} */}
1122+
RSI: {rsi}
10671123
</div>
1068-
);
1069-
}
1070-
1071-
return rows;
1124+
{/* dot값이 겹치는 경우 오차 등도 표시 */}
1125+
{dot && (dot as any).close !== undefined && (
1126+
<div className="text-[#e75480] font-bold">
1127+
오차: {((dot as any).close - tooltip.data.close).toFixed(2)}{" "}
1128+
(
1129+
{(
1130+
(((dot as any).close - tooltip.data.close) /
1131+
tooltip.data.close) *
1132+
100
1133+
).toFixed(2)}
1134+
%)
1135+
</div>
1136+
)}
1137+
</>
1138+
);
10721139
})()}
10731140
{/* 뉴스 영역 그대로 */}
10741141
{tooltipNews.length > 0 && (

0 commit comments

Comments
 (0)