148 lines
4.9 KiB
Python
148 lines
4.9 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("ma_crossover")
|
|
assert basic_factor is not None
|
|
assert basic_factor.metadata.price == 0.0
|
|
|
|
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
|
|
ml_factor = store.get_factor("ml_prediction")
|
|
if ml_factor:
|
|
assert not ml_factor.is_unlocked()
|
|
|
|
|
|
class TestFactorPurchaseFlow:
|
|
"""Tests for factor purchase flow."""
|
|
|
|
def test_purchase_with_sufficient_balance(self):
|
|
"""Test purchasing factor with sufficient balance."""
|
|
tracker = TradingEconomicTracker(agent_id="test_trader")
|
|
# Add sufficient balance
|
|
tracker.record_income(500.0, "test_income")
|
|
|
|
store = FactorStore(agent_id="test_trader", tracker=tracker)
|
|
|
|
# Try to purchase an advanced factor
|
|
result = store.purchase("sentiment_momentum")
|
|
# Should succeed if balance is sufficient
|
|
assert result["success"] is True or result["success"] is False
|
|
|
|
def test_purchase_with_insufficient_balance(self):
|
|
"""Test purchasing factor with insufficient balance."""
|
|
tracker = TradingEconomicTracker(agent_id="test_trader")
|
|
# Start with minimal balance
|
|
store = FactorStore(agent_id="test_trader", tracker=tracker)
|
|
|
|
initial_balance = tracker.get_balance()
|
|
|
|
# Try to purchase expensive factor
|
|
result = store.purchase("ml_prediction", price=1000.0)
|
|
|
|
if not result["success"]:
|
|
assert "insufficient" in result.get("message", "").lower() or \
|
|
"cannot" in result.get("message", "").lower()
|
|
|
|
def test_purchase_deducts_balance(self):
|
|
"""Test that purchase deducts balance correctly."""
|
|
tracker = TradingEconomicTracker(agent_id="test_trader")
|
|
tracker.record_income(200.0, "test_income")
|
|
|
|
initial_balance = tracker.get_balance()
|
|
store = FactorStore(agent_id="test_trader", tracker=tracker)
|
|
|
|
# Purchase a factor
|
|
result = store.purchase("test_factor", price=50.0)
|
|
|
|
if result["success"]:
|
|
# Balance should be deducted
|
|
assert tracker.get_balance() <= initial_balance
|
|
|
|
|
|
class TestFactorEvaluation:
|
|
"""Tests for factor evaluation."""
|
|
|
|
def test_moving_average_factor_evaluation(self):
|
|
"""Test MA crossover factor evaluation."""
|
|
from openclaw.factor.types import FactorContext
|
|
|
|
factor = MovingAverageCrossoverFactor()
|
|
|
|
# Create mock context
|
|
context = FactorContext(
|
|
symbol="AAPL",
|
|
current_price=150.0,
|
|
data={}, # Simplified
|
|
timestamp=datetime.now(),
|
|
)
|
|
|
|
result = factor.evaluate(context)
|
|
assert result is not None
|
|
assert hasattr(result, "signal") or isinstance(result, dict)
|
|
|
|
def test_rsi_factor_evaluation(self):
|
|
"""Test RSI factor evaluation."""
|
|
from openclaw.factor.types import FactorContext
|
|
|
|
factor = RSIOversoldFactor()
|
|
|
|
context = FactorContext(
|
|
symbol="AAPL",
|
|
current_price=150.0,
|
|
data={},
|
|
timestamp=datetime.now(),
|
|
)
|
|
|
|
result = factor.evaluate(context)
|
|
assert result is not None
|