Complete migration to Rust implementation
- Migrate optimization from optimize.sh to src/optimization/ - Remove all Python files (analytics/, server/, hooks/, pyproject.toml) - Add optimization module: optimizer.rs, parser.rs, mod.rs - Implement all missing MCP tool handlers (35 total tools) - Add handle_patch_set_file handler - Clean up orphan files: .venv/, __pycache__, test files - Move test_rcp_server.sh to tests/integration_test.sh - Add Rust integration tests in tests/integration_tests.rs - Fix all compiler warnings with #[allow(dead_code)] - Update test fixtures and structure
This commit is contained in:
@@ -1,226 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# optimize.sh — Launch MT5 genetic optimization (always background + detached)
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/optimize.sh [options]
|
||||
#
|
||||
# Options:
|
||||
# --expert NAME EA name
|
||||
# --set FILE Optimization .set file (with ||Y flags)
|
||||
# --symbol SYMBOL Trading symbol
|
||||
# --from YYYY.MM.DD Start date
|
||||
# --to YYYY.MM.DD End date
|
||||
# --deposit AMOUNT Initial deposit
|
||||
# --model 0|1|2 Tick model (ALWAYS use 0 for grid/martingale EAs)
|
||||
# --log FILE Log file path (default: /tmp/mt5opt_TIMESTAMP.log)
|
||||
#
|
||||
# IMPORTANT: This script launches MT5 as a detached background process.
|
||||
# It returns immediately. Do NOT set a timeout on this script.
|
||||
# Monitor /tmp/mt5opt_*.log and wait for user signal before parsing results.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
source "${SCRIPT_DIR}/platform_detect.sh"
|
||||
|
||||
# ── Defaults ──────────────────────────────────────────────────────────────────
|
||||
DEFAULT_SYMBOL=$(_cfg "backtest_symbol" "XAUUSD")
|
||||
DEFAULT_DEPOSIT=$(_cfg "backtest_deposit" "10000")
|
||||
DEFAULT_CURRENCY=$(_cfg "backtest_currency" "USD")
|
||||
DEFAULT_LEVERAGE=$(_cfg "backtest_leverage" "500")
|
||||
|
||||
EXPERT=""
|
||||
SET_FILE=""
|
||||
SYMBOL="$DEFAULT_SYMBOL"
|
||||
FROM_DATE=""
|
||||
TO_DATE=""
|
||||
DEPOSIT="$DEFAULT_DEPOSIT"
|
||||
CURRENCY="$DEFAULT_CURRENCY"
|
||||
LEVERAGE="$DEFAULT_LEVERAGE"
|
||||
MODEL=0 # ALWAYS 0 for optimization — see below
|
||||
LOG_FILE=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--expert) EXPERT="$2"; shift 2 ;;
|
||||
--set) SET_FILE="$2"; shift 2 ;;
|
||||
--symbol) SYMBOL="$2"; shift 2 ;;
|
||||
--from) FROM_DATE="$2"; shift 2 ;;
|
||||
--to) TO_DATE="$2"; shift 2 ;;
|
||||
--deposit) DEPOSIT="$2"; shift 2 ;;
|
||||
--model)
|
||||
# Warn if user tries to use model != 0
|
||||
if [[ "$2" != "0" ]]; then
|
||||
echo "WARNING: --model $2 ignored. Optimization always uses model=0." >&2
|
||||
echo " Model 1/2 overfits martingale/grid EAs (intra-bar price not simulated)." >&2
|
||||
fi
|
||||
shift 2
|
||||
;;
|
||||
--log) LOG_FILE="$2"; shift 2 ;;
|
||||
*) echo "Unknown option: $1" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ -z "$EXPERT" ]] && { echo "ERROR: --expert is required" >&2; exit 1; }
|
||||
[[ -z "$SET_FILE" ]] && { echo "ERROR: --set is required" >&2; exit 1; }
|
||||
[[ -z "$FROM_DATE" ]] && { echo "ERROR: --from is required" >&2; exit 1; }
|
||||
[[ -z "$TO_DATE" ]] && { echo "ERROR: --to is required" >&2; exit 1; }
|
||||
|
||||
[[ ! -f "$SET_FILE" ]] && { echo "ERROR: Set file not found: $SET_FILE" >&2; exit 1; }
|
||||
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
LOG_FILE="${LOG_FILE:-/tmp/mt5opt_${TIMESTAMP}.log}"
|
||||
JOB_ID="opt_${TIMESTAMP}"
|
||||
|
||||
# ── Resolve platform ──────────────────────────────────────────────────────────
|
||||
resolve_platform
|
||||
|
||||
# ── Write .set file as UTF-16LE with BOM (read-only) ─────────────────────────
|
||||
# MT5 REQUIREMENT: optimization .set files must be UTF-16LE with BOM.
|
||||
# If provided as UTF-8, MT5 strips the ||Y optimization flags silently —
|
||||
# every pass runs with the fixed base value and optimization is useless.
|
||||
python3 - << PYEOF
|
||||
import sys, os, shutil
|
||||
|
||||
src = "${SET_FILE}"
|
||||
dst = "${MT5_TESTER_DIR}/${EXPERT}.set"
|
||||
os.makedirs("${MT5_TESTER_DIR}", exist_ok=True)
|
||||
|
||||
with open(src, 'r', encoding='utf-8', errors='replace') as f:
|
||||
content = f.read()
|
||||
|
||||
# Write UTF-16LE with BOM
|
||||
with open(dst, 'w', encoding='utf-16-le') as f:
|
||||
f.write('\ufeff') # BOM
|
||||
f.write(content)
|
||||
|
||||
# Make read-only — prevents MT5 from overwriting ||Y flags during optimization
|
||||
os.chmod(dst, 0o444)
|
||||
print(f" .set → {dst} (UTF-16LE, read-only)")
|
||||
PYEOF
|
||||
|
||||
# ── Reset OptMode in terminal.ini ─────────────────────────────────────────────
|
||||
# After any optimization run (complete or aborted), MT5 writes OptMode=-1.
|
||||
# On next launch, MT5 reads OptMode=-1 and exits immediately without running.
|
||||
# Must reset to 0 before every optimization launch.
|
||||
TERMINAL_INI="${MT5_DIR}/terminal.ini"
|
||||
if [[ -f "$TERMINAL_INI" ]]; then
|
||||
# Use Python for safe in-place edit (sed -i behaves differently on macOS vs Linux)
|
||||
python3 - << PYEOF
|
||||
import re
|
||||
|
||||
ini_path = "${TERMINAL_INI}"
|
||||
with open(ini_path, 'r', errors='replace') as f:
|
||||
content = f.read()
|
||||
|
||||
# Reset OptMode
|
||||
content = re.sub(r'OptMode=.*', 'OptMode=0', content)
|
||||
# Remove LastOptimization (causes MT5 to skip running)
|
||||
content = re.sub(r'LastOptimization=.*\n?', '', content)
|
||||
|
||||
with open(ini_path, 'w') as f:
|
||||
f.write(content)
|
||||
print(f" terminal.ini: OptMode reset to 0")
|
||||
PYEOF
|
||||
fi
|
||||
|
||||
# ── Build optimization INI ────────────────────────────────────────────────────
|
||||
WINE_PREFIX_DIR=$(dirname "$(dirname "$MT5_DIR")")
|
||||
|
||||
cat > "${WINE_PREFIX_DIR}/drive_c/mt5mcp_backtest.ini" << INI
|
||||
[Tester]
|
||||
Expert=${EXPERT}
|
||||
Symbol=${SYMBOL}
|
||||
Period=M5
|
||||
Deposit=${DEPOSIT}
|
||||
Currency=${CURRENCY}
|
||||
Leverage=${LEVERAGE}
|
||||
Model=${MODEL}
|
||||
FromDate=${FROM_DATE}
|
||||
ToDate=${TO_DATE}
|
||||
Report=C:\\mt5mcp_opt_report
|
||||
Optimization=2
|
||||
ExpertParameters=${EXPERT}.set
|
||||
ShutdownTerminal=1
|
||||
INI
|
||||
|
||||
cat > "${WINE_PREFIX_DIR}/drive_c/mt5mcp_run.bat" << 'EOF'
|
||||
@echo off
|
||||
"C:\Program Files\MetaTrader 5\terminal64.exe" /config:C:\mt5mcp_backtest.ini
|
||||
EOF
|
||||
|
||||
# ── Count optimization combinations ──────────────────────────────────────────
|
||||
COMBINATIONS=$(python3 - << PYEOF
|
||||
import re, math
|
||||
|
||||
with open("${SET_FILE}", 'r', errors='replace') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
total = 1
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if line.startswith(';') or '=' not in line:
|
||||
continue
|
||||
# Format: param=value||start||step||stop||Y
|
||||
parts = line.split('||')
|
||||
if len(parts) >= 5 and parts[-1].strip().upper() == 'Y':
|
||||
try:
|
||||
start = float(parts[1])
|
||||
step = float(parts[2])
|
||||
stop = float(parts[3])
|
||||
count = max(1, int((stop - start) / step) + 1)
|
||||
total *= count
|
||||
except (ValueError, ZeroDivisionError):
|
||||
pass
|
||||
|
||||
print(total)
|
||||
PYEOF
|
||||
)
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo " MT5-Quant Genetic Optimization"
|
||||
echo " Job ID: $JOB_ID"
|
||||
echo " Expert: $EXPERT"
|
||||
echo " Symbol: $SYMBOL Model: ${MODEL} (every tick)"
|
||||
echo " Period: $FROM_DATE → $TO_DATE"
|
||||
echo " Set file: $SET_FILE"
|
||||
echo " Combos: $COMBINATIONS (genetic — converges in ~300-500 passes)"
|
||||
echo " Log: $LOG_FILE"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
# ── Launch detached ───────────────────────────────────────────────────────────
|
||||
# nohup: prevents SIGHUP when parent (Claude task, SSH session) exits
|
||||
# disown: removes from shell job table so shell exit doesn't kill it
|
||||
# Both are required for true detachment.
|
||||
|
||||
nohup bash -c "${MT5_ARCH} '${MT5_WINE}' cmd.exe /c 'C:\\mt5mcp_run.bat' 2>/dev/null || true" \
|
||||
> "$LOG_FILE" 2>&1 &
|
||||
OPT_PID=$!
|
||||
disown $OPT_PID
|
||||
|
||||
# Write job metadata
|
||||
JOBS_DIR="${ROOT_DIR}/.mt5mcp_jobs"
|
||||
mkdir -p "$JOBS_DIR"
|
||||
cat > "${JOBS_DIR}/${JOB_ID}.json" << JEOF
|
||||
{
|
||||
"job_id": "${JOB_ID}",
|
||||
"pid": ${OPT_PID},
|
||||
"expert": "${EXPERT}",
|
||||
"symbol": "${SYMBOL}",
|
||||
"from_date": "${FROM_DATE}",
|
||||
"to_date": "${TO_DATE}",
|
||||
"set_file": "${SET_FILE}",
|
||||
"combinations": ${COMBINATIONS},
|
||||
"log_file": "${LOG_FILE}",
|
||||
"wine_prefix": "${WINE_PREFIX_DIR}",
|
||||
"started_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
}
|
||||
JEOF
|
||||
|
||||
echo ""
|
||||
echo " Launched (pid: $OPT_PID)"
|
||||
echo " Optimization runs for 2-6 hours. Do NOT kill this process."
|
||||
echo " Signal when MT5 shows 'Optimization complete' and use:"
|
||||
echo " python3 analytics/optimize_parser.py --job $JOB_ID"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
Reference in New Issue
Block a user