- Introduced `train_daily.sh` for automating daily model retraining, including data download and model training steps. - Added `install_cron.sh` for setting up a cron job to run the daily training script. - Created `setup_schedule.sh` for configuring Systemd timers for daily training tasks. - Implemented a terminal UI using Rich for real-time monitoring of trading performance, including metrics display and log handling. - Updated `pyproject.toml` to include the `rich` dependency for UI functionality. - Enhanced `.gitignore` to exclude model and log files. - Added database support for trade persistence and metrics calculation. - Updated README with installation and usage instructions for the new features.
129 lines
2.9 KiB
Python
129 lines
2.9 KiB
Python
"""Keyboard input handling for terminal UI."""
|
|
import sys
|
|
import select
|
|
import termios
|
|
import tty
|
|
from typing import Optional, Callable
|
|
from dataclasses import dataclass
|
|
|
|
|
|
@dataclass
|
|
class KeyAction:
|
|
"""Represents a keyboard action."""
|
|
|
|
key: str
|
|
action: str
|
|
description: str
|
|
|
|
|
|
class KeyboardHandler:
|
|
"""
|
|
Non-blocking keyboard input handler.
|
|
|
|
Uses terminal raw mode to capture single keypresses
|
|
without waiting for Enter.
|
|
"""
|
|
|
|
# Key mappings
|
|
ACTIONS = {
|
|
"q": "quit",
|
|
"Q": "quit",
|
|
"\x03": "quit", # Ctrl+C
|
|
"r": "refresh",
|
|
"R": "refresh",
|
|
"f": "filter",
|
|
"F": "filter",
|
|
"t": "filter_trades",
|
|
"T": "filter_trades",
|
|
"l": "filter_all",
|
|
"L": "filter_all",
|
|
"e": "filter_errors",
|
|
"E": "filter_errors",
|
|
"1": "tab_general",
|
|
"2": "tab_monthly",
|
|
"3": "tab_weekly",
|
|
"4": "tab_daily",
|
|
}
|
|
|
|
def __init__(self):
|
|
self._old_settings = None
|
|
self._enabled = False
|
|
|
|
def enable(self) -> bool:
|
|
"""
|
|
Enable raw keyboard input mode.
|
|
|
|
Returns:
|
|
True if enabled successfully
|
|
"""
|
|
try:
|
|
if not sys.stdin.isatty():
|
|
return False
|
|
|
|
self._old_settings = termios.tcgetattr(sys.stdin)
|
|
tty.setcbreak(sys.stdin.fileno())
|
|
self._enabled = True
|
|
return True
|
|
except Exception:
|
|
return False
|
|
|
|
def disable(self) -> None:
|
|
"""Restore normal terminal mode."""
|
|
if self._enabled and self._old_settings:
|
|
try:
|
|
termios.tcsetattr(
|
|
sys.stdin,
|
|
termios.TCSADRAIN,
|
|
self._old_settings,
|
|
)
|
|
except Exception:
|
|
pass
|
|
self._enabled = False
|
|
|
|
def get_key(self, timeout: float = 0.1) -> Optional[str]:
|
|
"""
|
|
Get a keypress if available (non-blocking).
|
|
|
|
Args:
|
|
timeout: Seconds to wait for input
|
|
|
|
Returns:
|
|
Key character or None if no input
|
|
"""
|
|
if not self._enabled:
|
|
return None
|
|
|
|
try:
|
|
readable, _, _ = select.select([sys.stdin], [], [], timeout)
|
|
if readable:
|
|
return sys.stdin.read(1)
|
|
except Exception:
|
|
pass
|
|
|
|
return None
|
|
|
|
def get_action(self, timeout: float = 0.1) -> Optional[str]:
|
|
"""
|
|
Get action name for pressed key.
|
|
|
|
Args:
|
|
timeout: Seconds to wait for input
|
|
|
|
Returns:
|
|
Action name or None
|
|
"""
|
|
key = self.get_key(timeout)
|
|
if key:
|
|
return self.ACTIONS.get(key)
|
|
return None
|
|
|
|
def __enter__(self):
|
|
"""Context manager entry."""
|
|
self.enable()
|
|
return self
|
|
|
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
"""Context manager exit."""
|
|
self.disable()
|
|
return False
|