diff --git a/requirements.txt b/requirements.txt index f8792d1a..d0aa7092 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ +-e . typing-extensions langchain-openai langchain-experimental diff --git a/scripts/analyze_insider_transactions.py b/scripts/analyze_insider_transactions.py index 7381f303..cc1b4309 100644 --- a/scripts/analyze_insider_transactions.py +++ b/scripts/analyze_insider_transactions.py @@ -166,7 +166,9 @@ def analyze_insider_transactions(ticker: str, save_csv: bool = False, output_dir if pd.notna(row["Value"]) and row["Value"] > 0 else f"{'N/A':>16}" ) - logger.info(f" {row['Transaction']:15} | {row['Shares']:>12,.0f} shares | {value_str}") + logger.info( + f" {row['Transaction']:15} | {row['Shares']:>12,.0f} shares | {value_str}" + ) # ============================================================ # OVERALL SENTIMENT @@ -206,12 +208,16 @@ def analyze_insider_transactions(ticker: str, save_csv: bool = False, output_dir ) logger.info(f"Total Sales: {sales_count:>5} transactions | ${total_sales:>15,.0f}") - logger.info(f"Total Purchases: {purchases_count:>5} transactions | ${total_purchases:>15,.0f}") + logger.info( + f"Total Purchases: {purchases_count:>5} transactions | ${total_purchases:>15,.0f}" + ) if sentiment == "BULLISH": logger.info(f"\n⚡ BULLISH: Insiders are net BUYERS (${net_value:,.0f} net buying)") elif sentiment == "BEARISH": - logger.info(f"\n⚠️ BEARISH: Significant insider SELLING (${-net_value:,.0f} net selling)") + logger.info( + f"\n⚠️ BEARISH: Significant insider SELLING (${-net_value:,.0f} net selling)" + ) elif sentiment == "SLIGHTLY_BEARISH": logger.info( f"\n⚠️ SLIGHTLY BEARISH: More selling than buying (${-net_value:,.0f} net selling)" @@ -269,7 +275,9 @@ if __name__ == "__main__": ) logger.info("Example: python analyze_insider_transactions.py AAPL TSLA NVDA") logger.info(" python analyze_insider_transactions.py AAPL --csv") - logger.info(" python analyze_insider_transactions.py AAPL --csv --output-dir ./output") + logger.info( + " python analyze_insider_transactions.py AAPL --csv --output-dir ./output" + ) sys.exit(1) # Parse arguments diff --git a/scripts/build_ml_dataset.py b/scripts/build_ml_dataset.py index 7e0ba101..1bd384b7 100644 --- a/scripts/build_ml_dataset.py +++ b/scripts/build_ml_dataset.py @@ -18,7 +18,6 @@ import sys import time from pathlib import Path -import numpy as np import pandas as pd # Add project root to path @@ -40,35 +39,210 @@ logger = get_logger(__name__) # Can be overridden via --ticker-file DEFAULT_TICKERS = [ # Mega-cap tech - "AAPL", "MSFT", "GOOGL", "AMZN", "NVDA", "META", "TSLA", "AVGO", "ORCL", "CRM", - "AMD", "INTC", "CSCO", "ADBE", "NFLX", "QCOM", "TXN", "AMAT", "MU", "LRCX", - "KLAC", "MRVL", "SNPS", "CDNS", "PANW", "CRWD", "FTNT", "NOW", "UBER", "ABNB", + "AAPL", + "MSFT", + "GOOGL", + "AMZN", + "NVDA", + "META", + "TSLA", + "AVGO", + "ORCL", + "CRM", + "AMD", + "INTC", + "CSCO", + "ADBE", + "NFLX", + "QCOM", + "TXN", + "AMAT", + "MU", + "LRCX", + "KLAC", + "MRVL", + "SNPS", + "CDNS", + "PANW", + "CRWD", + "FTNT", + "NOW", + "UBER", + "ABNB", # Financials - "JPM", "BAC", "WFC", "GS", "MS", "C", "SCHW", "BLK", "AXP", "USB", - "PNC", "TFC", "COF", "BK", "STT", "FITB", "HBAN", "RF", "CFG", "KEY", + "JPM", + "BAC", + "WFC", + "GS", + "MS", + "C", + "SCHW", + "BLK", + "AXP", + "USB", + "PNC", + "TFC", + "COF", + "BK", + "STT", + "FITB", + "HBAN", + "RF", + "CFG", + "KEY", # Healthcare - "UNH", "JNJ", "LLY", "PFE", "ABBV", "MRK", "TMO", "ABT", "DHR", "BMY", - "AMGN", "GILD", "ISRG", "VRTX", "REGN", "MDT", "SYK", "BSX", "EW", "ZTS", + "UNH", + "JNJ", + "LLY", + "PFE", + "ABBV", + "MRK", + "TMO", + "ABT", + "DHR", + "BMY", + "AMGN", + "GILD", + "ISRG", + "VRTX", + "REGN", + "MDT", + "SYK", + "BSX", + "EW", + "ZTS", # Consumer - "WMT", "PG", "KO", "PEP", "COST", "MCD", "NKE", "SBUX", "TGT", "LOW", - "HD", "TJX", "ROST", "DG", "DLTR", "EL", "CL", "KMB", "GIS", "K", + "WMT", + "PG", + "KO", + "PEP", + "COST", + "MCD", + "NKE", + "SBUX", + "TGT", + "LOW", + "HD", + "TJX", + "ROST", + "DG", + "DLTR", + "EL", + "CL", + "KMB", + "GIS", + "K", # Energy - "XOM", "CVX", "COP", "EOG", "SLB", "MPC", "PSX", "VLO", "OXY", "DVN", - "HAL", "FANG", "HES", "BKR", "KMI", "WMB", "OKE", "ET", "TRGP", "LNG", + "XOM", + "CVX", + "COP", + "EOG", + "SLB", + "MPC", + "PSX", + "VLO", + "OXY", + "DVN", + "HAL", + "FANG", + "HES", + "BKR", + "KMI", + "WMB", + "OKE", + "ET", + "TRGP", + "LNG", # Industrials - "CAT", "DE", "UNP", "UPS", "HON", "RTX", "BA", "LMT", "GD", "NOC", - "GE", "MMM", "EMR", "ITW", "PH", "ROK", "ETN", "SWK", "CMI", "PCAR", + "CAT", + "DE", + "UNP", + "UPS", + "HON", + "RTX", + "BA", + "LMT", + "GD", + "NOC", + "GE", + "MMM", + "EMR", + "ITW", + "PH", + "ROK", + "ETN", + "SWK", + "CMI", + "PCAR", # Materials & Utilities - "LIN", "APD", "ECL", "SHW", "DD", "NEM", "FCX", "VMC", "MLM", "NUE", - "NEE", "DUK", "SO", "D", "AEP", "EXC", "SRE", "XEL", "WEC", "ES", + "LIN", + "APD", + "ECL", + "SHW", + "DD", + "NEM", + "FCX", + "VMC", + "MLM", + "NUE", + "NEE", + "DUK", + "SO", + "D", + "AEP", + "EXC", + "SRE", + "XEL", + "WEC", + "ES", # REITs & Telecom - "AMT", "PLD", "CCI", "EQIX", "SPG", "O", "PSA", "DLR", "WELL", "AVB", - "T", "VZ", "TMUS", "CHTR", "CMCSA", + "AMT", + "PLD", + "CCI", + "EQIX", + "SPG", + "O", + "PSA", + "DLR", + "WELL", + "AVB", + "T", + "VZ", + "TMUS", + "CHTR", + "CMCSA", # High-volatility / popular retail - "COIN", "MARA", "RIOT", "PLTR", "SOFI", "HOOD", "RBLX", "SNAP", "PINS", "SQ", - "SHOP", "SE", "ROKU", "DKNG", "PENN", "WYNN", "MGM", "LVS", "DASH", "TTD", + "COIN", + "MARA", + "RIOT", + "PLTR", + "SOFI", + "HOOD", + "RBLX", + "SNAP", + "PINS", + "SQ", + "SHOP", + "SE", + "ROKU", + "DKNG", + "PENN", + "WYNN", + "MGM", + "LVS", + "DASH", + "TTD", # Biotech - "MRNA", "BNTX", "BIIB", "SGEN", "ALNY", "BMRN", "EXAS", "DXCM", "HZNP", "INCY", + "MRNA", + "BNTX", + "BIIB", + "SGEN", + "ALNY", + "BMRN", + "EXAS", + "DXCM", + "HZNP", + "INCY", ] OUTPUT_DIR = Path("data/ml") @@ -221,10 +395,16 @@ def build_dataset( logger.info(f"\n{'='*60}") logger.info(f"Dataset built: {len(dataset)} total samples from {len(all_data)} tickers") - logger.info(f"Label distribution:") - logger.info(f" WIN (+1): {int((dataset['label'] == 1).sum()):>7} ({(dataset['label'] == 1).mean()*100:.1f}%)") - logger.info(f" LOSS (-1): {int((dataset['label'] == -1).sum()):>7} ({(dataset['label'] == -1).mean()*100:.1f}%)") - logger.info(f" TIMEOUT: {int((dataset['label'] == 0).sum()):>7} ({(dataset['label'] == 0).mean()*100:.1f}%)") + logger.info("Label distribution:") + logger.info( + f" WIN (+1): {int((dataset['label'] == 1).sum()):>7} ({(dataset['label'] == 1).mean()*100:.1f}%)" + ) + logger.info( + f" LOSS (-1): {int((dataset['label'] == -1).sum()):>7} ({(dataset['label'] == -1).mean()*100:.1f}%)" + ) + logger.info( + f" TIMEOUT: {int((dataset['label'] == 0).sum()):>7} ({(dataset['label'] == 0).mean()*100:.1f}%)" + ) logger.info(f"Features: {len(FEATURE_COLUMNS)}") logger.info(f"{'='*60}") @@ -233,12 +413,20 @@ def build_dataset( def main(): parser = argparse.ArgumentParser(description="Build ML training dataset") - parser.add_argument("--stocks", type=int, default=None, help="Limit to N stocks from default universe") - parser.add_argument("--ticker-file", type=str, default=None, help="File with tickers (one per line)") + parser.add_argument( + "--stocks", type=int, default=None, help="Limit to N stocks from default universe" + ) + parser.add_argument( + "--ticker-file", type=str, default=None, help="File with tickers (one per line)" + ) parser.add_argument("--start", type=str, default="2022-01-01", help="Start date (YYYY-MM-DD)") parser.add_argument("--end", type=str, default="2025-12-31", help="End date (YYYY-MM-DD)") - parser.add_argument("--profit-target", type=float, default=0.05, help="Profit target fraction (default: 0.05)") - parser.add_argument("--stop-loss", type=float, default=0.03, help="Stop loss fraction (default: 0.03)") + parser.add_argument( + "--profit-target", type=float, default=0.05, help="Profit target fraction (default: 0.05)" + ) + parser.add_argument( + "--stop-loss", type=float, default=0.03, help="Stop loss fraction (default: 0.03)" + ) parser.add_argument("--holding-days", type=int, default=7, help="Max holding days (default: 7)") parser.add_argument("--output", type=str, default=None, help="Output parquet path") args = parser.parse_args() @@ -246,7 +434,9 @@ def main(): # Determine ticker list if args.ticker_file: with open(args.ticker_file) as f: - tickers = [line.strip().upper() for line in f if line.strip() and not line.startswith("#")] + tickers = [ + line.strip().upper() for line in f if line.strip() and not line.startswith("#") + ] logger.info(f"Loaded {len(tickers)} tickers from {args.ticker_file}") else: tickers = DEFAULT_TICKERS diff --git a/scripts/build_strategy_specific_memories.py b/scripts/build_strategy_specific_memories.py index e8720d79..2849367c 100644 --- a/scripts/build_strategy_specific_memories.py +++ b/scripts/build_strategy_specific_memories.py @@ -11,7 +11,6 @@ This script creates memory sets optimized for: import os import sys -from pathlib import Path sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) diff --git a/scripts/track_recommendation_performance.py b/scripts/track_recommendation_performance.py index 72a02549..098faf27 100644 --- a/scripts/track_recommendation_performance.py +++ b/scripts/track_recommendation_performance.py @@ -17,7 +17,6 @@ import json import os import sys from datetime import datetime -from pathlib import Path from typing import Any, Dict, List # Add parent directory to path diff --git a/scripts/train_ml_model.py b/scripts/train_ml_model.py index 7045b29d..c6cab28e 100644 --- a/scripts/train_ml_model.py +++ b/scripts/train_ml_model.py @@ -80,13 +80,16 @@ def time_split( if max_train_samples is not None and len(train) > max_train_samples: train = train.sort_values("date").tail(max_train_samples) logger.info( - f"Limiting training samples to most recent {max_train_samples} " - f"before {val_start}" + f"Limiting training samples to most recent {max_train_samples} " f"before {val_start}" ) logger.info(f"Time-based split at {val_start}:") - logger.info(f" Train: {len(train)} samples ({train['date'].min().date()} to {train['date'].max().date()})") - logger.info(f" Val: {len(val)} samples ({val['date'].min().date()} to {val['date'].max().date()})") + logger.info( + f" Train: {len(train)} samples ({train['date'].min().date()} to {train['date'].max().date()})" + ) + logger.info( + f" Val: {len(val)} samples ({val['date'].min().date()} to {val['date'].max().date()})" + ) X_train = train[FEATURE_COLUMNS].values y_train = train["label"].values.astype(int) @@ -152,8 +155,12 @@ def train_lightgbm(X_train, y_train, X_val, y_val): class_weight = {c: total / (n_classes * count) for c, count in class_counts.items()} sample_weights = np.array([class_weight[y] for y in y_train_mapped]) - train_data = lgb.Dataset(X_train, label=y_train_mapped, weight=sample_weights, feature_name=FEATURE_COLUMNS) - val_data = lgb.Dataset(X_val, label=y_val_mapped, feature_name=FEATURE_COLUMNS, reference=train_data) + train_data = lgb.Dataset( + X_train, label=y_train_mapped, weight=sample_weights, feature_name=FEATURE_COLUMNS + ) + val_data = lgb.Dataset( + X_val, label=y_val_mapped, feature_name=FEATURE_COLUMNS, reference=train_data + ) params = { "objective": "multiclass", @@ -209,7 +216,8 @@ def evaluate(model, X_val, y_val, model_type: str) -> dict: accuracy = accuracy_score(y_val, y_pred) report = classification_report( - y_val, y_pred, + y_val, + y_pred, target_names=["LOSS (-1)", "TIMEOUT (0)", "WIN (+1)"], output_dict=True, ) @@ -253,13 +261,21 @@ def evaluate(model, X_val, y_val, model_type: str) -> dict: # Top decile (top 10% by P(WIN)) — most actionable metric top_decile_threshold = np.percentile(win_probs_all, 90) top_decile_mask = win_probs_all >= top_decile_threshold - top_decile_win_rate = float((y_val[top_decile_mask] == 1).mean()) if top_decile_mask.sum() > 0 else 0.0 - top_decile_loss_rate = float((y_val[top_decile_mask] == -1).mean()) if top_decile_mask.sum() > 0 else 0.0 + top_decile_win_rate = ( + float((y_val[top_decile_mask] == 1).mean()) if top_decile_mask.sum() > 0 else 0.0 + ) + top_decile_loss_rate = ( + float((y_val[top_decile_mask] == -1).mean()) if top_decile_mask.sum() > 0 else 0.0 + ) metrics = { "model_type": model_type, "accuracy": round(accuracy, 4), - "per_class": {k: {kk: round(vv, 4) for kk, vv in v.items()} for k, v in report.items() if isinstance(v, dict)}, + "per_class": { + k: {kk: round(vv, 4) for kk, vv in v.items()} + for k, v in report.items() + if isinstance(v, dict) + }, "confusion_matrix": cm.tolist(), "avg_win_prob_for_actual_wins": round(avg_win_prob_for_actual_wins, 4), "high_confidence_win_precision": round(high_conf_precision, 4), @@ -276,25 +292,31 @@ def evaluate(model, X_val, y_val, model_type: str) -> dict: logger.info(f"\n{'='*60}") logger.info(f"Model: {model_type}") logger.info(f"Overall Accuracy: {accuracy:.1%}") - logger.info(f"\nPer-class metrics:") + logger.info("\nPer-class metrics:") logger.info(f"{'':>15} {'Precision':>10} {'Recall':>10} {'F1':>10} {'Support':>10}") for label, name in [(-1, "LOSS"), (0, "TIMEOUT"), (1, "WIN")]: key = f"{name} ({label:+d})" if key in report: r = report[key] - logger.info(f"{name:>15} {r['precision']:>10.3f} {r['recall']:>10.3f} {r['f1-score']:>10.3f} {r['support']:>10.0f}") + logger.info( + f"{name:>15} {r['precision']:>10.3f} {r['recall']:>10.3f} {r['f1-score']:>10.3f} {r['support']:>10.0f}" + ) - logger.info(f"\nConfusion Matrix (rows=actual, cols=predicted):") + logger.info("\nConfusion Matrix (rows=actual, cols=predicted):") logger.info(f"{'':>10} {'LOSS':>8} {'TIMEOUT':>8} {'WIN':>8}") for i, name in enumerate(["LOSS", "TIMEOUT", "WIN"]): logger.info(f"{name:>10} {cm[i][0]:>8} {cm[i][1]:>8} {cm[i][2]:>8}") - logger.info(f"\nWin-class insights:") + logger.info("\nWin-class insights:") logger.info(f" Avg P(WIN) for actual winners: {avg_win_prob_for_actual_wins:.1%}") - logger.info(f" High-confidence (>60%) precision: {high_conf_precision:.1%} ({high_conf_count} samples)") + logger.info( + f" High-confidence (>60%) precision: {high_conf_precision:.1%} ({high_conf_count} samples)" + ) logger.info("\nCalibration (does higher P(WIN) = more actual wins?):") - logger.info(f"{'Quintile':>10} {'Avg P(WIN)':>12} {'Actual WIN%':>12} {'Actual LOSS%':>13} {'Count':>8}") + logger.info( + f"{'Quintile':>10} {'Avg P(WIN)':>12} {'Actual WIN%':>12} {'Actual LOSS%':>13} {'Count':>8}" + ) for q_name, q_data in calibration.items(): logger.info( f"{q_name:>10} {q_data['mean_predicted_win_prob']:>12.1%} " @@ -304,7 +326,9 @@ def evaluate(model, X_val, y_val, model_type: str) -> dict: logger.info("\nTop decile (top 10% by P(WIN)):") logger.info(f" Threshold: P(WIN) >= {top_decile_threshold:.1%}") - logger.info(f" Actual win rate: {top_decile_win_rate:.1%} ({int(top_decile_mask.sum())} samples)") + logger.info( + f" Actual win rate: {top_decile_win_rate:.1%} ({int(top_decile_mask.sum())} samples)" + ) logger.info(f" Actual loss rate: {top_decile_loss_rate:.1%}") baseline_win = float((y_val == 1).mean()) logger.info(f" Baseline win rate: {baseline_win:.1%}") @@ -318,12 +342,25 @@ def evaluate(model, X_val, y_val, model_type: str) -> dict: def main(): parser = argparse.ArgumentParser(description="Train ML model for win probability") parser.add_argument("--dataset", type=str, default="data/ml/training_dataset.parquet") - parser.add_argument("--model", type=str, choices=["tabpfn", "lightgbm", "auto"], default="auto", - help="Model type (auto tries TabPFN first, falls back to LightGBM)") - parser.add_argument("--val-start", type=str, default="2024-07-01", - help="Validation split date (default: 2024-07-01)") - parser.add_argument("--max-train-samples", type=int, default=None, - help="Limit training samples to the most recent N before val-start") + parser.add_argument( + "--model", + type=str, + choices=["tabpfn", "lightgbm", "auto"], + default="auto", + help="Model type (auto tries TabPFN first, falls back to LightGBM)", + ) + parser.add_argument( + "--val-start", + type=str, + default="2024-07-01", + help="Validation split date (default: 2024-07-01)", + ) + parser.add_argument( + "--max-train-samples", + type=int, + default=None, + help="Limit training samples to the most recent N before val-start", + ) parser.add_argument("--output-dir", type=str, default="data/ml") args = parser.parse_args() diff --git a/tools_testing.ipynb b/tools_testing.ipynb index c289f85a..9b35bf08 100644 --- a/tools_testing.ipynb +++ b/tools_testing.ipynb @@ -13,7 +13,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -6366,12 +6366,1000 @@ "print(get_options_activity(curr_date=\"2025-12-31\", num_expirations=2, ticker=\"AI\"))" ] }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from tradingagents.dataflows.y_finance import (\n", + " get_YFin_data_online,\n", + " get_technical_analysis,\n", + " get_balance_sheet as get_yfinance_balance_sheet,\n", + " get_cashflow as get_yfinance_cashflow,\n", + " get_income_statement as get_yfinance_income_statement,\n", + " get_insider_transactions as get_yfinance_insider_transactions,\n", + " validate_ticker as validate_ticker_yfinance,\n", + " validate_tickers_batch as validate_tickers_batch_yfinance,\n", + " get_fundamentals as get_yfinance_fundamentals,\n", + " get_options_activity as get_yfinance_options_activity,\n", + ")\n", + "from tradingagents.dataflows.alpha_vantage import (\n", + " get_stock as get_alpha_vantage_stock,\n", + " get_fundamentals as get_alpha_vantage_fundamentals,\n", + " get_balance_sheet as get_alpha_vantage_balance_sheet,\n", + " get_cashflow as get_alpha_vantage_cashflow,\n", + " get_income_statement as get_alpha_vantage_income_statement,\n", + " get_insider_transactions as get_alpha_vantage_insider_transactions,\n", + " get_insider_sentiment as get_alpha_vantage_insider_sentiment,\n", + " get_top_gainers_losers as get_alpha_vantage_movers,\n", + ")\n", + "from tradingagents.dataflows.openai import (\n", + " get_stock_news_openai,\n", + " get_global_news_openai,\n", + " get_fundamentals_openai,\n", + ")\n", + "from tradingagents.dataflows.reddit_api import (\n", + " get_reddit_news,\n", + " get_reddit_global_news as get_reddit_api_global_news,\n", + " get_reddit_trending_tickers,\n", + " get_reddit_discussions,\n", + ")\n", + "from tradingagents.dataflows.finnhub_api import (\n", + " get_recommendation_trends as get_finnhub_recommendation_trends,\n", + " get_earnings_calendar as get_finnhub_earnings_calendar,\n", + " get_ipo_calendar as get_finnhub_ipo_calendar,\n", + ")\n", + "from tradingagents.dataflows.twitter_data import (\n", + " get_tweets as get_twitter_tweets,\n", + ")\n", + "from tradingagents.dataflows.alpha_vantage_volume import (\n", + " get_alpha_vantage_unusual_volume,\n", + " get_cached_average_volume,\n", + " get_cached_average_volume_batch,\n", + ")\n", + "from tradingagents.dataflows.alpha_vantage_analysts import (\n", + " get_alpha_vantage_analyst_changes,\n", + ")\n", + "from tradingagents.dataflows.tradier_api import (\n", + " get_tradier_unusual_options,\n", + ")\n", + "from tradingagents.dataflows.finviz_scraper import (\n", + " get_finviz_short_interest,\n", + ")\n", + "\n", + "from dotenv import load_dotenv\n", + "\n", + "# Load environment variables from .env file\n", + "load_dotenv()" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Between January 4 and January 5, 2026, Apple Inc. (AAPL) experienced a notable decline in its stock price. On January 5, the stock closed at $267.26, marking a 1.38% decrease from the previous day. ([zacks.com](https://www.zacks.com/stock/news/2812056/apple-%28aapl%29-stock-sinks-as-market-gains%3A-what-you-should-know?utm_source=openai))\\n\\nThis decline occurred despite the broader market's positive performance on January 5, with the S&P 500 gaining 0.64%, the Dow increasing by 1.23%, and the Nasdaq rising by 0.69%. ([zacks.com](https://www.zacks.com/stock/news/2812056/apple-%28aapl%29-stock-sinks-as-market-gains%3A-what-you-should-know?utm_source=openai))\\n\\nThe trading volume on January 5 was substantial, with AAPL shares totaling $12.16 billion, making it the third-highest traded stock of the day. However, no direct news catalysts were identified to explain the price movement, leading analysts to speculate that macroeconomic factors or algorithmic trading might have influenced the decline. ([ainvest.com](https://www.ainvest.com/news/apple-1-38-drop-hits-highest-trading-volume-mystery-2601/?utm_source=openai))\\n\\nAnalyst sentiment remains optimistic for AAPL's long-term prospects. For instance, Wedbush Securities analyst Daniel Ives set a price target of $350 for AAPL, suggesting a potential upside of approximately 28% from the current levels. ([red94.net](https://www.red94.net/news/44841-aapl-stock-poised-to-thrive-in-2026-after-33-gain-trades-at-271-86-to-start-new/?utm_source=openai))\\n\\nIn summary, while AAPL's stock experienced a short-term decline between January 4 and January 5, 2026, the broader market's positive performance and continued analyst optimism indicate potential for future growth.\\n\\n## Stock market information for Apple Inc (AAPL)\\n- Apple Inc is a equity in the USA market.\\n- The price is 262.36 USD currently with a change of -5.04 USD (-0.02%) from the previous close.\\n- The latest open price was 266.96 USD and the intraday volume is 52352090.\\n- The intraday high is 267.56 USD and the intraday low is 262.18 USD.\\n- The latest trade time is Tuesday, January 6, 17:15:00 PST.\\n \"" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "get_stock_news_openai(query=None, ticker=\"AAPL\", start_date=\"2026-01-04\", end_date=\"2026-01-05\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'## Analyst Recommendation Trends for AAPL\\n\\n### 2026-01-01\\n- **Strong Buy**: 14\\n- **Buy**: 21\\n- **Hold**: 16\\n- **Sell**: 2\\n- **Strong Sell**: 0\\n- **Total Analysts**: 53\\n\\n**Sentiment**: 66.0% Bullish, 3.8% Bearish\\n\\n### 2025-12-01\\n- **Strong Buy**: 15\\n- **Buy**: 23\\n- **Hold**: 16\\n- **Sell**: 2\\n- **Strong Sell**: 0\\n- **Total Analysts**: 56\\n\\n**Sentiment**: 67.9% Bullish, 3.6% Bearish\\n\\n### 2025-11-01\\n- **Strong Buy**: 15\\n- **Buy**: 23\\n- **Hold**: 17\\n- **Sell**: 2\\n- **Strong Sell**: 0\\n- **Total Analysts**: 57\\n\\n**Sentiment**: 66.7% Bullish, 3.5% Bearish\\n\\n### 2025-10-01\\n- **Strong Buy**: 15\\n- **Buy**: 22\\n- **Hold**: 17\\n- **Sell**: 2\\n- **Strong Sell**: 0\\n- **Total Analysts**: 56\\n\\n**Sentiment**: 66.1% Bullish, 3.6% Bearish\\n\\n'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "get_finnhub_recommendation_trends(ticker=\"AAPL\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Latest Market Discovery ---\n", + "Ticker: META\n", + "Summary: Meta Platforms reported stronger-than-expected Q4 2025 results (revenue about $59.9B; EPS $8.88), and guided 2026 capital expenditures of $115–$135B. The company highlighted AI infrastructure investments and posted substantial Reality Labs cost despite a plan to trim some spend, with shares rising after the results.\n", + "Date: 2026-01-28\n", + "\n", + "Ticker: TSLA\n", + "Summary: Tesla signaled a strategic pivot toward robotics by announcing the discontinuation of the Model S and Model X to repurpose factories for humanoid-robotics initiatives (Optimus). The period also included a Q4 2025 earnings beat and a roughly $20B 2026 capex plan, framing a shift from traditional carmaking to AI-driven initiatives.\n", + "Date: 2026-01-28\n", + "\n", + "Ticker: AAPL\n", + "Summary: No widely reported, Major Apple headlines in the window of 2026-01-22 to 2026-01-28 based on coverage from top financial outlets; analysts were focused on earnings timing and AI strategy ahead of Q1 results.\n", + "Date: 2026-01-28\n", + "\n", + "Ticker: NVDA\n", + "Summary: No Nvidia-specific major news reported in the week of 2026-01-22 to 2026-01-28; the closest notable coverage around this period focused on CES-related Nvidia discussions earlier in January (e.g., Bloomberg and TechCrunch reporting on Nvidia’s CES presence and H200 demand as of early January).\n", + "Date: 2026-01-28\n", + "\n" + ] + } + ], + "source": [ + "from pydantic import BaseModel\n", + "from typing import List\n", + "\n", + "# This is your individual object\n", + "class TickerNews(BaseModel):\n", + " ticker: str\n", + " news_summary: str\n", + " date: str\n", + "\n", + "# This is the \"container\" that ensures the output is a list\n", + "class PortfolioUpdate(BaseModel):\n", + " items: List[TickerNews]\n", + " \n", + "from openai import OpenAI\n", + "from dotenv import load_dotenv\n", + "\n", + "load_dotenv()\n", + "\n", + "client = OpenAI()\n", + "\n", + "def discover_market_news():\n", + " # We use parse() to ensure the output matches our Pydantic class\n", + " completion = client.responses.parse(\n", + " model=\"gpt-5-nano\",\n", + " # Enable the web search tool\n", + " tools=[{\"type\": \"web_search\"}],\n", + " input=\"Find the most significant news stories for ['AAPL', 'NVDA', 'TSLA', 'META'] from 2026-01-22 to 2026-01-28.\"\n", + " \"Return them as a list of ticker symbols and summaries.\",\n", + " # Define the structure of the response\n", + " text_format=PortfolioUpdate,\n", + " )\n", + "\n", + " # Access the parsed data\n", + " portfolio_data = completion.output_parsed\n", + " \n", + " print(\"--- Latest Market Discovery ---\")\n", + " for item in portfolio_data.items:\n", + " print(f\"Ticker: {item.ticker}\")\n", + " print(f\"Summary: {item.news_summary}\")\n", + " print(f\"Date: {item.date}\\n\")\n", + "\n", + "\n", + "discover_market_news()" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from tradingagents.dataflows.openai import get_batch_stock_news_openai, get_batch_stock_news_google\n", + "news = get_batch_stock_news_openai(tickers= ['AAPL', 'NVDA', 'TSLA', 'META'], start_date='2026-01-22', end_date='2026-01-28', batch_size=100)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{}" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "google_news = get_batch_stock_news_google(tickers= ['AAPL', 'NVDA', 'TSLA', 'META'], start_date='2026-01-22', end_date='2026-01-28', batch_size=100)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'google_news' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mNameError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[2]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[43mgoogle_news\u001b[49m\n", + "\u001b[31mNameError\u001b[39m: name 'google_news' is not defined" + ] + } + ], + "source": [ + "google_news" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "ename": "KeyError", + "evalue": "'google_search'", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mKeyError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[9]\u001b[39m\u001b[32m, line 27\u001b[39m\n\u001b[32m 24\u001b[39m structured_search_chain = _llm.with_structured_output(PortfolioUpdate)\n\u001b[32m 26\u001b[39m \u001b[38;5;66;03m# This will search the web AND format the result into your class\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m27\u001b[39m result = \u001b[43mstructured_search_chain\u001b[49m\u001b[43m.\u001b[49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mCheck the latest news for AAPL and TSLA from the last 24 hours.\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[32m 29\u001b[39m result\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/envs/tradingagents/lib/python3.13/site-packages/langchain_core/runnables/base.py:3090\u001b[39m, in \u001b[36mRunnableSequence.invoke\u001b[39m\u001b[34m(self, input, config, **kwargs)\u001b[39m\n\u001b[32m 3088\u001b[39m input_ = context.run(step.invoke, input_, config, **kwargs)\n\u001b[32m 3089\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m-> \u001b[39m\u001b[32m3090\u001b[39m input_ = \u001b[43mcontext\u001b[49m\u001b[43m.\u001b[49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[43mstep\u001b[49m\u001b[43m.\u001b[49m\u001b[43minvoke\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minput_\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 3091\u001b[39m \u001b[38;5;66;03m# finish the root run\u001b[39;00m\n\u001b[32m 3092\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/envs/tradingagents/lib/python3.13/site-packages/langchain_core/output_parsers/base.py:200\u001b[39m, in \u001b[36mBaseOutputParser.invoke\u001b[39m\u001b[34m(self, input, config, **kwargs)\u001b[39m\n\u001b[32m 192\u001b[39m \u001b[38;5;129m@override\u001b[39m\n\u001b[32m 193\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34minvoke\u001b[39m(\n\u001b[32m 194\u001b[39m \u001b[38;5;28mself\u001b[39m,\n\u001b[32m (...)\u001b[39m\u001b[32m 197\u001b[39m **kwargs: Any,\n\u001b[32m 198\u001b[39m ) -> T:\n\u001b[32m 199\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(\u001b[38;5;28minput\u001b[39m, BaseMessage):\n\u001b[32m--> \u001b[39m\u001b[32m200\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_call_with_config\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 201\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mlambda\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43minner_input\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mparse_result\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 202\u001b[39m \u001b[43m \u001b[49m\u001b[43m[\u001b[49m\u001b[43mChatGeneration\u001b[49m\u001b[43m(\u001b[49m\u001b[43mmessage\u001b[49m\u001b[43m=\u001b[49m\u001b[43minner_input\u001b[49m\u001b[43m)\u001b[49m\u001b[43m]\u001b[49m\n\u001b[32m 203\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 204\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 205\u001b[39m \u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 206\u001b[39m \u001b[43m \u001b[49m\u001b[43mrun_type\u001b[49m\u001b[43m=\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mparser\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 207\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 208\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m._call_with_config(\n\u001b[32m 209\u001b[39m \u001b[38;5;28;01mlambda\u001b[39;00m inner_input: \u001b[38;5;28mself\u001b[39m.parse_result([Generation(text=inner_input)]),\n\u001b[32m 210\u001b[39m \u001b[38;5;28minput\u001b[39m,\n\u001b[32m 211\u001b[39m config,\n\u001b[32m 212\u001b[39m run_type=\u001b[33m\"\u001b[39m\u001b[33mparser\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m 213\u001b[39m )\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/envs/tradingagents/lib/python3.13/site-packages/langchain_core/runnables/base.py:2021\u001b[39m, in \u001b[36mRunnable._call_with_config\u001b[39m\u001b[34m(self, func, input_, config, run_type, serialized, **kwargs)\u001b[39m\n\u001b[32m 2017\u001b[39m child_config = patch_config(config, callbacks=run_manager.get_child())\n\u001b[32m 2018\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m set_config_context(child_config) \u001b[38;5;28;01mas\u001b[39;00m context:\n\u001b[32m 2019\u001b[39m output = cast(\n\u001b[32m 2020\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mOutput\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m-> \u001b[39m\u001b[32m2021\u001b[39m \u001b[43mcontext\u001b[49m\u001b[43m.\u001b[49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 2022\u001b[39m \u001b[43m \u001b[49m\u001b[43mcall_func_with_variable_args\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;66;43;03m# type: ignore[arg-type]\u001b[39;49;00m\n\u001b[32m 2023\u001b[39m \u001b[43m \u001b[49m\u001b[43mfunc\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 2024\u001b[39m \u001b[43m \u001b[49m\u001b[43minput_\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 2025\u001b[39m \u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 2026\u001b[39m \u001b[43m \u001b[49m\u001b[43mrun_manager\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 2027\u001b[39m \u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 2028\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m,\n\u001b[32m 2029\u001b[39m )\n\u001b[32m 2030\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[32m 2031\u001b[39m run_manager.on_chain_error(e)\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/envs/tradingagents/lib/python3.13/site-packages/langchain_core/runnables/config.py:428\u001b[39m, in \u001b[36mcall_func_with_variable_args\u001b[39m\u001b[34m(func, input, config, run_manager, **kwargs)\u001b[39m\n\u001b[32m 426\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m run_manager \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m accepts_run_manager(func):\n\u001b[32m 427\u001b[39m kwargs[\u001b[33m\"\u001b[39m\u001b[33mrun_manager\u001b[39m\u001b[33m\"\u001b[39m] = run_manager\n\u001b[32m--> \u001b[39m\u001b[32m428\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/envs/tradingagents/lib/python3.13/site-packages/langchain_core/output_parsers/base.py:201\u001b[39m, in \u001b[36mBaseOutputParser.invoke..\u001b[39m\u001b[34m(inner_input)\u001b[39m\n\u001b[32m 192\u001b[39m \u001b[38;5;129m@override\u001b[39m\n\u001b[32m 193\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34minvoke\u001b[39m(\n\u001b[32m 194\u001b[39m \u001b[38;5;28mself\u001b[39m,\n\u001b[32m (...)\u001b[39m\u001b[32m 197\u001b[39m **kwargs: Any,\n\u001b[32m 198\u001b[39m ) -> T:\n\u001b[32m 199\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(\u001b[38;5;28minput\u001b[39m, BaseMessage):\n\u001b[32m 200\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m._call_with_config(\n\u001b[32m--> \u001b[39m\u001b[32m201\u001b[39m \u001b[38;5;28;01mlambda\u001b[39;00m inner_input: \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mparse_result\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 202\u001b[39m \u001b[43m \u001b[49m\u001b[43m[\u001b[49m\u001b[43mChatGeneration\u001b[49m\u001b[43m(\u001b[49m\u001b[43mmessage\u001b[49m\u001b[43m=\u001b[49m\u001b[43minner_input\u001b[49m\u001b[43m)\u001b[49m\u001b[43m]\u001b[49m\n\u001b[32m 203\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m,\n\u001b[32m 204\u001b[39m \u001b[38;5;28minput\u001b[39m,\n\u001b[32m 205\u001b[39m config,\n\u001b[32m 206\u001b[39m run_type=\u001b[33m\"\u001b[39m\u001b[33mparser\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m 207\u001b[39m )\n\u001b[32m 208\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m._call_with_config(\n\u001b[32m 209\u001b[39m \u001b[38;5;28;01mlambda\u001b[39;00m inner_input: \u001b[38;5;28mself\u001b[39m.parse_result([Generation(text=inner_input)]),\n\u001b[32m 210\u001b[39m \u001b[38;5;28minput\u001b[39m,\n\u001b[32m 211\u001b[39m config,\n\u001b[32m 212\u001b[39m run_type=\u001b[33m\"\u001b[39m\u001b[33mparser\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m 213\u001b[39m )\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/envs/tradingagents/lib/python3.13/site-packages/langchain_core/output_parsers/openai_tools.py:338\u001b[39m, in \u001b[36mPydanticToolsParser.parse_result\u001b[39m\u001b[34m(self, result, partial)\u001b[39m\n\u001b[32m 336\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(msg)\n\u001b[32m 337\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m338\u001b[39m pydantic_objects.append(\u001b[43mname_dict\u001b[49m\u001b[43m[\u001b[49m\u001b[43mres\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mtype\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m]\u001b[49m(**res[\u001b[33m\"\u001b[39m\u001b[33margs\u001b[39m\u001b[33m\"\u001b[39m]))\n\u001b[32m 339\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m (ValidationError, \u001b[38;5;167;01mValueError\u001b[39;00m):\n\u001b[32m 340\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m partial:\n", + "\u001b[31mKeyError\u001b[39m: 'google_search'" + ] + } + ], + "source": [ + "from google import genai\n", + "from google.genai import types\n", + "\n", + "from dotenv import load_dotenv\n", + "\n", + "load_dotenv()\n", + "# Initialize the client\n", + "google_api_key = os.getenv(\"GOOGLE_API_KEY\")\n", + "\n", + "from langchain_google_genai import ChatGoogleGenerativeAI\n", + "\n", + "# Define the search tool for Gemini 3\n", + "# In 2026, 'google_search' is a built-in tool type\n", + "google_search_tool = {\"google_search\": {}}\n", + "\n", + "# Initializing your LLM with the search tool bound\n", + "_llm = ChatGoogleGenerativeAI(\n", + " model=\"gemini-3-flash-preview\", \n", + " api_key=google_api_key, # Updated keyword\n", + " temperature=1.0 # Optimized for web grounding\n", + ").bind_tools([google_search_tool])\n", + "\n", + "# Assuming you have the PortfolioUpdate class from our previous conversation\n", + "structured_search_chain = _llm.with_structured_output(PortfolioUpdate)\n", + "\n", + "# This will search the web AND format the result into your class\n", + "result = structured_search_chain.invoke(\"Check the latest news for AAPL and TSLA from the last 24 hours.\")\n", + "\n", + "result" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Ticker: AAPL\n", + "Summary: Apple is set to report quarterly results today with analysts expecting record revenue driven by iPhone 17 demand and services growth. Evercore ISI reiterated a Buy rating ahead of the report, while options traders anticipate a 4% price move.\n", + "Date: Last 24 hours\n", + "\n", + "Ticker: NVDA\n", + "Summary: NVIDIA is facing congressional scrutiny over technical support provided to DeepSeek. The company is also reportedly considering shifting some production to Intel by 2028 and has invested $2 billion with CoreWeave to build AI factories.\n", + "Date: Last 24 hours\n", + "\n", + "Ticker: TSLA\n", + "Summary: Tesla shares rose 4% after-hours as the company announced it will stop producing Model S and Model X vehicles to focus on the Optimus humanoid robot. The company reported revenue of $24.9B-$25.7B and plans to invest $2 billion into xAI.\n", + "Date: Last 24 hours\n", + "\n", + "Ticker: META\n", + "Summary: Meta shares surged up to 10% following a Q4 beat with $59.89 billion in revenue and $8.88 EPS. The company projected up to $135 billion in 2026 capital expenditures for AI and signed a $6 billion fiber optic deal with Corning.\n", + "Date: Last 24 hours\n", + "\n" + ] + } + ], + "source": [ + "from langchain_google_genai import ChatGoogleGenerativeAI\n", + "from langchain_core.prompts import ChatPromptTemplate\n", + "\n", + "# 1. THE SEARCHER: Enable web search tool\n", + "search_llm = ChatGoogleGenerativeAI(\n", + " model=\"gemini-3-flash-preview\", \n", + " api_key=google_api_key,\n", + " temperature=1.0\n", + ").bind_tools([{\"google_search\": {}}])\n", + "\n", + "# 2. THE FORMATTER: Native JSON mode (no tools allowed here)\n", + "structured_llm = ChatGoogleGenerativeAI(\n", + " model=\"gemini-3-flash-preview\",\n", + " api_key=google_api_key\n", + ").with_structured_output(PortfolioUpdate, method=\"json_schema\")\n", + "\n", + "def get_grounded_portfolio_news(query: str):\n", + " # Step A: Perform the search\n", + " # Gemini will browse the web and return a 'grounded' text response\n", + " raw_news = search_llm.invoke(query)\n", + " \n", + " # Step B: Feed the grounded text into the structured parser\n", + " # We explicitly tell the model to use the gathered info\n", + " return structured_llm.invoke(\n", + " f\"Using this verified news data: {raw_news.content}\\n\\n\"\n", + " f\"Format the following into the JSON structure: {query}\"\n", + " )\n", + "\n", + "# Execution\n", + "tickers = ['AAPL', 'NVDA', 'TSLA', 'META']\n", + "result = get_grounded_portfolio_news(f\"What is the latest 24h news for {tickers}?\")\n", + "for item in result.items:\n", + " print(f\"Ticker: {item.ticker}\")\n", + " print(f\"Summary: {item.news_summary}\")\n", + " print(f\"Date: {item.date}\\n\")" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: google-genai in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (1.61.0)\n", + "Requirement already satisfied: anyio<5.0.0,>=4.8.0 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from google-genai) (4.11.0)\n", + "Requirement already satisfied: google-auth<3.0.0,>=2.47.0 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from google-auth[requests]<3.0.0,>=2.47.0->google-genai) (2.48.0)\n", + "Requirement already satisfied: httpx<1.0.0,>=0.28.1 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from google-genai) (0.28.1)\n", + "Requirement already satisfied: pydantic<3.0.0,>=2.9.0 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from google-genai) (2.12.3)\n", + "Requirement already satisfied: requests<3.0.0,>=2.28.1 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from google-genai) (2.32.5)\n", + "Requirement already satisfied: tenacity<9.2.0,>=8.2.3 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from google-genai) (9.1.2)\n", + "Requirement already satisfied: websockets<15.1.0,>=13.0.0 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from google-genai) (15.0.1)\n", + "Requirement already satisfied: typing-extensions<5.0.0,>=4.11.0 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from google-genai) (4.15.0)\n", + "Requirement already satisfied: distro<2,>=1.7.0 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from google-genai) (1.9.0)\n", + "Requirement already satisfied: sniffio in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from google-genai) (1.3.1)\n", + "Requirement already satisfied: idna>=2.8 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from anyio<5.0.0,>=4.8.0->google-genai) (3.11)\n", + "Requirement already satisfied: pyasn1-modules>=0.2.1 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from google-auth<3.0.0,>=2.47.0->google-auth[requests]<3.0.0,>=2.47.0->google-genai) (0.4.2)\n", + "Requirement already satisfied: cryptography>=38.0.3 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from google-auth<3.0.0,>=2.47.0->google-auth[requests]<3.0.0,>=2.47.0->google-genai) (46.0.3)\n", + "Requirement already satisfied: rsa<5,>=3.1.4 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from google-auth<3.0.0,>=2.47.0->google-auth[requests]<3.0.0,>=2.47.0->google-genai) (4.9.1)\n", + "Requirement already satisfied: certifi in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from httpx<1.0.0,>=0.28.1->google-genai) (2025.10.5)\n", + "Requirement already satisfied: httpcore==1.* in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from httpx<1.0.0,>=0.28.1->google-genai) (1.0.9)\n", + "Requirement already satisfied: h11>=0.16 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from httpcore==1.*->httpx<1.0.0,>=0.28.1->google-genai) (0.16.0)\n", + "Requirement already satisfied: annotated-types>=0.6.0 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from pydantic<3.0.0,>=2.9.0->google-genai) (0.7.0)\n", + "Requirement already satisfied: pydantic-core==2.41.4 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from pydantic<3.0.0,>=2.9.0->google-genai) (2.41.4)\n", + "Requirement already satisfied: typing-inspection>=0.4.2 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from pydantic<3.0.0,>=2.9.0->google-genai) (0.4.2)\n", + "Requirement already satisfied: charset_normalizer<4,>=2 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from requests<3.0.0,>=2.28.1->google-genai) (3.4.4)\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from requests<3.0.0,>=2.28.1->google-genai) (2.3.0)\n", + "Requirement already satisfied: pyasn1>=0.1.3 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from rsa<5,>=3.1.4->google-auth<3.0.0,>=2.47.0->google-auth[requests]<3.0.0,>=2.47.0->google-genai) (0.6.1)\n", + "Requirement already satisfied: cffi>=2.0.0 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from cryptography>=38.0.3->google-auth<3.0.0,>=2.47.0->google-auth[requests]<3.0.0,>=2.47.0->google-genai) (2.0.0)\n", + "Requirement already satisfied: pycparser in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from cffi>=2.0.0->cryptography>=38.0.3->google-auth<3.0.0,>=2.47.0->google-auth[requests]<3.0.0,>=2.47.0->google-genai) (2.23)\n", + "Note: you may need to restart the kernel to use updated packages.\n" + ] + } + ], + "source": [ + "pip install -U google-genai" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "As of **May 23, 2024**, the P/E (Price-to-Earnings) ratio for Apple Inc. (AAPL) is approximately:\n", + "\n", + "* **Trailing P/E (TTM): 29.11**\n", + "* **Forward P/E (Estimated next 12 months): 28.25**\n", + "\n", + "### Key Data Points:\n", + "* **Current Stock Price:** ~$186.88\n", + "* **Earnings Per Share (TTM):** $6.42\n", + "* **Market Cap:** ~$2.87 Trillion\n", + "\n", + "### Context:\n", + "Apple's P/E ratio has seen a slight expansion recently following its Q2 earnings report (released May 2), which included a record $110 billion share buyback announcement and a dividend increase. \n", + "\n", + "A P/E of around 29 is currently higher than Apple’s 5-year historical average (which sits closer to 25–26), suggesting that investors are currently paying a premium for the stock, likely in anticipation of upcoming AI announcements at WWDC in June.\n", + "\n", + "***Note:** Because stock prices and earnings estimates change throughout the trading day, these numbers fluctuate in real-time. You can find the most up-to-the-minute data on sites like Google Finance, Yahoo Finance, or CNBC.*\n" + ] + } + ], + "source": [ + "from google import genai\n", + "import os\n", + "\n", + "client = genai.Client(api_key=os.getenv(\"GOOGLE_API_KEY\"))\n", + "response = client.models.generate_content(\n", + " model=\"gemini-3-flash-preview\",\n", + " contents=\"What is the current P/E ratio for AAPL?\"\n", + ")\n", + "print(response.text)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Available Gemini Models:\n", + "- models/gemini-2.5-flash\n", + "- models/gemini-2.5-pro\n", + "- models/gemini-2.0-flash\n", + "- models/gemini-2.0-flash-001\n", + "- models/gemini-2.0-flash-exp-image-generation\n", + "- models/gemini-2.0-flash-lite-001\n", + "- models/gemini-2.0-flash-lite\n", + "- models/gemini-exp-1206\n", + "- models/gemini-2.5-flash-preview-tts\n", + "- models/gemini-2.5-pro-preview-tts\n", + "- models/gemma-3-1b-it\n", + "- models/gemma-3-4b-it\n", + "- models/gemma-3-12b-it\n", + "- models/gemma-3-27b-it\n", + "- models/gemma-3n-e4b-it\n", + "- models/gemma-3n-e2b-it\n", + "- models/gemini-flash-latest\n", + "- models/gemini-flash-lite-latest\n", + "- models/gemini-pro-latest\n", + "- models/gemini-2.5-flash-lite\n", + "- models/gemini-2.5-flash-image\n", + "- models/gemini-2.5-flash-preview-09-2025\n", + "- models/gemini-2.5-flash-lite-preview-09-2025\n", + "- models/gemini-3-pro-preview\n", + "- models/gemini-3-flash-preview\n", + "- models/gemini-3-pro-image-preview\n", + "- models/nano-banana-pro-preview\n", + "- models/gemini-robotics-er-1.5-preview\n", + "- models/gemini-2.5-computer-use-preview-10-2025\n", + "- models/deep-research-pro-preview-12-2025\n", + "- models/embedding-001\n", + "- models/text-embedding-004\n", + "- models/gemini-embedding-001\n", + "- models/aqa\n", + "- models/imagen-4.0-generate-preview-06-06\n", + "- models/imagen-4.0-ultra-generate-preview-06-06\n", + "- models/imagen-4.0-generate-001\n", + "- models/imagen-4.0-ultra-generate-001\n", + "- models/imagen-4.0-fast-generate-001\n", + "- models/veo-2.0-generate-001\n", + "- models/veo-3.0-generate-001\n", + "- models/veo-3.0-fast-generate-001\n", + "- models/veo-3.1-generate-preview\n", + "- models/veo-3.1-fast-generate-preview\n", + "- models/gemini-2.5-flash-native-audio-latest\n", + "- models/gemini-2.5-flash-native-audio-preview-09-2025\n", + "- models/gemini-2.5-flash-native-audio-preview-12-2025\n" + ] + } + ], + "source": [ + "from google import genai\n", + "import os\n", + "\n", + "# Initialize the client\n", + "client = genai.Client(api_key=os.environ.get(\"GOOGLE_API_KEY\"))\n", + "\n", + "# Fetch and print the list of models\n", + "print(\"Available Gemini Models:\")\n", + "for model in client.models.list():\n", + " print(f\"- {model.name}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Collecting exa-py\n", + " Downloading exa_py-2.3.0-py3-none-any.whl.metadata (3.4 kB)\n", + "Requirement already satisfied: httpcore>=1.0.9 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from exa-py) (1.0.9)\n", + "Requirement already satisfied: httpx>=0.28.1 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from exa-py) (0.28.1)\n", + "Requirement already satisfied: openai>=1.48 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from exa-py) (2.7.1)\n", + "Requirement already satisfied: pydantic>=2.10.6 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from exa-py) (2.12.3)\n", + "Requirement already satisfied: python-dotenv>=1.0.1 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from exa-py) (1.2.1)\n", + "Requirement already satisfied: requests>=2.32.3 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from exa-py) (2.32.5)\n", + "Requirement already satisfied: typing-extensions>=4.12.2 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from exa-py) (4.15.0)\n", + "Requirement already satisfied: certifi in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from httpcore>=1.0.9->exa-py) (2025.10.5)\n", + "Requirement already satisfied: h11>=0.16 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from httpcore>=1.0.9->exa-py) (0.16.0)\n", + "Requirement already satisfied: anyio in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from httpx>=0.28.1->exa-py) (4.11.0)\n", + "Requirement already satisfied: idna in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from httpx>=0.28.1->exa-py) (3.11)\n", + "Requirement already satisfied: distro<2,>=1.7.0 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from openai>=1.48->exa-py) (1.9.0)\n", + "Requirement already satisfied: jiter<1,>=0.10.0 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from openai>=1.48->exa-py) (0.11.1)\n", + "Requirement already satisfied: sniffio in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from openai>=1.48->exa-py) (1.3.1)\n", + "Requirement already satisfied: tqdm>4 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from openai>=1.48->exa-py) (4.67.1)\n", + "Requirement already satisfied: annotated-types>=0.6.0 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from pydantic>=2.10.6->exa-py) (0.7.0)\n", + "Requirement already satisfied: pydantic-core==2.41.4 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from pydantic>=2.10.6->exa-py) (2.41.4)\n", + "Requirement already satisfied: typing-inspection>=0.4.2 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from pydantic>=2.10.6->exa-py) (0.4.2)\n", + "Requirement already satisfied: charset_normalizer<4,>=2 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from requests>=2.32.3->exa-py) (3.4.4)\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in /Users/youssefaitousarrah/miniconda3/envs/tradingagents/lib/python3.13/site-packages (from requests>=2.32.3->exa-py) (2.3.0)\n", + "Downloading exa_py-2.3.0-py3-none-any.whl (62 kB)\n", + "Installing collected packages: exa-py\n", + "Successfully installed exa-py-2.3.0\n", + "Note: you may need to restart the kernel to use updated packages.\n" + ] + } + ], + "source": [ + "pip install exa-py" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "SNDK Stock Price Quote & News - Sandisk Corporation - Robinhood https://robinhood.com/us/en/stocks/SNDK/\n", + "​​Sorry, Micron: Smart Money Is Moving To Sandisk (Here's Why) (NASDAQ:SNDK) https://seekingalpha.com/article/4864909-sorry-micron-smart-money-is-moving-to-sandisk-heres-why\n", + "SNDK: Sandisk Corp - Stock Price, Quote and News - CNBC https://www.cnbc.com/quotes/SNDK\n", + "Sandisk Celebrates Nasdaq Listing After Completing Separation from Western Digital | Sandisk https://www.sandisk.com/company/newsroom/press-releases/2025/sandisk-celebrates-nasdaq-listing-after-completing-separation\n", + "2025-02-24 | Sandisk Celebrates Nasdaq Listing After Completing Separation from Western Digital | NDAQ:SNDK | Press Release https://stockhouse.com/news/press-releases/2025/02/24/sandisk-celebrates-nasdaq-listing-after-completing-separation-from-western\n", + "Sandisk Celebrates Nasdaq Listing After Completing Separation from Western Digital https://www.sandisk.com/el-gr/company/newsroom/press-releases/2025/sandisk-celebrates-nasdaq-listing-after-completing-separation\n", + "Sandisk Celebrates Nasdaq Listing After Completing Separation | Sandisk https://shop.sandisk.com/en-se/company/newsroom/press-releases/2025/sandisk-celebrates-nasdaq-listing-after-completing-separation\n", + " https://www.sandisk.com/content/sndsk/ar-sa/company/newsroom/press-releases/2025/sandisk-celebrates-nasdaq-listing-after-completing-separation\n", + " https://www.sandisk.com/en-in/company/newsroom/press-releases/2025/sandisk-celebrates-nasdaq-listing-after-completing-separation\n", + " https://shop.sandisk.com/content/sandisk/en-us/company/newsroom/press-releases/2025/sandisk-celebrates-nasdaq-listing-after-completing-separation\n" + ] + } + ], + "source": [ + "from exa_py import Exa\n", + "\n", + "exa = Exa(api_key=\"aec6743f-8776-46bf-890b-8c68a5b9d759\")\n", + "\n", + "results = exa.search(\n", + " query=\"Latest news for SNDK\",\n", + " type=\"auto\",\n", + " num_results=10,\n", + " contents={\"text\":{\"max_characters\":20000}}\n", + ")\n", + "\n", + "for result in results.results:\n", + " print(result.title, result.url)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "SearchResponse(results=[Result(url='https://www.fool.com/investing/2026/02/01/sandisk-stock-up-1500-ai-buy-wall-street-invest/', id='https://www.fool.com/investing/2026/02/01/sandisk-stock-up-1500-ai-buy-wall-street-invest/', title='Sandisk Stock Is Up 1500% in the Past Year Due to AI', score=None, published_date='2026-02-01T19:22:17.406Z', author='Trevor Jennewine', image='https://g.foolcdn.com/image/?url=https%3A%2F%2Fg.foolcdn.com%2Feditorial%2Fimages%2F854042%2Finvestor-33.jpg&w=1200&op=resize', favicon='https://www.fool.com/favicon.ico', subpages=None, extras=None, entities=None, text='Sandisk Stock Is Up 1,500% in the Past Year Due to AI -- Is It Still a Buy? Wall Street Has a Surprising Answer for Investors. | The Motley Fool\\n[Accessibility Menu] \\nSearch for a company\\n[Accessibility]...[Help] \\n[![The Motley Fool]![The Motley Fool]] \\n[Top 10 Stocks to Buy Now ›] \\nBars\\n[\\nArrow-Thin-Down\\nS&P 500\\n6,939.03\\n-0.4%\\n-29.98\\n] \\n[\\nArrow-Thin-Down\\nDJI\\n48,892.47\\n-0.4%\\n-179.09\\n] \\n[\\nArrow-Thin-Down\\nNASDAQ\\n23,461.82\\n-0.9%\\n-223.30\\n] \\n[\\nArrow-Thin-Down\\nBitcoin\\n$77,434.00\\n-2.0%\\n-$1,550.47\\n] \\n[\\nArrow-Thin-Down\\nTCGL\\n$172.84\\n+100.1%\\n+$86.48\\n] \\n[\\nArrow-Thin-Down\\nSNDK\\n$576.25\\n+6.9%\\n+$36.95\\n] \\n[\\nArrow-Thin-Down\\nVZ\\n$44.52\\n+11.8%\\n+$4.71\\n] \\n[\\nArrow-Thin-Down\\nDECK\\n$119.34\\n+19.5%\\n+$19.44\\n] \\n[\\nArrow-Thin-Down\\nRKT\\n$17.93\\n-13.7%\\n-$2.84\\n] \\n[\\nArrow-Thin-Down\\nU\\n$29.03\\n-24.4%\\n-$9.37\\n] \\n[\\nArrow-Thin-Down\\nAMZN\\n$239.26\\n-1.0%\\n-$2.47\\n] \\n[\\nArrow-Thin-Down\\nGOOG\\n$338.53\\n-0.0%\\n-$0.13\\n] \\n[\\nArrow-Thin-Down\\nMETA\\n$716.50\\n-3.0%\\n-$21.81\\n] \\n[\\nArrow-Thin-Down\\nMSFT\\n$430.29\\n-0.7%\\n-$3.21\\n] \\n[\\nArrow-Thin-Down\\nNVDA\\n$191.12\\n-0.7%\\n-$1.39\\n] \\n[\\nArrow-Thin-Down\\nTSLA\\n$430.62\\n+3.4%\\n+$14.06\\n] \\n[Daily Stock Gainers] [Daily Stock Losers] [Most Active Stocks] \\narrow-leftarrow-right\\n[Daily Stock Gainers] [Daily Stock Losers] [Most Active Stocks] \\n# Sandisk Stock Is Up 1,500% in the Past Year Due to AI -- Is It Still a Buy? Wall Street Has a Surprising Answer for Investors.\\nBy[Trevor Jennewine] –Feb 1, 2026 at 3:24AM EST\\n## Key Points\\n* Among Wall Street analysts, Sandisk stock has a median target price of $690 per share. That implies 20% upside from its current share price of $576.\\n* Sandisk is gaining market share in NAND flash memory, and it has benefited from a severe supply shortage caused by increased construction of AI infrastructure.\\n* Wall Street expects Sandisk's earnings to increase at 156% annually through the fiscal year ending in June 2027, which makes the current valuation look reasonable.\\n* ## [NASDAQ:SNDK] \\n### Sandisk\\n![Sandisk Stock Quote] \\nMarket Cap\\n$84B\\nToday's Change\\nangle-down\\n(6.85%)\\xa0$36.95\\nCurrent\\xa0Price\\n$576.25\\nPrice as ofJanuary 30, 2026 at 4:00 PMET\\nSandisk stock increased 16x in the past year as demand for artificial intelligence infrastructure led to a severe supply shortage in memory chips and storage devices.\\nSemiconductor company**Sandisk**([SNDK] +6.85%)is one of the hottest artificial intelligence (AI) trades on the market. The stock led the**S&P 500**higher in 2025, its price increasing more than sixfold, and it has more than doubled in 2026. That brings the total return to 1,500% since the company was spun off from**Western Digital**last February.\\nInterestingly, Wall Street analysts generally see Sandisk as undervalued. As of Jan. 30, the stock trades at $576 per share.\\n* The highest target price of $1,000 per share implies 73% upside.\\n* The median target price of $690 per share implies 20% upside.\\n* The lowest target price of $235 per share implies 59% downside.\\nImportantly, many analysts raised their target prices after Sandisk delivered exceptional earnings results on Jan. 29. Prior to the report, the median target price had been $400 per share. So, Wall Street is having trouble keeping pace with this AI stock. Is it too late to buy?\\n![A person in a suit wears a thoughtful expression while looking at a personal computer.] \\nImage source: Getty Images.\\n## Sandisk is gaining market share in flash memory\\nSandisk is a[semiconductor company] that designs and manufactures data storage solutions based on NAND flash technology for data centers and edge devices (e.g., automotive systems, personal computers, smartphones, gaming consoles). Its products include USB flash drives and solid state drives (SSDs) in external, internal, and embedded form factors.\\nCentral to Sandisk's business is a joint venture with Japanese manufacturer Kioxia.\\xa0Both companies realize cost efficiencies and supply chain security by sharing[research and development (R&D) expenses] and[capital expenditures] related to process technology (i.e., the multistep manufacturing process used to transform raw silicon into chips) and memory chip design.\\nSandisk has another important advantage in[vertical integration]. The company not only manufactures memory wafers through its joint venture but also packages the wafers into chips and integrates the chips into final products, such as SSDs. That lets Sandisk optimize the performance and reliability of its storage devices in ways that most other suppliers cannot.\\nSandisk is the fifth-largest player in the NAND flash memory market. But the company gained 2 percentage points of market share during the 12-month period that ended in September 2025, while industry leaders**Samsung**and**SK Hynix**lost market share. Sandisk is likely to maintain that momentum because several[hyperscalers] are testing its enterprise SSDs.\\nExpand\\n![Sandisk Stock Quote] \\n## [NASDAQ:SNDK] \\nSandisk\\nToday's Change\\n(6.85%) $36.95\\nCurrent Price\\n$576.25\\n### Key Data Points\\nMarket Cap\\n$84B\\nDay's Range\\n$533.00- $676.69\\n52wk Range\\n$27.89- $676.69\\nVolume\\n210K\\nAvg Vol\\n14M\\nGross Margin\\n34.81%\\n## Sandisk is growing quickly due to a memory chip supply shortage\\nDemand for[artificial intelligence (AI)] infrastructure has led to an unprecedented supply shortage in memory chips needed to produce SSDs and DRAM (dynamic random access memory). "Prices for memory shot up 50% in the last quarter of 2025 and are projected to increase another 40% to 50% by the end of the first quarter of 2026," according to*The Wall Street Journal*.\\nSandisk has been a major beneficiary. The company reported exceptional financial results in the second quarter of fiscal 2026 (ended Jan. 2). Revenue increased 61% to $3 billion on especially strong sales growth in the data center segment. Meanwhile,[non-GAAP (generally accepted accounting principles)] earnings increased 404% to $6.20 per diluted share.\\nManagement's third-quarter guidance\\xa0also beat Wall Street's estimates, calling for revenue of $4.6 billion and non-GAAP net income of $13.00 per diluted share at the midpoint. If that is accurate, earnings will more than double versus the prior quarter. "We continue to see customer demand well above supply beyond calendar year 2026," CEO David Goeckeler told analysts.\\n## Sandisk stock trades at a reasonable valuation, but the chip industry is cyclical\\nWall Street estimates Sandisk's adjusted earnings will grow at 156% annually through the fiscal year ending in June 2027. That makes the current valuation of 80 times earnings look quite reasonable, which itself is an argument for buying the stock.\\nHowever, this situation is complicated because semiconductor sales tend to be cyclical, meaning the market oscillates between supply shortages and supply gluts. At some point, the market will probably afford Sandisk a much lower price-to-earnings multiple because investors will anticipate that shift.\\nTherein lies the danger. It is impossible to predict when the market will look beyond the supply shortage -- it may happen in the next few months, or it may not happen for more than a year -- but when that day comes, Sandisk shares could decline sharply. Investors who are comfortable with that risk can buy a small position.\\n## Read Next\\n[![SNDK stock]] \\nJan 30, 2026•By[Joe Tenebruso] \\n[Why Sandisk Stock Popped Today] \\n[![Young person at a desk using a PC and tablet computer simultaneously]] \\nJan 30, 2026•By[Eric Volkman] \\n[Why Sandisk Stock Rocked the Market This Week] \\n[![shocked surprised person adjusts glasses in office]] \\nJan 24, 2026•By[Jon Quast] \\n[Sandisk Stock Shows Why Investing Is Hard] \\n[![diverse business team data servers IT (1)]] \\nJan 23, 2026•By[Stefon Walters] \\n[Up 1,000% in Less Than a Year, Is This AI Stock a Buy to Start 2026?] \\n[![Investor 43]] \\nJan 22, 2026•By[Trevor Jennewine] \\n[Billionaire Ken Griffin Sells Sandisk Stock and Buys a Quantum Stock Up 1,900% Since Early 2023] \\n[![GettyImages-469753498]] \\nJan 21, 2026•By[Patrick Sanders] \\n[Is Sandisk the Smartest Investment You Can Make Today?] \\n### About the Author\\n![Trevor Jennewine] \\nTrevor Jennewine is a contributing Motley Fool stock market analyst covering technology, cryptocurrency, and investment planning. Prior to The Motley Fool, Trevor managed several pharmacies. He holds a doctor of pharmacy degree from Oregon State University, a master’s degree in business administration from Miami University, and a bachelor’s degree in biology from Miami University.\\n[TMFphoenix12] \\nX[@tjennewine1] \\n### Stocks Mentioned\\n[\\n![Sandisk Stock Quote] \\n#### Sandisk\\nNASDAQ:SNDK\\n$576.25(+0.07%)$+36.95\\n] \\n\\\\*Average returns of all recommendations since inception. Cost basis and return based on previous market day close.\\n## Premium Investing Services\\nInvest better with The Motley Fool. Get stock recommendations, portfolio guidance, and more from The Motley Fool's premium services.\\n[View Premium Services]', summary=None, highlights=None, highlight_scores=None), Result(url='https://robinhood.com/us/en/stocks/SNDK/', id='https://robinhood.com/us/en/stocks/SNDK/', title='SNDK Stock Price Quote & News - Sandisk Corporation', score=None, published_date='2026-02-02T00:22:17.406Z', author=None, image='https://cdn.robinhood.com/assets/robinhood/shared/robinhood-preview_v2.png', favicon='https://robinhood.com/us/en/rh_favicon_32.png?v=2024', subpages=None, extras=None, entities=None, text='Sandisk Corporation: SNDK Stock Price Quote & News | Robinhood\\n# Sandisk Corporation\\n\\u200c\\u200c\\u200c1D\\n1W\\n1M\\n3M\\nYTD\\n1Y\\n5Y\\nALL\\n![] \\n#### Trade Sandisk Corporation 24 hours a day, five days a week on Robinhood.\\nRobinhood gives you the tools to revolutionize your trading experience. Use the streamlined mobile app, or access advanced charts and execute precise trades on our browser-based platform,[Robinhood Legend]. Risks and limitations apply.\\n[Sign up and join over 25 million investors.] \\n## About SNDK\\n### Sandisk Corp. engages in the development, manufacture, and provision of storage devices and solutions based on NAND flash technology. Its products include solid state drives, memory cards, and USB flash drives.Show more\\nCEO\\nDavid V. Goeckeler\\nCEODavid V. Goeckeler\\nEmployees\\n11,000\\nEmployees11,000\\nHeadquarters\\nMilpitas, California\\nHeadquartersMilpitas, California\\nFounded\\n2024\\nFounded2024\\nEmployees\\n11,000\\nEmployees11,000\\n## SNDK Key Statistics\\nMarket cap\\n84.42B\\nMarket cap84.42B\\nPrice-Earnings ratio\\n-75.95\\nPrice-Earnings ratio-75.95\\nDividend yield\\n—Dividend yield—\\nAverage volume\\n20.45M\\nAverage volume20.45M\\nHigh today\\n$676.69\\nHigh today$676.69\\nLow today\\n$533.00\\nLow today$533.00\\nOpen price\\n$651.34\\nOpen price$651.34\\nVolume\\n40.93M\\nVolume40.93M\\n52 Week high\\n$676.69\\n52 Week high$676.69\\n52 Week low\\n$27.89\\n52 Week low$27.89\\n## Stock Snapshot\\nSandisk Corporation(SNDK) stock is priced at $569.00, giving the company a market capitalization of 84.42B. It carries a P/E multiple of -75.95.\\nDuring the trading session on 2026-02-02, Sandisk Corporation(SNDK) shares reached a daily high of $676.69 and a low of $533.00. At a current price of $569.00, the stock is +6.8% higher than the low and still -15.9% under the high.\\nTrading activity shows a volume of 40.93M, compared to an average daily volume of 20.45M.\\nThe stock's 52-week range extends from a low of $27.89 to a high of $676.69.\\nThe stock's 52-week range extends from a low of $27.89 to a high of $676.69.\\nSee More\\n## SNDK News\\n[\\nTipRanks11h\\nSanDisk Corp Earnings Call Highlights Powerful Upswing\\nSanDisk Corp ((SNDK)) has held its Q2 earnings call. Read on for the main highlights of the call. Claim 50% Off TipRanks Premium Unlock hedge fund-level data an...\\n] [\\nThe Motley Fool1d\\nSandisk Stock Is Up 1,500% in the Past Year Due to AI -- Is It Still a Buy? Wall Street Has a Surprising Answer for Investors.\\nSandisk stock increased 16x in the past year as demand for artificial intelligence infrastructure led to a severe supply shortage in memory chips and storage de...\\n![Sandisk Stock Is Up 1,500% in the Past Year Due to AI -- Is It Still a Buy? Wall Street Has a Surprising Answer for Investors.] \\n] [\\nSimply Wall St2d\\nWhy Sandisk Is Up 21.6% After AI-Driven Beat And Extended Kioxia Capacity Pact\\nIn late January 2026, Sandisk reported past second-quarter results showing sharply higher sales and profits and issued upbeat third-quarter revenue guidance, ci...\\n![Why Sandisk Is Up 21.6% After AI-Driven Beat And Extended Kioxia Capacity Pact] \\n] \\n## Analyst ratings\\n## 68%\\nof 25 ratings\\nBuy\\n68%\\nHold\\n32%\\nSell\\n0%\\n## More SNDK News\\n[\\nThe Motley Fool2d\\nWhy Sandisk Stock Popped Today\\nThe flash storage leader's profits crushed Wall Street's expectations.\\nShares of Sandisk (SNDK +6.85%) climbed on Friday after the data storage device maker an...\\n![Why Sandisk Stock Popped Today] \\n] [\\nThe Motley Fool2d\\nWhy Sandisk Stock Rocked the Market This Week\\nIt's absolutely the right time to be in the digital storage business.\\nAccording to data compiled by S&P Global Market Intelligence, Sandisk (SNDK +6.60%) was a...\\n![Why Sandisk Stock Rocked the Market This Week] \\n] [\\nBenzinga3d\\nThese 10 Stocks Just Had Their Best Or Worst Month Ever —And You Might Not Know Why\\nThe first month of 2026 is drawing to a close, and a cluster of U.S. stocks has just posted their best —or worst —monthly performances on record, driven by a...\\n![These 10 Stocks Just Had Their Best Or Worst Month Ever —And You Might Not Know Why] \\n] [\\nSimply Wall St3d\\nSandisk Extends Kioxia JV As AI Deals Reshape Growth And Valuation\\nSandisk extended its manufacturing joint venture with Kioxia through 2034, securing access to advanced 3D flash capacity for AI and data center customers.\\nThe...\\n![Sandisk Extends Kioxia JV As AI Deals Reshape Growth And Valuation] \\n] [\\nBarron's3d\\nUp 1,600%, Is Sandisk the Best Spinoff Ever? Five More to Watch\\nTechnology Up 1,600%, Is Sandisk the Best Spinoff Ever? Five More to Watch In this article SNDK WDC SPX DJIA Sandisk’s explosive post-spinoff rally is drawing a...\\n![Up 1,600%, Is Sandisk the Best Spinoff Ever? Five More to Watch] \\n] [\\nTipRanks3d\\nSanDisk rises 15.1%\\nSanDisk (SNDK) is up 15.1%, or $81.29 to $620.59.\\nPublished first on TheFly –the ultimate source for real-time, market-moving breaking financial news. Try Now...\\n] [\\nMarketWatch3d\\nSandisk’s stock gets ‘one of the most delayed upgrades in history’ after blowout earnings\\nMissed out on Sandisk’s 1,400% stock rally? The company’s blockbuster earnings suggest to analysts that there’s more room for shares to run higher.\\nSandisk SND...\\n![Sandisk’s stock gets ‘one of the most delayed upgrades in history’ after blowout earnings] \\n] \\n## People also own\\nBased on the portfolios of people who own SNDK. This list is generated using Robinhood data, and it’s not a recommendation.\\n[\\nWestern Digital\\n] \\n[\\nMicron Technology\\n] \\n[\\nSeagate Technology\\n] \\n[\\nBroadcom\\n] \\n[\\nTaiwan Semiconductor Manu…\\n] \\n[\\nAMD\\n] \\n## Similar Marketcap\\nThis list is generated by looking at the six larger and six smaller companies by market cap in relation to this company.\\n[\\nING1.56%\\n] [\\nMAR1.37%\\n] [\\nNGG0.22%\\n] [\\nAMT1.12%\\n] [\\nHWM0.35%\\n] [\\nNU5.26%\\n] [\\nWDC10.09%\\n] [\\nCM2.83%\\n] [\\nORLY0.45%\\n] [\\nEMR2.49%\\n] [\\nBK1.31%\\n] [\\nFCX7.50%\\n] \\n## Popular Stocks\\nThis list is generated by looking at the top 100 stocks and ETFs most commonly held by Robinhood customers and showing a random subset\\n[\\nRGTI8.41%\\n] [\\nUNH1.78%\\n] [\\nSPMO0.51%\\n] [\\nAVGO0.21%\\n] [\\nARKK3.54%\\n] [\\nSPHQ1.40%\\n] [\\nLCID2.34%\\n] [\\nPFE1.36%\\n] [\\nNOK2.15%\\n] [\\nBRK.B0.86%\\n] [\\nVWO2.03%\\n] [\\nSOUN6.88%\\n] \\n## Newly Listed\\nThis list is generated by showing companies that recently went public.\\n[\\nVHUB73.62%\\n] [\\nMESH0.20%\\n] [\\nYSS1.37%\\n] [\\nDSAC0.70%\\n] [\\nLIFE10.65%\\n] [\\nBBCQ0.10%\\n] [\\nPPHC0.00%\\n] [\\nKBON0.20%\\n] [\\nNWAX0.64%\\n] [\\nSAC0.00%\\n] [\\nAEAQ0.10%\\n] [\\nEQPT2.45%\\n] \\nBuy SNDK\\nInvest In\\nShares\\nShares\\nMarket Price\\n\\u200cCommissions\\n$0.00\\nEstimated Cost\\n$0.00\\nSign up for a Robinhood brokerage account to buy or sell SNDK stocks, ETFs, and their options commission-free. Other fees may apply. See Robinhood Financial's[fee schedule] to learn more.\\nSign Up to Buy\\nTrade SNDK Options\\nWatch SNDK', summary=None, highlights=None, highlight_scores=None), Result(url='https://www.shacknews.com/article/147642/sandisk-sndk-q2-2026-earnings-results', id='https://www.shacknews.com/article/147642/sandisk-sndk-q2-2026-earnings-results', title='Sandisk (SNDK) Q2 FY26 earnings results beat EPS and revenue expectations', score=None, published_date='2026-01-29T00:00:00.000Z', author='Shacknews', image='https://d1lss44hh2trtw.cloudfront.net/assets/article/2026/01/29/sandisk-sndk-q2-fy26-earnings-results-beat-eps-and-revenue-expectations_feature.jpg', favicon='https://d1lss44hh2trtw.cloudfront.net/deploy/www-28837a9/images/favicon/favicon-32x32.png', subpages=None, extras=None, entities=None, text='Sandisk (SNDK) Q2 FY26 earnings results beat EPS and revenue expectations | Shacknews\\nNew to Shacknews?[Signup for a Free Account] \\nAlready have an account?[Login Now] \\n\\uea66[2026 Video Game Release Dates Calendar] [Shacknews Hall of Fame: Class of 2025] [The Shacknews Awards 2025 nominees] [Shacknews Direct: Introducing Bubbletron!] \\n[Lola\\uea22] \\n* [\\ueb81Facebook] \\n* [\\ueb98Twitter] \\n* [\\ueb9eYoutube] \\n* [\\uead6Twitch] \\n* [\\ueb90Subscribe] \\n[![Shacknews Logo]] \\n[![Shacknews Logo]] \\n\\uea99* [\\uea5fTheme] \\n* [\\uec70Shackmaps] \\n* [\\uee52Latest Pets] \\n* [\\uebfaCortex] \\n* [\\ue923Log In] \\n* [\\uee70Forum] \\n* [\\uebc7Topics] \\n* [Reviews] \\n* [News] \\n* [Videos] \\n* [Guides] \\n* [Podcasts] \\n* [Features] \\n* [Long Reads] \\n* [\\uea66Search] \\n[\\n2026 Video Game Release Dates Calendar\\n] [\\nShacknews Hall of Fame: Class of 2025\\n] [\\nThe Shacknews Awards 2025 nominees\\n] [\\nShacknews Direct: Introducing Bubbletron!\\n] \\n![Sandisk (SNDK) Q2 FY26 earnings results beat EPS and revenue expectations] \\n* [News] \\n# Sandisk (SNDK) Q2 FY26 earnings results beat EPS and revenue expectations\\nThe popular consumer data storage company had a stellar quarter for revenue and EPS metrics on the skyrocketing demand for memory technology.\\n[![TJ Denzer]] \\n[TJ Denzer] \\nJanuary 29, 2026 1:45 PM\\n[Image via Sandisk] \\n[1] \\nThis week, Sandisk brought out its latest earnings results, and they were quite good to say the least. The company’s revenue and earnings-per-share (EPS) were well above expectations, marking a major win for the previous quarter.\\nSandisk released its Q2 FY26 results on its[investor relations website]. For the company’s revenue, it came in at $3.03 billion, which was more than enough to overcome analyst estimates set at $2.62 billion. Meanwhile, EPS shook out to an actual bottom line of $6.20 per share, well above estimates for 3.78 per share, as well as the $3.70 per share set in the Whisper Number.\\n![Sandisk (SNDK) stock chart in after-hours trading on January 29, 2026.] Sandisk (SNDK) stock was up in value in after-hours trading following the release of its Q2 FY26 results and Q3 FY26 guidance.\\nSandisk was in a great position this last quarter when demand for memory and RAM products skyrocketed. As Sandisk specializes in flash memory, USB flash drives, and SSD technology and products, it sold incredibly well alongside other computer memory and storage companies like it. The company particularly ended up in good position for this moment last year when it picked up full ownership of SSD and SAND division from fellow tech data[storage company Western Digital]. Sandisk is also confident in what’s ahead, forecasting that its Q3 FY26 earnings will double the stats given in Q2 FY26.\\nWith a solid quarter in the books and confidence in the time ahead, Sandisk looks to be in good shape for the back half of its fiscal year. Stay tuned to the[Sandisk topic] for more news and updates.\\n[![Senior News Editor]] \\n[TJ Denzer] \\nSenior News Editor\\nTJ Denzer is a player and writer with a passion for games that has dominated a lifetime. He found his way to the Shacknews roster in late 2019 and has worked his way to Senior News Editor since. Between news coverage, he also aides notably in livestream projects like the indie game-focused Indie-licious, the Shacknews Stimulus Games, and the Shacknews Dump. You can reach him at[tj.denzer@shacknews.com] and also find him on BlueSky[@JohnnyChugs].\\nFiled Under\\n* [Business] \\n* [Financial] \\n* [Earnings] \\n* [Technology] \\n* [Stock Market] \\n* [News] \\n* [ssd] \\n* [Sandisk] \\n* [Earnings Report] \\n* [Market News] \\n* [Memory chips] \\nFrom The Chatty\\n[Refresh] [Go To Thread] \\n* [Shacknews]![legacy 10 years]![legacy 20 years] \\n[\\ueaa3] [reply] \\nJanuary 29, 2026 1:45 PM\\nTJ Denzer posted a new article,[Sandisk (SNDK) Q2 FY26 earnings results beat EPS and revenue expectations] \\n[Login / Register] \\n![Hello, Meet Lola]', summary=None, highlights=None, highlight_scores=None), Result(url='https://www.cnbc.com/quotes/SNDK', id='https://www.cnbc.com/quotes/SNDK', title='SNDK: Sandisk Corp - Stock Price, Quote and News', score=None, published_date='2026-01-31T19:22:17.406Z', author=None, image='https://fm.cnbc.com/applications/cnbc.com/staticcontent/img/versant/cnbc_share_versant.png?v=1524171804', favicon='https://fm.cnbc.com/applications/cnbc.com/resources/img/CNBC_LOGO_FAVICON_1C_KO_RGB.ico', subpages=None, extras=None, entities=None, text=\"SNDK: Sandisk Corp - Stock Price, Quote and News - CNBC\\n[Skip Navigation] \\n[![CNBC]] \\n[Markets] \\n* [Pre-Markets] \\n* [U.S. Markets] \\n* [Currencies] \\n* [Cryptocurrency] \\n* [Futures & Commodities] \\n* [Bonds] \\n* [Funds & ETFs] \\n[Business] \\n* [Economy] \\n* [Finance] \\n* [Health & Science] \\n* [Media] \\n* [Real Estate] \\n* [Energy] \\n* [Climate] \\n* [Transportation] \\n* [Investigations] \\n* [Industrials] \\n* [Retail] \\n* [Wealth] \\n* [Sports] \\n* [Life] \\n* [Small Business] \\n[Investing] \\n* [Personal Finance] \\n* [Fintech] \\n* [Financial Advisors] \\n* [Options Action] \\n* [ETF Street] \\n* [Buffett Archive] \\n* [Earnings] \\n* [Trader Talk] \\n[Tech] \\n* [Cybersecurity] \\n* [AI] \\n* [Enterprise] \\n* [Internet] \\n* [Media] \\n* [Mobile] \\n* [Social Media] \\n* [CNBC Disruptor 50] \\n* [Tech Guide] \\n[Politics] \\n* [White House] \\n* [Policy] \\n* [Defense] \\n* [Congress] \\n* [Expanding Opportunity] \\n[Video] \\n* [Latest Video] \\n* [Full Episodes] \\n* [Livestream] \\n* [Live Audio] \\n* [Live TV Schedule] \\n* [CNBC Podcasts] \\n* [CEO Interviews] \\n* [CNBC Documentaries] \\n* [Digital Originals] \\n[Watchlist] \\n[Investing Club] \\n* [Trust Portfolio] \\n* [Analysis] \\n* [Trade Alerts] \\n* [Meeting Videos] \\n* [Homestretch] \\n* [Jim's Columns] \\n* [Education] \\n* [Subscribe] \\n![Join IC] \\n[PRO] \\n* [Pro News] \\n* [Josh Brown] \\n* [Mike Santoli] \\n* [Calls of the Day] \\n* [My Portfolio] \\n* [Livestream] \\n* [Full Episodes] \\n* [Stock Screener] \\n* [Market Forecast] \\n* [Options Investing] \\n* [Chart Investing] \\n* [Subscribe] \\n![Join Pro] \\n[Livestream] \\nMenu\\n* [Make It] \\n* select\\n* [USA] \\n* [INTL] \\n* [Livestream] \\nSearch quotes, news & videos**\\n* [Livestream] \\n[Watchlist] \\n[SIGN IN] \\n[Create free account] \\n[Markets] \\n[Business] \\n[Investing] \\n[Tech] \\n[Politics] \\n[Video] \\n[Watchlist] \\n[Investing Club] \\n![Join IC] \\n[PRO] \\n![Join Pro] \\n[Livestream] \\nMenu\\nNew 52 Week High Today\\n# Sandisk CorpSNDK:NASDAQ\\nEXPORT![download chart] \\nWATCHLIST+\\nRT Quote|Last NASDAQ LS, VOL From CTA|USD\\nLast | 1:15 PM EST\\n592.11![quote price arrow up] +52.81(+9.79%)\\nVolume\\n22,498,977\\n52 week range\\n27.89-676.69\\n![Loading...] \\n* Open651.23\\n* Day High676.69\\n* Day Low586.01\\n* Prev Close539.30\\n* 52 Week High676.69\\n* 52 Week High Date01/30/26\\n* 52 Week Low27.89\\n* 52 Week Low Date04/07/25\\n## Latest On Sandisk Corp\\nALL CNBCINVESTING CLUBPRO\\n* [] [Cramer says this Big Tech stock should be bought after its blowout quarter] 51 Min AgoCNBC.com\\n* [Sandisk stock soars 14% after blowout earnings report shows strong AI demand] 3 Hours AgoCNBC.com\\n* [] [Sandisk gets upgrade from Raymond James after blockbuster earnings] 3 Hours AgoCNBC.com\\n* [Jim Cramer's top 10 things to watch in the stock market Friday] 4 Hours AgoCNBC.com\\n* [] [Friday's biggest analyst calls: Nvidia, Apple, Tesla, Spotify, Broadcom, Southwest, AMD & more] 5 Hours AgoCNBC.com\\n* [Stocks making the biggest moves premarket: Apple, Chevron, KLA, Sandisk & more] 6 Hours AgoCNBC.com\\n* [Stocks making the biggest moves after hours: Apple, Robinhood, Sandisk and more] 20 Hours AgoCNBC.com\\n* [Sandisk stock pops more than 15% on better-than-expected quarterly results![CNBC Video]] 21 Hours AgoCNBC.com\\n* [] [These 2 stocks getting unfairly slammed in the software sector rout are buys] 23 Hours AgoCNBC.com\\n* [] [Jim Cramer's top 10 things to watch in the stock market Thursday] January 29, 2026CNBC.com\\n## Key Stats\\n* Market Cap86.776B\\n* Shares Out146.55M\\n* 10 Day Average Volume17.77M\\n* Dividend-\\n* Dividend Yield-\\n* Beta-\\nShow Ratios / Profitability & Events\\n## Latest On Sandisk Corp\\nALL CNBCINVESTING CLUBPRO\\n* [] [Cramer says this Big Tech stock should be bought after its blowout quarter] 51 Min AgoCNBC.com\\n* [Sandisk stock soars 14% after blowout earnings report shows strong AI demand] 3 Hours AgoCNBC.com\\n* [] [Sandisk gets upgrade from Raymond James after blockbuster earnings] 3 Hours AgoCNBC.com\\n* [Jim Cramer's top 10 things to watch in the stock market Friday] 4 Hours AgoCNBC.com\\n* [] [Friday's biggest analyst calls: Nvidia, Apple, Tesla, Spotify, Broadcom, Southwest, AMD & more] 5 Hours AgoCNBC.com\\n* [Stocks making the biggest moves premarket: Apple, Chevron, KLA, Sandisk & more] 6 Hours AgoCNBC.com\\n* [Stocks making the biggest moves after hours: Apple, Robinhood, Sandisk and more] 20 Hours AgoCNBC.com\\n* [Sandisk stock pops more than 15% on better-than-expected quarterly results![CNBC Video]] 21 Hours AgoCNBC.com\\n* [] [These 2 stocks getting unfairly slammed in the software sector rout are buys] 23 Hours AgoCNBC.com\\n* [] [Jim Cramer's top 10 things to watch in the stock market Thursday] January 29, 2026CNBC.com\\nSummaryNewsProfileEarningsPeersFinancialsOptions\\n### KEY STATS\\n* Open651.23\\n* Day High676.69\\n* Day Low586.01\\n* Prev Close539.30\\n* 52 Week High676.69\\n* 52 Week High Date01/30/26\\n* 52 Week Low27.89\\n* 52 Week Low Date04/07/25\\n* Market Cap86.776B\\n* Shares Out146.55M\\n* 10 Day Average Volume17.77M\\n* Dividend-\\n* Dividend Yield-\\n* Beta-\\n### RATIOS/PROFITABILITY\\n* EPS (TTM)-12.39\\n* P/E (TTM)-47.81\\n* Fwd P/E (NTM)23.71\\n* EBITDA (TTM)531.00M\\n* ROE (TTM)-16.18%\\n* Revenue (TTM)7.78B\\n* Gross Margin (TTM)27.93%\\n* Net Margin (TTM)-22.37%\\n* Debt To Equity (MRQ)14.40%\\n### EVENTS\\n* Earnings Date05/05/2026(est)\\n* Ex Div Date-\\n* Div Amount-\\n* Split Date-\\n* Split Factor-\\n## Content From Our Affiliates\\n* [SanDisk rises 15.1%] 3 Hours AgoTipRanks\\n* [Early notable gainers among liquid option names on January 30th] 3 Hours AgoTipRanks\\n* [Video: Markets point lower after Apple report, Warsh nomination news] 4 Hours AgoTipRanks\\n* [Morning Movers: SanDisk and Deckers Outdoor surge after quarterly results] 4 Hours AgoTipRanks\\n* [SanDisk price target raised to $850 from $390 at BofA] 5 Hours AgoTipRanks\\n### Related Video\\n![Sandisk stock pops more than 15% on better-than-expected quarterly results] \\nwatch now\\nVIDEO2:1702:17\\nSandisk stock pops more than 15% on better-than-expected quarterly results\\n[] \\n### Profile\\n[MORE] \\nSanDisk Corporation is a developer, manufacturer and provider of data storage devices and solutions based on NAND flash technology and has consumer brands and franchises globally. The Company's solutions include a range of solid state drives (SSDs) embedded products, removable cards, universal serial bus (USB) drives, and wafers and components. Its broad portfolio of technology and products addresses multiple end markets of cloud, client and consumer. Its cloud end market is...More\\nDavid Goeckeler\\nChief Executive Officer, Director\\nLuis Visoso\\nChief Financial Officer, Executive Vice President\\nAlper Ilkbahar\\nExecutive Vice President, Chief Technology Officer\\nAddress\\n951 Sandisk Drive\\nMilpitas, CA\\n95035\\nUnited States\\n[https://www.sandisk.com/]\", summary=None, highlights=None, highlight_scores=None), Result(url='https://www.investors.com/news/technology/sandisk-stock-rockets-memory-called-new-gold/', id='https://www.investors.com/news/technology/sandisk-stock-rockets-memory-called-new-gold/', title=\"Sandisk Rockets As Memory Stocks Called 'The New Gold'\", score=None, published_date='2026-01-30T19:22:17.406Z', author=\"Investor's Business Daily\", image='https://www.investors.com/wp-content/uploads/2026/01/IT-Sandisk-Optimus-GX-company.jpg', favicon='https://www.investors.com/wp-content/uploads/2023/10/ibd-favicon-20231010.png', subpages=None, extras=None, entities=None, text='Sandisk Stock Rockets As Memory Called ‘The New Gold’ | Investor's Business Daily\\n![] \\n* [Market Trend] \\n* [Market Trend] \\n* [The Big Picture] \\n* [Stock Market Data] \\n* [Stock Market Today] \\n* [New? Start Here] \\n* [ETF Market Strategy] \\n* [IBD Digital: 2 Months for $20] \\n* [Psychological Indicators] \\n* [Stock Lists] \\n* [Stock Lists] \\n* [**IBD 50**] \\n* [My Stock Lists] \\n* [Stocks Near A Buy Zone] \\n* [IBD ETF Indexes] \\n* [**IBD Sector Leaders**] \\n* [Stock Lists Update] \\n* [Relative Strength at New High] \\n* [IBD Data Tables] \\n* [**IBD Big Cap 20**] \\n* [Stocks On The Move] \\n* [Rising Profit Estimates] \\n* [IBD Digital: 2 Months for $20] \\n* [**IBD Long-Term Leaders**] \\n* [New Highs] \\n* [Stocks Funds Are Buying] \\n* [New? Start Here] \\n* [**IPO Leaders**] \\n* [Stock Spotlight] \\n* [Your Weekly Review] \\n* [Research] \\n* [Stock Research] \\n* [IBD Stock Checkup] \\n* [Investing Action Plan] \\n* [The Income Investor] \\n* [Stock Of The Day] \\n* [Earnings Preview] \\n* [IBD Stock Analysis] \\n* [Screen Of The Day] \\n* [Earnings Calendar] \\n* [Industry Snapshot] \\n* [IBD Charts] \\n* [Industry Themes] \\n* [IBD 50 Stocks To Watch] \\n* [Stock Screener] \\n* [The New America] \\n* [IBD Data Stories] \\n* [Swing Trading] \\n* [Best ETFs] \\n* [IBD Digital: 2 Months for $20] \\n* [Options] \\n* [Best Mutual Funds] \\n* [Premium Investing Tools] \\n* [MarketSurge] \\n* [IBD Live] \\n* [Leaderboard] \\n* [SwingTrader] \\n* [MarketDiem] \\n* [NEWS] \\n* [NEWS] \\n* [*e*IBD] \\n* [Cryptocurrency] \\n* [Technology] \\n* [Retirement] \\n* [Magnificent Seven Stocks] \\n* [Management] \\n* [Personal Finance] \\n* [Industry News Pages] \\n* [Special Reports] \\n* [ECONOMY] \\n* [Economic Calendar] \\n* [IBD Digital: 2 Months for $20] \\n* [Economic News] \\n* [Videos] \\n* [VIDEOS & PODCASTS] \\n* [IBD Live] \\n* [Investing With IBD Podcast] \\n* [How To Invest Videos] \\n* [Growth Stories Podcast] \\n* [Options Videos] \\n* [Online Courses] \\n* [Webinars] \\n* [IBD Digital: 2 Months for $20] \\n* [Learn] \\n* [How To Invest] \\n* [How To Invest In Stocks] \\n* [When To Sell Stocks] \\n* [3 Keys To Stock Investing] \\n* [Short Selling] \\n* [Stock Market Timing] \\n* [Swing Trading] \\n* [Tracking Stock Market Trends] \\n* [What Is Crypto] \\n* [How To Read Stock Charts] \\n* [Premium Online Courses] \\n* [How To Buy Stocks] \\n* Educational Resources\\n* [New To IBD] \\n* [Online Investing Courses] \\n* [Investor\\'s Corner] \\n* [12 Days Of Learning] \\n* [Investing With IBD Podcast] \\n* [Investing Infographics] \\n* [Events & Webinars] \\n* [Premium Workshops] \\n* [**IBD Moneyworks**] \\n* [MarketSurge] \\n* [IBD Live] \\n* [Leaderboard] \\n* [SwingTrader] \\n* [MarketDiem] \\n* [Store] \\n**BREAKING:[Stocks Test Key Levels; U.S. Government Enters Shutdown] **\\n* [![ibddigital]] \\n* [![marketsurge]] \\n* [![ibdlive]] \\n* [![leaderboard]] \\n* [![swingtrader]] \\n* [![marketdiem]] \\nStore\\n[Subscribe] Sign In\\nMy Subscriptions\\n[![Founder\\'s Club] Founder\\'s Club] \\n[![SwingTrader] SwingTrader] \\n[![Leaderboard] Leaderboard] \\n[![MarketSurge] MarketSurge] \\n[![eIBD] eIBD] \\n[![IBD Digital] IBD Digital] \\n[![IBD Live] IBD Live] \\n[\\nCustomer Center\\n] [\\nMy Stock Lists\\n] [\\nEmail Preferences\\n] [\\nHelp & Support\\n] Sign Out\\n[![ibd logo]] \\n* [Market Trend\\n] \\n* [Stock Lists\\n] \\n* [Research\\n] \\n* [News\\n] \\n* [Videos\\n] \\n* [Learn\\n] \\n* [Store\\n] \\nSearch stocks or keywords\\nSections\\nMy IBD\\nMarket Trend\\n[MARKET TREND] \\n[\\nThe Big Picture\\n] [\\nStock Market Today\\n] [\\nETF Market Strategy\\n] [\\nStock Market Data\\n] [\\nPsychological Indicators\\n] [\\nNew? Start Here\\n] [\\nIBD Digital: 2 Months for $20\\n] \\nStock Lists\\n[STOCK LISTS] \\n[\\nIBD 50\\n] [\\nIBD Sector Leaders\\n] [\\nIBD Big Cap 20\\n] [\\nIBD Long-Term Leaders\\n] [\\nIPO Leaders\\n] [\\nMy Stock Lists\\n] [\\nStock Lists Update\\n] [\\nStocks On The Move\\n] [\\nNew Highs\\n] [\\nStock Spotlight\\n] [\\nStocks Near Buy Zone\\n] [\\nRS Line At New High\\n] [\\nRising Profit Estimates\\n] [\\nStocks Funds Are Buying\\n] [\\nYour Weekly Review\\n] [\\nIBD ETF Indexes\\n] [\\nIBD Data Tables\\n] [\\nIBD Digital: 2 Months for $20\\n] [\\nNew? Start Here\\n] \\nResearch\\n[STOCK RESEARCH] \\n[\\nIBD Stock Checkup\\n] [\\nStock Of The Day\\n] [\\nScreen Of The Day\\n] [\\nIBD Charts\\n] [\\nStock Screener\\n] [\\nSwing Trading\\n] [\\nOptions\\n] [\\nInvesting Action Plan\\n] [\\nEarnings Preview\\n] [\\nEarnings Calendar\\n] [\\nIBD Industry Themes\\n] [\\nThe New America\\n] [\\nBest ETFs\\n] [\\nBest Mutual Funds\\n] [\\nThe Income Investor\\n] [\\nIBD Stock Analysis\\n] [\\nIndustry Snapshot\\n] [\\nIBD 50 Stocks To Watch\\n] [\\nIBD Data Stories\\n] [\\nIBD Digital: 2 Months for $20\\n] \\nNews\\n[NEWS] \\n[\\neIBD\\n] [\\nTechnology\\n] [\\nMagnificent Seven Stocks\\n] [\\nPersonal Finance\\n] [\\nSpecial Reports\\n] [\\nCryptocurrency\\n] [\\nRetirement\\n] [\\nManagement\\n] \\n[ECONOMY] \\n[\\nEconomic Calendar\\n] [\\nEconomic News\\n] [\\nIBD Digital: 2 Months for $20\\n] \\nVideos\\n[VIDEOS & PODCASTS] \\n[\\nIBD Live\\n] [\\nHow To Invest Videos\\n] [\\nOptions Videos\\n] [\\nInvesting With IBD Podcast\\n] [\\nGrowth Stories Podcast\\n] [\\nWebinars\\n] [\\nOnline Courses\\n] [\\nIBD Digital: 2 Months for $20\\n] \\nLearn\\n[HOW TO INVEST] \\n[\\nHow To Invest In Stocks\\n] [\\nStock Investing Keys\\n] [\\nStock Market Timing\\n] [\\nTrack Market Trends\\n] [\\nHow To Read Stock Charts\\n] [\\nHow To Buy Stocks\\n] [\\nWhen To Sell Stocks\\n] [\\nShort Selling\\n] [\\nSwing Trading\\n] [\\nWhat Is Crypto\\n] \\n[EDUCATIONAL RESOURCES] \\n[\\nNew to IBD\\n] [\\nInvestor\\'s Corner\\n] [\\nInvesting With IBD Podcast\\n] [\\nEvents & Webinars\\n] [\\nIBD Moneyworks\\n] [\\n12 Days Of Learning\\n] [\\nInvesting Infographics\\n] [\\nPremium Online Courses\\n] [\\nPremium Workshops\\n] \\n[Store] \\nMy Products\\n[Founder\\'s Club] [SwingTrader] [Leaderboard] [MarketSurge] [eIBD] [IBD Digital] [IBD Live] \\nRecently Searched\\n![image] \\nAAOI10.21%\\nAAOI10.21%\\n[Research] 7:05 PM ET\\n[AI Play Willdan Leads 12 Newcomers To IBD 50, Stock Spotlight, Other Best Stock Lists] \\n![image] \\nAMZN1.01%\\nAMZN1.01%\\n[The Big Picture] 5:52 PM ET\\n[Stock Market Falls Amid Trump Fed Chair Pick, Hot Inflation; Jobs Report, Amazon Earnings Due] \\n![image] \\n[Stock Market Today] 5:45 PM ET\\n[Stocks Fall To Key Levels, Silver Dives; Meta, Viking, Guardant Health In Focus] \\n* [Technology] # Sandisk Rockets As Memory Stocks Called \\'The New Gold\\'\\n[![Facebook]] [![X]] [![LinkedIn]] [![Share]] \\n[Licensing] \\n* [PATRICK SEITZ] \\n* Updated 04:11 PM ET 01/30/2026\\nSandisk (SNDK) stock rocketed on Friday after the memory-chip maker obliterated Wall Street\\'s targets with its December-quarter results and guidance. Surging demand for memory and data storage technology led one analyst to call that tech \"the new gold.\" Late Thursday, Sandisk said its earnings soared 404% year over year to an adjusted $6.20 a share while sales jumped 61% to…\\n## Related news\\n[![President Donald Trump]] [## Dow Jones Futures Fall As Trump Picks Kevin Warsh To Be Fed Chairman; Sandisk Soars, Tesla Rises On SpaceX Buzz\\n] \\n1/29/2026Futures fell as President Trump named Kevin Warsh as his Fed chief pick. Sandisk soared on earnings. Tesla rose on...\\n1/29/2026Futures fell as President Trump named Kevin Warsh as his...\\n* ##### [Sandisk Stock Soars As Memory-Chip Maker Smashes Estimates] \\n* ##### [Stock Market Dives On Trump Tariff Threats; Nasdaq, S&P 500 Break Key Level] \\n* ##### [Stock Market Today: Dow Craters On Greenland Tariff News; Nvidia Sees Big Loss (Live Coverage)] \\n* ##### [Last Year\\'s No. 1 S&P 500 Stock Is Top Dog Again In 2026] \\n* ##### [Growth Stocks Lead Stock Market Higher For Second Day As S&P 500 Ascends To Record High] \\n* ##### [What AI Bubble? Data Center Market Will Expand 14% In 2026; Report] \\n* ##### [Investors Already Pick Their 5 Favorite S&P 500 Stocks This Year] \\n* ##### [Dow Jones Futures Rise As Market Extends Bullish Shift; Palantir, GE Lead 12 Stocks In Buy Zones] \\n### Today\\'s Spotlight\\n[## Find Market Winners, Save $125\\nDavid Ryan’s Ants Indicator spots stocks under heavy accumulation—try it in MarketSurge.] [## Join IBD’s Virtual Trading Summit\\nWant to learn proven strategies for picking top stocks? Join IBD’s free online workshop on 2/7.] [## Get Over 70% Off IBD Digital\\nNavigate volatility and stay on top of your trades—try 2 months of IBD Digital for $20.] \\n### More News\\n* [![Hollywood AI actors] \\nAs employment levels in the movie business languish, the rise of AI in Hollywood poses a new threat an already precarious job market. (© Chris Gash)\\nIs Artificial Intelligence Ready For Its Close-Up? Hollywood vs. The Tillyverse] \\n* [![stock market today] Warsh Pick To Lead Fed Firms Up Dollar As Gold, Silver Reverse] \\n* [![Amentum nuclear facility] Private Equity\\'s Amentum Playbook: A Nuclear And Space Makeover] \\n### Partner Center\\nINVESTING RESOURCES\\n* [\\n![] \\n###### Take a Trial Today\\nGet instant access to exclusive stock lists, expert market analysis and powerful tools with 2 months of IBD Digital for only $20!\\n] \\n* [\\n![] \\n###### IBD Videos\\nGet market updates, educational videos, webinars, and stock analysis.\\n] \\n* [\\n![] \\n###### Get Started\\nLearn how you can make more money with IBD\\'s investing tools, top-performing stock lists, and educational content.\\n]', summary=None, highlights=None, highlight_scores=None)], resolved_search_type='neural', auto_date=None, context=None, statuses=None, cost_dollars=CostDollars(total=0.01, search={'neural': 0.005}, contents={'text': 0.005}), search_time=1058.8)" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + " # Option 1: search_and_contents() - gets full text \n", + " results = exa.search_and_contents( \n", + " query=\"SNDK stock news\", \n", + " type=\"auto\", # \"neural\", \"keyword\", or \"auto\" \n", + " num_results=5, \n", + " start_published_date=\"2026-01-25T00:00:00Z\", \n", + " end_published_date=\"2026-02-02T00:00:00Z\", \n", + " text=True, # ← This gets FULL CONTENT \n", + " ) \n", + " results" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Fetching FDA catalysts for next 1000 days...\n", + " FDA debug: total=19 industry=0 with_ticker=19 date_parsed=19 in_window=0 final=0\n" + ] + }, + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from tradingagents.dataflows.fda_catalysts import ( \n", + " get_fda_catalysts, get_upcoming_fda_catalysts \n", + ") \n", + "\n", + "get_upcoming_fda_catalysts(\n", + " days_ahead=1000,\n", + " phases=[\"Phase 3\", \"Phase 2\", \"Phase 1\"],\n", + " return_structured=True,\n", + " ticker_universe_file=\"data/ticker_universe.csv\",\n", + " debug_counts=True,\n", + " max_pages=20\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Fetching FDA catalysts for next 1000 days...\n", + " Saved raw FDA studies to results/fda/clinicaltrials_raw.json\n" + ] + }, + { + "data": { + "text/plain": [ + "'No upcoming FDA catalysts found in ClinicalTrials.gov database.'" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from tradingagents.dataflows.fda_catalysts import get_upcoming_fda_catalysts\n", + "\n", + "get_upcoming_fda_catalysts(\n", + " days_ahead=1000,\n", + " save_raw_path=\"results/fda/clinicaltrials_raw.json\",\n", + " debug_counts=True,\n", + ")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Warning: ticker_matcher not available, falling back to regex\n", + " Fetching recent 13F filings (last 7 days)...\n", + " Found 25 13F filings, analyzing holdings...\n", + " Identified 50 significant institutional holdings\n" + ] + }, + { + "data": { + "text/plain": [ + "\"# Recent 13F Institutional Holdings\\n\\n**Data Source**: SEC EDGAR (LLM-parsed)\\n**Period**: Last 7 days\\n**Minimum Position**: $10M\\n\\n**Found**: 50 stocks with significant institutional interest\\n\\n| Security | Ticker | # Funds | Total Value | Notable Holders |\\n|----------|--------|---------|-------------|----------------|\\n| ISHARES TR | TR | 11 | $303,020M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC +2 |\\n| SPDR SER TR | SPDR | 9 | $480,235M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC +2 |\\n| DIMENSIONAL ETF TRUST | ETF | 7 | $103M | Navigoe, LLC, Navigoe, LLC, Navigoe, LLC +2 |\\n| ALPHABET INC | INC | 6 | $106,628M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC +2 |\\n| AMAZON COM INC | COM | 6 | $73,948M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC +2 |\\n| APPLE INC | APPLE | 5 | $567,526M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC +2 |\\n| MICROSOFT CORP | CORP | 5 | $78,068M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC +2 |\\n| VANGUARD INDEX FDS | INDEX | 4 | $108,610M | Silver Oak Advisory Group, Inc., Koshinski Asset Management, Inc., Koshinski Asset Management, Inc. +1 |\\n| NVIDIA CORPORATION | N/A | 4 | $61,724M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC +1 |\\n| PACER FDS TR | PACER | 3 | $92,691M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC |\\n| INVESCO QQQ TR | QQQ | 3 | $89,241M | Koshinski Asset Management, Inc., RD Finance Ltd, Juniper Hill Capital Management LP |\\n| J P MORGAN EXCHANGE TRADE | J | 3 | $41,943M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC |\\n| BLACKROCK ETF TRUST | ETF | 3 | $35,483M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC |\\n| BERKSHIRE HATHAWAY INC DE | INC | 3 | $35,127M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC |\\n| FIRST TR EXCHANGE-TRADED | FIRST | 3 | $32,493M | Koshinski Asset Management, Inc., Entruity Wealth, LLC, Entruity Wealth, LLC |\\n| ISHARES INC | INC | 3 | $31,699M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC |\\n| UNITEDHEALTH GROUP INC | GROUP | 3 | $31,642M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC |\\n| SELECT SECTOR SPDR TR | SPDR | 3 | $30,374M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC |\\n| VANGUARD WHITEHALL FDS | FDS | 3 | $26,390M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC |\\n| JPMORGAN CHASE & CO. | CHASE | 3 | $26,307M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC |\\n| MORGAN STANLEY ETF TRUST | ETF | 3 | $25,660M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC |\\n| CAPITAL GRP FIXED INCM ET | GRP | 3 | $22,846M | Entruity Wealth, LLC, Entruity Wealth, LLC, Entruity Wealth, LLC |\\n| SPDR S&P 500 ETF TR | SPDR | 2 | $225,488M | Koshinski Asset Management, Inc., Juniper Hill Capital Management LP |\\n| SCHWAB STRATEGIC TR | TR | 2 | $91,061M | Koshinski Asset Management, Inc., Koshinski Asset Management, Inc. |\\n| FIRST TR EXCHANGE TRADED | FIRST | 2 | $44,362M | Koshinski Asset Management, Inc., Entruity Wealth, LLC |\\n| SPROTT ASSET MANAGEMENT L | ASSET | 2 | $14,241M | Entruity Wealth, LLC, Entruity Wealth, LLC |\\n| VANGUARD BD INDEX FDS | BD | 2 | $61M | Silver Oak Advisory Group, Inc., Silver Oak Advisory Group, Inc. |\\n| Alphabet Inc Cl C | INC | 1 | $106,936M | Violich Capital Management, Inc. |\\n| Alphabet Inc Cl A | INC | 1 | $101,922M | Violich Capital Management, Inc. |\\n| Microsoft Corp | CORP | 1 | $87,667M | Violich Capital Management, Inc. |\\n| Apple Inc | APPLE | 1 | $57,897M | Violich Capital Management, Inc. |\\n| Visa Inc Cl A | VISA | 1 | $50,277M | Violich Capital Management, Inc. |\\n| SS SPDR S&P 500 ETF TRUST | SS | 1 | $43,511M | Anfield Capital Management, LLC |\\n| Costco Wholesale Corp | CORP | 1 | $42,369M | Violich Capital Management, Inc. |\\n| Oracle Corp | CORP | 1 | $41,415M | Violich Capital Management, Inc. |\\n| AMPHENOL | N/A | 1 | $39,747M | JLB & ASSOCIATES INC |\\n| APPLE | APPLE | 1 | $39,301M | JLB & ASSOCIATES INC |\\n| TJX COMPANIES | TJX | 1 | $34,830M | JLB & ASSOCIATES INC |\\n| MASTERCARD | N/A | 1 | $32,101M | JLB & ASSOCIATES INC |\\n| ORACLE | N/A | 1 | $31,205M | JLB & ASSOCIATES INC |\\n| Berkshire Hathaway Inc Cl | INC | 1 | $29,257M | Violich Capital Management, Inc. |\\n| MICROSOFT | N/A | 1 | $28,384M | JLB & ASSOCIATES INC |\\n| CASEY'S GENERAL STORES | CASEY | 1 | $28,256M | JLB & ASSOCIATES INC |\\n| SS COMM SELECT SECTOR SPD | SS | 1 | $27,216M | Anfield Capital Management, LLC |\\n| Abbvie Inc | INC | 1 | $25,452M | Violich Capital Management, Inc. |\\n| KLA CORP | KLA | 1 | $24,221M | JLB & ASSOCIATES INC |\\n| FIDELITY MERRIMACK STR TR | STR | 1 | $23,993M | Koshinski Asset Management, Inc. |\\n| ROSS STORES | ROSS | 1 | $23,358M | JLB & ASSOCIATES INC |\\n| ISHARES SEMICONDUCTOR ETF | ETF | 1 | $23,352M | Anfield Capital Management, LLC |\\n| OREILLY AUTOMOTIVE, INC | INC | 1 | $23,331M | Horrell Capital Management, Inc. |\\n\\n\\n## Signal Interpretation\\n\\n- **Multiple institutions** buying same stock = strong conviction signal\\n- **Notable investors** (Buffett, ARK, etc.) = high-profile validation\\n- **New positions** vs existing = fresh conviction\\n\\n**Note**: 13F data is delayed 45 days. Holdings may have changed.\\n\"" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from tradingagents.dataflows.sec_13f import get_sec_13f_filings\n", + "\n", + "get_sec_13f_filings()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "# Recent 13F Institutional Holdings\n", + "\n", + "**Data Source**: SEC EDGAR (LLM-parsed)\n", + "**Period**: Last 7 days\n", + "**Minimum Position**: $10M\n", + "\n", + "**Found**: 50 stocks with significant institutional interest\n", + "\n", + "| Security | Ticker | # Funds | Total Value | Notable Holders |\n", + "|----------|--------|---------|-------------|----------------|\n", + "| ISHARES TR | TR | 11 | $303,020M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC +2 |\n", + "| SPDR SER TR | SPDR | 9 | $480,235M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC +2 |\n", + "| DIMENSIONAL ETF TRUST | ETF | 7 | $103M | Navigoe, LLC, Navigoe, LLC, Navigoe, LLC +2 |\n", + "| ALPHABET INC | INC | 6 | $106,628M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC +2 |\n", + "| AMAZON COM INC | COM | 6 | $73,948M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC +2 |\n", + "| APPLE INC | APPLE | 5 | $567,526M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC +2 |\n", + "| MICROSOFT CORP | CORP | 5 | $78,068M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC +2 |\n", + "| VANGUARD INDEX FDS | INDEX | 4 | $108,610M | Silver Oak Advisory Group, Inc., Koshinski Asset Management, Inc., Koshinski Asset Management, Inc. +1 |\n", + "| NVIDIA CORPORATION | N/A | 4 | $61,724M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC +1 |\n", + "| PACER FDS TR | PACER | 3 | $92,691M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC |\n", + "| INVESCO QQQ TR | QQQ | 3 | $89,241M | Koshinski Asset Management, Inc., RD Finance Ltd, Juniper Hill Capital Management LP |\n", + "| J P MORGAN EXCHANGE TRADE | J | 3 | $41,943M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC |\n", + "| BLACKROCK ETF TRUST | ETF | 3 | $35,483M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC |\n", + "| BERKSHIRE HATHAWAY INC DE | INC | 3 | $35,127M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC |\n", + "| FIRST TR EXCHANGE-TRADED | FIRST | 3 | $32,493M | Koshinski Asset Management, Inc., Entruity Wealth, LLC, Entruity Wealth, LLC |\n", + "| ISHARES INC | INC | 3 | $31,699M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC |\n", + "| UNITEDHEALTH GROUP INC | GROUP | 3 | $31,642M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC |\n", + "| SELECT SECTOR SPDR TR | SPDR | 3 | $30,374M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC |\n", + "| VANGUARD WHITEHALL FDS | FDS | 3 | $26,390M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC |\n", + "| JPMORGAN CHASE & CO. | CHASE | 3 | $26,307M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC |\n", + "| MORGAN STANLEY ETF TRUST | ETF | 3 | $25,660M | ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC, ELEVATION POINT WEALTH PARTNERS, LLC |\n", + "| CAPITAL GRP FIXED INCM ET | GRP | 3 | $22,846M | Entruity Wealth, LLC, Entruity Wealth, LLC, Entruity Wealth, LLC |\n", + "| SPDR S&P 500 ETF TR | SPDR | 2 | $225,488M | Koshinski Asset Management, Inc., Juniper Hill Capital Management LP |\n", + "| SCHWAB STRATEGIC TR | TR | 2 | $91,061M | Koshinski Asset Management, Inc., Koshinski Asset Management, Inc. |\n", + "| FIRST TR EXCHANGE TRADED | FIRST | 2 | $44,362M | Koshinski Asset Management, Inc., Entruity Wealth, LLC |\n", + "| SPROTT ASSET MANAGEMENT L | ASSET | 2 | $14,241M | Entruity Wealth, LLC, Entruity Wealth, LLC |\n", + "| VANGUARD BD INDEX FDS | BD | 2 | $61M | Silver Oak Advisory Group, Inc., Silver Oak Advisory Group, Inc. |\n", + "| Alphabet Inc Cl C | INC | 1 | $106,936M | Violich Capital Management, Inc. |\n", + "| Alphabet Inc Cl A | INC | 1 | $101,922M | Violich Capital Management, Inc. |\n", + "| Microsoft Corp | CORP | 1 | $87,667M | Violich Capital Management, Inc. |\n", + "| Apple Inc | APPLE | 1 | $57,897M | Violich Capital Management, Inc. |\n", + "| Visa Inc Cl A | VISA | 1 | $50,277M | Violich Capital Management, Inc. |\n", + "| SS SPDR S&P 500 ETF TRUST | SS | 1 | $43,511M | Anfield Capital Management, LLC |\n", + "| Costco Wholesale Corp | CORP | 1 | $42,369M | Violich Capital Management, Inc. |\n", + "| Oracle Corp | CORP | 1 | $41,415M | Violich Capital Management, Inc. |\n", + "| AMPHENOL | N/A | 1 | $39,747M | JLB & ASSOCIATES INC |\n", + "| APPLE | APPLE | 1 | $39,301M | JLB & ASSOCIATES INC |\n", + "| TJX COMPANIES | TJX | 1 | $34,830M | JLB & ASSOCIATES INC |\n", + "| MASTERCARD | N/A | 1 | $32,101M | JLB & ASSOCIATES INC |\n", + "| ORACLE | N/A | 1 | $31,205M | JLB & ASSOCIATES INC |\n", + "| Berkshire Hathaway Inc Cl | INC | 1 | $29,257M | Violich Capital Management, Inc. |\n", + "| MICROSOFT | N/A | 1 | $28,384M | JLB & ASSOCIATES INC |\n", + "| CASEY'S GENERAL STORES | CASEY | 1 | $28,256M | JLB & ASSOCIATES INC |\n", + "| SS COMM SELECT SECTOR SPD | SS | 1 | $27,216M | Anfield Capital Management, LLC |\n", + "| Abbvie Inc | INC | 1 | $25,452M | Violich Capital Management, Inc. |\n", + "| KLA CORP | KLA | 1 | $24,221M | JLB & ASSOCIATES INC |\n", + "| FIDELITY MERRIMACK STR TR | STR | 1 | $23,993M | Koshinski Asset Management, Inc. |\n", + "| ROSS STORES | ROSS | 1 | $23,358M | JLB & ASSOCIATES INC |\n", + "| ISHARES SEMICONDUCTOR ETF | ETF | 1 | $23,352M | Anfield Capital Management, LLC |\n", + "| OREILLY AUTOMOTIVE, INC | INC | 1 | $23,331M | Horrell Capital Management, Inc. |\n", + "\n", + "\n", + "## Signal Interpretation\n", + "\n", + "- **Multiple institutions** buying same stock = strong conviction signal\n", + "- **Notable investors** (Buffett, ARK, etc.) = high-profile validation\n", + "- **New positions** vs existing = fresh conviction\n", + "\n", + "**Note**: 13F data is delayed 45 days. Holdings may have changed.\n", + "\n" + ] + } + ], + "source": [ + "print(_)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Retrying langchain_google_genai.chat_models._chat_with_retry.._chat_with_retry in 2.0 seconds as it raised DeadlineExceeded: 504 Deadline Exceeded.\n", + "Retrying langchain_google_genai.chat_models._chat_with_retry.._chat_with_retry in 4.0 seconds as it raised DeadlineExceeded: 504 Deadline Exceeded.\n" + ] + }, + { + "ename": "TypeError", + "evalue": "can only concatenate list (not \"str\") to list", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mTypeError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[9]\u001b[39m\u001b[32m, line 56\u001b[39m\n\u001b[32m 54\u001b[39m insights = get_latest_13f_insights(\u001b[32m2\u001b[39m)\n\u001b[32m 55\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m r \u001b[38;5;129;01min\u001b[39;00m insights: \n\u001b[32m---> \u001b[39m\u001b[32m56\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[43mr\u001b[49m\u001b[43m \u001b[49m\u001b[43m+\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[38;5;130;43;01m\\n\u001b[39;49;00m\u001b[33;43m\"\u001b[39;49m + \u001b[33m\"\u001b[39m\u001b[33m-\u001b[39m\u001b[33m\"\u001b[39m*\u001b[32m50\u001b[39m)\n", + "\u001b[31mTypeError\u001b[39m: can only concatenate list (not \"str\") to list" + ] + } + ], + "source": [ + "from sec_api import QueryApi\n", + "from langchain_google_genai import ChatGoogleGenerativeAI\n", + "import pandas as pd\n", + "import os\n", + "\n", + "from dotenv import load_dotenv\n", + "load_dotenv()\n", + "# 1. Setup APIs\n", + "sec_api = \"8d4cd7f499520130c208b0f056918cac265b8e94ecf96d342edf21b0c4e275c8\"\n", + "query_api = QueryApi(api_key=sec_api)\n", + "google_api_key = os.getenv(\"GOOGLE_API_KEY\")\n", + "\n", + "# 2. Setup Gemini (Optimized for deep reasoning/financial analysis)\n", + "insight_llm = ChatGoogleGenerativeAI(\n", + " model=\"gemini-3-flash-preview\", \n", + " api_key=google_api_key,\n", + " temperature=0.3 # Lower temperature for better factual accuracy\n", + ")\n", + "\n", + "def get_latest_13f_insights(limit=3):\n", + " # Step A: Get metadata for the most recent 13F-HR filings\n", + " query = {\n", + " \"query\": { \"query_string\": { \"query\": \"formType:\\\"13F-HR\\\"\" } },\n", + " \"from\": \"0\", \"size\": str(limit),\n", + " \"sort\": [{ \"filedAt\": { \"order\": \"desc\" } }]\n", + " }\n", + " filings = query_api.get_filings(query)['filings']\n", + " \n", + " reports = []\n", + " for f in filings:\n", + " fund_name = f['companyName']\n", + " url = f['linkToFilingDetails']\n", + " \n", + " # Step B: Use Gemini to analyze the context of the filing\n", + " # We use a specific prompt to find 'Alpha' (unique insights)\n", + " prompt = f\"\"\"\n", + " You are a Senior Hedge Fund Analyst. Look at the latest 13F filing for {fund_name} at this URL: {url}\n", + " Using your web search tool, find the top 3 largest NEW positions or SIGNIFICANT increases.\n", + " Summarize the 'Investment Thesis' based on current market news for those stocks.\n", + " Format your response as:\n", + " - Fund: [Name]\n", + " - Top Moves: [List]\n", + " - Sentiment: [Bullish/Bearish]\n", + " - The 'Why': [1-2 sentences on the market context]\n", + " \"\"\"\n", + " \n", + " # We use grounding here so Gemini can actually 'read' the URL or search for news about it\n", + " insight = insight_llm.bind_tools([{\"google_search\": {}}]).invoke(prompt)\n", + " reports.append(insight.content)\n", + " \n", + " return reports\n", + "\n", + "# Execute\n", + "insights = get_latest_13f_insights(2)\n", + "for r in insights: \n", + " print(r + \"\\n\" + \"-\"*50)" + ] + }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "import os\n", + "import requests\n", + "import xml.etree.ElementTree as ET\n", + "import pandas as pd\n", + "from langchain_google_genai import ChatGoogleGenerativeAI\n", + "from dotenv import load_dotenv\n", + "\n", + "load_dotenv()\n", + "\n", + "# --- CONFIGURATION ---\n", + "# The SEC requires a specific User-Agent format: \"Name Email@domain.com\"\n", + "SEC_HEADERS = {\"User-Agent\": \"Institutional Tracker youssef.aitousarrah@gmail.com\"}\n", + "GEMINI_API_KEY = os.getenv(\"GOOGLE_API_KEY\")\n", + "\n", + "# 1. Initialize Gemini\n", + "llm = ChatGoogleGenerativeAI(\n", + " model=\"gemini-3-flash-preview\", \n", + " api_key=GEMINI_API_KEY,\n", + " temperature=0.2\n", + ")\n", + "\n", + "def get_latest_13f_filings(limit=3):\n", + " \"\"\"Fetch the latest 13F-HR filings from the SEC's free Atom feed.\"\"\"\n", + " url = f\"https://www.sec.gov/cgi-bin/browse-edgar?action=getcurrent&type=13F-HR&count={limit}&output=atom\"\n", + " response = requests.get(url, headers=SEC_HEADERS)\n", + " \n", + " if response.status_code != 200:\n", + " print(f\"Failed to fetch feed: {response.status_code}\")\n", + " return []\n", + "\n", + " root = ET.fromstring(response.content)\n", + " ns = {'atom': 'http://www.w3.org/2005/Atom'}\n", + " \n", + " filings = []\n", + " for entry in root.findall('atom:entry', ns):\n", + " title = entry.find('atom:title', ns).text\n", + " # Extract CIK and Company Name from title\n", + " # Format: \"13F-HR - APPLE INC (0000320193) (Filer)\"\n", + " company_name = title.split(' - ')[1].split(' (')[0]\n", + " link = entry.find('atom:link', ns).attrib['href']\n", + " \n", + " filings.append({\n", + " \"company\": company_name,\n", + " \"url\": link.replace(\"-index.htm\", \".txt\") # Convert to raw text link\n", + " })\n", + " return filings\n", + "\n", + "def get_ai_insight(fund_name, filing_url):\n", + " \"\"\"Sends the filing URL to Gemini for analysis using web grounding.\"\"\"\n", + " prompt = f\"\"\"\n", + " Analyze the latest 13F filing for the fund '{fund_name}' found at this URL: {filing_url}\n", + " \n", + " Tasks:\n", + " 1. Identify the top 5 largest holdings by market value.\n", + " 2. Determine if there were any significant new positions (Alpha signals).\n", + " 3. Summarize the overall investment sentiment (e.g., pivot to AI, defensive move to cash).\n", + " \n", + " Format the output as a clean summary for a portfolio manager.\n", + " \"\"\"\n", + " \n", + " # We use Google Search grounding so Gemini can 'read' the live URL and recent news\n", + " search_tool = {\"google_search\": {}}\n", + " grounded_llm = llm.bind_tools([search_tool])\n", + " \n", + " response = grounded_llm.invoke(prompt)\n", + " return response.content\n", + "\n", + "# --- MAIN EXECUTION ---\n", + "if __name__ == \"__main__\":\n", + " print(\"🚀 Fetching latest 13F-HR filings from SEC.gov...\")\n", + " latest_filings = get_latest_13f_filings(2)\n", + " \n", + " for filing in latest_filings:\n", + " print(f\"\\n🔍 Analyzing Fund: {filing['company']}\")\n", + " print(f\"📄 Filing Source: {filing['url']}\")\n", + " \n", + " insight = get_ai_insight(filing['company'], filing['url'])\n", + " \n", + " print(\"\\n--- AI INSIGHTS ---\")\n", + " print(insight)\n", + " print(\"-\" * 50)" + ] } ], "metadata": { diff --git a/tradingagents/agents/risk_mgmt/neutral_debator.py b/tradingagents/agents/risk_mgmt/neutral_debator.py index cc624610..14b9ca45 100644 --- a/tradingagents/agents/risk_mgmt/neutral_debator.py +++ b/tradingagents/agents/risk_mgmt/neutral_debator.py @@ -68,6 +68,8 @@ Choose BUY or SELL (no HOLD). If the edge is unclear, pick the less-bad side and response_text = parse_llm_response(response.content) argument = f"Neutral Analyst: {response_text}" - return {"risk_debate_state": update_risk_debate_state(risk_debate_state, argument, "Neutral")} + return { + "risk_debate_state": update_risk_debate_state(risk_debate_state, argument, "Neutral") + } return neutral_node diff --git a/tradingagents/agents/utils/agent_utils.py b/tradingagents/agents/utils/agent_utils.py index 4c88f27f..2bf315e9 100644 --- a/tradingagents/agents/utils/agent_utils.py +++ b/tradingagents/agents/utils/agent_utils.py @@ -1,4 +1,4 @@ -from typing import Any, Callable, Dict, List +from typing import Any, Callable, Dict from langchain_core.messages import HumanMessage, RemoveMessage @@ -95,9 +95,7 @@ def update_risk_debate_state( "count": debate_state["count"] + 1, } # Append to the speaker's own history and set their current response - new_state[f"{role_key}_history"] = ( - debate_state.get(f"{role_key}_history", "") + "\n" + argument - ) + new_state[f"{role_key}_history"] = debate_state.get(f"{role_key}_history", "") + "\n" + argument new_state[f"current_{role_key}_response"] = argument return new_state diff --git a/tradingagents/agents/utils/historical_memory_builder.py b/tradingagents/agents/utils/historical_memory_builder.py index 5a099591..0b997796 100644 --- a/tradingagents/agents/utils/historical_memory_builder.py +++ b/tradingagents/agents/utils/historical_memory_builder.py @@ -203,7 +203,9 @@ class HistoricalMemoryBuilder: except (IndexError, KeyError): continue - logger.info(f"Found {len([m for m in high_movers if m['ticker'] == ticker])} moves for {ticker}") + logger.info( + f"Found {len([m for m in high_movers if m['ticker'] == ticker])} moves for {ticker}" + ) else: logger.debug(f"{ticker}: No significant moves") @@ -440,7 +442,9 @@ class HistoricalMemoryBuilder: high_movers = self.find_high_movers(tickers, start_date, end_date, min_move_pct) if not high_movers: - logger.warning("⚠️ No high movers found. Try a different date range or lower threshold.") + logger.warning( + "⚠️ No high movers found. Try a different date range or lower threshold." + ) return {} # Step 1.5: Sample/filter high movers based on strategy @@ -449,7 +453,9 @@ class HistoricalMemoryBuilder: logger.info(f"📊 Sampling Strategy: {sample_strategy}") logger.info(f"Total high movers found: {len(high_movers)}") logger.info(f"Samples to analyze: {len(sampled_movers)}") - logger.info(f"Estimated runtime: ~{len(sampled_movers) * len(analysis_windows) * 2} minutes") + logger.info( + f"Estimated runtime: ~{len(sampled_movers) * len(analysis_windows) * 2} minutes" + ) # Initialize memory stores agent_memories = { diff --git a/tradingagents/dataflows/alpha_vantage_volume.py b/tradingagents/dataflows/alpha_vantage_volume.py index 2094c321..87444969 100644 --- a/tradingagents/dataflows/alpha_vantage_volume.py +++ b/tradingagents/dataflows/alpha_vantage_volume.py @@ -11,6 +11,7 @@ from pathlib import Path from typing import Annotated, Dict, List, Optional, Union import pandas as pd + from tradingagents.dataflows.y_finance import _get_ticker_universe, get_ticker_history from tradingagents.utils.logger import get_logger @@ -460,7 +461,9 @@ def download_volume_data( logger.info("Skipping cache (use_cache=False), forcing fresh download...") # Download fresh data - logger.info(f"Downloading {history_period_days} days of volume data for {len(tickers)} tickers...") + logger.info( + f"Downloading {history_period_days} days of volume data for {len(tickers)} tickers..." + ) raw_data = {} with ThreadPoolExecutor(max_workers=15) as executor: diff --git a/tradingagents/dataflows/discovery/analytics.py b/tradingagents/dataflows/discovery/analytics.py index 2babdad2..47c2dc55 100644 --- a/tradingagents/dataflows/discovery/analytics.py +++ b/tradingagents/dataflows/discovery/analytics.py @@ -349,7 +349,9 @@ class DiscoveryAnalytics: indent=2, ) - logger.info(f" 📊 Saved {len(enriched_rankings)} recommendations for tracking: {output_file}") + logger.info( + f" 📊 Saved {len(enriched_rankings)} recommendations for tracking: {output_file}" + ) def save_discovery_results(self, state: dict, trade_date: str, config: Dict[str, Any]): """Save full discovery results and tool logs.""" diff --git a/tradingagents/dataflows/discovery/discovery_config.py b/tradingagents/dataflows/discovery/discovery_config.py index 77be1217..cfcf7ce3 100644 --- a/tradingagents/dataflows/discovery/discovery_config.py +++ b/tradingagents/dataflows/discovery/discovery_config.py @@ -158,9 +158,7 @@ class DiscoveryConfig: max_candidates_to_analyze=disc.get( "max_candidates_to_analyze", _rd.max_candidates_to_analyze ), - analyze_all_candidates=disc.get( - "analyze_all_candidates", _rd.analyze_all_candidates - ), + analyze_all_candidates=disc.get("analyze_all_candidates", _rd.analyze_all_candidates), final_recommendations=disc.get("final_recommendations", _rd.final_recommendations), truncate_ranking_context=disc.get( "truncate_ranking_context", _rd.truncate_ranking_context @@ -189,12 +187,8 @@ class DiscoveryConfig: # Logging logging_cfg = LoggingConfig( log_tool_calls=disc.get("log_tool_calls", _ld.log_tool_calls), - log_tool_calls_console=disc.get( - "log_tool_calls_console", _ld.log_tool_calls_console - ), - log_prompts_console=disc.get( - "log_prompts_console", _ld.log_prompts_console - ), + log_tool_calls_console=disc.get("log_tool_calls_console", _ld.log_tool_calls_console), + log_prompts_console=disc.get("log_prompts_console", _ld.log_prompts_console), tool_log_max_chars=disc.get("tool_log_max_chars", _ld.tool_log_max_chars), tool_log_exclude=disc.get("tool_log_exclude", _ld.tool_log_exclude), ) diff --git a/tradingagents/dataflows/discovery/filter.py b/tradingagents/dataflows/discovery/filter.py index 720e5c90..7864d1f8 100644 --- a/tradingagents/dataflows/discovery/filter.py +++ b/tradingagents/dataflows/discovery/filter.py @@ -185,7 +185,9 @@ class CandidateFilter: # Print consolidated list of failed tickers if failed_tickers: - logger.warning(f"⚠️ {len(failed_tickers)} tickers failed data fetch (possibly delisted)") + logger.warning( + f"⚠️ {len(failed_tickers)} tickers failed data fetch (possibly delisted)" + ) if len(failed_tickers) <= 10: logger.warning(f"{', '.join(failed_tickers)}") else: @@ -501,7 +503,9 @@ class CandidateFilter: ) # Extract short interest from fundamentals (no extra API call) - short_pct_raw = fund.get("ShortPercentOfFloat", fund.get("ShortPercentFloat")) + short_pct_raw = fund.get( + "ShortPercentOfFloat", fund.get("ShortPercentFloat") + ) short_interest_pct = None if short_pct_raw and short_pct_raw != "N/A": try: @@ -747,9 +751,7 @@ class CandidateFilter: logger.info(f" ❌ No data available: {filtered_reasons['no_data']}") logger.info(f" ✅ Passed filters: {len(filtered_candidates)}") - def _predict_ml( - self, cand: Dict[str, Any], ticker: str, end_date: str - ) -> Any: + def _predict_ml(self, cand: Dict[str, Any], ticker: str, end_date: str) -> Any: """Run ML win probability prediction for a candidate.""" # Lazy-load predictor on first call if not self._ml_predictor_loaded: @@ -767,10 +769,10 @@ class CandidateFilter: return None try: + from tradingagents.dataflows.y_finance import download_history from tradingagents.ml.feature_engineering import ( compute_features_single, ) - from tradingagents.dataflows.y_finance import download_history # Fetch OHLCV for feature computation (needs ~210 rows of history) ohlcv = download_history( diff --git a/tradingagents/dataflows/discovery/ranker.py b/tradingagents/dataflows/discovery/ranker.py index 330f7857..30f7a284 100644 --- a/tradingagents/dataflows/discovery/ranker.py +++ b/tradingagents/dataflows/discovery/ranker.py @@ -52,7 +52,9 @@ class StockRanking(BaseModel): strategy_match: str = Field(description="Strategy that matched") final_score: int = Field(description="Score 0-100") confidence: int = Field(description="Confidence 1-10") - reason: str = Field(description="Detailed investment thesis (4-6 sentences) defending the trade with specific catalysts, risk/reward, and timing") + reason: str = Field( + description="Detailed investment thesis (4-6 sentences) defending the trade with specific catalysts, risk/reward, and timing" + ) description: str = Field(description="Company description") diff --git a/tradingagents/dataflows/discovery/scanners/__init__.py b/tradingagents/dataflows/discovery/scanners/__init__.py index 556ac8ce..b814e3b7 100644 --- a/tradingagents/dataflows/discovery/scanners/__init__.py +++ b/tradingagents/dataflows/discovery/scanners/__init__.py @@ -5,10 +5,10 @@ from . import ( earnings_calendar, # noqa: F401 insider_buying, # noqa: F401 market_movers, # noqa: F401 + ml_signal, # noqa: F401 options_flow, # noqa: F401 reddit_dd, # noqa: F401 reddit_trending, # noqa: F401 semantic_news, # noqa: F401 volume_accumulation, # noqa: F401 - ml_signal, # noqa: F401 ) diff --git a/tradingagents/dataflows/discovery/scanners/ml_signal.py b/tradingagents/dataflows/discovery/scanners/ml_signal.py index b0744e3a..5bd557fc 100644 --- a/tradingagents/dataflows/discovery/scanners/ml_signal.py +++ b/tradingagents/dataflows/discovery/scanners/ml_signal.py @@ -7,7 +7,6 @@ Default: data/tickers.txt. Override via config: discovery.scanners.ml_signal.tic from concurrent.futures import ThreadPoolExecutor, as_completed from typing import Any, Dict, List, Optional -import numpy as np import pandas as pd from tradingagents.dataflows.discovery.scanner_registry import SCANNER_REGISTRY, BaseScanner @@ -109,7 +108,9 @@ class MLSignalScanner(BaseScanner): # Log individual candidate results if candidates: - header = f"{'Ticker':<8} {'P(WIN)':>8} {'P(LOSS)':>9} {'Prediction':>12} {'Priority':>10}" + header = ( + f"{'Ticker':<8} {'P(WIN)':>8} {'P(LOSS)':>9} {'Prediction':>12} {'Priority':>10}" + ) separator = "-" * len(header) lines = ["\n ML Signal Scanner Results:", f" {header}", f" {separator}"] for c in candidates: @@ -143,7 +144,9 @@ class MLSignalScanner(BaseScanner): try: from tradingagents.dataflows.y_finance import download_history - logger.info(f"Batch-downloading {len(self.universe)} tickers ({self.lookback_period})...") + logger.info( + f"Batch-downloading {len(self.universe)} tickers ({self.lookback_period})..." + ) # yfinance batch download — single HTTP request for all tickers raw = download_history( diff --git a/tradingagents/dataflows/finnhub_api.py b/tradingagents/dataflows/finnhub_api.py index 7a6359a6..09051c0e 100644 --- a/tradingagents/dataflows/finnhub_api.py +++ b/tradingagents/dataflows/finnhub_api.py @@ -4,9 +4,8 @@ from typing import Annotated, Any, Dict import finnhub from dotenv import load_dotenv -from tradingagents.utils.logger import get_logger - from tradingagents.config import config +from tradingagents.utils.logger import get_logger load_dotenv() diff --git a/tradingagents/dataflows/local.py b/tradingagents/dataflows/local.py index 9b3ac144..59aef12a 100644 --- a/tradingagents/dataflows/local.py +++ b/tradingagents/dataflows/local.py @@ -7,11 +7,11 @@ import pandas as pd from dateutil.relativedelta import relativedelta from tqdm import tqdm +from tradingagents.utils.logger import get_logger + from .config import DATA_DIR from .reddit_utils import fetch_top_from_category -from tradingagents.utils.logger import get_logger - logger = get_logger(__name__) diff --git a/tradingagents/dataflows/news_semantic_scanner.py b/tradingagents/dataflows/news_semantic_scanner.py index 2620f6d5..15a1f07d 100644 --- a/tradingagents/dataflows/news_semantic_scanner.py +++ b/tradingagents/dataflows/news_semantic_scanner.py @@ -807,11 +807,15 @@ Return as JSON with "news" array.""" logger.info(f"Found {len(google_news)} items from Google News") min_date, max_date = self._publish_date_range(google_news) if min_date: - logger.debug(f"Min publish date (Google News): {min_date.strftime('%Y-%m-%d %H:%M')}") + logger.debug( + f"Min publish date (Google News): {min_date.strftime('%Y-%m-%d %H:%M')}" + ) else: logger.debug("Min publish date (Google News): N/A") if max_date: - logger.debug(f"Max publish date (Google News): {max_date.strftime('%Y-%m-%d %H:%M')}") + logger.debug( + f"Max publish date (Google News): {max_date.strftime('%Y-%m-%d %H:%M')}" + ) else: logger.debug("Max publish date (Google News): N/A") @@ -837,11 +841,15 @@ Return as JSON with "news" array.""" logger.info(f"Found {len(av_news)} items from Alpha Vantage") min_date, max_date = self._publish_date_range(av_news) if min_date: - logger.debug(f"Min publish date (Alpha Vantage): {min_date.strftime('%Y-%m-%d %H:%M')}") + logger.debug( + f"Min publish date (Alpha Vantage): {min_date.strftime('%Y-%m-%d %H:%M')}" + ) else: logger.debug("Min publish date (Alpha Vantage): N/A") if max_date: - logger.debug(f"Max publish date (Alpha Vantage): {max_date.strftime('%Y-%m-%d %H:%M')}") + logger.debug( + f"Max publish date (Alpha Vantage): {max_date.strftime('%Y-%m-%d %H:%M')}" + ) else: logger.debug("Max publish date (Alpha Vantage): N/A") diff --git a/tradingagents/dataflows/reddit_api.py b/tradingagents/dataflows/reddit_api.py index 9b7c71b8..bdd2ce9c 100644 --- a/tradingagents/dataflows/reddit_api.py +++ b/tradingagents/dataflows/reddit_api.py @@ -493,7 +493,9 @@ Extract all stock ticker symbols mentioned in the post or comments.""" # Handle None result (Gemini blocked content despite safety settings) if result is None: - logger.warning(f"⚠️ Content blocked for '{post['title'][:50]}...' - Skipping") + logger.warning( + f"⚠️ Content blocked for '{post['title'][:50]}...' - Skipping" + ) post["quality_score"] = 0 post["quality_reason"] = ( "Content blocked by LLM safety filter. " diff --git a/tradingagents/graph/discovery_graph.py b/tradingagents/graph/discovery_graph.py index 84dd54f1..76a54432 100644 --- a/tradingagents/graph/discovery_graph.py +++ b/tradingagents/graph/discovery_graph.py @@ -286,9 +286,7 @@ class DiscoveryGraph: else: self._add_context(incoming_context, existing, prepend=False) - def _add_context( - self, new_context: str, candidate: Dict[str, Any], *, prepend: bool - ) -> None: + def _add_context(self, new_context: str, candidate: Dict[str, Any], *, prepend: bool) -> None: """ Add context string to a candidate's context fields. @@ -492,7 +490,9 @@ class DiscoveryGraph: try: # Get result with per-scanner timeout - name, pipeline, candidates, error, scanner_logs = future.result(timeout=timeout_seconds) + name, pipeline, candidates, error, scanner_logs = future.result( + timeout=timeout_seconds + ) # Initialize pipeline list if needed if pipeline not in pipeline_candidates: diff --git a/tradingagents/graph/price_charts.py b/tradingagents/graph/price_charts.py index aed7d84b..7dab26aa 100644 --- a/tradingagents/graph/price_charts.py +++ b/tradingagents/graph/price_charts.py @@ -324,11 +324,7 @@ def _extract_close_series(data: Any) -> Any: if isinstance(data.columns, pd.MultiIndex): if "Close" in data.columns.get_level_values(0): close_data = data["Close"] - series = ( - close_data.iloc[:, 0] - if isinstance(close_data, pd.DataFrame) - else close_data - ) + series = close_data.iloc[:, 0] if isinstance(close_data, pd.DataFrame) else close_data elif "Close" in data.columns: series = data["Close"] diff --git a/tradingagents/graph/trading_graph.py b/tradingagents/graph/trading_graph.py index b7049c00..4140b419 100644 --- a/tradingagents/graph/trading_graph.py +++ b/tradingagents/graph/trading_graph.py @@ -14,7 +14,6 @@ from tradingagents.default_config import DEFAULT_CONFIG # Import tools from new registry-based system from tradingagents.tools.generator import get_agent_tools - from tradingagents.utils.logger import get_logger from .conditional_logic import ConditionalLogic diff --git a/tradingagents/ml/feature_engineering.py b/tradingagents/ml/feature_engineering.py index 18e6177b..1cb177d8 100644 --- a/tradingagents/ml/feature_engineering.py +++ b/tradingagents/ml/feature_engineering.py @@ -132,9 +132,7 @@ def compute_features_bulk(ohlcv: pd.DataFrame, market_cap: Optional[float] = Non # 7. Position within Bollinger Bands (0 = lower band, 1 = upper band) bb_range = bb_upper - bb_lower - features["bb_position"] = np.where( - bb_range > 0, (close - bb_lower) / bb_range, 0.5 - ) + features["bb_position"] = np.where(bb_range > 0, (close - bb_lower) / bb_range, 0.5) # 8. ADX (trend strength) features["adx"] = ss["dx_14"] @@ -181,7 +179,9 @@ def compute_features_bulk(ohlcv: pd.DataFrame, market_cap: Optional[float] = Non # 21. Momentum × Compression: strong trend direction + tight Bollinger = breakout setup # High absolute MACD + low BB width = coiled spring - features["momentum_x_compression"] = features["macd_hist"].abs() / features["bb_width_pct"].replace(0, np.nan) + features["momentum_x_compression"] = features["macd_hist"].abs() / features[ + "bb_width_pct" + ].replace(0, np.nan) # 22. RSI momentum: 5-day rate of change of RSI (acceleration of momentum) features["rsi_momentum"] = features["rsi_14"] - features["rsi_14"].shift(5) @@ -190,7 +190,9 @@ def compute_features_bulk(ohlcv: pd.DataFrame, market_cap: Optional[float] = Non features["volume_price_confirm"] = features["volume_ratio_5d"] * features["return_1d"] # 24. Trend alignment: both SMAs agree (1 = aligned bullish, -1 = aligned bearish) - features["trend_alignment"] = np.sign(features["sma50_distance"]) * np.sign(features["sma200_distance"]) + features["trend_alignment"] = np.sign(features["sma50_distance"]) * np.sign( + features["sma200_distance"] + ) # 25. Volatility regime: ATR percentile within rolling 60-day window (0-1) atr_pct_series = features["atr_pct"] @@ -202,18 +204,20 @@ def compute_features_bulk(ohlcv: pd.DataFrame, market_cap: Optional[float] = Non # 26. Mean reversion signal: oversold RSI + price below lower Bollinger features["mean_reversion_signal"] = ( (100 - features["rsi_14"]) / 100 # inversed RSI (higher = more oversold) - ) * (1 - features["bb_position"].clip(0, 1)) # below lower band amplifies signal + ) * ( + 1 - features["bb_position"].clip(0, 1) + ) # below lower band amplifies signal # 27. Breakout signal: above upper BB + high volume ratio - features["breakout_signal"] = ( - features["bb_position"].clip(0, 2) * features["volume_ratio_20d"] - ) + features["breakout_signal"] = features["bb_position"].clip(0, 2) * features["volume_ratio_20d"] # 28. MACD strength: histogram normalized by volatility features["macd_strength"] = features["macd_hist"] / features["atr_pct"].replace(0, np.nan) # 29. Return/Volatility ratio: Sharpe-like metric - features["return_volatility_ratio"] = features["return_5d"] / features["atr_pct"].replace(0, np.nan) + features["return_volatility_ratio"] = features["return_5d"] / features["atr_pct"].replace( + 0, np.nan + ) # 30. Trend-momentum composite score features["trend_momentum_score"] = ( diff --git a/tradingagents/ml/predictor.py b/tradingagents/ml/predictor.py index cbac034d..3c479650 100644 --- a/tradingagents/ml/predictor.py +++ b/tradingagents/ml/predictor.py @@ -9,7 +9,7 @@ from __future__ import annotations import os import pickle from pathlib import Path -from typing import Any, Dict, List, Optional, Tuple +from typing import Any, Dict, List, Optional import numpy as np import pandas as pd diff --git a/tradingagents/tools/registry.py b/tradingagents/tools/registry.py index 82da24a8..ea181260 100644 --- a/tradingagents/tools/registry.py +++ b/tradingagents/tools/registry.py @@ -12,8 +12,6 @@ Adding a new tool: Just add one entry here, everything else is auto-generated. from typing import Any, Dict, List, Optional -from tradingagents.utils.logger import get_logger - from tradingagents.dataflows.alpha_vantage import ( get_balance_sheet as get_alpha_vantage_balance_sheet, ) @@ -105,6 +103,7 @@ from tradingagents.dataflows.y_finance import ( from tradingagents.dataflows.y_finance import ( validate_tickers_batch as validate_tickers_batch_yfinance, ) +from tradingagents.utils.logger import get_logger logger = get_logger(__name__) diff --git a/tradingagents/ui/pages/performance.py b/tradingagents/ui/pages/performance.py index 38b6e6d2..ef6c6bb2 100644 --- a/tradingagents/ui/pages/performance.py +++ b/tradingagents/ui/pages/performance.py @@ -33,7 +33,9 @@ def render() -> None: # Check if data is available if not strategy_metrics: - st.warning("No strategy performance data available. Run performance tracking to generate data.") + st.warning( + "No strategy performance data available. Run performance tracking to generate data." + ) return # Strategy Performance section diff --git a/tradingagents/ui/pages/todays_picks.py b/tradingagents/ui/pages/todays_picks.py index e196033a..47b907b9 100644 --- a/tradingagents/ui/pages/todays_picks.py +++ b/tradingagents/ui/pages/todays_picks.py @@ -66,8 +66,7 @@ def render(): with col1: pipelines = list( set( - (r.get("pipeline") or r.get("strategy_match") or "unknown") - for r in recommendations + (r.get("pipeline") or r.get("strategy_match") or "unknown") for r in recommendations ) ) pipeline_filter = st.multiselect("Pipeline", pipelines, default=pipelines)