Phase 1: initial codes

This commit is contained in:
2026-05-26 11:04:27 -04:00
parent 03bff49523
commit f49a283059
32 changed files with 4194 additions and 0 deletions
View File
+70
View File
@@ -0,0 +1,70 @@
import os
from pathlib import Path
from dotenv import load_dotenv
_env_loaded = False
def _load_env():
global _env_loaded
if not _env_loaded:
env_path = Path(__file__).parent.parent.parent / ".env"
load_dotenv(env_path)
_env_loaded = True
class Config:
def __init__(self):
_load_env()
self._db_session = None
def get(self, key: str, default: str = "") -> str:
_load_env()
return os.getenv(key, default)
def get_setting(self, key: str, default: str = "") -> str:
from db.database import get_session
from db.models import Setting
try:
with get_session() as session:
row = session.query(Setting).filter_by(key=key).first()
if row:
return row.value
except Exception:
pass
return default
def set_setting(self, key: str, value: str) -> None:
from db.database import get_session
from db.models import Setting
with get_session() as session:
row = session.query(Setting).filter_by(key=key).first()
if row:
row.value = value
else:
session.add(Setting(key=key, value=value))
session.commit()
@property
def anthropic_key(self) -> str:
return self.get("ANTHROPIC_API_KEY") or self.get_setting("anthropic_api_key")
@property
def news_api_key(self) -> str:
return self.get("NEWS_API_KEY") or self.get_setting("news_api_key")
@property
def schwab_app_key(self) -> str:
return self.get("SCHWAB_APP_KEY") or self.get_setting("schwab_app_key")
@property
def schwab_app_secret(self) -> str:
return self.get("SCHWAB_APP_SECRET") or self.get_setting("schwab_app_secret")
@property
def robinhood_username(self) -> str:
return self.get("ROBINHOOD_USERNAME") or self.get_setting("robinhood_username")
@property
def robinhood_password(self) -> str:
return self.get("ROBINHOOD_PASSWORD") or self.get_setting("robinhood_password")
+40
View File
@@ -0,0 +1,40 @@
def fmt_currency(value: float, decimals: int = 2) -> str:
if value is None:
return ""
sign = "-" if value < 0 else ""
return f"{sign}${abs(value):,.{decimals}f}"
def fmt_percent(value: float, decimals: int = 2, plus: bool = True) -> str:
if value is None:
return ""
sign = "+" if value > 0 and plus else ""
return f"{sign}{value:.{decimals}f}%"
def fmt_large_number(value: float) -> str:
if value is None:
return ""
abs_val = abs(value)
sign = "-" if value < 0 else ""
if abs_val >= 1_000_000_000_000:
return f"{sign}{abs_val / 1_000_000_000_000:.2f}T"
if abs_val >= 1_000_000_000:
return f"{sign}{abs_val / 1_000_000_000:.2f}B"
if abs_val >= 1_000_000:
return f"{sign}{abs_val / 1_000_000:.2f}M"
if abs_val >= 1_000:
return f"{sign}{abs_val / 1_000:.1f}K"
return f"{sign}{abs_val:.2f}"
def fmt_volume(value: int) -> str:
return fmt_large_number(float(value)) if value else ""
def pnl_color(value: float) -> str:
if value > 0:
return "#a6e3a1"
if value < 0:
return "#f38ba8"
return "#cdd6f4"
+30
View File
@@ -0,0 +1,30 @@
import threading
def send_toast(title: str, message: str, duration: int = 5) -> None:
def _send():
try:
from win10toast import ToastNotifier
toaster = ToastNotifier()
toaster.show_toast(title, message, duration=duration, threaded=True)
except Exception:
try:
import subprocess
ps_script = (
f"[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, "
f"ContentType = WindowsRuntime] | Out-Null; "
f"$template = [Windows.UI.Notifications.ToastNotificationManager]::GetTemplateContent("
f"[Windows.UI.Notifications.ToastTemplateType]::ToastText02); "
f"$template.SelectSingleNode('//text[@id=1]').InnerText = '{title}'; "
f"$template.SelectSingleNode('//text[@id=2]').InnerText = '{message}'; "
f"$notifier = [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier('StockMind'); "
f"$notifier.Show([Windows.UI.Notifications.ToastNotification]::new($template))"
)
subprocess.Popen(
["powershell", "-WindowStyle", "Hidden", "-Command", ps_script],
creationflags=0x08000000,
)
except Exception:
pass
threading.Thread(target=_send, daemon=True).start()