File size: 3,856 Bytes
c4eac91
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#!/usr/bin/env python3
"""parse_alerts.py — extract Trade Copilot alerts from a Telegram Desktop JSON export.

Usage:  python3 parse_alerts.py result.json [--out alerts.csv]
Input:  Telegram Desktop > bot chat > Export chat history > JSON  (result.json)
Output: alerts.csv  (ts_utc, iso, symbol, direction, conf, entry, sl, tp1, tp2, tp3)

Stdlib only. Handles TG's mixed text arrays (strings + entity dicts), skips
heartbeats/tests/skip-notices, dedups same symbol+direction within 4h (keeps first).
"""
import json, re, csv, sys, datetime

DEDUP_WINDOW_S = 4 * 3600

RE_HEAD  = re.compile(r'(?:🟢|🔴)?\s*([A-Z0-9]{1,15}-USDT)\s+(LONG|SHORT)\s*·\s*([\d.]+)\s*/\s*10')
RE_ENTRY = re.compile(r'Entry\s+([\d.,]+)')
RE_SL    = re.compile(r'\bSL\s+([\d.,]+)')
RE_TP1   = re.compile(r'TP1\s+([\d.,]+)')
RE_TP2   = re.compile(r'TP2\s+([\d.,]+)')
RE_TP3   = re.compile(r'TP3\s+([\d.,]+)')

SKIP_MARKERS = ("Trade Copilot alive", "Test Message", "Test:", "Skipped", "funds in use",
                "NOT executed", "PROFIT", "LOSS", "ENTERED", "LIQUIDATED", "summary")

def flatten_text(t):
    """TG export 'text' is str OR list of str/{'text':...} — flatten to one string."""
    if isinstance(t, str):
        return t
    out = []
    for part in t:
        out.append(part if isinstance(part, str) else str(part.get("text", "")))
    return "".join(out)

def num(s):
    return float(s.replace(",", ""))

def main():
    if len(sys.argv) < 2:
        sys.exit("usage: python3 parse_alerts.py result.json [--out alerts.csv]")
    src = sys.argv[1]
    out = sys.argv[sys.argv.index("--out") + 1] if "--out" in sys.argv else "alerts.csv"

    with open(src, encoding="utf-8") as f:
        data = json.load(f)
    messages = data.get("messages", data if isinstance(data, list) else [])

    rows, skipped, last_seen = [], 0, {}
    for m in messages:
        if m.get("type") != "message":
            continue
        text = flatten_text(m.get("text", ""))
        if not text or any(k in text for k in SKIP_MARKERS):
            continue
        h = RE_HEAD.search(text)
        e, s_, t1 = RE_ENTRY.search(text), RE_SL.search(text), RE_TP1.search(text)
        if not (h and e and s_ and t1):
            continue
        # timestamp: prefer unixtime if present
        if m.get("date_unixtime"):
            ts = int(m["date_unixtime"])
        else:
            # TG export date is local time of the exporting machine; treat as-is, note in README
            ts = int(datetime.datetime.fromisoformat(m["date"]).timestamp())
        sym, direction, conf = h.group(1), h.group(2), float(h.group(3))
        key = f"{sym}:{direction}"
        if key in last_seen and ts - last_seen[key] < DEDUP_WINDOW_S:
            skipped += 1
            continue
        last_seen[key] = ts
        t2, t3 = RE_TP2.search(text), RE_TP3.search(text)
        rows.append({
            "ts_utc": ts,
            "iso": datetime.datetime.fromtimestamp(ts, datetime.timezone.utc).isoformat(),
            "symbol": sym, "direction": direction, "conf": conf,
            "entry": num(e.group(1)), "sl": num(s_.group(1)), "tp1": num(t1.group(1)),
            "tp2": num(t2.group(1)) if t2 else "", "tp3": num(t3.group(1)) if t3 else "",
        })

    rows.sort(key=lambda r: r["ts_utc"])
    with open(out, "w", newline="", encoding="utf-8") as f:
        w = csv.DictWriter(f, fieldnames=list(rows[0].keys()) if rows else
                           ["ts_utc","iso","symbol","direction","conf","entry","sl","tp1","tp2","tp3"])
        w.writeheader()
        w.writerows(rows)
    print(f"parsed {len(rows)} alerts  (deduped {skipped})  →  {out}")
    if rows:
        print(f"range: {rows[0]['iso']}{rows[-1]['iso']}")
        hi = [r for r in rows if r["conf"] >= 8.8]
        print(f"conf ≥ 8.8: {len(hi)} alerts")

if __name__ == "__main__":
    main()