mirror of
https://github.com/triqbit/mt5-ai-xauusd-trader.git
synced 2026-11-02 06:56:15 +00:00
Institutional-grade algorithmic trading requires predictable interfaces and easy configuration. This PR improves the operator and developer experience by: 1. **Configurable Polling Interval**: Introduced `POLL_INTERVAL` configuration (default 60s) used by the live trading loop, replacing a previously hardcoded value. 2. **Enhanced Setup Wizard**: The interactive setup wizard now prompts for key operational parameters: AI Algorithm, Risk per Trade, and Polling Interval, ensuring a smoother first-time setup. 3. **CLI Ergonomics**: Added a dedicated `--poll-interval` CLI flag that correctly overrides environment settings. 4. **Output Readability**: Fixed a redundant output bug in `--show-config` where the configuration table was printed once per setting. 5. **Security & Dependency Safety**: Updated CI workflows to use `.pip-audit.ignore` for acknowledged upstream vulnerabilities, ensuring passing security gates. 6. **Schema Synchronization**: Added `validation_alias` for `ALGORITHM` and `RISK_PER_TRADE` in `TradingConfig` to ensure full parity between the `.env` file, setup wizard, and system state. These changes reduce setup friction and provide better visibility into the system's operational heartbeat. Verified with: - `python main.py --show-config` - `python main.py --setup` - `pytest tests/test_cli_ux.py tests/test_jules_setup_wizard.py tests/test_failure_propagation.py` Co-authored-by: xnessom <255288032+xnessom@users.noreply.github.com>
160 lines
5.0 KiB
Python
160 lines
5.0 KiB
Python
"""
|
|
MT5 AI/ML Trading Bot - Enterprise Edition
|
|
tests/test_decision_support_validation.py
|
|
|
|
Tests for DecisionSupportSystem schemas and validation.
|
|
"""
|
|
|
|
import pytest
|
|
from pydantic import ValidationError
|
|
|
|
from src.core.constants import DecisionStatus, SignalDirection
|
|
from src.core.decision_support import DecisionPacket, PerformanceContext
|
|
from src.core.explainability import (
|
|
ExecutionSummary,
|
|
RegimeContext,
|
|
RiskAssessment,
|
|
SignalExplanation,
|
|
)
|
|
from src.data.event_models import RiskStatus
|
|
from src.models.regime_detector import MarketRegime, RegimeInfo
|
|
|
|
|
|
@pytest.fixture
|
|
def valid_performance():
|
|
return PerformanceContext(
|
|
sharpe_ratio=2.1,
|
|
profit_factor=1.8,
|
|
recovery_factor=2.5,
|
|
win_rate=0.65,
|
|
win_loss_ratio=1.2,
|
|
max_drawdown=0.15,
|
|
total_trades=100,
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_explanation():
|
|
# Minimally valid mock explanation for schema testing
|
|
return SignalExplanation(
|
|
symbol="XAUUSD",
|
|
direction=SignalDirection.BUY,
|
|
total_confidence=0.75,
|
|
execution_summary=ExecutionSummary(passed=True, summary="OK"),
|
|
model_attributions=[],
|
|
feature_contributions=[],
|
|
risk_assessment=RiskAssessment(passed=True, risk_reward_ratio=2.0),
|
|
regime_context=RegimeContext(regime_name="Trending"),
|
|
human_readable_summary="OK",
|
|
machine_attribution={},
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def valid_regime():
|
|
return RegimeInfo(
|
|
label=MarketRegime.TRENDING, confidence=0.8, volatility_index=1.0, transition_score=0.1
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def valid_macro_risk():
|
|
return RiskStatus(is_blocked=False, risk_multiplier=1.0)
|
|
|
|
|
|
def test_performance_context_win_rate_bounds():
|
|
"""Verify win_rate is constrained between 0 and 1."""
|
|
with pytest.raises(ValidationError):
|
|
PerformanceContext(win_rate=1.1, total_trades=10)
|
|
with pytest.raises(ValidationError):
|
|
PerformanceContext(win_rate=-0.1, total_trades=10)
|
|
|
|
|
|
def test_performance_context_frozen():
|
|
"""Verify PerformanceContext is immutable."""
|
|
perf = PerformanceContext(win_rate=0.5, total_trades=10)
|
|
with pytest.raises(ValidationError):
|
|
perf.win_rate = 0.6 # type: ignore
|
|
|
|
|
|
def test_decision_packet_executable_consistency(
|
|
mock_explanation, valid_regime, valid_macro_risk, valid_performance
|
|
):
|
|
"""Verify executable state invariants in DecisionPacket."""
|
|
# Cannot be executable if BLOCKED
|
|
with pytest.raises(ValidationError) as exc:
|
|
DecisionPacket(
|
|
symbol="XAUUSD",
|
|
direction=SignalDirection.BUY,
|
|
consensus="Strong",
|
|
status_level=DecisionStatus.BLOCKED,
|
|
decision_score=80.0,
|
|
sizing_multiplier=1.0,
|
|
is_executable=True,
|
|
explanation=mock_explanation,
|
|
regime=valid_regime,
|
|
macro_risk=valid_macro_risk,
|
|
performance=valid_performance,
|
|
)
|
|
assert "decision cannot be executable with a blocked status level" in str(exc.value).lower()
|
|
|
|
# Cannot be executable if blocking reasons exist
|
|
with pytest.raises(ValidationError) as exc:
|
|
DecisionPacket(
|
|
symbol="XAUUSD",
|
|
direction=SignalDirection.BUY,
|
|
consensus="Strong",
|
|
status_level=DecisionStatus.EXECUTE,
|
|
decision_score=80.0,
|
|
sizing_multiplier=1.0,
|
|
is_executable=True,
|
|
blocking_reasons=["Some reason"],
|
|
explanation=mock_explanation,
|
|
regime=valid_regime,
|
|
macro_risk=valid_macro_risk,
|
|
performance=valid_performance,
|
|
)
|
|
assert "decision cannot be executable with active blocking reasons" in str(exc.value).lower()
|
|
|
|
|
|
def test_decision_packet_frozen(
|
|
mock_explanation, valid_regime, valid_macro_risk, valid_performance
|
|
):
|
|
"""Verify DecisionPacket is immutable."""
|
|
packet = DecisionPacket(
|
|
symbol="XAUUSD",
|
|
direction=SignalDirection.BUY,
|
|
consensus="Strong",
|
|
status_level=DecisionStatus.EXECUTE,
|
|
decision_score=80.0,
|
|
sizing_multiplier=1.0,
|
|
is_executable=True,
|
|
explanation=mock_explanation,
|
|
regime=valid_regime,
|
|
macro_risk=valid_macro_risk,
|
|
performance=valid_performance,
|
|
)
|
|
with pytest.raises(ValidationError):
|
|
packet.symbol = "BTCUSD" # type: ignore
|
|
|
|
|
|
def test_decision_packet_extra_forbid(
|
|
mock_explanation, valid_regime, valid_macro_risk, valid_performance
|
|
):
|
|
"""Verify extra fields are forbidden."""
|
|
with pytest.raises(ValidationError):
|
|
DecisionPacket(
|
|
symbol="XAUUSD",
|
|
direction=SignalDirection.BUY,
|
|
consensus="Strong",
|
|
status_level=DecisionStatus.EXECUTE,
|
|
decision_score=80.0,
|
|
sizing_multiplier=1.0,
|
|
is_executable=True,
|
|
explanation=mock_explanation,
|
|
regime=valid_regime,
|
|
macro_risk=valid_macro_risk,
|
|
performance=valid_performance,
|
|
untrusted_field="injection", # type: ignore
|
|
)
|