diff --git a/src/powertrader/core/credentials.py b/src/powertrader/core/credentials.py index 14883f51b..22ed109e9 100644 --- a/src/powertrader/core/credentials.py +++ b/src/powertrader/core/credentials.py @@ -59,7 +59,7 @@ def load(cls, base_dir: Path | None = None) -> BinanceCredentials: return cls(api_key=key, api_secret=secret) except ImportError: logger.debug("keyring package not installed, skipping keyring lookup.") - except Exception as exc: + except (OSError, RuntimeError, ValueError) as exc: logger.debug("Keyring lookup failed: %s", exc) # 3. Legacy plaintext files diff --git a/src/powertrader/core/trading_client.py b/src/powertrader/core/trading_client.py index a5207d73e..9c9d1fd9c 100644 --- a/src/powertrader/core/trading_client.py +++ b/src/powertrader/core/trading_client.py @@ -190,10 +190,12 @@ def get_current_prices(self, coins: list[str]) -> dict[str, float]: mid = (ask + bid) / 2.0 if ask > 0 and bid > 0 else 0.0 if mid > 0: result[coin] = mid - except (OSError, ConnectionError, ValueError, TypeError) as exc: + except (OSError, ConnectionError, ValueError, TypeError, KeyError) as exc: logger.debug("Price fetch failed for %s: %s", coin, exc) + except ExchangeError: + raise except Exception as exc: - logger.warning("Unexpected price fetch error for %s: %s", coin, exc) + raise ExchangeError(f"Unexpected price fetch error for {coin}: {exc}") from exc return result # -- order execution (private) ------------------------------------------- @@ -234,11 +236,12 @@ def _place_order( except (OSError, ConnectionError) as exc: logger.error("Order %s %s %s network error: %s", side, quantity, coin, exc) return None + except (ExchangeError, OrderError): + raise except Exception as exc: - logger.error( - "Order %s %s %s unexpected error: %s", side, quantity, coin, exc, exc_info=True - ) - return None + raise OrderError( + f"Order {side} {quantity} {coin} unexpected error: {exc}" + ) from exc adapted = self._adapt_order(raw) order_id = str(adapted.get("id", "")) @@ -273,10 +276,8 @@ def _wait_terminal(self, symbol: str, order_id: str, timeout: float = 30.0) -> d state = str(adapted.get("state", "")).lower() if state in _TERMINAL_STATES: return adapted - except (OSError, ConnectionError, ValueError, TypeError) as exc: + except (OSError, ConnectionError, ValueError, TypeError, KeyError) as exc: logger.debug("Order poll for %s/%s failed: %s", symbol, order_id, exc) - except Exception as exc: - logger.debug("Order poll for %s/%s unexpected error: %s", symbol, order_id, exc) time.sleep(1) logger.warning("Order %s/%s: timeout waiting for terminal state", symbol, order_id) return None @@ -301,10 +302,8 @@ def _get_lot_size(self, symbol: str) -> dict[str, str]: "minQty": f.get("minQty", "0.00000001"), } break - except (OSError, ConnectionError, ValueError, TypeError, KeyError) as exc: + except (OSError, ConnectionError, ValueError, TypeError, KeyError, AttributeError) as exc: logger.debug("get_symbol_info(%s) failed: %s", symbol, exc) - except Exception as exc: - logger.warning("get_symbol_info(%s) unexpected error: %s", symbol, exc) self._lot_size_cache[symbol] = default return default @@ -329,12 +328,9 @@ def _get_ask_price(self, symbol: str) -> float: self._rate_limiter.acquire() ticker = self._client.get_orderbook_ticker(symbol=symbol) # type: ignore[union-attr] return float(ticker.get("askPrice", 0.0) or 0.0) - except (OSError, ConnectionError, ValueError, TypeError) as exc: + except (OSError, ConnectionError, ValueError, TypeError, KeyError) as exc: logger.debug("Ask price fetch failed for %s: %s", symbol, exc) return 0.0 - except Exception as exc: - logger.warning("Ask price fetch unexpected error for %s: %s", symbol, exc) - return 0.0 # -- response adaptation -------------------------------------------------- diff --git a/src/powertrader/hub/app.py b/src/powertrader/hub/app.py index 05b9c471d..363dfaf37 100644 --- a/src/powertrader/hub/app.py +++ b/src/powertrader/hub/app.py @@ -212,7 +212,7 @@ def train_all_coins(self, force_retrain: bool = False) -> None: if skipped: try: self.status.config(text=f"Skipped already-trained: {', '.join(skipped)}") - except Exception as exc: + except (tk.TclError, AttributeError) as exc: logger.debug("Failed to update status for skipped coins: %s", exc) def force_retrain_all_coins(self) -> None: @@ -226,7 +226,7 @@ def start_trainer_for_selected_coin(self, force_retrain: bool = False) -> None: def _on_status(msg: str) -> None: try: self.status.config(text=msg) - except Exception as exc: + except (tk.TclError, AttributeError) as exc: logger.debug("Failed to update trainer status label: %s", exc) self.pm.start_trainer_for_coin(coin, force_retrain=force_retrain, on_status=_on_status) @@ -240,7 +240,7 @@ def stop_trainer_for_selected_coin(self) -> None: def _apply_forced_dark_mode(self) -> None: try: self.configure(bg=DARK_BG) - except Exception as exc: + except (tk.TclError, AttributeError) as exc: logger.debug("Failed to set root bg: %s", exc) try: @@ -257,52 +257,52 @@ def _apply_forced_dark_mode(self) -> None: self.option_add("*Menu.foreground", DARK_FG) self.option_add("*Menu.activeBackground", DARK_SELECT_BG) self.option_add("*Menu.activeForeground", DARK_SELECT_FG) - except Exception as exc: + except (tk.TclError, AttributeError) as exc: logger.debug("Failed to set widget option defaults: %s", exc) style = ttk.Style(self) try: style.theme_use("clam") - except Exception as exc: + except (tk.TclError, AttributeError) as exc: logger.debug("Failed to set ttk theme to clam: %s", exc) try: style.configure(".", background=DARK_BG, foreground=DARK_FG) - except Exception as exc: + except (tk.TclError, AttributeError) as exc: logger.debug("Failed to configure base style: %s", exc) for name in ("TFrame", "TLabel", "TCheckbutton", "TRadiobutton"): try: style.configure(name, background=DARK_BG, foreground=DARK_FG) - except Exception as exc: + except (tk.TclError, AttributeError) as exc: logger.debug("Failed to configure style %s: %s", name, exc) try: style.configure("TLabelframe", background=DARK_BG, foreground=DARK_FG, bordercolor=DARK_BORDER) style.configure("TLabelframe.Label", background=DARK_BG, foreground=DARK_ACCENT) - except Exception as exc: + except (tk.TclError, AttributeError) as exc: logger.debug("Failed to configure TLabelframe style: %s", exc) try: style.configure("TSeparator", background=DARK_BORDER) - except Exception as exc: + except (tk.TclError, AttributeError) as exc: logger.debug("Failed to configure TSeparator style: %s", exc) try: style.configure("TButton", background=DARK_BG2, foreground=DARK_FG, bordercolor=DARK_BORDER, focusthickness=1, focuscolor=DARK_ACCENT, padding=(10, 6)) style.map("TButton", background=[("active", DARK_PANEL2), ("pressed", DARK_PANEL), ("disabled", DARK_BG2)], foreground=[("active", DARK_ACCENT), ("disabled", DARK_MUTED)], bordercolor=[("active", DARK_ACCENT2), ("focus", DARK_ACCENT)]) - except Exception as exc: + except (tk.TclError, AttributeError) as exc: logger.debug("Failed to configure TButton style: %s", exc) try: style.configure("TEntry", fieldbackground=DARK_PANEL, foreground=DARK_FG, bordercolor=DARK_BORDER, insertcolor=DARK_FG) - except Exception as exc: + except (tk.TclError, AttributeError) as exc: logger.debug("Failed to configure TEntry style: %s", exc) try: style.configure("TCombobox", fieldbackground=DARK_PANEL, background=DARK_PANEL, foreground=DARK_FG, bordercolor=DARK_BORDER, arrowcolor=DARK_ACCENT) style.map("TCombobox", fieldbackground=[("readonly", DARK_PANEL), ("focus", DARK_PANEL2)], foreground=[("readonly", DARK_FG)], background=[("readonly", DARK_PANEL)]) - except Exception as exc: + except (tk.TclError, AttributeError) as exc: logger.debug("Failed to configure TCombobox style: %s", exc) try: @@ -317,7 +317,7 @@ def _apply_forced_dark_mode(self) -> None: style.map("ChartTab.TButton", background=[("active", DARK_PANEL2), ("pressed", DARK_PANEL)], foreground=[("active", DARK_ACCENT2)], bordercolor=[("active", DARK_ACCENT2), ("focus", DARK_ACCENT)]) style.configure("ChartTabSelected.TButton", background=DARK_PANEL, foreground=DARK_ACCENT, bordercolor=DARK_ACCENT2, padding=(10, 6)) - except Exception as exc: + except (tk.TclError, AttributeError) as exc: logger.debug("Failed to configure TNotebook/ChartTab styles: %s", exc) try: @@ -325,18 +325,18 @@ def _apply_forced_dark_mode(self) -> None: style.map("Treeview", background=[("selected", DARK_SELECT_BG)], foreground=[("selected", DARK_SELECT_FG)]) style.configure("Treeview.Heading", background=DARK_BG2, foreground=DARK_ACCENT, relief="flat") style.map("Treeview.Heading", background=[("active", DARK_PANEL2)], foreground=[("active", DARK_ACCENT2)]) - except Exception as exc: + except (tk.TclError, AttributeError) as exc: logger.debug("Failed to configure Treeview style: %s", exc) try: style.configure("TPanedwindow", background=DARK_BG) - except Exception as exc: + except (tk.TclError, AttributeError) as exc: logger.debug("Failed to configure TPanedwindow style: %s", exc) for sb in ("Vertical.TScrollbar", "Horizontal.TScrollbar"): try: style.configure(sb, background=DARK_BG2, troughcolor=DARK_BG, bordercolor=DARK_BORDER, arrowcolor=DARK_ACCENT) - except Exception as exc: + except (tk.TclError, AttributeError) as exc: logger.debug("Failed to configure %s style: %s", sb, exc) # ---- menu ---- @@ -379,7 +379,7 @@ def _build_layout(self) -> None: try: outer.paneconfigure(left, minsize=360) outer.paneconfigure(right, minsize=520) - except Exception as exc: + except tk.TclError as exc: logger.debug("Failed to set outer pane min sizes: %s", exc) left_split = ttk.Panedwindow(left, orient="vertical") @@ -416,7 +416,7 @@ def _init_outer_sash_once(): target = max(min_left, min(total - min_right, desired_left)) outer.sashpos(0, int(target)) self._did_init_outer_sash = True - except Exception as exc: + except (tk.TclError, ValueError, TypeError) as exc: logger.debug("Failed to init outer sash position: %s", exc) self.after_idle(_init_outer_sash_once) @@ -457,7 +457,7 @@ def _init_outer_sash_once(): def _sync_train_coin(*_): try: self.trainer_coin_var.set(self.train_coin_var.get()) - except Exception as exc: + except tk.TclError as exc: logger.debug("Failed to sync train coin var: %s", exc) self.train_coin_combo.bind("<>", _sync_train_coin) @@ -513,13 +513,13 @@ def _btn_update_scrollbars(event=None): else: btn_scroll_y.grid_remove() btn_canvas.yview_moveto(0) - except Exception as exc: + except (tk.TclError, ValueError, TypeError) as exc: logger.debug("Failed to update button scrollbars: %s", exc) def _btn_canvas_on_configure(event=None): try: btn_canvas.coords(_btn_inner_id, 0, 0) - except Exception as exc: + except tk.TclError as exc: logger.debug("Failed to reset button canvas coords: %s", exc) _btn_update_scrollbars() @@ -650,15 +650,15 @@ def _update_neural_overview_scrollbars(event=None) -> None: self._neural_overview_scroll.grid_remove() try: c.yview_moveto(0) - except Exception as exc: + except tk.TclError as exc: logger.debug("Failed to reset neural canvas yview: %s", exc) - except Exception as exc: + except (tk.TclError, ValueError, TypeError) as exc: logger.debug("Failed to update neural overview scrollbars: %s", exc) def _on_neural_canvas_configure(e) -> None: try: self._neural_overview_canvas.itemconfigure(self._neural_overview_window, width=int(e.width)) - except Exception as exc: + except tk.TclError as exc: logger.debug("Failed to configure neural canvas window width: %s", exc) _update_neural_overview_scrollbars() @@ -670,7 +670,7 @@ def _wheel(e): try: if self._neural_overview_scroll.winfo_ismapped(): self._neural_overview_canvas.yview_scroll(int(-1 * (e.delta / 120)), "units") - except Exception as exc: + except (tk.TclError, ValueError, TypeError) as exc: logger.debug("Failed to handle mousewheel scroll: %s", exc) self._neural_overview_canvas.bind("", lambda _e: self._neural_overview_canvas.focus_set(), add="+") @@ -682,7 +682,7 @@ def _wheel(e): self._rebuild_neural_overview() try: self.after_idle(self._update_neural_overview_scrollbars) - except Exception as exc: + except tk.TclError as exc: logger.debug("Failed to schedule neural overview scrollbar update: %s", exc) # ---- LEFT: Live Output ---- @@ -743,7 +743,7 @@ def _wheel(e): try: left_split.paneconfigure(top_controls, minsize=360) left_split.paneconfigure(logs_frame, minsize=220) - except Exception as exc: + except tk.TclError as exc: logger.debug("Failed to set left split pane min sizes: %s", exc) def _init_left_split_sash_once(): @@ -762,7 +762,7 @@ def _init_left_split_sash_once(): target = max(min_top, min(total - min_bottom, target)) left_split.sashpos(0, int(target)) self._did_init_left_split_sash = True - except Exception as exc: + except (tk.TclError, ValueError, TypeError) as exc: logger.debug("Failed to init left split sash position: %s", exc) self.after_idle(_init_left_split_sash_once) @@ -786,7 +786,7 @@ def _show_page(name: str) -> None: for f in self.chart_pages.values(): try: f.pack_forget() - except Exception as exc: + except tk.TclError as exc: logger.debug("Failed to pack_forget chart page: %s", exc) f = self.chart_pages.get(name) if f is not None: @@ -794,7 +794,7 @@ def _show_page(name: str) -> None: for txt, b in self._chart_tab_buttons.items(): try: b.configure(style=("ChartTabSelected.TButton" if txt == name else "ChartTab.TButton")) - except Exception as exc: + except tk.TclError as exc: logger.debug("Failed to update chart tab button style: %s", exc) # Immediately refresh coin chart on page switch try: @@ -810,7 +810,7 @@ def _do_refresh_visible(): if getattr(self, "_coin_folders_sig", None) != cf_sig: self._coin_folders_sig = cf_sig self.coin_folders = build_coin_folders(self.settings["main_neural_dir"], self.coins) - except Exception as exc: + except (tk.TclError, OSError, ValueError, TypeError) as exc: logger.debug("Failed to rebuild coin folders on page switch: %s", exc) pos = self._last_positions.get(coin, {}) if isinstance(self._last_positions, dict) else {} chart.refresh( @@ -821,10 +821,10 @@ def _do_refresh_visible(): dca_line_price=pos.get("dca_line_price"), avg_cost_basis=pos.get("avg_cost_basis"), ) - except Exception as exc: + except (tk.TclError, OSError, ValueError, TypeError) as exc: logger.debug("Failed to refresh chart on page switch: %s", exc) self.after(1, _do_refresh_visible) - except Exception as exc: + except (tk.TclError, OSError, ValueError, TypeError) as exc: logger.debug("Failed to trigger chart refresh on page switch: %s", exc) self._show_chart_page = _show_page @@ -934,13 +934,13 @@ def _resize_trades_columns(*_): try: right_split.paneconfigure(charts_frame, minsize=360) right_split.paneconfigure(right_bottom_split, minsize=220) - except Exception as exc: + except tk.TclError as exc: logger.debug("Failed to set right split pane min sizes: %s", exc) try: right_bottom_split.paneconfigure(trades_frame, minsize=140) right_bottom_split.paneconfigure(hist_frame, minsize=120) - except Exception as exc: + except tk.TclError as exc: logger.debug("Failed to set right bottom split pane min sizes: %s", exc) def _init_right_split_sash_once(): @@ -958,7 +958,7 @@ def _init_right_split_sash_once(): target = max(min_top, min(total - min_bottom, desired_top)) right_split.sashpos(0, int(target)) self._did_init_right_split_sash = True - except Exception as exc: + except (tk.TclError, ValueError, TypeError) as exc: logger.debug("Failed to init right split sash position: %s", exc) def _init_right_bottom_split_sash_once(): @@ -976,7 +976,7 @@ def _init_right_bottom_split_sash_once(): target = max(min_top, min(total - min_bottom, desired_top)) right_bottom_split.sashpos(0, int(target)) self._did_init_right_bottom_split_sash = True - except Exception as exc: + except (tk.TclError, ValueError, TypeError) as exc: logger.debug("Failed to init right bottom split sash position: %s", exc) self.after_idle(_init_right_split_sash_once) @@ -1013,7 +1013,7 @@ def _run(): try: self._paned_clamp_after_ids[key] = self.after(1, _run) - except Exception as exc: + except tk.TclError as exc: logger.debug("Failed to schedule paned clamp: %s", exc) def _clamp_panedwindow_sashes(self, pw: ttk.Panedwindow) -> None: @@ -1059,7 +1059,7 @@ def _get_minsize(pane_id) -> int: pw.sashpos(i, new) except tk.TclError as exc: logger.debug("Failed to set sash position %d: %s", i, exc) - except Exception as exc: + except (tk.TclError, ValueError, TypeError) as exc: logger.debug("Failed to clamp panedwindow sashes: %s", exc) # ---- timeframe change ---- @@ -1083,7 +1083,7 @@ def _on_timeframe_changed(self, event: Any) -> None: avg_cost_basis=pos.get("avg_cost_basis"), ) self._last_chart_refresh = time.time() - except Exception as exc: + except (tk.TclError, OSError, ValueError, TypeError) as exc: logger.debug("Failed to handle timeframe change: %s", exc) # ---- refresh loop ---- @@ -1097,7 +1097,7 @@ def _drain_queue_to_text(self, q: "queue.Queue[str]", txt: tk.Text, max_lines: i changed = True except queue.Empty: pass - except Exception as exc: + except (tk.TclError, ValueError, TypeError) as exc: logger.debug("Failed to drain queue to text widget: %s", exc) if changed: @@ -1105,7 +1105,7 @@ def _drain_queue_to_text(self, q: "queue.Queue[str]", txt: tk.Text, max_lines: i current = int(txt.index("end-1c").split(".")[0]) if current > max_lines: txt.delete("1.0", f"{current - max_lines}.0") - except Exception as exc: + except (tk.TclError, ValueError, TypeError) as exc: logger.debug("Failed to trim text widget lines: %s", exc) pass txt.see("end") @@ -1123,7 +1123,7 @@ def _tick(self) -> None: self.btn_toggle_all.config(text="Stop All") else: self.btn_toggle_all.config(text="Start All") - except Exception as exc: + except tk.TclError as exc: logger.debug("Failed to update toggle-all button text: %s", exc) # Flow gating @@ -1136,7 +1136,7 @@ def _tick(self) -> None: try: self.btn_toggle_all.configure(state=("normal" if can_toggle_all else "disabled")) - except Exception as exc: + except tk.TclError as exc: logger.debug("Failed to update toggle-all button state: %s", exc) # Training overview @@ -1185,13 +1185,13 @@ def _tick(self) -> None: self.lbl_flow_hint.config(text="Flow: Running (use the button to stop)") else: self.lbl_flow_hint.config(text="Flow: Start All") - except Exception as exc: + except (tk.TclError, OSError, ValueError, TypeError) as exc: logger.debug("Failed to refresh training overview: %s", exc) # Training progress bar try: self._refresh_training_progress(self.pm.running_trainers()) - except Exception as exc: + except (tk.TclError, OSError, ValueError, TypeError) as exc: logger.debug("Failed to refresh training progress: %s", exc) self._refresh_neural_overview() @@ -1206,7 +1206,7 @@ def _tick(self) -> None: try: if self.account_chart: self.account_chart.refresh() - except Exception as exc: + except (tk.TclError, OSError, ValueError, TypeError) as exc: logger.debug("Failed to refresh account chart: %s", exc) try: @@ -1214,11 +1214,11 @@ def _tick(self) -> None: if getattr(self, "_coin_folders_sig", None) != cf_sig: self._coin_folders_sig = cf_sig self.coin_folders = build_coin_folders(self.settings["main_neural_dir"], self.coins) - except Exception as exc: + except (OSError, ValueError, TypeError) as exc: logger.debug("Failed to check coin_folders signature: %s", exc) try: self.coin_folders = build_coin_folders(self.settings["main_neural_dir"], self.coins) - except Exception as exc2: + except (OSError, ValueError, TypeError) as exc2: logger.debug("Failed to rebuild coin_folders fallback: %s", exc2) selected_tab = getattr(self, "_current_chart_page", None) @@ -1245,7 +1245,7 @@ def _tick(self) -> None: dca_line_price=pos.get("dca_line_price"), avg_cost_basis=pos.get("avg_cost_basis"), ) - except Exception as exc: + except (tk.TclError, OSError, ValueError, TypeError) as exc: logger.debug("Failed to refresh chart for %s: %s", coin, exc) self._last_chart_refresh = now @@ -1261,7 +1261,7 @@ def _tick(self) -> None: lp = self.pm.trainers.get(sel) if lp: self._drain_queue_to_text(lp.log_q, self.trainer_text) - except Exception as exc: + except (tk.TclError, ValueError, TypeError) as exc: logger.debug("Failed to drain trainer logs: %s", exc) self.status.config(text=f"{now_str()} | hub_dir={self.hub_dir}") @@ -1306,7 +1306,7 @@ def _refresh_training_progress(self, training_running: list) -> None: avg_pct = sum(p[2] for p in coin_progress) / len(coin_progress) self.lbl_training_progress.config(text=f"Training {len(coin_progress)} coins ({avg_pct:.0f}%)") self.training_progress_bar["value"] = avg_pct - except Exception as exc: + except (tk.TclError, OSError, ValueError, TypeError) as exc: logger.debug("Failed to refresh training progress bar: %s", exc) def _refresh_trader_status(self) -> None: @@ -1329,7 +1329,7 @@ def _refresh_trader_status(self) -> None: self.lbl_acct_percent_in_trade.config(text="Percent In Trade: N/A") self.lbl_acct_dca_spread.config(text="DCA Levels (spread): N/A") self.lbl_acct_dca_single.config(text="DCA Levels (single): N/A") - except Exception as exc: + except tk.TclError as exc: logger.debug("Failed to reset account labels: %s", exc) for iid in self.trades_tree.get_children(): self.trades_tree.delete(iid) @@ -1395,7 +1395,7 @@ def _refresh_trader_status(self) -> None: self.lbl_acct_dca_spread.config(text=f"DCA Levels (spread): {spread_levels}") self.lbl_acct_dca_single.config(text=f"DCA Levels (single): {single_levels}") - except Exception as exc: + except (tk.TclError, ValueError, TypeError) as exc: logger.debug("Failed to refresh account info panel: %s", exc) positions = data.get("positions", {}) or {} @@ -1477,7 +1477,7 @@ def _refresh_trader_status(self) -> None: max_dca_24h = 0 try: self.trades_tree.heading("dca_24h", text=f"DCA 24h (max {max_dca_24h})") - except Exception as exc: + except tk.TclError as exc: logger.debug("Failed to update DCA 24h column heading: %s", exc) dca_24h_display = f"{dca_24h}/{max_dca_24h}" @@ -1558,7 +1558,7 @@ def _refresh_health_dashboard(self, neural_running: bool, trader_running: bool) trader_status_age=trader_age, last_error=last_error, ) - except Exception as exc: + except (tk.TclError, OSError, ValueError, TypeError) as exc: logger.debug("Failed to refresh health dashboard: %s", exc) def _refresh_pnl(self) -> None: @@ -1663,21 +1663,21 @@ def _refresh_coin_dependent_ui(self, prev_coins: List[str]) -> None: if hasattr(self, "train_coin_var") and hasattr(self, "trainer_coin_var"): if self.train_coin_var.get(): self.trainer_coin_var.set(self.train_coin_var.get()) - except Exception as exc: + except tk.TclError as exc: logger.debug("Failed to update coin combo boxes: %s", exc) try: if hasattr(self, "neural_wrap") and self.neural_wrap.winfo_exists(): self._rebuild_neural_overview() self._refresh_neural_overview() - except Exception as exc: + except (tk.TclError, OSError, ValueError, TypeError) as exc: logger.debug("Failed to rebuild neural overview: %s", exc) try: prev_set = set([str(c).strip().upper() for c in (prev_coins or []) if str(c).strip()]) if prev_set != set(self.coins): self._rebuild_coin_chart_tabs() - except Exception as exc: + except (tk.TclError, OSError, ValueError, TypeError) as exc: logger.debug("Failed to rebuild coin chart tabs: %s", exc) def _rebuild_neural_overview(self) -> None: @@ -1689,7 +1689,7 @@ def _rebuild_neural_overview(self) -> None: else: for ch in list(self.neural_wrap.winfo_children()): ch.destroy() - except Exception as exc: + except (tk.TclError, AttributeError) as exc: logger.debug("Failed to clear neural wrap children: %s", exc) self.neural_tiles = {} @@ -1700,7 +1700,7 @@ def _rebuild_neural_overview(self) -> None: def _on_enter(_e=None, t=tile): try: t.set_hover(True) - except Exception as exc: + except (tk.TclError, AttributeError) as exc: logger.debug("Failed to set tile hover on: %s", exc) def _on_leave(_e=None, t=tile): @@ -1712,11 +1712,11 @@ def _on_leave(_e=None, t=tile): if w == t: return w = getattr(w, "master", None) - except Exception as exc: + except (tk.TclError, AttributeError) as exc: logger.debug("Failed to check pointer containment on leave: %s", exc) try: t.set_hover(False) - except Exception as exc: + except (tk.TclError, AttributeError) as exc: logger.debug("Failed to set tile hover off: %s", exc) tile.bind("", _on_enter, add="+") @@ -1725,7 +1725,7 @@ def _on_leave(_e=None, t=tile): for w in tile.winfo_children(): w.bind("", _on_enter, add="+") w.bind("", _on_leave, add="+") - except Exception as exc: + except (tk.TclError, AttributeError) as exc: logger.debug("Failed to bind Enter/Leave on tile children: %s", exc) def _open_coin_chart(_e=None, c=coin): @@ -1733,14 +1733,14 @@ def _open_coin_chart(_e=None, c=coin): fn = getattr(self, "_show_chart_page", None) if callable(fn): fn(str(c).strip().upper()) - except Exception as exc: + except (tk.TclError, AttributeError) as exc: logger.debug("Failed to open coin chart page: %s", exc) tile.bind("", _open_coin_chart, add="+") try: for w in tile.winfo_children(): w.bind("", _open_coin_chart, add="+") - except Exception as exc: + except (tk.TclError, AttributeError) as exc: logger.debug("Failed to bind Button-1 on tile children: %s", exc) self.neural_wrap.add(tile, padx=(0, 6), pady=(0, 6)) @@ -1748,13 +1748,13 @@ def _open_coin_chart(_e=None, c=coin): try: self.neural_wrap._schedule_reflow() - except Exception as exc: + except (tk.TclError, AttributeError) as exc: logger.debug("Failed to schedule neural wrap reflow: %s", exc) try: fn = getattr(self, "_update_neural_overview_scrollbars", None) if callable(fn): self.after_idle(fn) - except Exception as exc: + except (tk.TclError, AttributeError) as exc: logger.debug("Failed to schedule neural overview scrollbar update after rebuild: %s", exc) def _refresh_neural_overview(self) -> None: @@ -1766,7 +1766,7 @@ def _refresh_neural_overview(self) -> None: if getattr(self, "_coin_folders_sig", None) != sig: self._coin_folders_sig = sig self.coin_folders = build_coin_folders(self.settings.get("main_neural_dir") or self.project_dir, self.coins) - except Exception as exc: + except (tk.TclError, OSError, ValueError, TypeError) as exc: logger.debug("Failed to rebuild coin folders for neural overview: %s", exc) if not hasattr(self, "_neural_overview_cache"): @@ -1838,7 +1838,7 @@ def _load_short_from_memory_json(path: str) -> int: self.lbl_neural_overview_last.config(text=f"Last: {time.strftime('%H:%M:%S', time.localtime(float(latest_ts)))}") else: self.lbl_neural_overview_last.config(text="Last: N/A") - except Exception as exc: + except (tk.TclError, ValueError, TypeError) as exc: logger.debug("Failed to update neural overview last timestamp: %s", exc) def _rebuild_coin_chart_tabs(self) -> None: @@ -1853,12 +1853,12 @@ def _rebuild_coin_chart_tabs(self) -> None: try: if hasattr(self, "chart_tabs_bar") and self.chart_tabs_bar.winfo_exists(): self.chart_tabs_bar.destroy() - except Exception as exc: + except (tk.TclError, AttributeError) as exc: logger.debug("Failed to destroy chart tabs bar: %s", exc) try: if hasattr(self, "chart_pages_container") and self.chart_pages_container.winfo_exists(): self.chart_pages_container.destroy() - except Exception as exc: + except (tk.TclError, AttributeError) as exc: logger.debug("Failed to destroy chart pages container: %s", exc) self.chart_tabs_bar = WrapFrame(charts_frame) @@ -1876,7 +1876,7 @@ def _show_page(name: str) -> None: for f in self.chart_pages.values(): try: f.pack_forget() - except Exception as exc: + except tk.TclError as exc: logger.debug("Failed to pack_forget chart page: %s", exc) f = self.chart_pages.get(name) if f is not None: @@ -1884,7 +1884,7 @@ def _show_page(name: str) -> None: for txt, b in self._chart_tab_buttons.items(): try: b.configure(style=("ChartTabSelected.TButton" if txt == name else "ChartTab.TButton")) - except Exception as exc: + except tk.TclError as exc: logger.debug("Failed to update chart tab button style: %s", exc) self._show_chart_page = _show_page @@ -1956,7 +1956,7 @@ def _on_save(settings: dict, added_prev_coins: Set[str]) -> None: def _on_close(self) -> None: try: self.stop_all_scripts() - except Exception as exc: + except (tk.TclError, OSError) as exc: logger.debug("Failed to stop all scripts on close: %s", exc) self.destroy() diff --git a/src/powertrader/hub/components/account_chart.py b/src/powertrader/hub/components/account_chart.py index 4d19e080d..a925dda53 100644 --- a/src/powertrader/hub/components/account_chart.py +++ b/src/powertrader/hub/components/account_chart.py @@ -78,7 +78,7 @@ def _on_canvas_configure(e: Any) -> None: except (ValueError, tk.TclError): pass self._resize_after_id = self.after_idle(self.canvas.draw_idle) - except Exception as exc: + except (tk.TclError, ValueError, TypeError, AttributeError) as exc: logger.debug("Account chart configure error: %s", exc) canvas_w.bind("", _on_canvas_configure, add="+") @@ -91,7 +91,7 @@ def _apply_dark_chart_style(self) -> None: for spine in self.ax.spines.values(): spine.set_color(DARK_BORDER) self.ax.grid(True, color=DARK_BORDER, linewidth=0.6, alpha=0.35) - except Exception as exc: + except (ValueError, TypeError, AttributeError) as exc: logger.debug("Failed to apply dark chart style: %s", exc) def refresh(self) -> None: @@ -251,12 +251,12 @@ def refresh(self) -> None: label, (x, y), textcoords="offset points", xytext=(0, 10), ha="center", fontsize=8, color=DARK_FG, zorder=7, ) - except Exception as exc: + except (OSError, ValueError, TypeError, KeyError, IndexError) as exc: logger.debug("Failed to draw trade markers on account chart: %s", exc) try: self.ax.yaxis.set_major_formatter(FuncFormatter(lambda y, _pos: f"${y:,.2f}")) - except Exception as exc: + except (ValueError, TypeError) as exc: logger.debug("Failed to set y formatter: %s", exc) n = len(points) @@ -283,7 +283,7 @@ def refresh(self) -> None: self.ax.set_xticks(tick_x) self.ax.set_xticklabels(tick_lbl) self.ax.tick_params(axis="x", labelsize=8) - except Exception as exc: + except (ValueError, TypeError) as exc: logger.debug("Failed to set account chart x labels: %s", exc) self.ax.set_xlim(-0.5, (len(points) - 0.5) + 0.6) diff --git a/src/powertrader/hub/components/candle_chart.py b/src/powertrader/hub/components/candle_chart.py index 7cb7a30ec..1289475d2 100644 --- a/src/powertrader/hub/components/candle_chart.py +++ b/src/powertrader/hub/components/candle_chart.py @@ -122,7 +122,7 @@ def _on_canvas_configure(e: Any) -> None: except (ValueError, tk.TclError): pass self._resize_after_id = self.after_idle(self.canvas.draw_idle) - except Exception as exc: + except (tk.TclError, ValueError, TypeError, AttributeError) as exc: logger.debug("Canvas configure error: %s", exc) canvas_w.bind("", _on_canvas_configure, add="+") @@ -138,7 +138,7 @@ def _apply_dark_chart_style(self) -> None: for spine in self.ax.spines.values(): spine.set_color(DARK_BORDER) self.ax.grid(True, color=DARK_BORDER, linewidth=0.6, alpha=0.35) - except Exception as exc: + except (ValueError, TypeError, AttributeError) as exc: logger.debug("Failed to apply dark chart style: %s", exc) def refresh( @@ -309,7 +309,7 @@ def _label_right(y: Optional[float], tag: str, color: str) -> None: _label_right(avg_cost_basis, "AVG", "yellow") _label_right(dca_line_price, "DCA", "red") _label_right(trail_line, "SELL", "green") - except Exception as exc: + except (ValueError, TypeError, IndexError) as exc: logger.debug("Failed to draw chart labels: %s", exc) try: @@ -376,7 +376,7 @@ def _label_right(y: Optional[float], tag: str, color: str) -> None: label, (x, y), textcoords="offset points", xytext=(0, 10), ha="center", fontsize=8, color=DARK_FG, zorder=7, ) - except Exception as exc: + except (OSError, ValueError, TypeError, KeyError, IndexError) as exc: logger.debug("Failed to draw trade markers: %s", exc) self.ax.set_xlim(-0.5, (len(candles) - 0.5) + 0.6) @@ -410,7 +410,7 @@ def _label_right(y: Optional[float], tag: str, color: str) -> None: self.ax.set_xticks(tick_x) self.ax.set_xticklabels(tick_lbl) self.ax.tick_params(axis="x", labelsize=8) - except Exception as exc: + except (ValueError, TypeError) as exc: logger.debug("Failed to set x tick labels: %s", exc) self.canvas.draw_idle() diff --git a/src/powertrader/hub/components/candle_fetcher.py b/src/powertrader/hub/components/candle_fetcher.py index d177c2931..d1daee784 100644 --- a/src/powertrader/hub/components/candle_fetcher.py +++ b/src/powertrader/hub/components/candle_fetcher.py @@ -24,7 +24,7 @@ def __init__(self) -> None: logger.debug("kucoin package not installed, using REST fallback") self._mode = "rest" self._market = None - except Exception as exc: + except (OSError, ConnectionError, RuntimeError, ValueError) as exc: logger.debug("KuCoin client init failed: %s", exc) self._mode = "rest" self._market = None diff --git a/src/powertrader/hub/components/health_dashboard.py b/src/powertrader/hub/components/health_dashboard.py index 92117b717..c7c384d64 100644 --- a/src/powertrader/hub/components/health_dashboard.py +++ b/src/powertrader/hub/components/health_dashboard.py @@ -146,5 +146,5 @@ def set_status(self, text: str, color: str) -> None: try: self._canvas.itemconfigure(self._dot, fill=color) self._label.config(text=f"{self._name}: {text}") - except Exception as exc: + except (tk.TclError, AttributeError) as exc: logger.debug("Failed to update health row %s: %s", self._name, exc) diff --git a/src/powertrader/hub/components/wrap_frame.py b/src/powertrader/hub/components/wrap_frame.py index 076300573..f839ba732 100644 --- a/src/powertrader/hub/components/wrap_frame.py +++ b/src/powertrader/hub/components/wrap_frame.py @@ -37,7 +37,7 @@ def clear(self, destroy_widgets: bool = True) -> None: if destroy_widgets: try: it.w.destroy() - except Exception: + except (tk.TclError, AttributeError): pass self._items = [] self._schedule_reflow() diff --git a/src/powertrader/hub/dialogs/settings_dialog.py b/src/powertrader/hub/dialogs/settings_dialog.py index 562372ece..b4be9ab43 100644 --- a/src/powertrader/hub/dialogs/settings_dialog.py +++ b/src/powertrader/hub/dialogs/settings_dialog.py @@ -327,7 +327,7 @@ def save() -> None: messagebox.showinfo("Saved", "Settings saved.") win.destroy() - except Exception as e: + except (ValueError, TypeError, OSError, tk.TclError) as e: messagebox.showerror("Error", f"Failed to save settings:\n{e}") ttk.Button(btns, text="Save", command=save).pack(side="left") @@ -379,7 +379,7 @@ def _open_api_folder() -> None: subprocess.Popen(["open", folder]) return subprocess.Popen(["xdg-open", folder]) - except Exception as e: + except OSError as e: messagebox.showerror("Couldn't open folder", f"Tried to open:\n{self.project_dir}\n\nError:\n{e}") def _clear_api_files() -> None: @@ -398,7 +398,7 @@ def _clear_api_files() -> None: os.remove(key_path) if os.path.isfile(secret_path): os.remove(secret_path) - except Exception as e: + except OSError as e: messagebox.showerror("Delete failed", f"Couldn't delete the files:\n\n{e}") return _refresh_api_status() @@ -575,7 +575,7 @@ def _test_credentials() -> None: "Your API Key + Secret Key worked!\n\n" f"Binance responded successfully.\nUSDT balance: {usdt_balance}\n\nNext: click Save." ) - except Exception as e: + except (OSError, ConnectionError, ValueError, RuntimeError) as e: err_str = str(e) hint = "" if "APIError(code=-2015)" in err_str or "Invalid API-key" in err_str: @@ -636,7 +636,7 @@ def do_save() -> None: f.write(api_key) with open(secret_path, "w", encoding="utf-8") as f: f.write(secret_key) - except Exception as e: + except OSError as e: messagebox.showerror("Save failed", f"Couldn't write the credential files.\n\nError:\n{e}") return diff --git a/src/powertrader/hub/process_manager.py b/src/powertrader/hub/process_manager.py index 200a5c9ee..1b815b3c0 100644 --- a/src/powertrader/hub/process_manager.py +++ b/src/powertrader/hub/process_manager.py @@ -230,7 +230,7 @@ def _poll_runner_ready_then_start_trader( if after_cb: try: after_cb(250, lambda: self._poll_runner_ready_then_start_trader(after_cb)) - except Exception as exc: + except (RuntimeError, ValueError, TypeError) as exc: logger.debug("Polling schedule failed: %s", exc) # ---- training ---- diff --git a/src/powertrader/thinker/runner.py b/src/powertrader/thinker/runner.py index c50de69eb..babbbc45e 100644 --- a/src/powertrader/thinker/runner.py +++ b/src/powertrader/thinker/runner.py @@ -103,7 +103,7 @@ def step(self) -> dict[str, Signal]: logger.error("Signal generation I/O error for %s: %s", coin, exc) if self._health: self._health.record_error("thinker", exc) - except Exception as exc: + except (RuntimeError, ValueError, TypeError, KeyError, IndexError, ArithmeticError) as exc: logger.error("Signal generation failed for %s: %s", coin, exc, exc_info=True) if self._health: self._health.record_error("thinker", exc) diff --git a/src/powertrader/trader/runner.py b/src/powertrader/trader/runner.py index 16fe42b1b..bcccbe8de 100644 --- a/src/powertrader/trader/runner.py +++ b/src/powertrader/trader/runner.py @@ -98,7 +98,7 @@ def run(self) -> None: logger.error("Trade management error: %s", exc) if self._health: self._health.record_error("trader", exc) - except Exception as exc: + except (RuntimeError, ValueError, TypeError, KeyError, IndexError, ArithmeticError) as exc: logger.error("Unexpected trade management error: %s", exc, exc_info=True) if self._health: self._health.record_error("trader", exc) @@ -157,7 +157,7 @@ def _sync_positions(self, prices: dict[str, float]) -> None: except (ExchangeError, OSError, ConnectionError) as exc: logger.error("Failed to fetch holdings: %s", exc) return - except Exception as exc: + except (RuntimeError, ValueError, TypeError, KeyError) as exc: logger.error("Unexpected error fetching holdings: %s", exc, exc_info=True) return @@ -394,7 +394,7 @@ def _calculate_account_value(self, prices: dict[str, float]) -> float: except (ExchangeError, OSError, ConnectionError) as exc: logger.error("Failed to fetch account balance: %s", exc) return 0.0 - except Exception as exc: + except (RuntimeError, ValueError, TypeError, KeyError) as exc: logger.error("Unexpected error fetching balance: %s", exc, exc_info=True) return 0.0 diff --git a/src/powertrader/trainer/runner.py b/src/powertrader/trainer/runner.py index 2369b4271..e293376a7 100644 --- a/src/powertrader/trainer/runner.py +++ b/src/powertrader/trainer/runner.py @@ -165,7 +165,7 @@ def _train_coin( logger.error("Training %s/%s failed: %s", coin, timeframe, exc) if self._health: self._health.record_error("trainer", exc) - except Exception as exc: + except (RuntimeError, ValueError, TypeError, KeyError, IndexError, ArithmeticError) as exc: logger.error( "Training %s/%s unexpected error: %s", coin, timeframe, exc, exc_info=True )