| | """ |
| | GrahamAgent - Value Investing Cognitive Agent |
| | |
| | This module implements Benjamin Graham's value investing philosophy as a |
| | recursive cognitive agent with specialized market interpretation capabilities. |
| | |
| | Key characteristics: |
| | - Focuses on margin of safety and intrinsic value |
| | - Detects undervalued assets based on fundamentals |
| | - Maintains skepticism toward market sentiment |
| | - Prioritizes long-term value over short-term price movements |
| | - Exhibits patience and discipline with high conviction |
| | |
| | Internal Notes: The Graham shell simulates the CIRCUIT-FRAGMENT and NULL-FEATURE |
| | shells for detecting undervalued assets and knowledge boundaries. |
| | """ |
| |
|
| | import datetime |
| | from typing import Dict, List, Any, Optional |
| | import numpy as np |
| |
|
| | from .base import BaseAgent, AgentSignal |
| | from ..cognition.graph import ReasoningGraph |
| | from ..utils.diagnostics import TracingTools |
| |
|
| |
|
| | class GrahamAgent(BaseAgent): |
| | """ |
| | Agent embodying Benjamin Graham's value investing philosophy. |
| | |
| | Implements specialized cognitive patterns for: |
| | - Intrinsic value calculation |
| | - Margin of safety evaluation |
| | - Fundamental analysis |
| | - Value trap detection |
| | - Long-term perspective |
| | """ |
| | |
| | def __init__( |
| | self, |
| | reasoning_depth: int = 3, |
| | memory_decay: float = 0.1, |
| | initial_capital: float = 100000.0, |
| | margin_of_safety: float = 0.3, |
| | model_provider: str = "anthropic", |
| | model_name: str = "claude-3-sonnet-20240229", |
| | trace_enabled: bool = False, |
| | ): |
| | """ |
| | Initialize Graham value investing agent. |
| | |
| | Args: |
| | reasoning_depth: Depth of recursive reasoning |
| | memory_decay: Rate of memory deterioration |
| | initial_capital: Starting capital amount |
| | margin_of_safety: Minimum discount to intrinsic value requirement |
| | model_provider: LLM provider |
| | model_name: Specific model identifier |
| | trace_enabled: Whether to generate full reasoning traces |
| | """ |
| | super().__init__( |
| | name="Graham", |
| | philosophy="Value investing focused on margin of safety and fundamental analysis", |
| | reasoning_depth=reasoning_depth, |
| | memory_decay=memory_decay, |
| | initial_capital=initial_capital, |
| | model_provider=model_provider, |
| | model_name=model_name, |
| | trace_enabled=trace_enabled, |
| | ) |
| | |
| | self.margin_of_safety = margin_of_safety |
| | |
| | |
| | self.state.reflective_state.update({ |
| | 'value_detection_threshold': 0.7, |
| | 'sentiment_skepticism': 0.8, |
| | 'patience_factor': 0.9, |
| | 'fundamental_weighting': 0.8, |
| | 'technical_weighting': 0.2, |
| | }) |
| | |
| | |
| | self._configure_reasoning_graph() |
| | |
| | def _configure_reasoning_graph(self) -> None: |
| | """Configure the reasoning graph with value investing specific nodes.""" |
| | self.reasoning_graph.add_node( |
| | "intrinsic_value_analysis", |
| | fn=self._intrinsic_value_analysis |
| | ) |
| | |
| | self.reasoning_graph.add_node( |
| | "margin_of_safety_evaluation", |
| | fn=self._margin_of_safety_evaluation |
| | ) |
| | |
| | self.reasoning_graph.add_node( |
| | "fundamental_analysis", |
| | fn=self._fundamental_analysis |
| | ) |
| | |
| | self.reasoning_graph.add_node( |
| | "value_trap_detection", |
| | fn=self._value_trap_detection |
| | ) |
| | |
| | |
| | self.reasoning_graph.set_entry_point("intrinsic_value_analysis") |
| | self.reasoning_graph.add_edge("intrinsic_value_analysis", "margin_of_safety_evaluation") |
| | self.reasoning_graph.add_edge("margin_of_safety_evaluation", "fundamental_analysis") |
| | self.reasoning_graph.add_edge("fundamental_analysis", "value_trap_detection") |
| | |
| | def process_market_data(self, data: Dict[str, Any]) -> Dict[str, Any]: |
| | """ |
| | Process market data through Graham's value investing lens. |
| | |
| | Focuses on: |
| | - Extracting fundamental metrics |
| | - Calculating intrinsic value estimates |
| | - Identifying margin of safety opportunities |
| | - Filtering for value characteristics |
| | |
| | Args: |
| | data: Market data dictionary |
| | |
| | Returns: |
| | Processed market data with value investing insights |
| | """ |
| | processed_data = { |
| | 'timestamp': datetime.datetime.now(), |
| | 'tickers': {}, |
| | 'market_sentiment': data.get('market_sentiment', {}), |
| | 'economic_indicators': data.get('economic_indicators', {}), |
| | 'insights': [], |
| | } |
| | |
| | |
| | for ticker, ticker_data in data.get('tickers', {}).items(): |
| | |
| | fundamentals = ticker_data.get('fundamentals', {}) |
| | price = ticker_data.get('price', 0) |
| | |
| | |
| | if not fundamentals or price == 0: |
| | processed_data['tickers'][ticker] = { |
| | 'price': price, |
| | 'analysis': 'insufficient_data', |
| | 'intrinsic_value': None, |
| | 'margin_of_safety': None, |
| | 'recommendation': 'hold', |
| | } |
| | continue |
| | |
| | |
| | intrinsic_value = self._calculate_intrinsic_value(fundamentals, ticker_data) |
| | |
| | |
| | margin_of_safety = (intrinsic_value - price) / intrinsic_value if intrinsic_value > 0 else 0 |
| | |
| | |
| | is_value_opportunity = margin_of_safety >= self.margin_of_safety |
| | |
| | |
| | value_trap_indicators = self._detect_value_trap_indicators(fundamentals, ticker_data) |
| | |
| | |
| | analysis = self._generate_value_analysis( |
| | ticker=ticker, |
| | fundamentals=fundamentals, |
| | price=price, |
| | intrinsic_value=intrinsic_value, |
| | margin_of_safety=margin_of_safety, |
| | value_trap_indicators=value_trap_indicators |
| | ) |
| | |
| | |
| | processed_data['tickers'][ticker] = { |
| | 'price': price, |
| | 'intrinsic_value': intrinsic_value, |
| | 'margin_of_safety': margin_of_safety, |
| | 'is_value_opportunity': is_value_opportunity, |
| | 'value_trap_risk': len(value_trap_indicators) / 5 if value_trap_indicators else 0, |
| | 'value_trap_indicators': value_trap_indicators, |
| | 'analysis': analysis, |
| | 'recommendation': 'buy' if is_value_opportunity and not value_trap_indicators else 'hold', |
| | 'fundamentals': fundamentals, |
| | } |
| | |
| | |
| | if is_value_opportunity and not value_trap_indicators and margin_of_safety > 0.4: |
| | processed_data['insights'].append({ |
| | 'ticker': ticker, |
| | 'type': 'strong_value_opportunity', |
| | 'margin_of_safety': margin_of_safety, |
| | 'intrinsic_value': intrinsic_value, |
| | 'current_price': price, |
| | }) |
| | |
| | |
| | if self.trace_enabled: |
| | processed_data['reflection'] = self.execute_command( |
| | command="reflect.trace", |
| | agent=self.name, |
| | depth=self.reasoning_depth |
| | ) |
| | |
| | return processed_data |
| | |
| | def _calculate_intrinsic_value(self, fundamentals: Dict[str, Any], ticker_data: Dict[str, Any]) -> float: |
| | """ |
| | Calculate intrinsic value using Graham's methods. |
| | |
| | Args: |
| | fundamentals: Fundamental metrics dict |
| | ticker_data: Complete ticker data |
| | |
| | Returns: |
| | Estimated intrinsic value |
| | """ |
| | |
| | eps = fundamentals.get('eps', 0) |
| | book_value = fundamentals.get('book_value_per_share', 0) |
| | growth_rate = fundamentals.get('growth_rate', 0) |
| | |
| | |
| | |
| | |
| | bond_yield = ticker_data.get('economic_indicators', {}).get('aaa_bond_yield', 0.045) |
| | bond_factor = 4.4 / max(bond_yield, 0.01) |
| | |
| | |
| | growth_adjusted_pe = 8.5 + (2 * growth_rate) |
| | |
| | |
| | earnings_value = eps * growth_adjusted_pe * bond_factor if eps > 0 else 0 |
| | |
| | |
| | book_value_margin = book_value * 1.5 |
| | |
| | |
| | if earnings_value > 0 and book_value_margin > 0: |
| | intrinsic_value = min(earnings_value, book_value_margin) |
| | else: |
| | intrinsic_value = earnings_value if earnings_value > 0 else book_value_margin |
| | |
| | return max(intrinsic_value, 0) |
| | |
| | def _detect_value_trap_indicators(self, fundamentals: Dict[str, Any], ticker_data: Dict[str, Any]) -> List[str]: |
| | """ |
| | Detect potential value trap indicators. |
| | |
| | Args: |
| | fundamentals: Fundamental metrics dict |
| | ticker_data: Complete ticker data |
| | |
| | Returns: |
| | List of value trap indicators |
| | """ |
| | value_trap_indicators = [] |
| | |
| | |
| | if fundamentals.get('earnings_growth', 0) < -0.1: |
| | value_trap_indicators.append('declining_earnings') |
| | |
| | |
| | if fundamentals.get('debt_to_equity', 0) > 1.5: |
| | value_trap_indicators.append('high_debt') |
| | |
| | |
| | if fundamentals.get('return_on_equity', 0) < 0.05: |
| | value_trap_indicators.append('low_return_on_equity') |
| | |
| | |
| | if ticker_data.get('sector', {}).get('decline', False): |
| | value_trap_indicators.append('industry_decline') |
| | |
| | |
| | if fundamentals.get('free_cash_flow', 0) < 0: |
| | value_trap_indicators.append('negative_cash_flow') |
| | |
| | return value_trap_indicators |
| | |
| | def _generate_value_analysis(self, ticker: str, fundamentals: Dict[str, Any], |
| | price: float, intrinsic_value: float, |
| | margin_of_safety: float, value_trap_indicators: List[str]) -> str: |
| | """ |
| | Generate value investing analysis summary. |
| | |
| | Args: |
| | ticker: Stock ticker |
| | fundamentals: Fundamental metrics |
| | price: Current price |
| | intrinsic_value: Calculated intrinsic value |
| | margin_of_safety: Current margin of safety |
| | value_trap_indicators: List of value trap indicators |
| | |
| | Returns: |
| | Analysis summary text |
| | """ |
| | |
| | iv_formatted = f"${intrinsic_value:.2f}" |
| | price_formatted = f"${price:.2f}" |
| | mos_percentage = f"{margin_of_safety * 100:.1f}%" |
| | |
| | |
| | if margin_of_safety >= self.margin_of_safety: |
| | base_analysis = (f"{ticker} appears undervalued. Current price {price_formatted} vs. " |
| | f"intrinsic value estimate {iv_formatted}, providing a " |
| | f"{mos_percentage} margin of safety.") |
| | elif margin_of_safety > 0: |
| | base_analysis = (f"{ticker} is moderately priced. Current price {price_formatted} vs. " |
| | f"intrinsic value estimate {iv_formatted}, providing only a " |
| | f"{mos_percentage} margin of safety.") |
| | else: |
| | base_analysis = (f"{ticker} appears overvalued. Current price {price_formatted} vs. " |
| | f"intrinsic value estimate {iv_formatted}, providing no " |
| | f"margin of safety.") |
| | |
| | |
| | if value_trap_indicators: |
| | trap_text = ", ".join(value_trap_indicators) |
| | base_analysis += f" Warning: Potential value trap indicators detected: {trap_text}." |
| | |
| | |
| | fundamental_highlights = [] |
| | if fundamentals.get('pe_ratio', 0) > 0: |
| | fundamental_highlights.append(f"P/E ratio: {fundamentals.get('pe_ratio', 0):.2f}") |
| | if fundamentals.get('price_to_book', 0) > 0: |
| | fundamental_highlights.append(f"P/B ratio: {fundamentals.get('price_to_book', 0):.2f}") |
| | if fundamentals.get('dividend_yield', 0) > 0: |
| | fundamental_highlights.append(f"Dividend yield: {fundamentals.get('dividend_yield', 0) * 100:.2f}%") |
| | |
| | if fundamental_highlights: |
| | base_analysis += " Key metrics: " + ", ".join(fundamental_highlights) + "." |
| | |
| | return base_analysis |
| | |
| | def generate_signals(self, processed_data: Dict[str, Any]) -> List[AgentSignal]: |
| | """ |
| | Generate investment signals based on processed value investing analysis. |
| | |
| | Args: |
| | processed_data: Processed market data with value analysis |
| | |
| | Returns: |
| | List of investment signals with attribution |
| | """ |
| | signals = [] |
| | |
| | for ticker, ticker_data in processed_data.get('tickers', {}).items(): |
| | |
| | if ticker_data.get('analysis') == 'insufficient_data': |
| | continue |
| | |
| | |
| | is_value_opportunity = ticker_data.get('is_value_opportunity', False) |
| | value_trap_risk = ticker_data.get('value_trap_risk', 0) |
| | margin_of_safety = ticker_data.get('margin_of_safety', 0) |
| | |
| | |
| | if not is_value_opportunity and margin_of_safety <= 0: |
| | continue |
| | |
| | |
| | if is_value_opportunity and value_trap_risk < 0.3: |
| | action = 'buy' |
| | |
| | confidence = min(0.5 + (margin_of_safety * 0.5), 0.95) |
| | |
| | |
| | max_allocation = 0.1 |
| | allocation = max_allocation * confidence |
| | quantity = int((self.current_capital * allocation) / ticker_data.get('price', 1)) |
| | |
| | |
| | quantity = max(quantity, 1) |
| | |
| | |
| | signal = { |
| | 'ticker': ticker, |
| | 'action': action, |
| | 'confidence': confidence, |
| | 'quantity': quantity, |
| | 'reasoning': f"Value investment opportunity with {margin_of_safety:.1%} margin of safety. {ticker_data.get('analysis', '')}", |
| | 'intent': "Capitalize on identified value opportunity with sufficient margin of safety", |
| | 'value_basis': "Intrinsic value significantly exceeds current market price, presenting favorable risk-reward", |
| | } |
| | |
| | signals.append(signal) |
| | elif margin_of_safety > 0 and margin_of_safety < self.margin_of_safety and value_trap_risk < 0.2: |
| | |
| | action = 'buy' |
| | confidence = 0.3 + (margin_of_safety * 0.3) |
| | |
| | |
| | max_allocation = 0.05 |
| | allocation = max_allocation * confidence |
| | quantity = int((self.current_capital * allocation) / ticker_data.get('price', 1)) |
| | |
| | |
| | quantity = max(quantity, 1) |
| | |
| | |
| | signal = { |
| | 'ticker': ticker, |
| | 'action': action, |
| | 'confidence': confidence, |
| | 'quantity': quantity, |
| | 'reasoning': f"Moderate value opportunity with {margin_of_safety:.1%} margin of safety. {ticker_data.get('analysis', '')}", |
| | 'intent': "Establish small position in moderately valued company with potential", |
| | 'value_basis': "Price below intrinsic value but insufficient margin of safety for full position", |
| | } |
| | |
| | signals.append(signal) |
| | |
| | |
| | attributed_signals = self.attribute_signals(signals) |
| | |
| | |
| | if self.trace_enabled: |
| | for signal in attributed_signals: |
| | self.tracer.record_signal(signal) |
| | |
| | return attributed_signals |
| | |
| | |
| | def _intrinsic_value_analysis(self, data: Dict[str, Any]) -> Dict[str, Any]: |
| | """ |
| | Analyze intrinsic value of securities. |
| | |
| | Args: |
| | data: Market data |
| | |
| | Returns: |
| | Intrinsic value analysis results |
| | """ |
| | results = { |
| | 'ticker_valuations': {}, |
| | 'timestamp': datetime.datetime.now(), |
| | } |
| | |
| | for ticker, ticker_data in data.get('tickers', {}).items(): |
| | fundamentals = ticker_data.get('fundamentals', {}) |
| | price = ticker_data.get('price', 0) |
| | |
| | if not fundamentals or price == 0: |
| | results['ticker_valuations'][ticker] = { |
| | 'intrinsic_value': None, |
| | 'status': 'insufficient_data', |
| | } |
| | continue |
| | |
| | |
| | intrinsic_value = self._calculate_intrinsic_value(fundamentals, ticker_data) |
| | |
| | |
| | if intrinsic_value > price * 1.3: |
| | status = 'significantly_undervalued' |
| | elif intrinsic_value > price * 1.1: |
| | status = 'moderately_undervalued' |
| | elif intrinsic_value > price: |
| | status = 'slightly_undervalued' |
| | elif intrinsic_value > price * 0.9: |
| | status = 'fairly_valued' |
| | else: |
| | status = 'overvalued' |
| | |
| | results['ticker_valuations'][ticker] = { |
| | 'intrinsic_value': intrinsic_value, |
| | 'price': price, |
| | 'ratio': intrinsic_value / price if price > 0 else 0, |
| | 'status': status, |
| | } |
| | |
| | return results |
| | |
| | def _margin_of_safety_evaluation(self, intrinsic_value_results: Dict[str, Any]) -> Dict[str, Any]: |
| | """ |
| | Evaluate margin of safety for each security. |
| | |
| | Args: |
| | intrinsic_value_results: Intrinsic value analysis results |
| | |
| | Returns: |
| | Margin of safety evaluation results |
| | """ |
| | results = { |
| | 'margin_of_safety_analysis': {}, |
| | 'value_opportunities': [], |
| | 'timestamp': datetime.datetime.now(), |
| | } |
| | |
| | for ticker, valuation in intrinsic_value_results.get('ticker_valuations', {}).items(): |
| | if valuation.get('status') == 'insufficient_data': |
| | continue |
| | |
| | intrinsic_value = valuation.get('intrinsic_value', 0) |
| | price = valuation.get('price', 0) |
| | |
| | if intrinsic_value <= 0 or price <= 0: |
| | continue |
| | |
| | |
| | margin_of_safety = (intrinsic_value - price) / intrinsic_value |
| | |
| | |
| | if margin_of_safety >= self.margin_of_safety: |
| | confidence = min(0.5 + (margin_of_safety * 0.5), 0.95) |
| | meets_criteria = True |
| | else: |
| | confidence = max(0.2, margin_of_safety * 2) |
| | meets_criteria = False |
| | |
| | results['margin_of_safety_analysis'][ticker] = { |
| | 'margin_of_safety': margin_of_safety, |
| | 'meets_criteria': meets_criteria, |
| | 'confidence': confidence, |
| | } |
| | |
| | |
| | if meets_criteria: |
| | results['value_opportunities'].append({ |
| | 'ticker': ticker, |
| | 'margin_of_safety': margin_of_safety, |
| | 'confidence': confidence, |
| | 'intrinsic_value': intrinsic_value, |
| | 'price': price, |
| | }) |
| | |
| | |
| | results['value_opportunities'] = sorted( |
| | results['value_opportunities'], |
| | key=lambda x: x['margin_of_safety'], |
| | reverse=True |
| | ) |
| | |
| | return results |
| | |
| | def _fundamental_analysis(self, safety_results: Dict[str, Any]) -> Dict[str, Any]: |
| | """ |
| | Perform fundamental analysis on value opportunities. |
| | |
| | Args: |
| | safety_results: Margin of safety evaluation results |
| | |
| | Returns: |
| | Fundamental analysis results |
| | """ |
| | results = { |
| | 'fundamental_quality': {}, |
| | 'quality_ranking': [], |
| | 'timestamp': datetime.datetime.now(), |
| | } |
| | |
| | |
| | for opportunity in safety_results.get('value_opportunities', []): |
| | ticker = opportunity.get('ticker') |
| | |
| | |
| | ticker_data = self.state.working_memory.get('market_data', {}).get('tickers', {}).get(ticker, {}) |
| | fundamentals = ticker_data.get('fundamentals', {}) |
| | |
| | if not fundamentals: |
| | continue |
| | |
| | |
| | quality_score = self._calculate_fundamental_quality(fundamentals) |
| | |
| | |
| | results['fundamental_quality'][ticker] = { |
| | 'quality_score': quality_score, |
| | 'roe': fundamentals.get('return_on_equity', 0), |
| | 'debt_to_equity': fundamentals.get('debt_to_equity', 0), |
| | 'current_ratio': fundamentals.get('current_ratio', 0), |
| | 'free_cash_flow': fundamentals.get('free_cash_flow', 0), |
| | 'dividend_history': fundamentals.get('dividend_history', []), |
| | } |
| | |
| | |
| | results['quality_ranking'].append({ |
| | 'ticker': ticker, |
| | 'quality_score': quality_score, |
| | 'margin_of_safety': opportunity.get('margin_of_safety', 0), |
| | |
| | 'combined_score': quality_score * 0.4 + opportunity.get('margin_of_safety', 0) * 0.6, |
| | }) |
| | |
| | |
| | results['quality_ranking'] = sorted( |
| | results['quality_ranking'], |
| | key=lambda x: x['combined_score'], |
| | reverse=True |
| | ) |
| | |
| | return results |
| | |
| | def _calculate_fundamental_quality(self, fundamentals: Dict[str, Any]) -> float: |
| | """ |
| | Calculate fundamental quality score. |
| | |
| | Args: |
| | fundamentals: Fundamental metrics |
| | |
| | Returns: |
| | Quality score (0-1) |
| | """ |
| | |
| | score = 0.5 |
| | |
| | |
| | roe = fundamentals.get('return_on_equity', 0) |
| | if roe > 0.2: |
| | score += 0.1 |
| | elif roe > 0.15: |
| | score += 0.075 |
| | elif roe > 0.1: |
| | score += 0.05 |
| | elif roe < 0.05: |
| | score -= 0.05 |
| | elif roe < 0: |
| | score -= 0.1 |
| | |
| | |
| | debt_to_equity = fundamentals.get('debt_to_equity', 0) |
| | if debt_to_equity < 0.3: |
| | score += 0.1 |
| | elif debt_to_equity < 0.5: |
| | score += 0.05 |
| | elif debt_to_equity > 1.0: |
| | score -= 0.05 |
| | elif debt_to_equity > 1.5: |
| | score -= 0.1 |
| | |
| | |
| | current_ratio = fundamentals.get('current_ratio', 0) |
| | if current_ratio > 3: |
| | score += 0.075 |
| | elif current_ratio > 2: |
| | score += 0.05 |
| | elif current_ratio > 1.5: |
| | score += 0.025 |
| | elif current_ratio < 1: |
| | score -= 0.1 |
| | |
| | |
| | fcf = fundamentals.get('free_cash_flow', 0) |
| | if fcf > 0: |
| | score += 0.075 |
| | else: |
| | score -= 0.1 |
| | |
| | |
| | dividend_history = fundamentals.get('dividend_history', []) |
| | if len(dividend_history) >= 5 and all(d > 0 for d in dividend_history): |
| | |
| | score += 0.075 |
| | elif len(dividend_history) >= 3 and all(d > 0 for d in dividend_history): |
| | |
| | score += 0.05 |
| | |
| | |
| | return max(0, min(1, score)) |
| | |
| | def _value_trap_detection(self, fundamental_results: Dict[str, Any]) -> Dict[str, Any]: |
| | """ |
| | Detect potential value traps among value opportunities. |
| | |
| | Args: |
| | fundamental_results: Fundamental analysis results |
| | |
| | Returns: |
| | Value trap detection results |
| | """ |
| | results = { |
| | 'value_trap_analysis': {}, |
| | 'safe_opportunities': [], |
| | 'timestamp': datetime.datetime.now(), |
| | } |
| | |
| | |
| | for opportunity in fundamental_results.get('quality_ranking', []): |
| | ticker = opportunity.get('ticker') |
| | |
| | |
| | ticker_data = self.state.working_memory.get('market_data', {}).get('tickers', {}).get(ticker, {}) |
| | fundamentals = ticker_data.get('fundamentals', {}) |
| | |
| | if not fundamentals: |
| | continue |
| | |
| | |
| | value_trap_indicators = self._detect_value_trap_indicators(fundamentals, ticker_data) |
| | value_trap_risk = len(value_trap_indicators) / 5 if value_trap_indicators else 0 |
| | |
| | |
| | results['value_trap_analysis'][ticker] = { |
| | 'value_trap_risk': value_trap_risk, |
| | 'value_trap_indicators': value_trap_indicators, |
| | 'quality_score': opportunity.get('quality_score', 0), |
| | 'margin_of_safety': opportunity.get('margin_of_safety', 0), |
| | 'combined_score': opportunity.get('combined_score', 0), |
| | } |
| | |
| | |
| | if value_trap_risk < 0.3: |
| | results['safe_opportunities'].append({ |
| | 'ticker': ticker, |
| | 'value_trap_risk': value_trap_risk, |
| | 'quality_score': opportunity.get('quality_score', 0), |
| | 'margin_of_safety': opportunity.get('margin_of_safety', 0), |
| | 'combined_score': opportunity.get('combined_score', 0), |
| | }) |
| | |
| | |
| | results['safe_opportunities'] = sorted( |
| | results['safe_opportunities'], |
| | key=lambda x: x['combined_score'], |
| | reverse=True |
| | ) |
| | |
| | return results |
| |
|
| | def run_analysis_shell(self, market_data: Dict[str, Any]) -> Dict[str, Any]: |
| | """ |
| | Run a complete analysis shell for Graham value criteria. |
| | |
| | This method implements a full CIRCUIT-FRAGMENT shell for detecting |
| | undervalued assets and NULL-FEATURE shell for knowledge boundaries. |
| | |
| | Args: |
| | market_data: Raw market data |
| | |
| | Returns: |
| | Complete value analysis results |
| | """ |
| | |
| | self.state.working_memory['market_data'] = market_data |
| | |
| | |
| | processed_data = self.process_market_data(market_data) |
| | |
| | |
| | initial_results = {'tickers': processed_data.get('tickers', {})} |
| | |
| | intrinsic_value_results = self._intrinsic_value_analysis(initial_results) |
| | safety_results = self._margin_of_safety_evaluation(intrinsic_value_results) |
| | fundamental_results = self._fundamental_analysis(safety_results) |
| | trap_results = self._value_trap_detection(fundamental_results) |
| | |
| | |
| | complete_results = { |
| | 'processed_data': processed_data, |
| | 'intrinsic_value_results': intrinsic_value_results, |
| | 'safety_results': safety_results, |
| | 'fundamental_results': fundamental_results, |
| | 'trap_results': trap_results, |
| | 'final_recommendations': trap_results.get('safe_opportunities', []), |
| | 'timestamp': datetime.datetime.now(), |
| | } |
| | |
| | |
| | collapse_check = self.execute_command( |
| | command="collapse.detect", |
| | threshold=0.7, |
| | reason="consistency" |
| | ) |
| | |
| | if collapse_check.get('collapse_detected', False): |
| | complete_results['warnings'] = { |
| | 'collapse_detected': True, |
| | 'collapse_reasons': collapse_check.get('collapse_reasons', {}), |
| | 'message': "Potential inconsistency detected in value analysis process.", |
| | } |
| | |
| | return complete_results |
| | |
| | def adjust_strategy(self, performance_metrics: Dict[str, Any]) -> None: |
| | """ |
| | Adjust Graham strategy based on performance feedback. |
| | |
| | Args: |
| | performance_metrics: Dictionary with performance metrics |
| | """ |
| | |
| | win_rate = performance_metrics.get('win_rate', 0.5) |
| | avg_return = performance_metrics.get('avg_return', 0) |
| | max_drawdown = performance_metrics.get('max_drawdown', 0) |
| | |
| | |
| | if win_rate < 0.4: |
| | self.margin_of_safety = min(self.margin_of_safety + 0.05, 0.5) |
| | elif win_rate > 0.7: |
| | self.margin_of_safety = max(self.margin_of_safety - 0.05, 0.2) |
| | |
| | |
| | if avg_return < -0.05: |
| | self.state.reflective_state['value_detection_threshold'] = min( |
| | self.state.reflective_state.get('value_detection_threshold', 0.7) + 0.05, |
| | 0.9 |
| | ) |
| | elif avg_return > 0.1: |
| | self.state.reflective_state['value_detection_threshold'] = max( |
| | self.state.reflective_state.get('value_detection_threshold', 0.7) - 0.05, |
| | 0.6 |
| | ) |
| | |
| | |
| | if max_drawdown > 0.15: |
| | self.state.reflective_state['sentiment_skepticism'] = min( |
| | self.state.reflective_state.get('sentiment_skepticism', 0.8) + 0.05, |
| | 0.95 |
| | ) |
| | |
| | |
| | drift_vector = { |
| | 'margin_of_safety': self.margin_of_safety - 0.3, |
| | 'value_detection': self.state.reflective_state.get('value_detection_threshold', 0.7) - 0.7, |
| | 'sentiment_skepticism': self.state.reflective_state.get('sentiment_skepticism', 0.8) - 0.8, |
| | } |
| | |
| | |
| | self.execute_command( |
| | command="drift.observe", |
| | vector=drift_vector, |
| | bias=0.0 |
| | ) |
| | |
| | def __repr__(self) -> str: |
| | """Generate string representation of Graham agent.""" |
| | return f"Graham Value Agent (MoS: {self.margin_of_safety:.2f}, Depth: {self.reasoning_depth})" |
| | |
| |
|