from fastapi import APIRouter from pydantic import BaseModel from typing import Dict, Any, Optional import os from services.database import get_all_config, set_config, get_config, get_sources, update_sources, get_analysis_config, save_analysis_config router = APIRouter(prefix="/api/config", tags=["config"]) class ApiKeysRequest(BaseModel): openai_api_key: Optional[str] = None newsapi_key: Optional[str] = None eia_api_key: Optional[str] = None fred_api_key: Optional[str] = None calendar_refresh_h: Optional[str] = None class SourcesRequest(BaseModel): sources: Dict[str, Any] class SettingsRequest(BaseModel): ai_enabled: Optional[str] = None ai_auto_rescore: Optional[str] = None class AnalysisConfigRequest(BaseModel): top_n: Optional[int] = None category_filter: Optional[str] = None template: Optional[str] = None @router.get("/") def get_config_all(): return get_all_config() @router.get("/sources") def list_sources(): return get_sources() @router.put("/sources") def update_sources_endpoint(req: SourcesRequest): update_sources(req.sources) return {"status": "ok", "updated": len(req.sources)} @router.put("/api-keys") def update_api_keys(req: ApiKeysRequest): updated = [] if req.openai_api_key is not None: set_config("openai_api_key", req.openai_api_key) os.environ["OPENAI_API_KEY"] = req.openai_api_key updated.append("openai") if req.newsapi_key is not None: set_config("newsapi_key", req.newsapi_key) updated.append("newsapi") if req.eia_api_key is not None: set_config("eia_api_key", req.eia_api_key) updated.append("eia") if req.fred_api_key is not None: set_config("fred_api_key", req.fred_api_key) updated.append("fred") if req.calendar_refresh_h is not None: set_config("calendar_refresh_h", req.calendar_refresh_h) updated.append("calendar_refresh_h") return {"status": "ok", "updated": updated} @router.put("/settings") def update_settings(req: SettingsRequest): if req.ai_enabled is not None: set_config("ai_enabled", req.ai_enabled) if req.ai_auto_rescore is not None: set_config("ai_auto_rescore", req.ai_auto_rescore) return {"status": "ok"} @router.get("/analysis") def get_analysis_config_endpoint(): return get_analysis_config() @router.put("/analysis") def save_analysis_config_endpoint(req: AnalysisConfigRequest): current = get_analysis_config() if req.top_n is not None: current["top_n"] = req.top_n if req.category_filter is not None: current["category_filter"] = req.category_filter if req.template is not None: current["template"] = req.template save_analysis_config(current) return {"status": "ok", "config": current} class OptionsGateRequest(BaseModel): iv_gate_enabled: Optional[bool] = None iv_gate_ivr_high: Optional[float] = None iv_gate_ivr_extreme: Optional[float] = None iv_gate_skew_threshold: Optional[float] = None @router.get("/options-gate") def get_options_gate(): return { "iv_gate_enabled": (get_config("iv_gate_enabled") or "true").lower() == "true", "iv_gate_ivr_high": float(get_config("iv_gate_ivr_high") or 60), "iv_gate_ivr_extreme": float(get_config("iv_gate_ivr_extreme") or 80), "iv_gate_skew_threshold": float(get_config("iv_gate_skew_threshold") or 8), } @router.put("/options-gate") def save_options_gate(req: OptionsGateRequest): if req.iv_gate_enabled is not None: set_config("iv_gate_enabled", "true" if req.iv_gate_enabled else "false") if req.iv_gate_ivr_high is not None: set_config("iv_gate_ivr_high", str(req.iv_gate_ivr_high)) if req.iv_gate_ivr_extreme is not None: set_config("iv_gate_ivr_extreme", str(req.iv_gate_ivr_extreme)) if req.iv_gate_skew_threshold is not None: set_config("iv_gate_skew_threshold", str(req.iv_gate_skew_threshold)) return {"status": "ok"} # ── Tech Indicators config ────────────────────────────────────────────────── class TechIndicatorsRequest(BaseModel): tech_indicators_enabled: Optional[bool] = None tech_indicators_list: Optional[str] = None # comma-separated: "rsi,ma,bollinger,atr" tech_indicators_auto_calibrate: Optional[bool] = None @router.get("/tech-indicators") def get_tech_indicators(): return { "tech_indicators_enabled": (get_config("tech_indicators_enabled") or "true").lower() == "true", "tech_indicators_list": get_config("tech_indicators_list") or "rsi,ma,bollinger,atr", "tech_indicators_auto_calibrate": (get_config("tech_indicators_auto_calibrate") or "true").lower() == "true", } @router.put("/tech-indicators") def save_tech_indicators(req: TechIndicatorsRequest): if req.tech_indicators_enabled is not None: set_config("tech_indicators_enabled", "true" if req.tech_indicators_enabled else "false") if req.tech_indicators_list is not None: set_config("tech_indicators_list", req.tech_indicators_list) if req.tech_indicators_auto_calibrate is not None: set_config("tech_indicators_auto_calibrate", "true" if req.tech_indicators_auto_calibrate else "false") return {"status": "ok"}