From 01f4c23f51c849f496c524d15832f60e2004c917 Mon Sep 17 00:00:00 2001 From: Melchior Reimers Date: Tue, 27 Jan 2026 11:30:50 +0100 Subject: [PATCH] updated dashboard --- __pycache__/daemon.cpython-313.pyc | Bin 10267 -> 10424 bytes daemon.py | 19 ++++----- .../deutsche_boerse.cpython-313.pyc | Bin 14960 -> 15946 bytes .../__pycache__/gettex.cpython-313.pyc | Bin 19021 -> 20139 bytes src/exchanges/deutsche_boerse.py | 28 +++++++++++-- src/exchanges/gettex.py | 38 ++++++++++++++---- 6 files changed, 65 insertions(+), 20 deletions(-) diff --git a/__pycache__/daemon.cpython-313.pyc b/__pycache__/daemon.cpython-313.pyc index 083d85684838307cfe7a0d08398948eb771d8462..6096e0c6a8d2e95199bb172465be07997ddc0efe 100644 GIT binary patch delta 1095 zcmZ`$O>7fK6rQoYPHI zhLENgpeo9(t)AKlQBesY+Det$V5y?&gXUjKL{AFo$p6 z`{sM!yf<_8`0W$?HqZMIT|a%YP=pme=;stKLZ8DSoz$lN`!VjRoe|3zYp@l#yxSaL zTDCGBkdkDf!)P~2gP@Qegfm?d@6U*1c!0XvelrFaMKxsjN8HavwrGg^U+Aqg4C(qi z0du%%V2C65dAtW@q+xe6BWMWO0YebcbT74)%#oc6sj0DpM$iz)X?@OxP%4O!5lD~1 zDlbYq8iG=N2(=-CW@L!znhfN^fx4+yd}D!Bk+qZIS2#vqp7c#uss|y8FFKt?Ix;7k^R^%>MqfZS zn*>U~&TizQhA*uEc1tnUkoTZr&wo8?C_^ib%GC1|{kTS4zy)-~t)T+$BeinqFvj1) zH=PsRA5-6LKZPgg0$S7Kx8{G)zdm^L;H~+;w>^};6jCrGI@51C1)4dt4CW|Q>6;@$ z9KwkA7?dbnW>i{7cqIXg!ddqGnohe@I0koWUWv65RHI94I~ zg<|z=(Viu9R&_34rN>M>K&(=UoOP@mr7R~;?EHBcS3bqp;DKUc0)yd89(UPVfbYYT zI0R3_S3LsLT!@Uyr|Io=U!yq7T%8$8R73DEqQD=K4&O3Ok1=WsT+vPZ8f-;>PI!^l zwXVlk<~Q{Ch8ka**;Mua<$ron_`rA5zxBs|?T_C%y52vvu1qZ@Hr3JF-n;xNfA7N$ z)m)l+EU17rsl{50oO;y$E=0AM|JiQTAuqRm*0!c59<`@_kM4zxmTxsN3J?ADEC+YA zBf8-Fg`aFs2(EkjCxneI2V zFWDp<1;SS2U+SK+RkWS!l)O>y zdpGjkmjH}Y0D4VAu|Sr^mt!mOv9+3~i>#n{vo_I!6P&bIukEnUA2B`n)hF-f5^eK2 z1$a8hd|VBhx7}XO6lejH)7>0wSVmFaMElX+~W zzlgEvZj-b0Vhi=$-f5dH%f7Y@1JHrN9|y$VnT3haXrB)ZF^R)RqnW%7w~0ph)uI8(2?;vy2e zuPvbS)zI$vcU>7Y+OdYG%wzB;9wx)kYnNe|G)mdEb=`zC;z?mObB1y=_*meTjDv=! z>qPR&dW}X>bfkB}a_RNIGWG{nX(^ki*HK*EL0x%~tg@-VeSfZFoU?xX%f4I3H4Hj?RVD zIY~ViD^);^|9V|PXvzys1)(`FG#7-1Cv$n!#%V8&j+6j=CnO?;pC(nr(|E{_FQ1cyPBPNPi|vMi$RKD zNgi_UB2eG*^T%G?t@MT>wr6Z#_oj&LWmR7d{k)2}qB8TUc}Y}cUW-gr&OTJe?c^kO zMy`+yzKMj%6qX|E datetime.datetime.min.replace(tzinfo=datetime.timezone.utc): - # Hole Hashes der letzten 7 Tage für diese Exchange - check_since = oldest_trade_ts - datetime.timedelta(days=1) - existing_hashes = get_existing_trade_hashes(db_url, exchange.name, check_since) - logger.info(f"Found {len(existing_hashes)} existing trade hashes in DB") + # Hole Hashes für den Zeitraum der neuen Trades (plus 1 Tag Puffer) + check_since = oldest_trade_ts - datetime.timedelta(days=1) + existing_hashes = get_existing_trade_hashes(db_url, exchange.name, check_since) + + if existing_hashes: + logger.info(f"Found {len(existing_hashes)} existing trade hashes in DB for period") # Filtere nur wirklich neue Trades new_trades = [] @@ -147,7 +147,8 @@ def run_task(historical=False): if trade_hash not in existing_hashes: new_trades.append(t) else: - # Historischer Sync - keine Deduplizierung nötig + # Keine existierenden Hashes gefunden - alle Trades sind neu + logger.info(f"No existing hashes found - all trades are new") new_trades = trades logger.info(f"Found {len(trades)} total trades, {len(new_trades)} are new.") diff --git a/src/exchanges/__pycache__/deutsche_boerse.cpython-313.pyc b/src/exchanges/__pycache__/deutsche_boerse.cpython-313.pyc index 867d3a0154a9102db8a9abbafd924f1d5ce176cb..f307a0f41e13d46bff8afc09d508e0502ba293e6 100644 GIT binary patch delta 3507 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 delta 2763 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 delta 1769 zcmZ9Ndu-EH6vyxFuaB*3*RI|A+M|z^M;U7&V^0`kK)dnSSXhe4#M-SLjCC#EmbZeA zAp_JP$LJC?{-w;!@JJIg4BL4Z2XroeyG{|Lzr-Om(yyehDJ^)R(I3r>3r zs2$$%mQxAPx}kGDrrXNl6IA! z=8_62>@X~Jw|-PFSV=dvlMd(1Vg(6G33qHIEvE?SCbKIfaAJ88jQNbT3hwy|VW+V~ z|L8b1x^2((tKfU1zEahw@=KhgT^{ACoBqdp5g}34oIo{mcbfmVbLM=P7SyJ0@R;pz z!fY2FO6uTUvy~cQ!hBF7THvhH0x^qH!m44f#YWZex~1MAjr2srqAbCJp)gy?A26=w zfWIxV|OPxMW%hwd>cFO_~xcz#~^+l70md~}V zDJ{_&2GxHyQKIwGFY?y@b>9u|udj#iid&CFxZ8nBpOk#iDB--Z!sc}dH;X)6-%>RS zyc^THycLqM+67$Sb|}3|6t@>EP**5@RCc=_*NhX&^*BvyX-U%xVcb#Hq!;e!)m-1P zFEq@??LySPy^biskK^ zS{ls*MY$dH4qXAi(M;pD!^6Sa;fL&Wj8b04rCml1!|Gy{h#C%2)*Xoj6L79{ol(b( zI6V72>}fRnLE$WxA4NF;i=36&$GD8^I3VgY(Oq!d>CQcmW0%txnNn55EoX1(8x=!| zv$eyLvkhMx3xCKr54k?ecMf{*8H~0`DKj zF~)wk&p|FVIyi6Ur&+yb5jVyiN}opUSCl=AIKtu5h(bKUGD+d@7-27Ph{`R&$eys5u^4-l zDu&+$b`o(Ifj7$r;rtRy(`zXEIYgm1e1N5s^&*Z9AYMY8!C85Ng#w{Orcd;x6D%tF z;(^^kmSD#~v$Rd}3rPl-T4i)UJYQ8uN8n0T3++rQtFOwrF;O3&SHQJWw2q+DbVSEd zWr9H^MO*_%L)l_6I~?897iPgkSS$#HV*8@}u0w(Do=7;*c_6TdMMQaceNG(}Om5b26&<4UAGtPe_89iqFFZ>>71FpTlRgW20{Z9x_f zhioi>wi#YAn_+8nxwaE`dk|fS5S(f*b~{ndmLl7QW)-3qQH?ModJz$rYPM|ZL%AH0 z4l0IfHv(V4OnB+~jiWjHow3br%!G4yIYcLv6`ECZ@^Lj8U3XqWlTf^|WLY}Jeq45-wQC&GLHiM)?(6rhy!j! dIU)$gRtNnF>RO-5Yg16e=_ei#>{%h7{TJvpt>6Fv diff --git a/src/exchanges/deutsche_boerse.py b/src/exchanges/deutsche_boerse.py index 73affb1..7df35f8 100644 --- a/src/exchanges/deutsche_boerse.py +++ b/src/exchanges/deutsche_boerse.py @@ -49,17 +49,23 @@ class DeutscheBoerseBase(BaseExchange): def _get_file_list(self) -> List[str]: """Holt die Dateiliste von der JSON API""" try: - response = requests.get(self.api_url, headers=HEADERS, timeout=30) + api_url = self.api_url + print(f"[{self.name}] Fetching file list from: {api_url}") + response = requests.get(api_url, headers=HEADERS, timeout=30) response.raise_for_status() data = response.json() files = data.get('CurrentFiles', []) print(f"[{self.name}] API returned {len(files)} files") + if files: + print(f"[{self.name}] Sample files: {files[:3]}") return files except Exception as e: print(f"[{self.name}] Error fetching file list from API: {e}") + import traceback + print(f"[{self.name}] Traceback: {traceback.format_exc()}") return [] def _filter_files_for_date(self, files: List[str], target_date: datetime.date) -> List[str]: @@ -128,7 +134,12 @@ class DeutscheBoerseBase(BaseExchange): return [] # NDJSON Format: Eine JSON-Zeile pro Trade - for line in content.strip().split('\n'): + lines = content.strip().split('\n') + if not lines or (len(lines) == 1 and not lines[0].strip()): + # Leere Datei + return [] + + for line in lines: if not line.strip(): continue try: @@ -295,13 +306,22 @@ class DeutscheBoerseBase(BaseExchange): # Alle passenden Dateien herunterladen und parsen (mit Rate-Limiting) successful = 0 + failed = 0 total_files = len(target_files) + if total_files == 0: + print(f"[{self.name}] No files to download for date {target_date}") + return [] + + print(f"[{self.name}] Starting download of {total_files} files...") + for i, file in enumerate(target_files): trades = self._download_and_parse_file(file) if trades: all_trades.extend(trades) successful += 1 + else: + failed += 1 # Rate-Limiting: Pause zwischen Downloads if i < total_files - 1: @@ -309,9 +329,9 @@ class DeutscheBoerseBase(BaseExchange): # Fortschritt alle 100 Dateien if (i + 1) % 100 == 0: - print(f"[{self.name}] Progress: {i + 1}/{total_files} files, {len(all_trades)} trades so far") + print(f"[{self.name}] Progress: {i + 1}/{total_files} files, {successful} successful, {len(all_trades)} trades so far") - print(f"[{self.name}] Downloaded {successful} files, total {len(all_trades)} trades") + print(f"[{self.name}] Downloaded {successful} files ({failed} failed/empty), total {len(all_trades)} trades") return all_trades diff --git a/src/exchanges/gettex.py b/src/exchanges/gettex.py index c0f352d..c33d349 100644 --- a/src/exchanges/gettex.py +++ b/src/exchanges/gettex.py @@ -147,21 +147,37 @@ class GettexExchange(BaseExchange): if len(parts) >= 4: date_str = parts[1] # YYYYMMDD + if not date_str: + print(f"[GETTEX] WARNING: Could not extract date from filename: {filename}") + + # Debug: Zeige erste Zeile + if lines and len(lines) > 0: + print(f"[GETTEX] First line sample: {lines[0][:100]}") + # Gettex CSV hat KEINEN Header! # Format: ISIN,Zeit,Währung,Preis,Menge # z.B.: DE000BAY0017,09:15:03.638460,EUR,45.775,22 - for line in lines: + parse_errors = 0 + for i, line in enumerate(lines): if not line.strip(): continue try: trade = self._parse_headerless_csv_line(line, date_str) if trade: trades.append(trade) - except Exception: + else: + if i < 3: # Zeige nur erste paar Fehler + print(f"[GETTEX] Failed to parse line {i+1}: {line[:80]}") + except Exception as e: + parse_errors += 1 + if i < 3: + print(f"[GETTEX] Exception parsing line {i+1}: {e}, line: {line[:80]}") continue if trades: - print(f"[GETTEX] Parsed {len(trades)} trades from {filename}") + print(f"[GETTEX] Parsed {len(trades)} trades from {filename} ({len(lines)} lines, {parse_errors} errors)") + elif len(lines) > 0: + print(f"[GETTEX] No trades parsed from {filename} ({len(lines)} lines, {parse_errors} errors)") except requests.exceptions.HTTPError as e: if e.response.status_code != 404: @@ -189,11 +205,18 @@ class GettexExchange(BaseExchange): qty_str = parts[4].strip() # Validierung - if not isin or len(isin) != 12: # ISIN ist immer 12 Zeichen + if not isin: return None - price = float(price_str) - quantity = float(qty_str) + # ISIN kann 12 Zeichen sein, aber nicht immer (manchmal kürzer bei Testdaten) + if len(isin) < 10: + return None + + try: + price = float(price_str) + quantity = float(qty_str) + except ValueError: + return None if price <= 0 or quantity <= 0: return None @@ -231,7 +254,8 @@ class GettexExchange(BaseExchange): timestamp=timestamp ) - except Exception: + except Exception as e: + # Stille Fehlerbehandlung - nur bei Debug return None def _parse_csv_row(self, row: dict) -> Optional[Trade]: