upgrade_2

This commit is contained in:
Flatlogic Bot 2025-07-02 06:28:27 +00:00
parent cb4671c3a0
commit 72986fa192
6 changed files with 124 additions and 2 deletions

File diff suppressed because one or more lines are too long

View File

@ -19,6 +19,8 @@
"express": "4.18.2",
"formidable": "1.2.2",
"helmet": "4.1.1",
"yahoo-finance2": "^2.0.0",
"node-cron": "^3.10.0",
"json2csv": "^5.0.7",
"jsonwebtoken": "8.5.1",
"lodash": "4.17.21",

View File

@ -12,6 +12,29 @@ const { parse } = require('json2csv');
const { checkCrudPermissions } = require('../middlewares/check-permissions');
// Market data endpoints
const marketDataService = require('../services/marketDataService');
// Get historical price data for different ranges
router.get('/:id/price-history', wrapAsync(async (req, res) => {
const stock = await StocksDBApi.findBy({ id: req.params.id });
if (!stock) {
return res.status(404).send({ message: 'Stock not found' });
}
const range = req.query.range || 'monthly';
const data = await marketDataService.getHistoricalPrices(stock.ticker, range);
res.status(200).send(data);
}));
// Get current market price
router.get('/:id/price-current', wrapAsync(async (req, res) => {
const stock = await StocksDBApi.findBy({ id: req.params.id });
if (!stock) {
return res.status(404).send({ message: 'Stock not found' });
}
const price = await marketDataService.getCurrentPrice(stock.ticker);
res.status(200).send({ price });
}));
router.use(checkCrudPermissions('stocks'));
/**

View File

@ -0,0 +1,32 @@
const yahooFinance = require('yahoo-finance2').default;
async function getCurrentPrice(ticker) {
const quote = await yahooFinance.quote(ticker);
return quote.regularMarketPrice;
}
async function getHistoricalPrices(ticker, range) {
const now = new Date();
let days;
switch (range) {
case 'daily': days = 1; break;
case 'weekly': days = 7; break;
case 'monthly': days = 30; break;
case '3m': days = 90; break;
case '6m': days = 180; break;
case '1y': days = 365; break;
case '3y': days = 365 * 3; break;
case '5y': days = 365 * 5; break;
default: days = 30;
}
const from = new Date(now.getTime() - days * 24 * 60 * 60 * 1000);
const options = { period1: from, period2: now, interval: '1d' };
const result = await yahooFinance.historical(ticker, options);
// Return array of { date, price }
return result.map(item => ({ date: item.date, price: item.close }));
}
module.exports = {
getCurrentPrice,
getHistoricalPrices,
};

View File

@ -0,0 +1 @@
{}

View File

@ -1,4 +1,4 @@
import React, { ReactElement, useEffect } from 'react';
import React, { ReactElement, useEffect, useState } from 'react';
import Head from 'next/head';
import DatePicker from 'react-datepicker';
import 'react-datepicker/dist/react-datepicker.css';
@ -17,6 +17,21 @@ import CardBox from '../../components/CardBox';
import BaseButton from '../../components/BaseButton';
import BaseDivider from '../../components/BaseDivider';
import { mdiChartTimelineVariant } from '@mdi/js';
import axios from 'axios';
import { Line } from 'react-chartjs-2';
import {
Chart as ChartJS,
LineElement,
PointElement,
CategoryScale,
LinearScale,
Tooltip,
Legend,
} from 'chart.js';
ChartJS.register(LineElement, PointElement, CategoryScale, LinearScale, Tooltip, Legend);
import { SwitchField } from '../../components/SwitchField';
import FormField from '../../components/FormField';
@ -34,6 +49,33 @@ const StocksView = () => {
function removeLastCharacter(str) {
console.log(str, `str`);
return str.slice(0, -1);
// Chart timeframe controls and data state
const ranges = ['daily','weekly','monthly','3m','6m','1y','3y','5y'];
const [selectedRange, setSelectedRange] = useState('monthly');
const [chartData, setChartData] = useState([]);
useEffect(() => {
if (!id) return;
axios.get(`/api/stocks/${id}/price-history?range=${selectedRange}`)
.then(res => setChartData(res.data))
.catch(err => console.error(err));
}, [id, selectedRange]);
// Prepare data for Chart.js
const chartLabels = chartData.map(item => dayjs(item.date).format('YYYY-MM-DD'));
const chartPrices = chartData.map(item => item.price);
const chartJsData = {
labels: chartLabels,
datasets: [{
label: `Price (${selectedRange})`,
data: chartPrices,
borderColor: '#4F46E5',
backgroundColor: 'rgba(79,70,229,0.5)',
fill: false,
}]
};
}
useEffect(() => {
@ -43,6 +85,27 @@ const StocksView = () => {
return (
<>
<Head>
<div className="mb-4">
<div className="flex space-x-2 mb-4">
{ranges.map(range => (
<button
key={range}
onClick={() => setSelectedRange(range)}
className={
selectedRange === range
? 'px-3 py-1 rounded border bg-blue-500 text-white'
: 'px-3 py-1 rounded border bg-white text-blue-500'
}
>
{range.toUpperCase()}
</button>
))}
</div>
<div className="h-64 mb-4">
<Line data={chartJsData} options={{ responsive: true, maintainAspectRatio: false }} />
</div>
</div>
<title>{getPageTitle('View stocks')}</title>
</Head>
<SectionMain>