Skip to main content
Glama

MaverickMCP

by wshobson
MIT License
165
  • Apple
stock_helpers.py4.33 kB
""" Stock data helper utilities for routers. This module provides common stock data fetching and processing utilities that are shared across multiple routers to avoid code duplication. """ import asyncio from concurrent.futures import ThreadPoolExecutor from datetime import UTC, datetime, timedelta import pandas as pd from maverick_mcp.providers.stock_data import EnhancedStockDataProvider # Thread pool for async operations executor = ThreadPoolExecutor(max_workers=4) def get_stock_dataframe(ticker: str, days: int = 365) -> pd.DataFrame: """ Get stock data as a DataFrame with technical indicators. Args: ticker: Stock ticker symbol (e.g., "AAPL") days: Number of days of historical data to fetch (default: 365) Returns: DataFrame with stock price data and technical indicators Raises: ValueError: If ticker is invalid or data cannot be fetched """ from maverick_mcp.core.technical_analysis import add_technical_indicators # Calculate date range end_date = datetime.now(UTC) start_date = end_date - timedelta(days=days) start_str = start_date.strftime("%Y-%m-%d") end_str = end_date.strftime("%Y-%m-%d") # Get stock data provider stock_provider = EnhancedStockDataProvider() # Fetch data and add technical indicators df = stock_provider.get_stock_data(ticker, start_str, end_str) df = add_technical_indicators(df) return df async def get_stock_dataframe_async(ticker: str, days: int = 365) -> pd.DataFrame: """ Async wrapper for get_stock_dataframe to avoid blocking the event loop. Args: ticker: Stock ticker symbol (e.g., "AAPL") days: Number of days of historical data to fetch (default: 365) Returns: DataFrame with stock price data and technical indicators Raises: ValueError: If ticker is invalid or data cannot be fetched """ loop = asyncio.get_event_loop() return await loop.run_in_executor(executor, get_stock_dataframe, ticker, days) async def get_multiple_stock_dataframes_async( tickers: list[str], days: int = 365 ) -> dict[str, pd.DataFrame]: """ Fetch multiple stock dataframes concurrently. Args: tickers: List of stock ticker symbols days: Number of days of historical data to fetch (default: 365) Returns: Dictionary mapping ticker symbols to their DataFrames Raises: ValueError: If any ticker is invalid or data cannot be fetched """ tasks = [get_stock_dataframe_async(ticker, days) for ticker in tickers] results = await asyncio.gather(*tasks) return dict(zip(tickers, results, strict=False)) def validate_ticker(ticker: str) -> str: """ Validate and normalize a stock ticker symbol. Args: ticker: Stock ticker symbol to validate Returns: Normalized ticker symbol (uppercase, stripped) Raises: ValueError: If ticker is invalid """ if not ticker or not isinstance(ticker, str): # type: ignore[arg-type] raise ValueError("Ticker must be a non-empty string") ticker = ticker.strip().upper() if not ticker: raise ValueError("Ticker cannot be empty") # Basic validation - ticker should be alphanumeric with possible dots/hyphens if not ticker.replace(".", "").replace("-", "").isalnum(): raise ValueError("Ticker contains invalid characters") if len(ticker) > 10: raise ValueError("Ticker is too long (max 10 characters)") return ticker def calculate_date_range(days: int) -> tuple[str, str]: """ Calculate start and end date strings for stock data fetching. Args: days: Number of days of historical data Returns: Tuple of (start_date_str, end_date_str) in YYYY-MM-DD format Raises: ValueError: If days is not a positive integer """ if not isinstance(days, int) or days <= 0: # type: ignore[arg-type] raise ValueError("Days must be a positive integer") if days > 3650: # ~10 years raise ValueError("Days cannot exceed 3650 (10 years)") end_date = datetime.now(UTC) start_date = end_date - timedelta(days=days) start_str = start_date.strftime("%Y-%m-%d") end_str = end_date.strftime("%Y-%m-%d") return start_str, end_str

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/wshobson/maverick-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server