refactor: rename project from Predix to NexQuant

Rename all source files, scripts, tests, documentation, and configuration
from Predix/predix to NexQuant/nexquant across the entire codebase.
This commit is contained in:
TPTBusiness
2026-05-09 17:48:22 +02:00
parent 85b56b8179
commit cbe1c52e00
92 changed files with 3690 additions and 1015 deletions
+3 -3
View File
@@ -26,7 +26,7 @@ Die Pakete sind in `requirements.txt` enthalten.
### Alle Tests ausführen
```bash
cd /home/nico/Predix
cd /home/nico/NexQuant
pytest test/backtesting/
```
@@ -226,8 +226,8 @@ Für GitHub Actions oder andere CI/CD-Systeme:
```bash
# Stelle sicher dass du im Projekt-Verzeichnis bist
cd /home/nico/Predix
export PYTHONPATH=/home/nico/Predix:$PYTHONPATH
cd /home/nico/NexQuant
export PYTHONPATH=/home/nico/NexQuant:$PYTHONPATH
pytest test/backtesting/
```
+1 -1
View File
@@ -1 +1 @@
"""Predix Backtesting Test Package"""
"""NexQuant Backtesting Test Package"""
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Predix Backtesting Test Fixtures
NexQuant Backtesting Test Fixtures
Wiederverwendbare Test-Daten und Fixtures für alle Backtesting-Tests
"""
import pytest
+29 -29
View File
@@ -25,8 +25,8 @@ def _make_ohlcv(n: int = 600, freq: str = "1min") -> pd.DataFrame:
}, index=idx)
def _make_predix_hdf5(tmp_path: Path, n: int = 300) -> Path:
"""Write a minimal Predix-format HDF5 file and return its path."""
def _make_nexquant_hdf5(tmp_path: Path, n: int = 300) -> Path:
"""Write a minimal NexQuant-format HDF5 file and return its path."""
idx = pd.MultiIndex.from_arrays(
[pd.date_range("2024-01-01", periods=n, freq="1min"), ["EURUSD"] * n],
names=["datetime", "instrument"],
@@ -63,12 +63,12 @@ def _make_mock_adapter():
# ---------------------------------------------------------------------------
# Unit tests: _ohlcv_from_predix
# Unit tests: _ohlcv_from_nexquant
# ---------------------------------------------------------------------------
class TestOhlcvConversion:
def test_renames_dollar_columns(self):
from rdagent.components.coder.kronos_adapter import _ohlcv_from_predix
from rdagent.components.coder.kronos_adapter import _ohlcv_from_nexquant
idx = pd.MultiIndex.from_arrays(
[pd.date_range("2024-01-01", periods=3, freq="1min"), ["EURUSD"] * 3],
names=["datetime", "instrument"],
@@ -78,17 +78,17 @@ class TestOhlcvConversion:
"$low": [1.05, 1.15, 1.25], "$close": [1.12, 1.22, 1.32],
"$volume": [100.0, 200.0, 300.0],
}, index=idx)
result = _ohlcv_from_predix(df)
result = _ohlcv_from_nexquant(df)
assert list(result.columns) == ["open", "high", "low", "close", "volume"]
def test_no_dollar_columns_passthrough(self):
from rdagent.components.coder.kronos_adapter import _ohlcv_from_predix
from rdagent.components.coder.kronos_adapter import _ohlcv_from_nexquant
df = pd.DataFrame({"open": [1.0], "close": [1.1], "high": [1.2], "low": [0.9], "volume": [100.0]})
result = _ohlcv_from_predix(df)
result = _ohlcv_from_nexquant(df)
assert "close" in result.columns
def test_output_is_float64(self):
from rdagent.components.coder.kronos_adapter import _ohlcv_from_predix
from rdagent.components.coder.kronos_adapter import _ohlcv_from_nexquant
df = pd.DataFrame({
"$open": np.array([1.1], dtype="float32"),
"$close": np.array([1.1], dtype="float32"),
@@ -96,7 +96,7 @@ class TestOhlcvConversion:
"$low": np.array([1.1], dtype="float32"),
"$volume": np.array([100.0], dtype="float32"),
})
result = _ohlcv_from_predix(df)
result = _ohlcv_from_nexquant(df)
assert result["close"].dtype == np.float64
@@ -134,7 +134,7 @@ class TestBuildKronosFactor:
def test_output_has_correct_multiindex(self, tmp_path, monkeypatch):
import rdagent.components.coder.kronos_adapter as mod
monkeypatch.setattr(mod, "KronosAdapter", lambda **kw: _make_mock_adapter())
h5 = _make_predix_hdf5(tmp_path)
h5 = _make_nexquant_hdf5(tmp_path)
result = mod.build_kronos_factor(h5, context_bars=100, pred_bars=20, stride_bars=20, device="cpu")
assert result.index.names == ["datetime", "instrument"]
assert result.index.nlevels == 2
@@ -142,14 +142,14 @@ class TestBuildKronosFactor:
def test_output_column_name(self, tmp_path, monkeypatch):
import rdagent.components.coder.kronos_adapter as mod
monkeypatch.setattr(mod, "KronosAdapter", lambda **kw: _make_mock_adapter())
h5 = _make_predix_hdf5(tmp_path)
h5 = _make_nexquant_hdf5(tmp_path)
result = mod.build_kronos_factor(h5, context_bars=100, pred_bars=20, stride_bars=20, device="cpu")
assert "KronosPredReturn" in result.columns
def test_output_has_non_nan_values(self, tmp_path, monkeypatch):
import rdagent.components.coder.kronos_adapter as mod
monkeypatch.setattr(mod, "KronosAdapter", lambda **kw: _make_mock_adapter())
h5 = _make_predix_hdf5(tmp_path)
h5 = _make_nexquant_hdf5(tmp_path)
result = mod.build_kronos_factor(h5, context_bars=100, pred_bars=20, stride_bars=20, device="cpu")
assert result["KronosPredReturn"].notna().sum() > 0
@@ -157,7 +157,7 @@ class TestBuildKronosFactor:
import rdagent.components.coder.kronos_adapter as mod
monkeypatch.setattr(mod, "KronosAdapter", lambda **kw: _make_mock_adapter())
n = 300
h5 = _make_predix_hdf5(tmp_path, n=n)
h5 = _make_nexquant_hdf5(tmp_path, n=n)
result = mod.build_kronos_factor(h5, context_bars=100, pred_bars=20, stride_bars=20, device="cpu")
assert len(result) == n
@@ -165,7 +165,7 @@ class TestBuildKronosFactor:
"""Values within a predicted window should be forward-filled, not NaN."""
import rdagent.components.coder.kronos_adapter as mod
monkeypatch.setattr(mod, "KronosAdapter", lambda **kw: _make_mock_adapter())
h5 = _make_predix_hdf5(tmp_path, n=300)
h5 = _make_nexquant_hdf5(tmp_path, n=300)
result = mod.build_kronos_factor(h5, context_bars=100, pred_bars=20, stride_bars=20, device="cpu")
non_nan_ratio = result["KronosPredReturn"].notna().mean()
assert non_nan_ratio > 0.5, f"Expected >50% non-NaN, got {non_nan_ratio:.2%}"
@@ -185,7 +185,7 @@ class TestEvaluateKronosModel:
def test_returns_required_keys(self, tmp_path, monkeypatch):
import rdagent.components.coder.kronos_adapter as mod
monkeypatch.setattr(mod, "KronosAdapter", lambda **kw: _make_mock_adapter())
h5 = _make_predix_hdf5(tmp_path, n=400)
h5 = _make_nexquant_hdf5(tmp_path, n=400)
metrics = mod.evaluate_kronos_model(h5, context_bars=100, pred_bars=20, stride_bars=20, device="cpu")
for key in ["IC_mean", "IC_std", "IC_IR", "hit_rate", "n_predictions"]:
assert key in metrics, f"Missing key: {key}"
@@ -193,14 +193,14 @@ class TestEvaluateKronosModel:
def test_hit_rate_in_valid_range(self, tmp_path, monkeypatch):
import rdagent.components.coder.kronos_adapter as mod
monkeypatch.setattr(mod, "KronosAdapter", lambda **kw: _make_mock_adapter())
h5 = _make_predix_hdf5(tmp_path, n=400)
h5 = _make_nexquant_hdf5(tmp_path, n=400)
metrics = mod.evaluate_kronos_model(h5, context_bars=100, pred_bars=20, stride_bars=20, device="cpu")
assert 0.0 <= metrics["hit_rate"] <= 1.0
def test_n_predictions_positive(self, tmp_path, monkeypatch):
import rdagent.components.coder.kronos_adapter as mod
monkeypatch.setattr(mod, "KronosAdapter", lambda **kw: _make_mock_adapter())
h5 = _make_predix_hdf5(tmp_path, n=400)
h5 = _make_nexquant_hdf5(tmp_path, n=400)
metrics = mod.evaluate_kronos_model(h5, context_bars=100, pred_bars=20, stride_bars=20, device="cpu")
assert metrics["n_predictions"] > 0
@@ -213,41 +213,41 @@ class TestCLICommands:
def test_kronos_factor_missing_data_exits(self, tmp_path, monkeypatch):
"""kronos-factor exits with code 1 when HDF5 data is missing."""
from typer.testing import CliRunner
import predix as predix_mod
import nexquant as nexquant_mod
monkeypatch.chdir(tmp_path)
runner = CliRunner()
result = runner.invoke(predix_mod.app, ["kronos-factor"])
result = runner.invoke(nexquant_mod.app, ["kronos-factor"])
assert result.exit_code == 1
def test_kronos_eval_missing_data_exits(self, tmp_path, monkeypatch):
"""kronos-eval exits with code 1 when HDF5 data is missing."""
from typer.testing import CliRunner
import predix as predix_mod
import nexquant as nexquant_mod
monkeypatch.chdir(tmp_path)
runner = CliRunner()
result = runner.invoke(predix_mod.app, ["kronos-eval"])
result = runner.invoke(nexquant_mod.app, ["kronos-eval"])
assert result.exit_code == 1
def test_kronos_factor_runs_with_mock(self, tmp_path, monkeypatch):
"""kronos-factor completes and saves parquet + json when adapter is mocked."""
from typer.testing import CliRunner
import rdagent.components.coder.kronos_adapter as mod
import predix as predix_mod
import nexquant as nexquant_mod
monkeypatch.setattr(mod, "KronosAdapter", lambda **kw: _make_mock_adapter())
data_dir = tmp_path / "git_ignore_folder" / "factor_implementation_source_data"
data_dir.mkdir(parents=True)
_make_predix_hdf5(data_dir.parent.parent, n=300)
_make_nexquant_hdf5(data_dir.parent.parent, n=300)
h5_src = tmp_path / "intraday_pv.h5"
# Put HDF5 where the CLI expects it
import shutil
src = _make_predix_hdf5(tmp_path, n=300)
src = _make_nexquant_hdf5(tmp_path, n=300)
shutil.copy(src, data_dir / "intraday_pv.h5")
monkeypatch.chdir(tmp_path)
runner = CliRunner()
result = runner.invoke(predix_mod.app, [
result = runner.invoke(nexquant_mod.app, [
"kronos-factor", "--context", "100", "--pred", "20", "--device", "cpu"
])
assert result.exit_code == 0, result.output
@@ -257,20 +257,20 @@ class TestCLICommands:
"""kronos-eval completes and prints IC metrics when adapter is mocked."""
from typer.testing import CliRunner
import rdagent.components.coder.kronos_adapter as mod
import predix as predix_mod
import nexquant as nexquant_mod
monkeypatch.setattr(mod, "KronosAdapter", lambda **kw: _make_mock_adapter())
data_dir = tmp_path / "git_ignore_folder" / "factor_implementation_source_data"
data_dir.mkdir(parents=True)
_make_predix_hdf5(data_dir.parent.parent, n=400)
src = _make_predix_hdf5(tmp_path, n=400)
_make_nexquant_hdf5(data_dir.parent.parent, n=400)
src = _make_nexquant_hdf5(tmp_path, n=400)
import shutil
shutil.copy(src, data_dir / "intraday_pv.h5")
monkeypatch.chdir(tmp_path)
runner = CliRunner()
result = runner.invoke(predix_mod.app, [
result = runner.invoke(nexquant_mod.app, [
"kronos-eval", "--context", "100", "--pred", "20", "--device", "cpu"
])
assert result.exit_code == 0, result.output
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Shared fixtures for Predix integration tests.
Shared fixtures for NexQuant integration tests.
Provides common test data, mock objects, and utilities.
"""
import pytest
+42 -42
View File
@@ -1,5 +1,5 @@
"""
Comprehensive Integration Test Suite for Predix
Comprehensive Integration Test Suite for NexQuant
Tests all 13 implemented features to ensure they work correctly.
Usage:
@@ -1457,22 +1457,22 @@ class TestFinQuantCriticalIntegrations:
# =============================================================================
# CLI Model Selection Tests (predix.py, cli.py)
# CLI Model Selection Tests (nexquant.py, cli.py)
# =============================================================================
class TestCLIModelSelection:
"""Test CLI model selection (--model/-m flag) for local vs OpenRouter."""
def test_predix_cli_imports(self):
"""Test that predix.py CLI module can be imported."""
def test_nexquant_cli_imports(self):
"""Test that nexquant.py CLI module can be imported."""
import importlib.util
spec = importlib.util.spec_from_file_location(
"predix", Path(__file__).parent.parent.parent / "predix.py"
"nexquant", Path(__file__).parent.parent.parent / "nexquant.py"
)
predix = importlib.util.module_from_spec(spec)
spec.loader.exec_module(predix)
assert hasattr(predix, "app")
assert hasattr(predix, "quant")
nexquant = importlib.util.module_from_spec(spec)
spec.loader.exec_module(nexquant)
assert hasattr(nexquant, "app")
assert hasattr(nexquant, "quant")
def test_fin_quant_cli_has_model_option(self):
"""Test that fin_quant CLI has --model option."""
@@ -1488,36 +1488,36 @@ class TestCLIModelSelection:
# (Typer auto-generates help from function signatures)
assert isinstance(result.output, str)
def test_predix_quant_has_model_option(self):
"""Test that predix quant CLI has --model option."""
def test_nexquant_quant_has_model_option(self):
"""Test that nexquant quant CLI has --model option."""
from typer.testing import CliRunner
import importlib.util
spec = importlib.util.spec_from_file_location(
"predix", Path(__file__).parent.parent.parent / "predix.py"
"nexquant", Path(__file__).parent.parent.parent / "nexquant.py"
)
predix = importlib.util.module_from_spec(spec)
spec.loader.exec_module(predix)
nexquant = importlib.util.module_from_spec(spec)
spec.loader.exec_module(nexquant)
runner = CliRunner()
result = runner.invoke(predix.app, ["quant", "--help"])
result = runner.invoke(nexquant.app, ["quant", "--help"])
assert result.exit_code == 0
assert "--model" in result.output or "-m" in result.output
def test_predix_quant_has_log_file_option(self):
"""Test that predix quant CLI has --log-file option."""
def test_nexquant_quant_has_log_file_option(self):
"""Test that nexquant quant CLI has --log-file option."""
from typer.testing import CliRunner
import importlib.util
spec = importlib.util.spec_from_file_location(
"predix", Path(__file__).parent.parent.parent / "predix.py"
"nexquant", Path(__file__).parent.parent.parent / "nexquant.py"
)
predix = importlib.util.module_from_spec(spec)
spec.loader.exec_module(predix)
nexquant = importlib.util.module_from_spec(spec)
spec.loader.exec_module(nexquant)
runner = CliRunner()
result = runner.invoke(predix.app, ["quant", "--help"])
result = runner.invoke(nexquant.app, ["quant", "--help"])
assert result.exit_code == 0
assert "--log-file" in result.output
@@ -1541,12 +1541,12 @@ class TestCLIModelSelection:
import inspect
import importlib.util
spec = importlib.util.spec_from_file_location(
"predix", Path(__file__).parent.parent.parent / "predix.py"
"nexquant", Path(__file__).parent.parent.parent / "nexquant.py"
)
predix = importlib.util.module_from_spec(spec)
spec.loader.exec_module(predix)
nexquant = importlib.util.module_from_spec(spec)
spec.loader.exec_module(nexquant)
source = inspect.getsource(predix.quant)
source = inspect.getsource(nexquant.quant)
assert "OPENROUTER_API_KEY" in source
assert "not set" in source or "not set in" in source
finally:
@@ -1554,50 +1554,50 @@ class TestCLIModelSelection:
os.environ["OPENROUTER_API_KEY"] = original_key
def test_tee_writer_class_exists(self):
"""Test that TeeWriter class is defined in predix.py."""
"""Test that TeeWriter class is defined in nexquant.py."""
import importlib.util
spec = importlib.util.spec_from_file_location(
"predix", Path(__file__).parent.parent.parent / "predix.py"
"nexquant", Path(__file__).parent.parent.parent / "nexquant.py"
)
predix = importlib.util.module_from_spec(spec)
spec.loader.exec_module(predix)
nexquant = importlib.util.module_from_spec(spec)
spec.loader.exec_module(nexquant)
# TeeWriter is defined inside the quant function
# Verify the function source contains TeeWriter
import inspect
source = inspect.getsource(predix.quant)
source = inspect.getsource(nexquant.quant)
assert "TeeWriter" in source
def test_predix_health_command(self):
"""Test that predix health command exists."""
def test_nexquant_health_command(self):
"""Test that nexquant health command exists."""
from typer.testing import CliRunner
import importlib.util
spec = importlib.util.spec_from_file_location(
"predix", Path(__file__).parent.parent.parent / "predix.py"
"nexquant", Path(__file__).parent.parent.parent / "nexquant.py"
)
predix = importlib.util.module_from_spec(spec)
spec.loader.exec_module(predix)
nexquant = importlib.util.module_from_spec(spec)
spec.loader.exec_module(nexquant)
runner = CliRunner()
result = runner.invoke(predix.app, ["health", "--help"])
result = runner.invoke(nexquant.app, ["health", "--help"])
assert result.exit_code == 0
def test_predix_status_command(self):
"""Test that predix status command exists."""
def test_nexquant_status_command(self):
"""Test that nexquant status command exists."""
from typer.testing import CliRunner
import importlib.util
spec = importlib.util.spec_from_file_location(
"predix", Path(__file__).parent.parent.parent / "predix.py"
"nexquant", Path(__file__).parent.parent.parent / "nexquant.py"
)
predix = importlib.util.module_from_spec(spec)
spec.loader.exec_module(predix)
nexquant = importlib.util.module_from_spec(spec)
spec.loader.exec_module(nexquant)
runner = CliRunner()
result = runner.invoke(predix.app, ["status", "--help"])
result = runner.invoke(nexquant.app, ["status", "--help"])
assert result.exit_code == 0
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Integration Tests for Full Predix Pipeline (P6-P9)
Integration Tests for Full NexQuant Pipeline (P6-P9)
Tests the complete end-to-end pipeline including:
- Feedback Loop Integration (P6)
+18 -18
View File
@@ -1,4 +1,4 @@
"""Deep tests for predix_autopilot.py — property-based, mocks, edge cases.
"""Deep tests for nexquant_autopilot.py — property-based, mocks, edge cases.
Tests the core logic of the 24/7 strategy generator by mocking
the StrategyOrchestrator at the correct import path.
@@ -39,14 +39,14 @@ class TestMainRound:
"rdagent.scenarios.qlib.local.strategy_orchestrator.StrategyOrchestrator",
side_effect=RuntimeError("no data"),
):
from scripts.predix_autopilot import main_round
from scripts.nexquant_autopilot import main_round
result = main_round("daytrading", 1)
assert result == 0
def test_returns_zero_on_generate_failure(self, mock_orch):
mock_cls, instance = mock_orch
instance.generate_strategies.side_effect = RuntimeError("crash")
from scripts.predix_autopilot import main_round
from scripts.nexquant_autopilot import main_round
result = main_round("daytrading", 1)
assert result == 0
@@ -56,7 +56,7 @@ class TestMainRound:
{"status": "accepted", "strategy_name": "s1", "sharpe_ratio": 0.5, "oos_sharpe": 0.3},
{"status": "rejected", "strategy_name": "s2", "reason": "low"},
]
from scripts.predix_autopilot import main_round
from scripts.nexquant_autopilot import main_round
result = main_round("daytrading", 1)
assert result == 1
@@ -70,7 +70,7 @@ class TestMainRound:
"status": "success", "sharpe_ratio": 0.95, "oos_sharpe": 0.65,
"members": ["a", "b"],
}
from scripts.predix_autopilot import main_round
from scripts.nexquant_autopilot import main_round
main_round("daytrading", 1)
instance.build_ensemble.assert_called_once()
@@ -80,7 +80,7 @@ class TestMainRound:
{"status": "accepted", "strategy_name": "a", "sharpe_ratio": 0.5, "oos_sharpe": 0.3},
{"status": "rejected", "strategy_name": "b", "reason": "no"},
]
from scripts.predix_autopilot import main_round
from scripts.nexquant_autopilot import main_round
main_round("daytrading", 1)
instance.build_ensemble.assert_not_called()
@@ -91,14 +91,14 @@ class TestMainRound:
{"status": "accepted", "strategy_name": "b", "sharpe_ratio": 0.6, "oos_sharpe": 0.4},
]
instance.build_ensemble.side_effect = RuntimeError("boom")
from scripts.predix_autopilot import main_round
from scripts.nexquant_autopilot import main_round
result = main_round("daytrading", 1)
assert result == 2 # Still counts accepted
def test_empty_results_returns_zero(self, mock_orch):
mock_cls, instance = mock_orch
instance.generate_strategies.return_value = []
from scripts.predix_autopilot import main_round
from scripts.nexquant_autopilot import main_round
result = main_round("daytrading", 1)
assert result == 0
@@ -109,7 +109,7 @@ class TestMainRound:
{"status": "accepted", "strategy_name": "b", "sharpe_ratio": 0.6, "oos_sharpe": 0.4},
]
instance.build_ensemble.return_value = None
from scripts.predix_autopilot import main_round
from scripts.nexquant_autopilot import main_round
result = main_round("daytrading", 1)
assert result == 2
@@ -130,27 +130,27 @@ class TestMainRound:
"reason": "test"},
]
mock_cls.return_value = instance
from scripts.predix_autopilot import main_round
from scripts.nexquant_autopilot import main_round
result = main_round("daytrading", 1)
assert isinstance(result, int) and result >= 0
class TestConfig:
def test_batch_size_positive(self):
from scripts import predix_autopilot
assert predix_autopilot.BATCH_SIZE > 0
from scripts import nexquant_autopilot
assert nexquant_autopilot.BATCH_SIZE > 0
def test_optuna_trials_positive(self):
from scripts import predix_autopilot
assert predix_autopilot.OPTUNA_TRIALS > 0
from scripts import nexquant_autopilot
assert nexquant_autopilot.OPTUNA_TRIALS > 0
def test_cooldown_positive(self):
from scripts import predix_autopilot
assert predix_autopilot.COOLDOWN > 0
from scripts import nexquant_autopilot
assert nexquant_autopilot.COOLDOWN > 0
def test_max_consecutive_fails_positive(self):
from scripts import predix_autopilot
assert predix_autopilot.MAX_CONSECUTIVE_FAILS > 0
from scripts import nexquant_autopilot
assert nexquant_autopilot.MAX_CONSECUTIVE_FAILS > 0
class TestStyleCycling:
+33 -33
View File
@@ -2,9 +2,9 @@
Tests for background task infrastructure (parallel runner, CLI paths, env loading).
Verifies bugs that were previously present:
- predix_parallel.py: project_root pointing to scripts/ instead of repo root
- predix_parallel.py: .env loaded from scripts/ instead of repo root
- predix_parallel.py: API key round-robin overwritten by comma-separated list
- nexquant_parallel.py: project_root pointing to scripts/ instead of repo root
- nexquant_parallel.py: .env loaded from scripts/ instead of repo root
- nexquant_parallel.py: API key round-robin overwritten by comma-separated list
- cli.py: project_root depth wrong (4 .parent hops instead of 3)
- cli.py start_loop: hardcoded "python" instead of sys.executable
- cli.py parallel: hardcoded model=local
@@ -18,7 +18,7 @@ from unittest.mock import Mock, patch
import pytest
# ── predix_parallel.py ──────────────────────────────────────────────────
# ── nexquant_parallel.py ──────────────────────────────────────────────────
class TestParallelRunnerProjectRoot:
@@ -26,36 +26,36 @@ class TestParallelRunnerProjectRoot:
def test_project_root_is_repo_root(self):
"""Bug: project_root was Path(__file__).parent (= scripts/)."""
from scripts.predix_parallel import ParallelRunner
from scripts.nexquant_parallel import ParallelRunner
runner = ParallelRunner(num_runs=1, num_api_keys=1, model="local")
root = runner.project_root
# Must contain predix.py (repo root), NOT be the scripts/ dir
assert (root / "predix.py").exists(), (
f"project_root={root} does not contain predix.py — "
# Must contain nexquant.py (repo root), NOT be the scripts/ dir
assert (root / "nexquant.py").exists(), (
f"project_root={root} does not contain nexquant.py — "
f"likely still pointing to scripts/ instead of repo root"
)
assert root.name != "scripts", (
f"project_root={root} ends with 'scripts/' — should be repo root"
)
def test_build_command_points_to_predix_py(self):
"""Bug: command pointed to scripts/predix.py which doesn't exist."""
from scripts.predix_parallel import ParallelRunner, RunState
def test_build_command_points_to_nexquant_py(self):
"""Bug: command pointed to scripts/nexquant.py which doesn't exist."""
from scripts.nexquant_parallel import ParallelRunner, RunState
runner = ParallelRunner(num_runs=1, num_api_keys=1, model="local")
run = RunState(run_id=1, api_key_idx=0, model="local")
cmd = runner._build_command(run)
predix_path = Path(cmd[1])
assert predix_path.exists(), (
f"Command references {predix_path} which does not exist — "
nexquant_path = Path(cmd[1])
assert nexquant_path.exists(), (
f"Command references {nexquant_path} which does not exist — "
f"project_root likely still wrong"
)
assert predix_path.name == "predix.py"
assert predix_path.parent.name != "scripts", (
"predix.py should be in repo root, not scripts/"
assert nexquant_path.name == "nexquant.py"
assert nexquant_path.parent.name != "scripts", (
"nexquant.py should be in repo root, not scripts/"
)
def test_env_loading_from_repo_root(self):
@@ -75,7 +75,7 @@ class TestParallelRunnerAPIKeys:
def test_single_api_key_no_overwrite(self):
"""Bug: with num_api_keys=1, individual key was set then overwritten."""
from scripts.predix_parallel import ParallelRunner, RunState
from scripts.nexquant_parallel import ParallelRunner, RunState
with patch.dict(os.environ, {}, clear=True):
os.environ["OPENROUTER_API_KEY"] = "sk-test-key-1"
@@ -95,7 +95,7 @@ class TestParallelRunnerAPIKeys:
def test_multi_api_key_comma_separated(self):
"""With 2+ keys, all runs get comma-separated list for load balancing."""
from scripts.predix_parallel import ParallelRunner, RunState
from scripts.nexquant_parallel import ParallelRunner, RunState
with patch.dict(os.environ, {}, clear=True):
os.environ["OPENROUTER_API_KEY"] = "sk-key-a"
@@ -111,7 +111,7 @@ class TestParallelRunnerAPIKeys:
def test_round_robin_api_key_index(self):
"""Verify round-robin API key index assignment is computed correctly."""
from scripts.predix_parallel import ParallelRunner
from scripts.nexquant_parallel import ParallelRunner
with patch.dict(os.environ, {}, clear=True):
os.environ["OPENROUTER_API_KEY"] = "a"
@@ -129,7 +129,7 @@ class TestParallelRunnerLogFileHandling:
def test_log_file_paths_in_repo_root(self):
"""Bug: logs went to scripts/fin_quant_runN.log."""
from scripts.predix_parallel import ParallelRunner
from scripts.nexquant_parallel import ParallelRunner
runner = ParallelRunner(num_runs=2, num_api_keys=1, model="local")
@@ -170,7 +170,7 @@ class TestCLIProjectRoot:
"4 .parent hops should NOT yield repo root "
f"(got {buggy}, expected {self.REPO_ROOT.parent})"
)
assert (buggy / "Predix").exists() or buggy == self.REPO_ROOT.parent, (
assert (buggy / "NexQuant").exists() or buggy == self.REPO_ROOT.parent, (
f"4 .parent hops overshoots repo root: {buggy}"
)
@@ -210,12 +210,12 @@ class TestCLIProjectRoot:
# All these commands use Path(__file__).parent.parent.parent as project_root
commands = {
"eval_all": "scripts/predix_full_eval.py",
"batch_backtest": "scripts/predix_batch_backtest.py",
"simple_eval": "scripts/predix_simple_eval.py",
"rebacktest": "scripts/predix_rebacktest_strategies.py",
"report": "scripts/predix_strategy_report.py",
"parallel": "scripts/predix_parallel.py",
"eval_all": "scripts/nexquant_full_eval.py",
"batch_backtest": "scripts/nexquant_batch_backtest.py",
"simple_eval": "scripts/nexquant_simple_eval.py",
"rebacktest": "scripts/nexquant_rebacktest_strategies.py",
"report": "scripts/nexquant_strategy_report.py",
"parallel": "scripts/nexquant_parallel.py",
}
for cmd_name, script_path in commands.items():
@@ -231,12 +231,12 @@ class TestCLIProjectRoot:
import inspect
source = inspect.getsource(start_loop_cli)
# The generator should reference scripts/predix_smart_strategy_gen.py
assert "predix_smart_strategy_gen.py" in source, (
"start_loop_cli should reference predix_smart_strategy_gen.py"
# The generator should reference scripts/nexquant_smart_strategy_gen.py
assert "nexquant_smart_strategy_gen.py" in source, (
"start_loop_cli should reference nexquant_smart_strategy_gen.py"
)
script = self.REPO_ROOT / "scripts" / "predix_smart_strategy_gen.py"
script = self.REPO_ROOT / "scripts" / "nexquant_smart_strategy_gen.py"
assert script.exists(), (
f"Generator script not found at {script}"
)
@@ -266,7 +266,7 @@ class TestImportsDontCrash:
def test_import_parallel_runner(self):
"""ParallelRunner should import without errors."""
from scripts.predix_parallel import ParallelRunner, RunState
from scripts.nexquant_parallel import ParallelRunner, RunState
runner = ParallelRunner(num_runs=1, num_api_keys=1, model="local")
assert runner.num_runs == 1
assert len(runner.runs) == 1
+8 -8
View File
@@ -15,8 +15,8 @@ Verifies:
- strategy_orchestrator.py exec() exception logged at ERROR level
- strategy_orchestrator.py template validation warns on unreplaced {{...}}
- factor_runner.py IC_max guard against scalar (AttributeError)
- predix_parallel.py handle leak on Popen failure
- predix_rebacktest_strategies.py bare except replaced with except Exception
- nexquant_parallel.py handle leak on Popen failure
- nexquant_rebacktest_strategies.py bare except replaced with except Exception
"""
import ast
@@ -228,32 +228,32 @@ class TestFactorRunnerICMaxGuard:
)
# ── Fix 13: predix_parallel.py handle leak ───────────────────────────────
# ── Fix 13: nexquant_parallel.py handle leak ───────────────────────────────
class TestParallelRunnerHandleLeak:
def test_log_f_close_on_popen_failure(self):
"""Bug: open() file handle leaked if Popen failed."""
source = (REPO_ROOT / "scripts/predix_parallel.py").read_text()
source = (REPO_ROOT / "scripts/nexquant_parallel.py").read_text()
# After fix, log_f.close() is called before re-raise
assert "log_f.close()" in source, (
"predix_parallel.py should close log file handle on Popen failure"
"nexquant_parallel.py should close log file handle on Popen failure"
)
# ── Fix 14: predix_rebacktest_strategies.py bare except ──────────────────
# ── Fix 14: nexquant_rebacktest_strategies.py bare except ──────────────────
class TestRebacktestBareExcept:
def test_not_bare_except(self):
"""Bug: bare except: pass swallowed all errors including SystemExit."""
source = (REPO_ROOT / "scripts/predix_rebacktest_strategies.py").read_text()
source = (REPO_ROOT / "scripts/nexquant_rebacktest_strategies.py").read_text()
# After fix, should use except Exception, not bare except
assert "except Exception:" in source
assert "except:" not in source, (
"predix_rebacktest_strategies.py should not use bare except:"
"nexquant_rebacktest_strategies.py should not use bare except:"
)
+9 -9
View File
@@ -1,4 +1,4 @@
"""Deep tests for predix_continuous_strategies.py — ML model building, style cycling.
"""Deep tests for nexquant_continuous_strategies.py — ML model building, style cycling.
Tests the build_ml_model function and the round/style alternation logic
without requiring real StrategyOrchestrator connections.
@@ -45,7 +45,7 @@ def close_data():
class TestBuildMLModel:
def test_insufficient_data_returns_none(self, factor_data, close_data):
"""<5000 rows should return None."""
from scripts.predix_continuous_strategies import build_ml_model
from scripts.nexquant_continuous_strategies import build_ml_model
result = build_ml_model(factor_data.iloc[:100], close_data.iloc[:100], "swing")
assert result is None
@@ -55,7 +55,7 @@ class TestBuildMLModel:
"sharpe": 1.5, "max_drawdown": -0.1, "win_rate": 0.55,
"n_trades": 200, "wf_oos_sharpe_mean": 0.8,
}
from scripts.predix_continuous_strategies import build_ml_model
from scripts.nexquant_continuous_strategies import build_ml_model
result = build_ml_model(factor_data, close_data, "daytrading")
assert result is not None
assert "strategy_name" in result
@@ -69,7 +69,7 @@ class TestBuildMLModel:
"sharpe": 1.5, "max_drawdown": -0.1, "win_rate": 0.55,
"n_trades": 200, "wf_oos_sharpe_mean": -0.3,
}
from scripts.predix_continuous_strategies import build_ml_model
from scripts.nexquant_continuous_strategies import build_ml_model
result = build_ml_model(factor_data, close_data, "swing")
assert result is None
@@ -90,7 +90,7 @@ class TestBuildMLModel:
}, index=factor_data.index[:n])
c = pd.Series(1.10 + rng.normal(0, 0.001, n).cumsum(), index=f.index)
try:
from scripts.predix_continuous_strategies import build_ml_model
from scripts.nexquant_continuous_strategies import build_ml_model
result = build_ml_model(f, c, "swing")
assert result is None or isinstance(result, dict)
except Exception as e:
@@ -102,12 +102,12 @@ class TestBuildMLModel:
class TestConfig:
def test_batch_size_is_positive(self):
from scripts import predix_continuous_strategies
assert predix_continuous_strategies.BATCH_SIZE > 0
from scripts import nexquant_continuous_strategies
assert nexquant_continuous_strategies.BATCH_SIZE > 0
def test_cooldown_is_positive(self):
from scripts import predix_continuous_strategies
assert predix_continuous_strategies.COOLDOWN_SECONDS > 0
from scripts import nexquant_continuous_strategies
assert nexquant_continuous_strategies.COOLDOWN_SECONDS > 0
class TestStyleCycling:
+4 -4
View File
@@ -1,4 +1,4 @@
"""Deep tests for predix_gen_strategies_real_bt.py — property-based, edge cases.
"""Deep tests for nexquant_gen_strategies_real_bt.py — property-based, edge cases.
Tests factor loading, threshold rescaling, backtest runner, acceptance
criteria, and the TeeFile logger — without requiring real OHLCV data or LLM.
@@ -27,7 +27,7 @@ from hypothesis import strategies as st
@pytest.fixture
def gen_module():
import importlib
import scripts.predix_gen_strategies_real_bt as m
import scripts.nexquant_gen_strategies_real_bt as m
return m
@@ -280,7 +280,7 @@ class TestConfiguration:
"""Daytrading config uses tighter risk limits."""
os.environ["TRADING_STYLE"] = "daytrading"
import importlib
import scripts.predix_gen_strategies_real_bt as m
import scripts.nexquant_gen_strategies_real_bt as m
importlib.reload(m)
assert m.MIN_IC == 0.02
assert m.MIN_SHARPE == 0.5
@@ -289,7 +289,7 @@ class TestConfiguration:
def test_swing_defaults(self):
os.environ["TRADING_STYLE"] = "swing"
import importlib
import scripts.predix_gen_strategies_real_bt as m
import scripts.nexquant_gen_strategies_real_bt as m
importlib.reload(m)
assert m.MIN_TRADES == 10
assert m.MAX_DRAWDOWN == -0.30
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Tests for MLTrainer - ML Training Pipeline for Predix quant trading system.
Tests for MLTrainer - ML Training Pipeline for NexQuant quant trading system.
Tests cover:
- Feature matrix building
@@ -1,4 +1,4 @@
"""Deep tests for predix_parallel.py — property-based, state transitions, edge cases.
"""Deep tests for nexquant_parallel.py — property-based, state transitions, edge cases.
Tests RunState, ParallelRunner configuration, environment building,
command building, and API key loading logic.
@@ -25,7 +25,7 @@ from hypothesis import strategies as st
@pytest.fixture
def runstate():
from scripts.predix_parallel import RunState
from scripts.nexquant_parallel import RunState
return RunState(run_id=1, api_key_idx=0, model="local")
@@ -43,7 +43,7 @@ class TestRunState:
def test_elapsed_running(self, runstate):
runstate.start_time = datetime(2024, 1, 1, 12, 0, 0)
with patch("scripts.predix_parallel.datetime") as mock_dt:
with patch("scripts.nexquant_parallel.datetime") as mock_dt:
mock_dt.now.return_value = datetime(2024, 1, 1, 13, 30, 45)
assert runstate.elapsed == "01:30:45"
@@ -94,7 +94,7 @@ class TestRunState:
class TestParallelRunnerConfig:
@patch.dict(os.environ, {}, clear=True)
def test_load_api_keys_openrouter(self):
from scripts.predix_parallel import ParallelRunner
from scripts.nexquant_parallel import ParallelRunner
with patch.dict(os.environ, {
"OPENROUTER_API_KEY": "sk-key1",
"OPENROUTER_API_KEY_2": "sk-key2",
@@ -106,13 +106,13 @@ class TestParallelRunnerConfig:
@patch.dict(os.environ, {}, clear=True)
def test_load_api_keys_local(self):
from scripts.predix_parallel import ParallelRunner
from scripts.nexquant_parallel import ParallelRunner
runner = ParallelRunner(num_runs=1, num_api_keys=1, model="local")
assert runner.api_keys == ["local"]
@patch.dict(os.environ, {}, clear=True)
def test_load_api_keys_round_robin(self):
from scripts.predix_parallel import ParallelRunner
from scripts.nexquant_parallel import ParallelRunner
runner = ParallelRunner(num_runs=5, num_api_keys=2, model="local")
assert len(runner.runs) == 5
idxs = [r.api_key_idx for r in runner.runs]
@@ -120,7 +120,7 @@ class TestParallelRunnerConfig:
@patch.dict(os.environ, {}, clear=True)
def test_build_env_local_model(self):
from scripts.predix_parallel import ParallelRunner, RunState
from scripts.nexquant_parallel import ParallelRunner, RunState
with patch.dict(os.environ, {
"OPENAI_API_KEY": "local",
"OPENAI_API_BASE": "http://localhost:8081/v1",
@@ -135,7 +135,7 @@ class TestParallelRunnerConfig:
@patch.dict(os.environ, {}, clear=True)
def test_build_env_openrouter(self):
from scripts.predix_parallel import ParallelRunner, RunState
from scripts.nexquant_parallel import ParallelRunner, RunState
with patch.dict(os.environ, {
"OPENROUTER_API_KEY": "sk-test",
"OPENROUTER_API_KEY_2": "sk-test2",
@@ -147,7 +147,7 @@ class TestParallelRunnerConfig:
@patch.dict(os.environ, {}, clear=True)
def test_build_env_sets_workspace(self):
from scripts.predix_parallel import ParallelRunner, RunState
from scripts.nexquant_parallel import ParallelRunner, RunState
runner = ParallelRunner(num_runs=1, num_api_keys=1, model="local")
rs = RunState(run_id=42, api_key_idx=0, model="local")
env = runner._build_env(rs)
@@ -156,11 +156,11 @@ class TestParallelRunnerConfig:
@patch.dict(os.environ, {}, clear=True)
def test_build_command(self):
from scripts.predix_parallel import ParallelRunner, RunState
from scripts.nexquant_parallel import ParallelRunner, RunState
runner = ParallelRunner(num_runs=1, num_api_keys=1, model="local")
rs = RunState(run_id=7, api_key_idx=0, model="local")
cmd = runner._build_command(rs)
assert "predix.py" in cmd[1] or "predix" in cmd[1]
assert "nexquant.py" in cmd[1] or "nexquant" in cmd[1]
assert "quant" in cmd
assert "--model" in cmd
assert "local" in cmd
@@ -168,7 +168,7 @@ class TestParallelRunnerConfig:
@patch.dict(os.environ, {}, clear=True)
def test_parallel_runner_init_counts(self):
from scripts.predix_parallel import ParallelRunner
from scripts.nexquant_parallel import ParallelRunner
for n in [1, 3, 10]:
runner = ParallelRunner(num_runs=n, num_api_keys=2, model="local")
assert len(runner.runs) == n
@@ -178,20 +178,20 @@ class TestParallelRunnerConfig:
class TestParallelRunnerEdgeCases:
@patch.dict(os.environ, {}, clear=True)
def test_max_runs_limit(self):
from scripts.predix_parallel import ParallelRunner
from scripts.nexquant_parallel import ParallelRunner
runner = ParallelRunner(num_runs=100, num_api_keys=1, model="local")
assert len(runner.runs) == 100
@patch.dict(os.environ, {}, clear=True)
def test_api_keys_empty_uses_local(self):
from scripts.predix_parallel import ParallelRunner
from scripts.nexquant_parallel import ParallelRunner
runner = ParallelRunner(num_runs=1, num_api_keys=2, model="openrouter")
assert len(runner.api_keys) >= 1
@patch.dict(os.environ, {}, clear=True)
def test_build_env_preserves_existing_env(self, monkeypatch):
monkeypatch.setenv("MY_CUSTOM_VAR", "custom_value")
from scripts.predix_parallel import ParallelRunner, RunState
from scripts.nexquant_parallel import ParallelRunner, RunState
runner = ParallelRunner(num_runs=1, num_api_keys=1, model="local")
rs = RunState(run_id=1, api_key_idx=0, model="local")
env = runner._build_env(rs)
+2 -2
View File
@@ -643,14 +643,14 @@ class TestStrategySaver:
'passed': True,
'checks': {},
},
metadata={'version': '1.0', 'author': 'Predix'},
metadata={'version': '1.0', 'author': 'NexQuant'},
)
with open(filepath) as f:
data = json.load(f)
assert data['metadata']['version'] == '1.0'
assert data['metadata']['author'] == 'Predix'
assert data['metadata']['author'] == 'NexQuant'
def test_save_strategy_with_llm_response(self, strategy_saver):
"""Test saving strategy with LLM response preview."""
+10 -10
View File
@@ -123,9 +123,9 @@ class TestWebDashboard:
class TestScriptsImportable:
def test_predix_full_eval(self):
def test_nexquant_full_eval(self):
import importlib
spec = importlib.util.spec_from_file_location("m", PROJECT_ROOT / "scripts/predix_full_eval.py")
spec = importlib.util.spec_from_file_location("m", PROJECT_ROOT / "scripts/nexquant_full_eval.py")
assert spec is not None
def test_extract_results(self):
@@ -153,24 +153,24 @@ class TestScriptsImportable:
spec = importlib.util.spec_from_file_location("m", PROJECT_ROOT / "scripts/kronos_model_eval.py")
assert spec is not None
def test_predix_add_risk_management(self):
def test_nexquant_add_risk_management(self):
import importlib
spec = importlib.util.spec_from_file_location("m", PROJECT_ROOT / "scripts/predix_add_risk_management.py")
spec = importlib.util.spec_from_file_location("m", PROJECT_ROOT / "scripts/nexquant_add_risk_management.py")
assert spec is not None
def test_predix_gen_strategies(self):
def test_nexquant_gen_strategies(self):
import importlib
spec = importlib.util.spec_from_file_location("m", PROJECT_ROOT / "scripts/predix_gen_strategies_real_bt.py")
spec = importlib.util.spec_from_file_location("m", PROJECT_ROOT / "scripts/nexquant_gen_strategies_real_bt.py")
assert spec is not None
def test_predix_quick_daytrading(self):
def test_nexquant_quick_daytrading(self):
import importlib
spec = importlib.util.spec_from_file_location("m", PROJECT_ROOT / "scripts/predix_quick_daytrading.py")
spec = importlib.util.spec_from_file_location("m", PROJECT_ROOT / "scripts/nexquant_quick_daytrading.py")
assert spec is not None
def test_predix_rebacktest_unified(self):
def test_nexquant_rebacktest_unified(self):
import importlib
spec = importlib.util.spec_from_file_location("m", PROJECT_ROOT / "scripts/predix_rebacktest_unified.py")
spec = importlib.util.spec_from_file_location("m", PROJECT_ROOT / "scripts/nexquant_rebacktest_unified.py")
assert spec is not None
def test_realistic_backtest_all(self):
+3 -3
View File
@@ -196,7 +196,7 @@ class TestInfNanHandlingInsertion:
# =============================================================================
# Bug 5: scan_factors reads factor_code twice (predix_full_eval.py:174 + 195)
# Bug 5: scan_factors reads factor_code twice (nexquant_full_eval.py:174 + 195)
# =============================================================================
class TestScanFactorsDoubleRead:
@@ -205,9 +205,9 @@ class TestScanFactorsDoubleRead:
def test_factor_code_read_only_when_needed(self):
"""Confirm the scan_factors double-read behavior (line 174+195)."""
import inspect
from scripts import predix_full_eval
from scripts import nexquant_full_eval
source = inspect.getsource(predix_full_eval.scan_factors)
source = inspect.getsource(nexquant_full_eval.scan_factors)
# Count occurrences of `.read_text()`
count = source.count(".read_text()")
+3 -3
View File
@@ -18,8 +18,8 @@ class TestContinuousGenerator:
def test_module_imports(self):
import importlib.util
spec = importlib.util.spec_from_file_location(
"predix_autopilot",
PROJECT_ROOT / "scripts/predix_autopilot.py",
"nexquant_autopilot",
PROJECT_ROOT / "scripts/nexquant_autopilot.py",
)
assert spec is not None
@@ -107,6 +107,6 @@ class TestAutopilotIntegration:
def test_autopilot_pid_running(self):
import os
result = os.system("pgrep -f predix_autopilot > /dev/null 2>&1")
result = os.system("pgrep -f nexquant_autopilot > /dev/null 2>&1")
# 0 = running, 1 = not running — both are valid states
assert result in (0, 1)
@@ -1,4 +1,4 @@
"""Tests for scripts/predix_full_eval.py pure functions and dataclasses."""
"""Tests for scripts/nexquant_full_eval.py pure functions and dataclasses."""
from __future__ import annotations
@@ -13,7 +13,7 @@ sys.path.insert(0, str(PROJECT_ROOT))
class TestFactorInfo:
def test_construction(self):
from scripts.predix_full_eval import FactorInfo
from scripts.nexquant_full_eval import FactorInfo
fi = FactorInfo(
workspace_hash="abc123",
factor_name="test_factor",
@@ -26,7 +26,7 @@ class TestFactorInfo:
class TestEvalResult:
def test_defaults(self):
from scripts.predix_full_eval import EvalResult
from scripts.nexquant_full_eval import EvalResult
er = EvalResult(factor_name="f1", workspace_hash="h1")
assert er.status == ""
assert er.ic is None
@@ -34,7 +34,7 @@ class TestEvalResult:
assert er.non_null_count == 0
def test_failed_result(self):
from scripts.predix_full_eval import EvalResult
from scripts.nexquant_full_eval import EvalResult
er = EvalResult(
factor_name="f1", workspace_hash="h1",
status="failed", error_message="timeout",
@@ -43,7 +43,7 @@ class TestEvalResult:
assert er.error_message == "timeout"
def test_to_dict(self):
from scripts.predix_full_eval import EvalResult
from scripts.nexquant_full_eval import EvalResult
er = EvalResult(factor_name="f1", workspace_hash="h1", status="success", ic=0.05)
d = er.to_dict()
assert d["factor_name"] == "f1"
@@ -53,26 +53,26 @@ class TestEvalResult:
class TestExtractFactorDescription:
def test_docstring_extracted(self):
from scripts.predix_full_eval import _extract_factor_description
from scripts.nexquant_full_eval import _extract_factor_description
code = '"""This is a test factor.\nComputes momentum."""\nx=1'
desc = _extract_factor_description(code)
assert "test factor" in desc
def test_comment_extraction(self):
from scripts.predix_full_eval import _extract_factor_description
from scripts.nexquant_full_eval import _extract_factor_description
code = "# Momentum factor\n# Uses 20-bar window\nx=1"
desc = _extract_factor_description(code)
assert "Momentum factor" in desc
assert "20-bar window" in desc
def test_no_docstring_or_comments(self):
from scripts.predix_full_eval import _extract_factor_description
from scripts.nexquant_full_eval import _extract_factor_description
code = "x = 1\ny = 2\n"
desc = _extract_factor_description(code)
assert desc == "No description available"
def test_shebang_skipped(self):
from scripts.predix_full_eval import _extract_factor_description
from scripts.nexquant_full_eval import _extract_factor_description
code = "#!/usr/bin/env python\n# Real comment\nx=1"
desc = _extract_factor_description(code)
assert "Real comment" in desc
+2 -2
View File
@@ -16,10 +16,10 @@ PROJECT_ROOT = Path(__file__).parent.parent.parent
sys.path.insert(0, str(PROJECT_ROOT))
class TestPredixCLI:
class TestNexQuantCLI:
def test_cli_commands_available(self):
import subprocess
r = subprocess.run([sys.executable, "predix.py", "--help"], capture_output=True, text=True, timeout=10)
r = subprocess.run([sys.executable, "nexquant.py", "--help"], capture_output=True, text=True, timeout=10)
assert r.returncode == 0
for cmd in ["evaluate", "top", "best", "portfolio", "build-strategies", "generate-strategies", "health"]:
assert cmd in r.stdout.lower(), f"Missing command: {cmd}"