Files
mymt5opp/scripts/diag_mt5_trades.py
gavindiaz 63a829cc46 phase 7-8 完成 + warmup 修复 + 产物结构化重组
主要内容:
- Phase 8 PROMOTE: finalist #1 (trial #324) registry 条目,自动生成
- Optuna objective warmup bug 修复 (shared/optimizer/objective.py)
- studies/ 目录按用途重组为 optuna/ + finalists/ + features/ 三层
- reports/ 加入 Optuna 中文 dashboard (5 主图 + 18 slice + 15 contour)
- 新增 PROJECT_GUIDE.md 项目说明文档
- 新增 build_registry_entry.py / build_optuna_dashboard.py / build_feature_datasets.py
- .gitignore: 允许提交 studies/*.db (Optuna DB) 和 reports/*.html (MT5 + dashboard)
2026-06-27 00:28:07 +08:00

135 lines
5.0 KiB
Python

"""Parse the MT5 IS HTML report and dump the first N deal rows so we can
compare per-trade lots/entry/exit against Python's diag_size_after_warmup.py
output.
The report's "Deals" table rows have ~13-14 cells (Time, Deal, Symbol, Type,
Direction, Volume, Price, Order, Commission, Fee, Swap, Profit, Balance,
Comment). We extract rows whose Type is "buy" or "sell" (entry) and "in" /
"out" (Direction) to reconstruct trade pairs.
Usage:
python scripts/diag_mt5_trades.py 10
"""
from __future__ import annotations
import sys
from pathlib import Path
PROJECT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(PROJECT))
from shared.data.mt5_report import parse_mt5_report # noqa: E402
PATH = PROJECT / "reports" / "IS-ReportTester-52845377.html"
def main() -> int:
n = int(sys.argv[1]) if len(sys.argv) > 1 else 10
raw = PATH.read_bytes()
if raw[:2] in (b"\xff\xfe", b"\xfe\xff"):
text = raw.decode("utf-16")
else:
text = raw.decode("utf-8", errors="replace")
try:
from lxml import html
tree = html.fromstring(text)
except Exception:
import html5lib
tree = html5lib.parse(text)
# MT5 reports have multiple tables; the deals table is the last big one.
# Each <tr> is a deal. Header row has "Time / Deal / Symbol / Type / ...
rows = tree.iter("tr")
deals = []
headers_seen = False
for row in rows:
cells = row.findall("td") or row.findall("th")
if not cells:
continue
texts = [c.text_content().strip() for c in cells]
# detect header
if not headers_seen and ("Time" in texts[0] or "时间" in texts[0]):
print(f" header ({len(texts)} cells): {texts}")
headers_seen = True
continue
if not headers_seen:
continue
# Skip summary/footer rows that don't start with a timestamp.
first = texts[0]
if not first or not any(c.isdigit() for c in first[:4]):
continue
if len(texts) < 8:
continue
deals.append(texts)
print(f"\n parsed {len(deals)} deal rows from {PATH.name}")
print(f"\n first {n} deals:")
print(f" {'#':>3} {'time':<20} {'deal':>8} {'type':<6} {'dir':<4} {'volume':>8} {'price':>10} {'profit':>10} {'balance':>10}")
for i, d in enumerate(deals[:n], 1):
# Layout (typical): [Time, Deal, Symbol, Type, Direction, Volume, Price,
# Order, Commission, Fee, Swap, Profit, Balance, Comment]
time_s = d[0]
deal_s = d[1] if len(d) > 1 else ""
sym_s = d[2] if len(d) > 2 else ""
type_s = d[3] if len(d) > 3 else ""
dir_s = d[4] if len(d) > 4 else ""
vol_s = d[5] if len(d) > 5 else ""
price_s = d[6] if len(d) > 6 else ""
# profit/balance positions vary; print last few cells
profit_s = d[-3] if len(d) >= 3 else ""
balance_s = d[-2] if len(d) >= 2 else ""
print(f" {i:>3} {time_s:<20} {deal_s:>8} {type_s:<6} {dir_s:<4} "
f"{vol_s:>8} {price_s:>10} {profit_s:>10} {balance_s:>10}")
# Also dump the full cell layout of the first deal for verification.
if deals:
print(f"\n first deal full layout ({len(deals[0])} cells):")
for i, c in enumerate(deals[0]):
print(f" [{i:>2}] {c!r}")
# Try to pair entry/exit deals to reconstruct trades.
# An "in" deal (Direction="in") opens a position; an "out" deal closes it.
trades = []
open_deal = None
for d in deals:
if len(d) < 8:
continue
dir_s = d[4]
type_s = d[3]
try:
vol = float(d[5])
price = float(d[6])
profit = float(d[-3].split()[0]) if d[-3] else 0.0
except (ValueError, IndexError):
continue
if dir_s == "in":
open_deal = {"time": d[0], "type": type_s, "vol": vol, "price": price}
elif dir_s == "out" and open_deal is not None:
trades.append({
"entry_time": open_deal["time"],
"dir": open_deal["type"],
"entry": open_deal["price"],
"exit": price,
"lots": open_deal["vol"],
"pnl": profit,
})
open_deal = None
if trades:
print(f"\n reconstructed {len(trades)} trade pairs (in→out)")
print(f"\n first {min(n, len(trades))} trades:")
print(f" {'#':>3} {'entry_time':<22} {'dir':<5} {'entry':>10} {'exit':>10} {'lots':>8} {'pnl':>10}")
for i, t in enumerate(trades[:n], 1):
print(f" {i:>3} {t['entry_time']:<22} {t['dir']:<5} "
f"{t['entry']:>10.2f} {t['exit']:>10.2f} {t['lots']:>8.4f} {t['pnl']:>10.2f}")
import numpy as np
pnls = np.array([t["pnl"] for t in trades])
wins = (pnls > 0).sum()
print(f"\n PnL stats : trades={len(trades)} wins={wins} ({wins/len(trades):.1%}) "
f"sum=${pnls.sum():.2f} avg=${pnls.mean():.2f}")
return 0
if __name__ == "__main__":
raise SystemExit(main())