278 lines
9.8 KiB
Python
278 lines
9.8 KiB
Python
"""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']
|