We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/knishioka/ib-sec-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
"""Base analyzer class"""
from abc import ABC, abstractmethod
from decimal import Decimal
from typing import Any
from ib_sec_mcp.models.account import Account
from ib_sec_mcp.models.portfolio import Portfolio
from ib_sec_mcp.models.position import Position
from ib_sec_mcp.models.trade import Trade
class AnalysisResult(dict[str, Any]):
"""
Analysis result container
Flexible dictionary-based result with metadata
"""
def __init__(self, analyzer_name: str, **kwargs: Any) -> None:
super().__init__(**kwargs)
self["analyzer"] = analyzer_name
self["timestamp"] = None # Set during analysis
class BaseAnalyzer(ABC):
"""
Base class for all analyzers
Provides common interface and utilities for analysis modules
"""
def __init__(
self,
portfolio: Portfolio | None = None,
account: Account | None = None,
):
"""
Initialize analyzer
Args:
portfolio: Portfolio to analyze (for multi-account)
account: Single account to analyze
Raises:
ValueError: If neither portfolio nor account is provided
"""
if portfolio is None and account is None:
raise ValueError("Either portfolio or account must be provided")
self.portfolio = portfolio
self.account = account
self.is_multi_account = portfolio is not None
@property
def analyzer_name(self) -> str:
"""Get analyzer name (class name without 'Analyzer' suffix)"""
return self.__class__.__name__.replace("Analyzer", "")
@abstractmethod
def analyze(self) -> AnalysisResult:
"""
Run analysis
Returns:
AnalysisResult with findings
"""
pass
def _create_result(self, **kwargs: Any) -> AnalysisResult:
"""
Create analysis result with metadata
Args:
**kwargs: Result data
Returns:
AnalysisResult instance
"""
from datetime import datetime
result = AnalysisResult(self.analyzer_name, **kwargs)
result["timestamp"] = datetime.now().isoformat()
result["is_multi_account"] = self.is_multi_account
if self.is_multi_account and self.portfolio:
result["account_count"] = self.portfolio.account_count
result["from_date"] = self.portfolio.from_date.isoformat()
result["to_date"] = self.portfolio.to_date.isoformat()
elif self.account:
result["account_id"] = self.account.account_id
result["from_date"] = self.account.from_date.isoformat()
result["to_date"] = self.account.to_date.isoformat()
return result
def get_trades(self) -> list[Trade]:
"""Get trades from portfolio or account"""
if self.is_multi_account and self.portfolio:
return self.portfolio.all_trades
elif self.account:
return self.account.trades
return []
def get_positions(self) -> list[Position]:
"""Get positions from portfolio or account"""
if self.is_multi_account and self.portfolio:
return self.portfolio.all_positions
elif self.account:
return self.account.positions
return []
def get_total_value(self) -> Decimal:
"""Get total value from portfolio or account"""
if self.is_multi_account and self.portfolio:
return self.portfolio.total_value
elif self.account:
return self.account.total_value
return Decimal("0")
def get_total_cash(self) -> Decimal:
"""Get total cash from portfolio or account"""
if self.is_multi_account and self.portfolio:
return self.portfolio.total_cash
elif self.account:
return self.account.total_cash
return Decimal("0")