Merge pull request #4 from aguzererler/copilot/review-last-commit
Address review feedback on Global Macro Scanner (PR #2)
This commit is contained in:
commit
08575188c3
|
|
@ -217,3 +217,10 @@ __marimo__/
|
|||
|
||||
# Cache
|
||||
**/data_cache/
|
||||
|
||||
# Scan results and execution plans (generated artifacts)
|
||||
results/
|
||||
plans/
|
||||
|
||||
# Backup files
|
||||
*.backup
|
||||
|
|
|
|||
|
|
@ -6,12 +6,12 @@ Multi-agent LLM trading framework using LangGraph for financial analysis and dec
|
|||
|
||||
## Development Environment
|
||||
|
||||
**Conda Environment**: `trasingagetns`
|
||||
**Conda Environment**: `tradingagents`
|
||||
|
||||
Before starting any development work, activate the conda environment:
|
||||
|
||||
```bash
|
||||
conda activate trasingagetns
|
||||
conda activate tradingagents
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
|
|
|||
59
cli/main.py
59
cli/main.py
|
|
@ -1178,6 +1178,30 @@ def run_analysis():
|
|||
display_complete_report(final_state)
|
||||
|
||||
|
||||
def _is_scanner_error(result: str) -> bool:
|
||||
"""Return True when *result* indicates an error or missing data from a scanner tool."""
|
||||
error_prefixes = (
|
||||
"Error",
|
||||
"No data",
|
||||
"No quotes",
|
||||
"No movers",
|
||||
"No news",
|
||||
"No industry",
|
||||
"Invalid",
|
||||
"Alpha Vantage",
|
||||
)
|
||||
return any(result.startswith(prefix) for prefix in error_prefixes)
|
||||
|
||||
|
||||
def _invoke_and_save(tool, args: dict, save_dir: Path, filename: str, label: str) -> str:
|
||||
"""Invoke a scanner tool, print a preview, and save the result if it is valid."""
|
||||
result = tool.invoke(args)
|
||||
if not _is_scanner_error(result):
|
||||
(save_dir / filename).write_text(result)
|
||||
console.print(result[:500] + "..." if len(result) > 500 else result)
|
||||
return result
|
||||
|
||||
|
||||
def run_scan():
|
||||
console.print(Panel("[bold green]Global Macro Scanner[/bold green]", border_style="green"))
|
||||
default_date = datetime.datetime.now().strftime("%Y-%m-%d")
|
||||
|
|
@ -1190,35 +1214,20 @@ def run_scan():
|
|||
|
||||
# Call scanner tools
|
||||
console.print("[bold]1. Market Movers[/bold]")
|
||||
movers = get_market_movers.invoke({"category": "day_gainers"})
|
||||
if not (movers.startswith("Error") or movers.startswith("No data")):
|
||||
(save_dir / "market_movers.txt").write_text(movers)
|
||||
console.print(movers[:500] + "..." if len(movers) > 500 else movers)
|
||||
|
||||
_invoke_and_save(get_market_movers, {"category": "day_gainers"}, save_dir, "market_movers.txt", "Market Movers")
|
||||
|
||||
console.print("[bold]2. Market Indices[/bold]")
|
||||
indices = get_market_indices.invoke({})
|
||||
if not (indices.startswith("Error") or indices.startswith("No data")):
|
||||
(save_dir / "market_indices.txt").write_text(indices)
|
||||
console.print(indices[:500] + "..." if len(indices) > 500 else indices)
|
||||
|
||||
_invoke_and_save(get_market_indices, {}, save_dir, "market_indices.txt", "Market Indices")
|
||||
|
||||
console.print("[bold]3. Sector Performance[/bold]")
|
||||
sectors = get_sector_performance.invoke({})
|
||||
if not (sectors.startswith("Error") or sectors.startswith("No data")):
|
||||
(save_dir / "sector_performance.txt").write_text(sectors)
|
||||
console.print(sectors[:500] + "..." if len(sectors) > 500 else sectors)
|
||||
|
||||
_invoke_and_save(get_sector_performance, {}, save_dir, "sector_performance.txt", "Sector Performance")
|
||||
|
||||
console.print("[bold]4. Industry Performance (Technology)[/bold]")
|
||||
industry = get_industry_performance.invoke({"sector_key": "technology"})
|
||||
if not (industry.startswith("Error") or industry.startswith("No data")):
|
||||
(save_dir / "industry_performance.txt").write_text(industry)
|
||||
console.print(industry[:500] + "..." if len(industry) > 500 else industry)
|
||||
|
||||
_invoke_and_save(get_industry_performance, {"sector_key": "technology"}, save_dir, "industry_performance.txt", "Industry Performance")
|
||||
|
||||
console.print("[bold]5. Topic News (Market)[/bold]")
|
||||
news = get_topic_news.invoke({"topic": "market", "limit": 10})
|
||||
if not (news.startswith("Error") or news.startswith("No data")):
|
||||
(save_dir / "topic_news.txt").write_text(news)
|
||||
console.print(news[:500] + "..." if len(news) > 500 else news)
|
||||
|
||||
_invoke_and_save(get_topic_news, {"topic": "market", "limit": 10}, save_dir, "topic_news.txt", "Topic News")
|
||||
|
||||
console.print(f"[green]Results saved to {save_dir}[/green]")
|
||||
|
||||
|
||||
|
|
|
|||
1236
cli/main.py.backup
1236
cli/main.py.backup
File diff suppressed because it is too large
Load Diff
|
|
@ -1,82 +0,0 @@
|
|||
# Data Layer Fix and Test Plan for Global Macro Analyzer
|
||||
|
||||
## Current State Assessment
|
||||
|
||||
- ✅ pyproject.toml configured correctly
|
||||
- ✅ Removed stray scanner_tools.py files outside tradingagents/
|
||||
- ✅ yfinance_scanner.py implements all required functions
|
||||
- ✅ alpha_vantage_scanner.py implements fallback get_market_movers_alpha_vantage correctly
|
||||
- ✅ scanner_tools.py wrappers properly use route_to_vendor for all scanner methods
|
||||
- ✅ default_config.py updated with scanner_data vendor configuration
|
||||
- ✅ All scanner tools import successfully without runtime errors
|
||||
|
||||
## Outstanding Issues
|
||||
|
||||
- CLI scan command not yet implemented in cli/main.py
|
||||
- Scanner graph components (MacroScannerGraph) not yet created
|
||||
- No end-to-end testing of the data layer functionality
|
||||
|
||||
## Fix Plan
|
||||
|
||||
### 1. Implement Scanner Graph Components
|
||||
|
||||
Create the following files in tradingagents/graph/:
|
||||
|
||||
- scanner_setup.py: Graph setup logic for scanner components
|
||||
- scanner_conditional_logic.py: Conditional logic for scanner graph flow
|
||||
- scanner_graph.py: Main MacroScannerGraph class
|
||||
|
||||
### 2. Add Scan Command to CLI
|
||||
|
||||
Modify cli/main.py to include:
|
||||
|
||||
- @app.command() def scan(): entry point
|
||||
- Date prompt (default: today)
|
||||
- LLM provider config prompt (reuse existing helpers)
|
||||
- MacroScannerGraph instantiation and scan() method call
|
||||
- Rich panel display for results
|
||||
- Report saving to results/macro_scan/{date}/ directory
|
||||
|
||||
### 3. Create MacroScannerGraph
|
||||
|
||||
Implement the scanner graph that:
|
||||
|
||||
- Runs parallel Phase 1 scanners (geopolitical, market movers, sectors)
|
||||
- Coordinates Phase 2 industry deep dive
|
||||
- Produces Phase 3 macro synthesis output
|
||||
- Uses ScannerState for state management
|
||||
|
||||
### 4. End-to-End Testing
|
||||
|
||||
Execute the scan command and verify:
|
||||
|
||||
- Rich panels display correctly for each report section
|
||||
- Top-10 stock watchlist is generated and displayed
|
||||
- Reports are saved to results/macro_scan/{date}/ directory
|
||||
- No import or runtime errors occur
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
1. [ ] Create scanner graph components (scanner_setup.py, scanner_conditional_logic.py, scanner_graph.py)
|
||||
2. [ ] Add scan command to cli/main.py with proper argument handling
|
||||
3. [ ] Implement MacroScannerGraph with proper node/edge connections
|
||||
4. [ ] Test scan command functionality
|
||||
5. [ ] Verify output formatting and file generation
|
||||
6. [ ] Document test results and any issues found
|
||||
|
||||
## Verification Criteria
|
||||
|
||||
- ✅ All scanner tools can be imported and used
|
||||
- ✅ CLI scan command executes without errors
|
||||
- ✅ Rich panels display market movers, indices, sector performance, and news
|
||||
- ✅ Top-10 stock watchlist is generated and displayed
|
||||
- ✅ Reports saved to results/macro_scan/{date}/ directory
|
||||
- ✅ No runtime exceptions or import errors
|
||||
|
||||
## Contingency
|
||||
|
||||
- If errors occur during scan execution, check:
|
||||
- Vendor routing configuration in default_config.py
|
||||
- Function implementations in yfinance_scanner.py and alpha_vantage_scanner.py
|
||||
- Graph node/edge connections in scanner graph components
|
||||
- Rich panel formatting and output generation logic
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
# Data Layer Fix and Test Plan
|
||||
|
||||
## Goal
|
||||
|
||||
Verify and test the data layer for the Global Macro Analyzer implementation.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Python environment with dependencies installed
|
||||
- yfinance and alpha_vantage configured
|
||||
|
||||
## Steps
|
||||
|
||||
1. Import and test scanner tools individually
|
||||
2. Run CLI scan command
|
||||
3. Validate output
|
||||
4. Document results
|
||||
|
||||
## Testing Scanner Tools
|
||||
|
||||
- Test get_market_movers
|
||||
- Test get_market_indices
|
||||
- Test get_sector_performance
|
||||
- Test get_industry_performance
|
||||
- Test get_topic_news
|
||||
|
||||
## Running CLI Scan
|
||||
|
||||
- Command: python -m tradingagents scan --date 2026-03-14
|
||||
- Expected output: Rich panels with market movers, indices, sector performance, news, and top-10 watchlist
|
||||
|
||||
## Expected Results
|
||||
|
||||
- No import errors
|
||||
- Successful execution without exceptions
|
||||
- Output files generated under results/
|
||||
- Top-10 stock watchlist displayed
|
||||
|
||||
## Contingency
|
||||
|
||||
- If errors occur, check import paths and configuration
|
||||
- Verify default_config.py scanner_data setting is correct
|
||||
- Ensure vendor routing works correctly
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Address any failures
|
||||
- Refine output formatting
|
||||
- Add additional test cases
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
# Industry Performance: Technology
|
||||
# Data retrieved on: 2026-03-15 11:17:42
|
||||
|
||||
| Company | Symbol | Industry | Market Cap | Change % |
|
||||
|---------|--------|----------|------------|----------|
|
||||
| NVIDIA Corporation | N/A | N/A | N/A | N/A |
|
||||
| Apple Inc. | N/A | N/A | N/A | N/A |
|
||||
| Microsoft Corporation | N/A | N/A | N/A | N/A |
|
||||
| Broadcom Inc. | N/A | N/A | N/A | N/A |
|
||||
| Micron Technology, Inc. | N/A | N/A | N/A | N/A |
|
||||
| Oracle Corporation | N/A | N/A | N/A | N/A |
|
||||
| Palantir Technologies Inc. | N/A | N/A | N/A | N/A |
|
||||
| Advanced Micro Devices, Inc. | N/A | N/A | N/A | N/A |
|
||||
| Cisco Systems, Inc. | N/A | N/A | N/A | N/A |
|
||||
| Applied Materials, Inc. | N/A | N/A | N/A | N/A |
|
||||
| Lam Research Corporation | N/A | N/A | N/A | N/A |
|
||||
| International Business Machine | N/A | N/A | N/A | N/A |
|
||||
| Intel Corporation | N/A | N/A | N/A | N/A |
|
||||
| KLA Corporation | N/A | N/A | N/A | N/A |
|
||||
| Salesforce, Inc. | N/A | N/A | N/A | N/A |
|
||||
| Texas Instruments Incorporated | N/A | N/A | N/A | N/A |
|
||||
| Arista Networks, Inc. | N/A | N/A | N/A | N/A |
|
||||
| Amphenol Corporation | N/A | N/A | N/A | N/A |
|
||||
| Shopify Inc. | N/A | N/A | N/A | N/A |
|
||||
| Uber Technologies, Inc. | N/A | N/A | N/A | N/A |
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
# Major Market Indices
|
||||
# Data retrieved on: 2026-03-15 11:17:38
|
||||
|
||||
| Index | Current Price | Change | Change % | 52W High | 52W Low |
|
||||
|-------|---------------|--------|----------|----------|----------|
|
||||
| S&P 500 | 6632.19 | -40.43 | -0.61% | 7002.28 | 4835.04 |
|
||||
| Dow Jones | 46558.47 | -119.38 | -0.26% | 50512.79 | 36611.78 |
|
||||
| NASDAQ | 22105.36 | -206.62 | -0.93% | 24019.99 | 14784.03 |
|
||||
| VIX (Volatility Index) | 27.19 | -0.10 | -0.37% | 60.13 | 13.38 |
|
||||
| Russell 2000 | 2480.05 | -8.94 | -0.36% | 2735.10 | 1732.99 |
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
# Market Movers: Day Gainers
|
||||
# Data retrieved on: 2026-03-15 11:17:38
|
||||
|
||||
| Symbol | Name | Price | Change % | Volume | Market Cap |
|
||||
|--------|------|-------|----------|--------|------------|
|
||||
| NP | Neptune Insurance Holdings Inc | $21.87 | 20.23% | 924,853 | $3,021,417,984 |
|
||||
| VEON | VEON Ltd. | $50.60 | 14.20% | 687,398 | $3,491,177,216 |
|
||||
| KLAR | Klarna Group plc | $15.91 | 8.82% | 8,979,495 | $6,006,150,656 |
|
||||
| KYIV | Kyivstar Group Ltd. | $11.07 | 8.53% | 2,498,383 | $2,555,660,032 |
|
||||
| GLXY | Galaxy Digital Inc. | $22.35 | 8.34% | 7,046,744 | $8,730,140,672 |
|
||||
| BLLN | BillionToOne, Inc. | $69.11 | 7.93% | 230,655 | $3,165,566,720 |
|
||||
| IBRX | ImmunityBio, Inc. | $8.39 | 7.29% | 30,384,030 | $8,625,855,488 |
|
||||
| SNDK | Sandisk Corporation | $661.62 | 6.92% | 18,684,442 | $97,655,758,848 |
|
||||
| SSL | Sasol Ltd. | $11.31 | 6.70% | 5,267,106 | $7,210,551,296 |
|
||||
| SEDG | SolarEdge Technologies, Inc. | $37.44 | 6.39% | 1,971,961 | $2,260,113,920 |
|
||||
| MARA | MARA Holdings, Inc. | $9.32 | 6.39% | 73,011,343 | $3,543,786,496 |
|
||||
| MUR | Murphy Oil Corporation | $36.81 | 6.02% | 5,770,011 | $5,257,585,664 |
|
||||
| ADPT | Adaptive Biotechnologies Corpo | $13.17 | 5.78% | 3,892,105 | $2,027,937,280 |
|
||||
| NIO | NIO Inc. | $5.86 | 5.59% | 57,679,174 | $14,817,943,552 |
|
||||
| CRDO | Credo Technology Group Holding | $117.69 | 5.49% | 4,460,224 | $21,707,913,216 |
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
# Sector Performance Overview
|
||||
# Data retrieved on: 2026-03-15 11:17:40
|
||||
|
||||
| Sector | 1-Day % | 1-Week % | 1-Month % | YTD % |
|
||||
|--------|---------|----------|-----------|-------|
|
||||
| Communication Services | N/A | N/A | N/A | N/A |
|
||||
| Consumer Cyclical | N/A | N/A | N/A | N/A |
|
||||
| Consumer Defensive | N/A | N/A | N/A | N/A |
|
||||
| Energy | N/A | N/A | N/A | N/A |
|
||||
| Financial Services | N/A | N/A | N/A | N/A |
|
||||
| Healthcare | N/A | N/A | N/A | N/A |
|
||||
| Industrials | N/A | N/A | N/A | N/A |
|
||||
| Basic Materials | N/A | N/A | N/A | N/A |
|
||||
| Real Estate | N/A | N/A | N/A | N/A |
|
||||
| Technology | N/A | N/A | N/A | N/A |
|
||||
| Utilities | N/A | N/A | N/A | N/A |
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
# News for Topic: market
|
||||
# Data retrieved on: 2026-03-15 11:17:42
|
||||
|
||||
### Opinion: A Stock Market Crash Is Much More Likely Now Than It Was 2 Months Ago (source: Motley Fool)
|
||||
Link: https://finance.yahoo.com/m/f5bf5eda-ecb7-3918-9b7a-0b4ebce070cf/opinion%3A-a-stock-market-crash.html
|
||||
|
||||
### UBS: AI investment is ‘lone buffer’ for emerging markets as energy costs soar (source: Investing.com)
|
||||
Link: https://finance.yahoo.com/news/ubs-ai-investment-lone-buffer-021705455.html
|
||||
|
||||
### The Stock Market May Be Shifting From Risky Tech Stocks to Safer Sectors. Here Are 3 Stocks to Buy Before They Soar. (source: Motley Fool)
|
||||
Link: https://finance.yahoo.com/m/36037b15-c941-3d1a-8b65-3fef291ca4ad/the-stock-market-may-be.html
|
||||
|
||||
### BizTips: Boost your business by using the marketing funnel (source: Cape Cod Times)
|
||||
Link: https://finance.yahoo.com/m/22db7f27-19ca-372f-9eb3-d6bfe6531a87/biztips%3A-boost-your-business.html
|
||||
|
||||
### Sandisk (SNDK) Rockets 25.5%, Investors Makes Use of Market Bloodbath for Gains (source: Insider Monkey)
|
||||
Link: https://finance.yahoo.com/news/sandisk-sndk-rockets-25-5-094041196.html
|
||||
|
||||
### Goldman: AI PCs to buck 10% market slump as ‘edge computing’ demand accelerates (source: Investing.com)
|
||||
Link: https://finance.yahoo.com/news/goldman-ai-pcs-buck-10-003503068.html
|
||||
|
||||
### Fed to weigh interest rates amid Iran war, potential price increases (source: USA TODAY)
|
||||
Link: https://finance.yahoo.com/m/fd7d2f56-6374-324a-87a7-ead11eed5d62/fed-to-weigh-interest-rates.html
|
||||
|
||||
### Is Mobileye (MBLY) Now Offering Value After A 49% One Year Share Price Decline (source: Simply Wall St.)
|
||||
Link: https://finance.yahoo.com/news/mobileye-mbly-now-offering-value-080612183.html
|
||||
|
||||
### Will the Trump Bull Market Come to an Abrupt End Due to the Iran War? History Offers Its Objective and Potentially Uncomfortable Take. (source: Motley Fool)
|
||||
Link: https://finance.yahoo.com/m/1b1369ec-855e-3380-9ed4-274a58d0c2be/will-the-trump-bull-market.html
|
||||
|
||||
### 3 Magnificent High-Yield Dividend Stocks to Buy and Hold (source: Motley Fool)
|
||||
Link: https://finance.yahoo.com/m/1f5939f6-20c4-3c84-ac7a-f41d6218897c/3-magnificent-high-yield.html
|
||||
|
||||
|
|
@ -15,7 +15,7 @@ import pytest
|
|||
|
||||
# Set up the Python path to include the project root
|
||||
import sys
|
||||
sys.path.insert(0, '/Users/Ahmet/Repo/TradingAgents')
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
from tradingagents.agents.utils.scanner_tools import (
|
||||
get_market_movers,
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ def get_market_movers_alpha_vantage(
|
|||
return f"Invalid category '{category}'. Must be one of: day_gainers, day_losers, most_actives"
|
||||
|
||||
if category == 'most_actives':
|
||||
return "Alpha Vantage does not support 'most_actives' category. Please use yfinance instead."
|
||||
return "Error: Alpha Vantage does not support 'most_actives'. Use yfinance (default vendor) for this category."
|
||||
|
||||
# Make API request for TOP_GAINERS_LOSERS endpoint
|
||||
response = _make_api_request("TOP_GAINERS_LOSERS", {})
|
||||
|
|
@ -38,7 +38,7 @@ def get_market_movers_alpha_vantage(
|
|||
return f"Error from Alpha Vantage: {data['Error Message']}"
|
||||
|
||||
if "Note" in data:
|
||||
return f"Alpha Vantage API limit reached: {data['Note']}"
|
||||
return f"Error: Alpha Vantage API limit reached: {data['Note']}"
|
||||
|
||||
# Map category to Alpha Vantage response key
|
||||
if category == 'day_gainers':
|
||||
|
|
@ -46,7 +46,7 @@ def get_market_movers_alpha_vantage(
|
|||
elif category == 'day_losers':
|
||||
key = 'top_losers'
|
||||
else:
|
||||
return f"Unsupported category: {category}"
|
||||
return f"Error: unsupported category '{category}'"
|
||||
|
||||
if key not in data:
|
||||
return f"No data found for {category}"
|
||||
|
|
@ -74,7 +74,7 @@ def get_market_movers_alpha_vantage(
|
|||
if isinstance(price, str):
|
||||
try:
|
||||
price = f"${float(price):.2f}"
|
||||
except:
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
if isinstance(change_pct, str):
|
||||
change_pct = change_pct.rstrip('%') # Remove % if present
|
||||
|
|
@ -83,7 +83,7 @@ def get_market_movers_alpha_vantage(
|
|||
if isinstance(volume, (int, str)):
|
||||
try:
|
||||
volume = f"{int(volume):,}"
|
||||
except:
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
result_str += f"| {symbol} | {price} | {change_pct} | {volume} |\n"
|
||||
|
|
|
|||
|
|
@ -191,8 +191,7 @@ def route_to_vendor(method: str, *args, **kwargs):
|
|||
|
||||
try:
|
||||
return impl_func(*args, **kwargs)
|
||||
except Exception:
|
||||
# Continue to next vendor on any exception
|
||||
continue
|
||||
except AlphaVantageRateLimitError:
|
||||
continue # Only rate limits trigger fallback
|
||||
|
||||
raise RuntimeError(f"No available vendor for '{method}'")
|
||||
|
|
@ -31,7 +31,7 @@ def get_market_movers_yfinance(
|
|||
# Use yfinance screener module's screen function
|
||||
data = yf.screener.screen(screener_keys[category], count=25)
|
||||
|
||||
if not data or 'quotes' not in data:
|
||||
if not data or not isinstance(data, dict) or 'quotes' not in data:
|
||||
return f"No data found for {category}"
|
||||
|
||||
quotes = data['quotes']
|
||||
|
|
@ -97,29 +97,46 @@ def get_market_indices_yfinance() -> str:
|
|||
result_str += "| Index | Current Price | Change | Change % | 52W High | 52W Low |\n"
|
||||
result_str += "|-------|---------------|--------|----------|----------|----------|\n"
|
||||
|
||||
# Batch-download 1-day history for all symbols in a single request
|
||||
symbols = list(indices.keys())
|
||||
indices_history = yf.download(symbols, period="2d", auto_adjust=True, progress=False, threads=True)
|
||||
|
||||
for symbol, name in indices.items():
|
||||
try:
|
||||
ticker = yf.Ticker(symbol)
|
||||
info = ticker.info
|
||||
hist = ticker.history(period="1d")
|
||||
|
||||
if hist.empty:
|
||||
# fast_info is a lightweight cached property (no extra HTTP call)
|
||||
fast = ticker.fast_info
|
||||
|
||||
# Extract history for this symbol from the batch download
|
||||
try:
|
||||
if len(symbols) > 1:
|
||||
closes = indices_history["Close"][symbol].dropna()
|
||||
else:
|
||||
closes = indices_history["Close"].dropna()
|
||||
except KeyError:
|
||||
closes = None
|
||||
|
||||
if closes is None or len(closes) == 0:
|
||||
result_str += f"| {name} | N/A | - | - | - | - |\n"
|
||||
continue
|
||||
|
||||
current_price = hist['Close'].iloc[-1]
|
||||
prev_close = info.get('previousClose', current_price)
|
||||
|
||||
current_price = closes.iloc[-1]
|
||||
prev_close = closes.iloc[-2] if len(closes) >= 2 else fast.previous_close
|
||||
if prev_close is None or prev_close == 0:
|
||||
prev_close = current_price
|
||||
|
||||
change = current_price - prev_close
|
||||
change_pct = (change / prev_close * 100) if prev_close else 0
|
||||
|
||||
high_52w = info.get('fiftyTwoWeekHigh', 'N/A')
|
||||
low_52w = info.get('fiftyTwoWeekLow', 'N/A')
|
||||
|
||||
|
||||
high_52w = fast.year_high
|
||||
low_52w = fast.year_low
|
||||
|
||||
# Format numbers
|
||||
current_str = f"{current_price:.2f}"
|
||||
change_str = f"{change:+.2f}"
|
||||
change_pct_str = f"{change_pct:+.2f}%"
|
||||
high_str = f"{high_52w:.2f}" if isinstance(high_52w, (int, float)) else high_52w
|
||||
low_str = f"{low_52w:.2f}" if isinstance(low_52w, (int, float)) else low_52w
|
||||
high_str = f"{high_52w:.2f}" if isinstance(high_52w, (int, float)) else str(high_52w)
|
||||
low_str = f"{low_52w:.2f}" if isinstance(low_52w, (int, float)) else str(low_52w)
|
||||
|
||||
result_str += f"| {name} | {current_str} | {change_str} | {change_pct_str} | {high_str} | {low_str} |\n"
|
||||
|
||||
|
|
@ -140,7 +157,9 @@ def get_sector_performance_yfinance() -> str:
|
|||
Formatted string containing sector performance data
|
||||
"""
|
||||
try:
|
||||
# Get all GICS sectors
|
||||
# All 11 standard GICS (Global Industry Classification Standard) sectors.
|
||||
# These keys are fixed by yfinance's Sector API and cannot be fetched
|
||||
# dynamically; the GICS taxonomy is maintained by MSCI/S&P and is stable.
|
||||
sector_keys = [
|
||||
"communication-services",
|
||||
"consumer-cyclical",
|
||||
|
|
|
|||
|
|
@ -3,57 +3,47 @@
|
|||
from typing import Any
|
||||
from tradingagents.agents.utils.scanner_states import ScannerState
|
||||
|
||||
_ERROR_PREFIXES = ("Error", "No data", "No quotes", "No movers", "No news", "No industry", "Invalid")
|
||||
|
||||
|
||||
def _report_is_valid(report: str) -> bool:
|
||||
"""Return True when *report* contains usable data (non-empty, non-error)."""
|
||||
if not report or not report.strip():
|
||||
return False
|
||||
return not any(report.startswith(prefix) for prefix in _ERROR_PREFIXES)
|
||||
|
||||
|
||||
class ScannerConditionalLogic:
|
||||
"""Conditional logic for scanner graph flow control."""
|
||||
|
||||
def should_continue_geopolitical(self, state: ScannerState) -> bool:
|
||||
"""
|
||||
Determine if geopolitical scanning should continue.
|
||||
|
||||
Args:
|
||||
state: Current scanner state
|
||||
|
||||
Returns:
|
||||
bool: Whether to continue geopolitical scanning
|
||||
|
||||
Returns True only when the geopolitical report contains usable data.
|
||||
"""
|
||||
# Always continue for initial scan - no filtering logic implemented
|
||||
return True
|
||||
return _report_is_valid(state.get("geopolitical_report", ""))
|
||||
|
||||
def should_continue_movers(self, state: ScannerState) -> bool:
|
||||
"""
|
||||
Determine if market movers scanning should continue.
|
||||
|
||||
Args:
|
||||
state: Current scanner state
|
||||
|
||||
Returns:
|
||||
bool: Whether to continue market movers scanning
|
||||
|
||||
Returns True only when the market movers report contains usable data.
|
||||
"""
|
||||
# Always continue for initial scan - no filtering logic implemented
|
||||
return True
|
||||
return _report_is_valid(state.get("market_movers_report", ""))
|
||||
|
||||
def should_continue_sector(self, state: ScannerState) -> bool:
|
||||
"""
|
||||
Determine if sector scanning should continue.
|
||||
|
||||
Args:
|
||||
state: Current scanner state
|
||||
|
||||
Returns:
|
||||
bool: Whether to continue sector scanning
|
||||
|
||||
Returns True only when the sector performance report contains usable data.
|
||||
"""
|
||||
# Always continue for initial scan - no filtering logic implemented
|
||||
return True
|
||||
return _report_is_valid(state.get("sector_performance_report", ""))
|
||||
|
||||
def should_continue_industry(self, state: ScannerState) -> bool:
|
||||
"""
|
||||
Determine if industry deep dive should continue.
|
||||
|
||||
Args:
|
||||
state: Current scanner state
|
||||
|
||||
Returns:
|
||||
bool: Whether to continue industry deep dive
|
||||
|
||||
Returns True only when the industry deep dive report contains usable data.
|
||||
"""
|
||||
# Always continue for initial scan - no filtering logic implemented
|
||||
return True
|
||||
return _report_is_valid(state.get("industry_deep_dive_report", ""))
|
||||
|
|
|
|||
Loading…
Reference in New Issue