penti/backend/tests/test_strategies.py

250 lines
8.3 KiB
Python
Raw Permalink Normal View History

2026-01-31 23:30:51 +09:00
"""
Strategy consistency tests
"""
import pytest
from datetime import date
from sqlalchemy.orm import Session
from app.strategies.composite.multi_factor import MultiFactorStrategy
from app.strategies.composite.magic_formula import MagicFormulaStrategy
from app.strategies.composite.super_quality import SuperQualityStrategy
from app.strategies.factors.momentum import MomentumStrategy
from app.strategies.factors.f_score import FScoreStrategy
from app.strategies.factors.value import ValueStrategy
from app.strategies.factors.quality import QualityStrategy
from app.strategies.factors.all_value import AllValueStrategy
@pytest.mark.unit
class TestStrategyInterface:
"""Test strategy interface implementation"""
def test_multi_factor_strategy_interface(self):
"""Test MultiFactorStrategy implements BaseStrategy"""
strategy = MultiFactorStrategy(config={"count": 20})
assert hasattr(strategy, "select_stocks")
assert hasattr(strategy, "get_prices")
assert strategy.name == "multi_factor"
def test_magic_formula_strategy_interface(self):
"""Test MagicFormulaStrategy implements BaseStrategy"""
strategy = MagicFormulaStrategy(config={"count": 20})
assert hasattr(strategy, "select_stocks")
assert hasattr(strategy, "get_prices")
assert strategy.name == "magic_formula"
def test_super_quality_strategy_interface(self):
"""Test SuperQualityStrategy implements BaseStrategy"""
strategy = SuperQualityStrategy(config={"count": 20})
assert hasattr(strategy, "select_stocks")
assert hasattr(strategy, "get_prices")
assert strategy.name == "super_quality"
def test_momentum_strategy_interface(self):
"""Test MomentumStrategy implements BaseStrategy"""
strategy = MomentumStrategy(config={"count": 20})
assert hasattr(strategy, "select_stocks")
assert hasattr(strategy, "get_prices")
assert strategy.name == "momentum"
def test_f_score_strategy_interface(self):
"""Test FScoreStrategy implements BaseStrategy"""
strategy = FScoreStrategy(config={"count": 20})
assert hasattr(strategy, "select_stocks")
assert hasattr(strategy, "get_prices")
assert strategy.name == "f_score"
def test_value_strategy_interface(self):
"""Test ValueStrategy implements BaseStrategy"""
strategy = ValueStrategy(config={"count": 20})
assert hasattr(strategy, "select_stocks")
assert hasattr(strategy, "get_prices")
assert strategy.name == "value"
def test_quality_strategy_interface(self):
"""Test QualityStrategy implements BaseStrategy"""
strategy = QualityStrategy(config={"count": 20})
assert hasattr(strategy, "select_stocks")
assert hasattr(strategy, "get_prices")
assert strategy.name == "quality"
def test_all_value_strategy_interface(self):
"""Test AllValueStrategy implements BaseStrategy"""
strategy = AllValueStrategy(config={"count": 20})
assert hasattr(strategy, "select_stocks")
assert hasattr(strategy, "get_prices")
assert strategy.name == "all_value"
@pytest.mark.integration
@pytest.mark.slow
class TestStrategyExecution:
"""Test strategy execution with sample data"""
def test_multi_factor_select_stocks(
self,
db_session: Session,
sample_assets,
sample_price_data
):
"""Test MultiFactorStrategy stock selection"""
strategy = MultiFactorStrategy(config={"count": 3})
rebal_date = date(2023, 1, 15)
# Note: May fail if insufficient data, that's expected
try:
selected_stocks = strategy.select_stocks(rebal_date, db_session)
# Should return list of tickers
assert isinstance(selected_stocks, list)
assert len(selected_stocks) <= 3
for ticker in selected_stocks:
assert isinstance(ticker, str)
assert len(ticker) == 6
except Exception as e:
# Insufficient data is acceptable for test
pytest.skip(f"Insufficient data for strategy execution: {e}")
def test_momentum_select_stocks(
self,
db_session: Session,
sample_assets,
sample_price_data
):
"""Test MomentumStrategy stock selection"""
strategy = MomentumStrategy(config={"count": 3})
rebal_date = date(2023, 1, 15)
try:
selected_stocks = strategy.select_stocks(rebal_date, db_session)
assert isinstance(selected_stocks, list)
assert len(selected_stocks) <= 3
except Exception as e:
pytest.skip(f"Insufficient data for strategy execution: {e}")
def test_value_select_stocks(
self,
db_session: Session,
sample_assets,
sample_price_data
):
"""Test ValueStrategy stock selection"""
strategy = ValueStrategy(config={"count": 3})
rebal_date = date(2023, 1, 15)
try:
selected_stocks = strategy.select_stocks(rebal_date, db_session)
assert isinstance(selected_stocks, list)
assert len(selected_stocks) <= 3
for ticker in selected_stocks:
assert isinstance(ticker, str)
assert len(ticker) == 6
except Exception as e:
pytest.skip(f"Insufficient data for strategy execution: {e}")
def test_quality_select_stocks(
self,
db_session: Session,
sample_assets,
sample_price_data
):
"""Test QualityStrategy stock selection"""
strategy = QualityStrategy(config={"count": 3})
rebal_date = date(2023, 1, 15)
try:
selected_stocks = strategy.select_stocks(rebal_date, db_session)
assert isinstance(selected_stocks, list)
assert len(selected_stocks) <= 3
for ticker in selected_stocks:
assert isinstance(ticker, str)
assert len(ticker) == 6
except Exception as e:
pytest.skip(f"Insufficient data for strategy execution: {e}")
def test_all_value_select_stocks(
self,
db_session: Session,
sample_assets,
sample_price_data
):
"""Test AllValueStrategy stock selection"""
strategy = AllValueStrategy(config={"count": 3})
rebal_date = date(2023, 1, 15)
try:
selected_stocks = strategy.select_stocks(rebal_date, db_session)
assert isinstance(selected_stocks, list)
assert len(selected_stocks) <= 3
for ticker in selected_stocks:
assert isinstance(ticker, str)
assert len(ticker) == 6
except Exception as e:
pytest.skip(f"Insufficient data for strategy execution: {e}")
def test_strategy_get_prices(
self,
db_session: Session,
sample_assets,
sample_price_data
):
"""Test strategy price retrieval"""
strategy = MultiFactorStrategy(config={"count": 3})
tickers = ["005930", "000660", "035420"]
price_date = date(2023, 1, 15)
prices = strategy.get_prices(tickers, price_date, db_session)
# Should return dict of prices
assert isinstance(prices, dict)
# May not have all prices if data is incomplete
for ticker, price in prices.items():
assert ticker in tickers
assert price > 0
@pytest.mark.integration
class TestStrategyConfiguration:
"""Test strategy configuration handling"""
def test_strategy_default_config(self):
"""Test strategy with default configuration"""
strategy = MultiFactorStrategy(config={})
# Should use default count
assert "count" in strategy.config or hasattr(strategy, "count")
def test_strategy_custom_count(self):
"""Test strategy with custom count"""
strategy = MultiFactorStrategy(config={"count": 50})
assert strategy.config["count"] == 50
def test_strategy_invalid_config(self):
"""Test strategy with invalid configuration"""
# Should handle gracefully or raise appropriate error
try:
strategy = MultiFactorStrategy(config={"count": -1})
# If it doesn't raise, it should handle gracefully
assert True
except ValueError:
# Expected for negative count
assert True