Move test_mcp.py to tests directory

This commit is contained in:
Devid HW
2026-04-22 12:26:37 +07:00
parent e1adcad45c
commit ccc9e7e4d8
+193
View File
@@ -0,0 +1,193 @@
#!/usr/bin/env python3
"""MCP test harness: run backtest then exercise all granular analysis tools."""
import json
import subprocess
import sys
import time
import threading
BINARY = "/Users/masdevid/.cargo/bin/mt5-quant"
TIMEOUT = 1200 # 20 min for backtest
def send(proc, msg):
line = json.dumps(msg) + "\n"
proc.stdin.write(line.encode())
proc.stdin.flush()
def recv(proc, timeout=30):
"""Read one JSON-RPC response line."""
proc.stdout.readline() # skip blank / notification lines
deadline = time.time() + timeout
while time.time() < deadline:
line = proc.stdout.readline().decode("utf-8", errors="replace").strip()
if not line:
continue
try:
return json.loads(line)
except json.JSONDecodeError:
pass # skip non-JSON lines (logs etc)
return None
def recv_with_id(proc, expected_id, timeout=1200):
"""Read lines until we get a response with the expected id."""
deadline = time.time() + timeout
while time.time() < deadline:
line = proc.stdout.readline().decode("utf-8", errors="replace").strip()
if not line:
continue
try:
msg = json.loads(line)
if msg.get("id") == expected_id:
return msg
except json.JSONDecodeError:
pass
return None
def tool_call(proc, call_id, name, arguments=None):
send(proc, {
"jsonrpc": "2.0",
"id": call_id,
"method": "tools/call",
"params": {"name": name, "arguments": arguments or {}}
})
def extract_text(resp):
if not resp:
return "NO RESPONSE"
result = resp.get("result", {})
content = result.get("content", [])
if content:
text = content[0].get("text", "")
try:
parsed = json.loads(text)
return json.dumps(parsed, indent=2)[:1500]
except Exception:
return text[:1500]
if "error" in resp:
return f"ERROR: {resp['error']}"
return str(resp)[:500]
def ok(resp):
if not resp:
return False
result = resp.get("result", {})
content = result.get("content", [])
is_error = result.get("isError", True)
if is_error:
return False
if content:
text = content[0].get("text", "")
try:
parsed = json.loads(text)
return parsed.get("success", True) is not False
except Exception:
return True
return not is_error
# ── Start server ──────────────────────────────────────────────────────────────
print("Starting mt5-quant MCP server...")
proc = subprocess.Popen(
[BINARY],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
)
# ── Handshake ─────────────────────────────────────────────────────────────────
send(proc, {
"jsonrpc": "2.0", "id": 0,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": {"name": "test-harness", "version": "1.0"}
}
})
init_resp = recv_with_id(proc, 0, timeout=10)
if not init_resp:
print("FATAL: no initialize response")
proc.terminate()
sys.exit(1)
print("Initialized OK")
send(proc, {"jsonrpc": "2.0", "method": "notifications/initialized", "params": {}})
# ── Run backtest ──────────────────────────────────────────────────────────────
print("\n=== run_backtest (DPS21, XAUUSD, M5, OHLC, skip_compile) ===")
tool_call(proc, 1, "run_backtest", {
"expert": "DPS21",
"symbol": "XAUUSDc",
"timeframe": "M5",
"model": 1, # OHLC faster
"skip_compile": True,
"timeout": 900,
"startup_delay_secs": 15,
})
resp = recv_with_id(proc, 1, timeout=TIMEOUT)
print(extract_text(resp))
backtest_ok = ok(resp)
print(f"backtest_ok={backtest_ok}")
if not backtest_ok:
print("Backtest failed aborting analysis tests")
proc.terminate()
sys.exit(1)
# ── Full analysis ─────────────────────────────────────────────────────────────
print("\n=== analyze_report (latest, all analytics) ===")
tool_call(proc, 2, "analyze_report", {})
resp = recv_with_id(proc, 2, timeout=60)
print(extract_text(resp))
print(f"analyze_report ok={ok(resp)}")
# ── Granular analytics ────────────────────────────────────────────────────────
granular = [
("analyze_monthly_pnl", {}),
("analyze_drawdown_events", {}),
("analyze_top_losses", {"limit": 5}),
("analyze_loss_sequences", {}),
("analyze_position_pairs", {}),
("analyze_direction_bias", {}),
("analyze_streaks", {}),
("analyze_concurrent_peak", {}),
("analyze_profit_distribution", {}),
("analyze_time_performance", {}),
("analyze_hold_time_distribution",{}),
("analyze_layer_performance", {}),
("analyze_volume_vs_profit", {}),
("analyze_costs", {}),
("analyze_efficiency", {}),
("list_deals", {"limit": 5}),
("search_deals_by_comment", {"query": "tp"}),
("search_deals_by_magic", {"magic": "0"}),
]
results = {}
for i, (tool, args) in enumerate(granular, start=10):
print(f"\n=== {tool} ===")
tool_call(proc, i, tool, args)
resp = recv_with_id(proc, i, timeout=30)
status = "OK" if ok(resp) else "FAIL"
results[tool] = status
print(f" {status}")
if status == "FAIL":
print(" " + extract_text(resp)[:400])
# ── Summary ───────────────────────────────────────────────────────────────────
print("\n\n========== SUMMARY ==========")
passed = [t for t, s in results.items() if s == "OK"]
failed = [t for t, s in results.items() if s != "OK"]
print(f"PASSED ({len(passed)}): {', '.join(passed)}")
if failed:
print(f"FAILED ({len(failed)}): {', '.join(failed)}")
else:
print("All granular analytics PASSED")
proc.terminate()