We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/ajtgjmdjp/edinet-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
"""Financial statement diff and period comparison.
Compares financial statements for the same company across two periods,
calculating changes (増減額) and growth rates (増減率) for each line item.
"""
from __future__ import annotations
from typing import TYPE_CHECKING, Any, TypedDict, cast
import httpx
from loguru import logger
from edinet_mcp.client import EdinetAPIError
if TYPE_CHECKING:
from edinet_mcp.client import EdinetClient
from edinet_mcp.models import StatementData
class LineItemDiff(TypedDict):
"""Diff result for a single line item."""
statement: str # "income_statement", "balance_sheet", or "cash_flow_statement"
科目: str
period1_value: float | None
period2_value: float | None
増減額: float | None
増減率: str | None # Formatted as "+21.38%" or "-10.50%"
class DiffResult(TypedDict):
"""Complete diff result for two periods."""
edinet_code: str
company_name: str
period1: str
period2: str
accounting_standard: str
diffs: list[LineItemDiff]
summary: dict[str, Any]
async def _fetch_period_statements(
client: EdinetClient,
edinet_code: str,
period1: str,
period2: str,
doc_type: str,
) -> tuple[Any, Any]:
"""Fetch financial statements for two periods.
Returns:
Tuple of (stmt1, stmt2).
Raises:
ValueError: If either period's statement cannot be fetched.
"""
try:
stmt1 = await client.get_financial_statements(
edinet_code=edinet_code,
doc_type=doc_type,
period=period1,
)
except (ValueError, EdinetAPIError, httpx.HTTPError) as e:
raise ValueError(f"Failed to fetch {period1} statement: {e}") from e
try:
stmt2 = await client.get_financial_statements(
edinet_code=edinet_code,
doc_type=doc_type,
period=period2,
)
except (ValueError, EdinetAPIError, httpx.HTTPError) as e:
raise ValueError(f"Failed to fetch {period2} statement: {e}") from e
return stmt1, stmt2
def _compute_changes(
stmt1: Any,
stmt2: Any,
) -> tuple[list[LineItemDiff], dict[str, Any]]:
"""Compare all statement sections and compute diffs with summary.
Returns:
Tuple of (sorted diffs list, summary dict).
"""
diffs: list[LineItemDiff] = []
for section, label in (
("income_statement", "income_statement"),
("balance_sheet", "balance_sheet"),
("cash_flow_statement", "cash_flow_statement"),
):
diffs.extend(
_compare_statement(
getattr(stmt1, section),
getattr(stmt2, section),
label,
)
)
diffs.sort(key=lambda x: abs(x.get("増減額") or 0), reverse=True)
summary = _calculate_summary(diffs)
return diffs, summary
async def diff_statements(
client: EdinetClient,
edinet_code: str,
period1: str,
period2: str,
*,
doc_type: str = "annual_report",
) -> DiffResult:
"""Compare financial statements for the same company across two periods.
Fetches financial statements for both periods and calculates changes
(増減額) and growth rates (増減率) for each line item.
Args:
client: EdinetClient instance.
edinet_code: Company's EDINET code (e.g. "E02144").
period1: First period year (e.g. "2024").
period2: Second period year (e.g. "2025").
doc_type: Document type label (default: "annual_report").
Returns:
DiffResult with detailed diffs and summary statistics.
Raises:
ValueError: If either period's statement cannot be fetched.
"""
logger.info(f"Fetching statements for {edinet_code}: {period1} vs {period2}")
stmt1, stmt2 = await _fetch_period_statements(
client,
edinet_code,
period1,
period2,
doc_type,
)
diffs, summary = _compute_changes(stmt1, stmt2)
return DiffResult(
edinet_code=edinet_code,
company_name=stmt2.filing.company_name,
period1=period1,
period2=period2,
accounting_standard=stmt2.accounting_standard.value,
diffs=diffs,
summary=summary,
)
def _compare_statement(
stmt1: StatementData,
stmt2: StatementData,
statement_type: str,
) -> list[LineItemDiff]:
"""Compare two statements and return diffs for each line item."""
diffs: list[LineItemDiff] = []
# Get all unique labels from both statements
labels1 = set(stmt1.labels)
labels2 = set(stmt2.labels)
all_labels = labels1 | labels2
for label in all_labels:
val1 = _extract_current_value(stmt1, label)
val2 = _extract_current_value(stmt2, label)
if val1 is None and val2 is None:
continue
change = _calculate_change(val1, val2)
change_rate = _calculate_change_rate(val1, val2)
diffs.append(
LineItemDiff(
statement=statement_type,
科目=label,
period1_value=val1,
period2_value=val2,
増減額=change,
増減率=change_rate,
)
)
return diffs
def _extract_current_value(stmt: StatementData, label: str) -> float | None:
"""Extract the '当期' value for a label from a statement."""
try:
item = stmt[label]
value = item.get("当期")
if value is None:
return None
return float(value)
except (KeyError, ValueError, TypeError):
return None
def _calculate_change(val1: float | None, val2: float | None) -> float | None:
"""Calculate absolute change between two values."""
if val1 is None or val2 is None:
return None
return val2 - val1
def _calculate_change_rate(val1: float | None, val2: float | None) -> str | None:
"""Calculate percentage change between two values."""
if val1 is None or val2 is None:
return None
if val1 == 0:
return None # Cannot calculate rate when base is zero
rate = (val2 - val1) / abs(val1) * 100
sign = "+" if rate >= 0 else ""
return f"{sign}{rate:.2f}%"
def _calculate_summary(diffs: list[LineItemDiff]) -> dict[str, Any]:
"""Calculate summary statistics from diffs."""
total_items = len(diffs)
increased = sum(1 for d in diffs if (d.get("増減額") or 0) > 0)
decreased = sum(1 for d in diffs if (d.get("増減額") or 0) < 0)
unchanged = total_items - increased - decreased
# Top 5 increases and decreases
valid_diffs = [d for d in diffs if d.get("増減額") is not None]
top_increases = [d for d in valid_diffs if cast("int", d["増減額"]) > 0][:5]
top_decreases = [d for d in valid_diffs if cast("int", d["増減額"]) < 0][:5]
return {
"total_items": total_items,
"increased": increased,
"decreased": decreased,
"unchanged": unchanged,
"top_increases": [
{"科目": d["科目"], "増減額": d["増減額"], "増減率": d["増減率"]}
for d in top_increases
],
"top_decreases": [
{"科目": d["科目"], "増減額": d["増減額"], "増減率": d["増減率"]}
for d in top_decreases
],
}