Coverage for src/cc_liquid/cli_callbacks.py: 0%
51 statements
« prev ^ index » next coverage.py v7.10.3, created at 2025-10-13 20:16 +0000
« prev ^ index » next coverage.py v7.10.3, created at 2025-10-13 20:16 +0000
1"""Rich-based CLI callbacks implementation.
3This module depends on Rich and rendering helpers and is intentionally
4separated from the core `callbacks` definitions to avoid UI deps in core.
5"""
7from typing import Any
9from rich.console import Console
10from rich.prompt import Confirm
12from .callbacks import CCLiquidCallbacks
13from .cli_display import display_execution_summary, show_rebalancing_plan
16class RichCLICallbacks(CCLiquidCallbacks):
17 """Rich CLI/Terminal UI implementation of `CCLiquidCallbacks`."""
19 def __init__(self):
20 self.console = Console()
22 def ask_confirmation(self, message: str) -> bool:
23 return Confirm.ask(f"\n[bold yellow]{message}[/bold yellow]")
25 def info(self, message: str) -> None:
26 self.console.print(f"[cyan]{message}[/cyan]")
28 def warn(self, message: str) -> None:
29 self.console.print(f"[yellow]{message}[/yellow]")
31 def error(self, message: str) -> None:
32 self.console.print(f"[red]{message}[/red]")
34 def on_config_override(self, overrides: list[str]) -> None:
35 if overrides:
36 self.console.print(
37 f"[yellow]Applied CLI overrides: {', '.join(overrides)}[/yellow]"
38 )
40 def on_trade_start(self, idx: int, total: int, trade: dict[str, Any]) -> None:
41 side = "BUY" if trade["is_buy"] else "SELL"
42 side_style = "green" if side == "BUY" else "red"
43 self.console.print(
44 f" [{idx}/{total}] {trade['coin']} [{side_style}]{side}[/{side_style}] "
45 f"{trade['sz']:.4f} @ ${trade['price']:,.4f}...",
46 end="",
47 )
49 def on_trade_fill(
50 self, trade: dict[str, Any], fill_data: dict[str, Any], slippage_pct: float
51 ) -> None:
52 avg_px = float(fill_data.get("avgPx", trade["price"]))
53 slippage_style = "green" if slippage_pct <= 0 else "red"
54 self.console.print(
55 f" [green]✓[/green] Filled @ ${avg_px:,.4f} "
56 f"([{slippage_style}]{slippage_pct:+.3f}%[/{slippage_style}])"
57 )
59 def on_trade_fail(self, trade: dict[str, Any], reason: str) -> None:
60 self.console.print(f" [red]✗ {reason}[/red]")
62 def on_batch_complete(self, success: list[dict], failed: list[dict]) -> None:
63 if not success and not failed:
64 return
65 total = len(success) + len(failed)
66 self.console.print(
67 f"\n[bold]Batch Complete:[/bold] "
68 f"[green]{len(success)}/{total} succeeded[/green]"
69 f"{f' [red]{len(failed)} failed[/red]' if failed else ''}"
70 )
72 def show_trade_plan(
73 self,
74 target_positions: dict,
75 trades: list,
76 account_value: float,
77 leverage: float,
78 ) -> None:
79 show_rebalancing_plan(
80 self.console, target_positions, trades, account_value, leverage
81 )
83 def show_execution_summary(
84 self,
85 successful_trades: list[dict],
86 all_trades: list[dict],
87 target_positions: dict,
88 account_value: float,
89 ) -> None:
90 display_execution_summary(
91 self.console, successful_trades, all_trades, target_positions, account_value
92 )
94 def on_order_submitted(self, order) -> None:
95 strategy = order.strategy.upper().replace("_", " ")
96 self.console.print(
97 f"[cyan]Order submitted:[/cyan] {order.coin} {order.side} "
98 f"({strategy}, ID: {order.order_id[:12]}...)"
99 )
101 def on_order_update(self, order, fill_pct: float) -> None:
102 self.console.print(
103 f" {order.coin}: {fill_pct:.0f}% filled "
104 f"({order.filled_size:.2f}/{order.size:.2f})"
105 )
107 def show_open_orders(self, orders: list) -> None:
108 from .cli_display import create_open_orders_panel
109 panel = create_open_orders_panel(orders)
110 self.console.print(panel)
112 def show_order_history(self, history, days: int) -> None:
113 from .cli_display import create_order_history_panel
114 panel = create_order_history_panel(history, days)
115 self.console.print(panel)