Spaces:
Running
Running
| import { Component, OnInit, inject } from '@angular/core'; | |
| import { CommonModule } from '@angular/common'; | |
| import { MatCardModule } from '@angular/material/card'; | |
| import { Location } from '@angular/common'; | |
| import { NgApexchartsModule } from 'ng-apexcharts'; | |
| import { ChartService } from './chart.service'; | |
| import { MatPaginatorModule } from '@angular/material/paginator'; | |
| import { PageEvent } from '@angular/material/paginator'; | |
| import { MatSlideToggleModule } from '@angular/material/slide-toggle'; | |
| ({ | |
| selector: 'app-analysispage', | |
| standalone: true, | |
| imports: [CommonModule, MatSlideToggleModule, MatCardModule, MatPaginatorModule, NgApexchartsModule], | |
| templateUrl: './analysispage.html', | |
| styleUrls: ['./analysispage.scss'] | |
| }) | |
| export class Analysispage implements OnInit { | |
| originalOrder = (): number => 0; | |
| private location = inject(Location); | |
| result: any; | |
| pageIndex = 0; | |
| pageSize = 5; | |
| sort: { | |
| key: 'combined_overall_score' | 'overall_ta_score' | 'overall_fa_score' | 'news_overall_score', | |
| direction: 'asc' | 'desc' | |
| } = { | |
| key: 'combined_overall_score', | |
| direction: 'desc' | |
| }; | |
| closeAreaOptions: any; | |
| candlestickChartOptions: any; | |
| overallChart: any; | |
| strategyChart: any; | |
| strategyChartIndex: number = 0; | |
| predictedChart: any; | |
| highLowChartOptions: any; | |
| selectedIndicator: any = 'RSI'; | |
| selectedStrategy: any = 'RSI 14'; | |
| activeCompany: number = 0; | |
| isCandlestick = true; | |
| constructor(private chartService: ChartService) { } | |
| ngOnInit(): void { | |
| const s = this.location.getState() as { result?: unknown } | null; | |
| this.result = s?.result ?? null; | |
| const validData = this.result.filter((item: any) => !item.error); | |
| const invalidData = this.result.filter((item: any) => item.error); | |
| validData.sort((a: any, b: any) => b.combined_overall_score - a.combined_overall_score); | |
| this.result = [...validData, ...invalidData]; | |
| console.log('Analysis result:', this.result); | |
| this.loadCharts(); | |
| this.loadPredictedCharts(); | |
| this.loadStrategiesChart('RSI 14'); | |
| } | |
| // Map a row to the numeric value for a given sort key | |
| private sortValue(row: any, key: typeof this.sort.key): number { | |
| switch (key) { | |
| case 'combined_overall_score': return Number(row?.combined_overall_score ?? -Infinity); | |
| case 'overall_ta_score': return Number(row?.overall_ta_score ?? -Infinity); | |
| case 'overall_fa_score': return Number(row?.fundamental_analysis?.overall_fa_score ?? -Infinity); | |
| case 'news_overall_score': return Number(row?.news_overall_score ?? -Infinity); | |
| } | |
| } | |
| // Sorted list (used by the paginator slice) | |
| get sortedResults(): any[] { | |
| if (!Array.isArray(this.result)) return []; | |
| const arr = [...this.result]; | |
| arr.sort((a, b) => { | |
| const av = this.sortValue(a, this.sort.key); | |
| const bv = this.sortValue(b, this.sort.key); | |
| return this.sort.direction === 'asc' ? av - bv : bv - av; | |
| }); | |
| return arr; | |
| } | |
| // Page slice over the sorted list | |
| get pagedResults(): any[] { | |
| const start = this.pageIndex * this.pageSize; | |
| return this.sortedResults.slice(start, start + this.pageSize); | |
| } | |
| // Toggle sort on header click | |
| toggleSort(key: typeof this.sort.key) { | |
| if (this.sort.key === key) { | |
| this.sort.direction = this.sort.direction === 'asc' ? 'desc' : 'asc'; | |
| } else { | |
| this.sort.key = key; | |
| this.sort.direction = 'desc'; // default direction on new column | |
| } | |
| this.pageIndex = 0; // reset to first page on sort change | |
| } | |
| // paginator change | |
| onPage(e: PageEvent) { | |
| this.pageIndex = e.pageIndex; | |
| this.pageSize = e.pageSize; | |
| } | |
| showStrategies(selectedIndicator: any) { | |
| this.selectedIndicator = selectedIndicator; | |
| const strategies = this.result[this.activeCompany][selectedIndicator]; | |
| const firstStrategyKey = Object.keys(strategies)[0]; | |
| if (firstStrategyKey) { | |
| this.selectedStrategy = firstStrategyKey; | |
| this.loadStrategiesChart(firstStrategyKey); | |
| } | |
| } | |
| loadStrategiesChart(strategyName: any) { | |
| this.strategyChartIndex = 0; | |
| this.selectedStrategy = strategyName; | |
| this.strategyChart = this.chartService.getChartOptions(strategyName, this.result[this.activeCompany]); | |
| console.log(this.strategyChart); | |
| } | |
| getClass(signal: any) { | |
| if (typeof signal === 'string') { | |
| switch (signal.toLowerCase()) { | |
| case 'buy': | |
| case 'good': | |
| case 'positive': | |
| case 'bullish': | |
| return 'green'; | |
| case 'dbuy': | |
| case 'bad': | |
| case 'negative': | |
| case 'bearish': | |
| return 'red'; | |
| case 'neutral': | |
| case 'none': | |
| return 'yellow'; | |
| default: | |
| return ''; | |
| } | |
| } else { | |
| return 'strategyvalue' | |
| } | |
| } | |
| loadCharts() { | |
| // close price chart | |
| const data = this.result[this.activeCompany]['ohlc_data'] as Array<{ x: string; y: [number, number, number, number] }>; | |
| // Optional: keep sorted and remove duplicate dates | |
| const closeSeries = [...data] | |
| .sort((a, b) => a.x.localeCompare(b.x)) | |
| .filter((d, i, arr) => i === 0 || d.x !== arr[i - 1].x) | |
| .map(d => ({ x: d.x, y: d.y[3] })); // close = index 3 | |
| this.closeAreaOptions = { | |
| chart: { type: 'area', height: 400, width: 1200 }, | |
| series: [ | |
| { | |
| name: 'Close', | |
| data: closeSeries // [{x: '2025-08-01', y: 2350.9}, ...] | |
| } | |
| ], | |
| xaxis: { | |
| type: 'category', | |
| labels: { style: { colors: '#ffffff' } } | |
| }, | |
| yaxis: { | |
| opposite: true, | |
| labels: { style: { colors: '#ffffff' } } | |
| }, | |
| tooltip: { | |
| theme: 'dark' | |
| } | |
| }; | |
| //candlestick chart | |
| this.candlestickChartOptions = { | |
| chart: { type: 'candlestick', height: 400, width: 1200 }, | |
| series: [{ name: this.activeCompany, data }], // x can be a plain string when type='category' | |
| xaxis: { | |
| type: 'category', | |
| labels: { style: { colors: '#ffffff' } } | |
| }, | |
| yaxis: { | |
| opposite: true, | |
| labels: { style: { colors: '#ffffff' } } | |
| }, | |
| tooltip: { theme: 'dark' } | |
| }; | |
| } | |
| loadPredictedCharts() { | |
| //donut chart | |
| this.overallChart = { | |
| series: [ | |
| this.result[this.activeCompany].overall_ta_score, | |
| this.result[this.activeCompany].overall_fa_score, | |
| this.result[this.activeCompany].news_overall_score | |
| ], // Three values for the donut chart | |
| chart: { | |
| type: 'donut', | |
| width: 500 | |
| }, | |
| labels: ['TA', 'FA', 'News'], // Set the labels to show on the chart | |
| plotOptions: { | |
| pie: { | |
| donut: { | |
| size: '60%', // Control the thickness of the donut | |
| labels: { | |
| show: true, | |
| name: { | |
| show: false | |
| }, | |
| value: { | |
| show: true, // Show values inside the donut chart | |
| fontSize: '16px', // Font size for values | |
| color: 'white' // White color for the values | |
| }, | |
| total: { | |
| show: true, | |
| label: 'Total', // Label at the center | |
| formatter: (w: any) => { | |
| // Display the total value (sum of all slices) | |
| return w.globals.seriesTotals.reduce((a: any, b: any) => a + b, 0) + '%'; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }, | |
| fill: { | |
| type: 'solid', | |
| colors: ['#ff5733', '#33ff57', '#ffcc00'] // Colors for TA, FA, and News | |
| }, | |
| tooltip: { | |
| enabled: true, // Enable tooltips for the chart | |
| fillSeriesColor: false, | |
| theme: 'dark', | |
| style: { | |
| fontSize: '14px', | |
| color: 'white' // White text color for tooltips | |
| } | |
| }, | |
| legend: { | |
| show: true, | |
| position: 'bottom', // Position the legend at the bottom | |
| labels: { | |
| useSeriesColors: true, // Use the series colors for legend | |
| colors: ['white'] // White text color for legend | |
| } | |
| } | |
| }; | |
| // Predicted High/Low 15-day chart (single chart with two series) | |
| const highsRaw = this.result[this.activeCompany].ai_predicted_daily_high_15 ?? []; | |
| const lowsRaw = this.result[this.activeCompany].ai_predicted_daily_low_15 ?? []; | |
| const dates15 = this.result[this.activeCompany].ai_predicted_dates_15 ?? []; | |
| const highs = highsRaw.map((v: any) => Number(Number(v).toFixed(2))); | |
| const lows = lowsRaw.map((v: any) => Number(Number(v).toFixed(2))); | |
| if (highs.length && lows.length && dates15.length && highs.length === lows.length && highs.length === dates15.length) { | |
| this.highLowChartOptions = { | |
| chart: { type: 'line', height: 400, width: 1500, toolbar: { show: false } }, | |
| series: [ | |
| { name: 'Predicted High', data: highs, color: '#22C55E' }, // green | |
| { name: 'Predicted Low', data: lows, color: '#EF4444' } // red | |
| ], | |
| stroke: { width: 2, curve: 'smooth' }, | |
| markers: { size: 3 }, | |
| xaxis: { categories: dates15, type: 'category', tickPlacement: 'on', labels: { rotate: -45, rotateAlways: true, hideOverlappingLabels: false, trim: false, minHeight: 70, style: { colors: '#ffffff', fontSize: '12px' } } }, | |
| yaxis: { | |
| opposite: true, | |
| labels: { style: { colors: '#ffffff' } } | |
| }, | |
| tooltip: { | |
| shared: true, | |
| intersect: false, | |
| theme: 'dark', | |
| y: { | |
| formatter: (val: number) => (val != null ? val.toFixed(2) : '') | |
| } | |
| }, | |
| legend: { | |
| position: 'top', | |
| labels: { | |
| colors: '#ffffff', | |
| useSeriesColors: false | |
| } | |
| } | |
| }; | |
| } else { | |
| this.highLowChartOptions = null; | |
| } | |
| } | |
| selectCompany(index: number) { | |
| this.activeCompany = index; | |
| this.loadCharts(); | |
| this.loadPredictedCharts(); | |
| } | |
| onModeChange(checked: boolean) { | |
| this.isCandlestick = checked; | |
| this.loadCharts(); | |
| } | |
| previousChart(): void { | |
| if (this.strategyChartIndex > 0) { | |
| this.strategyChartIndex--; | |
| } | |
| } | |
| nextChart(): void { | |
| if (this.strategyChartIndex < this.strategyChart.length - 1) { | |
| this.strategyChartIndex++; | |
| } | |
| } | |
| } | |