File size: 3,548 Bytes
d5b7ee9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
"""Configuration management β€” stores settings in ~/.config/trading-cli/config.toml."""
 
from __future__ import annotations
 
import toml
from pathlib import Path
 
CONFIG_DIR = Path("~/.config/trading-cli").expanduser()
CONFIG_PATH = CONFIG_DIR / "config.toml"
DB_PATH = CONFIG_DIR / "trades.db"
 
DEFAULT_CONFIG: dict = {
    "alpaca_api_key": "",
    "alpaca_api_secret": "",
    "alpaca_paper": True,
    "adapter_id": "alpaca",
    "auto_trading": True,
    "sentiment_model": "finbert",
    "strategy_id": "trend_following",
    "risk_pct": 0.02,
    "max_drawdown": 0.15,
    "stop_loss_pct": 0.05,
    "max_positions": 10,
    "default_symbols": ["AAPL", "TSLA", "NVDA"],
    "poll_interval_prices": 30,
    "poll_interval_news": 900,
    "poll_interval_signals": 60,
    "poll_interval_positions": 60,
    "initial_cash": 100000.0,
    "finbert_batch_size": 50,
    "debug_fast_cycle": True,
    "sma_short": 20,
    "sma_long": 50,
    "rsi_period": 14,
    "signal_buy_threshold": 0.15,
    "signal_sell_threshold": -0.15,
    "position_size_warning": 1000.0,
    # ── Strategy weights ──────────────────────────────────────────────────────
    "tech_weight": 0.6,
    "sent_weight": 0.4,
    # ── Technical indicator weights ───────────────────────────────────────────
    "weight_sma": 0.25,
    "weight_rsi": 0.25,
    "weight_bb": 0.20,
    "weight_ema": 0.15,
    "weight_volume": 0.15,
    # ── Bollinger Bands ───────────────────────────────────────────────────────
    "bb_window": 20,
    "bb_std": 2.0,
    # ── EMA periods ───────────────────────────────────────────────────────────
    "ema_fast": 12,
    "ema_slow": 26,
    # ── Volume SMA window ─────────────────────────────────────────────────────
    "volume_window": 20,
    # ── Sentiment event weights ───────────────────────────────────────────────
    "event_weight_earnings": 1.5,
    "event_weight_executive": 1.3,
    "event_weight_product": 1.2,
    "event_weight_macro": 1.4,
    "event_weight_generic": 0.8,
    "sentiment_half_life_hours": 24.0,
}
 
 
def load_config() -> dict:
    """Load config from disk, creating defaults if absent."""
    CONFIG_DIR.mkdir(parents=True, exist_ok=True)
    if not CONFIG_PATH.exists():
        save_config(DEFAULT_CONFIG)
        return dict(DEFAULT_CONFIG)
    with open(CONFIG_PATH) as f:
        on_disk = toml.load(f)
    merged = dict(DEFAULT_CONFIG)
    merged.update(on_disk)
    return merged
 
 
def save_config(config: dict) -> None:
    """Persist config to disk."""
    CONFIG_DIR.mkdir(parents=True, exist_ok=True)
    with open(CONFIG_PATH, "w") as f:
        toml.dump(config, f)
 
 
def get_db_path() -> Path:
    CONFIG_DIR.mkdir(parents=True, exist_ok=True)
    return DB_PATH
 
 
def is_demo_mode(config: dict) -> bool:
    """True if Alpaca keys are not configured."""
    return not (config.get("alpaca_api_key") and config.get("alpaca_api_secret"))