682 lines
23 KiB
Python
682 lines
23 KiB
Python
"""Unit tests for BullResearcher Agent.
|
|
|
|
This module tests the BullResearcher class including bull case generation,
|
|
counter-arguments, price targets, and decision cost deduction.
|
|
"""
|
|
|
|
import asyncio
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
from openclaw.agents.base import ActivityType
|
|
from openclaw.agents.bull_researcher import BullReport, BullResearcher
|
|
from openclaw.agents.trader import MarketAnalysis
|
|
from openclaw.core.economy import SurvivalStatus
|
|
|
|
|
|
class TestBullReport:
|
|
"""Test BullReport dataclass."""
|
|
|
|
def test_default_creation(self):
|
|
"""Test creating BullReport with defaults."""
|
|
report = BullReport(symbol="AAPL")
|
|
|
|
assert report.symbol == "AAPL"
|
|
assert report.bullish_factors == []
|
|
assert report.counter_arguments == {}
|
|
assert report.price_target == 0.0
|
|
assert report.conviction_level == 0.5
|
|
assert report.summary == ""
|
|
assert report.risk_factors == []
|
|
assert report.catalysts == []
|
|
|
|
def test_full_creation(self):
|
|
"""Test creating BullReport with all fields."""
|
|
report = BullReport(
|
|
symbol="TSLA",
|
|
bullish_factors=["Strong growth", "Market leadership"],
|
|
counter_arguments={"Overvalued": "Growth justifies premium"},
|
|
price_target=250.0,
|
|
conviction_level=0.75,
|
|
summary="Bull case for TSLA",
|
|
risk_factors=["Competition", "Regulation"],
|
|
catalysts=["Earnings beat", "New product launch"],
|
|
)
|
|
|
|
assert report.symbol == "TSLA"
|
|
assert len(report.bullish_factors) == 2
|
|
assert report.price_target == 250.0
|
|
assert report.conviction_level == 0.75
|
|
|
|
def test_conviction_bounds(self):
|
|
"""Test conviction level is bounded between 0 and 1."""
|
|
report_high = BullReport(symbol="AAPL", conviction_level=1.5)
|
|
assert report_high.conviction_level == 1.0
|
|
|
|
report_low = BullReport(symbol="AAPL", conviction_level=-0.5)
|
|
assert report_low.conviction_level == 0.0
|
|
|
|
|
|
class TestBullResearcherInitialization:
|
|
"""Test BullResearcher initialization."""
|
|
|
|
def test_default_initialization(self):
|
|
"""Test agent with default parameters."""
|
|
agent = BullResearcher(agent_id="bull-1", initial_capital=10000.0)
|
|
|
|
assert agent.agent_id == "bull-1"
|
|
assert agent.balance == 10000.0
|
|
assert agent.skill_level == 0.5
|
|
assert agent.decision_cost == 0.15
|
|
assert agent._last_report is None
|
|
assert agent._report_history == []
|
|
|
|
def test_custom_initialization(self):
|
|
"""Test agent with custom parameters."""
|
|
agent = BullResearcher(
|
|
agent_id="bull-2",
|
|
initial_capital=5000.0,
|
|
skill_level=0.8,
|
|
)
|
|
|
|
assert agent.agent_id == "bull-2"
|
|
assert agent.balance == 5000.0
|
|
assert agent.skill_level == 0.8
|
|
|
|
def test_inherits_from_base_agent(self):
|
|
"""Test that BullResearcher inherits from BaseAgent."""
|
|
from openclaw.agents.base import BaseAgent
|
|
|
|
agent = BullResearcher(agent_id="test", initial_capital=10000.0)
|
|
|
|
assert isinstance(agent, BaseAgent)
|
|
|
|
|
|
class TestDecideActivity:
|
|
"""Test decide_activity method."""
|
|
|
|
@pytest.fixture
|
|
def agent(self):
|
|
"""Create a test agent."""
|
|
return BullResearcher(agent_id="test", initial_capital=10000.0)
|
|
|
|
def test_bankrupt_agent_only_rests(self, agent):
|
|
"""Test that bankrupt agent can only rest."""
|
|
agent.economic_tracker.balance = 0 # Bankrupt
|
|
|
|
result = asyncio.run(agent.decide_activity())
|
|
|
|
assert result == ActivityType.REST
|
|
|
|
def test_critical_status_prefers_learning(self, agent):
|
|
"""Test critical status leads to learning."""
|
|
agent.economic_tracker.balance = 3500.0 # Critical
|
|
agent.state.skill_level = 0.5
|
|
|
|
result = asyncio.run(agent.decide_activity())
|
|
|
|
assert result in [ActivityType.LEARN, ActivityType.PAPER_TRADE]
|
|
|
|
def test_stable_status_prefers_analysis(self, agent):
|
|
"""Test stable status leads to analysis/paper trade."""
|
|
agent.economic_tracker.balance = 12000.0 # Stable
|
|
|
|
result = asyncio.run(agent.decide_activity())
|
|
|
|
assert result in [ActivityType.ANALYZE, ActivityType.PAPER_TRADE]
|
|
|
|
def test_thriving_status_prefers_analysis(self, agent):
|
|
"""Test thriving status leads to analysis."""
|
|
agent.economic_tracker.balance = 20000.0 # Thriving
|
|
|
|
result = asyncio.run(agent.decide_activity())
|
|
|
|
assert result == ActivityType.ANALYZE
|
|
|
|
|
|
class TestAnalyze:
|
|
"""Test analyze method (async)."""
|
|
|
|
@pytest.fixture
|
|
def agent(self):
|
|
"""Create a test agent."""
|
|
return BullResearcher(agent_id="test", initial_capital=10000.0)
|
|
|
|
def test_analyze_returns_dict(self, agent):
|
|
"""Test that analyze returns a dictionary."""
|
|
result = asyncio.run(agent.analyze("AAPL"))
|
|
|
|
assert isinstance(result, dict)
|
|
assert result["symbol"] == "AAPL"
|
|
assert "bull_report" in result
|
|
assert "cost" in result
|
|
|
|
def test_analyze_deducts_cost(self, agent):
|
|
"""Test that analyze deducts decision cost."""
|
|
initial_balance = agent.balance
|
|
|
|
asyncio.run(agent.analyze("AAPL"))
|
|
|
|
assert agent.balance == initial_balance - 0.15
|
|
|
|
def test_analyze_stores_last_report(self, agent):
|
|
"""Test that analyze stores the report."""
|
|
assert agent._last_report is None
|
|
|
|
asyncio.run(agent.analyze("TSLA"))
|
|
|
|
assert agent._last_report is not None
|
|
assert agent._last_report.symbol == "TSLA"
|
|
|
|
def test_analyze_adds_to_history(self, agent):
|
|
"""Test that analyze adds to report history."""
|
|
assert len(agent._report_history) == 0
|
|
|
|
asyncio.run(agent.analyze("AAPL"))
|
|
asyncio.run(agent.analyze("TSLA"))
|
|
|
|
assert len(agent._report_history) == 2
|
|
|
|
|
|
class TestGenerateBullCase:
|
|
"""Test generate_bull_case method."""
|
|
|
|
@pytest.fixture
|
|
def agent(self):
|
|
"""Create a test agent."""
|
|
return BullResearcher(agent_id="test", initial_capital=10000.0)
|
|
|
|
def test_returns_bull_report(self, agent):
|
|
"""Test that generate_bull_case returns BullReport."""
|
|
result = asyncio.run(agent.generate_bull_case("AAPL"))
|
|
|
|
assert isinstance(result, BullReport)
|
|
assert result.symbol == "AAPL"
|
|
|
|
def test_deducts_decision_cost(self, agent):
|
|
"""Test that decision cost is deducted."""
|
|
initial_balance = agent.balance
|
|
|
|
asyncio.run(agent.generate_bull_case("AAPL"))
|
|
|
|
assert agent.balance == initial_balance - 0.15
|
|
|
|
def test_includes_bullish_factors(self, agent):
|
|
"""Test that bull report includes bullish factors."""
|
|
result = asyncio.run(agent.generate_bull_case("AAPL"))
|
|
|
|
assert isinstance(result.bullish_factors, list)
|
|
assert len(result.bullish_factors) > 0
|
|
|
|
def test_includes_counter_arguments(self, agent):
|
|
"""Test that bull report includes counter-arguments."""
|
|
result = asyncio.run(agent.generate_bull_case("AAPL"))
|
|
|
|
assert isinstance(result.counter_arguments, dict)
|
|
assert len(result.counter_arguments) > 0
|
|
|
|
def test_includes_price_target(self, agent):
|
|
"""Test that bull report includes price target."""
|
|
result = asyncio.run(agent.generate_bull_case("AAPL"))
|
|
|
|
assert isinstance(result.price_target, float)
|
|
assert result.price_target >= 0
|
|
|
|
def test_includes_conviction_level(self, agent):
|
|
"""Test that bull report includes conviction level."""
|
|
result = asyncio.run(agent.generate_bull_case("AAPL"))
|
|
|
|
assert 0.0 <= result.conviction_level <= 1.0
|
|
|
|
def test_includes_summary(self, agent):
|
|
"""Test that bull report includes summary."""
|
|
result = asyncio.run(agent.generate_bull_case("AAPL"))
|
|
|
|
assert isinstance(result.summary, str)
|
|
assert len(result.summary) > 0
|
|
|
|
def test_includes_risk_factors(self, agent):
|
|
"""Test that bull report includes risk factors."""
|
|
result = asyncio.run(agent.generate_bull_case("AAPL"))
|
|
|
|
assert isinstance(result.risk_factors, list)
|
|
assert len(result.risk_factors) > 0
|
|
|
|
def test_includes_catalysts(self, agent):
|
|
"""Test that bull report includes catalysts."""
|
|
result = asyncio.run(agent.generate_bull_case("AAPL"))
|
|
|
|
assert isinstance(result.catalysts, list)
|
|
assert len(result.catalysts) > 0
|
|
|
|
def test_stores_report_in_history(self, agent):
|
|
"""Test that generated report is stored in history."""
|
|
assert len(agent._report_history) == 0
|
|
|
|
asyncio.run(agent.generate_bull_case("AAPL"))
|
|
|
|
assert len(agent._report_history) == 1
|
|
assert agent._last_report is not None
|
|
|
|
|
|
class TestExtractBullishFactors:
|
|
"""Test bullish factor extraction."""
|
|
|
|
@pytest.fixture
|
|
def agent(self):
|
|
"""Create a test agent."""
|
|
return BullResearcher(agent_id="test", initial_capital=10000.0)
|
|
|
|
def test_extract_from_technical_dict(self, agent):
|
|
"""Test extracting factors from technical report (dict format)."""
|
|
technical = {
|
|
"trend": "uptrend",
|
|
"indicators": {"rsi": 35.0, "macd": 0.5, "current_price": 100.0},
|
|
}
|
|
|
|
factors = agent._extract_bullish_factors(technical, None, None)
|
|
|
|
assert any("uptrend" in f.lower() for f in factors)
|
|
assert any("RSI" in f or "oversold" in f.lower() for f in factors)
|
|
|
|
def test_extract_from_technical_object(self, agent):
|
|
"""Test extracting factors from technical report (object format)."""
|
|
technical = MarketAnalysis(
|
|
symbol="AAPL",
|
|
trend="uptrend",
|
|
volatility=0.2,
|
|
volume_trend="increasing",
|
|
support_level=90.0,
|
|
resistance_level=110.0,
|
|
indicators={"rsi": 35.0, "macd": 0.5, "current_price": 100.0},
|
|
)
|
|
|
|
factors = agent._extract_bullish_factors(technical, None, None)
|
|
|
|
assert any("uptrend" in f.lower() for f in factors)
|
|
|
|
def test_extract_from_sentiment_dict(self, agent):
|
|
"""Test extracting factors from sentiment report (dict format)."""
|
|
sentiment = {"sentiment": "bullish", "score": 0.75}
|
|
|
|
factors = agent._extract_bullish_factors(None, sentiment, None)
|
|
|
|
assert any("sentiment" in f.lower() for f in factors)
|
|
|
|
def test_extract_from_fundamental_dict(self, agent):
|
|
"""Test extracting factors from fundamental report (dict format)."""
|
|
fundamental = {
|
|
"valuation": "undervalued",
|
|
"growth_rate": 0.25,
|
|
"pe_ratio": 15.0,
|
|
}
|
|
|
|
factors = agent._extract_fundamental_bullish_factors(fundamental)
|
|
|
|
assert any("undervalued" in f.lower() for f in factors)
|
|
assert any("growth" in f.lower() for f in factors)
|
|
|
|
def test_empty_reports_placeholder(self, agent):
|
|
"""Test placeholder factor when no reports provided."""
|
|
factors = agent._extract_bullish_factors(None, None, None)
|
|
|
|
assert len(factors) == 1
|
|
assert "pending" in factors[0].lower()
|
|
|
|
|
|
class TestGenerateCounterArguments:
|
|
"""Test counter-argument generation."""
|
|
|
|
@pytest.fixture
|
|
def agent(self):
|
|
"""Create a test agent."""
|
|
return BullResearcher(agent_id="test", initial_capital=10000.0)
|
|
|
|
def test_includes_common_counters(self, agent):
|
|
"""Test that common counter-arguments are included."""
|
|
counters = agent._generate_counter_arguments(None, None, None)
|
|
|
|
assert "Stock is overbought" in counters
|
|
assert "Valuation is stretched" in counters
|
|
assert "Recent rally is unsustainable" in counters
|
|
assert "Market sentiment is too optimistic" in counters
|
|
|
|
def test_counters_are_strings(self, agent):
|
|
"""Test that all counter-arguments are strings."""
|
|
counters = agent._generate_counter_arguments(None, None, None)
|
|
|
|
for key, value in counters.items():
|
|
assert isinstance(key, str)
|
|
assert isinstance(value, str)
|
|
assert len(value) > 0
|
|
|
|
|
|
class TestCalculateConviction:
|
|
"""Test conviction calculation."""
|
|
|
|
@pytest.fixture
|
|
def agent(self):
|
|
"""Create a test agent."""
|
|
return BullResearcher(agent_id="test", initial_capital=10000.0)
|
|
|
|
def test_base_conviction(self, agent):
|
|
"""Test base conviction level."""
|
|
conviction = agent._calculate_conviction([], None, None, None)
|
|
|
|
assert conviction >= 0.5 # Base conviction
|
|
assert conviction <= 1.0
|
|
|
|
def test_factors_boost_conviction(self, agent):
|
|
"""Test that more factors increase conviction."""
|
|
low_factors = ["Factor 1"]
|
|
high_factors = ["Factor 1", "Factor 2", "Factor 3", "Factor 4", "Factor 5"]
|
|
|
|
low_conviction = agent._calculate_conviction(low_factors, None, None, None)
|
|
high_conviction = agent._calculate_conviction(high_factors, None, None, None)
|
|
|
|
assert high_conviction >= low_conviction
|
|
|
|
def test_all_reports_max_conviction(self, agent):
|
|
"""Test that having all reports allows higher conviction."""
|
|
factors = ["Factor 1", "Factor 2", "Factor 3"]
|
|
|
|
partial_conviction = agent._calculate_conviction(factors, None, None, None)
|
|
full_conviction = agent._calculate_conviction(
|
|
factors, {"trend": "up"}, {"sentiment": "bullish"}, {"pe": 15}
|
|
)
|
|
|
|
# With all reports, max conviction is higher
|
|
assert full_conviction >= partial_conviction
|
|
|
|
|
|
class TestPriceTarget:
|
|
"""Test price target generation."""
|
|
|
|
@pytest.fixture
|
|
def agent(self):
|
|
"""Create a test agent."""
|
|
return BullResearcher(agent_id="test", initial_capital=10000.0)
|
|
|
|
def test_default_price(self, agent):
|
|
"""Test default price when no reports."""
|
|
target = agent._generate_price_target("AAPL", None, None)
|
|
|
|
# Default current price is 100, with 10-20% upside
|
|
assert target >= 110.0
|
|
assert target <= 130.0
|
|
|
|
def test_price_from_technical(self, agent):
|
|
"""Test price target from technical report."""
|
|
technical = {"indicators": {"current_price": 150.0}}
|
|
|
|
target = agent._generate_price_target("AAPL", None, technical)
|
|
|
|
# Target should be above current price
|
|
assert target > 150.0
|
|
|
|
def test_fundamental_boosts_target(self, agent):
|
|
"""Test that fundamental report adds upside."""
|
|
technical = {"indicators": {"current_price": 100.0}}
|
|
fundamental = {"pe_ratio": 15.0}
|
|
|
|
target_without = agent._generate_price_target("AAPL", None, technical)
|
|
target_with = agent._generate_price_target("AAPL", fundamental, technical)
|
|
|
|
# With fundamental, target should be higher
|
|
assert target_with > target_without
|
|
|
|
|
|
class TestIdentifyCatalysts:
|
|
"""Test catalyst identification."""
|
|
|
|
@pytest.fixture
|
|
def agent(self):
|
|
"""Create a test agent."""
|
|
return BullResearcher(agent_id="test", initial_capital=10000.0)
|
|
|
|
def test_basic_catalysts(self, agent):
|
|
"""Test that basic catalysts are identified."""
|
|
catalysts = agent._identify_catalysts(None, None, None)
|
|
|
|
assert len(catalysts) >= 4
|
|
assert any("Earnings" in c for c in catalysts)
|
|
assert any("Institutional" in c for c in catalysts)
|
|
|
|
def test_high_skill_extra_catalyst(self, agent):
|
|
"""Test that high skill adds extra catalysts."""
|
|
agent.state.skill_level = 0.8
|
|
|
|
catalysts = agent._identify_catalysts(None, None, None)
|
|
|
|
assert any("sector" in c.lower() for c in catalysts)
|
|
|
|
|
|
class TestIdentifyRisks:
|
|
"""Test risk identification."""
|
|
|
|
@pytest.fixture
|
|
def agent(self):
|
|
"""Create a test agent."""
|
|
return BullResearcher(agent_id="test", initial_capital=10000.0)
|
|
|
|
def test_basic_risks(self, agent):
|
|
"""Test that basic risks are identified."""
|
|
risks = agent._identify_risks(None, None, None)
|
|
|
|
assert len(risks) >= 3
|
|
assert any("market" in r.lower() for r in risks)
|
|
assert any("earnings" in r.lower() for r in risks)
|
|
|
|
def test_high_skill_extra_risk(self, agent):
|
|
"""Test that high skill adds extra risks."""
|
|
agent.state.skill_level = 0.7
|
|
|
|
risks = agent._identify_risks(None, None, None)
|
|
|
|
assert any("regulatory" in r.lower() for r in risks)
|
|
|
|
|
|
class TestGetLastReport:
|
|
"""Test get_last_report method."""
|
|
|
|
@pytest.fixture
|
|
def agent(self):
|
|
"""Create a test agent."""
|
|
return BullResearcher(agent_id="test", initial_capital=10000.0)
|
|
|
|
def test_no_report_returns_none(self, agent):
|
|
"""Test that None is returned when no reports."""
|
|
result = agent.get_last_report()
|
|
|
|
assert result is None
|
|
|
|
def test_returns_last_report(self, agent):
|
|
"""Test that last report is returned."""
|
|
asyncio.run(agent.generate_bull_case("AAPL"))
|
|
|
|
result = agent.get_last_report()
|
|
|
|
assert result is not None
|
|
assert result.symbol == "AAPL"
|
|
|
|
|
|
class TestGetReportHistory:
|
|
"""Test get_report_history method."""
|
|
|
|
@pytest.fixture
|
|
def agent(self):
|
|
"""Create a test agent."""
|
|
return BullResearcher(agent_id="test", initial_capital=10000.0)
|
|
|
|
def test_empty_history(self, agent):
|
|
"""Test empty history."""
|
|
history = agent.get_report_history()
|
|
|
|
assert history == []
|
|
|
|
def test_returns_copy(self, agent):
|
|
"""Test that history returns a copy."""
|
|
asyncio.run(agent.generate_bull_case("AAPL"))
|
|
|
|
history = agent.get_report_history()
|
|
history.append(None) # Modify the copy
|
|
|
|
# Original should be unchanged
|
|
assert len(agent._report_history) == 1
|
|
|
|
def test_multiple_reports(self, agent):
|
|
"""Test history with multiple reports."""
|
|
asyncio.run(agent.generate_bull_case("AAPL"))
|
|
asyncio.run(agent.generate_bull_case("TSLA"))
|
|
asyncio.run(agent.generate_bull_case("NVDA"))
|
|
|
|
history = agent.get_report_history()
|
|
|
|
assert len(history) == 3
|
|
|
|
|
|
class TestGetBullishRecommendation:
|
|
"""Test get_bullish_recommendation method."""
|
|
|
|
@pytest.fixture
|
|
def agent(self):
|
|
"""Create a test agent."""
|
|
return BullResearcher(agent_id="test", initial_capital=10000.0)
|
|
|
|
def test_no_analysis_returns_hold(self, agent):
|
|
"""Test HOLD recommendation when no analysis."""
|
|
result = agent.get_bullish_recommendation("AAPL")
|
|
|
|
assert result["symbol"] == "AAPL"
|
|
assert result["recommendation"] == "HOLD"
|
|
assert result["conviction"] == 0.0
|
|
|
|
def test_strong_buy_for_high_conviction(self, agent):
|
|
"""Test STRONG_BUY for high conviction."""
|
|
# Create a high conviction report
|
|
report = BullReport(
|
|
symbol="AAPL",
|
|
conviction_level=0.8,
|
|
bullish_factors=["F1", "F2", "F3"],
|
|
price_target=200.0,
|
|
)
|
|
agent._last_report = report
|
|
|
|
result = agent.get_bullish_recommendation("AAPL")
|
|
|
|
assert result["recommendation"] == "STRONG_BUY"
|
|
|
|
def test_buy_for_moderate_conviction(self, agent):
|
|
"""Test BUY for moderate conviction."""
|
|
report = BullReport(
|
|
symbol="AAPL",
|
|
conviction_level=0.65,
|
|
bullish_factors=["F1", "F2"],
|
|
price_target=150.0,
|
|
)
|
|
agent._last_report = report
|
|
|
|
result = agent.get_bullish_recommendation("AAPL")
|
|
|
|
assert result["recommendation"] == "BUY"
|
|
|
|
def test_accumulate_for_low_conviction(self, agent):
|
|
"""Test ACCUMULATE for lower conviction."""
|
|
report = BullReport(
|
|
symbol="AAPL",
|
|
conviction_level=0.5,
|
|
bullish_factors=["F1"],
|
|
price_target=120.0,
|
|
)
|
|
agent._last_report = report
|
|
|
|
result = agent.get_bullish_recommendation("AAPL")
|
|
|
|
assert result["recommendation"] == "ACCUMULATE"
|
|
|
|
def test_different_symbol_returns_hold(self, agent):
|
|
"""Test HOLD when asking for different symbol than last analyzed."""
|
|
report = BullReport(symbol="AAPL", conviction_level=0.8)
|
|
agent._last_report = report
|
|
|
|
result = agent.get_bullish_recommendation("TSLA")
|
|
|
|
assert result["recommendation"] == "HOLD"
|
|
|
|
|
|
class TestDecisionCost:
|
|
"""Test decision cost deduction."""
|
|
|
|
@pytest.fixture
|
|
def agent(self):
|
|
"""Create a test agent."""
|
|
return BullResearcher(agent_id="test", initial_capital=10000.0)
|
|
|
|
def test_decision_cost_constant(self):
|
|
"""Test that decision cost is $0.15."""
|
|
agent = BullResearcher(agent_id="test", initial_capital=10000.0)
|
|
|
|
assert agent.decision_cost == 0.15
|
|
|
|
def test_analyze_deducts_fixed_cost(self, agent):
|
|
"""Test that analyze deducts exactly $0.15."""
|
|
initial_balance = agent.balance
|
|
|
|
asyncio.run(agent.analyze("AAPL"))
|
|
|
|
assert agent.balance == initial_balance - 0.15
|
|
|
|
def test_generate_bull_case_deducts_fixed_cost(self, agent):
|
|
"""Test that generate_bull_case deducts exactly $0.15."""
|
|
initial_balance = agent.balance
|
|
|
|
asyncio.run(agent.generate_bull_case("AAPL"))
|
|
|
|
assert agent.balance == initial_balance - 0.15
|
|
|
|
def test_multiple_calls_deduct_multiple_times(self, agent):
|
|
"""Test that each call deducts cost."""
|
|
initial_balance = agent.balance
|
|
|
|
asyncio.run(agent.generate_bull_case("AAPL"))
|
|
asyncio.run(agent.generate_bull_case("TSLA"))
|
|
asyncio.run(agent.generate_bull_case("NVDA"))
|
|
|
|
expected_balance = initial_balance - (0.15 * 3)
|
|
assert agent.balance == expected_balance
|
|
|
|
|
|
class TestSkillLevelImpact:
|
|
"""Test impact of skill level on analysis."""
|
|
|
|
def test_high_skill_higher_conviction(self):
|
|
"""Test that high skill produces higher conviction."""
|
|
low_skill = BullResearcher(agent_id="low", initial_capital=10000.0, skill_level=0.3)
|
|
high_skill = BullResearcher(agent_id="high", initial_capital=10000.0, skill_level=0.9)
|
|
|
|
low_report = asyncio.run(low_skill.generate_bull_case("AAPL"))
|
|
high_report = asyncio.run(high_skill.generate_bull_case("AAPL"))
|
|
|
|
assert high_report.conviction_level >= low_report.conviction_level
|
|
|
|
def test_high_skill_more_catalysts(self):
|
|
"""Test that high skill identifies more catalysts."""
|
|
low_skill = BullResearcher(agent_id="low", initial_capital=10000.0, skill_level=0.3)
|
|
high_skill = BullResearcher(agent_id="high", initial_capital=10000.0, skill_level=0.9)
|
|
|
|
low_catalysts = low_skill._identify_catalysts(None, None, None)
|
|
high_catalysts = high_skill._identify_catalysts(None, None, None)
|
|
|
|
assert len(high_catalysts) >= len(low_catalysts)
|
|
|
|
def test_high_skill_more_risks(self):
|
|
"""Test that high skill identifies more risks."""
|
|
low_skill = BullResearcher(agent_id="low", initial_capital=10000.0, skill_level=0.3)
|
|
high_skill = BullResearcher(agent_id="high", initial_capital=10000.0, skill_level=0.7)
|
|
|
|
low_risks = low_skill._identify_risks(None, None, None)
|
|
high_risks = high_skill._identify_risks(None, None, None)
|
|
|
|
assert len(high_risks) >= len(low_risks)
|