"""Integration tests for factor market system. Tests the complete factor purchase and usage flow. """ import pytest from datetime import datetime from openclaw.factor import FactorStore from openclaw.factor.base import BuyFactor, SellFactor from openclaw.factor.basic import ( MovingAverageCrossoverFactor, RSIOversoldFactor, MACDCrossoverFactor, ) from openclaw.factor.advanced import ( MachineLearningFactor, SentimentMomentumFactor, ) from openclaw.core.economy import TradingEconomicTracker class TestFactorMarketIntegration: """Integration tests for factor market.""" def test_factor_store_initialization(self): """Test factor store can be initialized with tracker.""" tracker = TradingEconomicTracker(agent_id="test_trader") store = FactorStore(agent_id="test_trader", tracker=tracker) assert store.agent_id == "test_trader" assert store.tracker == tracker assert len(store.inventory) >= 0 def test_list_available_factors(self): """Test listing available factors.""" tracker = TradingEconomicTracker(agent_id="test_trader") store = FactorStore(agent_id="test_trader", tracker=tracker) factors = store.list_available() assert isinstance(factors, list) assert len(factors) > 0 def test_basic_factors_unlocked_by_default(self): """Test basic factors are unlocked by default.""" tracker = TradingEconomicTracker(agent_id="test_trader") store = FactorStore(agent_id="test_trader", tracker=tracker) # Basic factors should be available basic_factor = store.get_factor("buy_ma_crossover") assert basic_factor is not None assert basic_factor.metadata.price == 0.0 assert basic_factor.is_unlocked def test_advanced_factors_locked_by_default(self): """Test advanced factors are locked by default.""" tracker = TradingEconomicTracker(agent_id="test_trader") store = FactorStore(agent_id="test_trader", tracker=tracker) # Advanced factors should not be usable without purchase # They exist in catalog but not in inventory ml_factor = store.catalog.get("buy_ml_prediction") assert ml_factor is not None assert not ml_factor.is_unlocked # Cannot get factor from store without purchasing assert store.get_factor("buy_ml_prediction") is None class TestFactorPurchaseFlow: """Tests for factor purchase flow.""" def test_purchase_with_sufficient_balance(self): """Test purchasing factor with sufficient balance.""" # Create tracker with sufficient capital tracker = TradingEconomicTracker( agent_id="test_trader", initial_capital=1000.0 ) store = FactorStore(agent_id="test_trader", tracker=tracker) # Try to purchase an advanced factor (SentimentMomentum costs $75) result = store.purchase("buy_sentiment_momentum") # Should succeed with sufficient balance assert result.success is True assert result.price == 75.0 assert result.new_balance == 925.0 def test_purchase_with_insufficient_balance(self): """Test purchasing factor with insufficient balance.""" tracker = TradingEconomicTracker( agent_id="test_trader", initial_capital=50.0 # Not enough for ML factor ($100) ) store = FactorStore(agent_id="test_trader", tracker=tracker) initial_balance = tracker.balance # Try to purchase expensive factor result = store.purchase("buy_ml_prediction") assert result.success is False assert "insufficient" in result.message.lower() assert tracker.balance == initial_balance # Balance unchanged def test_purchase_deducts_balance(self): """Test that purchase deducts balance correctly.""" tracker = TradingEconomicTracker( agent_id="test_trader", initial_capital=200.0 ) store = FactorStore(agent_id="test_trader", tracker=tracker) initial_balance = tracker.balance # Purchase a factor (SentimentMomentum costs $75) result = store.purchase("buy_sentiment_momentum") assert result.success is True # Balance should be deducted assert tracker.balance < initial_balance assert tracker.balance == initial_balance - 75.0 def test_purchase_already_owned_fails(self): """Test that purchasing an already owned factor fails.""" tracker = TradingEconomicTracker( agent_id="test_trader", initial_capital=200.0 ) store = FactorStore(agent_id="test_trader", tracker=tracker) # Purchase once result1 = store.purchase("buy_sentiment_momentum") assert result1.success is True # Try to purchase again result2 = store.purchase("buy_sentiment_momentum") assert result2.success is False assert "already owned" in result2.message.lower() def test_factor_unlocked_after_purchase(self): """Test that factor is unlocked after purchase.""" tracker = TradingEconomicTracker( agent_id="test_trader", initial_capital=200.0 ) store = FactorStore(agent_id="test_trader", tracker=tracker) # Before purchase - cannot get factor assert store.get_factor("buy_sentiment_momentum") is None # Purchase result = store.purchase("buy_sentiment_momentum") assert result.success is True # After purchase - factor is available factor = store.get_factor("buy_sentiment_momentum") assert factor is not None assert factor.is_unlocked class TestFactorInventory: """Tests for factor inventory management.""" def test_list_owned_factors(self): """Test listing owned factors.""" tracker = TradingEconomicTracker( agent_id="test_trader", initial_capital=500.0 ) store = FactorStore(agent_id="test_trader", tracker=tracker) # Initially should have only free basic factors owned = store.list_owned() basic_owned = [f for f in owned if f['price'] == 0.0] assert len(basic_owned) > 0 # Purchase an advanced factor store.purchase("buy_sentiment_momentum") owned = store.list_owned() advanced_owned = [f for f in owned if f['price'] > 0.0] assert len(advanced_owned) == 1 assert advanced_owned[0]['id'] == "buy_sentiment_momentum" def test_get_factor_info(self): """Test getting factor information.""" tracker = TradingEconomicTracker(agent_id="test_trader") store = FactorStore(agent_id="test_trader", tracker=tracker) info = store.get_factor_info("buy_ml_prediction") assert info is not None assert info['name'] == "ML Prediction" assert info['price'] == 100.0 assert info['category'] == "advanced" def test_inventory_value(self): """Test calculating inventory value.""" tracker = TradingEconomicTracker( agent_id="test_trader", initial_capital=500.0 ) store = FactorStore(agent_id="test_trader", tracker=tracker) initial_value = store.get_inventory_value() # Purchase an advanced factor store.purchase("buy_sentiment_momentum") new_value = store.get_inventory_value() assert new_value == initial_value + 75.0 class TestAdvancedFactors: """Tests for advanced factor functionality.""" def test_machine_learning_factor_creation(self): """Test ML factor can be created.""" factor = MachineLearningFactor() assert factor.metadata.name == "ML Prediction" assert factor.metadata.price == 100.0 assert factor.metadata.category.value == "advanced" def test_sentiment_momentum_factor_creation(self): """Test sentiment momentum factor can be created.""" factor = SentimentMomentumFactor() assert factor.metadata.name == "Sentiment Momentum" assert factor.metadata.price == 75.0 assert factor.metadata.category.value == "advanced" def test_multi_factor_combination_creation(self): """Test multi-factor combination can be created.""" from openclaw.factor.advanced import MultiFactorCombination factor = MultiFactorCombination() assert factor.metadata.name == "Multi-Factor Ensemble" assert factor.metadata.price == 150.0 assert factor.metadata.category.value == "premium" def test_purchase_persistence(self): """Test purchase history is tracked.""" tracker = TradingEconomicTracker( agent_id="test_trader", initial_capital=500.0 ) store = FactorStore(agent_id="test_trader", tracker=tracker) # Purchase factors store.purchase("buy_sentiment_momentum") store.purchase("buy_ml_prediction") # Check purchase history history = store.get_purchase_history() assert len(history) == 2 assert history[0].factor_id == "buy_sentiment_momentum" assert history[0].price == 75.0 assert history[1].factor_id == "buy_ml_prediction" assert history[1].price == 100.0 class TestStoreSummary: """Tests for store summary functionality.""" def test_store_summary(self): """Test getting store summary.""" tracker = TradingEconomicTracker( agent_id="test_trader", initial_capital=500.0 ) store = FactorStore(agent_id="test_trader", tracker=tracker) summary = store.get_store_summary() assert summary['agent_id'] == "test_trader" assert summary['current_balance'] == 500.0 assert 'factors_owned' in summary assert 'basic' in summary['factors_owned'] assert 'advanced' in summary['factors_owned']