Skip to main content
Glama

compare_spending_by_year

Analyze category spending trends across multiple years to identify patterns and track budget performance over time with visual comparisons.

Instructions

Compare spending for a category across multiple years.

Args: budget_id: The ID of the budget (use 'last-used' for default budget) category_id: The category ID to analyze start_year: Starting year (e.g., 2020) num_years: Number of years to compare (default: 5) include_graph: Include terminal graph visualization (default: True) Returns: JSON string with year-over-year comparison including totals, changes, percentage changes, and optional graph

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
budget_idYes
category_idYes
include_graphNo
num_yearsNo
start_yearYes

Implementation Reference

  • MCP tool registration for compare_spending_by_year. Thin wrapper that gets YNABClient singleton and calls its method, returning JSON.
    @mcp.tool() async def compare_spending_by_year( budget_id: str, category_id: str, start_year: int, num_years: int = 5, include_graph: bool = True, ) -> str: """Compare spending for a category across multiple years. Args: budget_id: The ID of the budget (use 'last-used' for default budget) category_id: The category ID to analyze start_year: Starting year (e.g., 2020) num_years: Number of years to compare (default: 5) include_graph: Include terminal graph visualization (default: True) Returns: JSON string with year-over-year comparison including totals, changes, percentage changes, and optional graph """ client = get_ynab_client() result = await client.compare_spending_by_year( budget_id, category_id, start_year, num_years, include_graph ) return json.dumps(result, indent=2)
  • Core handler implementation in YNABClient. Fetches transactions via YNAB API since start_year, filters by category_id and date range, aggregates yearly spending totals, computes year-over-year absolute and percentage changes, calculates average, and optionally generates a terminal graph using _generate_graph.
    async def compare_spending_by_year( self, budget_id: str, category_id: str, start_year: int, num_years: int = 5, include_graph: bool = True, ) -> dict[str, Any]: """Compare spending for a category across multiple years. Args: budget_id: The budget ID or 'last-used' category_id: The category ID to analyze start_year: Starting year (e.g., 2020) num_years: Number of years to compare (default: 5) include_graph: Include terminal graph visualization (default: True) Returns: Year-over-year comparison with totals and percentage changes """ try: # Get all transactions since the start year since_date = f"{start_year}-01-01" end_year = start_year + num_years - 1 until_date = f"{end_year}-12-31" url = f"{self.api_base_url}/budgets/{budget_id}/transactions" params = {"since_date": since_date} result = await self._make_request_with_retry("get", url, params=params) txn_data = result["data"]["transactions"] # Aggregate by year yearly_totals = {} for year in range(start_year, end_year + 1): yearly_totals[str(year)] = 0 for txn in txn_data: # Filter by category and date range if txn.get("category_id") != category_id: continue if txn["date"] > until_date: continue year = txn["date"][:4] if year in yearly_totals: amount = txn["amount"] / 1000 if txn.get("amount") else 0 yearly_totals[year] += amount # Calculate year-over-year changes comparisons = [] years_sorted = sorted(yearly_totals.keys()) for i, year in enumerate(years_sorted): year_data = { "year": year, "total_spent": yearly_totals[year], } if i > 0: prev_year = years_sorted[i - 1] prev_total = yearly_totals[prev_year] change = yearly_totals[year] - prev_total if prev_total != 0: percent_change = (change / abs(prev_total)) * 100 else: percent_change = 0 if change == 0 else float("inf") year_data["change_from_previous"] = change year_data["percent_change"] = percent_change comparisons.append(year_data) # Calculate overall statistics totals = [yearly_totals[year] for year in years_sorted] average_per_year = sum(totals) / len(totals) if totals else 0 result_data = { "category_id": category_id, "years": f"{start_year}-{end_year}", "average_per_year": average_per_year, "yearly_comparison": comparisons, } # Add graph if requested if include_graph and yearly_totals: graph_data = [(year, yearly_totals[year]) for year in years_sorted] result_data["graph"] = self._generate_graph( graph_data, f"Year-over-Year Comparison: {start_year}-{end_year}" ) return result_data except Exception as e: raise Exception(f"Failed to compare spending by year: {e}") from e
  • Helper method to generate ASCII terminal bar graph using termgraph library. Captures stdout to return graph as string. Used in compare_spending_by_year for visual year-over-year comparison.
    def _generate_graph(self, data: list[tuple], title: str = "") -> str: """Generate a terminal graph using termgraph. Args: data: List of (label, value) tuples title: Graph title Returns: String containing the terminal graph """ if not data: return "" # Capture termgraph output old_stdout = sys.stdout sys.stdout = StringIO() try: # Prepare data for termgraph labels = [label for label, _ in data] values = [[abs(value)] for _, value in data] # Configure termgraph args = { "stacked": False, "width": 50, "format": "{:.2f}", "suffix": "", "no_labels": False, "color": None, "vertical": False, "different_scale": False, "calendar": False, "start_dt": None, "custom_tick": "", "delim": "", "verbose": False, "label_before": False, "histogram": False, "no_values": False, } # Print title if title: print(f"\n{title}") print("=" * len(title)) # Generate graph tg.chart(colors=[], data=values, args=args, labels=labels) # Get the output output = sys.stdout.getvalue() return output finally: sys.stdout = old_stdout

Latest Blog Posts

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/dgalarza/ynab-mcp'

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