Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ export default function ModelMetadata({ metadata }: ModelMetadataProps) {
<Separator />
<div>
<p className="text-sm font-medium text-muted-foreground mb-2">Training Data Points</p>
{/* <p className="text-lg font-semibold">{metadata.trainingDataPoints.toLocaleString()}</p> */}
<p className="text-lg font-semibold">{metadata.trainingDataPoints.toLocaleString()}</p>
</div>
<div>
<p className="text-sm font-medium text-muted-foreground mb-2">Features Used</p>
{/* <p className="text-sm font-medium text-muted-foreground mb-2">Features Used</p> */}
{/* <div className="flex flex-wrap gap-2">
{metadata.features.map((feature, index) => (
<div key={index} className="rounded-full bg-muted px-3 py-1 text-xs">
Expand All @@ -68,7 +68,7 @@ export default function ModelMetadata({ metadata }: ModelMetadataProps) {
<div>
<div className="flex items-center justify-between mb-2">
<p className="text-sm font-medium">Mean Absolute Error (MAE)</p>
<p className="text-sm font-medium">${metadata.maeScore.toFixed(2)}</p>
<p className="text-sm font-medium">{metadata.maeScore.toFixed(5)}</p>
</div>
<Progress value={100 - metadata.maeScore * 10} className="h-2" />
<p className="text-xs text-muted-foreground mt-1">Average error in price predictions</p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import StockDataTable from "./stock-data-table"
import ModelMetadata from "./model-metadata"
//import {getStockData, getPredictionData, getModelMetadata} from "@/lib/data"
import {exportToCSV} from "@/lib/export"
import {BACKEND_BASE_URL} from "@/lib/const"
import {PredictionData, StockData, ModelMetadataType} from "@/lib/types/stock_prediction";
// import { number } from "zod"

Expand All @@ -32,33 +33,46 @@ const formatDate = (date: Date) => {
return date.toLocaleDateString("en-CA"); // "YYYY-MM-DD" format
};

const BASE_URL = process.env.NEXT_PUBLIC_BACKEND_BASE_URL || "http://localhost:8000";
const BASE_URL = BACKEND_BASE_URL

export default function StockDashboard() {
const [selectedCompany, setSelectedCompany] = useState<string>("")
const [dateRange, setDateRange] = useState<DateRange>({
from: new Date(new Date().setDate(new Date().getDate() - 30)),
to: new Date(),
from: new Date(new Date().setDate(new Date().getDate() - 31)),
to: new Date(new Date().setDate(new Date().getDate() - 1)),
})
const [stockData, setStockData] = useState<StockData | null>(null)
const [predictionData, setPredictionData] = useState<PredictionData | null>(null)
const [modelMetadata, setModelMetadata] = useState<ModelMetadataType | null>(null)
const [activeTab, setActiveTab] = useState("overview")
const [sevchange, setSevchange] = useState<number | null>(null)
const [isLoading, setIsLoading] = useState(false);
const [isPredicting,setPredicting] = useState(false);
const [error,setError] = useState<string|null>(null);
const [dataerror,setDataerror] = useState<string|null>(null);


const [companies, setCompanies] = useState<companyType[]>([]);

useEffect(() => {
const fetchCompanies = async () => {
try {
setIsLoading(true);
console.log(`${BASE_URL}/get-active-symbols`)
const response = await fetch(`${BASE_URL}/get-active-symbols`);
const data = await response.json();
setCompanies(data.symbols);
setSelectedCompany(data.symbols[0].value);
} catch (error) {
console.error("Error fetching company symbols:", error);
if(response.ok){
const data = await response.json();
setCompanies(data.symbols);
setSelectedCompany(data.symbols[0].value);
// if(companies.length == 0){
// setError("No active symbols")
// }
}else{
setError("cannot get stock symbols")
}
setIsLoading(false)
} catch {
setError("Error fetching company symbols");
}
};

Expand All @@ -70,7 +84,7 @@ export default function StockDashboard() {

// Fetch stock data based on selected company and date range
const fetchStockData = async () => {
setIsLoading(true); // Show loading popup
setPredicting(true); // Show loading popup
try {
const response = await fetch(`${BASE_URL}/V2/get-predicted-prices`, {
method: "POST",
Expand All @@ -83,20 +97,29 @@ export default function StockDashboard() {
ticker_symbol: selectedCompany,
}),
});
const data = await response.json();
setStockData(data.stockData);
setModelMetadata(data.modelMetadata);
setPredictionData(data.predictionData)
if (data.predictionData && data.stockData && data.stockData.history.length > 0 && data.predictionData.predictions.length > 0) {
const lastPrediction = data.predictionData.predictions[data.predictionData.predictions.length - 1].predicted;
const lastPrice = data.stockData.history[data.stockData.history.length - 1].price;
const percentageChange = ((lastPrediction - lastPrice) / lastPrice) * 100;
setSevchange(percentageChange);
if(response.ok){
const data = await response.json();
setStockData(data.stockData);
setModelMetadata(data.modelMetadata);
setPredictionData(data.predictionData)
if (data.predictionData && data.stockData && data.stockData.history.length > 0 && data.predictionData.predictions.length > 0) {
const lastPrediction = data.predictionData.predictions[data.predictionData.predictions.length - 1].predicted;
const lastPrice = data.stockData.history[data.stockData.history.length - 1].price;
const percentageChange = ((lastPrediction - lastPrice) / lastPrice) * 100;
setSevchange(percentageChange);
}

}
} catch (error) {
console.error("Error fetching stock data:", error);
else{
const result = await response.json();
setDataerror(result.detail || "Resource not found");
return;
}

} catch {
console.error("Error fetching stock data");
} finally {
setIsLoading(false); // Hide loading popup
setPredicting(false); // Hide loading popup
}
};

Expand All @@ -114,20 +137,47 @@ export default function StockDashboard() {
const handleExport = () => {
exportToCSV(predictionData, `${selectedCompany}_predictions`)
}
if(error){
return (
<div className="min-h-screen bg-background text-gray-100">

return (
<div className="container mx-auto py-6 px-4 md:px-6 relative">
{isLoading && (
<div
className="absolute inset-0 z-50 flex items-center justify-center backdrop-blur-sm bg-background/70 rounded-lg">
<div
className="bg-card text-card-foreground rounded-xl shadow-xl p-6 flex flex-col items-center space-y-4">
<div
className="w-10 h-10 border-4 border-primary border-t-transparent rounded-full animate-spin"/>
<p className="text-lg font-medium">Loading...</p>
<div className="flex flex-col items-center justify-center h-[70vh]">
<p className="text-gray-300">{error}</p>
</div>

</div>
)

}
if (isLoading) {
return (
<div className="min-h-screen bg-background text-gray-100">

<div className="flex flex-col items-center justify-center h-[70vh]">
<div
className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-blue-500 mb-4"></div>
<p className="text-gray-300">Loading History...</p>
</div>

</div>
)
}
else if (isPredicting) {
return (
<div className="min-h-screen bg-background text-gray-100">

<div className="flex flex-col items-center justify-center h-[70vh]">
<div
className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-blue-500 mb-4"></div>
<p className="text-gray-300">Predicting stock close prices...</p>
</div>
)}

</div>
)
}
else {
return (
<div className="container mx-auto py-6 px-4 md:px-6 relative">
<div className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between mb-6">
<div>
<h1 className="text-3xl font-bold tracking-tight">Stock Prediction Dashboard</h1>
Expand Down Expand Up @@ -159,8 +209,15 @@ export default function StockDashboard() {
</Button>
</div>
</div>
{dataerror && (<div className="min-h-screen bg-background text-gray-100">

<div className="flex flex-col items-center justify-center h-[70vh]">
<p className="text-gray-300">{dataerror}</p>
</div>

<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-2">
</div>)}

{!dataerror && (<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-2">
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<div className="space-y-1">
Expand Down Expand Up @@ -205,16 +262,16 @@ export default function StockDashboard() {
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">${predictionData?.nextWeek.predicted.toFixed(2)}</div>
{/* <p className="text-xs text-muted-foreground mt-1">
<p className="text-xs text-muted-foreground mt-1">
Confidence: {predictionData?.nextWeek.confidenceLow.toFixed(2)} -{" "}
{predictionData?.nextWeek.confidenceHigh.toFixed(2)}
</p> */}
</p>
</CardContent>
</Card>

</div>
</div>)}

<Tabs value={activeTab} onValueChange={setActiveTab} className="mt-6">
{!dataerror && (<Tabs value={activeTab} onValueChange={setActiveTab} className="mt-6">
<TabsList className="grid w-full grid-cols-3 md:w-auto">
<TabsTrigger value="overview">Overview</TabsTrigger>
<TabsTrigger value="predictions">Predictions</TabsTrigger>
Expand Down Expand Up @@ -275,7 +332,7 @@ export default function StockDashboard() {
<TabsContent value="model" className="space-y-6">
{modelMetadata && <ModelMetadata metadata={modelMetadata}/>}
</TabsContent>
</Tabs>
</Tabs>)}
</div>
)
}
}}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export default function StockDataTable({data}: StockDataTableProps) {
<TableRow>
<TableHead>Date</TableHead>
<TableHead>Predicted Price</TableHead>
<TableHead>Confidence Interval</TableHead>
<TableHead>Change</TableHead>
</TableRow>
</TableHeader>
Expand All @@ -25,9 +26,9 @@ export default function StockDataTable({data}: StockDataTableProps) {
<TableRow key={index}>
<TableCell className="font-medium">{format(new Date(item.date), "MMM dd, yyyy")}</TableCell>
<TableCell>${item.predicted.toFixed(2)}</TableCell>
{/* <TableCell>
<TableCell>
${item.confidenceLow.toFixed(2)} - ${item.confidenceHigh.toFixed(2)}
</TableCell> */}
</TableCell>
<TableCell>
<Badge variant={item.change > 0 ? "success" : "destructive"}>
{item.change > 0 ? "+" : ""}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ interface ChartDataItem {
formattedDate: string
price: number | null
predicted: number | null
// confidenceLow: number | null
// confidenceHigh: number | null
confidenceLow: number | null
confidenceHigh: number | null
}

export default function StockPredictionChart({historicalData, predictionData}: StockPredictionChartProps) {
Expand All @@ -28,17 +28,17 @@ export default function StockPredictionChart({historicalData, predictionData}: S
...item,
formattedDate: format(new Date(item.date), "MMM dd"),
predicted: null,
// confidenceLow: null,
// confidenceHigh: null,
confidenceLow: null,
confidenceHigh: null,
}))

const predictions = predictionData.map((item) => ({
date: item.date,
formattedDate: format(new Date(item.date), "MMM dd"),
price: null, // No actual price for future dates
predicted: item.predicted,
// confidenceLow: item.confidenceLow,
// confidenceHigh: item.confidenceHigh,
confidenceLow: item.confidenceLow,
confidenceHigh: item.confidenceHigh,
}))

// Combine the data sets
Expand Down
6 changes: 3 additions & 3 deletions src/lib/types/stock_prediction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ export interface StockData {
export interface PredictionItem {
date: string;
predicted: number;
// confidenceLow: number;
// confidenceHigh: number;
confidenceLow: number;
confidenceHigh: number;
change: number;
}

Expand All @@ -39,5 +39,5 @@ export interface ModelMetadataType {
maeScore: number;
// features: string[];
lastUpdated: string;
// trainingDataPoints: number;
trainingDataPoints: number;
}