Files
mt5-ai-xauusd-trader/scripts/generate_research_report.py
LeoGitHubtriqbitgoogle-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
c6668af65b docs: improve developer onboarding and contribution experience (#1446)
* docs: improve developer onboarding and contribution experience

- Harden DX domain: format and fix 35+ files in scripts/ and migrations/.
- Sync health dashboards (PROJECT_HEALTH, EXECUTIVE_SUMMARY) with June 5 metrics.
- Update maturity maps in ARCHITECTURE_QUICK and SCORECARD to 'Production'.
- Reduce total repo lint debt from 146 to 33.

Co-authored-by: triqbit <242833443+triqbit@users.noreply.github.com>

* docs: improve developer onboarding and contribution experience

- Harden DX domain: format and fix 25+ files in scripts/ and utility modules.
- Sync health dashboards (PROJECT_HEALTH, EXECUTIVE_SUMMARY) with June 5 metrics.
- Update maturity maps in ARCHITECTURE_QUICK and SCORECARD to 'Production'.
- Ensure strictly non-restricted files are modified to comply with auto-merge policy.

Co-authored-by: triqbit <242833443+triqbit@users.noreply.github.com>

---------

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Co-authored-by: triqbit <242833443+triqbit@users.noreply.github.com>
2026-05-30 13:14:09 +00:00

252 lines
9.2 KiB
Python

"""
MT5 AI/ML Trading Bot - Enterprise Edition
scripts/generate_research_report.py
Automated institutional-grade research report generation demonstration.
"""
import os
import sys
from datetime import datetime, timedelta, timezone
from unittest.mock import MagicMock, patch
import numpy as np
import pandas as pd
# Ensure src is in path
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from src.analytics.drift_analyzer import DriftAnalyzer
from src.analytics.execution_quality import ExecutionAnalyzer
from src.analytics.journal_mining import JournalMiner
from src.core.trade_logger import TradeLogger
from src.models.regime_detector import RegimeDetector
from src.research.benchmarks import EMACrossoverStrategy
from src.research.hyperopt_walkforward import WalkForwardConfig, WalkForwardOptimizer
from src.research.rare_event_simulator import RareEventConfig, RareEventSimulator, RareEventType
from src.research.reporting import RareEventSection, ResearchOrchestrator, ResearchReporter
from src.research.stress_lab import StressLab, StressTestMetrics
from src.trading.capital_allocator import CapitalAllocator, StrategyConfig
def generate_synthetic_data(n=1000):
"""Generate professional synthetic XAUUSD data."""
np.random.seed(42)
close = 2300 + np.cumsum(np.random.randn(n) * 2)
df = pd.DataFrame(
{
"open": close - np.random.randn(n),
"high": close + np.abs(np.random.randn(n) * 2),
"low": close - np.abs(np.random.randn(n) * 2),
"close": close,
"tick_volume": np.random.randint(100, 1000, n),
"spread": 0.2 + np.random.rand(n) * 0.2,
}
)
df.index = pd.date_range(start="2024-01-01", periods=n, freq="5min")
return df
def setup_mock_journal_db(db_url="sqlite:///mock_trades.db"):
"""Populate a mock database for journal mining."""
logger = TradeLogger(db_url)
# Add some signals and trades
for i in range(10):
sig_id = logger.log_signal(
{
"symbol": "XAUUSD",
"direction": 1 if i % 2 == 0 else -1,
"entry_price": 2300.0 + i,
"algorithm": "PPO_Agent",
"confidence": 0.8,
"volatility": 0.0005,
}
)
logger.log_trade(
ticket=1000 + i,
symbol="XAUUSD",
direction=1 if i % 2 == 0 else -1,
entry_price=2300.0 + i,
lot_size=0.1,
signal_id=sig_id,
)
# Close some trades with PnL
pnl = 50.0 if i < 7 else -100.0 # mostly wins, then some losses for clusters
logger.update_trade(ticket=1000 + i, exit_price=2300.0 + i + (pnl / 100), pnl=pnl)
# Add risk events for blocked signal analysis
for _ in range(3):
sig_id = logger.log_signal(
{
"symbol": "XAUUSD",
"direction": 1,
"entry_price": 2300.0,
"algorithm": "RL_Agent",
"confidence": 0.5,
"timestamp": datetime.now(timezone.utc) - timedelta(days=1),
}
)
logger.log_risk_event("SIGNAL_REJECTED", "Low confidence score", "XAUUSD", signal_id=sig_id)
logger.log_risk_event("SPREAD_WIDENING", "Spread too wide during news", "XAUUSD")
return db_url
def main():
print("🚀 Starting Institutional Research Report Generation...")
# 1. Initialization & Data Setup
data = generate_synthetic_data(1000)
orchestrator = ResearchOrchestrator(
title="Q1 2025 Institutional Strategy Robustness & Alpha Audit",
executive_summary=(
"Comprehensive audit of the XAUUSD ensemble strategy. The system demonstrates "
"high resilience (Score: 88/100) across adversarial simulations, though performance "
"degradation was noted during flash-crash scenarios. Capital allocation remains "
"optimally diversified with a score of 0.85."
),
conclusion=(
"Deployment recommended with a 15% risk reduction during high-impact macro windows. "
"Model drift is STABLE, but continued monitoring of feature importance shifts is advised."
),
)
# 2. Market Regime Analysis
print("📊 Analyzing Market Regimes...")
detector = RegimeDetector()
regime_section = detector.generate_summary(data)
orchestrator.add_section(regime_section)
# 3. Stress Test Outcomes
print("🛡️ Running Stress Tests...")
strategy = EMACrossoverStrategy(9, 21)
lab = StressLab(strategy, data)
baseline_metrics = StressTestMetrics(
total_return=0.15,
max_drawdown=0.05,
sharpe_ratio=2.1,
win_rate=0.55,
num_trades=50,
execution_quality_score=0.98,
latency_impact=0.01,
)
resilience_report = lab.run_standard_suite(baseline_metrics)
orchestrator.add_section(resilience_report.to_report_section())
# 4. Hyperparameter Robustness
print("🧪 Optimizing Walk-Forward Robustness...")
def ema_param_space(trial):
return {
"fast_window": trial.suggest_int("fast_window", 5, 20),
"slow_window": trial.suggest_int("slow_window", 21, 50),
}
wf_optimizer = WalkForwardOptimizer(
data=data,
strategy_factory=EMACrossoverStrategy,
param_space=ema_param_space,
config=WalkForwardConfig(n_trials=5, train_size=400, test_size=100, step_size=100),
)
wf_result = wf_optimizer.run_optimization()
orchestrator.add_section(wf_result.to_report_section())
# 5. Trade Pattern Findings (Journal Mining)
print("🔍 Mining Trade Journal for Patterns...")
db_url = setup_mock_journal_db()
miner = JournalMiner(db_url)
journal_report = miner.run_mining()
orchestrator.add_section(journal_report.to_report_section())
# 6. Model Drift Observations
print("📉 Detecting Model Drift...")
analyzer = DriftAnalyzer()
drift_report = analyzer.calculate_drift(data.iloc[:500], data.iloc[500:])
orchestrator.add_section(drift_report.to_report_section())
# 7. Capital Allocation Insights
print("💰 Evaluating Capital Allocation...")
allocator = CapitalAllocator(total_budget=100000.0)
allocator.add_strategy(
StrategyConfig(
strategy_id="XAUUSD_PPO_V2",
symbol="XAUUSD",
model_family="RL",
capital_cap=50000.0,
performance_multiplier=1.2,
)
)
allocator.update_allocation("XAUUSD_PPO_V2", 45000.0)
orchestrator.add_section(allocator.to_report_section())
# 8. Rare Event Simulations
print("☄️ Simulating Rare Events...")
simulator = RareEventSimulator(seed=123)
_, flash_crash_res = simulator.generate_scenario(
RareEventConfig(event_type=RareEventType.FLASH_CRASH)
)
_, vacuum_res = simulator.generate_scenario(
RareEventConfig(event_type=RareEventType.LIQUIDITY_VACUUM)
)
rare_event_section = RareEventSection(
scenarios=[flash_crash_res.to_report_summary(), vacuum_res.to_report_summary()],
insights="The strategy successfully navigated the liquidity vacuum but suffered 8% peak drawdown during the flash crash.",
)
orchestrator.add_section(rare_event_section)
# 9. Execution Quality Analysis
print("⚡ Analyzing Execution Quality...")
# Mock connector for analyzer
mock_connector = MagicMock()
mock_connector.get_symbol_properties.return_value = {"digits": 2, "contract_size": 100.0}
mock_connector.get_rates_range.return_value = pd.DataFrame()
mock_connector.get_ticks_range.return_value = pd.DataFrame()
exec_analyzer = ExecutionAnalyzer(db_url=db_url, connector=mock_connector)
# We need to ensure we don't try to fetch from MT5 since it's not connected
# In a real scenario, this would use a live connector.
with patch.object(exec_analyzer, "analyze_trade", return_value=None):
exec_summary = exec_analyzer.generate_summary_report(days=7)
# Manually fill some metrics for the demo if it came back empty
if exec_summary.executed_trade_count == 0:
exec_summary.avg_slippage = 0.5
exec_summary.avg_broker_slippage = 0.2
exec_summary.avg_latency_ms = 120.0
exec_summary.avg_fill_quality = 0.92
exec_summary.avg_edge_capture = 0.78
exec_summary.execution_efficiency_score = 0.88
exec_summary.executed_trade_count = 10
orchestrator.add_section(exec_summary.to_report_section())
# 10. Build & Export
print("📝 Finalizing Report...")
report = orchestrator.build()
reporter = ResearchReporter()
md_path = "research_audit_report.md"
html_path = "research_audit_report.html"
reporter.save_markdown(report, md_path)
reporter.save_html(report, html_path)
print("\n✅ Report generated successfully!")
print(f" - Markdown: {os.path.abspath(md_path)}")
print(f" - HTML: {os.path.abspath(html_path)}")
# Print scannable version to terminal
print("\n" + "=" * 50)
reporter.format_for_terminal(report)
# Cleanup mock DB
if os.path.exists("mock_trades.db"):
os.remove("mock_trades.db")
if __name__ == "__main__":
main()