1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
import React, { useEffect, useState } from 'react';
import { Typography, Spin, Message, Card, Grid, PageHeader } from '@arco-design/web-react';
import '@arco-design/web-react/dist/css/arco.css';
import { VChart } from '@visactor/react-vchart';
const { Row, Col } = Grid;
const ghostBgStyle = {
backgroundImage: 'radial-gradient(var(--color-fill-3) 1px, rgba(0, 0, 0, 0) 1px)',
backgroundSize: '16px 16px',
padding: 20,
};
function App() {
const [fiveMinData, setFiveMinData] = useState([]);
const [dayAheadData, setDayAheadData] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
Promise.all([
fetch('http://127.0.0.1:8000/market/real-time'),
fetch('http://127.0.0.1:8000/market/day-ahead')
])
.then(async ([res1, res2]) => {
if (!res1.ok || !res2.ok) throw new Error('Failed to fetch data');
const json1 = await res1.json();
const json2 = await res2.json();
setFiveMinData(json1);
setDayAheadData(json2);
})
.catch((err) => {
console.error(err);
Message.error('Failed to load market data.');
})
.finally(() => setLoading(false));
}, []);
const getChartSpec = (data, title) => ({
type: 'line',
data: {
values: data.map(d => ({
timestamp: new Date(d.timestamp).toLocaleString(),
lmp: d.lmp,
energy: d.energy,
congestion: d.congestion,
loss: d.loss
})),
transforms: [
{
type: 'fold',
options: {
key: 'name',
value: 'value',
fields: ['lmp', 'energy', 'congestion', 'loss']
}
}
]
},
xField: 'timestamp',
yField: 'value',
seriesField: 'name',
smooth: true,
legend: {
position: 'top'
},
tooltip: {
formatter: (datum) => ({
name: datum.name,
value: datum.value.toFixed(2)
})
},
dataZoom: [
{
orient: 'bottom',
height: 20,
start: title === 'fiveMin' ? 0.9 : 0.05,
end: 1
}
]
});
return (
<>
<div style={ghostBgStyle}>
<PageHeader title={
<Typography.Title heading={3}>PolyEnergy</Typography.Title>
} subTitle={
<Typography.Title type="secondary" heading={4}>Virtual Energy Trading</Typography.Title>
} />
</div>
<div style={{ padding: 20, backgroundColor: 'var(--color-fill-2)' }}>
<Typography.Title heading={4}>Market Data Visualization</Typography.Title>
{loading ? (
<Spin />
) : (
<>
<div
style={{
width: '100%',
backgroundColor: 'var(--color-fill-2)',
}}
>
<Row gutter={24}>
<Col span={12}>
<Card style={{ height: 600}} title='Real-Time Market Data'>
<VChart spec={getChartSpec(fiveMinData, 'fiveMin')} />
</Card>
</Col>
<Col span={12}>
<Card style={{ height: 600}} title='Day-Ahead Market Data'>
<VChart spec={getChartSpec(dayAheadData, 'dayAhead')} />
</Card>
</Col>
</Row>
</div>
</>
)}
</div>
</>
);
}
export default App;
|