stock-bot/apps/dashboard/src/app/pages/market-data/market-data.component.ts

198 lines
5.8 KiB
TypeScript

import { Component, signal, OnInit, OnDestroy, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatCardModule } from '@angular/material/card';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatTableModule } from '@angular/material/table';
import { MatTabsModule } from '@angular/material/tabs';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSnackBarModule, MatSnackBar } from '@angular/material/snack-bar';
import { ApiService } from '../../services/api.service';
import { WebSocketService } from '../../services/websocket.service';
import { interval, Subscription } from 'rxjs';
export interface ExtendedMarketData {
symbol: string;
price: number;
change: number;
changePercent: number;
volume: number;
marketCap: string;
high52Week: number;
low52Week: number;
}
@Component({
selector: 'app-market-data',
standalone: true,
imports: [
CommonModule,
MatCardModule,
MatButtonModule,
MatIconModule,
MatTableModule,
MatTabsModule,
MatProgressSpinnerModule,
MatSnackBarModule
],
templateUrl: './market-data.component.html',
styleUrl: './market-data.component.css'
})
export class MarketDataComponent implements OnInit, OnDestroy {
private apiService = inject(ApiService);
private webSocketService = inject(WebSocketService);
private snackBar = inject(MatSnackBar);
private subscriptions: Subscription[] = [];
protected marketData = signal<ExtendedMarketData[]>([]);
protected currentTime = signal<string>(new Date().toLocaleTimeString());
protected isLoading = signal<boolean>(true);
protected error = signal<string | null>(null);
protected displayedColumns: string[] = ['symbol', 'price', 'change', 'changePercent', 'volume', 'marketCap'];
ngOnInit() {
// Update time every second
const timeSubscription = interval(1000).subscribe(() => {
this.currentTime.set(new Date().toLocaleTimeString());
});
this.subscriptions.push(timeSubscription);
// Load initial market data
this.loadMarketData();
// Subscribe to real-time market data updates
const wsSubscription = this.webSocketService.getMarketDataUpdates().subscribe({
next: (update) => {
this.updateMarketData(update);
},
error: (err) => {
console.error('WebSocket market data error:', err);
}
});
this.subscriptions.push(wsSubscription);
// Fallback: Refresh market data every 30 seconds if WebSocket fails
const dataSubscription = interval(30000).subscribe(() => {
if (!this.webSocketService.isConnected()) {
this.loadMarketData();
}
});
this.subscriptions.push(dataSubscription);
}
ngOnDestroy() {
this.subscriptions.forEach(sub => sub.unsubscribe());
}
private loadMarketData() {
this.apiService.getMarketData().subscribe({
next: (response) => {
// Convert MarketData to ExtendedMarketData with mock extended properties
const extendedData: ExtendedMarketData[] = response.data.map(item => ({
...item,
marketCap: this.getMockMarketCap(item.symbol),
high52Week: item.price * 1.3, // Mock 52-week high (30% above current)
low52Week: item.price * 0.7 // Mock 52-week low (30% below current)
}));
this.marketData.set(extendedData);
this.isLoading.set(false);
this.error.set(null);
},
error: (err) => {
console.error('Failed to load market data:', err);
this.error.set('Failed to load market data');
this.isLoading.set(false);
this.snackBar.open('Failed to load market data', 'Dismiss', { duration: 5000 });
// Use mock data as fallback
this.marketData.set(this.getMockData());
}
});
}
private getMockMarketCap(symbol: string): string {
const marketCaps: { [key: string]: string } = {
'AAPL': '2.98T',
'GOOGL': '1.78T',
'MSFT': '3.08T',
'TSLA': '789.2B',
'AMZN': '1.59T'
};
return marketCaps[symbol] || '1.00T';
}
private getMockData(): ExtendedMarketData[] {
return [
{
symbol: 'AAPL',
price: 192.53,
change: 2.41,
changePercent: 1.27,
volume: 45230000,
marketCap: '2.98T',
high52Week: 199.62,
low52Week: 164.08
},
{
symbol: 'GOOGL',
price: 2847.56,
change: -12.34,
changePercent: -0.43,
volume: 12450000,
marketCap: '1.78T',
high52Week: 3030.93,
low52Week: 2193.62
},
{
symbol: 'MSFT',
price: 415.26,
change: 8.73,
changePercent: 2.15,
volume: 23180000,
marketCap: '3.08T',
high52Week: 468.35,
low52Week: 309.45
},
{
symbol: 'TSLA',
price: 248.50,
change: -5.21,
changePercent: -2.05,
volume: 89760000,
marketCap: '789.2B',
high52Week: 299.29,
low52Week: 152.37
},
{
symbol: 'AMZN',
price: 152.74,
change: 3.18,
changePercent: 2.12,
volume: 34520000,
marketCap: '1.59T',
high52Week: 170.17,
low52Week: 118.35
}
];
}
refreshData() {
this.isLoading.set(true);
this.loadMarketData();
}
private updateMarketData(update: any) {
const currentData = this.marketData();
const updatedData = currentData.map(item => {
if (item.symbol === update.symbol) {
return {
...item,
price: update.price,
change: update.change,
changePercent: update.changePercent,
volume: update.volume
};
}
return item;
});
this.marketData.set(updatedData);
}
}