Files
NexQuant/test/qlib/test_factor_coder.py
T

222 lines
9.0 KiB
Python
Raw Normal View History

"""Tests for factor_coder — evaluators, task, workspace."""
from __future__ import annotations
import sys
from pathlib import Path
from unittest.mock import MagicMock, patch
import pytest
PROJECT_ROOT = Path(__file__).parent.parent.parent
sys.path.insert(0, str(PROJECT_ROOT))
# =============================================================================
# FactorTask
# =============================================================================
class TestFactorTask:
def test_construction_fields(self):
from rdagent.components.coder.factor_coder.factor import FactorTask
t = FactorTask(
factor_name="f1",
factor_description="desc",
factor_formulation="formula",
variables={"x": 1},
resource="r1",
)
assert t.factor_name == "f1"
assert t.factor_description == "desc"
assert t.factor_formulation == "formula"
assert t.variables == {"x": 1}
assert t.factor_resources == "r1"
assert t.factor_implementation is False
assert t.base_code is None # from CoSTEERTask
def test_get_task_information(self):
from rdagent.components.coder.factor_coder.factor import FactorTask
t = FactorTask("f1", "desc", "formula", variables={"x": 1})
info = t.get_task_information()
assert "factor_name: f1" in info
assert "factor_description: desc" in info
assert "factor_formulation: formula" in info
assert "variables: {'x': 1}" in info
def test_get_task_brief_information(self):
from rdagent.components.coder.factor_coder.factor import FactorTask
t = FactorTask("f1", "desc", "formula")
info = t.get_task_brief_information()
assert "factor_name: f1" in info
def test_get_task_information_and_implementation_result(self):
from rdagent.components.coder.factor_coder.factor import FactorTask
t = FactorTask("f1", "desc", "formula")
result = t.get_task_information_and_implementation_result()
assert result["factor_name"] == "f1"
assert result["factor_description"] == "desc"
assert "factor_implementation" in result
def test_from_dict(self):
from rdagent.components.coder.factor_coder.factor import FactorTask
d = {
"factor_name": "f2",
"factor_description": "d2",
"factor_formulation": "f2",
"variables": {},
"resource": None,
"factor_implementation": True,
}
t = FactorTask.from_dict(d)
assert t.factor_name == "f2"
assert t.factor_implementation is True
def test_repr(self):
from rdagent.components.coder.factor_coder.factor import FactorTask
t = FactorTask("myfactor", "desc", "formula")
assert "FactorTask" in repr(t)
assert "myfactor" in repr(t)
# =============================================================================
# FactorFBWorkspace
# =============================================================================
class TestFactorFBWorkspace:
def test_init_sets_workspace_path(self):
from rdagent.components.coder.factor_coder.factor import FactorFBWorkspace, FactorTask
t = FactorTask("f1", "desc", "formula")
ws = FactorFBWorkspace(target_task=t)
assert ws.workspace_path is not None
# Directory is created lazily by execute(), not in __init__
assert isinstance(ws.workspace_path, Path)
def test_execute_returns_message_and_dataframe(self):
from rdagent.components.coder.factor_coder.factor import FactorFBWorkspace, FactorTask
t = FactorTask("f1", "desc", "formula")
t.version = 1
ws = FactorFBWorkspace(target_task=t)
# Inject valid factor code
ws.inject_files(**{
"factor.py": (
"import pandas as pd\n"
"import numpy as np\n"
"data = pd.read_hdf('intraday_pv.h5', key='data')\n"
"factor_val = data['$close'].pct_change()\n"
"factor_val = factor_val.to_frame('f1')\n"
"factor_val.to_hdf('result.h5', key='data', mode='w')\n"
),
})
msg, df = ws.execute()
assert isinstance(msg, str)
assert df is not None
def test_execute_succeeds_and_returns_data(self):
from rdagent.components.coder.factor_coder.factor import FactorFBWorkspace, FactorTask
t = FactorTask("fl1", "desc", "formula")
ws = FactorFBWorkspace(target_task=t)
ws.inject_files(**{
"factor.py": (
"import pandas as pd\n"
"data = pd.read_hdf('intraday_pv.h5', key='data')\n"
"factor_val = data['$close'].pct_change().to_frame('fl1')\n"
"factor_val.to_hdf('result.h5', key='data', mode='w')\n"
),
})
msg, df = ws.execute()
assert FactorFBWorkspace.FB_EXEC_SUCCESS in msg
assert FactorFBWorkspace.FB_OUTPUT_FILE_FOUND in msg
assert df is not None
# =============================================================================
# FactorEvaluatorForCoder (partial integration)
# =============================================================================
class TestFactorEvaluatorForCoder:
def test_init_creates_sub_evaluators(self):
from rdagent.components.coder.factor_coder.evaluators import FactorEvaluatorForCoder
mock_scen = MagicMock()
eva = FactorEvaluatorForCoder(scen=mock_scen)
assert eva.value_evaluator is not None
assert eva.code_evaluator is not None
assert eva.final_decision_evaluator is not None
def test_evaluate_with_none_implementation(self):
from rdagent.components.coder.factor_coder.evaluators import FactorEvaluatorForCoder
eva = FactorEvaluatorForCoder(scen=MagicMock())
assert eva.evaluate(target_task=MagicMock(), implementation=None) is None
def test_evaluate_returns_queried_knowledge_if_present(self):
from rdagent.components.coder.factor_coder.evaluators import FactorEvaluatorForCoder
from rdagent.components.coder.factor_coder.factor import FactorTask
eva = FactorEvaluatorForCoder(scen=MagicMock())
t = FactorTask("f1", "desc", "formula")
qk = MagicMock()
qk.success_task_to_knowledge_dict = {"info_f1": MagicMock(feedback="cached_fb")}
t.get_task_information = MagicMock(return_value="info_f1")
qk.failed_task_info_set = set()
fb = eva.evaluate(target_task=t, implementation=MagicMock(), queried_knowledge=qk)
assert fb == "cached_fb" # returned from cache
def test_evaluate_skips_failed_task(self):
from rdagent.components.coder.factor_coder.evaluators import FactorEvaluatorForCoder
from rdagent.components.coder.factor_coder.factor import FactorTask
eva = FactorEvaluatorForCoder(scen=MagicMock())
t = FactorTask("f1", "desc", "formula")
qk = MagicMock()
qk.success_task_to_knowledge_dict = {}
t.get_task_information = MagicMock(return_value="info_f1")
qk.failed_task_info_set = {"info_f1"}
fb = eva.evaluate(target_task=t, implementation=MagicMock(), queried_knowledge=qk)
assert fb.final_decision is False
assert "failed too many times" in fb.execution_feedback
# =============================================================================
# FactorEvaluator (eva_utils) — constructors and identity
# =============================================================================
class TestFactorEvaluatorsInit:
def test_factor_inf_evaluator_init(self):
from rdagent.components.coder.factor_coder.eva_utils import FactorInfEvaluator
eva = FactorInfEvaluator()
assert str(eva) == "FactorInfEvaluator"
def test_factor_single_column_evaluator_init(self):
from rdagent.components.coder.factor_coder.eva_utils import FactorSingleColumnEvaluator
eva = FactorSingleColumnEvaluator()
assert str(eva) == "FactorSingleColumnEvaluator"
def test_factor_output_format_evaluator_init(self):
from rdagent.components.coder.factor_coder.eva_utils import FactorOutputFormatEvaluator
eva = FactorOutputFormatEvaluator()
assert str(eva) == "FactorOutputFormatEvaluator"
def test_factor_missing_values_evaluator_init(self):
from rdagent.components.coder.factor_coder.eva_utils import FactorMissingValuesEvaluator
eva = FactorMissingValuesEvaluator()
assert str(eva) == "FactorMissingValuesEvaluator"
def test_factor_correlation_evaluator_init(self):
from rdagent.components.coder.factor_coder.eva_utils import FactorCorrelationEvaluator
eva = FactorCorrelationEvaluator(hard_check=True)
assert eva.hard_check is True
assert str(eva) == "FactorCorrelationEvaluator"
def test_factor_value_evaluator_init(self):
from rdagent.components.coder.factor_coder.eva_utils import FactorValueEvaluator
mock_scen = MagicMock()
eva = FactorValueEvaluator(mock_scen)
assert eva.scen is mock_scen