From f99f642a85e2396211d647133435894bd7933ced Mon Sep 17 00:00:00 2001
From: dongree
Date: Tue, 12 Nov 2024 16:40:39 +0900
Subject: [PATCH 1/4] =?UTF-8?q?=E2=9C=A8=20feat:=20=EC=A3=BC=EC=8B=9D=20?=
=?UTF-8?q?=EC=84=A0=EC=B0=A8=ED=8A=B8,=20=EA=B1=B0=EB=9E=98=EB=9F=89=20?=
=?UTF-8?q?=EB=A7=89=EB=8C=80=20=EC=B0=A8=ED=8A=B8=20=EA=B5=AC=ED=98=84=20?=
=?UTF-8?q?#60?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
FE/src/components/StocksDetail/Chart.tsx | 122 ++++-
FE/src/components/StocksDetail/dummy.ts | 611 +++++++++++++++++++++++
2 files changed, 732 insertions(+), 1 deletion(-)
create mode 100644 FE/src/components/StocksDetail/dummy.ts
diff --git a/FE/src/components/StocksDetail/Chart.tsx b/FE/src/components/StocksDetail/Chart.tsx
index be34074b..337169ad 100644
--- a/FE/src/components/StocksDetail/Chart.tsx
+++ b/FE/src/components/StocksDetail/Chart.tsx
@@ -1,3 +1,123 @@
+import { useEffect, useRef } from 'react';
+import { dummy, DummyStock } from './dummy';
+
+type Padding = {
+ top: number;
+ left: number;
+ right: number;
+ bottom: number;
+};
+
export default function Chart() {
- return 차트
;
+ const containerRef = useRef(null);
+ const canvasRef = useRef(null);
+
+ useEffect(() => {
+ const parent = containerRef.current;
+ const canvas = canvasRef.current;
+
+ if (!canvas || !parent) return;
+
+ canvas.width = parent.clientWidth;
+ canvas.height = parent.clientHeight;
+
+ const ctx = canvas.getContext('2d');
+ if (!ctx) return;
+
+ ctx.fillStyle = 'white';
+
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+
+ const padding = {
+ top: 10,
+ right: 10,
+ bottom: 10,
+ left: 10,
+ };
+
+ const chartWidth = canvas.width - padding.left - padding.right;
+ const chartHeight = canvas.height - padding.top - padding.bottom;
+ const volumeBoundary = chartHeight * 0.2; // chartHeight의 20%
+ const mainHeight = chartHeight - volumeBoundary;
+
+ drawLineChart(ctx, dummy, chartWidth, mainHeight, padding);
+
+ drawBarChart(
+ ctx,
+ dummy,
+ chartWidth,
+ chartHeight,
+ padding,
+ 0,
+ chartHeight * 0.8,
+ );
+ }, []);
+
+ return (
+
+
+
+ );
+}
+
+function drawLineChart(
+ ctx: CanvasRenderingContext2D,
+ data: DummyStock[],
+ width: number,
+ height: number,
+ padding: Padding,
+ x: number = 0,
+ y: number = 0,
+) {
+ ctx.beginPath();
+
+ const n = data.length;
+
+ const yMax = Math.round(Math.max(...data.map((d) => d.low)) * 1.006 * 100);
+ const yMin = Math.round(Math.min(...data.map((d) => d.low)) * 0.994 * 100);
+
+ data.forEach((v, i) => {
+ const value = Math.round(v.low * 100);
+ const cx = x + padding.left + (width * i) / (n - 1);
+ const cy =
+ y + padding.top + height - (height * (value - yMin)) / (yMax - yMin);
+
+ if (i === 0) {
+ ctx.moveTo(cx, cy);
+ } else {
+ ctx.lineTo(cx, cy);
+ }
+ });
+
+ ctx.lineWidth = 1;
+ ctx.stroke();
+}
+
+function drawBarChart(
+ ctx: CanvasRenderingContext2D,
+ data: DummyStock[],
+ width: number,
+ height: number,
+ padding: Padding,
+ x: number,
+ y: number,
+) {
+ ctx.beginPath();
+
+ const yMax = Math.round(Math.max(...data.map((d) => d.volume)) * 1.006 * 100);
+ const yMin = Math.round(Math.min(...data.map((d) => d.volume)) * 0.994 * 100);
+
+ const gap = Math.floor((width / dummy.length) * 0.8);
+
+ data.forEach((e, i) => {
+ const value = Math.round(e.volume * 100);
+ const cx = x + padding.left + (width * i) / (dummy.length - 1);
+ const cy = ((height - y) * (value - yMin)) / (yMax - yMin);
+
+ ctx.fillStyle = e.open < e.close ? 'red' : 'blue';
+ ctx.fillRect(cx, height, gap, -cy);
+ });
+
+ ctx.lineWidth = 2;
+ ctx.stroke();
}
diff --git a/FE/src/components/StocksDetail/dummy.ts b/FE/src/components/StocksDetail/dummy.ts
new file mode 100644
index 00000000..ec2288da
--- /dev/null
+++ b/FE/src/components/StocksDetail/dummy.ts
@@ -0,0 +1,611 @@
+export type DummyStock = {
+ date: string;
+ open: number;
+ close: number;
+ high: number;
+ low: number;
+ volume: number;
+};
+
+export const dummy: DummyStock[] = [
+ {
+ date: '2024-11-07',
+ open: 58000,
+ close: 57000,
+ high: 58300,
+ low: 57000,
+ volume: 13451844,
+ },
+ {
+ date: '2024-11-06',
+ open: 56900,
+ close: 57500,
+ high: 58100,
+ low: 56800,
+ volume: 16951844,
+ },
+ {
+ date: '2024-11-05',
+ open: 57600,
+ close: 57300,
+ high: 58000,
+ low: 56300,
+ volume: 21901844,
+ },
+ {
+ date: '2024-11-04',
+ open: 57800,
+ close: 57600,
+ high: 58100,
+ low: 57200,
+ volume: 17291844,
+ },
+ {
+ date: '2024-11-03',
+ open: 58600,
+ close: 58700,
+ high: 59400,
+ low: 58400,
+ volume: 15471844,
+ },
+ {
+ date: '2024-10-31',
+ open: 59000,
+ close: 58300,
+ high: 59600,
+ low: 58100,
+ volume: 18831844,
+ },
+ {
+ date: '2024-10-30',
+ open: 58500,
+ close: 59200,
+ high: 61200,
+ low: 58300,
+ volume: 34901844,
+ },
+ {
+ date: '2024-10-29',
+ open: 59100,
+ close: 59100,
+ high: 59800,
+ low: 58600,
+ volume: 19181844,
+ },
+ {
+ date: '2024-10-28',
+ open: 58000,
+ close: 59600,
+ high: 59600,
+ low: 57300,
+ volume: 27871844,
+ },
+ {
+ date: '2024-10-27',
+ open: 55700,
+ close: 58100,
+ high: 58500,
+ low: 55700,
+ volume: 27571844,
+ },
+ {
+ date: '2024-10-24',
+ open: 56000,
+ close: 55900,
+ high: 56900,
+ low: 55800,
+ volume: 25511844,
+ },
+ {
+ date: '2024-10-23',
+ open: 58200,
+ close: 56600,
+ high: 58500,
+ low: 56600,
+ volume: 30701844,
+ },
+ {
+ date: '2024-10-22',
+ open: 57500,
+ close: 59100,
+ high: 60000,
+ low: 57100,
+ volume: 27111844,
+ },
+ {
+ date: '2024-10-21',
+ open: 58800,
+ close: 57700,
+ high: 58900,
+ low: 57700,
+ volume: 26881844,
+ },
+ {
+ date: '2024-10-20',
+ open: 59000,
+ close: 59000,
+ high: 59600,
+ low: 58500,
+ volume: 18331844,
+ },
+ {
+ date: '2024-10-17',
+ open: 59900,
+ close: 59200,
+ high: 60100,
+ low: 59100,
+ volume: 14171844,
+ },
+ {
+ date: '2024-10-16',
+ open: 59400,
+ close: 59700,
+ high: 60100,
+ low: 59100,
+ volume: 23001844,
+ },
+ {
+ date: '2024-10-15',
+ open: 59400,
+ close: 59500,
+ high: 60000,
+ low: 59200,
+ volume: 23021844,
+ },
+ {
+ date: '2024-10-14',
+ open: 61100,
+ close: 61000,
+ high: 61400,
+ low: 60100,
+ volume: 20651844,
+ },
+ {
+ date: '2024-10-13',
+ open: 59500,
+ close: 60800,
+ high: 61200,
+ low: 59400,
+ volume: 20371844,
+ },
+ {
+ date: '2024-10-10',
+ open: 59100,
+ close: 59300,
+ high: 60100,
+ low: 59000,
+ volume: 28901844,
+ },
+ {
+ date: '2024-10-09',
+ open: 60100,
+ close: 58900,
+ high: 60200,
+ low: 58900,
+ volume: 44601844,
+ },
+ {
+ date: '2024-10-08',
+ open: 60000,
+ close: 60100,
+ high: 60500,
+ low: 59800,
+ volume: 18631844,
+ },
+ {
+ date: '2024-10-07',
+ open: 59800,
+ close: 60000,
+ high: 60300,
+ low: 59500,
+ volume: 17651844,
+ },
+ {
+ date: '2024-10-06',
+ open: 59500,
+ close: 59800,
+ high: 60000,
+ low: 59300,
+ volume: 16671844,
+ },
+ {
+ date: '2024-10-03',
+ open: 59000,
+ close: 59200,
+ high: 59500,
+ low: 58800,
+ volume: 16671844,
+ },
+ {
+ date: '2024-10-02',
+ open: 59500,
+ close: 59000,
+ high: 59800,
+ low: 58800,
+ volume: 17651844,
+ },
+ {
+ date: '2024-10-01',
+ open: 60000,
+ close: 59500,
+ high: 60200,
+ low: 59300,
+ volume: 18631844,
+ },
+ {
+ date: '2024-09-30',
+ open: 60500,
+ close: 60000,
+ high: 60800,
+ low: 59800,
+ volume: 19611844,
+ },
+ {
+ date: '2024-09-27',
+ open: 61000,
+ close: 60500,
+ high: 61200,
+ low: 60300,
+ volume: 20591844,
+ },
+ {
+ date: '2024-09-26',
+ open: 61500,
+ close: 61000,
+ high: 61800,
+ low: 60800,
+ volume: 21571844,
+ },
+ {
+ date: '2024-09-25',
+ open: 62000,
+ close: 61500,
+ high: 62300,
+ low: 61300,
+ volume: 22551844,
+ },
+ {
+ date: '2024-09-24',
+ open: 62500,
+ close: 62000,
+ high: 62800,
+ low: 61800,
+ volume: 23531844,
+ },
+ {
+ date: '2024-09-23',
+ open: 63000,
+ close: 62500,
+ high: 63300,
+ low: 62300,
+ volume: 24511844,
+ },
+ {
+ date: '2024-09-20',
+ open: 63500,
+ close: 63000,
+ high: 63800,
+ low: 62800,
+ volume: 25491844,
+ },
+ {
+ date: '2024-09-19',
+ open: 64000,
+ close: 63500,
+ high: 64300,
+ low: 63300,
+ volume: 26471844,
+ },
+ {
+ date: '2024-09-18',
+ open: 64500,
+ close: 64000,
+ high: 64800,
+ low: 63800,
+ volume: 27451844,
+ },
+ {
+ date: '2024-09-17',
+ open: 65000,
+ close: 64500,
+ high: 65300,
+ low: 64300,
+ volume: 28431844,
+ },
+ {
+ date: '2024-09-16',
+ open: 65500,
+ close: 65000,
+ high: 65800,
+ low: 64800,
+ volume: 29411844,
+ },
+ {
+ date: '2024-09-13',
+ open: 66000,
+ close: 65500,
+ high: 66300,
+ low: 65300,
+ volume: 30391844,
+ },
+ {
+ date: '2024-09-12',
+ open: 66500,
+ close: 66000,
+ high: 66800,
+ low: 65800,
+ volume: 31371844,
+ },
+ {
+ date: '2024-09-11',
+ open: 67000,
+ close: 66500,
+ high: 67300,
+ low: 66300,
+ volume: 32351844,
+ },
+ {
+ date: '2024-09-10',
+ open: 67500,
+ close: 67000,
+ high: 67800,
+ low: 66800,
+ volume: 33331844,
+ },
+ {
+ date: '2024-09-09',
+ open: 68000,
+ close: 67500,
+ high: 68300,
+ low: 67300,
+ volume: 34311844,
+ },
+ {
+ date: '2024-09-06',
+ open: 68500,
+ close: 68000,
+ high: 68800,
+ low: 67800,
+ volume: 35291844,
+ },
+ {
+ date: '2024-09-05',
+ open: 69000,
+ close: 68500,
+ high: 69300,
+ low: 68300,
+ volume: 36271844,
+ },
+ {
+ date: '2024-09-04',
+ open: 69500,
+ close: 69000,
+ high: 69800,
+ low: 68800,
+ volume: 37251844,
+ },
+ {
+ date: '2024-09-03',
+ open: 70000,
+ close: 69500,
+ high: 70300,
+ low: 69300,
+ volume: 38231844,
+ },
+ {
+ date: '2024-09-02',
+ open: 70500,
+ close: 70000,
+ high: 70800,
+ low: 69800,
+ volume: 39211844,
+ },
+ {
+ date: '2024-08-30',
+ open: 71000,
+ close: 70500,
+ high: 71300,
+ low: 70300,
+ volume: 40191844,
+ },
+ {
+ date: '2024-08-29',
+ open: 71500,
+ close: 71000,
+ high: 71800,
+ low: 70800,
+ volume: 40191844,
+ },
+ {
+ date: '2024-08-28',
+ open: 72000,
+ close: 71500,
+ high: 72300,
+ low: 71300,
+ volume: 39171844,
+ },
+ {
+ date: '2024-08-27',
+ open: 72500,
+ close: 72000,
+ high: 72800,
+ low: 71800,
+ volume: 38151844,
+ },
+ {
+ date: '2024-08-26',
+ open: 73000,
+ close: 72500,
+ high: 73300,
+ low: 72300,
+ volume: 37131844,
+ },
+ {
+ date: '2024-08-23',
+ open: 73500,
+ close: 73000,
+ high: 73800,
+ low: 72800,
+ volume: 36111844,
+ },
+ {
+ date: '2024-08-22',
+ open: 74000,
+ close: 73500,
+ high: 74300,
+ low: 73300,
+ volume: 35091844,
+ },
+ {
+ date: '2024-08-21',
+ open: 74500,
+ close: 74000,
+ high: 74800,
+ low: 73800,
+ volume: 34071844,
+ },
+ {
+ date: '2024-08-20',
+ open: 75000,
+ close: 74500,
+ high: 75300,
+ low: 74300,
+ volume: 33051844,
+ },
+ {
+ date: '2024-08-19',
+ open: 75500,
+ close: 75000,
+ high: 75800,
+ low: 74800,
+ volume: 32031844,
+ },
+ {
+ date: '2024-08-16',
+ open: 76000,
+ close: 75500,
+ high: 76300,
+ low: 75300,
+ volume: 31011844,
+ },
+ {
+ date: '2024-08-15',
+ open: 76500,
+ close: 76000,
+ high: 76800,
+ low: 75800,
+ volume: 29991844,
+ },
+ {
+ date: '2024-08-14',
+ open: 77000,
+ close: 76500,
+ high: 77300,
+ low: 76300,
+ volume: 28971844,
+ },
+ {
+ date: '2024-08-13',
+ open: 77500,
+ close: 77000,
+ high: 77800,
+ low: 76800,
+ volume: 27951844,
+ },
+ {
+ date: '2024-08-12',
+ open: 78000,
+ close: 77500,
+ high: 78300,
+ low: 77300,
+ volume: 26931844,
+ },
+ {
+ date: '2024-08-09',
+ open: 78500,
+ close: 78000,
+ high: 78800,
+ low: 77800,
+ volume: 25911844,
+ },
+ {
+ date: '2024-08-08',
+ open: 79000,
+ close: 78500,
+ high: 79300,
+ low: 78300,
+ volume: 24891844,
+ },
+ {
+ date: '2024-08-07',
+ open: 79500,
+ close: 79000,
+ high: 79800,
+ low: 78800,
+ volume: 23871844,
+ },
+ {
+ date: '2024-08-06',
+ open: 80000,
+ close: 79500,
+ high: 80300,
+ low: 79300,
+ volume: 22851844,
+ },
+ {
+ date: '2024-08-05',
+ open: 80500,
+ close: 80000,
+ high: 80800,
+ low: 79800,
+ volume: 21831844,
+ },
+ {
+ date: '2024-08-02',
+ open: 81000,
+ close: 80500,
+ high: 81300,
+ low: 80300,
+ volume: 20811844,
+ },
+ {
+ date: '2024-08-01',
+ open: 81500,
+ close: 81000,
+ high: 81800,
+ low: 80800,
+ volume: 19791844,
+ },
+ {
+ date: '2024-07-31',
+ open: 82000,
+ close: 81500,
+ high: 82300,
+ low: 81300,
+ volume: 18771844,
+ },
+ {
+ date: '2024-07-30',
+ open: 82500,
+ close: 82000,
+ high: 82800,
+ low: 81800,
+ volume: 17751844,
+ },
+ {
+ date: '2024-07-29',
+ open: 83000,
+ close: 82500,
+ high: 83300,
+ low: 82300,
+ volume: 16731844,
+ },
+ {
+ date: '2024-07-26',
+ open: 83500,
+ close: 83000,
+ high: 83800,
+ low: 82800,
+ volume: 15711844,
+ },
+].reverse();
From 0dddf84942c3c46a1abfdd398e59e0b41dcc6df2 Mon Sep 17 00:00:00 2001
From: dongree
Date: Tue, 12 Nov 2024 17:22:52 +0900
Subject: [PATCH 2/4] =?UTF-8?q?=E2=9C=A8=20feat:=20=EC=8B=9C=EA=B0=80,=20?=
=?UTF-8?q?=EC=A2=85=EA=B0=80=EB=A5=BC=20=EB=B0=98=EC=98=81=ED=95=9C=20?=
=?UTF-8?q?=EC=BA=94=EB=93=A4=20=EC=B0=A8=ED=8A=B8=20=EA=B5=AC=ED=98=84=20?=
=?UTF-8?q?#60?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
FE/src/components/StocksDetail/Chart.tsx | 58 ++++++++++++++++++++++--
1 file changed, 55 insertions(+), 3 deletions(-)
diff --git a/FE/src/components/StocksDetail/Chart.tsx b/FE/src/components/StocksDetail/Chart.tsx
index 337169ad..709768df 100644
--- a/FE/src/components/StocksDetail/Chart.tsx
+++ b/FE/src/components/StocksDetail/Chart.tsx
@@ -18,8 +18,15 @@ export default function Chart() {
if (!canvas || !parent) return;
- canvas.width = parent.clientWidth;
- canvas.height = parent.clientHeight;
+ const displayWidth = parent.clientWidth;
+ const displayHeight = parent.clientHeight;
+
+ // 해상도 높이기
+ canvas.width = displayWidth * 4;
+ canvas.height = displayHeight * 4;
+
+ canvas.style.width = `${displayWidth}px`;
+ canvas.style.height = `${displayHeight}px`;
const ctx = canvas.getContext('2d');
if (!ctx) return;
@@ -51,6 +58,8 @@ export default function Chart() {
0,
chartHeight * 0.8,
);
+
+ drawCandleChart(ctx, dummy, chartWidth, mainHeight, padding, 0, 0);
}, []);
return (
@@ -112,7 +121,7 @@ function drawBarChart(
data.forEach((e, i) => {
const value = Math.round(e.volume * 100);
const cx = x + padding.left + (width * i) / (dummy.length - 1);
- const cy = ((height - y) * (value - yMin)) / (yMax - yMin);
+ const cy = padding.top + ((height - y) * (value - yMin)) / (yMax - yMin);
ctx.fillStyle = e.open < e.close ? 'red' : 'blue';
ctx.fillRect(cx, height, gap, -cy);
@@ -121,3 +130,46 @@ function drawBarChart(
ctx.lineWidth = 2;
ctx.stroke();
}
+
+function drawCandleChart(
+ ctx: CanvasRenderingContext2D,
+ data: DummyStock[],
+ width: number,
+ height: number,
+ padding: Padding,
+ x: number,
+ y: number,
+) {
+ ctx.beginPath();
+
+ const yMax = Math.round(
+ Math.max(...data.map((d) => Math.max(d.close, d.open))) * 1.006 * 100,
+ );
+ const yMin = Math.round(
+ Math.min(...data.map((d) => Math.max(d.close, d.open))) * 0.994 * 100,
+ );
+
+ data.forEach((e, i) => {
+ const { open, close } = e;
+ const gap = Math.floor((width / dummy.length) * 0.8);
+ const cx = x + padding.left + (width * i) / (dummy.length - 1);
+
+ const value1 = Math.round(e.open * 100);
+ const value2 = Math.round(e.close * 100);
+ const cy1 =
+ y + padding.top + height - (height * (value1 - yMin)) / (yMax - yMin);
+ const cy2 =
+ y + padding.top + height - (height * (value2 - yMin)) / (yMax - yMin);
+
+ if (open > close) {
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(cx, cy2, gap, cy1 - cy2);
+ } else {
+ ctx.fillStyle = 'red';
+ ctx.fillRect(cx, cy1, gap, cy2 - cy1);
+ }
+ });
+
+ ctx.lineWidth = 2;
+ ctx.stroke();
+}
From 0e70808a5ba00acfd284e0bed28045756ac60fbe Mon Sep 17 00:00:00 2001
From: dongree
Date: Tue, 12 Nov 2024 17:38:02 +0900
Subject: [PATCH 3/4] =?UTF-8?q?=E2=9C=A8=20feat:=20=EC=BA=94=EB=93=A4?=
=?UTF-8?q?=EC=B0=A8=ED=8A=B8=20=EC=B5=9C=EA=B3=A0=EA=B0=80,=20=EC=B5=9C?=
=?UTF-8?q?=EC=A0=80=EA=B0=80=20=EA=BC=AC=EB=A6=AC=EC=84=A0=20=EB=B0=98?=
=?UTF-8?q?=EC=98=81=20#60?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
FE/src/components/Login/index.tsx | 10 +++---
FE/src/components/StocksDetail/Chart.tsx | 46 ++++++++++++++++--------
2 files changed, 38 insertions(+), 18 deletions(-)
diff --git a/FE/src/components/Login/index.tsx b/FE/src/components/Login/index.tsx
index bc6f626b..eee0869e 100644
--- a/FE/src/components/Login/index.tsx
+++ b/FE/src/components/Login/index.tsx
@@ -46,8 +46,8 @@ export default function Login() {
}[errorCode]
}
-