diff --git a/web_app/backend/main.py b/web_app/backend/main.py
index 08818a12..8029ea3d 100644
--- a/web_app/backend/main.py
+++ b/web_app/backend/main.py
@@ -142,18 +142,34 @@ async def get_companies():
for company_dir in os.listdir(results_dir):
company_path = os.path.join(results_dir, company_dir)
if os.path.isdir(company_path):
- # Get latest analysis date
+ # Check both regular logs and transformed logs
logs_dir = os.path.join(company_path, "TradingAgentsStrategy_logs")
+ transformed_logs_dir = os.path.join(company_path, "TradingAgentsStrategy_transformed_logs")
+
+ total_analyses = 0
+ latest_date = None
+
+ # Count regular analyses
if os.path.exists(logs_dir):
json_files = glob.glob(os.path.join(logs_dir, "*.json"))
+ total_analyses += len(json_files)
if json_files:
latest_file = max(json_files, key=os.path.getctime)
latest_date = os.path.basename(latest_file).replace("full_states_log_", "").replace(".json", "")
- companies.append({
- "symbol": company_dir,
- "latest_analysis": latest_date,
- "total_analyses": len(json_files)
- })
+
+ # Count transformed analyses
+ transformed_count = 0
+ if os.path.exists(transformed_logs_dir):
+ transformed_files = glob.glob(os.path.join(transformed_logs_dir, "*_transformed.json"))
+ transformed_count = len(transformed_files)
+
+ if total_analyses > 0 or transformed_count > 0:
+ companies.append({
+ "symbol": company_dir,
+ "latest_analysis": latest_date,
+ "total_analyses": total_analyses,
+ "transformed_analyses": transformed_count
+ })
return {"companies": companies}
@@ -195,6 +211,47 @@ async def get_company_results(symbol: str):
return {"symbol": symbol.upper(), "results": results}
+@app.get("/transformed-results/{symbol}")
+async def get_transformed_company_results(symbol: str):
+ """Get all transformed analysis results for a specific company"""
+ results_dir = f"/home/brabus61/Desktop/Github Repos/TradingAgents/scripts/eval_results/{symbol.upper()}/TradingAgentsStrategy_transformed_logs"
+
+ if not os.path.exists(results_dir):
+ raise HTTPException(status_code=404, detail=f"No transformed results found for {symbol}")
+
+ results = []
+ json_files = glob.glob(os.path.join(results_dir, "*_transformed.json"))
+
+ for file_path in sorted(json_files, key=os.path.getctime, reverse=True):
+ filename = os.path.basename(file_path)
+ # Extract date from filename like "full_states_log_2025-07-26_transformed.json"
+ analysis_date = filename.replace("full_states_log_", "").replace("_transformed.json", "")
+
+ try:
+ with open(file_path, 'r') as f:
+ data = json.load(f)
+
+ results.append({
+ "date": analysis_date,
+ "filename": filename,
+ "file_size": os.path.getsize(file_path),
+ "modified_at": datetime.fromtimestamp(os.path.getmtime(file_path)).isoformat(),
+ "preview": {
+ "company_ticker": data.get("metadata", {}).get("company_ticker", "N/A"),
+ "final_recommendation": data.get("metadata", {}).get("final_recommendation", "N/A"),
+ "confidence_level": data.get("metadata", {}).get("confidence_level", "N/A"),
+ "current_price": data.get("financial_data", {}).get("current_price", 0)
+ }
+ })
+ except Exception as e:
+ results.append({
+ "date": analysis_date,
+ "filename": filename,
+ "error": f"Could not read file: {str(e)}"
+ })
+
+ return {"symbol": symbol.upper(), "results": results}
+
@app.get("/results/{symbol}/{date}")
async def get_specific_result(symbol: str, date: str):
"""Get specific analysis result"""
@@ -219,6 +276,31 @@ async def get_specific_result(symbol: str, date: str):
except Exception as e:
raise HTTPException(status_code=500, detail=f"Error reading result: {str(e)}")
+@app.get("/transformed-results/{symbol}/{date}")
+async def get_specific_transformed_result(symbol: str, date: str):
+ """Get specific transformed analysis result"""
+ file_path = f"/home/brabus61/Desktop/Github Repos/TradingAgents/scripts/eval_results/{symbol.upper()}/TradingAgentsStrategy_transformed_logs/full_states_log_{date}_transformed.json"
+
+ if not os.path.exists(file_path):
+ raise HTTPException(status_code=404, detail=f"Transformed result not found for {symbol} on {date}")
+
+ try:
+ with open(file_path, 'r') as f:
+ data = json.load(f)
+
+ return {
+ "symbol": symbol.upper(),
+ "date": date,
+ "data": data,
+ "file_info": {
+ "filename": os.path.basename(file_path),
+ "file_size": os.path.getsize(file_path),
+ "modified_at": datetime.fromtimestamp(os.path.getmtime(file_path)).isoformat()
+ }
+ }
+ except Exception as e:
+ raise HTTPException(status_code=500, detail=f"Error reading file: {str(e)}")
+
@app.get("/config")
async def get_default_config():
"""Get the default configuration"""
diff --git a/web_app/frontend/src/App.js b/web_app/frontend/src/App.js
index f2b2ee75..d5e527ff 100644
--- a/web_app/frontend/src/App.js
+++ b/web_app/frontend/src/App.js
@@ -1,4 +1,4 @@
-import React, { useState, useEffect } from 'react';
+import React, { useState, useEffect, useMemo } from 'react';
import axios from 'axios';
import AnalysisDataAdapter from './components/AnalysisDataAdapter.tsx';
import TransformedDataAdapter from './components/TransformedDataAdapter.tsx';
@@ -26,10 +26,12 @@ function App() {
const [selectedTransformedData, setSelectedTransformedData] = useState(null);
const [isLoadingTransformedData, setIsLoadingTransformedData] = useState(false);
const [transformedDataError, setTransformedDataError] = useState(null);
+ const [selectedTransformedCompany, setSelectedTransformedCompany] = useState(null);
+ const [transformedCompanyFiles, setTransformedCompanyFiles] = useState([]);
const [activeDetailTab, setActiveDetailTab] = useState(null);
// Fields to display as pretty cards in the Details modal
- const detailFields = [
+ const detailFields = useMemo(() => ([
'market_report',
'sentiment_report',
'news_report',
@@ -49,7 +51,7 @@ function App() {
'risk_debate_state.judge_decision',
'company_of_interest',
'trade_date',
- ];
+ ]), []);
const fieldLabelMap = {
market_report: 'Market Report',
@@ -169,7 +171,7 @@ function App() {
} else if (!activeDetailTab || !available.includes(activeDetailTab)) {
setActiveDetailTab(available[0]);
}
- }, [resultDetail, selectedResult]);
+ }, [resultDetail, selectedResult, detailFields, activeDetailTab]);
const checkBackendStatus = async () => {
try {
@@ -250,7 +252,8 @@ function App() {
setTransformedDataError(null);
try {
- const transformedData = await transformedDataService.loadTransformedData(file.filename);
+ // Load by company and date to avoid relying on cached index
+ const transformedData = await transformedDataService.loadByCompanyAndDate(selectedTransformedCompany, file.date);
setSelectedTransformedData(transformedData);
setShowWidgetsView(true);
} catch (error) {
@@ -279,9 +282,23 @@ function App() {
setCompanyResults([]);
};
- const handleViewTransformedData = () => {
+ const handleViewTransformedData = async () => {
setShowTransformedDataModal(true);
setTransformedDataError(null);
+ setSelectedTransformedCompany(null);
+ setTransformedCompanyFiles([]);
+ try {
+ transformedDataService.clearCache();
+ const [files, summary] = await Promise.all([
+ transformedDataService.getAvailableFiles(),
+ transformedDataService.getDataSummary(),
+ ]);
+ setTransformedDataFiles(files);
+ setTransformedDataSummary(summary);
+ } catch (e) {
+ console.error('Failed to load transformed data on open:', e);
+ setTransformedDataError('Failed to load transformed data');
+ }
};
const runAnalysis = async () => {
@@ -312,6 +329,17 @@ function App() {
setTransformedDataError(null);
};
+ const fetchTransformedCompanyFiles = async (symbol) => {
+ try {
+ const response = await axios.get(`/transformed-results/${symbol}`);
+ setTransformedCompanyFiles(response.data.results || []);
+ setSelectedTransformedCompany(symbol);
+ } catch (error) {
+ console.error('Error fetching transformed company files:', error);
+ setTransformedDataError('Error loading transformed company files');
+ }
+ };
+
return (
{/* Header */}
@@ -424,7 +452,7 @@ function App() {
📈
-
View Results
+
View Agent Outputs
Browse analysis results
@@ -437,7 +465,7 @@ function App() {
🔄
-
Transformed Data
+
Visualize Output Data
View enhanced analyses
@@ -451,16 +479,51 @@ function App() {
-
Transformed Analysis Data
-
+
+ {selectedTransformedCompany ? `${selectedTransformedCompany} Transformed Analyses` : 'Transformed Analysis Data'}
+
+
+ {selectedTransformedCompany && (
+
+ )}
+
+
+
{transformedDataError && (
@@ -501,31 +564,57 @@ function App() {
)}
- {transformedDataFiles.length === 0 ? (
-
-
No transformed data files found
-
- Run the data transformation agent to generate transformed analyses
-
-
- ) : (
-
- {transformedDataFiles.map((file, index) => (
-
-
-
{file.displayName}
-
{file.filename}
-
-
+ ))}
+
+ ) : (
+
+
No companies with transformed data found
+
Run the data transformation agent to generate transformed analyses
+
+ )
+ ) : (
+ // File list for selected transformed company
+ transformedCompanyFiles.length > 0 ? (
+
+ {transformedCompanyFiles.map((file, index) => (
+
+
+
{selectedTransformedCompany} - {file.date}
+
{file.filename}
+
+
+
+ ))}
+
+ ) : (
+
+
No transformed files found for {selectedTransformedCompany}
+
+ )
)}
diff --git a/web_app/frontend/src/components/Layout.tsx b/web_app/frontend/src/components/Layout.tsx
index 796d83b0..a453652d 100644
--- a/web_app/frontend/src/components/Layout.tsx
+++ b/web_app/frontend/src/components/Layout.tsx
@@ -8,7 +8,7 @@ const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const navigation = [
{ name: 'Dashboard', href: '/', icon: Home },
{ name: 'Run Analysis', href: '/run-analysis', icon: Play },
- { name: 'View Results', href: '/results', icon: Database },
+ { name: 'View Agent Outputs', href: '/results', icon: Database },
];
return (
diff --git a/web_app/frontend/src/pages/Dashboard.tsx b/web_app/frontend/src/pages/Dashboard.tsx
index e5e4f36e..1f8678e5 100644
--- a/web_app/frontend/src/pages/Dashboard.tsx
+++ b/web_app/frontend/src/pages/Dashboard.tsx
@@ -53,7 +53,7 @@ const Dashboard: React.FC = () => {
-
View Results
+
View Agent Outputs
Browse historical analysis results
diff --git a/web_app/frontend/src/services/transformedDataService.ts b/web_app/frontend/src/services/transformedDataService.ts
index 619ea38d..c3f2f047 100644
--- a/web_app/frontend/src/services/transformedDataService.ts
+++ b/web_app/frontend/src/services/transformedDataService.ts
@@ -1,126 +1,193 @@
-import { TransformedAnalysisData } from '../components/TransformedDataAdapter.tsx';
+import axios from 'axios';
-export interface TransformedDataFile {
+const API_BASE_URL = 'http://localhost:8000';
+
+export interface TransformedAnalysis {
filename: string;
- company: string;
date: string;
+ modified_at: string;
+ file_size: number;
+ preview?: {
+ final_recommendation: string;
+ confidence_level: string;
+ current_price: number;
+ };
+ error?: string;
+ symbol: string;
displayName: string;
}
-class TransformedDataService {
- private baseUrl = '/transformed_data';
- private cachedFiles: TransformedDataFile[] | null = null;
- private cachedData: Map
= new Map();
+export interface TransformedData {
+ metadata: {
+ company_ticker: string;
+ analysis_date: string;
+ final_recommendation: string;
+ confidence_level: string;
+ current_price: number;
+ target_price: number;
+ risk_level: string;
+ time_horizon: string;
+ };
+ financial_data: {
+ current_price: number;
+ target_price: number;
+ price_change_1d: number;
+ price_change_1w: number;
+ price_change_1m: number;
+ market_cap: number;
+ volume: number;
+ pe_ratio: number;
+ revenue: number;
+ profit_margin: number;
+ debt_to_equity: number;
+ roe: number;
+ dividend_yield: number;
+ };
+ technical_indicators: {
+ rsi: number;
+ macd: number;
+ moving_avg_20: number;
+ moving_avg_50: number;
+ bollinger_upper: number;
+ bollinger_lower: number;
+ support_level: number;
+ resistance_level: number;
+ };
+ investment_strategy: {
+ position_size: number;
+ entry_price: number;
+ stop_loss: number;
+ take_profit: number;
+ holding_period: string;
+ risk_reward_ratio: number;
+ };
+ debate_summary: {
+ bull_case: {
+ key_points: string[];
+ strength_score: number;
+ };
+ bear_case: {
+ key_points: string[];
+ strength_score: number;
+ };
+ consensus: string;
+ };
+ text_content: {
+ executive_summary: string;
+ key_takeaways: string[];
+ detailed_analysis: string;
+ risk_factors: string[];
+ catalysts: string[];
+ };
+ widget_config: {
+ charts_enabled: string[];
+ priority_widgets: string[];
+ display_preferences: Record;
+ };
+}
- /**
- * Get list of available transformed data files
- */
- async getAvailableFiles(): Promise {
+class TransformedDataService {
+ private baseUrl = API_BASE_URL;
+ private cachedFiles: TransformedAnalysis[] | null = null;
+ private cachedData: Map = new Map();
+
+ async getAvailableFiles(): Promise {
if (this.cachedFiles) {
return this.cachedFiles;
}
try {
- // In a real implementation, you might have an API endpoint that lists files
- // For now, we'll try to load a manifest file or use a predefined list
- const response = await fetch(`${this.baseUrl}/manifest.json`);
+ const companiesResponse = await axios.get(`${this.baseUrl}/results/companies`);
+ const companiesData = companiesResponse.data;
+ const companies = companiesData.companies || [];
- if (response.ok) {
- const manifest = await response.json();
- this.cachedFiles = manifest.files || [];
- } else {
- // Fallback: try to load some common files
- this.cachedFiles = await this.discoverFiles();
+ const files: TransformedAnalysis[] = [];
+
+ for (const company of companies) {
+ if (company.transformed_analyses > 0) {
+ try {
+ const resultsResponse = await axios.get(`${this.baseUrl}/transformed-results/${company.symbol}`);
+ const resultsData = resultsResponse.data;
+ const results = resultsData.results || [];
+
+ for (const result of results) {
+ files.push({
+ filename: result.filename,
+ date: result.date,
+ modified_at: result.modified_at,
+ file_size: result.file_size,
+ preview: result.preview,
+ error: result.error,
+ symbol: company.symbol,
+ displayName: `${company.symbol} - ${result.date}`
+ });
+ }
+ } catch (error) {
+ console.warn(`Failed to fetch transformed results for ${company.symbol}:`, error);
+ }
+ }
}
+
+ this.cachedFiles = files;
} catch (error) {
- console.warn('Could not load transformed data manifest, using fallback discovery:', error);
- this.cachedFiles = await this.discoverFiles();
+ console.warn('Could not load transformed data files:', error);
+ this.cachedFiles = [];
}
return this.cachedFiles;
}
- /**
- * Discover available files by trying common patterns
- */
- private async discoverFiles(): Promise {
- const companies = ['AVAH', 'PLTR', 'RDDT'];
- const dates = [
- '2025-07-26', '2025-08-05', '2025-08-06', '2025-08-07'
- ];
-
- const files: TransformedDataFile[] = [];
-
- for (const company of companies) {
- for (const date of dates) {
- const filename = `${company}_full_states_log_${date}_transformed.json`;
-
- try {
- const response = await fetch(`${this.baseUrl}/${filename}`, { method: 'HEAD' });
- if (response.ok) {
- files.push({
- filename,
- company,
- date,
- displayName: `${company} - ${date}`
- });
- }
- } catch (error) {
- // File doesn't exist, skip
- }
- }
- }
-
- return files;
- }
-
- /**
- * Load a specific transformed data file
- */
- async loadTransformedData(filename: string): Promise {
- // Check cache first
+ async loadTransformedData(filename: string): Promise {
if (this.cachedData.has(filename)) {
return this.cachedData.get(filename)!;
}
try {
- const response = await fetch(`${this.baseUrl}/${filename}`);
+ const match = filename.match(/full_states_log_(.+)_transformed\.json/);
+ if (!match) {
+ throw new Error(`Invalid filename format: ${filename}`);
+ }
+ const date = match[1];
- if (!response.ok) {
- throw new Error(`Failed to load ${filename}: ${response.status} ${response.statusText}`);
+ const files = await this.getAvailableFiles();
+ const fileEntry = files.find(f => f.filename === filename);
+ if (!fileEntry) {
+ throw new Error(`File not found in index: ${filename}`);
}
- const data: TransformedAnalysisData = await response.json();
+ const response = await axios.get(`${this.baseUrl}/transformed-results/${fileEntry.symbol}/${date}`);
+ const responseData = response.data;
+ const data: TransformedData = responseData.data;
- // Validate the data structure
this.validateTransformedData(data);
- // Cache the data
this.cachedData.set(filename, data);
return data;
} catch (error) {
- console.error(`Error loading transformed data file ${filename}:`, error);
+ console.error(`Error loading transformed data from ${filename}:`, error);
throw error;
}
}
- /**
- * Load transformed data by company and date
- */
- async loadByCompanyAndDate(company: string, date: string): Promise {
- const filename = `${company}_full_states_log_${date}_transformed.json`;
- return this.loadTransformedData(filename);
+ async loadByCompanyAndDate(company: string, date: string): Promise {
+ const files = await this.getAvailableFiles();
+ const entry = files.find(f => f.symbol === company && f.date === date);
+ if (entry) {
+ return this.loadTransformedData(entry.filename);
+ }
+
+ const response = await axios.get(`${this.baseUrl}/transformed-results/${company}/${date}`);
+ const data: TransformedData = response.data.data;
+ this.validateTransformedData(data);
+ return data;
}
- /**
- * Get the most recent analysis for a company
- */
- async getLatestForCompany(company: string): Promise {
+ async getLatestForCompany(company: string): Promise {
const files = await this.getAvailableFiles();
const companyFiles = files
- .filter(f => f.company === company)
- .sort((a, b) => b.date.localeCompare(a.date)); // Sort by date descending
+ .filter(f => f.symbol === company)
+ .sort((a, b) => b.date.localeCompare(a.date));
if (companyFiles.length === 0) {
return null;
@@ -129,31 +196,22 @@ class TransformedDataService {
return this.loadTransformedData(companyFiles[0].filename);
}
- /**
- * Get all available companies
- */
async getAvailableCompanies(): Promise {
const files = await this.getAvailableFiles();
- const companies = [...new Set(files.map(f => f.company))];
- return companies.sort();
+ const companies = [...new Set(files.map(f => f.symbol))].sort();
+ return companies;
}
- /**
- * Get available dates for a specific company
- */
async getAvailableDatesForCompany(company: string): Promise {
const files = await this.getAvailableFiles();
const dates = files
- .filter(f => f.company === company)
+ .filter(f => f.symbol === company)
.map(f => f.date)
- .sort((a, b) => b.localeCompare(a)); // Sort by date descending
+ .sort((a, b) => b.localeCompare(a));
return dates;
}
- /**
- * Validate that the loaded data conforms to the expected structure
- */
private validateTransformedData(data: any): void {
const requiredSections = [
'metadata',
@@ -162,7 +220,7 @@ class TransformedDataService {
'investment_strategy',
'debate_summary',
'text_content',
- 'widgets_config'
+ 'widget_config'
];
for (const section of requiredSections) {
@@ -171,49 +229,37 @@ class TransformedDataService {
}
}
- // Validate metadata
const metadata = data.metadata;
if (!metadata.company_ticker || !metadata.analysis_date) {
throw new Error('Invalid metadata: missing company_ticker or analysis_date');
}
- // Validate that dates are in correct format
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
if (!dateRegex.test(metadata.analysis_date)) {
throw new Error('Invalid date format in metadata.analysis_date');
}
}
- /**
- * Clear all cached data
- */
clearCache(): void {
this.cachedFiles = null;
this.cachedData.clear();
}
- /**
- * Create a manifest file content for available transformed data
- * This can be used to generate a manifest.json file
- */
- async generateManifest(): Promise<{ files: TransformedDataFile[] }> {
- const files = await this.discoverFiles();
+ async generateManifest(): Promise<{ files: TransformedAnalysis[] }> {
+ const files = await this.getAvailableFiles();
return { files };
}
- /**
- * Search for analyses by various criteria
- */
async searchAnalyses(criteria: {
company?: string;
dateFrom?: string;
dateTo?: string;
recommendation?: 'BUY' | 'SELL' | 'HOLD';
- }): Promise {
+ }): Promise {
const files = await this.getAvailableFiles();
return files.filter(file => {
- if (criteria.company && file.company !== criteria.company) {
+ if (criteria.company && file.symbol !== criteria.company) {
return false;
}
@@ -225,16 +271,10 @@ class TransformedDataService {
return false;
}
- // For recommendation filtering, we'd need to load the actual data
- // This is left as a future enhancement
-
return true;
});
}
- /**
- * Get summary statistics about available data
- */
async getDataSummary(): Promise<{
totalFiles: number;
companies: string[];
@@ -243,12 +283,13 @@ class TransformedDataService {
}> {
const files = await this.getAvailableFiles();
- const companies = [...new Set(files.map(f => f.company))].sort();
+ const companies = [...new Set(files.map(f => f.symbol))].sort();
const dates = files.map(f => f.date).sort();
const companyCounts: Record = {};
files.forEach(file => {
- companyCounts[file.company] = (companyCounts[file.company] || 0) + 1;
+ const company = file.symbol;
+ companyCounts[company] = (companyCounts[company] || 0) + 1;
});
return {
@@ -263,6 +304,5 @@ class TransformedDataService {
}
}
-// Export a singleton instance
export const transformedDataService = new TransformedDataService();
export default transformedDataService;