diff options
author | Navan Chauhan <navanchauhan@gmail.com> | 2025-04-27 21:03:25 -0600 |
---|---|---|
committer | Navan Chauhan <navanchauhan@gmail.com> | 2025-04-27 21:03:25 -0600 |
commit | 623a03cc02b2e87a187aca29db5f3c787713fc74 (patch) | |
tree | 8fa5a48eb998822ff631739d6c0bb86796d46297 | |
parent | cc68bc880407d55837e02052c7ab098079641843 (diff) |
add market option
-rw-r--r-- | client/src/BidsPage.jsx | 5 | ||||
-rw-r--r-- | client/src/MarketDataPage.jsx | 98 | ||||
-rw-r--r-- | client/src/SubmitBidPage.jsx | 36 |
3 files changed, 85 insertions, 54 deletions
diff --git a/client/src/BidsPage.jsx b/client/src/BidsPage.jsx index c6392b5..3a40f41 100644 --- a/client/src/BidsPage.jsx +++ b/client/src/BidsPage.jsx @@ -38,6 +38,11 @@ const columns = [ const color = val >= 0 ? 'green' : 'red'; return <Typography.Text style={{ color }}>{val.toFixed(2)}</Typography.Text>; } + }, + { + title: 'Market', + dataIndex: 'market', + render: (val) => val, } ]; diff --git a/client/src/MarketDataPage.jsx b/client/src/MarketDataPage.jsx index 76d8284..52ca47c 100644 --- a/client/src/MarketDataPage.jsx +++ b/client/src/MarketDataPage.jsx @@ -6,6 +6,10 @@ import { VChart } from '@visactor/react-vchart'; import API_BASE_URL from './config'; import { MarketContext, MARKET_FULL_NAMES } from './App'; +// TODO: +// +// - [ ] Cancel Promise if market changes +// - [ ] Fix Data Zoom being reset const { Row, Col } = Grid; @@ -37,7 +41,9 @@ function TimeAgo({ timestamp, prefix }) { function MarketDataPage() { const { selectedMarket } = useContext(MarketContext); - const [fiveMinData, setFiveMinData] = useState([]); + const [fiveMinZoom, setFiveMinZoom] = useState({ start: 0.9, end: 1 }); + const [dayAheadZoom, setDayAheadZoom] = useState({ start: 0.05, end: 1 }); + const [fiveMinData, setFiveMinData] = useState([]); const [dayAheadData, setDayAheadData] = useState([]); const [loading, setLoading] = useState(true); const [fiveMinLastUpdated, setFiveMinLastUpdated] = useState(null); @@ -95,49 +101,57 @@ function MarketDataPage() { }; }, [selectedMarket]); - 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: [ + const getChartSpec = (data, title, zoom) => ({ + 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: [ { - type: 'fold', - options: { - key: 'name', - value: 'value', - fields: ['LMP', 'Energy', 'Congestion', 'Loss'] + orient: 'bottom', + height: 20, + start: zoom?.start ?? (title === 'fiveMin' ? 0.9 : 0.05), + end: zoom?.end ?? 1, + onChange: (e) => { + if (title === 'fiveMin') { + setFiveMinZoom({ start: e.start, end: e.end }); + } else { + setDayAheadZoom({ start: e.start, end: e.end }); + } } } ] - }, - 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 - } - ] - }); + }); + const computeKPIs = (data) => { if (!data.length) return { @@ -243,7 +257,7 @@ function MarketDataPage() { title='Real-Time Market Data' > <div style={{ padding: 20 }}> - <VChart spec={getChartSpec(fiveMinData, 'fiveMin')} /> + <VChart spec={getChartSpec(fiveMinData, 'fiveMin', fiveMinZoom)} /> </div> </Card> </Col> @@ -259,7 +273,7 @@ function MarketDataPage() { title='Day-Ahead Market Data' > <div style={{ padding: 20 }}> - <VChart spec={getChartSpec(dayAheadData, 'dayAhead')} /> + <VChart spec={getChartSpec(dayAheadData, 'dayAhead', dayAheadZoom)} /> </div> </Card> </Col> diff --git a/client/src/SubmitBidPage.jsx b/client/src/SubmitBidPage.jsx index 65a9eb8..94a0c32 100644 --- a/client/src/SubmitBidPage.jsx +++ b/client/src/SubmitBidPage.jsx @@ -1,37 +1,46 @@ -import React, { useState, useEffect } from 'react'; -import { Form, InputNumber, DatePicker, Button, Message, Typography, Card } from '@arco-design/web-react'; +import React, { useState, useEffect, useContext } from 'react'; +import { Form, InputNumber, DatePicker, Button, Message, Typography, Card, Select } from '@arco-design/web-react'; import '@arco-design/web-react/dist/css/arco.css'; import dayjs from 'dayjs'; import utc from 'dayjs/plugin/utc'; import timezone from 'dayjs/plugin/timezone'; import API_BASE_URL from './config'; +import { MarketContext, MARKET_FULL_NAMES } from './App'; dayjs.extend(utc); dayjs.extend(timezone); +const MARKET_TIMEZONES = { + ISONE: 'America/New_York', + NYISO: 'America/New_York', + MISO: 'America/Chicago' +}; + function SubmitBidPage() { + const { selectedMarket } = useContext(MarketContext); const [form] = Form.useForm(); const [loading, setLoading] = useState(false); const [localNow, setLocalNow] = useState(''); - const [newEnglandNow, setNewEnglandNow] = useState(''); + const [marketNow, setMarketNow] = useState(''); useEffect(() => { const updateTime = () => { const now = dayjs(); const localFormatted = now.format('dddd, MMMM D, h:mm A'); - const newEngland = now.tz('America/New_York'); - const newEnglandFormatted = newEngland.format('dddd, MMMM D, h:mm A'); + const marketTz = MARKET_TIMEZONES[selectedMarket] || 'America/New_York'; + const marketTime = now.tz(marketTz); + const marketFormatted = marketTime.format('dddd, MMMM D, h:mm A'); setLocalNow(localFormatted); - setNewEnglandNow(newEnglandFormatted); + setMarketNow(marketFormatted); }; updateTime(); - const interval = setInterval(updateTime, 1000); // update every second + const interval = setInterval(updateTime, 1000); return () => clearInterval(interval); - }, []); + }, [selectedMarket]); const handleSubmit = async (values) => { setLoading(true); @@ -39,10 +48,14 @@ function SubmitBidPage() { const picked = dayjs(values.timestamp); const adjusted = picked.minute(0).second(0).millisecond(0); + const marketTz = MARKET_TIMEZONES[selectedMarket] || 'America/New_York'; + const timestampInMarketTz = adjusted.tz(marketTz).format(); + const payload = { - timestamp: adjusted.tz('America/New_York').format(), + timestamp: timestampInMarketTz, quantity: values.quantity, price: values.price, + market: selectedMarket, user_id: 1 // Hardcoded user id for now }; @@ -73,12 +86,11 @@ function SubmitBidPage() { <div style={{ padding: 20, backgroundColor: 'var(--color-fill-2)' }}> <Typography.Title heading={4}>Submit New Bid</Typography.Title> <Card style={{ marginTop: 16 }}> - {/* 🕰️ Show Current Time Info */} <Typography.Paragraph> <strong>Current Local Time:</strong> {localNow} </Typography.Paragraph> <Typography.Paragraph> - <strong>Current New England Time:</strong> {newEnglandNow} + <strong>Current {MARKET_FULL_NAMES[selectedMarket]} Time:</strong> {marketNow} </Typography.Paragraph> <Form @@ -105,8 +117,8 @@ function SubmitBidPage() { style={{ width: '100%' }} format="YYYY-MM-DD HH:00" placeholder="Select date and hour" - timezone='America/New_York' disabledMinutes={() => Array.from({ length: 60 }, (_, i) => i !== 0)} + timezone={MARKET_TIMEZONES[selectedMarket]} /> </Form.Item> |