config.py 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. from __future__ import annotations
  2. from dataclasses import dataclass
  3. from pathlib import Path
  4. import os
  5. ROOT = Path(__file__).resolve().parents[2]
  6. ENV_PATH = ROOT / ".env"
  7. def _load_env_file(path: Path = ENV_PATH) -> dict[str, str]:
  8. values: dict[str, str] = {}
  9. if not path.exists():
  10. return values
  11. for raw in path.read_text().splitlines():
  12. line = raw.strip()
  13. if not line or line.startswith("#") or "=" not in line:
  14. continue
  15. key, value = line.split("=", 1)
  16. values[key.strip()] = value.strip().strip('"').strip("'")
  17. return values
  18. def _env(name: str, default: str = "") -> str:
  19. file_values = _load_env_file()
  20. return os.getenv(name, file_values.get(name, default))
  21. def _env_bool(name: str, default: bool = False) -> bool:
  22. value = _env(name, "true" if default else "false").strip().lower()
  23. return value in {"1", "true", "yes", "on"}
  24. def _env_bool_any(*names: str, default: bool = False) -> bool:
  25. file_values = _load_env_file()
  26. for name in names:
  27. value = os.getenv(name)
  28. if value is None:
  29. value = file_values.get(name)
  30. if value is not None:
  31. return value.strip().lower() in {"1", "true", "yes", "on"}
  32. return default
  33. def _env_int(name: str, default: int) -> int:
  34. try:
  35. return int(_env(name, str(default)))
  36. except Exception:
  37. return default
  38. @dataclass(frozen=True)
  39. class HermesConfig:
  40. trader_url: str
  41. argus_url: str
  42. crypto_url: str
  43. metals_url: str
  44. news_url: str
  45. atlas_url: str
  46. exec_url: str
  47. crypto_timeframes: tuple[str, ...]
  48. retention_days: int
  49. prune_interval_hours: int
  50. cycle_seconds: int
  51. breakout_memory_window_seconds: int
  52. hermes_allow_actions: bool
  53. def load_config() -> HermesConfig:
  54. timeframes = tuple(
  55. part.strip()
  56. for part in _env("HERMES_CRYPTO_TIMEFRAMES", "5m,15m,1h,4h,1d").split(",")
  57. if part.strip()
  58. )
  59. return HermesConfig(
  60. trader_url=_env("HERMES_TRADER_URL", "http://127.0.0.1:8570"),
  61. argus_url=_env("HERMES_ARGUS_URL", "http://127.0.0.1:8520"),
  62. crypto_url=_env("HERMES_CRYPTO_URL", "http://127.0.0.1:8580"),
  63. metals_url=_env("HERMES_METALS_URL", "http://127.0.0.1:8591"),
  64. news_url=_env("HERMES_NEWS_URL", "http://127.0.0.1:8600"),
  65. atlas_url=_env("HERMES_ATLAS_URL", "http://127.0.0.1:8550"),
  66. exec_url=_env("HERMES_EXEC_URL", "http://127.0.0.1:8560"),
  67. crypto_timeframes=timeframes,
  68. retention_days=_env_int("HERMES_RETENTION_DAYS", 7),
  69. prune_interval_hours=_env_int("HERMES_PRUNE_INTERVAL_HOURS", 6),
  70. cycle_seconds=_env_int("HERMES_CYCLE_SECONDS", 60),
  71. breakout_memory_window_seconds=_env_int("HERMES_BREAKOUT_MEMORY_WINDOW_SECONDS", 900),
  72. hermes_allow_actions=_env_bool_any("HERMES_ALLOW_ACTIONS", "HERMES_ALLOW_AUTO_ACTIONS", default=False),
  73. )