| """
|
| Demo Data for Portfolio Optimization
|
|
|
| This module provides sample stock data for the portfolio optimization quickstart.
|
| The data includes 20 stocks across 4 sectors with ML-predicted returns.
|
|
|
| In a real application, these predictions would come from an ML model trained
|
| on historical stock data. For this quickstart, we use hardcoded realistic values.
|
|
|
| FINANCE CONCEPTS:
|
| - predicted_return: Expected percentage gain (0.12 = 12% expected return)
|
| - sector: Industry classification for diversification
|
| - Equal weight: Each selected stock gets 100%/20 = 5% of the portfolio
|
| """
|
| from enum import Enum
|
| from dataclasses import dataclass
|
|
|
| from .domain import StockSelection, PortfolioOptimizationPlan, PortfolioConfig
|
|
|
|
|
| class DemoData(Enum):
|
| """Available demo datasets."""
|
| SMALL = 'SMALL'
|
| LARGE = 'LARGE'
|
|
|
|
|
| @dataclass
|
| class DemoDataConfig:
|
| """Configuration for demo data generation."""
|
| target_position_count: int
|
| max_sector_percentage: float
|
|
|
|
|
| demo_data_configs = {
|
| DemoData.SMALL: DemoDataConfig(
|
| target_position_count=20,
|
| max_sector_percentage=0.25,
|
| ),
|
| DemoData.LARGE: DemoDataConfig(
|
| target_position_count=20,
|
| max_sector_percentage=0.25,
|
| ),
|
| }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| SMALL_DATASET_STOCKS = [
|
|
|
|
|
| ("AAPL", "Apple Inc.", "Technology", 0.12),
|
| ("GOOGL", "Alphabet (Google)", "Technology", 0.15),
|
| ("MSFT", "Microsoft Corp.", "Technology", 0.10),
|
| ("NVDA", "NVIDIA Corp.", "Technology", 0.18),
|
| ("META", "Meta Platforms", "Technology", 0.08),
|
| ("TSLA", "Tesla Inc.", "Technology", 0.20),
|
| ("AMD", "AMD Inc.", "Technology", 0.14),
|
|
|
|
|
|
|
| ("JNJ", "Johnson & Johnson", "Healthcare", 0.09),
|
| ("UNH", "UnitedHealth Group", "Healthcare", 0.11),
|
| ("PFE", "Pfizer Inc.", "Healthcare", 0.07),
|
| ("ABBV", "AbbVie Inc.", "Healthcare", 0.10),
|
| ("TMO", "Thermo Fisher", "Healthcare", 0.13),
|
| ("DHR", "Danaher Corp.", "Healthcare", 0.12),
|
|
|
|
|
|
|
| ("JPM", "JPMorgan Chase", "Finance", 0.08),
|
| ("BAC", "Bank of America", "Finance", 0.06),
|
| ("WFC", "Wells Fargo", "Finance", 0.07),
|
| ("GS", "Goldman Sachs", "Finance", 0.09),
|
| ("MS", "Morgan Stanley", "Finance", 0.08),
|
| ("C", "Citigroup", "Finance", 0.05),
|
|
|
|
|
|
|
| ("XOM", "Exxon Mobil", "Energy", 0.04),
|
| ("CVX", "Chevron Corp.", "Energy", 0.05),
|
| ("COP", "ConocoPhillips", "Energy", 0.06),
|
| ("SLB", "Schlumberger", "Energy", 0.03),
|
| ("EOG", "EOG Resources", "Energy", 0.07),
|
| ("PXD", "Pioneer Natural", "Energy", 0.08),
|
| ]
|
|
|
| LARGE_DATASET_STOCKS = SMALL_DATASET_STOCKS + [
|
|
|
| ("CRM", "Salesforce", "Technology", 0.11),
|
| ("ADBE", "Adobe Inc.", "Technology", 0.09),
|
| ("ORCL", "Oracle Corp.", "Technology", 0.07),
|
| ("CSCO", "Cisco Systems", "Technology", 0.06),
|
| ("IBM", "IBM Corp.", "Technology", 0.04),
|
| ("QCOM", "Qualcomm", "Technology", 0.13),
|
|
|
|
|
| ("MRK", "Merck & Co.", "Healthcare", 0.08),
|
| ("LLY", "Eli Lilly", "Healthcare", 0.16),
|
| ("BMY", "Bristol-Myers", "Healthcare", 0.06),
|
| ("AMGN", "Amgen Inc.", "Healthcare", 0.09),
|
| ("GILD", "Gilead Sciences", "Healthcare", 0.05),
|
| ("ISRG", "Intuitive Surgical", "Healthcare", 0.14),
|
|
|
|
|
| ("AXP", "American Express", "Finance", 0.10),
|
| ("BLK", "BlackRock", "Finance", 0.11),
|
| ("SCHW", "Charles Schwab", "Finance", 0.07),
|
| ("USB", "U.S. Bancorp", "Finance", 0.04),
|
|
|
|
|
| ("OXY", "Occidental Petroleum", "Energy", 0.06),
|
| ("HAL", "Halliburton", "Energy", 0.05),
|
|
|
|
|
| ("AMZN", "Amazon.com", "Consumer", 0.14),
|
| ("WMT", "Walmart", "Consumer", 0.06),
|
| ("HD", "Home Depot", "Consumer", 0.08),
|
| ("MCD", "McDonald's", "Consumer", 0.07),
|
| ("NKE", "Nike Inc.", "Consumer", 0.09),
|
| ("SBUX", "Starbucks", "Consumer", 0.05),
|
| ("PG", "Procter & Gamble", "Consumer", 0.04),
|
| ("KO", "Coca-Cola", "Consumer", 0.05),
|
| ]
|
|
|
|
|
|
|
| def generate_demo_data(demo_data: DemoData) -> PortfolioOptimizationPlan:
|
| """
|
| Generate demo data for portfolio optimization.
|
|
|
| Args:
|
| demo_data: Which demo dataset to generate (SMALL or LARGE)
|
|
|
| Returns:
|
| PortfolioOptimizationPlan with candidate stocks (all unselected initially)
|
|
|
| Example:
|
| >>> plan = generate_demo_data(DemoData.SMALL)
|
| >>> len(plan.stocks)
|
| 20
|
| >>> plan.stocks[0].stock_id
|
| 'AAPL'
|
| """
|
| config = demo_data_configs[demo_data]
|
| stock_data = SMALL_DATASET_STOCKS if demo_data == DemoData.SMALL else LARGE_DATASET_STOCKS
|
|
|
| stocks = [
|
| StockSelection(
|
| stock_id=ticker,
|
| stock_name=name,
|
| sector=sector,
|
| predicted_return=predicted_return,
|
| selection=None,
|
| )
|
| for ticker, name, sector, predicted_return in stock_data
|
| ]
|
|
|
|
|
| target_count = config.target_position_count
|
| max_per_sector = max(1, int(config.max_sector_percentage * target_count))
|
|
|
|
|
| portfolio_config = PortfolioConfig(
|
| target_count=target_count,
|
| max_per_sector=max_per_sector,
|
| unselected_penalty=10000,
|
| )
|
|
|
| return PortfolioOptimizationPlan(
|
| stocks=stocks,
|
| target_position_count=config.target_position_count,
|
| max_sector_percentage=config.max_sector_percentage,
|
| portfolio_config=portfolio_config,
|
| )
|
|
|
|
|
| def get_stock_summary(plan: PortfolioOptimizationPlan) -> str:
|
| """
|
| Generate a human-readable summary of the portfolio.
|
|
|
| Useful for debugging and understanding the solution.
|
| """
|
| lines = [
|
| "=" * 60,
|
| "PORTFOLIO SUMMARY",
|
| "=" * 60,
|
| ]
|
|
|
| selected = plan.get_selected_stocks()
|
| if not selected:
|
| lines.append("No stocks selected yet.")
|
| return "\n".join(lines)
|
|
|
| weight = plan.get_weight_per_stock()
|
| expected_return = plan.get_expected_return()
|
|
|
| lines.append(f"Selected: {len(selected)} stocks @ {weight*100:.1f}% each")
|
| lines.append(f"Expected Return: {expected_return*100:.2f}%")
|
| lines.append("")
|
|
|
|
|
| sector_stocks: dict[str, list[StockSelection]] = {}
|
| for stock in selected:
|
| if stock.sector not in sector_stocks:
|
| sector_stocks[stock.sector] = []
|
| sector_stocks[stock.sector].append(stock)
|
|
|
| lines.append("BY SECTOR:")
|
| for sector, stocks in sorted(sector_stocks.items()):
|
| sector_weight = len(stocks) * weight * 100
|
| lines.append(f" {sector}: {len(stocks)} stocks = {sector_weight:.1f}%")
|
| for stock in sorted(stocks, key=lambda s: -s.predicted_return):
|
| lines.append(f" - {stock.stock_id}: {stock.stock_name} ({stock.predicted_return*100:.1f}% pred)")
|
|
|
| lines.append("")
|
| lines.append(f"Score: {plan.score}")
|
| lines.append("=" * 60)
|
|
|
| return "\n".join(lines)
|
|
|