"""Diagnose-Script für DTC HISTORICAL_ORDER_FILL_RESPONSE Paket-Struktur. Startet SC, verbindet via DTC, empfängt Fill-Pakete und analysiert die Byte-Struktur um den Preis-Offset zu finden. Aufruf: python3 test_dtc_diag.py [account] [num_days] """ import socket import struct import sys import time from datetime import datetime # ── DTC Konstanten ────────────────────────────────────────────────────────── _LOGON_REQUEST = 1 _LOGON_RESPONSE = 2 _HEARTBEAT = 3 _HIST_FILLS_REQ = 303 _HIST_FILL_RESP = 304 _PROTOCOL_VER = 8 def _s(text, n): b = (text or "").encode("ascii", errors="replace")[:n] return b.ljust(n, b"\x00") def _build_logon(): fmt = "= 4: size = struct.unpack_from("= 4 and len(buf) >= size: msg = bytes(buf[:size]) return msg, buf[size:] try: chunk = sock.recv(65536) if not chunk: raise ConnectionError("Verbindung geschlossen") buf += chunk except socket.timeout: return None, buf def _unpack_str(data, offset, n): return data[offset:offset+n].rstrip(b"\x00").decode("ascii", errors="replace") def _ts(raw): try: return datetime.fromtimestamp(raw).strftime("%Y-%m-%d %H:%M:%S") if 0 < raw < 4_102_444_800 else f"invalid({raw})" except Exception: return f"error({raw})" def hex_dump(data, label=""): """Formatierter Hex-Dump mit ASCII-Darstellung.""" print(f"\n{'='*60}") if label: print(f" {label}") print(f" Länge: {len(data)} Bytes") print(f"{'='*60}") for i in range(0, len(data), 16): chunk = data[i:i+16] hex_part = " ".join(f"{b:02X}" for b in chunk) ascii_part = "".join(chr(b) if 32 <= b < 127 else "." for b in chunk) print(f" {i:4d} ({i:04X}): {hex_part:<48} |{ascii_part}|") def analyze_fill_packet(data, fill_num): """Umfassende Analyse eines HIST_FILL_RESP Pakets.""" n = len(data) print(f"\n{'#'*60}") print(f" FILL-PAKET #{fill_num} ({n} Bytes)") print(f"{'#'*60}") # Bekannte Felder ausgeben print("\n--- Bestätigte Felder ---") print(f" [0] Size (uint16) = {struct.unpack_from('= 16: print(f" [8] TotalMsg(int32) = {struct.unpack_from('= 80: sym = _unpack_str(data, 16, 64) print(f" [16] Symbol (ch[64]) = '{sym}'") if n >= 96: exch = _unpack_str(data, 80, 16) print(f" [80] Exchange(ch[16]) = '{exch}'") if n >= 132: bs = struct.unpack_from("= 152: dt_raw = struct.unpack_from("= 160: qty = struct.unpack_from("= 192: tid = _unpack_str(data, 160, 32) print(f" [160] TradeID (ch[32]) = '{tid}'") if n >= 256: acc = _unpack_str(data, 224, 32) print(f" [224] Account (ch[32]) = '{acc}'") # Unbekannte Zone 96-128 analysieren print("\n--- Zone 96-128 (UNBEKANNT, möglicherweise Preis) ---") for off in range(96, min(128, n-3), 4): if off + 4 <= n: as_uint32 = struct.unpack_from(" 1 else "" num_days = int(sys.argv[2]) if len(sys.argv) > 2 else 30 print(f"DTC Diagnose-Tool") print(f"Account: '{account}', Tage: {num_days}") print(f"Verbinde zu localhost:11099 ...") try: sock = socket.socket() sock.settimeout(10) sock.connect(("localhost", 11099)) sock.settimeout(5) print("Verbunden!") except Exception as e: print(f"FEHLER: {e}") return buf = b"" # Logon sock.sendall(_build_logon()) for _ in range(20): msg, buf = _recv_msg(sock, buf) if msg is None: continue mtype = struct.unpack_from(" check_off: val = struct.unpack_from(">> NoOrderFills=1 gefunden bei Offset {check_off}! (fill_count={fill_count})") # Letztes Paket immer anzeigen (NoOrderFills-Paket) if fill_count == 1: print(f"\n=== ERSTES PAKET (hex dump) ===") hex_dump(msg) print(f"\n\nGesamt empfangene Fill-Pakete: {fill_count}") if raw_packets: print("\n=== LETZTES PAKET (hex dump) ===") hex_dump(raw_packets[-1], f"Paket #{fill_count}") if len(raw_packets) >= 2: print("\n=== VERGLEICH: Preis-relevante Bytes in allen Paketen ===") print(f" {'Offset':<8} ", end="") for i in range(min(len(raw_packets), 12)): print(f"{'Paket'+str(i+1):>12}", end="") print() # Zeige uint32-Werte für Offsets 96-200 for off in range(88, 200, 4): row = f" [{off:3d}] " interesting = False vals = [] for pkt in raw_packets[:12]: if len(pkt) > off + 3: v = struct.unpack_from("12}" else: row += f"{'N/A':>12}" # Interessant wenn Werte variieren (könnten Preise sein) if vals and max(vals) != min(vals): interesting = True if off in (128, 144, 152, 160): interesting = True if interesting: print(row + " ← variiert!" if max(vals) != min(vals) else row) sock.close() print("\nFertig.") if __name__ == "__main__": main()