get_price_history
Retrieve historical stock price data for a specified symbol, period, and interval to analyze market trends and performance over time.
Instructions
Fetch historical price data for a given stock symbol over a specified period and interval.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| interval | No | Data interval frequency (e.g. '1d', '1h', '1m') | 1d |
| period | No | Time period to retrieve data for (e.g. '1d', '1mo', '1y') | 1mo |
| symbol | Yes | The stock symbol |
Implementation Reference
- src/yfmcp/server.py:190-222 (handler)The primary handler function for the 'get_price_history' tool, decorated with @mcp.tool() for automatic registration in FastMCP. It fetches historical price data using yfinance, handles empty data, returns markdown table or delegates to chart generation.@mcp.tool() def get_price_history( symbol: Annotated[str, Field(description="The stock symbol")], period: Annotated[Period, Field(description="Time period to retrieve data for (e.g. '1d', '1mo', '1y').")] = "1mo", interval: Annotated[Interval, Field(description="Data interval frequency (e.g. '1d', '1h', '1m')")] = "1d", chart_type: Annotated[ ChartType | None, Field( description=( "Type of chart: 'price_volume' for candlestick with volume bars, " "'vwap' for Volume Weighted Average Price, or 'volume_profile' " "for volume distribution by price level" ) ), ] = None, ) -> str | ImageContent: """Fetch historical price data for a given stock symbol over a specified period and interval.""" ticker = yf.Ticker(symbol) df = ticker.history( period=period, interval=interval, rounding=True, ) if df.empty: return f"No data available for symbol {symbol} with period {period} and interval {interval}" if chart_type is None: return df.to_markdown() return generate_chart(symbol=symbol, df=df, chart_type=chart_type)
- src/yfmcp/types.py:34-46 (schema)Pydantic-compatible Literal type defining valid periods for historical data retrieval in get_price_history.Period = Literal[ "1d", "5d", "1mo", "3mo", "6mo", "1y", "2y", "5y", "10y", "ytd", "max", ]
- src/yfmcp/types.py:49-63 (schema)Pydantic-compatible Literal type defining valid intervals for data frequency in get_price_history.Interval = Literal[ "1m", "2m", "5m", "15m", "30m", "60m", "90m", "1h", "1d", "5d", "1wk", "1mo", "3mo", ]
- src/yfmcp/types.py:66-70 (schema)Pydantic-compatible Literal type for chart_type parameter, enabling different chart visualizations.ChartType = Literal[ "price_volume", "vwap", "volume_profile", ]
- src/yfmcp/chart.py:44-157 (helper)Helper function that generates and returns image content for price/volume charts, VWAP overlay, or volume profile, invoked conditionally in get_price_history.def generate_chart(symbol: str, df: pd.DataFrame, chart_type: ChartType) -> ImageContent | str: """Generate a financial chart using mplfinance. Shows candlestick price data with volume, optionally with VWAP or volume profile. Returns base64-encoded WebP image for efficient token usage. """ import matplotlib matplotlib.use("Agg") # Use non-interactive backend import matplotlib.cm as cm import matplotlib.pyplot as plt import mplfinance as mpf # Prepare data for mplfinance (needs OHLCV columns) # Ensure column names match what mplfinance expects df = df[["Open", "High", "Low", "Close", "Volume"]] # Handle volume profile separately as it needs custom layout if chart_type == "volume_profile": # Calculate volume profile volume_profile = _calculate_volume_profile(df) # Create a custom figure with proper layout for side-by-side charts fig = plt.figure(figsize=(18, 10)) # Create gridspec for layout: left side for candlestick+volume, right side for volume profile gs = fig.add_gridspec( 2, 2, width_ratios=[3.5, 1], height_ratios=[3, 1], hspace=0.3, wspace=0.15, left=0.08, right=0.95, top=0.95, bottom=0.1, ) # Left side: candlestick chart (top) and volume bars (bottom) ax_price = fig.add_subplot(gs[0, 0]) ax_volume = fig.add_subplot(gs[1, 0], sharex=ax_price) # Right side: volume profile (aligned with price chart) ax_profile = fig.add_subplot(gs[0, 1], sharey=ax_price) # Plot candlestick and volume using mplfinance on our custom axes style = mpf.make_mpf_style(base_mpf_style="yahoo", rc={"figure.facecolor": "white"}) mpf.plot( df, type="candle", volume=ax_volume, style=style, ax=ax_price, show_nontrading=False, returnfig=False, ) # Plot volume profile as horizontal bars on the right viridis = cm.get_cmap("viridis") colors = viridis(np.linspace(0, 1, len(volume_profile))) ax_profile.barh(volume_profile.index, volume_profile.values, color=colors, alpha=0.7) ax_profile.set_xlabel("Volume", fontsize=10) ax_profile.set_title("Volume Profile", fontsize=12, fontweight="bold", pad=10) ax_profile.grid(True, alpha=0.3, axis="x") ax_profile.set_ylabel("") # Share y-axis label with main chart # Set overall title fig.suptitle(f"{symbol} - Volume Profile", fontsize=16, fontweight="bold", y=0.98) # Save directly to WebP format buf = io.BytesIO() fig.savefig(buf, format="webp", dpi=150, bbox_inches="tight") buf.seek(0) plt.close(fig) else: # Standard mplfinance chart (price_volume or vwap) addplots = [] if chart_type == "vwap": # VWAP = Sum(Price * Volume) / Sum(Volume) typical_price = (df["High"] + df["Low"] + df["Close"]) / 3 vwap = (typical_price * df["Volume"]).cumsum() / df["Volume"].cumsum() addplots.append(mpf.make_addplot(vwap, color="orange", width=2, linestyle="--", label="VWAP")) # Create style style = mpf.make_mpf_style(base_mpf_style="yahoo", rc={"figure.facecolor": "white"}) # Save chart directly to WebP format buf = io.BytesIO() plot_kwargs = { "type": "candle", "volume": True, "style": style, "title": f"{symbol} - {chart_type.replace('_', ' ').title()}", "ylabel": "Price", "ylabel_lower": "Volume", "savefig": {"fname": buf, "format": "webp", "dpi": 150, "bbox_inches": "tight"}, "show_nontrading": False, "returnfig": False, } if addplots: plot_kwargs["addplot"] = addplots mpf.plot(df, **plot_kwargs) buf.seek(0) return ImageContent( type="image", data=base64.b64encode(buf.read()).decode("utf-8"), mimeType="image/webp", )