162 lines
5.6 KiB
Python
162 lines
5.6 KiB
Python
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
|
|
fmp_api_key: Optional[str] = None
|
|
te_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.fmp_api_key is not None:
|
|
set_config("fmp_api_key", req.fmp_api_key)
|
|
updated.append("fmp")
|
|
if req.te_api_key is not None:
|
|
set_config("te_api_key", req.te_api_key)
|
|
updated.append("te")
|
|
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"}
|